123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942 |
- import 'dart:async';
- import 'dart:convert';
- import 'dart:io';
- import 'dart:math';
- import 'package:cached_network_image/cached_network_image.dart';
- import 'package:flutter/cupertino.dart';
- import 'package:flutter/material.dart';
- import 'package:flutter/scheduler.dart';
- import 'package:flutter/services.dart';
- import 'package:flutter_keyboard_visibility/flutter_keyboard_visibility.dart';
- import 'package:get_it/get_it.dart';
- import 'package:images_picker/images_picker.dart';
- import 'package:provider/provider.dart';
- import 'package:sport/bean/game.dart';
- import 'package:sport/bean/image.dart' as photo;
- import 'package:sport/bean/message.dart';
- import 'package:sport/bean/post.dart';
- import 'package:sport/bean/post_user.dart';
- import 'package:sport/bean/user_info.dart';
- import 'package:sport/db/message_db.dart';
- import 'package:sport/pages/game/game_detail.dart';
- import 'package:sport/pages/my/feedback_page.dart';
- import 'package:sport/pages/social/post_detail_page.dart';
- import 'package:sport/pages/social/share_webview.dart';
- import 'package:sport/pages/social/user_detail_page.dart';
- import 'package:sport/provider/game_model.dart';
- import 'package:sport/provider/message_model.dart';
- import 'package:sport/provider/user_model.dart';
- import 'package:sport/router/navigator_util.dart';
- import 'package:sport/services/api/inject_api.dart';
- import 'package:sport/services/userid.dart';
- import 'package:sport/utils/DateFormat.dart';
- import 'package:sport/utils/toast.dart';
- import 'package:sport/widgets/appbar.dart';
- import 'package:sport/widgets/button_cancel.dart';
- import 'package:sport/widgets/button_primary.dart';
- import 'package:sport/widgets/decoration.dart';
- import 'package:sport/widgets/dialog/popupmenu.dart' as menu;
- import 'package:sport/widgets/dialog/request_dialog.dart';
- import 'package:sport/widgets/loading.dart';
- import 'package:sport/widgets/menu_bar.dart';
- import 'package:sport/widgets/space.dart';
- import 'gallery_photo_view.dart';
- class ChatPage extends StatefulWidget {
- final UserInfo user;
- final Post? post; // 分享来的帖子
- final String? hash; // 分享来的link
- final String? image; // 分享来的图片 ...
- final bool fetch; // 分享来的图片 ...
- ChatPage(this.user, {this.post, this.hash, this.image, this.fetch = false});
- @override
- State<StatefulWidget> createState() => _ChatPageState();
- }
- // 服务端的数据 只有 拿了 和 没拿 ,客户端 就是 读了 和没读 ...
- class _ChatPageState extends State<ChatPage> with InjectLoginApi, InjectApi, UserId, WidgetsBindingObserver {
- StreamSubscription? _streamSubscription;
- List<MessageItem> messageList = [];
- // String othersAvatarUrl = "";
- GlobalKey SCROLLVIEW = new GlobalKey();
- double? emptyHeight;
- bool _resizeToAvoidBottomInset = true;
- late MessageModel messageModel;
- late ScrollController _scrollController;
- late TextEditingController _controller;
- late FocusNode _focusNode = new FocusNode();
- final ValueNotifier<bool> _postable = ValueNotifier(false);
- double keyBoardHeight = 0.0; // 初始化下面menu的高度 后续会动态调整后 优化
- double keyBoardHeightCache = 270.0; // 初始化下面menu的高度 后续会动态调整后 优化
- int funIndex = 0; // 初始化下面menu的高度 后续会动态调整后 优化
- List<dynamic> emojiData = [];
- @override
- dispose() {
- super.dispose();
- _streamSubscription?.cancel();
- }
- @override
- initState() {
- super.initState();
- _scrollController = new ScrollController();
- _controller = TextEditingController()
- ..addListener(() {
- _postable.value = _controller.value.text.isNotEmpty == true;
- });
- messageModel = GetIt.I<MessageModel>();
- WidgetsBinding.instance?.addObserver(this); // 监听一手自己...
- initMessageList();
- initListen(); // 开启监听... 先试试...
- addPost(); // 这里是分享过来的 ...
- // initScrollBottom();
- // print("${Provider.of<UserModel>(context, listen: false).user.toJson()}");
- // _menuController.scroll();
- if (widget.fetch == true) {
- SchedulerBinding.instance?.addPostFrameCallback((timeStamp) async {
- messageModel.loopMessage(context);
- });
- }
- initEmoji();
- }
- void initEmoji() async {
- String list = await DefaultAssetBundle.of(context).loadString("lib/assets/json/emoji_list.json");
- setState(() {
- List<dynamic> data = json.decode(list);
- emojiData.addAll(data);
- });
- }
- // 这个是初始化 聊天列表的...
- initMessageList() async {
- List<MessageItem> data = [];
- int me = Provider.of<UserModel>(context, listen: false).user.id;
- var chat = await MessageDB().findHasUserId(me, widget.user.id!);
- if (chat.isEmpty == true) return data;
- int chatId = chat[0]['id'];
- var list = await MessageDB().getMessageForUserId(chatId);
- // DateTime now = DateTime.now();
- int millisecondsSinceEpoch = 0;
- MessageItem? _item;
- for (var item in list) {
- _item = MessageItem.fromJson(item);
- data.add(_item);
- if (millisecondsSinceEpoch == 0 || millisecondsSinceEpoch - (_item.dateTime?.millisecondsSinceEpoch ?? 0) > 2 * 60 * 1000) {
- millisecondsSinceEpoch = (_item.dateTime?.millisecondsSinceEpoch ?? 0);
- data.add(new MessageItem(type: "time", dateTime: _item.dateTime));
- }
- }
- if (data.isNotEmpty == true && data.last.type != "time") {
- if (_item != null) data.add(new MessageItem(type: "time", dateTime: _item.dateTime));
- }
- // 读过就操作一手...
- MessageDB().updateStatus(chatId);
- // 这里每次都会 拿最新的 而不是添加...
- setState(() {
- messageList = data;
- });
- _scrollController.jumpTo(0);
- if(messageList.isNotEmpty){
- if(messageList.first.self == true){
- _focusNode.requestFocus();
- }
- }
- }
- Future add(MessageInstance? message) async {
- if (message == null) return;
- GetIt.I<MessageModel>().add(context, [message]);
- }
- Future addPost() async {
- // 这里是拿到了 帖子
- MessageInstance? _instance;
- if (widget.post != null) {
- // 拿到后还得 存
- _instance = (await api.shareForwardSubject(int.parse(widget.post!.id!), widget.user.id!)).data;
- }
- // 也可能是拿到了 link
- else if (widget.hash != null) {
- _instance = (await api.getshareForwardSport(widget.hash!, widget.user.id!)).data;
- } else if (widget.image != null) {
- _instance = (await api.postChatSend("${widget.user.id!}", "image", '{"url":"${widget.image}"}')).data;
- }
- if (_instance != null) {
- print("[_instance]${_instance.toJson()}---------------------------------------");
- messageModel.add(context, [_instance]);
- }
- }
- // 在本页中如果收到了就 ...
- initListen() {
- Stream<int> queryStream = messageModel.queryStream;
- _streamSubscription = queryStream.listen((count) async {
- print("new message");
- initMessageList();
- });
- }
- // 封装的聊天msg
- // @who 判断是用户自己的还是 聊的那个人
- // @msg 信息 聊天的那个
- // @type 聊天的类型 可能是通过分享过来的那种 ? 游戏 或者是 别的 社区 或者是 链接 ?
- Widget _buildChatItem(MessageItem item) {
- // type 是时间 还是 消息...
- // print("${item.type}----------------------------------");
- if (item.type == "time") {
- return Padding(
- padding: EdgeInsets.symmetric(vertical: 5.0),
- child: Center(
- child: Text(
- "${DateFormat.format(item.dateTime, level: 1)}",
- style: TextStyle(fontSize: 12.0),
- ),
- ),
- );
- }
- GlobalKey anchorKey = GlobalKey();
- MessageData? data = item.data;
- if (data == null) return Container();
- int who = item.self == true ? 1 : 0;
- UserModel userModel = Provider.of<UserModel>(context, listen: false);
- String userAvatar = (who == 1 ? userModel.user.avatar : widget.user.avatar)!;
- String userName = (who == 1 ? userModel.user.name : widget.user.name)!;
- int userId = (who == 1 ? userModel.user.id : widget.user.id)!;
- double widthScale = item.type == "game-invite"
- ? 0.65
- : item.type == "image"
- ? 0.4
- : 0.6;
- double maxWidth = MediaQuery.of(context).size.width * widthScale;
- Widget chatItemOfType(String type) {
- if (type == "text" || type == "image") {
- return _TypeImage(
- data: data,
- );
- }
- // 论坛消息 ...
- if (type == "forum-forward") {
- return InkWell(
- child: _TypeForumForward(
- data: data,
- ),
- onTap: () async {
- Post? post = (await api.getPostDetail("${data.subject!.id}")).data;
- if (post != null) NavigatorUtil.goPage(context, (context) => PostDetailPage(post, false, null));
- },
- );
- }
- // 链接..
- if (type == "share") {
- return InkWell(
- child: Row(crossAxisAlignment: CrossAxisAlignment.start, children: <Widget>[
- CachedNetworkImage(
- imageUrl: userAvatar,
- width: 60.0,
- height: 60.0,
- ),
- Space(
- width: 5.0,
- ),
- Expanded(
- child: RichText(
- text: TextSpan(children: [
- TextSpan(text: userName, style: Theme.of(context).textTheme.headline6!.copyWith(color: Color(0xFFFFC400))),
- TextSpan(text: "分享了他的运动记录,快来围观吧~", style: Theme.of(context).textTheme.subtitle1!),
- ]),
- ),
- )
- ]),
- onTap: () async {
- await NavigatorUtil.goPage(
- context,
- (context) => WebViewSharePage(
- data.url!,
- hash: data.share?.hash,
- ));
- },
- );
- }
- if (type == 'game-invite')
- return GestureDetector(
- onTap: () {
- startGame(context, data.game, invite: data.inviteInfo!, user: widget.user);
- },
- child: ClipRRect(
- borderRadius: BorderRadius.circular(10.0),
- child: Container(
- width: double.infinity,
- color: Colors.white,
- child: _GameInviteWidget(
- maxWidth: maxWidth,
- message: item,
- user: widget.user,
- me: item.self == true,
- ),
- ),
- ),
- );
- return Container();
- }
- Widget chatContent() {
- return ConstrainedBox(
- constraints: BoxConstraints(maxWidth: maxWidth),
- child: Container(
- // padding: who == 1 ? EdgeInsets.fromLTRB(12, 6, 20, 8) : EdgeInsets.fromLTRB(20, 6, 12, 8),
- key: anchorKey,
- child: GestureDetector(
- behavior: HitTestBehavior.opaque,
- onLongPressStart: (e) {
- RenderObject? renderBox = anchorKey.currentContext?.findRenderObject();
- if (renderBox is RenderBox) {
- var offset = renderBox.localToGlobal(Offset(0.0, renderBox.size.height));
- final RelativeRect position = RelativeRect.fromLTRB(
- e.globalPosition.dx, //取点击位置坐弹出x坐标
- offset.dy, //取text高度做弹出y坐标(这样弹出就不会遮挡文本)
- e.globalPosition.dx,
- offset.dy);
- PopupMenuEntry menuItem({String? imgUrl, String? text, Function? callBack}) => menu.PopupMenuItem(
- child: InkWell(
- onTap: () {
- callBack?.call();
- Navigator.pop(context);
- },
- child: Row(
- mainAxisSize: MainAxisSize.min,
- children: <Widget>[
- Image.asset(
- "lib/assets/img/$imgUrl",
- width: 24,
- ),
- SizedBox(
- width: 4,
- ),
- Text(
- text ?? "",
- )
- ],
- ),
- ),
- );
- showMenu(
- context: context,
- position: position,
- items: <PopupMenuEntry>[
- PopupMenuItem(
- child: Container(
- child: Column(
- children: <Widget>[
- menuItem(
- imgUrl: "linkpop_icon_copy.png",
- text: "复制",
- callBack: () {
- Clipboard.setData(ClipboardData(text: '${data.text}'));
- ToastUtil.show("复制成功");
- }),
- who == 1
- ? menuItem(
- imgUrl: "linkpop_icon_del.png",
- text: "删除",
- callBack: () async {
- await MessageDB().deleteMessageIdMessage(item.id!);
- messageList.remove(item);
- setState(() {
- messageList = messageList;
- });
- },
- )
- : menuItem(
- imgUrl: "linkpop_icon_modify_1.png",
- text: "举报",
- callBack: () async {
- await api.postForumReport(userId: widget.user.id, content: "该用户涉嫌发送不良消息内容为:${data.text}");
- ToastUtil.show("举报已受理...");
- }),
- menuItem(imgUrl: "linkpop_icon_cancel.png", text: "取消")
- ],
- ),
- ))
- ],
- );
- }
- },
- child: chatItemOfType(item.type ?? ""))),
- );
- }
- Widget customPoint() {
- if (item.type != "image" && item.type != "game-invite") {
- return CustomPaint(
- painter: who == 1 ? BubblePainterRight() : BubblePainter(),
- child: Padding(
- padding: who == 1 ? EdgeInsets.fromLTRB(12, 6, 20, 8) : EdgeInsets.fromLTRB(20, 6, 12, 8),
- child: chatContent(),
- ));
- }
- return chatContent();
- }
- Widget spaceItem = Space(
- width: 12,
- );
- Widget avatar = InkWell(
- onTap: () async {
- await NavigatorUtil.goTransparentPage(context, (context, _, __) => UserDetailPage(PostUser(id: "$userId", name: userName, avatar: userAvatar)));
- },
- child: CircleAvatar(
- backgroundColor: Colors.black26,
- backgroundImage: CachedNetworkImageProvider(userAvatar),
- radius: 20,
- ),
- );
- List<Widget> chatContentUsr = [customPoint(), spaceItem, avatar];
- List<Widget> chatContentOther = [avatar, spaceItem, customPoint()];
- return Padding(
- padding: const EdgeInsets.symmetric(vertical: 8.0, horizontal: 12.0),
- child: Row(
- mainAxisAlignment: who == 1 ? MainAxisAlignment.end : MainAxisAlignment.start,
- crossAxisAlignment: CrossAxisAlignment.start,
- children: who == 1 ? chatContentUsr : chatContentOther,
- ));
- }
- @override
- void didUpdateWidget(ChatPage oldWidget) {
- super.didUpdateWidget(oldWidget);
- }
- @override
- Widget build(BuildContext context) {
- return KeyboardVisibilityBuilder(builder: (context, isKeyboardVisible) {
- var bottom = MediaQuery.of(context).viewInsets.bottom;
- if (bottom != 0) keyBoardHeightCache = bottom;
- return Scaffold(
- appBar: AppBar(
- title: InkWell(
- child: Text(
- "${widget.user.name}",
- style: titleStyle,
- ),
- onTap: () {
- NavigatorUtil.goTransparentPage(context, (context, _, __) => UserDetailPage(PostUser.fromJson({"id": "${widget.user.id}"})));
- },
- ),
- leading: buildBackButton(context),
- ),
- // resizeToAvoidBottomInset: false, // 透传MediaQuery 的高度?
- body: WillPopScope(
- onWillPop: () async {
- // 这是分享.... 只能一步一步pop 出去
- if (widget.post != null || widget.hash != null || widget.image != null) {
- Navigator.pop(context, true);
- return false;
- }
- return true;
- },
- child: GestureDetector(
- behavior: HitTestBehavior.translucent,
- onTap: () async {
- await SystemChannels.textInput.invokeMethod('TextInput.hide');
- // final currentFocus = FocusScope.of(context);
- // if (!currentFocus.hasPrimaryFocus && currentFocus.hasFocus) {
- // FocusManager.instance.primaryFocus?.unfocus();
- // }
- setState(() {
- keyBoardHeight = 0;
- });
- },
- child: Column(
- children: [
- Expanded(
- child: Column(
- children: [
- Flexible(
- child: CustomScrollView(
- key: SCROLLVIEW,
- shrinkWrap: true,
- reverse: true,
- controller: _scrollController,
- // controller: scrollMenuController,
- slivers: <Widget>[
- SliverToBoxAdapter(
- child: Container(
- height: 20.0,
- ),
- ),
- if (messageList.length == 0)
- SliverToBoxAdapter(
- child: Container(),
- ),
- if (messageList.length != 0)
- SliverList(
- delegate: SliverChildBuilderDelegate((content, index) {
- // MessageInstance data = messageList[index];
- return _buildChatItem(messageList[index]);
- }, childCount: messageList.length),
- ),
- SliverToBoxAdapter(
- child: Container(
- height: 20.0,
- ),
- ),
- ],
- ),
- ),
- ],
- ),
- ),
- Container(
- padding: const EdgeInsets.symmetric(vertical: 8.0, horizontal: 12.0),
- decoration: shadowTop(),
- child: Row(
- children: <Widget>[
- GestureDetector(
- onTap: () async {
- setState(() {
- keyBoardHeight = max(bottom, keyBoardHeightCache);
- funIndex = 1;
- });
- await SystemChannels.textInput.invokeMethod('TextInput.hide');
- },
- child: Padding(
- // padding: const EdgeInsets.symmetric(vertical: 8.0, horizontal: 12.0),
- child: Image.asset("lib/assets/img/bbs_icon_addmore.png"),
- padding: EdgeInsets.only(right: 12.0),
- ),
- ),
- GestureDetector(
- onTap: () async {
- setState(() {
- keyBoardHeight = max(bottom, keyBoardHeightCache);
- funIndex = 2;
- });
- await SystemChannels.textInput.invokeMethod('TextInput.hide');
- },
- child: Padding(
- // padding: const EdgeInsets.symmetric(vertical: 8.0, horizontal: 12.0),
- child: Image.asset("lib/assets/img/bbs_icon_expression.png"),
- padding: EdgeInsets.only(right: 12.0),
- ),
- ),
- Expanded(
- child: CupertinoTextField(
- cursorColor: const Color(0xffFFC400),
- controller: _controller,
- focusNode: _focusNode,
- keyboardType: TextInputType.multiline,
- style: TextStyle(
- fontSize: 16.0,
- ),
- strutStyle: StrutStyle(forceStrutHeight: true, height: 1.4),
- minLines: 1,
- maxLines: 3,
- // maxLength: 200,
- decoration: BoxDecoration(shape: BoxShape.rectangle, borderRadius: BorderRadius.all(Radius.circular(10)), color: Color(0xfff1f1f1)),
- ),
- ),
- Space(
- width: 5.0,
- ),
- ValueListenableBuilder(
- valueListenable: _postable,
- builder: (_, able, __) => PrimaryButton(
- width: 75,
- height: 35.0,
- content: "发送",
- callback: () async {
- if (_controller.text.isEmpty == true) {
- // ToastUtil.show("请输入正确的内容");
- return;
- }
- var content = _controller.text;
- _controller.clear();
- MessageInstance? message = (await api.postChatSend("${widget.user.id!}", "text", '{"text":"$content"}')).data;
- if (message != null) await add(message); // await 是等待的标志 我等待完 在做后面的init 的事?
- },
- shadow: able == true,
- // buttonColor: able == false ? Color(0xffd2d2d2) : null,
- buttonColor: able == false ? Color(0xffFFC400).withOpacity(0.3) : null,
- )),
- ],
- )),
- Container(
- height: isKeyboardVisible ? 0 : keyBoardHeight,color: Colors.white,
- child: IndexedStack(
- index: funIndex,
- children: [
- Container(),
- Container(
- padding: EdgeInsets.only(left: 24.0, right: 24.0),
- decoration: BoxDecoration(border: Border(top: BorderSide(width: 1.0, color: Color(0xFFDCDCDC)))),
- child: GridView(
- shrinkWrap: true,
- padding: EdgeInsets.only(top: 24.0),
- physics: NeverScrollableScrollPhysics(),
- gridDelegate: SliverGridDelegateWithFixedCrossAxisCount(
- crossAxisCount: 4, //横轴三个子widget
- ),
- children: <Widget>[
- InkWell(
- child: Column(
- crossAxisAlignment: CrossAxisAlignment.center,
- children: <Widget>[
- Container(
- padding: EdgeInsets.fromLTRB(14.0, 16.0, 14.0, 14.0),
- child: Image.asset("lib/assets/img/bbs_icon_picture.png"),
- decoration: BoxDecoration(
- borderRadius: BorderRadius.circular(10.0),
- color: Color(0xfff1f1f1),
- ),
- ),
- SizedBox(
- height: 5.0,
- ),
- Text("图片")
- ],
- ),
- onTap: () async {
- try {
- var resultList = await ImagesPicker.pick(quality: 0.8, maxSize: 1024, count: 9);
- if (resultList != null) {
- List<File> files = [];
- for (var i = 0; i < resultList.length; i++) {
- Media asset = resultList[i];
- File file = File(asset.path);
- files.add(file);
- }
- _uploadImage(files);
- }
- } on Exception catch (e) {}
- },
- ),
- InkWell(
- child: Column(
- crossAxisAlignment: CrossAxisAlignment.center,
- children: <Widget>[
- Container(
- padding: EdgeInsets.fromLTRB(14.0, 16.0, 14.0, 14.0),
- child: Image.asset("lib/assets/img/bbs_icon_photo.png"),
- decoration: BoxDecoration(
- borderRadius: BorderRadius.circular(10.0),
- color: Color(0xfff1f1f1),
- ),
- ),
- SizedBox(
- height: 5.0,
- ),
- Text("拍照")
- ],
- ),
- onTap: () async {
- try {
- // 拍完直接发...
- final pickedFile = await ImagesPicker.openCamera(
- pickType: PickType.image,
- );
- if (pickedFile == null || pickedFile.isEmpty) return;
- _uploadImage([File(pickedFile[0].path)]);
- } on Exception catch (e) {}
- },
- ),
- ])),
- Container(
- padding: EdgeInsets.only(left: 5, top: 5, right: 5, bottom: 5),
- decoration: BoxDecoration(border: Border(top: BorderSide(width: 1.0, color: Color(0xFFDCDCDC)))),
- child: GridView.custom(
- padding: EdgeInsets.all(3),
- shrinkWrap: true,
- gridDelegate: SliverGridDelegateWithFixedCrossAxisCount(
- crossAxisCount: 6,
- mainAxisSpacing: 0.5,
- crossAxisSpacing: 6.0,
- ),
- childrenDelegate: SliverChildBuilderDelegate(
- (context, index) {
- return GestureDetector(
- onTap: () {
- String intPutString = _controller.text + String.fromCharCode(emojiData[index]["unicode"]);
- var content = intPutString;
- _controller.value = TextEditingValue(
- // 设置内容
- text: content,
- // 保持光标在最后
- selection: TextSelection.fromPosition(TextPosition(affinity: TextAffinity.downstream, offset: content.length)));
- // 主要是 onchange 没有办法 加上 表情 ...
- setState(() {});
- },
- child: Center(
- child: Text(
- String.fromCharCode(emojiData[index]["unicode"]),
- style: TextStyle(fontSize: 33),
- ),
- ),
- );
- },
- childCount: emojiData.length,
- ),
- ),
- )
- ],
- ),
- ),
- ],
- ),
- )),
- resizeToAvoidBottomInset: isKeyboardVisible,
- );
- });
- }
- _uploadImage(List<File> files) async {
- await request(context, () async {
- for (var file in files) {
- String path = file.path;
- var data = (await api.postChatUpload(file)).data;
- Image image = Image.file(File.fromUri(Uri.parse(path)));
- image.image.resolve(ImageConfiguration()).addListener(ImageStreamListener((ImageInfo image, bool synchronousCall) {
- // print("$data----------------------${image.image.width} - ${image.image.height}------");
- api.postChatSend("${widget.user.id!}", "image", '{ "url":"${data['url']}", "w":${image.image.width}, "h":${image.image.height} }').then((value) => add(value.data));
- file.delete();
- }));
- }
- });
- }
- }
- class _GameInviteWidget extends StatefulWidget {
- final double maxWidth;
- final MessageItem message;
- final UserInfo user;
- final bool me;
- const _GameInviteWidget({Key? key, required this.maxWidth, required this.message, required this.user, required this.me}) : super(key: key);
- @override
- State<StatefulWidget> createState() {
- return _GameInviteWidgetState();
- }
- }
- class _GameInviteWidgetState extends State<_GameInviteWidget> {
- @override
- Widget build(BuildContext context) {
- double maxWidth = widget.maxWidth;
- MessageItem item = widget.message;
- MessageData? data = item.data;
- GameInfoData? game = data?.game;
- return Column(
- children: [
- Container(
- height: maxWidth * 0.35,
- decoration: BoxDecoration(
- image: DecorationImage(
- image: CachedNetworkImageProvider(game?.coverHorizontal ?? ""),
- fit: BoxFit.cover,
- ),
- ),
- child: Container(
- width: double.infinity,
- // margin: EdgeInsets.symmetric(vertical: 8.0, horizontal: 12.0),
- // alignment: Alignment.bottomCenter,
- decoration: const BoxDecoration(
- gradient: const LinearGradient(begin: Alignment.topCenter, end: Alignment.bottomCenter, colors: [
- const Color(0x00000000),
- // Color(0x00000000),
- const Color(0xAA000000)
- ]),
- ),
- child: Center(
- child: Column(
- mainAxisAlignment: MainAxisAlignment.center,
- children: [
- Image.asset(
- widget.message.self == true
- ? "lib/assets/img/mine_image_pk.png"
- : data?.status == "wait"
- ? "lib/assets/img/mine_image_pk.png"
- : data?.status == "yes"
- ? "lib/assets/img/join_icon_pass.png"
- : "lib/assets/img/join_icon_no.png",
- height: 40.0,
- ),
- SizedBox(
- height: 8,
- ),
- Text(
- widget.message.self == true ? "你发起在${game?.name}的邀请" : "你的好友在${game?.name}向你发起了邀请",
- style: Theme.of(context).textTheme.subtitle2!.copyWith(color: Colors.white),
- ),
- ],
- ),
- )),
- ),
- if (widget.me != true)
- Padding(
- padding: const EdgeInsets.all(16.0),
- child: data?.status == "wait"
- ? Row(
- mainAxisAlignment: MainAxisAlignment.spaceBetween,
- children: <Widget>[
- Expanded(
- child: CancelButton(
- height: 35,
- callback: () async {
- data?.status = "no";
- await MessageDB().updateData(item);
- setState(() {});
- },
- content: "拒绝"),
- ),
- SizedBox(
- width: 16,
- ),
- Expanded(
- child: PrimaryButton(
- height: 35,
- callback: () async {
- data?.status = "yes";
- await MessageDB().updateData(item);
- setState(() {});
- GameModel gameModel = GetIt.I<GameModel>();
- GameInfoData? e = await gameModel.getGame(game?.id ?? 0);
- startGame(context, e, invite: data?.inviteInfo, user: widget.user);
- },
- content: "同意"))
- ],
- )
- : data?.status == "yes"
- ? Center(
- child: Text(
- "已同意",
- style: TextStyle(fontSize: 14.0, color: Color(0xff00DC42)),
- ))
- : Center(
- child: Text(
- "已拒绝",
- style: TextStyle(fontSize: 14.0, color: Color(0xffFF5B1D)),
- )),
- ),
- ],
- );
- }
- }
- class _TypeImage extends StatelessWidget {
- final MessageData data;
- const _TypeImage({Key? key, required this.data}) : super(key: key);
- @override
- Widget build(BuildContext context) {
- String? url = data.url;
- int w = data.w != 0 ? data.w! : (MediaQuery.of(context).size.width ~/ 4);
- int h = data.h != 0 ? data.h! : (MediaQuery.of(context).size.width ~/ 4);
- double ratio = w / h;
- // print("1111111 ${data.toJson()} w: $w h: $h $ratio -- ${data.w }:${data.h}");
- return Column(
- children: <Widget>[
- if (data.text != null)
- Text(
- "${data.text}",
- style: Theme.of(context).textTheme.subtitle1!.copyWith(fontSize: 16, color: Colors.black),
- ),
- if (url != null)
- InkWell(
- child: AspectRatio(
- aspectRatio: ratio,
- child: CachedNetworkImage(
- imageUrl: "${data.url}?x-oss-process=image/resize,p_50",
- fit: BoxFit.cover,
- ),
- ),
- onTap: () {
- Navigator.push(
- context,
- FadeRoute(
- page: GalleryPhotoViewWrapper(
- galleryItems: [photo.Image(id: "1", src: data.url)],
- backgroundDecoration: const BoxDecoration(
- color: Colors.black,
- ),
- scrollDirection: Axis.horizontal,
- loadingBuilder: (_, __) => RequestLoadingWidget(),
- ),
- ),
- );
- },
- )
- ],
- crossAxisAlignment: CrossAxisAlignment.start,
- );
- }
- }
- class _TypeForumForward extends StatelessWidget {
- final MessageData data;
- const _TypeForumForward({Key? key, required this.data}) : super(key: key);
- @override
- Widget build(BuildContext context) {
- return Row(
- crossAxisAlignment: CrossAxisAlignment.start,
- children: <Widget>[
- Image.asset(
- "lib/assets/img/chat.png",
- width: 14.0,
- height: 14.0,
- fit: BoxFit.cover,
- ),
- Space(
- width: 5.0,
- ),
- Expanded(
- child: RichText(
- maxLines: 4,
- overflow: TextOverflow.ellipsis,
- text: TextSpan(children: [
- // TextSpan(text: "@${data.user?.name}:", style: Theme.of(context).textTheme.headline6!.copyWith(color: Color(0xFFFFC400))),
- TextSpan(text: "分享了一篇帖子", style: Theme.of(context).textTheme.subtitle1),
- if(data.subject?.content?.isNotEmpty == true)
- TextSpan(text: "\n\n${data.subject?.content}", style: Theme.of(context).textTheme.subtitle1, ),
- ]),
- ),
- )
- ],
- );
- }
- }
|