This is the 20th day of my participation in the August More Text Challenge
preface
We spent a lot of time on Provider state management earlier because Provider is the preferred state management plugin for Flutter. This article uses instant chat as an example to describe the comprehensive application of Provider, which is also the conclusion of the Provider status management series. The contents of this article are as follows:
- Construction of contact human interface;
- Simple implementation of chat interface;
- StreamProvider receives Socket stream data and automatically notifies interface refresh;
- MultiProvider provides multiple Provider states for the main chat screen.
- Processing method when multiple providers have overlapping data.
Contact Interface Building
Before chatting, we need to select corresponding contacts for single chat, so we need to build a contact list. Here we use simple ListView.builder+Mock data to build the contact list. The interface is as follows. The key is to route the id of the contact when clicking the contact, so that the user ID can be specified to receive the message.
return ListTile(
leading: _getRoundImage(contactors[index].avatar, 50),
title: Text(contactors[index].nickname),
subtitle: Text(
contactors[index].description,
style: TextStyle(fontSize: 14.0, color: Colors.grey),
),
onTap: () {
debugPrint(contactors[index].id);
RouterManager.router.navigateTo(context,
'${RouterManager.chatPath}? toUserId=${contactors[index].id}'); });Copy the code
The implementation of chat interface
We place the sent message on the right, the received message on the left, and the margin of the Container is used to determine whether the message is positioned to the right. As for the distinction, it is distinguished by the fromUserId of the message object. If the fromUserId matches the current user ID, it is the sent message, otherwise it is the received message. In this case, we write the current user ID to death because there is no user system. To enable chat between emulators, we set one emulator to user1 and the other to user2. The interface also uses listView. builder. Build.
return ListView.builder(
itemBuilder: (context, index) {
MessageEntity message = messages[index];
double margin = 20;
double marginLeft = message.fromUserId == 'user1' ? 60 : margin;
double marginRight = message.fromUserId == 'user1' ? margin : 60;
return Container(
margin: EdgeInsets.fromLTRB(marginLeft, margin, marginRight, margin),
padding: EdgeInsets.all(15),
alignment: message.fromUserId == 'user1'
? Alignment.centerRight
: Alignment.centerLeft,
decoration: BoxDecoration(
color: message.fromUserId == 'user1'
? Colors.green[300]
: Colors.blue[400],
borderRadius: BorderRadius.circular(10)),
child: Text(
message.content,
style: TextStyle(color: Colors.white),
),
);
},
itemCount: messages.length,
);
Copy the code
One of the features of the Chat interface is that it receives the latest messages pushed by the StreamProvider. For the sake of uniformity, we will send and receive messages through the StreamProvider push update interface.
// Add the message to the flow controller when sending the message
void sendMessage(String event, T message) {
_socket.emit(event, message);
_socketResponse.sink.add(message);
}
// Messages are added to the flow controller when received
_socket.on(recvEvent, (data) {
_socketResponse.sink.add(data);
});
Copy the code
The StreamProvider reconstructs the chat interface for both receiving and sending messages. So how do you refresh the chat list data?
MultiProvider for message interface
The message interface needs to receive the StreamProvider’s message flow and also use the message list data, which we used here with MultiProvider. The message sending box and the chat interface share the ChatMessageModel (for demonstration only, can actually be separated).
final chatMessageModel = ChatMessageModel();
/ /...
body: Stack(
alignment: Alignment.bottomCenter,
children: [
MultiProvider(
providers: [
StreamProvider<Map<String.dynamic>? >( create: (context) => streamSocket.getResponse, initialData:null),
ChangeNotifierProvider.value(value: chatMessageModel)
],
child: StreamDemo(),
),
ChangeNotifierProvider.value(
child: MessageReplyBar(messageSendHandler: (message) {
Map<String.String> json = {
'fromUserId': 'user1'.'toUserId': widget.toUserId,
'contentType': 'text'.'content': message
};
streamSocket.sendMessage('chat', json);
}),
value: chatMessageModel,
),
]
/ /...
Copy the code
The ChatMessageModel is the message list status data, which contains only an array of message objects, a method to add messages, and a content property for the message reply box.
The StreamProvider sends a message to the StreamSocket, and the ChatMessageModel does not know about it. If you want to know, one way is to reference the ChatMessageModel object in StreamSocket and then call its addMessage method to add the message. But this increases the coupling of the two classes. Another trick is that the StreamdDemo build method gets the latest message pushed by StreamSocket and adds it to the message list. Since both sending and receiving messages were added to the message flow, the processing is unified.
Notice that the Provider is not allowed to call notifyListeners again in the component’s build method to notify the component of a refresh. Therefore, the addMessage method of ChatMessageModel cannot be used as a notifyListeners to notify the component of refreshing; otherwise, a refresh conflict may occur within the same component. In fact, because another Provider has already notified the component that it has been refreshed, there is no need to notify it. Of course, this is just a trick, and if the addMessage method also needs to tell other components to refresh, then this form is not desirable.
class ChatMessageModel with ChangeNotifier {
List<MessageEntity> _messages = [];
List<MessageEntity> get messages => _messages;
String content = ' ';
void addMessage(Map<String.dynamic> json) { _messages.add(MessageEntity.fromJson(json)); }}Copy the code
ChatMessageModel does not need to use the Watch method, and relies entirely on the StreamProvider stream push to update components. Each time a message is sent or received, build time updates the message list data before returning to the component tree, thus ensuring that the data is up to date. In fact, we have implemented the data interaction between the two providers opportunely.
@override
Widget build(BuildContext context) {
Map<String.dynamic>? messageJson = context.watch<Map<String.dynamic>? > ();if(messageJson ! =null) {
context.read<ChatMessageModel>().addMessage(messageJson);
}
List<MessageEntity> messages = context.read<ChatMessageModel>().messages;
/ / ListView
}
Copy the code
Running effect
To see how it works, the advantage of the simulator is that you can do multiple debugging. The effect is achieved, but the actual im chat is much more complicated than this, and sockets are generally not used, but WebSocket is a good choice if you want to implement instant message push inside the App after the application is opened. The source code has been submitted. The backend and Flutter code distribution is as follows:
- The Code for the Flutter Provider (NULL Safety version)
- Back-end code (Express version)
conclusion
That’s where the Provider section of the code ends, and we’ll move on to other state management plug-ins. Review the Provider chapter as follows:
- Use Provider to change the same code as 💩, reduce the code load by 2/3!
- Using MultiProvider to manage multiple states simultaneously
- Can providers implement nested state management?
- Introduction to Flutter (50) : Providers implement irrelevant page state sharing
- Introduction to Flutter (53) : learn about FutureProvider state management
- Introduction to Flutter (54) : Local changes in the monitoring state of a Provider
- Introduction to Flutter (55) : Play WebSocket with a Provider
- Introduction to The Flutter: Let the simulator talk to the Postman
Content of the basic coverage of the use of the Provider, if you want to further study can also go to see the official example and source code.
This is a column about the entry and practice of Flutter. The corresponding source code is here: Entry and Practice of Flutter.
👍🏻 : feel the harvest please point a praise to encourage!
🌟 : Collect articles, easy to look back!
💬 : Comment exchange, mutual progress!