获取网络数据

获取网络数据

获取网络数据

实用教程chevron_right网络 (Networking)chevron_right获取网络数据

对于大部分应用来说,获取网络数据都是必不可少的一个功能。幸运的是,Dart 和 Flutter 就为我们提供了这样的工具。

info提示

你需要避免直接使用 dart:io 或 dart:html 来进行 HTTP 请求。这些库依赖于平台并绑定单个实现。

这个教程包含以下步骤:

添加 http package。

使用 http package 进行网络请求。

将返回的响应转换成一个自定义的 Dart 对象。

使用 Flutter 对数据进行获取和展示。

1. 添加 http package

#

http package 为我们提供了获取网络数据最简单的方法。

要将 http package 添加到依赖中,运行 flutter pub add 命令:

flutter pub add http

content_copy

导入 http package。

dart

import 'package:http/http.dart' as http;

content_copy

如果你要部署 Android,请编辑 AndroidManifest.xml 文件,添加 Internet 权限。

xml

content_copy

同样,如果你要部署 macOS,请编辑 macos/Runner/DebugProfile.entitlements 和 macos/Runner/Release.entitlements 文件,添加 network client 权限。

xml

com.apple.security.network.client

content_copy

2. 进行网络请求

#

在这里,你可以使用 http.get() 方法从 JSONPlaceholder 上获取到一个样本相册数据。

dart

Future fetchAlbum() {

return http.get(Uri.parse('https://jsonplaceholder.typicode.com/albums/1'));

}

content_copy

这个 http.get() 方法会返回一个包含 Response 的 Future。

Future 是 Dart 用来处理异步操作的一个核心类,它通常代表一个可能的值或者将来或许会用到的错误。

http.Response 类包含成功的 http 请求接收到的数据。

3. 将返回的响应转换成一个自定义的 Dart 对象

#

虽然进行网络请求很容易,但是处理 Future 却并不简单,为了后续处理起来更加方便,我们需要将 http.Response 转换成一个 Dart 对象。

创建一个 Album 类

#

首先,创建一个包含网络请求返回数据的 Album 类,而且这个类还需要一个可以利用 json 创建 Album 的工厂构造器。

使用 模式匹配 转换 JSON 只是其中一种方式。想了解更多,请查看完整的文档:JSON 和序列化数据。

dart

class Album {

final int userId;

final int id;

final String title;

const Album({

required this.userId,

required this.id,

required this.title,

});

factory Album.fromJson(Map json) {

return switch (json) {

{

'userId': int userId,

'id': int id,

'title': String title,

} =>

Album(

userId: userId,

id: id,

title: title,

),

_ => throw const FormatException('Failed to load album.'),

};

}

}

content_copy

将 http.Response 转换成 Album

#

现在,我们需要更新 fetchPost() 函数并返回 Future,为了实现这个目标,我们需要做以下几步:

用 dart:convert package 将响应体转换成一个 json Map。

如果服务器返回了一个状态码为 200 的 "OK" 响应,那么就使用 fromJson 工厂方法将 json Map 转换成 Album。

如果服务器返回的不是我们预期的响应(返回一个OK,Http Header 是 200),那么就抛出异常。服务器如若返回 404 Not Found 错误,也同样要抛出异常,而不是返回一个 null,在检查如下所示的 snapshot 值的时候,这一点相当重要。

dart

Future fetchAlbum() async {

final response = await http

.get(Uri.parse('https://jsonplaceholder.typicode.com/albums/1'));

if (response.statusCode == 200) {

// If the server did return a 200 OK response,

// then parse the JSON.

return Album.fromJson(jsonDecode(response.body) as Map);

} else {

// If the server did not return a 200 OK response,

// then throw an exception.

throw Exception('Failed to load album');

}

}

content_copy

太棒了!现在你就拥有了一个可以获取网络数据的完整函数啦。

4. 获取数据

#

在 initState() 或 didChangeDependencies() 方法中调用获取数据的方法 fetch()。

initState() 方法仅会被调用一次。如果你想要响应 InheritedWidget 改变以重新加载 API 的话,请在 didChangeDependencies() 方法中进行调用,你可以在 State 文档里了解更多。

dart

class _MyAppState extends State {

late Future futureAlbum;

@override

void initState() {

super.initState();

futureAlbum = fetchAlbum();

}

// ···

}

