在Flutter中,借助 FutureBuilder 组件和 StreamBuilder 组件,可以非常方便地完成异步操作。

一、FutureBuilder

在讲解FutureBuilder之前,你首先要知道Future是什么,了解了这个,后面再了解该组件就轻松许多。

在不同的编程语言中会有不同的名词来定义,在Dart语言中 选择使用Future类型配合async、await关键字来实现异步支持。

Future 表示一个现在不确定,但以后应该可以确定的值。这个值可以是任意类型,如 Future<int>表示一个未来获取到的整型值,Future<String>表示一个未来获取到的字符串。

我们通常会在定时器、网络请求中使用Future,它会有三种状态:uncompleted(未完成)、completed with data(获取到一个数据)、completed with error(捕获到一个错误),所以在实战过程中,我们需要根据这三种状态来判断当前界面应该是怎样的,加载中、数据正常显示、提示错误 重新操作。

小示例:

定义一个getValue方法:

Future<String> getValue() async {
    await Future.delayed(Duration(seconds: 3));
    return "100";
}

结合FutureBuilder组件来调用方法:

FutureBuilder(
  future: getValue(),
   builder:
       (BuildContext context, AsyncSnapshot<String> snapshot) {
     if (snapshot.hasData) {
       return Text(
           "${snapshot.data}",
           style: TextStyle(fontSize: 30, fontWeight: FontWeight.bold),
         );
     } else if (snapshot.hasError) {
       return Text("${snapshot.error}");
     } else {
       return Container(
         height: 50,
         width: 50,
         margin: EdgeInsets.only(top: 10),
         child: CircularProgressIndicator(
           strokeWidth: 8.0,
         )
       );
     }
   }),

我们逐步来分解上面的示例代码:

  1. 这里使用async关键字将函数标为异步函数,这样函数的返回值就会被封装为异步。
  2. FutureBuilder组件的future属性是此组件的必传参数。FutureBuilder组件得到future之后,便开始通过future的then等方法追踪它(监听future执行的结果),当其状态改变时自动调用builder函数重绘。
  3. builder函数。每次绘制时,FutureBuilder都会调用这里的builder回传函数,并提供BuildContext(上下文)和AsyncSnapshot<>(异步快照)。在这里AsyncSnapshot<> 封装的类型就是future参数里的Future<> 所封装的类型。像上例一样,Future返回一个String,那么对应的AsyncSnapshot也是String类型。
  4. AsyncSnapshot 中含有 Future的最新状态及被封装的数据或异常。另外ConnectionState 属性描述了 Future 的四种状态,其分别为 none、waiting、active、done。
  5. 大多数时间做异步操作都是为了获取最终的数据,那么这个数据的获取即是在Future的done(完成)之后,所以我们的逻辑代码可以这样写。
FutureBuilder(
   future: getValue(),
   builder:
       (BuildContext context, AsyncSnapshot<String> snapshot) {
     if(snapshot.connectionState == ConnectionState.done) {
       if (snapshot.hasData) {
         return Text(
           "${snapshot.data}",
           style: TextStyle(fontSize: 30, fontWeight: FontWeight.bold),
         );
       } else {
         return Text("${snapshot.error}");
       }
     }else {
       return Container(
           height: 50,
           width: 50,
           margin: const EdgeInsets.only(top: 10),
           child: const CircularProgressIndicator(
             strokeWidth: 8.0,
           )
       );
     }
   }),

首先判断Future的状态是否完成,未完成的情况下就加载动画组件。加载完成的情况下再进行判断snapshot返回的是正确数据还是异常,此时data和error必有一个且只有一个不是空。

总结一下:在Future完成之前,data和error都为空,Future完成之后,data和error有且仅有一个为空。所以这个时候我们可以不检查Future的状态是否完成,而是直接通过snapshot的hasData数据和hasError异常来判断。如果既没有数据又没有异常,那就是当前的Future还未完成,可直接返回加载动画组件。这个时候的代码就如最开始的那个示例一样直接检查hasData和hasError。

初始值 initialData

在Future完成之前,initialData属性提供一个数据的初始值给FutureBuilder组件使用。在有初始值的情况下,Future完成之前hasData会返回true,并且data中存储着所设置的初始值。当Future完成之后,FutureBuilder组件会自动切换到Future的真实值并重新渲染。

