How do you do

After a period of exposure and iterations of the project, some means have to be used to control the state of the widgets. For most people new to Flutter, the only way to know how to update Flutter components is setState. When we see inheritedWidgets on some forums or websites, how do we update our UI? We’re not talking about inheritedWidgets this week, but when I click on The StreamBuilder and see that it inherits from StatefulWidget, I don’t.. StreamBuilder is also setState. So next, let’s find out.

StreamBuilder source

Class StreamBuilder<T> extends StreamBuilderBase<T, AsyncSnapshot<T>> { /// [builder] Const StreamBuilder({Key Key, this.initialData, Stream<T> Stream, @required this.builder, }) : assert(builder ! = null), super(key: key, stream: stream); Final AsyncWidgetBuilder<T> Builder; final AsyncWidgetBuilder<T> Builder; /// this is abstracted mainly because the data is asynchronous and /// requires an initial value for display. final T initialData; @override AsyncSnapshot<T> initial() => AsyncSnapshot<T>.withData(ConnectionState.none, initialData); @override AsyncSnapshot<T> afterConnected(AsyncSnapshot<T> current) => current.inState(ConnectionState.waiting); @override AsyncSnapshot<T> afterData(AsyncSnapshot<T> current, T data) {return AsyncSnapshot<T>.withData(ConnectionState.active, data);
  }

  @override
  AsyncSnapshot<T> afterError(AsyncSnapshot<T> current, Object error) {
    return AsyncSnapshot<T>.withError(ConnectionState.active, error);
  }

  @override
  AsyncSnapshot<T> afterDone(AsyncSnapshot<T> current) => current.inState(ConnectionState.done);

  @override
  AsyncSnapshot<T> afterDisconnected(AsyncSnapshot<T> current) => current.inState(ConnectionState.none);

  @override
  Widget build(BuildContext context, AsyncSnapshot<T> currentSummary) => builder(context, currentSummary);
}
Copy the code

Four key classes are observed here

  • StreamBuilderBase<T, AsyncSnapshot>
  • AsyncWidgetBuilder
  • AsyncSnapshot
  • I don’t know what Stream is, but let’s go ahead and analyze it one by one. Okay

StreamBuilderBase<T, AsyncSnapshot>

StreamBuilder’s base class, StreamBuilderBase, uses generics

  • T is the data type that we defined
  • The implementation of AsyncSnapshot is AsyncSnapshot. The implementation of AsyncSnapshot is AsyncSnapshot. Next, you can look directly at the source comments:
Abstract class StreamBuilderBase<T, S> extends StatefulWidget { Const StreamBuilderBase({Key Key, this.stream}) : super(Key: Key); /// The important thing to note here is that it might be empty, because we might not pass it. I have a question: if not, what does the UI handle? final Stream<T> stream; // AsyncSnapshot<T>. WithData (ConnectionState. None, initialData) // return AsyncSnapshot withData, Set the current AsyncSnapshot snapshot state to ConnectionState.None and pass in our initial data initialData S initial(); Waiting S afterConnected(S current) => current; AsyncSnapshot<T>. WithData (ConnectionState. Active, data) Data S afterData(S current, T data); /// Return AsyncSnapshot<T>.witherror (connectionState.active, error); S afterError(S current, Object error) => current; // return current. InState (connectionstate.done) to change the statusdoneS afterDone(S current) => current; /// current. InState (connectionState.none), change state None S afterDisconnected(S current) => current; Widget Build (BuildContext Context, S currentSummary); @override State<StreamBuilderBase<T, S>> createState() => _StreamBuilderBaseState<T, S>(); }Copy the code

These are abstract functions that are implemented by StreamBuilder one by one for a clear purpose. The abstract functions perform operations on AsyncSnapshot, from initialization to update to disconnection. AsyncSnapshot is a wrapper class that records and controls the state of the data we pass in, similar to the BaseResponse in our network request. Coming to the point, let’s see how StreamBuilder operates with AsyncSnapshot. Let’s look at the code

_StreamBuilderBaseState class

The general flow: the _SUBSCRIBE method is called in initState, didUpdateWidget, which calls the Stream’s listen, and then updates the UI with setState

/// State for [StreamBuilderBase].
class _StreamBuilderBaseState<T, S> extends State<StreamBuilderBase<T, S>> {
  StreamSubscription<T> _subscription;
  S _summary;

  @override
  void initState() {
    super.initState();
    _summary = widget.initial();
    _subscribe();
  }

  @override
  void didUpdateWidget(StreamBuilderBase<T, S> oldWidget) {
    super.didUpdateWidget(oldWidget);
    if(oldWidget.stream ! = widget.stream) {if(_subscription ! = null) { _unsubscribe(); _summary = widget.afterDisconnected(_summary); } _subscribe(); } } @override Widget build(BuildContext context) => widget.build(context, _summary); @override voiddispose() {
    _unsubscribe();
    super.dispose();
  }

  void _subscribe() {
    if(widget.stream ! = null) { _subscription = widget.stream.listen((T data) {setState(() {
          _summary = widget.afterData(_summary, data);
        });
      }, onError: (Object error) {
        setState(() {
          _summary = widget.afterError(_summary, error);
        });
      }, onDone: () {
        setState(() {
          _summary = widget.afterDone(_summary);
        });
      });
      _summary = widget.afterConnected(_summary);
    }
  }

  void _unsubscribe() {
    if(_subscription ! = null) { _subscription.cancel(); _subscription = null; }}}Copy the code

There is a new role, StreamSubscription, which you can see by looking at the _subscribe function,

_subscription = widget.stream.listen
Copy the code

The Stream is an observer, and the data in the Stream is listened to, notifying the _subscription subscriber when the data in the Stream changes. If we look further down,

_subscription = widget.stream.listen((T data) {
        setState(() {
          _summary = widget.afterData(_summary, data);
        });
      }, onError: (Object error) {
        setState(() {
          _summary = widget.afterError(_summary, error);
        });
      }, onDone: () {
        setState(() {
          _summary = widget.afterDone(_summary);
        });
      });
      _summary = widget.afterConnected(_summary);
    }
Copy the code

See, setState comes up, which is, oh, shit. AfterData, afterError, afterDone, etc. functions implemented in StreamBuilder are called according to the Stream implementation. After analyzing StreamBuilderBase, we don’t need to analyze it because it’s all clear, so let’s summarize

conclusion

StreamBuilder is an AsyncSnapshot that wraps a Stream and data T, initializes, binds a Stream listener at Statefulwiget’s appropriate lifecycle, and automatically unbinds the Stream at the end of its life. The Stream Stream is a producer-consumer model, but the data is notified through the observer mode, and most importantly, the data is updated through setState. To sum up:

  • StreamBuilder doesn’t have any real performance improvements
  • StreamBuilder is also a Statefulwidget, which adds a layer of nesting.
  • StreamBuilder is much like the role of VM, recommended, and a simple MVVM architecture is implemented.