Flutter中的异步(Future、async、await、FutureBuilder)和 网络请求

Flutter 异步编程(Future、async、await)

在Android开发中,异步编程是必不可少的,好比网络请求、IO操做等不少都是异步操做,而在Android原生中,有主线程和工做线程的概念,耗时操做都是要放到工做线程中的,ui要在主线程中更新,所以,原生Android开发中对线程的处理是必不可少的,幸运的是,一些第三方库例如Rxjava、RxAndroid让咱们的线程切换起来十分的方便。html

可是Flutter是基于Datt语言实现的,而Dart中的代码是在一个线程中运行的,所以,Flutter也是单线程模型的。
参考资料能够看官方说明:Dart异步编程
这里不由有一个问题,Flutter为啥要用Dart呢?单线程会不会比多线程的效率低呢?
在这里插入图片描述java

先无论效率的问题了,实际运行起来速度仍是很快的。git

也就是说,咱们在Flutter中对异步的处理并非像原生Android那样是多个线程去处理的。
那么,Flutter中是怎么处理异步操做的呢?github

Flutter给咱们提供了Future对象以及asyncawait关键字来支持异步编程。
咱们首先来看看 Futureweb

Future是一个抽象类,咱们经常使用的方法以下
在这里插入图片描述编程

Future对象表示异步操做的结果,咱们一般经过then()来处理返回的结果
async用于标明函数是一个异步函数,其返回值类型是Future类型
await用来等待耗时操做的返回结果,这个操做会阻塞到后面的代码api


Flutter中的网络请求

网络请求是很是典型的异步任务,下面咱们就来结合网络请求来看看Flutter中的异步是如何使用的。网络

网络请求的方式有不少,这里我就直接用目前比较好用的DIO网络请求库 了,你也可使用官方文档中的网络请求 ,都是能够的。多线程

下面咱们来简单用一用网络请求。app

这里我使用的聚合上的一个接口

接口地址:http://v.juhe.cn/toutiao/index?type=keji&key=4c52313fc9247e5b4176aed5ddd56ad7

关于DIO如何使用这里就不讲了,Github上文档很详细,使用起来也很简单。
下面咱们直接用:

首先要先导包

import 'package:dio/dio.dart';

请求接口获取数据的方法

/**
 * 请求接口获取数据
 */
Future<Response> getData() async {
  String url = "http://v.juhe.cn/toutiao/index";
  String key = "4c52313fc9247e5b4176aed5ddd56ad7";
  String type = "keji";

  print("开始请求数据");
  Response response =
      await Dio().get(url, queryParameters: {"type": type, "key": key});

  print("请求完成");

  return response;
}

注意一下几点:

  1. 网络请求是耗时操做
  2. 要使用async来标明getData这个函数是一个异步函数
  3. await 用于等待请求返回的结果,此时会阻塞掉后面的代码,只有当请求结束后面的代码才会执行
  4. async标注的函数其返回值类型是Future

而后咱们就能够在main函数中来接收网络请求后的结果了:

main() {
  getData().then((result) {
    print("接口返回的数据是:${result}");
  }).whenComplete((){
    print("异步任务处理完成");
  }).catchError((){
    print("出现异常了");
  });

  print("我是在请求数据后面的代码呦!");
}

咱们来看看执行的结果:在这里插入图片描述

这样一来,咱们就完成了Flutter中的异步操做了,能够看到,相对于原生Android来说,Flutter中的异步是很是简单的。


Flutter FutureBuilder

FutureBuilder 实际上就是对Future进行封装的一个Widget。咱们先来看看他的构造方法

const FutureBuilder({
    Key key,
    this.future,  
    this.initialData,
    @required this.builder
  })

其中,
future接收Future<T>类型的值,实际上就是咱们的异步函数,一般状况下都是网络请求函数
initialData 表示在异步函数执行完成以前能够给快照进行使用,简单理解就是初始数据,应该不是很经常使用
builder:接收一个AsyncWidgetBuilder<T>类型的值,看源码

