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

Viewed 401

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 ?

1 Answers

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.

Related