From 3f3f898d2cd57ea1b14af11734b36499e0b6c002 Mon Sep 17 00:00:00 2001 From: htylight Date: Sun, 27 Aug 2023 19:14:37 +0800 Subject: [PATCH] fix group chat bubble bug: flash --- lib/models/contact_model.dart | 15 + lib/models/websocket_model.dart | 36 +- lib/screens/chat/chat_screen.dart | 2 + .../components/group_chat_message_bubble.dart | 314 ++++++++++++------ .../more/setting_screen/setting_screen.dart | 5 + .../components/username_signin.dart | 14 +- 6 files changed, 263 insertions(+), 123 deletions(-) diff --git a/lib/models/contact_model.dart b/lib/models/contact_model.dart index a1be77f..66a2f6e 100644 --- a/lib/models/contact_model.dart +++ b/lib/models/contact_model.dart @@ -105,6 +105,15 @@ class Contact extends ChangeNotifier { groupChatCount--; notifyListeners(); } + + void clean() { + friends = {}; + friendGroups = []; + defaultGroup = ''; + groupChats = {}; + friendCount = 0; + groupChatCount = 0; + } } class FriendAccountProfile { @@ -272,4 +281,10 @@ class ContactAccountProfile extends ChangeNotifier { grouChatMemberProfiles.remove(groupChatId); notifyListeners(); } + + void clean() { + friends = {}; + groupChats = {}; + grouChatMemberProfiles = {}; + } } diff --git a/lib/models/websocket_model.dart b/lib/models/websocket_model.dart index a8c087d..fc536b2 100644 --- a/lib/models/websocket_model.dart +++ b/lib/models/websocket_model.dart @@ -14,11 +14,17 @@ import 'package:together_mobile/models/user_model.dart'; import 'package:web_socket_channel/web_socket_channel.dart'; import 'package:web_socket_channel/status.dart' as status; +enum SocketStatus { + connected, + closed, + error, +} + class WebSocketManager extends ChangeNotifier { late Uri wsUrl; late WebSocketChannel channel; String id = ''; - SocketStatus? socketStatus; + SocketStatus socketStatus = SocketStatus.closed; Timer? heartBeatTimer; Timer? serverTimer; Timer? reconnectTimer; @@ -42,6 +48,20 @@ class WebSocketManager extends ChangeNotifier { channel.stream.listen(onData, onError: onError, onDone: onDone); } + void disconnect() { + channel.sink.close(); + wsUrl = Uri(); + id = ''; + socketStatus = SocketStatus.closed; + heartBeatTimer?.cancel(); + serverTimer?.cancel(); + reconnectTimer?.cancel(); + heartBeatTimer = null; + serverTimer = null; + reconnectTimer = null; + reconnectTimes = 0; + } + void onData(jsonData) { heartBeatInspect(); Map data = json.decode(jsonData); @@ -65,6 +85,9 @@ class WebSocketManager extends ChangeNotifier { void onDone() { print('websocket disconnected <$channel>'); + if (socketStatus == SocketStatus.closed) { + return; + } reconnect(); } @@ -132,12 +155,6 @@ class WebSocketManager extends ChangeNotifier { } } -enum SocketStatus { - connected, - closed, - error, -} - void receiveFriendMsg(Map msg) async { print('=================收到了好友信息事件=================='); print(msg); @@ -252,10 +269,10 @@ void receiveGroupChatMsg(Map msg) async { if (chatSetting == null) { chatSettingBox.put( - senderId, + groupChatId, ChatSetting( groupChatId, - 0, + 1, false, true, false, @@ -271,7 +288,6 @@ void receiveGroupChatMsg(Map msg) async { } List attachments = List.from(msg['attachments']); - messageTBox.add( MessageT( senderId, diff --git a/lib/screens/chat/chat_screen.dart b/lib/screens/chat/chat_screen.dart index 072ac32..aecbd87 100755 --- a/lib/screens/chat/chat_screen.dart +++ b/lib/screens/chat/chat_screen.dart @@ -162,6 +162,7 @@ class _ChatScreenState extends State { messageTBox.getAt(length - 1)!; if (openedChat[index].type == 0) { return FriendChatTile( + key: ValueKey(contactId), index: index, contactId: contactId, senderId: messageT.senderId, @@ -174,6 +175,7 @@ class _ChatScreenState extends State { ); } else { return GroupChatChatTile( + key: ValueKey(contactId), index: index, contactId: contactId, senderId: messageT.senderId, diff --git a/lib/screens/message/components/group_chat_message_bubble.dart b/lib/screens/message/components/group_chat_message_bubble.dart index 8ec07be..a207a2b 100755 --- a/lib/screens/message/components/group_chat_message_bubble.dart +++ b/lib/screens/message/components/group_chat_message_bubble.dart @@ -37,12 +37,18 @@ class _GroupChatMessageBubbleState extends State { final List _timerList = []; Future _getMemberGroupChatProfile() async { - if (widget.senderId == getIt.get().id || - getIt + if (widget.senderId == getIt.get().id) { + // myself or already have profile + return Future(() => true); + } + if (getIt .get() .grouChatMemberProfiles + .containsKey(widget.contactId) && + getIt + .get() + .grouChatMemberProfiles[widget.contactId]! .containsKey(widget.senderId)) { - // myself or already have profile return Future(() => true); } else { Map res; @@ -136,50 +142,19 @@ class _GroupChatMessageBubbleState extends State { ? FutureBuilder( future: _getMemberGroupChatProfile(), builder: (context, snapshot) { - if (snapshot.hasData) { - if (isFriend) { - return getIt - .get() - .friends[widget.senderId]! - .avatar - .isEmpty - ? CircleAvatar( - child: - Image.asset('assets/images/user_2.png'), - ) - : CircleAvatar( - backgroundImage: CachedNetworkImageProvider( - '$avatarsUrl/${getIt.get().friends[widget.senderId]!.avatar}', - ), - ); - } else { - return getIt - .get() - .groupChats[widget.senderId]! - .avatar - .isEmpty - ? CircleAvatar( - child: - Image.asset('assets/images/user_2.png'), - ) - : CircleAvatar( - backgroundImage: CachedNetworkImageProvider( - '$avatarsUrl/${getIt.get().groupChats[widget.senderId]!.avatar}', - ), - ); - } - } else { - return CircleAvatar( - child: Image.asset('assets/images/user_2.png'), - ); - } + return _showAvatar(snapshot, isFriend); }, ) - : CircleAvatar( - backgroundImage: CachedNetworkImageProvider( - '$avatarsUrl/${getIt.get().avatar}', - ), - ), + : getIt.get().avatar.isNotEmpty + ? CircleAvatar( + backgroundImage: CachedNetworkImageProvider( + '$avatarsUrl/${getIt.get().avatar}', + ), + ) + : const CircleAvatar( + backgroundImage: + AssetImage('assets/images/user_4.png'), + ), const SizedBox( width: 10, ), @@ -190,8 +165,6 @@ class _GroupChatMessageBubbleState extends State { ? CrossAxisAlignment.start : CrossAxisAlignment.end, children: [ - // nickname - // used in group chat only widget.senderId == getIt.get().id ? getIt .get() @@ -208,70 +181,12 @@ class _GroupChatMessageBubbleState extends State { : FutureBuilder( future: _getMemberGroupChatProfile(), builder: (context, snapshot) { - if (snapshot.hasData) { - if (isFriend) { - return getIt - .get() - .friends[widget.senderId]! - .friendRemark - .isEmpty - ? getIt - .get() - .grouChatMemberProfiles[widget - .contactId]![widget.senderId]! - .remark - .isEmpty - ? Text( - getIt - .get() - .friends[widget.senderId]! - .nickname, - ) - : Text( - getIt - .get() - .grouChatMemberProfiles[ - widget.contactId]![ - widget.senderId]! - .remark, - ) - : Text( - getIt - .get() - .friends[widget.senderId]! - .friendRemark, - ); - } else { - return getIt - .get() - .grouChatMemberProfiles[widget - .contactId]![widget.senderId]! - .remark - .isEmpty - ? Text( - getIt - .get() - .grouChatMemberProfiles[widget - .contactId]![widget.senderId]! - .nickname, - ) - : Text( - getIt - .get() - .grouChatMemberProfiles[widget - .contactId]![widget.senderId]! - .remark, - ); - } - } else { - return Text(widget.senderId); - } + return _showName(snapshot, isFriend); }, ), const SizedBox( height: 5, ), - if (widget.type == 'text/multipart') // message box Container( @@ -347,4 +262,191 @@ class _GroupChatMessageBubbleState extends State { ), ); } + + CircleAvatar _showAvatar(AsyncSnapshot snapshot, bool isFriend) { + if (snapshot.hasData) { + if (isFriend) { + String avatar = + getIt.get().friends[widget.senderId]!.avatar; + return avatar.isEmpty + ? CircleAvatar( + child: Image.asset('assets/images/user_4.png'), + ) + : CircleAvatar( + backgroundImage: CachedNetworkImageProvider( + '$avatarsUrl/$avatar', + ), + ); + } else { + String avatar = getIt + .get() + .grouChatMemberProfiles[widget.contactId]![widget.senderId]! + .avatar; + return avatar.isEmpty + ? CircleAvatar( + child: Image.asset('assets/images/user_4.png'), + ) + : CircleAvatar( + backgroundImage: CachedNetworkImageProvider( + '$avatarsUrl/$avatar', + ), + ); + } + } else { + // fix the bug that when the future return, the avatar will flash + if (isFriend) { + String avatar = + getIt.get().friends[widget.senderId]!.avatar; + return avatar.isEmpty + ? CircleAvatar( + child: Image.asset('assets/images/user_4.png'), + ) + : CircleAvatar( + backgroundImage: CachedNetworkImageProvider( + '$avatarsUrl/$avatar', + ), + ); + } else { + if (getIt + .get() + .grouChatMemberProfiles + .containsKey(widget.contactId)) { + if (getIt + .get() + .grouChatMemberProfiles[widget.contactId]! + .containsKey(widget.senderId)) { + String avatar = getIt + .get() + .grouChatMemberProfiles[widget.contactId]![widget.senderId]! + .avatar; + return avatar.isNotEmpty + ? CircleAvatar( + backgroundImage: CachedNetworkImageProvider( + '$avatarsUrl/$avatar', + ), + ) + : const CircleAvatar( + backgroundImage: AssetImage( + 'assets/images/user_4.png', + ), + ); + } else { + return const CircleAvatar( + backgroundImage: AssetImage( + 'assets/images/user_4.png', + ), + ); + } + } else { + return const CircleAvatar( + backgroundImage: AssetImage( + 'assets/images/user_4.png', + ), + ); + } + } + } + } + + Text _showName(AsyncSnapshot snapshot, bool isFriend) { + if (snapshot.hasData) { + if (isFriend) { + String friendRemark = + getIt.get().friends[widget.senderId]!.friendRemark; + String remarkInGroupChat = getIt + .get() + .grouChatMemberProfiles[widget.contactId]![widget.senderId]! + .remark; + + return friendRemark.isEmpty + ? remarkInGroupChat.isEmpty + ? Text( + getIt + .get() + .friends[widget.senderId]! + .nickname, + ) + : Text(remarkInGroupChat) + : Text(friendRemark); + } else { + String remarkInGroupChat = getIt + .get() + .grouChatMemberProfiles[widget.contactId]![widget.senderId]! + .remark; + + return remarkInGroupChat.isEmpty + ? Text( + getIt + .get() + .grouChatMemberProfiles[widget.contactId]![widget.senderId]! + .nickname, + ) + : Text(remarkInGroupChat); + } + } else { + // fix the bug that when the future return, the name will flash + if (isFriend) { + String friendRemark = + getIt.get().friends[widget.senderId]!.friendRemark; + + if (friendRemark.isNotEmpty) { + return Text(friendRemark); + } else { + if (getIt + .get() + .grouChatMemberProfiles + .containsKey(widget.contactId) && + getIt + .get() + .grouChatMemberProfiles[widget.contactId]! + .containsKey(widget.senderId)) { + String remarkInGroupChat = getIt + .get() + .grouChatMemberProfiles[widget.contactId]![widget.senderId]! + .remark; + String nickname = getIt + .get() + .friends[widget.senderId]! + .nickname; + + return remarkInGroupChat.isEmpty + ? Text(nickname) + : Text(remarkInGroupChat); + } else { + return Text(widget.senderId.substring(0, 5)); + } + } + } else { + if (getIt + .get() + .grouChatMemberProfiles + .containsKey(widget.contactId)) { + if (getIt + .get() + .grouChatMemberProfiles[widget.contactId]! + .containsKey(widget.senderId)) { + String remarkInGroupChat = getIt + .get() + .grouChatMemberProfiles[widget.contactId]![widget.senderId]! + .remark; + String nickname = getIt + .get() + .grouChatMemberProfiles[widget.contactId]![widget.senderId]! + .nickname; + return remarkInGroupChat.isNotEmpty + ? Text(remarkInGroupChat) + : Text(nickname); + } else { + return Text( + widget.senderId.substring(0, 5), + ); + } + } else { + return Text( + widget.senderId.substring(0, 5), + ); + } + } + } + } } diff --git a/lib/screens/more/setting_screen/setting_screen.dart b/lib/screens/more/setting_screen/setting_screen.dart index 0256daa..dc85209 100644 --- a/lib/screens/more/setting_screen/setting_screen.dart +++ b/lib/screens/more/setting_screen/setting_screen.dart @@ -2,9 +2,11 @@ import 'package:flutter/material.dart'; import 'package:go_router/go_router.dart'; import 'package:hive_flutter/hive_flutter.dart'; import 'package:together_mobile/models/apply_list_model.dart'; +import 'package:together_mobile/models/contact_model.dart'; import 'package:together_mobile/models/init_get_it.dart'; import 'package:together_mobile/models/token_model.dart'; import 'package:together_mobile/models/user_model.dart'; +import 'package:together_mobile/models/websocket_model.dart'; class SettingScreen extends StatelessWidget { const SettingScreen({super.key}); @@ -22,9 +24,12 @@ class SettingScreen extends StatelessWidget { child: TextButton( onPressed: () async { await getIt.get().clean(); + getIt.get().disconnect(); getIt.get().clean(); getIt.get().clean(); getIt.get().clean(); + getIt.get().clean(); + getIt.get().clean(); // Hive.deleteFromDisk(); Hive.close(); // ignore: use_build_context_synchronously diff --git a/lib/screens/signin_signup/components/username_signin.dart b/lib/screens/signin_signup/components/username_signin.dart index bc0ab83..8c98eeb 100755 --- a/lib/screens/signin_signup/components/username_signin.dart +++ b/lib/screens/signin_signup/components/username_signin.dart @@ -30,6 +30,13 @@ class _UsernameSigninBodyState extends State { 'password': false, }; + @override + void dispose() { + usernameController.dispose(); + passwordController.dispose(); + super.dispose(); + } + void _isInputError(String type, bool error) { setState(() { _isError[type] = error; @@ -139,11 +146,4 @@ class _UsernameSigninBodyState extends State { CherryToast.error(title: const Text('用户名或密码错误')).show(context); } } - - @override - void dispose() { - usernameController.dispose(); - passwordController.dispose(); - super.dispose(); - } }