如何开始使用 Dart 和 Flutter 中的期货和流

简介

通常,应用程序需要与外部API和数据库交互的能力。随之而来的问题是,在等待某些请求和操作完成时,处理以不同于编写顺序的顺序运行的代码。

在本文中,您将探索Dart,特别是Flutter如何处理异步请求。

前提条件

要完成本教程,您需要:

本教程已通过Flutter v2.0.6、Android SDK v31.0.2和Android Studio v4.1验证。

了解异步码

使用同步代码时,当我们向外部API发送信息请求时,我们将需要一段时间才能得到响应。我们的机器将等待它完成,暂停可能与初始请求无关的事情。问题是,我们不希望脚本在每次需要一些时间时都停止运行,但我们也不希望它过早地运行任何依赖于返回数据的东西,这可能会导致错误,尽管请求成功。

两全其美的做法是设置我们的逻辑,允许我们的机器在等待请求返回的同时提前工作,同时只允许依赖于该请求的代码在它可用时运行。

我们的应用程序的数据很可能是四种形式中的一种,这取决于它们是否已经可用,以及它们是否是单一的。

包含四个单元格的表格-两行两列。有两个水平标签:第一个是可用的,第二个是等待。有两个垂直标签:第一个是单数,第二个是复数。可用的和多个需求列表/阵列数据。Available和Single需要字符串数据。等待和单一需要未来的数据。等待与多元需求流data.

此示例将探讨期货和流。

设置项目

将环境设置为颤动后,可以运行以下命令来创建新应用程序:

1flutter create flutter_futures_example

导航到新项目目录:

1cd flutter_futures_example

使用fltter create将生成一个演示应用程序,其中将显示按钮被点击的次数。

此示例的一部分依赖于REST国家/地区API.如果您提供国家/地区名称,此接口将返回国家/地区信息。例如,Canada的请求如下:

1https://restcountries.eu/rest/v2/name/Canada

这还需要http package

在代码编辑器中打开pubspec.yaml,添加以下插件:

1[label pubspec.yaml]
2dependencies:
3  flutter:
4    sdk: flutter
5
6  http: 0.13.3

现在,在代码编辑器中打开main.dart并修改以下代码行以显示 Get Country 按钮:

 1[label lib/main.dart]
 2import 'package:flutter/material.dart';
 3import 'package:http/http.dart' as http;
 4import 'dart:convert';
 5
 6void main() {
 7  runApp(MyApp());
 8}
 9
10class MyApp extends StatelessWidget {
11  @override
12  Widget build(BuildContext context) {
13    return MaterialApp(
14      title: 'Flutter Demo',
15      home: MyHomePage(),
16    );
17  }
18}
19
20class MyHomePage extends StatelessWidget {
21  void getCountry() {}
22
23  @override
24  Widget build(BuildContext context) {
25    return Scaffold(
26      body: Center(
27        child: MaterialButton(
28          onPressed: () => getCountry(),
29          child: Container(
30            color: Colors.blue,
31            padding: EdgeInsets.all(15),
32            child: Text('Get Country', style: TextStyle(color: Colors.white))
33          ),
34        ),
35      ),
36    );
37  }
38}

至此,您就有了一个新的带有HTTP包的Ffltter项目。

使用thencatchError