FutureBuilder(
   future: getValue(),
   initialData: "加载中...",
   builder:
       (BuildContext context, AsyncSnapshot<String> snapshot) {
       if (snapshot.hasData) {
         return Text(
           "${snapshot.data}",
           style: TextStyle(fontSize: 30, fontWeight: FontWeight.bold),
         );
       } else {
         return Text("${snapshot.error}");
       }
   }),

二、StreamBuilder

StreamBuilder组件与FutureBuilder组件相同之外的不同之处在于它是一个可以自动跟踪Stream的状态,并在Stream有变化时自动重绘的组件。

那么问题来了,什么是Stream?在这里,我们称之为数据流或事件流。Data Stream、Event Stream。

顾名思义,既然称之为“流”,那可以想象出这是一种不间断的操作。

我们可以通过使用Stream.periodic构造函数,并借助其count参数(当前Stream已被调用的次数,从0开始递增),制作一个一秒加一的计数器数据流。

方法:

Stream<int> counter() {
    return Stream.periodic(
      const Duration(seconds: 1),
      (count) => count
    );
  }

在组件中使用:

StreamBuilder(
    stream: counter(),
    builder: (BuildContext context, AsyncSnapshot<int> snapshot) {
      switch(snapshot.connectionState) {
        case ConnectionState.none:
          return Text("无数据流");
        case ConnectionState.done:
          return Text("数据流关闭");
        case ConnectionState.waiting:
          return Text("等待数据");
        case ConnectionState.active:
          if(snapshot.hasData) {
            return Text("${snapshot.data}");
          }else {
            return Text("${snapshot.error}");
          }
        default:
          throw "connect error";
      }
    })

这种用法有点像js中的 setInterval() 一样。

StreamBuilder组件与FutureBuilder组件用法类似,只是在这里所传的参数不是future,而是stream,同时可以通过builder回传函数 获取Stream的4中状态,编写业务逻辑。用法与FutureBuilder组件中的状态类似,这里不过多重复。

上面这种普通数据流的写法只监听count()方法,如果需要支持多个监听者同时监听,则可以通过控制器的StreamController.broadcast构造函数创建一个广播数据流。实现界面多处位置监听并重绘StreamBuilder组件。

例如:

final _stream = StreamController<int>();
StreamBuilder(
    stream: _stream.stream.map((event) => "value:${event}"),
    builder: (context, snapshot) {
      if(snapshot.connectionState == ConnectionState.done) {
        return Text("close stream");
      }
      if(snapshot.hasData) return Text("${snapshot.data}");
      if(snapshot.hasError) return Text("${snapshot.error}");
      return CircularProgressIndicator();
  }),
  Row(
    children: [
      ElevatedButton(onPressed: () => _stream.add(666), child: Text("添加666")),
    	SizedBox(width: 20,),
     ElevatedButton(onPressed: () => _stream.add(888), child: Text("添加888")),
     SizedBox(width: 20,),
     ElevatedButton(onPressed: () => _stream.close(), child: Text("关闭数据流")),
    ],
  )

在最开始,Stream没有开始释放任何事件,这时StreamBuilder会先渲染一个加载组件,当点击第一个按钮,界面将会显示 value:666 字样。只要不关闭数据流,StreamBuilder就会一直监听,任何一处controller的变化。

到此这篇关于Flutter异步操作实现流程详解的文章就介绍到这了,更多相关Flutter异步操作内容请搜索Devmax以前的文章或继续浏览下面的相关文章希望大家以后多多支持Devmax!

