Flutter StreamProvider not pushing data into UI Flutter StreamProvider not pushing data into UI dart dart

Flutter StreamProvider not pushing data into UI


Wrap directly ListView in a StreamBuilder listening to the interested stream to listen to

StreamBuilder<List<DisplayTaskData>>(    stream: getAllTasks(), // provide the correct reference to the stream    builder: (context, snapshot) {        if (snapshot.data != null)            // here your old ListView            return ListView.builder(                // itemCount: snapshot.data.length, // <- this is better                itemCount: contextsnapshot.watch<List<DisplayTaskData>>()?data.length,                // here you probably can pass directly the element snapshot.data[index]                itemBuilder: (context, index) => _taskListView(context, index),            );        else            return Text("No Tasks"),    })


Because the code you provided is fractional, I can only guess the problem from.

Widget consumer part in TaskView.

context.watch<List<DisplayTaskData>>()

You should consume the class you put in your provider. From code below, you should use TaskViewModel class the receive notification when data change.

providers: [  ChangeNotifierProvider(create: (_) => taskViewModel),  StreamProvider(create:(context) =>  taskViewModel.getAllTasks())],

getAllTasks() in TaskRepository

From the updated code and the original code, I think you want to do some data modification when stream data come in. The original one is more likely you want but maybe you would like to put formattedTasks inside the map function.

Stream<List<DisplayTaskData>> getAllTasks(){  var watchAllTasks = AppDatabase().watchAllTasks();  return watchAllTasks.map((tasks) {    final formattedTasks = List<DisplayTaskData>();    tasks.forEach((element) {    var date = element.dueDate;    String dueDateInString;    if(date != null){      dueDateInString = ""          "${date.year.toString()}-"          "${date.month.toString().padLeft(2,'0')}-"          "${date.day.toString().padLeft(2,'0')}";    }    var displayTaskData = DisplayTaskData(task : element.task, dueDate: dueDateInString);    formattedTasks.add(displayTaskData);  });  return formattedTasks;}

Other advises I have to mention:

*DON'T reuse an existing ChangeNotifier using the default constructor

You shouldn't use create: (_) => variable in the main function if it is created.Please check document in provider

final taskViewModel = TaskViewModel();return MultiProvider(  providers: [    ChangeNotifierProvider.value(value: taskViewModel),    StreamProvider.value(value: taskViewModel.getAllTasks()),  ],  child: ...


StreamProvider Example

I'm assuming most arriving to this question are wondering why StreamProvider isn't causing rebuilds of UI. So here's a more generic example of a StreamProvider to illustrate a StreamProvider updating the UI with new data from a Stream.

Full code example here on Github.

This is built on the default Counter example when you create a new Flutter project in Android Studio.

It uses Provider and the english_words packages.

In Android Studio, you'll need to edit pubspec.yaml and main.dart.

pubspec.yaml

In pubspec.yaml, add a couple package dependencies for this example:

  provider: ^4.3.2+2  english_words: 3.1.5

e.g.

dependencies:  flutter:    sdk: flutter  # The following adds the Cupertino Icons font to your application.  # Use with the CupertinoIcons class for iOS style icons.  cupertino_icons: ^0.1.3  provider: ^4.3.2+2  english_words: 3.1.5

main.dart

Import Provider & english_words:

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

Then replace the _MyHomePageState State class with the below, plus the RowItem class below that.

Note:Below the StreamProvider consumer/context.watch there is also a StreamBuilder added for comparison. Both the SteamProvider and StreamBuilder will show the same data on the UI.

class _MyHomePageState extends State<MyHomePage> {  int _counter = 0;  List<RowItem> row;  StreamController<List<RowItem>> streamController =      StreamController.broadcast();  @override  void initState() {    super.initState();    row = [RowItem(_counter)];    streamController.add(row);  }  @override  void dispose() {    streamController.close();    super.dispose();  }  void _incrementCounter() {    setState(() {      _counter++;      row.add(RowItem(_counter));      streamController.add(row);    });  }  @override  Widget build(BuildContext context) {    /// STREAM*PROVIDER* HERE    /// - wrapped Scaffold in Builder to make consuming widgets "children" of StreamProvider    /// instead of siblings. InheritedWidgets like StreamProvider are only useful    /// to children widgets who inherit its context from below in the widget tree hierarchy.    /// Often you'd wrap your entire MyApp with a Provider, but this keeps this example    /// more concise.    /// https://flutter.dev/docs/development/data-and-backend/state-mgmt/simple#changenotifierprovider    return StreamProvider<List<RowItem>>.value(      initialData: row,      value: streamController.stream,      child: Builder( // <-- Added to make everything below        builder: (context) { //<-- this context, children of/inherit from StreamProvider          return Scaffold(            appBar: AppBar(              title: Text(widget.title),            ),            body: Center(              child: Column(                mainAxisAlignment: MainAxisAlignment.center,                children: <Widget>[                  Expanded(                    flex: 1,                    child: Column(                      mainAxisAlignment: MainAxisAlignment.center,                      children: [                        Text(                          'You have pushed the button this many times:',                        ),                        Text(                          '$_counter',                          style: Theme.of(context).textTheme.headline4,                        )                      ],                    ),                  ),                  Expanded(                    flex: 2,                    child: Container(                      color: Colors.lightBlueAccent,/// CONSUMER / CONTEXT.WATCH of STREAM*PROVIDER* HERE                      child: ListView.builder(                        itemCount: context.watch<List<RowItem>>().length,                        itemBuilder: (context, index) {                          List<RowItem> _row = context.watch<List<RowItem>>();                          return ListTile(                            title: Text(                                '[${_row[index].num} | ${_row[index].name}]'),                          );                        },                      ),                    ),                  ),                  Expanded(                    flex: 2,                    child: Container(                      alignment: Alignment.center,                      color: Colors.lightGreenAccent,/// STREAM_BUILDER_ for contrast HERE                      child: StreamBuilder(                        initialData: row,                        stream: streamController.stream,                        builder: (context, snapshot) {                          if (snapshot.hasData) {                            List<RowItem> _row = snapshot.data;                            return ListView.builder(                              itemCount: _row.length,                              itemBuilder: (context, index) {                                return ListTile(                                  title: Text(                                      '[${_row[index].num} | ${_row[index].name}]'),                                );                              },                            );                          }                          return Text('Waiting on data...');                        },                      ),                    ),                  ),                ],              ),            ),            floatingActionButton: FloatingActionButton(              onPressed: _incrementCounter,              tooltip: 'Increment',              child: Icon(Icons.add),            ),          );        },      ),    );  }}class RowItem {  int num;  String name;  RowItem(this.num) {    name = WordPair.random().asPascalCase;  }}

Now stop/restart the project and then you can click the FloatingActionButton to increment the counter and add events to the Stream.

The RowItem objects should show up in both the StreamProvider and the StreamBuilder

For those not using Android Studio the StatefulWidget was this:

class MyHomePage extends StatefulWidget {  MyHomePage({Key key, this.title}) : super(key: key);  final String title;  @override  _MyHomePageState createState() => _MyHomePageState();}