Flutter Advanced - Detailed Explanation of Future, FutureBuilder, StreamBuilder

1. Time loop Event Loop mechanism

The reason why the program is stuck is that there is no time to update the UI interface and refresh the screen.

There are two main types of lag:

1. The CPU is too busy with a large amount of calculation

2. Waiting, waiting for the server's response, waiting for the user's input, waiting for the file to be read...etc.

In the multi-threaded mechanism, whenever something needs to be waited for, a new thread is opened to guard it. The main thread responsible for updating the UI will not hang and will not feel stuck.

But in Dart, each thread is encapsulated in an Isolate, each Isolate is isolated and isolated, does not share memory with each other, and communicates with each other by sending messages

run directly Microtask Event

Future.sync()

scheduleMicrotask()

Future()

Future.value()

Future.microtask()

Future.delayed()

_.then() _completed.then()

Two, FutureBuilder 

Refresh the interface by monitoring the change of the passed future value (without using setState)

 @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(
        title: const Text(''),
      ),
      body: FutureBuilder(
        initialData: "haha",
          future:
              Future.delayed(const Duration(seconds: 2), () => throw ('error')),
          builder: (context, snapshot) {
            if (snapshot.hasError) {
              return const Icon(Icons.error);
            }
            if (snapshot.hasData) {
              return Text("${snapshot.data}");
            }
            return const CircularProgressIndicator();
          }),
    );
  }

3. Stream and StreamBuilder

future will only print once, while Stream will continue to print

 final future = Future.delayed(const Duration(seconds: 2), () => 40);
 final stream = Stream.periodic(const Duration(seconds: 1), (_) => 42);


 void test() {
    future.then((value) => print('future completed: $value'));
    stream.listen((event) {
      print("stream: $event");
    });
  }

 @override
  Widget build(BuildContext context) {
    return Scaffold(
        appBar: AppBar(
          title: const Text(''),
        ),
        body: StreamBuilder(
            stream: stream,
            builder: (context, snapshot) {
              switch (snapshot.connectionState) {
                case ConnectionState.none:
                  return const Text("none: 没有数据");
                case ConnectionState.waiting:
                  return const Text("waiting: 等待数据流");
                case ConnectionState.active:
                  if (snapshot.hasError) {
                    return Text("active: 错误${snapshot.error}");
                  } else {
                    return Text("active: 正常${snapshot.data}");
                  }
                case ConnectionState.done:
                  return const Text("active: 数据流已经关闭");
              }
            }));
  }

StreamController manages stream more conveniently 

final StreamController streamController = StreamController();
  
@override
  void dispose() {
    streamController.close();
    super.dispose();
  }

  @override
  void initState() {
    super.initState();
    streamController.sink.add(72);
    streamController.stream.listen((event) {});
  }

  final StreamController streamController = StreamController();
  int count = 10;

  DefaultTextStyle(
                style: Theme.of(context).textTheme.headline4!,
                child: Column(children: [
                  RaisedButton(
                      child: const Text('10'),
                      onPressed: () {
                        count += 10;
                        streamController.sink.add(count);
                      }),
                  StreamBuilder(
                      stream: streamController.stream,
                      builder: (context, snapshot) {
                        switch (snapshot.connectionState) {
                          case ConnectionState.none:
                            return const Text("none: 没有数据");
                          case ConnectionState.waiting:
                            return const Text("waiting: 等待数据流");
                          case ConnectionState.active:
                            if (snapshot.hasError) {
                              return Text("active: 错误${snapshot.error}");
                            } else {
                              return Text("active: 正常${snapshot.data}");
                            }
                          case ConnectionState.done:
                            return const Text("active: 数据流已经关闭");
                        }
                      })
                ]));

 If it is monitored multiple times, it will report an error streamController.stream.listen((event) {});, it is really necessary to use final StreamController streamController = StreamController.broadcast(); to become a broadcast

Example:

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

  final String title;

  @override
  State<MyHomePage> createState() => _MyHomePageState();
}

class _MyHomePageState extends State<MyHomePage> {
  final _controller = StreamController();
  int count = 0;

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(
        title: StreamBuilder(
            stream: _controller.stream,
            builder: (context, snapshot) {
              if (snapshot.hasData) {
                return Text("你输入了: ${snapshot.data.toString()}",
                    style: const TextStyle(
                        fontSize: 24, fontWeight: FontWeight.bold));
              }
              return const Text("等待...");
            }),
      ),
      body: Stack(children: [
        Puzzle(inputStream: _controller.stream),
        Align(
            alignment: Alignment.bottomCenter,
            child: KeyPad(controller: _controller))
      ]
      ),
    );
  }
}

class KeyPad extends StatelessWidget {
  final StreamController controller;
  const KeyPad({Key? key, required this.controller}) : super(key: key);

  @override
  Widget build(BuildContext context) {
    return GridView.count(
        shrinkWrap: true,
        childAspectRatio: 2 / 1,
        crossAxisCount: 3,
        mainAxisSpacing: 10,
        crossAxisSpacing: 10,
        children: List.generate(
            9,
            (index) => FlatButton(
                shape: RoundedRectangleBorder(
                    borderRadius: BorderRadius.circular(10)),
                color: Colors.primaries[index][200],
                onPressed: () {
                  controller.add(index + 1);
                },
                child: Text(
                  "${index + 1}",
                  style: const TextStyle(fontSize: 24),
                ))));
  }
}

class Puzzle extends StatefulWidget {
  final Stream inputStream;
  const Puzzle({Key? key, required this.inputStream}) : super(key: key);

  @override
  State<Puzzle> createState() => _PuzzleState();
}

class _PuzzleState extends State<Puzzle> with SingleTickerProviderStateMixin {
  late int a, b;
  late Color color;

  late AnimationController _controller; //显示动画

  reset() {
    a = Random().nextInt(5) + 1;
    b = Random().nextInt(5);
    color = Colors.primaries[Random().nextInt(Colors.primaries.length)];
  }

  @override
  void initState() {
    reset();
    _controller =
        AnimationController(vsync: this, duration: const Duration(seconds: 10))
          ..forward();
    _controller.addStatusListener((status) {
      if (status == AnimationStatus.completed) {
        reset();
        _controller.forward(from: 0.0);
      }
    });
    widget.inputStream.listen((input) {
      if (input == a + b) {
        _controller.forward(from: 0.0);
      }
    });
    super.initState();
  }

  @override
  Widget build(BuildContext context) {
    return AnimatedBuilder(
        animation: _controller,
        builder: (context, _) {
          return Positioned(
            top: 400 * _controller.value,
            left: 200 * _controller.value,
            child: Container(
              decoration: BoxDecoration(
                  color: color,
                  border: Border.all(color: Colors.black),
                  borderRadius: BorderRadius.circular(24)),
              padding: const EdgeInsets.all(5),
              child: Text(
                "$a + $b",
                style: const TextStyle(fontSize: 24),
              ),
            ),
          );
        });
  }
}

Guess you like

Origin blog.csdn.net/RreamigOfGirls/article/details/131164963
Recommended