4
votes

I have a StreamBuilder inside my Widget build of UserListDart:

StreamBuilder(
  stream: stream.asStream(),
  builder: (BuildContext context, AsyncSnapshot snapshot) {
    if(snapshot.hasData) {
      return Expanded(
        child: ListView.builder(
          itemCount: snapshot.data.length,
          itemBuilder: (BuildContext context, int index) {
            return ListTile(
              title: Text(
                snapshot.data[index].firstname + " " +
                snapshot.data[index].lastname
              ),
              onTap: () {
                Navigator.of(context).push(DetailScreenDart(snapshot.data[index]));
              },
            ); 
          }
        )
      );
    }
  }
    ...
)

The Stream is defined in the initState:

Future<List> stream; 

@override
void initState() {
  super.initState();
  stream = fetchPost();
}

The fetchPost() is an api call:

Future<List<User>> fetchPost() async {
  final response = await http.get('url');
  final jsonResponse = json.decode(response.body);

  List<User> users = [];

  for(var u in jsonResponse){
    User user = User(
      firstname: u["firstname"],
      lastname: u["lastname"],
    );
    users.add(user);
  }
  return users;
}

I Navigate to another Page to change for example the firstname (api get updated) and I Navigate back to the UserList:

Navigator.pushReplacement(
  context,
  new MaterialPageRoute(builder: (context) => new UserListDart())
).then((onValue) {
  fetchPost();
});

But the StreamBuilder won't get updated and I don't know why.

Note:

I think the StreamBuilder don't realise that a change has happend when I navigate back. It only applies the changes if I reopen the Page..

1
Please show us more code. The split you made can be helpful, but in this case it's not making it easier to understand what is going on in your StreamBuilder and see where your problem is. - João Soares
I updated the StreamBuilder code - Timebreaker900

1 Answers

3
votes

You should be using setState and updating your stream variable with the result of the fetchList() call:

Navigator.pushReplacement(
  context,
  new MaterialPageRoute(builder: (context) => new UserListDart())
).then((onValue) {
  setState((){
    stream = fetchPost();
  });
});

Here's a working example of what you want to achieve:

class StreamBuilderIssue extends StatefulWidget {
  @override
  _StreamBuilderIssueState createState() => _StreamBuilderIssueState();
}

class _StreamBuilderIssueState extends State<StreamBuilderIssue> {
  Future<List<String>> futureList;
  List<String> itemList = [
    'item 1',
    'item 1',
    'item 1',
    'item 1',
    'item 1',
  ];

  @override
  void initState() {
    futureList = fetchList();
    super.initState();
  }

  @override
  Widget build(BuildContext context) {
    return Center(
      child: Column(
        mainAxisAlignment: MainAxisAlignment.center,
        crossAxisAlignment: CrossAxisAlignment.center,
        children: <Widget>[
          Expanded(
            child: Center(
              child: StreamBuilder(
                stream: futureList.asStream(),
                builder: (context, snapshot){
                  if(snapshot.hasData){
                    return ListView.builder(
                      itemCount: snapshot.data.length,
                      itemBuilder: (context, index){
                        return Text(snapshot.data[index]);
                      },
                    );
                  }else{
                    return CircularProgressIndicator();
                  }
                },
              ),
            ),
          ),
          RaisedButton(
            onPressed: goToAnotherView,
            child: Text('Next View'),
          ),
          RaisedButton(
            onPressed: addItem,
            child: Text('AddItem'),
          )
        ],
      ),
    );
  }

  Future<List<String>> fetchList(){
    return Future.delayed(Duration(seconds: 2), (){
      return itemList;
    });
  }

  void goToAnotherView(){
    Navigator.push(context, MaterialPageRoute(
      builder: (context){
        return StreamBuilderIssueNewView(addItem);
      })
    ).then((res){
      setState(() {
        futureList = fetchList();
      });
    });
  }

  void addItem(){
    itemList.add('anotherItem');
  }
}

class StreamBuilderIssueNewView extends StatelessWidget {
  final Function buttonAction;

  StreamBuilderIssueNewView(this.buttonAction);

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(),
      body: Center(
        child: Column(
          children: <Widget>[
            Text('New view'),
            RaisedButton(
              onPressed: buttonAction,
              child: Text('AddItem'),
            )
          ],
        ),
      ),
    );
  }
}

By the way, you could also just use a FutureBuilder as your are not using a real Stream here, just an api fetch and you have to update with setState anyway.