|
@@ -1,3 +1,4 @@
|
|
|
+import 'dart:async';
|
|
|
import 'dart:convert';
|
|
|
import 'dart:math';
|
|
|
|
|
@@ -8,16 +9,27 @@ import 'package:flutter/services.dart';
|
|
|
import 'package:shared_preferences/shared_preferences.dart';
|
|
|
import 'package:sport/bean/message.dart';
|
|
|
import 'package:sport/bean/post.dart';
|
|
|
+import 'package:sport/bean/post_user.dart';
|
|
|
import 'package:sport/db/message_db.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/lib/view_state_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/api/resp.dart';
|
|
|
+import 'package:sport/services/userid.dart';
|
|
|
+import 'package:sport/utils/print_map.dart';
|
|
|
import 'package:sport/utils/toast.dart';
|
|
|
import 'package:sport/widgets/appbar.dart';
|
|
|
import 'package:sport/widgets/decoration.dart';
|
|
|
+import 'package:sport/widgets/loading.dart';
|
|
|
import 'package:sport/widgets/menu_bar.dart';
|
|
|
import 'package:sport/widgets/space.dart';
|
|
|
import 'package:sport/widgets/dialog/popupmenu.dart' as menu;
|
|
|
+import 'package:provider/provider.dart';
|
|
|
|
|
|
final List<String> avatarList = [
|
|
|
"https://wx3.sinaimg.cn/mw1024/a6bdcd78gy1gfna8yznv5j20m90m9tbz.jpg",
|
|
@@ -33,23 +45,6 @@ final List<String> avatarList = [
|
|
|
"https://wx2.sinaimg.cn/mw1024/a6bdcd78gy1gfnaaubdhhj20m90m9786.jpg",
|
|
|
];
|
|
|
|
|
|
-// 不封装的话 就直接这样...
|
|
|
-//class ChatPageModel extends ViewStateModel with InjectApi {
|
|
|
-//// final Future _future;
|
|
|
-//// ChatPageModel(this._future);
|
|
|
-//
|
|
|
-// // 获取聊天信息
|
|
|
-// Future getUserChat(int userId) async {
|
|
|
-// ChatMessage message;
|
|
|
-// try {
|
|
|
-// message = (await api.getChatUser(userId)).data;
|
|
|
-// } catch (e) {
|
|
|
-// print(e);
|
|
|
-// }
|
|
|
-// return message;
|
|
|
-// }
|
|
|
-//}
|
|
|
-
|
|
|
class ChatPage extends StatefulWidget {
|
|
|
final String userName;
|
|
|
final int userId;
|
|
@@ -61,23 +56,401 @@ class ChatPage extends StatefulWidget {
|
|
|
State<StatefulWidget> createState() => _ChatPageState();
|
|
|
}
|
|
|
|
|
|
-class _ChatPageState extends State<ChatPage> with InjectLoginApi, InjectApi {
|
|
|
+// 服务端的数据 只有 拿了 和 没拿 ,客户端 就是 读了 和没读 ...
|
|
|
+class _ChatPageState extends State<ChatPage>
|
|
|
+ with InjectLoginApi, InjectApi, UserId, WidgetsBindingObserver {
|
|
|
GetMenuController _menuController = new GetMenuController();
|
|
|
-// ChatPageModel _chatPageModel = new ChatPageModel();
|
|
|
- List<MessageInstance> _messageList = []; // 初始化 为空...
|
|
|
+
|
|
|
+ StreamSubscription _streamSubscription;
|
|
|
+ List<MessageItem> messageList = [];
|
|
|
+
|
|
|
+ GlobalKey SCROLLVIEW = new GlobalKey();
|
|
|
+
|
|
|
+ dispose() {
|
|
|
+ super.dispose();
|
|
|
+ _streamSubscription?.cancel();
|
|
|
+ }
|
|
|
|
|
|
initState() {
|
|
|
super.initState();
|
|
|
+ WidgetsBinding.instance.addObserver(this); // 监听一手自己...
|
|
|
initMessageList();
|
|
|
+ initListen(); // 开启监听... 先试试...
|
|
|
+ addPost(); // 这里是分享过来的 ...
|
|
|
+// initScrollBottom();
|
|
|
+// print("${Provider.of<UserModel>(context, listen: false).user.toJson()}");
|
|
|
}
|
|
|
|
|
|
- void initMessageList() async {
|
|
|
- var list = await MessageDB().findLatest();
|
|
|
- // 这不是是个骚的?
|
|
|
+ // 这个是初始化 聊天列表的...
|
|
|
+ initMessageList() async {
|
|
|
+// List<MessageItem> messageList = [];
|
|
|
+ List<MessageItem> data = [];
|
|
|
+ var list = await MessageDB().getMessageForUserId(widget.userId);
|
|
|
for (var item in list) {
|
|
|
- _messageList.add(json.decode(item["message"]));
|
|
|
+ MessageItem _item = MessageItem.fromJson(item);
|
|
|
+ data.add(_item);
|
|
|
+ }
|
|
|
+ // 读过就操作一手...
|
|
|
+ await MessageDB().updateStatus(widget.userId);
|
|
|
+
|
|
|
+// messageList.addAll(data);
|
|
|
+ data.forEach((element) {
|
|
|
+ messageList.insert(0, element);
|
|
|
+ });
|
|
|
+
|
|
|
+ // 这里每次都会 拿最新的 而不是添加...
|
|
|
+ setState(() {
|
|
|
+ messageList = messageList;
|
|
|
+ });
|
|
|
+ }
|
|
|
+
|
|
|
+ Future addPost() async {
|
|
|
+ if (widget.post != null) {
|
|
|
+ // 拿到后还得 存
|
|
|
+ MessageInstance _instance = (await api.shareForwardSubject(
|
|
|
+ int.parse(widget.post.id), widget.userId))
|
|
|
+ .data;
|
|
|
+
|
|
|
+ await MessageDB().insert(new MessageItem(
|
|
|
+ message: _instance, status: 0, curId: 0, userId: widget.userId));
|
|
|
+
|
|
|
+ addMessageToPage(_instance);
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ // 在本页中如果收到了就 ...
|
|
|
+ initListen() {
|
|
|
+ Stream<List<MessageInstance>> queryStream =
|
|
|
+ Provider.of<MessageModel>(context, listen: false).queryStream;
|
|
|
+ _streamSubscription =
|
|
|
+ queryStream.listen((List<MessageInstance> list) async {
|
|
|
+ for (MessageInstance item in list) {
|
|
|
+ addMessageToPage(item);
|
|
|
+ }
|
|
|
+ await MessageDB().updateStatus(widget.userId);
|
|
|
+ });
|
|
|
+ }
|
|
|
+//
|
|
|
+// initScrollBottom() {
|
|
|
+// _timer = Timer(Duration.zero, () {
|
|
|
+// _menuController.scrollToBottom(context, SCROLLVIEW, true);
|
|
|
+// });
|
|
|
+// }
|
|
|
+
|
|
|
+ MessageItem instanceToItem(MessageInstance _instance) {
|
|
|
+ int id = _instance.fromUser.id == int.parse(selfId)
|
|
|
+ ? _instance.toUser.id
|
|
|
+ : _instance.fromUser.id;
|
|
|
+
|
|
|
+ // 里面收到 直接已读... 好像curId 可以不加 status 也可以不写 ...
|
|
|
+ MessageItem item =
|
|
|
+ new MessageItem(message: _instance, curId: 0, status: 0, userId: id);
|
|
|
+ return item;
|
|
|
+ }
|
|
|
+
|
|
|
+ // 各种来自别的地方收到的消息 输出到 页面上
|
|
|
+ addMessageToPage(MessageInstance _instance) {
|
|
|
+ MessageItem item = instanceToItem(_instance);
|
|
|
+ messageList.insert(0, item);
|
|
|
+// messageList.add(item);
|
|
|
+// messageList.insert(0, item); // 到头插...
|
|
|
+ setState(() {
|
|
|
+ messageList = messageList;
|
|
|
+ });
|
|
|
+ }
|
|
|
+
|
|
|
+// @override
|
|
|
+// void didChangeMetrics() {
|
|
|
+// super.didChangeMetrics();
|
|
|
+// WidgetsBinding.instance.addPostFrameCallback((_) {
|
|
|
+// // 当前是安卓系统并且在焦点聚焦的情况下
|
|
|
+//// _menuController.scrollToBottom();
|
|
|
+// });
|
|
|
+// }
|
|
|
+
|
|
|
+// 封装的聊天msg
|
|
|
+// @who 判断是用户自己的还是 聊的那个人
|
|
|
+// @msg 信息 聊天的那个
|
|
|
+// @type 聊天的类型 可能是通过分享过来的那种 ? 游戏 或者是 别的 社区 或者是 链接 ?
|
|
|
+
|
|
|
+ Widget _buildChatItem(MessageItem item) {
|
|
|
+ GlobalKey anchorKey = GlobalKey();
|
|
|
+
|
|
|
+ MessageInstance data = item.message;
|
|
|
+
|
|
|
+ int who = data.fromUser.id ==
|
|
|
+ Provider.of<UserModel>(context, listen: false).user.id
|
|
|
+ ? 1
|
|
|
+ : 0;
|
|
|
+
|
|
|
+ String userAvatar = data.fromUser.avatar;
|
|
|
+
|
|
|
+ Widget chatItemOfType(String type) {
|
|
|
+ // 文本或者是图片...
|
|
|
+ if (type == "text" || type == "image") {
|
|
|
+ return Column(
|
|
|
+ children: <Widget>[
|
|
|
+ if (data.data.text != null)
|
|
|
+ Text(
|
|
|
+ "${data.data.text}",
|
|
|
+ style: Theme.of(context)
|
|
|
+ .textTheme
|
|
|
+ .subtitle1
|
|
|
+ .copyWith(fontSize: 16, color: Colors.black),
|
|
|
+ key: anchorKey,
|
|
|
+ ),
|
|
|
+ if (data.data.url != null)
|
|
|
+ CachedNetworkImage(imageUrl: data.data.url)
|
|
|
+// SizedBox(
|
|
|
+// height: 10.0,
|
|
|
+// ),
|
|
|
+// if (imageUrls?.length != null)
|
|
|
+// GridView.count(
|
|
|
+// physics: new NeverScrollableScrollPhysics(),
|
|
|
+// shrinkWrap: true,
|
|
|
+// padding: EdgeInsets.zero,
|
|
|
+// crossAxisSpacing: 10.0,
|
|
|
+// crossAxisCount: imageUrls.length > 3 ? 3 : imageUrls.length,
|
|
|
+// mainAxisSpacing: 10.0,
|
|
|
+// children: imageUrls
|
|
|
+// .asMap()
|
|
|
+// .keys
|
|
|
+// .map((i) => CachedNetworkImage(
|
|
|
+// imageUrl: imageUrls[i],
|
|
|
+// fit: BoxFit.cover,
|
|
|
+// ))
|
|
|
+// .toList())
|
|
|
+ ],
|
|
|
+ crossAxisAlignment: CrossAxisAlignment.start,
|
|
|
+ );
|
|
|
+ }
|
|
|
+ // 论坛消息 ...
|
|
|
+ if (type == "forum-forward") {
|
|
|
+ return InkWell(
|
|
|
+ child: Column(
|
|
|
+ children: <Widget>[
|
|
|
+ RichText(
|
|
|
+ text: TextSpan(children: [
|
|
|
+ TextSpan(
|
|
|
+ text: data.data.user.name,
|
|
|
+ style: Theme.of(context)
|
|
|
+ .textTheme
|
|
|
+ .headline6
|
|
|
+ .copyWith(color: Color(0xFFFFC400))),
|
|
|
+ TextSpan(
|
|
|
+ text: data.data.subject.content,
|
|
|
+ style: Theme.of(context).textTheme.subtitle1),
|
|
|
+ ]),
|
|
|
+ ),
|
|
|
+ Space(
|
|
|
+ height: 5.0,
|
|
|
+ ),
|
|
|
+ if (data.data.subject.images.length > 0)
|
|
|
+ GridView.count(
|
|
|
+ physics: new NeverScrollableScrollPhysics(),
|
|
|
+ shrinkWrap: true,
|
|
|
+ padding: EdgeInsets.zero,
|
|
|
+ crossAxisSpacing: 10.0,
|
|
|
+ crossAxisCount: data.data.subject.images.length > 3
|
|
|
+ ? 3
|
|
|
+ : data.data.subject.images.length,
|
|
|
+ mainAxisSpacing: 10.0,
|
|
|
+ childAspectRatio: 9 / 6,
|
|
|
+ children: data.data.subject.images
|
|
|
+ .asMap()
|
|
|
+ .keys
|
|
|
+ .map(
|
|
|
+ (i) => CachedNetworkImage(
|
|
|
+ alignment: Alignment.centerLeft,
|
|
|
+ imageUrl: data.data.subject.images[i],
|
|
|
+ fit: BoxFit.cover,
|
|
|
+// width: data.data.subject.images[i]
|
|
|
+ ),
|
|
|
+ )
|
|
|
+ .toList()),
|
|
|
+ Divider(),
|
|
|
+ Row(
|
|
|
+ children: <Widget>[
|
|
|
+ Container(
|
|
|
+ width: 20.0,
|
|
|
+ height: 20.0,
|
|
|
+ child: ClipRRect(
|
|
|
+ borderRadius: BorderRadius.circular(6),
|
|
|
+ child: CachedNetworkImage(
|
|
|
+ imageUrl: data.data.forum.cover,
|
|
|
+ ),
|
|
|
+ ),
|
|
|
+ ),
|
|
|
+ Space(
|
|
|
+ width: 5,
|
|
|
+ ),
|
|
|
+ Text(
|
|
|
+ data.data.forum.name,
|
|
|
+ )
|
|
|
+ ],
|
|
|
+ )
|
|
|
+ ],
|
|
|
+ ),
|
|
|
+ onTap: () async {
|
|
|
+ Post post =
|
|
|
+ (await api.getPostDetail("${data.data.subject.id}")).data;
|
|
|
+ print(post);
|
|
|
+ NavigatorUtil.goPage(
|
|
|
+ context, (context) => PostDetailPage(post, false, null));
|
|
|
+ },
|
|
|
+ );
|
|
|
+ }
|
|
|
+ // 链接..
|
|
|
+ if (type == "link") {
|
|
|
+ return InkWell(
|
|
|
+ child: Row(
|
|
|
+ crossAxisAlignment: CrossAxisAlignment.start,
|
|
|
+ children: <Widget>[
|
|
|
+ CachedNetworkImage(
|
|
|
+ imageUrl: data.data.logo,
|
|
|
+ width: 60.0,
|
|
|
+ height: 60.0,
|
|
|
+ ),
|
|
|
+ Space(
|
|
|
+ width: 5.0,
|
|
|
+ ),
|
|
|
+ Expanded(
|
|
|
+ child: RichText(
|
|
|
+ text: TextSpan(children: [
|
|
|
+ TextSpan(
|
|
|
+ text: data.data.user.name,
|
|
|
+ style: Theme.of(context)
|
|
|
+ .textTheme
|
|
|
+ .headline6
|
|
|
+ .copyWith(color: Color(0xFFFFC400))),
|
|
|
+ TextSpan(
|
|
|
+ text: data.data.text,
|
|
|
+ style: Theme.of(context).textTheme.subtitle1),
|
|
|
+ ]),
|
|
|
+ ),
|
|
|
+ )
|
|
|
+ ]),
|
|
|
+ onTap: () async{
|
|
|
+ await NavigatorUtil.goPage(context, (context) => WebViewSharePage(data.data.url));
|
|
|
+ },
|
|
|
+ );
|
|
|
+ }
|
|
|
}
|
|
|
- print("$list---------------------------------------");
|
|
|
+
|
|
|
+ Widget customPoint = CustomPaint(
|
|
|
+ painter: who == 1 ? _BubblePainterRight() : _BubblePainter(),
|
|
|
+ child: ConstrainedBox(
|
|
|
+ constraints:
|
|
|
+ BoxConstraints(maxWidth: MediaQuery.of(context).size.width * 0.6),
|
|
|
+ child: Container(
|
|
|
+ padding: who == 1
|
|
|
+ ? EdgeInsets.fromLTRB(12, 6, 20, 8)
|
|
|
+ : EdgeInsets.fromLTRB(20, 6, 12, 8),
|
|
|
+ child: GestureDetector(
|
|
|
+ onLongPressStart: (e) {
|
|
|
+ RenderBox renderBox =
|
|
|
+ anchorKey.currentContext.findRenderObject();
|
|
|
+ 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();
|
|
|
+ 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.data.text}'));
|
|
|
+ ToastUtil.show("复制成功");
|
|
|
+ }),
|
|
|
+ who == 1
|
|
|
+ ? menuItem(
|
|
|
+ imgUrl: "linkpop_icon_del.png",
|
|
|
+ text: "删除")
|
|
|
+ : menuItem(
|
|
|
+ imgUrl: "linkpop_icon_modify_1.png",
|
|
|
+ text: "举报"),
|
|
|
+ menuItem(
|
|
|
+ imgUrl: "linkpop_icon_cancel.png", text: "取消")
|
|
|
+ ],
|
|
|
+ ),
|
|
|
+ ))
|
|
|
+ ],
|
|
|
+ );
|
|
|
+ },
|
|
|
+ child: chatItemOfType(data.type))),
|
|
|
+ ));
|
|
|
+
|
|
|
+ Widget spaceItem = Space(
|
|
|
+ width: 12,
|
|
|
+ );
|
|
|
+
|
|
|
+ Widget avatar = InkWell(
|
|
|
+ onTap: () async {
|
|
|
+ await NavigatorUtil.goPage(
|
|
|
+ context,
|
|
|
+ (context) => UserDetailPage(PostUser(
|
|
|
+ id: "${data.fromUser?.id}",
|
|
|
+ name: data.fromUser?.name,
|
|
|
+ avatar: data.fromUser?.id == selfId
|
|
|
+ ? Provider.of<UserModel>(context, listen: false).user.avatar
|
|
|
+ : data.fromUser.avatar)));
|
|
|
+ },
|
|
|
+ child: CircleAvatar(
|
|
|
+ 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
|
|
@@ -93,169 +466,33 @@ class _ChatPageState extends State<ChatPage> with InjectLoginApi, InjectApi {
|
|
|
resizeToAvoidBottomInset: false, // 透传MediaQuery 的高度?
|
|
|
body: MenuBar(
|
|
|
CustomScrollView(
|
|
|
+ key: SCROLLVIEW,
|
|
|
reverse: true,
|
|
|
controller: _menuController.scrollMenuController,
|
|
|
slivers: <Widget>[
|
|
|
- SliverList(
|
|
|
- delegate: SliverChildBuilderDelegate((content, index) {
|
|
|
- return Column(
|
|
|
- children: <Widget>[],
|
|
|
- );
|
|
|
- }, childCount: 10),
|
|
|
- )
|
|
|
+ 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),
|
|
|
+ ),
|
|
|
],
|
|
|
),
|
|
|
menuIdentity:
|
|
|
new MenuIdentity(menuScene: "chat", userId: widget.userId),
|
|
|
inputField: "",
|
|
|
- scrollToBottom: _menuController.scrollToBottom,
|
|
|
+// scrollToBottom: _menuController.scrollToBottom, // 暂时可以废弃...
|
|
|
+ sendCallBack: addMessageToPage,
|
|
|
+ globalkey: SCROLLVIEW,
|
|
|
));
|
|
|
}
|
|
|
}
|
|
|
|
|
|
-// 封装的聊天msg
|
|
|
-// @type 判断是用户自己的还是 聊的那个人
|
|
|
-// @msg 信息 聊天的那个
|
|
|
-//
|
|
|
-Widget _buildChatItem(BuildContext context,
|
|
|
- {int type, String msg, int index, List<String> imageUrls}) {
|
|
|
- GlobalKey anchorKey = GlobalKey();
|
|
|
-
|
|
|
- Widget customPoint = CustomPaint(
|
|
|
- painter: type == 1 ? _BubblePainterRight() : _BubblePainter(),
|
|
|
- child: ConstrainedBox(
|
|
|
- constraints:
|
|
|
- BoxConstraints(maxWidth: MediaQuery.of(context).size.width * 0.6),
|
|
|
- child: Container(
|
|
|
- padding: type == 1
|
|
|
- ? EdgeInsets.fromLTRB(12, 6, 20, 8)
|
|
|
- : EdgeInsets.fromLTRB(20, 6, 12, 8),
|
|
|
- child: GestureDetector(
|
|
|
- onLongPressStart: (e) {
|
|
|
- RenderBox renderBox =
|
|
|
- anchorKey.currentContext.findRenderObject();
|
|
|
- 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();
|
|
|
- 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: '$msg'));
|
|
|
- ToastUtil.show("复制成功");
|
|
|
- }),
|
|
|
- type == 1
|
|
|
- ? menuItem(
|
|
|
- imgUrl: "linkpop_icon_del.png", text: "删除")
|
|
|
- : menuItem(
|
|
|
- imgUrl: "linkpop_icon_modify_1.png",
|
|
|
- text: "举报"),
|
|
|
- menuItem(
|
|
|
- imgUrl: "linkpop_icon_cancel.png", text: "取消")
|
|
|
- ],
|
|
|
- ),
|
|
|
- ))
|
|
|
- ],
|
|
|
- );
|
|
|
- },
|
|
|
- child: Column(
|
|
|
- children: <Widget>[
|
|
|
- Text(
|
|
|
- "$msg",
|
|
|
- style: Theme.of(context)
|
|
|
- .textTheme
|
|
|
- .subtitle1
|
|
|
- .copyWith(fontSize: 16, color: Colors.black),
|
|
|
- key: anchorKey,
|
|
|
- ),
|
|
|
- if (imageUrls?.length != null)
|
|
|
- SizedBox(
|
|
|
- height: 10.0,
|
|
|
- ),
|
|
|
- GridView.count(
|
|
|
- physics: new NeverScrollableScrollPhysics(),
|
|
|
- shrinkWrap: true,
|
|
|
- padding: EdgeInsets.zero,
|
|
|
- crossAxisSpacing: 10.0,
|
|
|
- crossAxisCount:
|
|
|
- imageUrls.length > 3 ? 3 : imageUrls.length,
|
|
|
- mainAxisSpacing: 10.0,
|
|
|
- children: imageUrls
|
|
|
- .asMap()
|
|
|
- .keys
|
|
|
- .map((i) => CachedNetworkImage(
|
|
|
- imageUrl: imageUrls[i],
|
|
|
- fit: BoxFit.cover,
|
|
|
- ))
|
|
|
- .toList())
|
|
|
- ],
|
|
|
- crossAxisAlignment: CrossAxisAlignment.start,
|
|
|
- ),
|
|
|
- )),
|
|
|
- ));
|
|
|
-
|
|
|
- Widget spaceItem = Space(
|
|
|
- width: 12,
|
|
|
- );
|
|
|
-
|
|
|
- Widget avatar = CircleAvatar(
|
|
|
- backgroundImage: CachedNetworkImageProvider(avatarList[index % 11]),
|
|
|
- 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:
|
|
|
- type == 1 ? MainAxisAlignment.end : MainAxisAlignment.start,
|
|
|
- crossAxisAlignment: CrossAxisAlignment.start,
|
|
|
- children: type == 1 ? chatContentUsr : chatContentOther,
|
|
|
- ));
|
|
|
-}
|
|
|
-
|
|
|
class _BubblePainter extends CustomPainter {
|
|
|
final circular = Radius.circular(10);
|
|
|
final Paint _paint = Paint()..color = Colors.white;
|