Flutter 群聊发送消息后不刷新

Mar*_* Dz 5 flutter bloc flutter-bloc twilio-conversations flutter-streambuilder

我是 bloc 的新手,正在尝试使用包实现聊天flutter_bloc。我的消息传递服务是 twilio Conversations api。我的功能运行得很好,我只是无法刷新我的消息列表。有人可以告诉我我在这里缺少什么吗?如果我访问聊天页面,我可以看到所有消息,只有当我们有新消息时它才不会刷新。

\n

由于取得了小小的成功,我更新了我的代码。每当用户 A 或用户 B 加入聊天时,都会显示所有消息。如果我以用户 A 的身份发送消息,则该消息现在将在用户 A 的 UI 中可见,并且它是对话的一部分,但用户 B 在不重新加载的情况下不会收到添加到对话中的新消息。这里缺少哪个步骤以便其他用户也收到消息?我只需要帮助转换我的代码,以便我有一个流,聊天的其他参与者可以收听,这样他们的对话也令人耳目一新。

\n

我的 chat_event.dart

\n
  abstract class ChatEvent extends Equatable{\n  const ChatEvent();\n\n  @override\n  List<Object> get props => [];\n}\n\nclass InitialChatEvent extends ChatEvent {}\n\nclass AddMessage extends ChatEvent {\n  final String messageToPost;\n\n  AddMessage(this.messageToPost);\n}\n
Run Code Online (Sandbox Code Playgroud)\n

我的 chat_state.dart

\n
   class ChatState extends Equatable {\n  final Messages messages;\n\n  const ChatState({required this.messages});\n\n  factory ChatState.initial() =>  ChatState(messages: Messages(messages: []));\n\n  @override\n  List<Object> get props => [messages];\n\n  @override\n  bool get stringify => true;\n\n  ChatState copyWith({\n    List<Messages>? messages,\n  }) {\n    return ChatState(\n      messages: this.messages,\n    );\n  }\n}\n
Run Code Online (Sandbox Code Playgroud)\n

聊天页面的一部分