/// Signature for strategies that build widgets based on asynchronous
/// interaction.
///
/// See also:
///
///  * [StreamBuilder], which delegates to an [AsyncWidgetBuilder] to build
///    itself based on a snapshot from interacting with a [Stream].
///  * [FutureBuilder], which delegates to an [AsyncWidgetBuilder] to build
///    itself based on a snapshot from interacting with a [Future].
typedef AsyncWidgetBuilder<T> = Widget Function(BuildContext context, AsyncSnapshot<T> snapshot);

AsyncWidgetBuilder为构建器提供了一个AsyncSnapshot对象,咱们再来看看AsyncSnapshot的源码
在这里插入图片描述

下图是AsyncSnapshot中的属性和方法
在这里插入图片描述

AsyncSnapshot中封装了connectionState(链接状态)data(实际上就是future执行后返回的数据)以及error(实际上就是future错误时返回的错误信息)

dataerror比较好理解,咱们主要来看看connectionState
connectionState是一个enum 类型的值,其源码以下

enum ConnectionState {
  /// Not currently connected to any asynchronous computation.
  ///
  /// For example, a [FutureBuilder] whose [FutureBuilder.future] is null.
  none,

  /// Connected to an asynchronous computation and awaiting interaction.
  waiting,

  /// Connected to an active asynchronous computation.
  ///
  /// For example, a [Stream] that has returned at least one value, but is not
  /// yet done.
  active,

  /// Connected to a terminated asynchronous computation.
  done,
}
  • none :当前未链接到任何异步计算。
  • waiting : 链接成功等待交互
  • active :正在交互中,能够理解为正在返回数据
  • done :交互完成,能够理解为数据返回完成,此时若是是正确的返回则data就有数据了

搞清楚这些,咱们就能够开心的使用FutureBuilder了。


Flutter 请求网络数据时显示加载中

一个很常见的需求,在首次进入页面时,此时数据还须要从网络上获取,咱们但愿在网络请求完成以前显示一个加载页面,请求完成以后再显示数据。此时,咱们就可使用FutureBuilder来完成了

首先是接口请求函数,为了更明显的能看到加载控件的显示,这里的异步请求函数中我延时3秒后再请求数据,代码以下

import 'package:dio/dio.dart';
import 'package:flutter/material.dart';
import 'package:flutter_async/widget/loading.dart';

void main() => runApp(MyApp());

class MyApp extends StatelessWidget {
  // This widget is the root of your application.
  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      title: '新闻列表',
      theme: ThemeData(
        primarySwatch: Colors.blue,
      ),
      home: MyHomePage(title: '新闻列表'),
    );
  }
}

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

  final String title;

  @override
  _MyHomePageState createState() => _MyHomePageState();
}

class _MyHomePageState extends State<MyHomePage> {
  @override
  Widget build(BuildContext context) {
    return Scaffold(
        appBar: AppBar(
          title: Text(widget.title),
        ),
        body: FutureBuilder(
          future: _getNews(),
          builder: (BuildContext context, AsyncSnapshot<Response> snapshot) {
            /*表示数据成功返回*/
            if (snapshot.hasData) {
              Response response = snapshot.data;
              return Text("${response.data.toString()}");
            } else {
              return LoadingWidget();
            }
          },
        ));
  }
}


/**
 * 请求接口获取数据
 */
Future<Response> _getNews() async {
  await Future.delayed(Duration(seconds: 3), () {
    print("延时三秒后请求数据");
  });

  String url = "http://v.juhe.cn/toutiao/index";
  String key = "4c52313fc9247e5b4176aed5ddd56ad7";
  String type = "keji";

  print("开始请求数据");
  Response response =
      await Dio().get(url, queryParameters: {"type": type, "key": key});

  print("请求完成");

  return response;
}

运行效果:
在这里插入图片描述

下面是demo,须要的能够下载:
flutter_async Demo


若是你以为本文对你有帮助,麻烦动动手指顶一下,算是对本文的一个承认。也能够关注个人 Flutter 博客专栏,我会不按期的更新,若是文中有什么错误的地方,还望指正,转载请注明转自喻志强的博客 ,谢谢!