Dart is a single-threaded model, where all the code we write runs in the same thread. If you do time-consuming operations, the application will block. Dart uses Future and Stream to write asynchronous tasks.

Future

A Future is a computation that does not complete immediately. Colloquially, it is an asynchronous execution process that needs to be used with async and await. Code after this is not blocked until the computation is complete before the result is returned.

Similar to JavaScript promises and async and await.

usage

void main()  {
  requestApi();
  doSomething1();
  doSomething2();
}

doSomething1() {
  print("doSomething1");
}

doSomething2() {
  print("doSomething2");
}

/// Asynchronous request data
Future requestApi() async{...return 
}
Copy the code

Since the requestApi() is an asynchronous function, the following code starts executing immediately after the program runs without waiting for the function to complete.

Wait for the return result

If you want to do something after the asynchronous function returns the result, you can use the then() method to listen.

void main()  {
  requestApi().then((value) {
    /// The result is returned and processing begins
    showApiData(value);
  });
  print("The program begins to run..."); }... showApiData(dynamic value){
  print("Display the data obtained:$value");
}
Copy the code

FutureBuilder

const FutureBuilder({
    Key? key,
    this.future,
    this.initialData,
    required this.builder,
})
Copy the code

FutureBuilder builds widgets based on the returned results of the incoming Future.

Use the sample

Generally used to update the UI after a network request.

class FutureBuilderDemo extends StatefulWidget {
  const FutureBuilderDemo({Key key}) : super(key: key);

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

class _FutureBuilderDemoState extends State<FutureBuilderDemo> {
  Future<String> futureData;

  @override
  void initState() {
    super.initState();
    /// Network request data begins when UI is initialized
    futureData = getData();
  }

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(
        title: Text('FutureBuilder test'),
      ),
      body: Container(
        child: Center(
          child: FutureBuilder<String>(
              future: futureData,
              builder: (BuildContext context, AsyncSnapshot snapshot) {
                if (snapshot.hasData) {
                  return Text("Obtained data ==${snapshot.data}");
                } else if (snapshot.hasError) {
                  return Icon(Icons.error_outline);
                } else {
                  returnCircularProgressIndicator(); }}),),); } Future<String> getData() async {
    /// Simulate network request and wait 5 seconds for return result
    await Future.delayed(Duration(seconds: 5));

    /// Returns an error
    // return Future.error("error");
    /// Returns a successful result
    return "Hello,world"; }}Copy the code

Stream

Stream is a sequence of asynchronous events, equivalent to an asynchronous Iterable. Unlike a Future, which is a single event execution, a Stream can send and execute multiple events. Different scenarios.

usage

There are two ways to create a Stream, one is a single subscription Stream, the other is a multi-subscription Stream, that is, can broadcast.

Listen returns a StreamSubscription, which is an observer.

Both constructors take an optional parameter sync, which defaults to false.

.bool sync = false{})return sync? _SyncStreamController<T>(....) : _AsyncStreamController<T>(...) ;Copy the code

If sync is false, it creates an asynchronous StreamController. If sync is true, it creates a synchronous StreamController. Since streams are used for asynchronous purposes in most scenarios, we simply don’t pass them in.

A single subscription

/// Use the constructor directly
StreamController<String> _controller = StreamController();
StreamSubscription subscription = _controller.stream.listen((event) {
    print("Subscription to receive$event");
});
Copy the code

Radio subscription

void main() {
  /// Construct a broadcastable Controller using factory methods
  StreamController _controller = StreamController.broadcast();

  /// Use multiple observers to subscribe to the same Stream.
  StreamSubscription subscription1 =
      _controller.stream.listen((event) => print("Sub1 receives$event"));
  StreamSubscription subscription2 =
      _controller.stream.listen((event) => print("Sub2 to receive$event"));
  StreamSubscription subscription3 =
      _controller.stream.listen((event) => print("Sub3 to receive$event"));

  /// When an event is sent, it is received by all subscribed observers.
  _controller.add("Hello");
}
Copy the code

Run many times, the print result is as follows:

Sub1 receives Hello. Sub2 receives Hello. Sub3 receives Helloexit code 0
Copy the code

Conclusion:

The earlier an event subscription (LISTEN) starts, the higher priority the event receives.

Release resources

After using the event or when the Widget is turned off in the Flutter, remember to close the Stream as well.

_controller.stream.listen((event) {}, onDone: () => print("OnDone event received"));

_controller.close();
subscription.cancel();
Copy the code

When closed, the onDone callback method is automatically triggered.

StreamBuilder

In the interface, the general use of StreamBuilder to use with Stream. Multi-state interfaces can be implemented.

Use the sample

/// Define three UI states
enum UIState { type_1, type_2, type_3 }

class StreamBuilderDemo extends StatefulWidget {
  const StreamBuilderDemo({Key key}) : super(key: key);

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

class _StreamBuilderDemoState extends State<StreamBuilderDemo> {
  StreamController<UIState> _controller;

  @override
  void initState() {
    super.initState();

    /// Initialize the controller
    _controller = StreamController();
  }

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(
        title: Text('StreamBuilder test'),
      ),
      body: Container(
        // height: double.infinity,
        child: Center(
          child: StreamBuilder<UIState>(

              /// The incoming stream
              stream: _controller.stream,
              builder: (BuildContext context, AsyncSnapshot snapshot) {
                if (snapshot.hasData) {
                  /// HasData indicates that an event was received
                  var data = snapshot.data;
                  Widget widget;

                  ///Depending on the type of return, different images are displayed
                  switch (data) {
                    case UIState.type_1:
                      widget = Icon(Icons.timer, size: 100);
                      break;
                    case UIState.type_2:
                      widget = Icon(Icons.done, size: 100);
                      break;
                    case UIState.type_3:
                      widget = Icon(Icons.ac_unit, size: 100);
                      break;
                  }
                  return Column(
                    mainAxisAlignment: MainAxisAlignment.center,
                    children: [
                      widget,
                      Text("$data"),]); }else if (snapshot.hasError) {
                  /// An error event was received
                  return Icon(Icons.error_outline_rounded, size: 100);
                } else {
                  /// Nothing. Does that mean no more received events
                  return Column(
                    mainAxisAlignment: MainAxisAlignment.center,
                    children: [
                      CircularProgressIndicator(),
                      Text("I'm initializing. I haven't received the received state."),]); } }), ), ), floatingActionButton: FloatingActionButton( onPressed: () => generateState(), child: Icon(Icons.add)), ); }/// Randomly generate different states and send events
  generateState() {
    var randomIndex = Random().nextInt(UIState.values.length);
    _controller.add(UIState.values[randomIndex]);
  }

  @override
  void dispose() {
    super.dispose();

    /// recycling_controller.close(); }}Copy the code