FutureBuilder是一个可以自动追踪Future的状态并在其状态改变的时候自动重绘的组件。

FutureBuilder追踪Future的状态

  1. 定义一个模拟请求数据的异步方法
  2. 数据加载的时候加载一个Indicator,数据加载完毕后显示数据
import 'dart:math';

import 'package:flutter/material.dart';

class FormPage extends StatefulWidget {
  final Map arguments;
  const FormPage({
    super.key,
    required this.arguments,
  });

  @override
  State<FormPage> createState() => _FormPageState();
}

class _FormPageState extends State<FormPage> {
  Future<String> loadDate() async {
    await Future.delayed(const Duration(seconds: 3));
    return '我是表单页面的数据';
  }

  @override
  Widget build(BuildContext context) {
    return Scaffold(
        appBar: AppBar(
          title: Text(widget.arguments['title']),
        ),

        body: Center(
          child: FutureBuilder(
            future: loadDate(), 
            builder: (BuildContext context, AsyncSnapshot<String> snapshot) { 
              print(snapshot.connectionState);
              if (snapshot.connectionState == ConnectionState.done) {
                if (snapshot.hasError) {
                  return Text('Error: ${snapshot.error}');
                } else {
                  return Text('Contents: ${snapshot.data}');
                }
              } else {
                return const CircularProgressIndicator();
              }
            },
          ),
        )
      );
  }
}

FutureBuilder initialData

import 'dart:math';

import 'package:flutter/material.dart';

class FormPage extends StatefulWidget {
  final Map arguments;
  const FormPage({
    super.key,
    required this.arguments,
  });

  @override
  State<FormPage> createState() => _FormPageState();
}

class _FormPageState extends State<FormPage> {
  Future<String> loadDate() async {
    await Future.delayed(const Duration(seconds: 3));
    return '我是表单页面的数据';
  }

  @override
  Widget build(BuildContext context) {
    return Scaffold(
        appBar: AppBar(
          title: Text(widget.arguments['title']),
        ),

        body: Center(
          child: FutureBuilder(
            future: loadDate(), 
            initialData: '我是初始化的数据',
            builder: (BuildContext context, AsyncSnapshot<String> snapshot) { 
              if(snapshot.hasError) {
                return Text('Error: ${snapshot.error}');
              }else {
                return Text('Contents: ${snapshot.data}');
              }
            },
          ),
        )
      );
  }
}

Stream 、StreamBuilder

前面章节我们给大家介绍了Future,Future在未来只会获取一个值。Stream的字面意思是水流,Stream不像Future那样只会在未来获取一个值,它可以异步获取0个或者多个值。如果说Future是一个异步版本的int或者String,Stream则更像是异步版本的列表,List,List,列表里面可能会有0个或者多个元素。

final future=Future.delayed(const Duration(seconds: 1),()=>43);
final stream=Stream.periodic(const Duration(seconds: 1),(value)=>value);
@override
void initState() {
  super.initState();
  future.then((value) => print(value));
  stream.listen((event) {
    print("stream:$event");
  });
}
import 'dart:math';

import 'package:flutter/material.dart';

class FormPage extends StatefulWidget {
  final Map arguments;
  const FormPage({
    super.key,
    required this.arguments,
  });

  @override
  State<FormPage> createState() => _FormPageState();
}

class _FormPageState extends State<FormPage> {
  Future<String> loadDate() async {
    return '我是表单页面的数据';
  }

  Stream<int> loadStreamDate() {
    return Stream.periodic(Duration(seconds: 1), (v) => v);
  }

  @override
  void initState() {
    // TODO: implement initState
    super.initState();

    // 初始化数据
    loadStreamDate().listen((event) {
      print(event);
    });
  }

  @override
  Widget build(BuildContext context) {
    return Scaffold(
        appBar: AppBar(
          title: Text(widget.arguments['title']),
        ),

        body: Center(),
      );
  }
}

StreamBuilder

在 Flutter 中,StreamBuilder 是一个将 Stream 流与 Widget结合到一起的组件,可实现组件的局部数据更新 , StreamBuilder 组件和FutureBuilder组件比较相似,不同点在于它是一个可以自动跟踪Stream(数据流或事件流)的状态,并在Stream有变化时自动重绘的组件。Stream不同于Future,可能会在生命周期内释放出任意数量的数据值(正常)或者错误信息(异常),通常被用于读取文件或者下载网络资源等操作,也有时候用于状态管理。

