美文网首页
FutureBuilder

FutureBuilder

作者: 沉江小鱼 | 来源:发表于2019-09-26 10:01 被阅读0次

    很多时候我们会依赖一些异步数据来动态更新UI,比如在打开一个页面时我们需要先从互联网上获取数据,在获取数据的过程中我们显式一个加载框,等获取到数据时我们再渲染页面;又比如我们想展示Stream(比如文件流、互联网数据接收流)的进度。当然,通过StatefulWidget使用setState方法,我们完全可以实现上述这些功能。但由于在实际开发中依赖异步数据更新UI的这种场景非常常见,因此Flutter专门提供了FutureBuilder和StreamBuilder两个组件来快速实现这种功能。

    FutureBuilder
    FutureBuilder会依赖一个Future,它会根据所依赖的Future的状态来动态构建自身。我们看一下FutureBuilder构造函数:

    FutureBuilder({
      this.future,
      this.initialData,
      @required this.builder,
    })
    
    • future
      FutureBuilder所依赖的future,通常是一个异步任务
    • initialData
      用户提供给的默认数据
    • builder
      Widget构建器,该构建器会在Future执行的不同阶段被多次调用,构建器签名如下:
    typedef AsyncWidgetBuilder<T> = Widget Function(BuildContext context, AsyncSnapshot<T> snapshot);
    

    AsyncSnapshot类型的snapshot会包含当前异步任务的状态信息及结果信息 ,比如我们可以通过snapshot.connectionState获取异步任务的状态信息、通过snapshot.hasError判断异步任务是否有错误等等。
    另外,FutureBuilder的builder函数签名和StreamBuilder的builder是相同的。

    我们可以一起看下AsyncSnapshot这个类,常用的几个方法如下:

    final T data; // 所持有的数据
    final Object error; //  错误信息
    final ConnectionState connectionState; // 当前的状态
    bool get hasData => data != null;   // 是否有数据
    bool get hasError => error != null; // 是否有错误
    
    // 几个构造函数
    const AsyncSnapshot.withData(ConnectionState state, T data) : this._(state, data, null);
    const AsyncSnapshot.withError(ConnectionState state, Object error) : this._(state, null, error);
    const AsyncSnapshot.nothing() : this._(ConnectionState.none, null, null);
    const AsyncSnapshot._(this.connectionState, this.data, this.error)
        : assert(connectionState != null),
          assert(!(data != null && error != null));
    

    ConnectionState的几种状态说明:

    enum ConnectionState {
      /// 当前没有异步任务,比如[FutureBuilder]的[future]为null时
      none,
    
      /// 异步任务处于等待状态
      waiting,
    
      /// Stream处于激活状态(流上已经有数据传递了),对于FutureBuilder没有该状态。
      active,
    
      /// 异步任务已经终止.
      done,
    }
    

    注意,ConnectionState.active只在StreamBuilder中才会出现。

    在_FutureBuilderState的实现中:

      AsyncSnapshot<T> _snapshot;
    @override
      void initState() {
        super.initState();
        // 创建一个AsyncSnapshot,此时状态为none,其中的data为用户设置的初始数据
        _snapshot = AsyncSnapshot<T>.withData(ConnectionState.none, widget.initialData);
        _subscribe();
      }
    

    从上面我们可以看到_snapshot对象默认是有数据的,即它的data为 widget.initalData(如果用户提供了初始数据),所以这种情况下,我们通过 _snapshot.hasData 去判断是否有数据,不够严谨,应该先判断_snapshot. connectionState,再去判断_snapshot.hasData。

    下面为一个实例:

    我们实现一个路由,当该路由打开时我们从网上获取数据,获取数据时弹一个加载框;获取结束时,如果成功则显示获取到的数据,如果失败则显示错误。由于我们还没有介绍在flutter中如何发起网络请求,所以在这里我们不真正去网络请求数据,而是模拟一下这个过程,隔3秒后返回一个字符串:

    Future<String> mockNetworkData() async {
      return Future.delayed(Duration(seconds: 2), () => "我是从互联网上获取的数据");
    }
    
    FutureBuilder使用代码如下:
    ...
    Widget build(BuildContext context) {
      return Center(
        child: FutureBuilder<String>(
          future: mockNetworkData(),
          builder: (BuildContext context, AsyncSnapshot snapshot) {
            // 请求已结束
            if (snapshot.connectionState == ConnectionState.done) {
              if (snapshot.hasError) {
                // 请求失败,显示错误
                return Text("Error: ${snapshot.error}");
              } else {
                // 请求成功,显示数据
                return Text("Contents: ${snapshot.data}");
              }
            } else {
              // 请求未结束,显示loading
              return CircularProgressIndicator();
            }
          },
        ),
      );
    }
    

    相关文章

      网友评论

          本文标题:FutureBuilder

          本文链接:https://www.haomeiwen.com/subject/rvpvuctx.html