在 Flutter 开发中,异步编程和网络请求是两个非常重要的技能点。无论是处理耗时操作,还是与后端 API 进行交互,掌握这些技术都能让你的 Flutter 应用更加高效和流畅。学习Flutter 中异步编程(Future、async、await)以及网络请求的相关知识可以来帮助大家更好地理解和应用。

一、Flutter 的单线程模型与异步编程基础

与原生 Android 开发不同,Flutter 是基于 Dart 语言实现的,而 Dart 代码是在一个线程中运行的,这意味着 Flutter 采用的是单线程模型。那么问题来了,单线程会不会影响效率呢?其实,实际运行起来速度还是很快的,而且 Dart 提供了强大的异步编程机制,让我们能够轻松处理各种耗时操作,而不会阻塞主线程。

在 Dart 中,异步编程主要依赖于 Future 对象以及 async 和 await 关键字。Future 是一个抽象类,它表示异步操作的结果。我们通常通过 then() 方法来处理返回的结果。而 async 用于标明一个函数是异步的,其返回值类型是 Future 类型。await 则用来等待耗时操作的返回结果,这个操作会阻塞后面的代码执行,直到异步操作完成。

二、网络请求的实现

网络请求是典型的异步任务,在 Flutter 中,我们可以使用 Dio 网络请求库来方便地进行网络请求。Dio 是一个功能强大且易于使用的 HTTP 客户端库,支持拦截器、请求取消、超时设置等多种功能。

首先,我们需要在 pubspec.yaml 文件中添加 Dio 的依赖:

dependencies:
  dio: ^4.0.0

然后,在 Dart 代码中导入 Dio 包:

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;
}

在这个示例中,我们定义了一个异步函数 getData(),使用 async 标明其为异步函数。在函数内部,我们使用 await 来等待 Dio 的 get 请求返回结果。这样,当网络请求完成之前,后面的代码会被阻塞,直到请求完成并返回结果。

在 main 函数中,我们可以这样接收网络请求后的结果:

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

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

通过 then() 方法,我们可以在网络请求成功后处理返回的数据;whenComplete() 用于在异步任务完成时执行某些操作,无论是否成功;catchError() 则用于捕获异步操作中可能出现的错误。

三、FutureBuilder 的使用

FutureBuilder 是 Flutter 中一个非常实用的 Widget,它对 Future 进行了封装,使得我们可以在 UI 中方便地根据异步操作的状态来构建不同的界面。它的构造方法如下:

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

其中,future 接收 Future 类型的值,通常是我们发起的网络请求函数;initialData 表示在异步函数执行完成之前可以给快照使用的初始数据;builder 是一个构建器,接收一个 AsyncWidgetBuilder 类型的值,用于根据异步操作的状态构建相应的 Widget。

AsyncWidgetBuilder 为构建器提供了一个 AsyncSnapshot 对象,该对象封装了连接状态(connectionState)、数据(data)以及错误信息(error)等属性。connectionState 是一个枚举类型,包括 none(未连接)、waiting(等待)、active(活跃)和 done(完成)四种状态。

下面是一个使用 FutureBuilder 在请求网络数据时显示加载中的示例:

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 {
  @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;
}

在这个示例中,我们通过 FutureBuilder 的 builder 属性,根据 AsyncSnapshot 的状态来决定显示加载中的提示还是返回的数据。当异步操作完成且有数据返回时,显示数据;否则,显示加载中的 Widget。

四、总结

通过本文的介绍,相信大家对 Flutter 中的异步编程(Future、async、await)以及网络请求有了更深入的了解。异步编程是 Flutter 开发中不可或缺的一部分,它让我们能够高效地处理各种耗时操作,提升应用的性能和用户体验。而 Dio 网络请求库和 FutureBuilder 的结合使用,则为我们提供了强大的工具,使得网络数据的获取和展示变得更加便捷和灵活。