Using Stream/Sink in Flutter

2020-07-18 10:47发布

问题:

I'm trying to replace the increment flutter app code, by using Streams from Dart API without using scoped_model or rxdart.

So I read this and watched this, but could not get it work for me, my codes are:

StreamProvider.dart:

import 'package:flutter/widgets.dart';
import 'businessLogic.dart';
import 'dart:async';

class Something {

  final _additionalContrllerr = StreamController<int>();
  Sink<int> get addition => _additionalContrllerr.sink;

  Stream<int> get itemCount =>  _additionalContrllerr.stream;
}

class StreemProvider extends InheritedWidget {
  final Something myBloc;  // Business Logic Component

  StreemProvider({
    Key key,
    @required this.myBloc,
    Widget child,
  }) : super(key: key, child: child);

  @override
  bool updateShouldNotify(InheritedWidget oldWidget) => true;

  static Something of(BuildContext context) =>
      (context.inheritFromWidgetOfExactType(StreemProvider) as StreemProvider)
          .myBloc;
}

main.dart:

import 'package:flutter/material.dart';
import 'package:flutter_app/StreemProvider.dart';

void main() => runApp(MyApp(
  textInput: Text("Provided By the Main"),
));

class MyApp extends StatefulWidget {
  final Widget textInput;
  MyApp({this.textInput});

  @override
  State<StatefulWidget> createState() => MyAppState();
}

class MyAppState extends State<MyApp> {
  bool checkBoxValue = false;

  int _counter = 0;
  @override
  Widget build(BuildContext ctxt) {
    var x = Something();      //// Not sure if have to use this!
    return StreemProvider(
      myBloc: x,              //// Not sure about this!!
      child: MaterialApp(
      home: SafeArea(
           child: Scaffold(
              body: new Center(
              child: new Column(
              children: <Widget>[
                widget.textInput,
                Text("clickec $_counter times"),
                Text("clickec ${x.itemCount.listen((int i) => i)} times"),
      /// How to get the value of i??!
                Checkbox(
                    value: checkBoxValue,
                    onChanged: (bool newValue){
                      setState(() {
                        checkBoxValue = newValue;
                      });
                    }
                )
              ],
            )),
             floatingActionButton: Incrementer(_increment),
            // floatingActionButton: Incrementer(x),
           ),
          ),
      ),
    );
  }

  _increment() {
    setState(() {
      _counter += 1;
    });
  }
}

class Incrementer extends StatefulWidget {

  final Function increment;

  Incrementer(this.increment);

  @override
  State<StatefulWidget> createState() {
    return IncrementerState();
  }
}
  class IncrementerState extends State<Incrementer>{
    @override
    Widget build(BuildContext ctxt) {
      final myBloc = StreemProvider.of(context);
      return new FloatingActionButton(
        //onPressed: widget.increment,
        // How ot get the latest value!!
        onPressed: () async {
          var y =  await myBloc.itemCount.last;
          if (y.isNaN) y = 0;
          myBloc.addition.add(y+1);
        },
        child: new Icon(Icons.add),
        );
    }
  }

回答1:

don't know the restrictions on rx_dart, but I can only try to answer by you using it. lol

your bloc doesnt define wht to listen in your input stream, this is how I could get it to work

counter_bloc.dart

import 'package:rxdart/rxdart.dart';
import 'dart:async';

class CounterBloc {
  int _count = 0;

  ReplaySubject<int> _increment = ReplaySubject<int>();
  Sink<int> get increment => _increment;

  BehaviorSubject<int> _countStream = BehaviorSubject<int>(seedValue: 0);
  Stream<int> get count => _countStream.stream;

  CounterBloc() {
    _increment.listen((increment) {
      _count += increment;
      _countStream.add(_count);
    });
  }
}

In the constructor the listen method is set for that stream. for each increment sent, it'll increment the counter and send the current count to another stream.

In main.dart, removed the _counter property since that's now being handled by the BLOC. and to display I used a stream builder.

also added a second fab, with a +2 increment to test the logic.

hope this helps you model your bloc class. :)

a good bloc reference: https://www.youtube.com/watch?v=PLHln7wHgPE

main.dart

import 'counter_bloc.dart';
import 'package:flutter/material.dart';

void main() => runApp(MyApp());

class MyApp extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      title: 'Flutter Demo',
      theme: ThemeData(
        primarySwatch: Colors.blue,
      ),
      home: MyHomePage(title: 'Flutter Demo Home Page'),
    );
  }
}

class MyHomePage extends StatefulWidget {
  MyHomePage({Key key, this.title}) : super(key: key);

  final String title;

  @override
  _MyHomePageState createState() => _MyHomePageState();
}

