Lobe
Lobe

Reputation: 23

How to rebuild StreamBuilder on resumed state with Flutter

I develop an app using BLoC pattern. In my app there are 2 routes, route A and B, and both of them access same data. A problem caused when moving the routes as below.

  1. Move to route B from route A that shows the data.
  2. Update the data at route B.
  3. Back to route A.

After moving back to route A, the StreamBuilder of showing the data never updates automatically. How can I let the StreamBuilder update on resumed state?

Here are sample codes.

routeA.dart

class RouteA extends StatefulWidget {
  @override
  _RouteAState createState() => _RouteAState();
}

class _RouteAState extends State<RouteA> {
  @override
  Widget build(BuildContext context) {
    final bloc = Bloc();
    return Column(
      children: [
        StreamBuilder(    // this StreamBuilder never updates on resumed state
            stream: bloc.data, // mistake, fixed. before: bloc.count
            builder: (_, snapshot) => Text(
                  snapshot.data ?? "",
                )),
        RaisedButton(
          child: Text("Move to route B"),
          onPressed: () {
            Navigator.of(context).pushNamed("routeB");
          },
        ),
      ],
    );
  }
}

routeB.dart

class RouteB extends StatefulWidget {
  @override
  _RouteBState createState() => _RouteBState();
}

class _RouteBState extends State<RouteB> {
  @override
  Widget build(BuildContext context) {
    final bloc = Bloc();
    return Center(
      child: RaisedButton(
        child: Text("Update data"),
        onPressed: () {
          bloc.update.add(null);
        },
      ),
    );
  }
}

bloc.dart

class Bloc {
  Stream<String> data;
  Sink<void> update;
  Model _model;
  
  Bloc() {
    _model = Model();
    
    final update = PublishSubject<void>();
    this.update = update;
    
    final data = BehaviorSubject<String>(seedValue: "");
    this.data = data;
    
    update.map((event) => _model.update()).listen((event) => data.sink.add(_model.getData()));
  }
}

model.dart

class Model {
  static Model _model;

  factory Model() {    // model is singleton.
    _model ??= Model._();
    return _model;
  }

  Model._();

  int _data = 0;

  void update() {
    _data++;
  }

  String getData() {
    return _data.toString();
  }
}

Upvotes: 2

Views: 1801

Answers (1)

Jitesh Mohite
Jitesh Mohite

Reputation: 34180

StreamBuilder updates the data whenever it gets changed not when just by calling stream

RouteA

class RouteA extends StatefulWidget {
  @override
  _RouteAState createState() => _RouteAState();
}

class _RouteAState extends State<RouteA> {
  @override
  Widget build(BuildContext context) {
    return Column(
      children: [
        StreamBuilder(    // this StreamBuilder never updates on resumed state
            stream: bloc.data, // mistake, fixed. before: bloc.count
            builder: (_, snapshot) => Text(
              snapshot.data ?? "",
            )),
        RaisedButton(
          child: Text("Move to route B"),
          onPressed: () {
            Navigator.of(context).push(MaterialPageRoute(builder: (ctx) {
              return RouteB();
            }));
          },
        ),
      ],
    );
  }
}

Route B

class RouteB extends StatefulWidget {
  @override
  _RouteBState createState() => _RouteBState();
}

class _RouteBState extends State<RouteB> {
  @override
  Widget build(BuildContext context) {
    return Center(
      child: RaisedButton(
        child: Text("Update data"),
        onPressed: () {
          bloc.updateData();
        },
      ),
    );
  }
}

Bloc

class Bloc {
  final _update = PublishSubject<String>();
  Model _model = Model();
  Stream<String> get data => _update.stream;

  void updateData() async {
    _model.update();
    _update.sink.add(_model.getData());

    _update.stream.listen((event) {
      print(event);
    });
  }

  dispose() {
    _update.close();
  }
}

final bloc = Bloc();

just follow above changes, it will do the trick for you.

Upvotes: 2

Related Questions