Flutter异步操作实现流程详解的更多相关文章

  1. Flutter中文教程-Cookbook

    Flutter中文网的Cookbook中包含了在编写Flutter应用程序时常见问题及示例。设计基础使用主题共享颜色和字体样式Images显示来自网上的图片用占位符淡入图片使用缓存图Lists创建一个基本list创建一个水平list使用长列表创建不同类型子项的List创建一个gridList处理手势处理点击添加Material触摸水波效果实现滑动关闭导航导航到新页面并返回给新页面传值从新页面返回数据给上一个页面网络从网上获取数据进行认证请求使用WebSockets

  2. android-studio – 未配置Dart SDK

    Initializinggradle…

  3. 安卓 – 从一个扑动的应用程序拨打电话

    或者有更好的选择从我的应用程序拨打电话?

  4. android – 如何在Flutter中添加Webview?

    我知道可以将WebView添加为整页,但找不到任何示例代码.我假设你可以使用PageView作为它的基础,但不知道如何调用本机androidWebView并将其添加到PageView.谁能指出我正确的方向?

  5. android – 如何将消息从Flutter传递给Native?

    如果需要与特定的API/硬件组件进行交互,您如何将Flutter的信息传递回Android/Native代码?是否有任何事件频道可以通过其他方式发送信息或类似于回调?

  6. android – 如何在Flutter App中处理onPause / onResume?

    我是否过于复杂的事情?即使我的用例似乎不需要它,我仍然想知道:如何自己处理onPause/onResume事件?

  7. android – 如何使用Flutter构建Augment Reality应用程序?

    我对Android开发有一些基础知识.最近听说过Flutter并且非常有兴趣研究它.我想知道是否有可能使用颤振构建增强现实应用程序以及要实现此目的的方法?请帮忙.解决方法截至目前,颤振不支持3D.Flutter现在专注于2D,团队长期计划为颤振提供优化的3Dapi.你读了常见问题here.

  8. Flutter 网络请求框架封装详解

    这篇文章主要介绍了Flutter 网络请求框架封装详解,小编觉得挺不错的,现在分享给大家,也给大家做个参考。一起跟随小编过来看看吧

  9. 详解koa2学习中使用 async 、await、promise解决异步的问题

    这篇文章主要介绍了详解koa2学习中使用 async 、await、promise解决异步的问题,小编觉得挺不错的,现在分享给大家,也给大家做个参考。一起跟随小编过来看看吧

  10. Ajax简单的异步交互及Ajax原生编写

    一提到异步交互大家就会说ajax,仿佛ajax这个技术已经成为了异步交互的代名词.那下面将研究ajax的核心对象

随机推荐

  1. Flutter 网络请求框架封装详解

    这篇文章主要介绍了Flutter 网络请求框架封装详解,小编觉得挺不错的,现在分享给大家,也给大家做个参考。一起跟随小编过来看看吧

  2. Android单选按钮RadioButton的使用详解

    今天小编就为大家分享一篇关于Android单选按钮RadioButton的使用详解,小编觉得内容挺不错的,现在分享给大家,具有很好的参考价值,需要的朋友一起跟随小编来看看吧

  3. 解决android studio 打包发现generate signed apk 消失不见问题

    这篇文章主要介绍了解决android studio 打包发现generate signed apk 消失不见问题,具有很好的参考价值,希望对大家有所帮助。一起跟随小编过来看看吧

  4. Android 实现自定义圆形listview功能的实例代码

    这篇文章主要介绍了Android 实现自定义圆形listview功能的实例代码,本文通过实例代码给大家介绍的非常详细,对大家的学习或工作具有一定的参考借鉴价值,需要的朋友可以参考下

  5. 详解Android studio 动态fragment的用法

    这篇文章主要介绍了Android studio 动态fragment的用法,本文给大家介绍的非常详细,对大家的学习或工作具有一定的参考借鉴价值,需要的朋友可以参考下

  6. Android用RecyclerView实现图标拖拽排序以及增删管理

    这篇文章主要介绍了Android用RecyclerView实现图标拖拽排序以及增删管理的方法,帮助大家更好的理解和学习使用Android,感兴趣的朋友可以了解下

  7. Android notifyDataSetChanged() 动态更新ListView案例详解

    这篇文章主要介绍了Android notifyDataSetChanged() 动态更新ListView案例详解,本篇文章通过简要的案例,讲解了该项技术的了解与使用,以下就是详细内容,需要的朋友可以参考下

  8. Android自定义View实现弹幕效果

    这篇文章主要为大家详细介绍了Android自定义View实现弹幕效果,文中示例代码介绍的非常详细,具有一定的参考价值,感兴趣的小伙伴们可以参考一下

  9. Android自定义View实现跟随手指移动

    这篇文章主要为大家详细介绍了Android自定义View实现跟随手指移动,文中示例代码介绍的非常详细,具有一定的参考价值,感兴趣的小伙伴们可以参考一下

  10. Android实现多点触摸操作

    这篇文章主要介绍了Android实现多点触摸操作,实现图片的放大、缩小和旋转等处理,文中示例代码介绍的非常详细,具有一定的参考价值,感兴趣的小伙伴们可以参考一下

返回
顶部