StreamBuilder主要功能:

  1. 实现局部刷新
  2. 读取流实现读取文件或者下载网络资源等操作
  3. 父子组件之间的数据广播

StreamBuilder 实现一个计数器

import 'dart:math';

import 'package:flutter/material.dart';

class FormPage extends StatefulWidget {
  final Map arguments;
  const FormPage({
    super.key,
    required this.arguments,
  });

  @override
  State<FormPage> createState() => _FormPageState();
}

class _FormPageState extends State<FormPage> {
  Future<String> loadDate() async {
    return '我是表单页面的数据';
  }

  Stream<int> loadStreamDate() {
    return Stream.periodic(Duration(seconds: 1), (v){
      if(Random().nextBool()){
        return v;
      }
      throw Exception('随机数异常');
    });
  }

  @override
  void initState() {
    // TODO: implement initState
    super.initState();
  }

  @override
  Widget build(BuildContext context) {
    return Scaffold(
        appBar: AppBar(
          title: Text(widget.arguments['title']),
        ),

        body: Center(
          child: StreamBuilder(
          stream: loadStreamDate(),
          builder: (BuildContext context, AsyncSnapshot<dynamic> snapshot) { 
            if(snapshot.hasError){
              return Text('Error: ${snapshot.error}');
            }
            switch (snapshot.connectionState) {
              case ConnectionState.none:
                return Text('没有Stream');
              case ConnectionState.waiting:
                return Text('等待数据...');
              case ConnectionState.active:
                return Text('active: ${snapshot.data}');
              case ConnectionState.done:
                return Text('Stream已关闭');
            }
         },
        ),
        ),
      );
  }
}

StreamBuilder 实时显当前的时间

import 'dart:math';

import 'package:flutter/material.dart';

class FormPage extends StatefulWidget {
  final Map arguments;
  const FormPage({
    super.key,
    required this.arguments,
  });

  @override
  State<FormPage> createState() => _FormPageState();
}

class _FormPageState extends State<FormPage> {

  Stream<String> counter() {
    return Stream.periodic(const Duration(seconds: 1), (count){
      return "${DateTime.now().hour}:${DateTime.now().minute}:${DateTime.now().second}";
    });
  }

  @override
  void initState() {
    // TODO: implement initState
    super.initState();
  }

  @override
  Widget build(BuildContext context) {
    return Scaffold(
        appBar: AppBar(
          title: Text(widget.arguments['title']),
        ),

        body: Center(
          child: StreamBuilder(
          stream: counter(),
          builder: (BuildContext context, AsyncSnapshot<dynamic> snapshot) { 
            if(snapshot.hasError){
              return Text('Error: ${snapshot.error}');
            }
            switch (snapshot.connectionState) {
              case ConnectionState.none:
                return Text('没有Stream');
              case ConnectionState.waiting:
                return Text('等待数据...');
              case ConnectionState.active:
                return Text('active: ${snapshot.data}');
              case ConnectionState.done:
                return Text('Stream已关闭');
            }
         },
        ),
        ),
      );
  }
}

StreamController

一般情况我们都是监听别人给我们的数据流,比如File("").openRead() 读取文件, 刚才给大家讲了Stream.periodic 可以创建数据流,如果我们想创建更精确的数据流的话也可以使用StreamController

注意: import 'dart:async'; // 需要导入异步包

StreamController.broadcast()

StreamController 默认只能有一个监听这,如果有多个监听者的话就需要用StreamController.broadcast()

Stream过滤

flutter 中async和async*

  • async 返回Future
  • async* 返回Stream

async不必多言,有了解的都知道这是异步调用。当一个函数被标记成async的时候,意味这个方法可能要从事耗时工作,比如说网络请求、处理图片等等。被async标记的方法会把返回值用Future包裹一下。

async

async*

StreamBuilder 打字小游戏

import 'dart:async';
import 'dart:math';

import 'package:flutter/material.dart';

class FormPage extends StatefulWidget {
  final Map arguments;
  const FormPage({
    super.key,
    required this.arguments,
  });

