StreamProvider not updating state

Bram Vanbilsen

I am trying to use a StreamProvider (from this awesome package), but I've been struggling to get a particular stream to work.

I create a StreamController which I use to add data to its Stream via its Sink. All of this seems to be working fine. But when using this Stream with a StreamProvider, the widget tree does not reflect the changes of the Stream. It does however work fine using a StreamBuilder.

The code using a StreamProvider:

class TestPage extends StatelessWidget {
  final Mockup exchange = ExchangeApi.mockup;

  @override
  Widget build(BuildContext context) {
    return StreamProvider<List<Data>>(
      builder: (BuildContext context) => Wrapper.mockup.stream,
      initialData: null,
      catchError: (BuildContext context, e) {
        print("Error: $e");
        return null;
      },
      child: TestPageBody(),
    );
  }
}

class TestPageBody extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    List<Data> dataList = Provider.of<List<Data>>(context);
    return ListView.builder(
      itemCount: dataList?.length ?? 0,
      itemBuilder: (BuildContext context, int index) {
        return Text(dataList[index].name);
      },
    );
  }
}

I have been searching why this does not work, but haven't found an answer yet. But here are some things I did find:

  • Using Flutter Desktop Embedding, the UI did reflect changes from the stream when the window got resized (thus forcing to rebuild). The same effect is seen when using hot refresh.
  • The stream is constantly adding new data, I tested this by debugging and simply printing out the data

Any help would be greatly appreciated!

Rémi Rousselet

The default behavior of most providers (excluding ChangeNotifierProvider) is to assume that the values passed are immutable.

As such, if your stream emits the same value as previously emitted, this won't rebuild dependents.

There are two solutions:

  • Make the values emitted by your stream immutable, such that performing previousValue == newValue works correctly.

  • override updateShouldNotify to not filter values if they didn't change.

    A simple updateShouldNotify: (_, __) => true will do.


In the ideal world, prefer immutability.

This can be done by making a copy of your object before sending it to streamController.add(value):

List<T> value;
streamController.add(List.from(value));

A second (optional) step is to override updateShouldNotify (or operator== on your object) to notify the provider that the value didn't change.

With List, this can be done using ListEquality from collection/collection.dart:

import 'package:provider/provider.dart';
import 'package:collection/collection.dart';

StreamProvider<List<int>>(
  builder: (_) => stream,
  updateShouldNotify: const ListEquality<int>().equals,
);

Collected from the Internet

Please contact [email protected] to delete if infringement.

edited at
0

Comments

0 comments
Login to comment

Related