Future (asynchronous operation)
Future has three statuses: unfinished, completed with value and completed with exception. Using future can simplify event tasks. In Dart, future objects can be used to represent the results of asynchronous operations. The return type of future is future < T >
There are three ways to deal with the results of Future:
- then: process the operation execution result or error and return a new Future
- Catchrror: register a callback to handle errors
- whenComplete: similar to final, Future will always be called after execution regardless of error or correctness
Using then to callback, the scenario uses: the UI needs interface data, and some functions are executed asynchronously
# demo1 main() { Future f1 = new Future(() { print("I was the first"); }); f1.then((_) => print("f1 then")); print("I am main"); } # print: # I'm main # I was the first # f3 then
Future is used in conjunction with async/await
Using chained calls to connect multiple future s together will seriously reduce the readability of the code. async and await keywords are used to implement asynchronous functions. async and await can help us write asynchronous code like synchronous code
Future<String> getStr()async{ var str = HttpRequest.getString('www.fgyong.cn'); return str; }
Use the http request address www.fgyong.cn to obtain data, and then return. How do I receive text?
In fact, it is very simple. You only need to use the await keyword to register the then callback.
main(List<String> args) async { String string = await getStr(); print(string); }
Equivalent to
main(List<String> args) async { getStr().then((value) { print(value); }); }
stream -- continuous asynchronous operation
If Future represents the result of a single calculation, the flow is a series of results.
Listen to the stream for notifications about the results (data and errors) and the closure of the stream. You can also pause playback while listening to the stream or stop listening until the stream is complete.
It can be said that Future Used to handle a single asynchronous operation, Stream Used to handle continuous asynchronous operations.
Stream It is divided into subscription stream and broadcast stream.
A single subscription stream is allowed to set only one listener before sending the completion event, and the event will not be generated until the listener is set on the stream. The event will be stopped after the listener is cancelled. Even if the first listener is cancelled, it is not allowed to set other listeners on a single subscription stream. Broadcast stream allows multiple listeners to be set, or a new listener can be added after canceling the previous listener.
Stream There are synchronous flow and asynchronous flow.
The difference between them is that the synchronization flow will be executed in add,addError or close Method immediately to the streaming listener StreamSubscription Send events, and asynchronous flow always sends events after the code execution in the event queue is completed.
There are several ways to create in Dart Stream
- use async* Function, function marked as Async *, we can use yield As a keyword and returns Stream data
- Generate a new stream from an existing Stream, using map,where,takeWhile And other methods.
Stream<int> countStream(int to) async* { for (int i = 1; i <= to; i++) { yield i; } } Stream stream = countStream(10); stream.listen(print);
- use StreamController.
StreamController<Map> _streamController = StreamController( onCancel: () {}, onListen: () {}, onPause: () {}, onResume: () {}, sync: false, ); Stream _stream = _streamController.stream;
- use Future Object generation
Future<int> _delay(int seconds) async { await Future.delayed(Duration(seconds: seconds)); return seconds; } List<Future> futures = []; for (int i = 0; i < 10; i++) { futures.add(_delay(3)); } Stream _futuresStream = Stream.fromFutures(futures);
In the actual development process, we basically use the StreamContoller to create streams. Monitoring uses StreamBuilder to execute when the flow changes, for example:
import 'dart:async'; import 'package:flutter/material.dart'; class StreamCounter extends StatefulWidget { @override _StreamCounterState createState() => _StreamCounterState(); } class _StreamCounterState extends State<StreamCounter> { // Create a StreamController StreamController<int> _counterStreamController = StreamController<int>( onCancel: () { print('cancel'); }, onListen: () { print('listen'); }, ); int _counter = 0; Stream _counterStream; StreamSink _counterSink; // Send events to Stream using StreamSink when_ When the counter is greater than 9, call the close method to close the flow. void _incrementCounter() { if (_counter > 9) { _counterSink.close(); return; } _counter++; _counterSink.add(_counter); } // Active shutdown flow void _closeStream() { _counterStreamController.close(); } @override void initState() { super.initState(); _counterSink = _counterStreamController.sink; _counterStream = _counterStreamController.stream; } @override void dispose() { super.dispose(); _counterSink.close(); _counterStreamController.close(); } @override Widget build(BuildContext context) { return Scaffold( appBar: AppBar( title: Text('Stream Counter'), ), body: Center( child: Column( mainAxisAlignment: MainAxisAlignment.center, children: <Widget>[ Text('You have pushed the button this many times:'), // Displaying and updating the UI using StreamBuilder StreamBuilder<int>( stream: _counterStream, initialData: _counter, builder: (context, snapshot) { if (snapshot.connectionState == ConnectionState.done) { return Text( 'Done', style: Theme.of(context).textTheme.bodyText2, ); } int number = snapshot.data; return Text( '$number', style: Theme.of(context).textTheme.bodyText2, ); }, ), ], ), ), floatingActionButton: Row( mainAxisAlignment: MainAxisAlignment.center, children: <Widget>[ FloatingActionButton( onPressed: _incrementCounter, tooltip: 'Increment', child: Icon(Icons.add), ), SizedBox(width: 24.0), FloatingActionButton( onPressed: _closeStream, tooltip: 'Close', child: Icon(Icons.close), ), ], ), ); } }
bloc - state management
From the above, we know that stream is a necessary knowledge to use bloc. A basic use of bloc is cubit. Cubit is similar to the simplified version of bloc. Cubit can be used on some small projects. It is best to use bloc for the data maintained by the project. The following is the core knowledge and use cases of bloc
bloc processing diagram:
- Bloc mode
- bloc: logical layer
- state: data layer
- event: all interactive events
- view: page
StreamController and StreamBuilder: the combination of the two can easily refresh local widgets. Counting example
- view: the Stream stream must have a close operation. Here you need to use StatefulWidget and its dispose callback
class StreamPage extends StatefulWidget { const StreamPage({Key? key}) : super(key: key); @override _StreamPageState createState() => _StreamPageState(); } class _StreamPageState extends State<StreamPage> { final logic = StreamLogic(); @override Widget build(BuildContext context) { return Scaffold( appBar: AppBar(title: Text('Bloc-Bloc example')), body: Center( child: StreamBuilder<StreamState>( initialData: logic.state, stream: logic.stream, builder: (context, snapshot) { return Text( 'Click ${snapshot.data!.count} second', style: TextStyle(fontSize: 30.0), ); }, ), ), floatingActionButton: FloatingActionButton( onPressed: () => logic.increment(), child: Icon(Icons.add), ), ); } @override void dispose() { logic.dispose(); super.dispose(); } }
- logic: the Stream data source is generic and can directly use the basic type. The entity is used here to extend more data later
class StreamLogic { final state = StreamState(); // Instantiate flow controller final _controller = StreamController<StreamState>.broadcast(); Stream<StreamState> get stream => _controller.stream; void increment() { _controller.add(state..count = ++state.count); } void dispose() { // Close the flow controller and free up resources _controller.close(); } }
- state
class StreamState { int count = 0; }