Bram  Vanbilsen
Bram Vanbilsen

Reputation: 6505

StreamProvider not updating state

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:

Any help would be greatly appreciated!

Upvotes: 8

Views: 6040

Answers (1)

R&#233;mi Rousselet
R&#233;mi Rousselet

Reputation: 277707

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,
);

Upvotes: 20

Related Questions