\n
...\nExpanded(\n              child: BlocBuilder<ChatBloc, ChatState>(\n                builder: (context, state) {\n                  print('chatpage builder: ' + state.messages.toString());\n                  return ListView.builder(\n                      itemCount: state.messages.messages.length,\n                      scrollDirection: Axis.vertical,\n                      itemBuilder: (context, i) {\n                        return ListTile(\n                       tileColor: state.messages.messages[i].author.toString() == username ? Colors.amber : Colors.amber.shade100,\n                      title: Text(\n                        state.messages.messages[i].body.toString(),\n                        style: TextStyle(color: Colors.black),\n                      ),\n                    );\n                      });\n                },\n              ),\n            ),\n            ...\n                  Container(\n                      height: 50,\n                      padding: EdgeInsets.fromLTRB(10, 0, 10, 0),\n                      child: RaisedButton(\n                        textColor: Colors.white,\n                        color: Colors.red,\n                        child: Text('Button'),\n                        onPressed: () async {\n                          // print(chatMessage.text);\n                          context.read<ChatBloc>().add(AddMessage(chatMessage.text));\n                        },\n                      )),\n                ],\n...\n
Run Code Online (Sandbox Code Playgroud)\n

chat_bloc.dart

\n
class ChatBloc extends Bloc<ChatEvent, ChatState> {\n \n  ChatBloc() : super(ChatState.initial()) {\n    //  print('wird ausgef\xc3\xbchrt');\n    on<InitialChatEvent>((event, emit) async {\n      final chatFeed = await HttpService().getMessages();\n      emit(ChatState(messages: chatFeed));\n    });\n    \n    on<AddMessage>((event, emit) async {\n      final newConversation = await HttpService().postMessage(event.messageToPost);\n      final chatFeed = await HttpService().getMessages();\n      emit(ChatState(messages: chatFeed));\n    });\n  }\n}\n
Run Code Online (Sandbox Code Playgroud)\n

main.dart(如果需要)

\n
...\n\nvoid main() => runApp(MultiBlocProvider(\n        providers: [\n          BlocProvider(create: (context) => ColorBloc()),\n         BlocProvider(create: (context) => ChatBloc()),\n        ],\n        child: MaterialApp(\n          title: "App",\n          home: MyApp(),\n        )));\n\nclass MyApp extends StatefulWidget {\n  @override\n  State<MyApp> createState() => _MyAppState();\n}\n\nclass _MyAppState extends State<MyApp> {\n  // This widget is the root of your application.\n  TextEditingController nameController = TextEditingController();\n\n  @override\n  Widget build(BuildContext context) {\n    return Scaffold(\n      body: Column(\n        children: [\n          ...\n              child: RaisedButton(\n                textColor: Colors.white,\n                color: Colors.red,\n                child: Text('Button'),\n                onPressed: () {\n              print(nameController.text);\n              context.read<ChatBloc>().add(InitialChatEvent());\n              Navigator.of(context).push(\n                MaterialPageRoute(\n                  builder: (context) => ChatPage(userText: nameController.text)\n                ),\n              );\n            },\n              )),\n        ],\n      ),\n    );\n  }\n}\n
Run Code Online (Sandbox Code Playgroud)\n

http_service.dart

\n
  Future postMessage(String messageToPost) async {\n    Map<String, String> _messageToPost = {\n      'Author': 'User A',\n      'Body': messageToPost,\n    };\n\n    try {\n      // print(messageToPost);\n\n      var response = await dio.post(\n          "https://conversations.twilio.com/v1/Conversations/$sid/Messages",\n          data: _messageToPost,\n          options: Options(\n            contentType: Headers.formUrlEncodedContentType,\n            headers: <String, String>{'authorization': basicAuth},\n          ));\n\n      return messageToPost;\n    } catch (e) {\n      print('exception: ' + e.toString());\n      Future.error(e.toString());\n    }\n  }\n\n  Future getMessages() async {\n    try {\n      final response = await dio.get(\n          "https://conversations.twilio.com/v1/Conversations/$sid/Messages",\n          // data: _messageToPost,\n          options: Options(\n            // contentType: Headers.formUrlEncodedContentType,\n            headers: <String, String>{'authorization': basicAuth},\n          ));\nprint(response.data);\n      final messageList = Messages.fromJson(response.data);\n      print(messageList);\n      return messageList;\n    } catch (e) {\n      print('exception: ' + e.toString());\n      return Future.error(e.toString());\n    }\n  }\n}\n
Run Code Online (Sandbox Code Playgroud)\n

You*_*MOU 4

这个问题没有简短的答案,如果我必须在这里写出完整的过程,那就太冗长和多余了。其他人已经写了完整的教程,比如这个很棒的教程: https: //blog.codemagic.io/flutter-ui-socket/。但是,这可能会错过您感兴趣的 BLoC 部分。因此,下面我将描述如何使用此链接中提供的示例并使用 BLoC 改进它。

您将需要创建一个 ChatBloc 和一个 Socket。我更喜欢创建一个 Socket Singletone,以确保在应用程序的整个生命周期中始终只有一个套接字连接:D。

旁注
我承认可能还有其他方法可以做到这一点。在这里,我正在解释我的方法;)为了简单起见,我根据上面链接的书面教程旋转了下面的示例;)

聊天 Bloc

Will包含基本的消息事件:

  • SendMessage
  • GetMessage
  • LoadHistory
  • UpdateConnectedUsers

这里我将重点讨论最敏感的一个(即您关心的问题)。那是,GetMessage。这里的秘密是调用这个事件,不是从 UI,而是从 Socket Singletone。换句话说,您的套接字将侦听传入的消息,每当收到消息时,它就会触发 ChatBloc 的“GetMessage”事件,而该事件又会更新 UI。这是一个实施尝试:

套接字单调

class SocketApi {
  IO.Socket _socket;
  //You need to inject an instance of the ChatBloc here so that you can 
  //call the 'GetMessage' event --- see comment below ;)
  ChatBloc chatBloc; 

  /// We are creating a singleton here to make sure we have only one instance 
  /// of Io.io at all time 
  static final SocketApi _socketApi = SocketApi._internal();
  SocketApi._internal() {
    _socket = IO.io(<url_to_your_socket_Server>, <String, dynamic>{
      'transports': ['websocket'],
      'autoConnect': false,
    });

    // This is where the magic happens... 
    // you listen of incoming messages 
    // on the socket and then trigger the chatBloc event 'GetMessage'. 
    _socket.on('message', (data) {
      // I am converting here to a chatMessage object... 
      // on your side you might have something like 'fromJson',
      // etc..
      ChatMessage chatMessage = chatMessageFromMap(data);
      chatBloc.add(GetMessage(chatMessage));
    });
  }
  //...
}
Run Code Online (Sandbox Code Playgroud)

现在剩下的就是在您需要在小部件树中匹配消息的位置添加 BlocBuilder 或 BlocSelector。

最后的重要说明 如果您真的很认真地使用 BLoC,我建议您检查与 bloc 库密切相关的 Freezed 软件包。它可以帮助您快速高效地构建模型。我与这个软件包的开发者没有任何关系,我也没有通过广告获得任何好处。它只是让我的生活更轻松,我绝对喜欢它提高我的代码质量的方式,并想分享 <3

干杯,祝你好运!