Search code examples
flutterapiexpressstream

How to create a streaming API for flutter to subscribe to?


I'm trying to get message from an API I builed in express in Flutter.

The thing is, to get my messages I had the build a streambuilder beacause I want the list of message to update on new messages. To achieve this, a create a steam method that check the API every x seconds.

  Stream<List<Message>> streamMessage() async* {
    final storage = FlutterSecureStorage();
    var json = (await storage.read(key: 'user'))!;
    User user = User.fromJson(jsonDecode(json));
    jwt = user.jwt;

    yield* Stream.periodic(const Duration(seconds: 3), (_) async {
      http.Response response = await http.get(
          Uri.parse("https://myapi.com/messages"),
          headers: {
            'Authorization': 'Bearer $jwt',
          });

      List<Message> messages = [];
      if (response.statusCode == 200) {
        var list = List.from(jsonDecode(response.body)).reversed;
        messages = list.map((e) => Message.fromJson(e)).toList();
      } else {
        log("Error : ${response.statusCode} -- ${response.reasonPhrase}");
      }
      return messages;
    }).asyncMap(
      (value) async => await value,
    );
  }

It work, but it have a lot of issues.

  • the update is not as fast as I want
  • it's a lot of request for nothing
  • the API can easily get overloaded
  • This way look more like a workaround than a real function

I would like to create a mechanisme in my backend to act more like a real stream subscibtion. As it work with Firebase. But I have no clues how it work, or even what to search.

If you have any hint, how can I achieve this ?


Solution

  • You could achieve this with websockets which will require some config on your node app and your Flutter app.

    I'd suggest using Flutter socket.io client and node socket.io server.

    Note that the stable version of the flutter client is not up to date with the current version of the node server. You can either use the beta version 2 of the flutter client or an older version of the server.

    Last time I set this up I chose the latter. Version 1.01 of the client definitely works with node server version 2.4.1.

    Here's a basic example with a stateful widget but if you want to use a StreamBuilder there's an example on the client pub.dev page. It can be tied into any state management solution.

    class SocketExample extends StatefulWidget {
      const SocketExample({Key? key}) : super(key: key);
    
      @override
      _SocketExampleState createState() => _SocketExampleState();
    }
    
    class _SocketExampleState extends State<SocketExample> {
      static const _sockerURL = 'your node app url';
    
      static const _socketEvent = 'event_1';
    
      final _socket = IO.io(sockerURL, <String, dynamic>{
        "transports": ["websocket"],
        "autoConnect": false,
      });
    
      @override
      void initState() {
        super.initState();
        _socket.connect();
        _socket.onConnect((data) {
          log('Connected ${_socket.connected}');
        });
        _socket.on(socketEvent, (message) {
          // whatever needs to happen when you get an event from the server
          // put it here
        });
      }
    
     @override
      void dispose() {
        _socket.disconnect();
        super.dispose();
      }
    
    
      @override
      Widget build(BuildContext context) {
        return ...
      }
    }
    

    And a basic setup on the node side would look something like this

    const server = http.createServer(app);
    const io = require("socket.io")(server);
    
    const socketEvent = "event_1"; // this string needs to correspond to the event in your flutter app
    
    io.on("connection", (socket) => {
      socket.on(socketEvent, (msg) => {
        io.emit(socketEvent, msg);
    
        console.log("message: " + msg);
      });
    });
    

    Then on whichever GET/POST etc... you need in the node you send a message to Flutter with

     io.emit(socketEvent, whatever the payload to your Flutter app is goes here);
    
    

    That's pretty much all you have to do get real time updates in Flutter from a node app without manually refreshing or checking the API at any interval.