Flutter - Page Does Not Appear to Push Until FutureBuilder is Done Flutter - Page Does Not Appear to Push Until FutureBuilder is Done dart dart

Flutter - Page Does Not Appear to Push Until FutureBuilder is Done


Try this out to get proper state of your data.

  switch(snapshot.connectionState){                  case ConnectionState.none:                    return Text('none');                  case ConnectionState.active:                  case ConnectionState.waiting:                    return Text('Active and may be waiting');                  case ConnectionState.done:                    return Text('Done');                  default:                    return Text('Default');                }


You stated that the initial value of your stream is an empty list. Therefore, StreamBuilder will display the empty list right away, so you can't see CircularProgressIndicator.

Try the code below, please:

@overrideWidget build(BuildContext context) {  return Scaffold(    appBar: AppBar(      title: Text('My App'),      centerTitle: false,    ),    body: StreamBuilder<List<Widget>>(      stream: widget.bloc.myListStream,      builder: (context, snapshot) {        if (!snapshot.hasData || snapshot.data.length == 0) {          return Center(            child: CircularProgressIndicator(),          );        } else {          return SingleChildScrollView(            child: Column(              mainAxisSize: MainAxisSize.min,              children: snapshot.data,            ),          );        }      },    ),  );}

Don't forget that this code will show the CircularProgressIndicator even when there is no item added to the list after your future method completed. If you don't want this to happen, leave the initial value of your stream as null and create a new list when you call getListItems. So, you can just check the data for null like !snapshot.hasData to show the loading state.

Alternative Approach

There is another way to display data from a stream besides StreamBuilder. You can listen to the stream inside initState and change the state of your screen when there is data.

class MyListScreen extends StatefulWidget {  MyListScreen();  @override  _MyListScreenState createState() => _MyListScreenState();}class _MyListScreenState extends State<MyListScreen> {  final bloc = MyListBloc();  List<Widget> myList;  @override  void initState() {    super.initState();    bloc.getListItems(context).whenComplete(() {      bloc.myListStream.listen((List<Widget> listData) {        setState(() {          myList = listData;        });      });    });  }  @override  Widget build(BuildContext context) {    return Scaffold(      appBar: AppBar(        title: Text('My App'),        centerTitle: false,      ),      body: _buildBody(),    );  }  Widget _buildBody() {    if (myList == null) {      return Center(        child: CircularProgressIndicator(),      );    } else {      return SingleChildScrollView(        child: Column(          mainAxisSize: MainAxisSize.min,          children: myList,        ),      );    }  }}


I think the problem should be inside your myListStream. Please share what it doing inside if this is not working.

getListItems() is a Future function (data is null or a list of items)

myListStreamis a Stream function

If you want to concatenate the Future result to Stream, you should return a Stream immediately and yield the updated data if the data is changed.

DO NOT USE

StreamBuilder<List<Widget>>(  stream: myListStream(),  // myListStream will be null until the data is ready  builder:  ...Future getListItems() async {  List<Widget> listView = await bloc.getListItems(myContext);  myListStream = Stream(...)}

USE

StreamBuilder<List<Widget>>(  stream: myListStream(), // myListStream is a Stream but the data is null  builder:  ...Stream<List<Widget>> myListStream() async* {  List<Widget> listView = await bloc.getListItems(myContext);  yield listView;}  

For the best use of Stream, my suggestion is to implement something like getListItemsStream() that can be used directly by StreamBuilder. In your case, I think you can just use FutureBuilder to achieve the same result.