content_copy

我们将会在下一步中使用这个 Future。

5. 显示数据

#

为了能够获取数据并在屏幕上展示它,你可以使用 FutureBuilder widget。这个由 Flutter 提供的 FutureBuilder 组件可以让处理异步数据源变的非常简单。

此时,你必须要提供两个参数:

你想要处理的 Future,在这个例子中就是 fetchAlbum() 返回的 future。

一个告诉 Flutter 渲染哪些内容的 builder 函数,同时这也依赖于 Future 的状态:loading、success 或者是 error。

需要注意的是:当快照包含非空数据值, snapshot.hasData 将只返回 true。

因为 fetchAlbum 只能返回非空值,在服务器响应 "404 Not Found" 的时候应该引发异常抛出。发生异常的时候会将 snapshot.hasError 设定为 true,用来显示错误消息。

其他情况下,spinner 就会正常显示。

dart

FutureBuilder(

future: futureAlbum,

builder: (context, snapshot) {

if (snapshot.hasData) {

return Text(snapshot.data!.title);

} else if (snapshot.hasError) {

return Text('${snapshot.error}');

}

// By default, show a loading spinner.

return const CircularProgressIndicator();

},

)

content_copy

为何要在 initState() 中调用 fetchPost()?

#

虽然这样会比较方便,但是我们仍然不推荐将 API 调用置于 build() 方法内部。

每当 Flutter 需要更改视图中的任何内容时(并且这种更改出现的频率非常高),就会调用 build() 方法。因此,如果你将 fetchAlbum() 方法放在 build() 内,该方法会在每次重建应用时重复调用,同时还会拖慢应用程序的速度。

将 fetchAlbum() 的结果存储在状态变量中,可确保 Future 只执行一次,然后缓存(得到的数据)以备后续重新构建应用。

测试

#

关于如何测试这个功能,请查看下面的说明:

单元测试介绍

使用 Mockito 模拟依赖

完整样例

#

dart

import 'dart:async';

import 'dart:convert';

import 'package:flutter/material.dart';

import 'package:http/http.dart' as http;

Future fetchAlbum() async {

final response = await http

.get(Uri.parse('https://jsonplaceholder.typicode.com/albums/1'));

if (response.statusCode == 200) {

// If the server did return a 200 OK response,

// then parse the JSON.

return Album.fromJson(jsonDecode(response.body) as Map);

} else {

// If the server did not return a 200 OK response,

// then throw an exception.

throw Exception('Failed to load album');

}

}

class Album {

final int userId;

final int id;

final String title;

const Album({

required this.userId,

required this.id,

required this.title,

});

factory Album.fromJson(Map json) {

return switch (json) {

{

'userId': int userId,

'id': int id,

'title': String title,

} =>

Album(

userId: userId,

id: id,

title: title,

),

_ => throw const FormatException('Failed to load album.'),

};

}

}

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

class MyApp extends StatefulWidget {

const MyApp({super.key});

@override

State createState() => _MyAppState();

}

class _MyAppState extends State {

late Future futureAlbum;

@override

void initState() {

super.initState();

futureAlbum = fetchAlbum();

}

@override

Widget build(BuildContext context) {

return MaterialApp(

title: 'Fetch Data Example',

theme: ThemeData(

colorScheme: ColorScheme.fromSeed(seedColor: Colors.deepPurple),

),

home: Scaffold(

appBar: AppBar(

title: const Text('Fetch Data Example'),

),

body: Center(

child: FutureBuilder(

future: futureAlbum,

builder: (context, snapshot) {

if (snapshot.hasData) {

return Text(snapshot.data!.title);

} else if (snapshot.hasError) {

return Text('${snapshot.error}');

}

// By default, show a loading spinner.

return const CircularProgressIndicator();

},

),

),

),

);

}

}

上一篇: 利爪领袖任务详情-魔兽世界正式服任务数据库
下一篇: 营业部统计

相关文章

台群精机t500多少钱一台?
电脑肿么退出淘宝登录
特殊尊謙敬語,快速對照表!
【小辣椒T5 手机】小辣椒T5 手机最新信息
2024创业黑天鹅:那些陨落的科技公司们
Win7电脑怎么取消自动锁屏?Win7永久彻底关闭锁屏教程