Welcome to OStack Knowledge Sharing Community for programmer and developer-Open, Learning and Share
Welcome To Ask or Share your Answers For Others

Categories

0 votes
829 views
in Technique[技术] by (71.8m points)

stream - Flutter streambuilder between screens

I'm new in Flutter and I implemented the bloc architecture with streambuilder. I created 2 pages with just a button which change my background color. All of theses pages are listening a stream to change the background color but when I change on the first page, it doesn't on the second. But I want all my application change if 1 page decide to change it

Do I need to initialize a singleton bloc that my 2 screens used it ? Because for the moment each screen initializes its own bloc Here is an example of 1 page (the second one is the same)

class Test extends StatelessWidget {
  final ColorBloc _bloc = ColorBloc();

  @override
  Widget build(BuildContext context) {
    return StreamBuilder<Response<ColorResponse>>(
      stream: _bloc.stream,
      builder: (context, snapshot) {
        if (snapshot.hasData) {
          return Scaffold(
              appBar: AppBar(
                title: Text('First Route clicked'),
              ),
              backgroundColor: snapshot.data.data.color,
              body: new Center(
                  child: new InkWell(
                onTap: () {
                  Navigator.push(
                    context,
                    MaterialPageRoute(builder: (context) => Act2()),
                  );
                }, // Handle your callback
                child: Ink(height: 100, width: 100, color: Colors.blue),
              )),
              floatingActionButton: FloatingActionButton(
                onPressed: () {
                  _bloc.changeColor(Colors.yellow);
                },
                child: Icon(Icons.navigation),
                backgroundColor: Colors.green,
              ));
        }
        return Scaffold(
            appBar: AppBar(
              title: Text('First Route'),
            ),
            body: Center(
                child: new InkWell(
                    onTap: () {
                      Navigator.push(
                        context,
                        MaterialPageRoute(builder: (context) => Act2()),
                      );
                    }, // Handle your callback
                    child: Ink(height: 200, width: 200, color: Colors.red))),
            floatingActionButton: FloatingActionButton(
              onPressed: () {
                _bloc.changeColor(Colors.yellow);
              },
              child: Icon(Icons.navigation),
              backgroundColor: Colors.green,
            ));
      },
    );
  }
}

与恶龙缠斗过久,自身亦成为恶龙;凝视深渊过久,深渊将回以凝视…
Welcome To Ask or Share your Answers For Others

1 Answer

0 votes
by (71.8m points)

To change the state of all screen when a bloc fires an event, you can use multiple StreamBuilder, but all of them need to listen to the bloc that fire the event. You can try these 2 ways:

  • Passing the bloc as parameter into the 2nd screen
class Test extends StatelessWidget {
  final ColorBloc _bloc = ColorBloc();

  @override
  Widget build(BuildContext context) {
    return StreamBuilder<Response<ColorResponse>>(

           // ... other lines

              body: new Center(
                  child: new InkWell(
                onTap: () {

                  // Pass your bloc to the 2nd screen
                  Navigator.push(
                    context,
                    MaterialPageRoute(builder: (context) => Act2(bloc: _bloc)),
                  );
                }, 

                // ... other lines
  • Use package such as provider package to pass the bloc down the tree. In your first screen, you can do this:
class Test extends StatelessWidget {
  final ColorBloc _bloc = ColorBloc();

  @override
  Widget build(BuildContext context) {

    // Use Provider to provide the bloc down the widget tree
    return Provider(
      create: (_) => _bloc,
      child: StreamBuilder<Response<ColorResponse>>(
        
        // ... other lines

Then in the 2nd screen (which I assume is Act2()), you get the ColorBloc from the Provider:


class Act2 extends StatefulWidget {
  @override
  _Act2State createState() => _Act2State();
}

class _Act2State extends State<Act2> {
  ColorBloc _colorBloc;

  @override
  void didChangeDependencies() {
    // Get the bloc in the 1st page
    _colorBloc = Provider.of<ColorBloc>(context);
    super.didChangeDependencies();
  }

  @override
  Widget build(BuildContext context) {
    return StreamBuilder<Response<ColorResponse>>(

      // Use the bloc like in the 1st page
      stream: _colorBloc.stream, 
      builder: (context, snapshot) {
        if (snapshot.hasData) {
          // ... other lines

Small note: When using StreamBuilder you could initiate the value without the need to duplicate codes. Since I don't know the structure of your Response object, I'm taking Response(ColorResponse(color: Colors.green)) as the example:

// ... other lines

@override
  Widget build(BuildContext context) {
    return Provider(
      create: (_) => _bloc,
      child: StreamBuilder<Response<ColorResponse>>(

        // Initiate your data here
        initialData: Response(ColorResponse(color: Colors.green)), 

        stream: _bloc.stream,
        builder: (context, snapshot) {
          if (snapshot.hasData) {
            return Scaffold(
                appBar: AppBar(
                  title: Text('First Route clicked'),
                ),
                backgroundColor: snapshot.data.data.color,

                // ... other lines

          }

          // Don't need to copy the above code block for the case when the data is not streamed yet
          return Container(child: Center(child: CircularProgressIndicator()));
        },
      ),
    );
  }

与恶龙缠斗过久,自身亦成为恶龙;凝视深渊过久,深渊将回以凝视…
Welcome to OStack Knowledge Sharing Community for programmer and developer-Open, Learning and Share
Click Here to Ask a Question

...