非常类似于JavaScript](https://andsky.com/tech/tutorials/js-error-handling-try-catch),DART中的[Try...Catch],它允许我们将方法链接在一起,以便我们可以轻松地将返回数据从一个方法传递到下一个方法,它甚至返回一种类似于承诺的数据类型,称为Futures。期货是任何单一类型的数据,如字符串,稍后将提供。

要使用这种技术,执行您的操作,然后只需将.then与我们作为参数传入的返回数据链接起来,并按照我们想要的方式使用它。在这一点上,你可以继续链接额外的.then。对于错误处理,在末尾使用.catchError并抛出传递给它的任何内容。

使用您的代码编辑器重新访问main.dart,并使用.then.catchError。首先,将void GetCountry(){}替换为Future GetCountry(Country)。然后,在onPressed:()=>GetCountry()中添加国家名称:

 1[label lib/main.dart]
 2// ...
 3
 4class MyHomePage extends StatelessWidget {
 5  Future getCountry(country) {
 6    Uri countryUrl = Uri.http('restcountries.eu', '/rest/v2/name/$country');
 7
 8    http
 9      .get(countryUrl)
10      .then((response) => jsonDecode(response.body)[0]['name'])
11      .then((decoded) => print(decoded))
12      .catchError((error) => throw(error));
13  }
14
15  @override
16  Widget build(BuildContext context) {
17    return Scaffold(
18      body: Center(
19        child: MaterialButton(
20          onPressed: () => getCountry('Canada'),
21          child: Container(
22            color: Colors.blue,
23            padding: EdgeInsets.all(15),
24            child: Text('Get Country', style: TextStyle(color: Colors.white))
25          ),
26        ),
27      ),
28    );
29  }
30}

保存更改并在模拟器中运行应用程序。然后,点击[获取国家/地区]按钮。您的主机将记录国家/地区的名称。

使用asyncwait

许多人认为可读性很强的另一种语法是Async/AWait。

Async/Await的工作原理与JavaScript完全相同(https://andsky.com/tech/tutorials/js-tutoric-functions),我们在函数名后使用await关键字,并在需要一些时间运行的任何内容(如get请求)之前添加await关键字。

使用代码编辑器重新访问main.dart,并使用asyncwait。现在,它之后的所有内容都将在返回值时运行。对于错误处理,我们可以在Try/Catch块中抛出错误。

 1[label lib/main.dart]
 2// ...
 3
 4class MyHomePage extends StatelessWidget {
 5  Future getCountry(country) async {
 6    Uri countryUrl = Uri.http('restcountries.eu', '/rest/v2/name/$country');
 7
 8    try {
 9      http.Response response = await http.get(countryUrl);
10      Object decoded = jsonDecode(response.body)[0]['name'];
11      print(decoded);
12    } catch (e) { throw(e); }
13  }
14
15  @override
16  Widget build(BuildContext context) {
17    return Scaffold(
18      body: Center(
19        child: MaterialButton(
20          onPressed: () => getCountry('Canada'),
21          child: Container(
22            color: Colors.blue,
23            padding: EdgeInsets.all(15),
24            child: Text('Get Country', style: TextStyle(color: Colors.white))
25          ),
26        ),
27      ),
28    );
29  }
30}

保存更改并在模拟器中运行应用程序。然后,点击[获取国家/地区]按钮。您的主机将记录国家/地区的名称。

使用Streams

DART的特殊之处在于,当我们有许多值被异步加载时,它使用了流。我们可以让连接保持打开并为新数据做好准备,而不是像我们的GET请求那样打开一次连接。

由于我们的示例通过设置允许流的后端(如使用Firebase或GraphQL))会变得有点太复杂,因此我们将通过每秒发出一条新的‘消息’来模拟聊天应用程序数据库中的更改。

我们可以使用StreamController类创建Stream,它的工作方式类似于List,因为它的行为类似于Futures列表。

我们可以通过Stream上的属性来控制我们的Stream,比如listenclose来启动和停止它。

<$>[警告] 警告: 当您的微件被移除时,一定要始终使用Close()。数据流将持续运行,直到它们被关闭,并将侵蚀计算能力,即使原始窗口小部件消失了。 <$>

现在,在代码编辑器中打开main.dart,替换以下代码行,导入dart:async,并使用StatefulWidget

 1[label lib/main.dart]
 2import 'package:flutter/material.dart';
 3import 'dart:async';
 4
 5void main() {
 6  runApp(MyApp());
 7}
 8
 9class MyApp extends StatelessWidget {
10  @override
11  Widget build(BuildContext context) {
12    return MaterialApp(
13      title: 'Flutter Demo',
14      home: MyHomePage(),
15    );
16  }
17}
18
19class MyHomePage extends StatefulWidget {
20  @override
21  _MyHomePageState createState() => _MyHomePageState();
22}
23
24class _MyHomePageState extends State<MyHomePage> {
25  StreamController<String> streamController = StreamController();
26
27  void newMessage(int number, String message) {
28    final duration = Duration(seconds: number);
29
30    Timer.periodic(duration, (Timer t) => streamController.add(message));
31  }
32
33  void initState() {
34    super.initState();
35
36    streamController.stream.listen((messages) => print(messages));
37
38    newMessage(1, 'You got a message!');
39  }
40
41  void dispose() {
42    streamController.close();
43
44    super.dispose();
45  }
46
47  @override
48  Widget build(BuildContext context) {
49    return Scaffold(
50      body: Center(
51        child: Container(
52          padding: EdgeInsets.all(15),
53          child: Text('Streams Example'),
54        ),
55      ),
56    );
57  }
58}

该代码将在控制台中连续打印出‘您收到消息`。

标准流可能有点受限,因为它们一次只允许一个侦听器。相反,我们可以使用StreamController类上的broadcast属性来打开多个通道。

 1[label lib/main.dart]
 2// ...
 3
 4StreamController<String> streamController = StreamController.broadcast();
 5
 6// ...
 7
 8void initState() {
 9  super.initState();
10
11  streamController.stream.listen((messages) => print('$messages - First'));
12  streamController.stream.listen((messages) => print('$messages - Second'));
13
14  newMessage(1, 'You got a message!');
15}
16
17// ...

该代码将在控制台中连续打印出您有消息-第一和`您有消息-第二‘。

结论

在本文中,您探索了Dart(特别是Flutter)如何处理异步请求。Dart中的异步编程将允许您开始开发智能和动态应用程序。

如果您想了解更多有关Ffltter的知识,请查看我们的Ffltter主题页面以获取练习和编程项目。

Published At
Categories with 技术
Tagged with
comments powered by Disqus