This is the 18th day of my participation in the August Challenge

preface

In practical applications, we often encounter data flow processing, such as real-time location information flow generated when monitoring real-time location, such as Socket communication needs to monitor Socket data flow. A StreamProvider is designed to listen for changes in the Stream. When the Stream generates new data, it notifies the listener to refresh. This article describes how to use StreamProvider to listen on WebSocket data.

The knowledge points of this design are as follows:

  • WebSokcetThe client encapsulates the plug-insocket_io_clientThe use of.
  • StreamProviderMonitor Socket data.

IO server that matches socket_io_client. The NPM address is socket. IO, version 4.1.3. Note that the plugin for Flutter is version-compatible with the current version under development: ^ 2.0.0-beta.4-Nullsafety. 0 (the stable version 1.0.1 plugin does not support server version 4.1.3). The back-end source code address is: back-end source code.

Socket_io_client introduction

Socket_io_client is a popular Socket client plug-in with a version of javascript. Socket_io_client encapsulates Dart sockets, simplifying Socket usage.

Create a connection as follows, where $host and $port correspond to the host name and port, followed by the configuration parameters. When using webSocket, you need to specify that tranSports (array) elements include webSocket. AutoConnect is automatically connected after creation. ForceNew specifies whether a new Socket object is created each time, if false (the default) the old connection is used (if not closed). Here we use a new Socket object every time we use it because we close the connection after we exit the page.

_socket = SocketIO.io('ws://$host:$port', <String.dynamic> {'transports': ['websocket'].'autoConnect': true.'forceNew': true
});
Copy the code

After the Socket is created, you can set the event response callback method, commonly used by the following methods:

  • onConnect(EventHandler handler): Callback if the connection is established successfully.
  • onConnectTimeout(EventHandler handler): connection timeout callback;
  • onConnectError(EventHandler handler): connection error callback;
  • onError(EventHandler handler): Callback when an error occurs;
  • on(String event, (EventHandler handler): Subscribes to the message of the specified event, which can be received in this function when the server sends the message.
  • onDisconnect(EventHandler handler): Callback when disconnected;
  • connect/disconnect: Active connection/disconnection method;
  • open/close: Open and close methods.

The point is when the received data is added to a stream processing object after the connection is created. Flutter provides the StreamController class to handle Stream objects. StreamController allows only one subscriber. New stream data can be added using the Add method of the Sink attribute, and subscribers will be notified of the new data when added. So we can use StreamProvider to subscribe to this stream data.

StreamSocket = StreamSocket; StreamSocket = StreamSocket;

class StreamSocket {
  final _socketResponse = StreamController<String> (); Stream<String> get getResponse => _socketResponse.stream;

  final String host;
  final int port;
  late final Socket _socket;

  StreamSocket({required this.host, required this.port}) {
    _socket = SocketIO.io('ws://$host:$port', <String.dynamic> {'transports': ['websocket'].'autoConnect': true.'forceNew': true
    });
  }

  void connectAndListen() {
    _socket.onConnect((_) {
      debugPrint('connected');
    });

    _socket.onConnectTimeout((data) => debugPrint('timeout'));
    _socket.onConnectError((error) => debugPrint(error.toString()));
    _socket.onError((error) => debugPrint(error.toString()));
    _socket.on('msg', (data) {
      _socketResponse.sink.add(data);
    });
    _socket.onDisconnect((_) => debugPrint('disconnect'));
  }

  void sendTextMessage(String message) {
    _socket.emit('msg', message);
  }

  voidclose() { _socketResponse.close(); _socket.disconnect().close(); }}Copy the code

StreamProvider application

StreamProvider is similar to FutureProvider and ChangeNotifierProvider, and can be either created or value:

/ / the create form
StreamProvider({
  Key? key,
  requiredCreate<Stream<T>? > create,required T initialData,
  ErrorBuilder<T>? catchError,
  UpdateShouldNotify<T>? updateShouldNotify,
  bool? lazy,
  TransitionBuilder? builder,
  Widget? child,
}) 
  
/ / the value form
StreamProvider.value({
  Key? key,
  required Stream<T>? value,
  required T initialData,
  ErrorBuilder<T>? catchError,
  UpdateShouldNotify<T>? updateShouldNotify,
  bool? lazy,
  TransitionBuilder? builder,
  Widget? child,
}) 
Copy the code

Here we first use a StatefulWidget to manage StreamSocket initialization and Socket closure, and wrap the actual business component in two providers, one called StreamProvider, One is MessageModel. StreamProvider automatically monitors Socket data flows and displays them on the interface. MessageModel is currently only used to send messages.

class _SocketClientWrapperState extends State<SocketClientWrapper> {
  final StreamSocket streamSocket = StreamSocket(host: '127.0.0.1', port: 3001);
  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(
        title: Text('Stream Provicer'),
      ),
      body: Stack(
        alignment: Alignment.bottomCenter,
        children: [
          StreamProvider<String>(
            create: (context) => streamSocket.getResponse,
            initialData: ' ',
            child: StreamDemo(),
          ),
          ChangeNotifierProvider<MessageModel>(
            child: MessageReplyBar(messageSendHandler: (message) {
              streamSocket.sendTextMessage(message);
            }),
            create: (context) => MessageModel(),
          ),
        ],
      ),
    );
  }

  @override
  void initState() {
    streamSocket.connectAndListen();
    super.initState();
  }

  @override
  void dispose() {
    streamSocket.close();
    super.dispose(); }}Copy the code

The Stack is used to anchor the message input bar MessageReplyBar to the bottom. Note that if you want the input bar to float automatically with the keyboard, you need to put it into the body of the Scaffold. If you want the bar to float automatically with the keyboard, you need to put it in the body of the Scaffold.

Both StreamDemo and MessageReplyBar components are simple, with the business logic for MessageReplyBar to click the Send button to send a message over the Socket to the server. StreamDemo then returns the server message that the StreamProvider receives from the Socket.

Running effect

We can see that the connection establishment, message sending, message receiving and disconnection process are normal with the printed log of the server.

The source code has been uploaded to: State management related code – NULL Safety version.

conclusion

This paper introduces the StreamProvider stream state management, and introduces the socket_io_client plug-in to realize the WebSocket communication with the server, through the StreamProvider subscription stream data, and notification interface refresh. The interface layer of non-intrusive Socket data display. This article is more about the use of two tools. In the next article we will use these two tools to implement an instant chat application.


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!