class _MyHomePageState extends State<MyHomePage> {
  CounterBloc bloc = CounterBloc();

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(
        title: Text(widget.title),
      ),
      body: Center(
        child: Column(
          mainAxisAlignment: MainAxisAlignment.center,
          children: <Widget>[
            Text(
              'You have pushed the button this many times:',
            ),
            StreamBuilder<int>(
              stream: bloc.count,
              initialData: 0,
              builder: (BuildContext c, AsyncSnapshot<int> data) {
                return Text(
                  '${data.data}',
                  style: Theme.of(context).textTheme.display1,
                );
              },
            ),
          ],
        ),
      ),
      floatingActionButton: Row(
        mainAxisAlignment: MainAxisAlignment.end,
        children: <Widget>[
          FloatingActionButton(
            onPressed: () {
              bloc.increment.add(2);
            },
            tooltip: 'Increment 2',
            child: Text("+2"),
          ),
          FloatingActionButton(
            onPressed: () {
              bloc.increment.add(1);
            },
            tooltip: 'Increment 1',
            child: Text("+1"),
          ),
        ],
      ), // This trailing comma makes auto-formatting nicer for build methods.
    );
  }
}


回答2:

Thanks a lot to vbandrade his answer helped me figuring t out. the solution worked with me is:

I need to have 2 StreamController if I need to listen to a sink in my bloc Business Logic Component, then process and stream the output to other elements.

The counter_bloc.dart is:

import 'dart:async';

class CounterBloc {
  int _count = 0;

  // The controller to stream the final output to the required StreamBuilder
  final _counter = StreamController.broadcast<int>();
  Stream<int> get counter => _counter.stream;

  // The controller to receive the input form the app elements     
  final _query = StreamController<int>();
  Sink<int> get query => _query.sink;
  Stream<int> get result => _query.stream;

  // The business logic
  CounterBloc() {
    result.listen((increment) {     // Listen for incoming input         
      _count += increment;          // Process the required data
      _counter.add(_count);         // Stream the required output
    });
  }

  void dispose(){
    _query.close();
    _counter.close();
  }
}

And the main.dart is:

import 'counter_bloc.dart';
import 'package:flutter/material.dart';

void main() => runApp(MyApp());

class MyApp extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      title: 'Flutter Demo',
      theme: ThemeData(
        primarySwatch: Colors.blue,
      ),
      home: MyHomePage(title: 'Flutter Demo Home Page'),
    );
  }
}

class MyHomePage extends StatefulWidget {
  MyHomePage({Key key, this.title}) : super(key: key);

  final String title;

  @override
  State<StatefulWidget> createState() {
    return _MyHomePageState();
  }

}

class _MyHomePageState extends State<MyHomePage> {
  var bloc = CounterBloc();

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(
        title: Text(widget.title),
      ),
      body: Center(
        child: Column(
          mainAxisAlignment: MainAxisAlignment.center,
          children: <Widget>[
            Text(
              'You have pushed the button this many times:',
            ),
            StreamBuilder<int>(      // Listen to the final output sent from the Bloc
              stream: bloc.counter,
              initialData: 0,
              builder: (BuildContext c, AsyncSnapshot<int> data) {
                return Text(
                  '${data.data}',
                  style: Theme.of(context).textTheme.display1,
                );
              },
            ),
          ],
        ),
      ),
      floatingActionButton: Row(
        mainAxisAlignment: MainAxisAlignment.end,
        children: <Widget>[
          FloatingActionButton(
            onPressed: () {
              bloc.query.add(2);         // Send input to the Bloc
            },
            tooltip: 'Increment 2',
            child: Text("+2"),
          ),
          FloatingActionButton(
            onPressed: () {
              bloc.query.add(1);        // Send input to the Bloc
            },
            tooltip: 'Increment 1',
            child: Text("+1"),
          ),
        ],
      ), // This trailing comma makes auto-formatting nicer for build methods.
    );
  }
}


回答3:

A simple implementation

import 'dart:async';

import 'package:flutter/material.dart';

void main() => runApp(MyApp());

class MyApp extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      title: 'Counter Demo',
      debugShowCheckedModeBanner: false,
      theme: ThemeData(
        primarySwatch: Colors.blue,
      ),
      home: MyHomePage(),
    );
  }
}

class MyHomePage extends StatelessWidget {
  int _counter = 0;

  final StreamController<int> _streamController =
      StreamController<int>.broadcast();

  Stream<int> get _stream => _streamController.stream;

  void increamentCounter() {
    _counter++;
    _streamController.add(_counter);

  }

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(
        title: Text('Flutter demo'),
      ),
      body: Center(
        child: StreamBuilder<int>(
            stream: _stream,
            builder: (ctxt, snapshot) {
              if (snapshot.hasData) {
                return Text(
                    'You have pushed this button ${snapshot.data} times');
              }
              return Text('You have pushed this button ${0} times');
            }),
      ),
      floatingActionButton: FloatingActionButton(
        onPressed: () {
          increamentCounter();
        },
        tooltip: 'Increment',
        child: Icon(Icons.add),
      ),
    );
  }
}


标签: dart flutter