  @override
  State<FormPage> createState() => _FormPageState();
}

class _FormPageState extends State<FormPage> {
  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(
        title: Text(widget.arguments['title']),
      ),
      body: const TypeWritingPage(),
    );
  }
}

class TypeWritingPage extends StatefulWidget {
  const TypeWritingPage({super.key});

  @override
  State<TypeWritingPage> createState() => _TypeWritingPageState();
}

class _TypeWritingPageState extends State<TypeWritingPage> {
  StreamController<int> _inputController = StreamController.broadcast();


  @override
  Widget build(BuildContext context) {
    return Stack(
      children: [
        ...List.generate(3, (index) {
          return Game(inputController: _inputController);
        }),
        // Positioned(
        //   child: StreamBuilder<int>(
        //     stream: _inputController.stream,
        //     builder: (context, snapshot) {
        //       if(snapshot.hasData){
        //         return Text('${snapshot.data}');
        //       }
        //       if(snapshot.hasError){
        //         return Text('${snapshot.error}');
        //       }
        //       return const Text('监听中...');
        //     },
        //   ),
        // ),
        KeyPad(inputController: _inputController),
      ],
    );
  }
}


class Game extends StatefulWidget {
  final StreamController<int> inputController;
  const Game({super.key, required this.inputController});

  @override
  State<Game> createState() => _GameState();
}

class _GameState extends State<Game> with SingleTickerProviderStateMixin {
  late int a,b;
  late double x;
  late Color color;
  late AnimationController _animationController;

  void reset(){
    setState(() {
      a = Random().nextInt(5)+1;
      b = Random().nextInt(5);
      x = Random().nextDouble()*320;
      color = Colors.primaries[Random().nextInt(Colors.primaries.length)][300]!;
    });
  }

  @override
  void initState() {
    reset();
    widget.inputController.stream.listen((event) {
      print(event);
      if(event == a+b){
        _animationController.stop();
        reset();
        _animationController.reset();
        _animationController.forward();
      }
    });
    color = Colors.primaries[Random().nextInt(Colors.primaries.length)][300]!;
    super.initState();
    _animationController = AnimationController(
        duration: Duration(milliseconds: Random().nextInt(5000)+5000), vsync: this)..forward();

    _animationController.addStatusListener((status) {
      if(status == AnimationStatus.completed){
        reset();
        _animationController.reset();
        _animationController.forward();
      }
    });
  }

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



  @override
  Widget build(BuildContext context) {
    return AnimatedBuilder(
      animation: _animationController, 
      builder: (BuildContext context, Widget? child) { 
        return Positioned(
          top: Tween(begin: -50.0, end: 500.0).animate(_animationController).value,
          left: x,
          child: Container(
            padding: const EdgeInsets.fromLTRB(8, 5, 8, 5),
            decoration: BoxDecoration(
              color: color,
              borderRadius: BorderRadius.circular(18),
            ),
            child:  Text(
              '$a+$b=?',
              style: const TextStyle(
                fontSize: 24,
                color: Colors.white,
              ),
            )
          )
        );
      },
    );
  }
}


// 键盘
class KeyPad extends StatelessWidget {
  final StreamController<int> inputController;
  const KeyPad({super.key, required this.inputController});

  @override
  Widget build(BuildContext context) {
    return Align(
          alignment: Alignment.bottomCenter,
          child: Container(
            margin: const EdgeInsets.only(top: 100),
            child: GridView.count(
              shrinkWrap: true,
              crossAxisCount: 3,
              childAspectRatio: 5 / 2,
              children: List.generate(9, (index) {
                return TextButton(
                  onPressed: () {
                    inputController.add(index + 1);
                  },
                  style: ButtonStyle(
                    backgroundColor:
                        MaterialStateProperty.all(Colors.primaries[index][300]),
                    foregroundColor: MaterialStateProperty.all(Colors.white),
                    shape: MaterialStateProperty.all(
                        const RoundedRectangleBorder()),
                  ),
                  child: Text(
                    '${index + 1}',
                    style: const TextStyle(
                      fontSize: 32,
                      color: Colors.white,
                    ),
                  ),
                );
              }),
            ),
          ),
        );
  }
}

results matching ""

    No results matching ""