Flutter 中文文档:获取网络数据

640?wx_fmt=jpeg

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

这个教程包含以下步骤:

1. 添加  http  包
2. 使用  http  包进行网络请求
3. 将返回的响应转换成一个自定义的 Dart 对象
4. 使用 Flutter 对数据进行获取和展示

1. 添加 http 包

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

安装 http 包之前,你必须先把它添加到 pubspec.yaml 的依赖区域。你可以在 Pub site 找到 http 包的最新版本:https://pub.flutter-io.cn/packages/http#-installing-tab-。

dependencies:
  http: <latest_version>

2. 进行网络请求

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

Future<http.Response> fetchPost() {
  return http.get('https://jsonplaceholder.typicode.com/posts/1');
}

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

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

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

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

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

3.1 创建一个 Post 类

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

手动转换 JSON 是我们目前唯一的选项。想了解更多,请查看完整的文章 JSON 和序列化数据。

JSON 和序列化数据文章链接:https://flutter-io.cn/docs/development/data-and-backend/json

class Post {
  final int userId;
  final int id;
  final String title;
  final String body;

  Post({this.userId, this.id, this.title, this.body});

  factory Post.fromJson(Map<String, dynamic> json) {
    return Post(
      userId: json['userId'],
      id: json['id'],
      title: json['title'],
      body: json['body'],
    );
  }
}

3.2 将 http.Response 转换成 Post

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

  • 用 dart:convert 包将响应体转换成一个 json Map

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

  • 如果服务器返回的不是我们预期的响应,那么就抛出错误。

Future<Post> fetchPost() async {
  final response =
      await http.get('https://jsonplaceholder.typicode.com/posts/1');

  if (response.statusCode == 200) {
    // If server returns an OK response, parse the JSON.
    return Post.fromJson(json.decode(response.body));
  } else {
    // If that response was not OK, throw an error.
    throw Exception('Failed to load post');
  }
}}
}

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

4. 获取并展示数据

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

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

  • 你想要处理的 Future。在这里就是调用 fetchPost() 函数。

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

FutureBuilder<Post>(
  future: fetchPost(),
  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 CircularProgressIndicator();
  },
);

5. 将数据请求移出 build() 方法

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

每当 Flutter 需要改变视图中的一些内容时(这个发生的频率非常高),就会调用 build() 方法。因此,如果你将数据请求置于 build() 内部,就会造成大量的无效调用,同时还会拖慢应用程序的速度。

关于如何在页面初始化的时候,只调用 API,下面有一些更好的选择。

5.1 传入 StatelessWidget

使用这种策略的话,相当于父组件负责调用数据获取方法,存储结果并传入你的组件中。

class MyApp extends StatelessWidget {
  final Future<Post> post;

  MyApp({Key key, this.post}) : super(key: key);

你可以在下面看到一个关于这种策略的完整代码示例。

5.2 在 StatefulWidget 状态的生命周期中调用

如果你的组件是有状态的,你可以在 initState() 或者 didChangeDependencies() 方法中调用 fetch 方法。

initState() 只会被调用一次而且再也不会被调用。如果你需要在 InheritedWidget 改变的时候可以重新载入的话,可以把数据调用放在 didChangeDependencies() 方法中。想了解更多详细内容请查看 State。链接:https://api.flutter.dev/flutter/widgets/State-class.html

class _MyAppState extends State<MyApp> {
  Future<Post> post;

  @override
  void initState() {
    super.initState();
    post = fetchPost();
  }

测试

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

  • 单元测试介绍:https://flutter-io.cn/docs/cookbook/testing/unit/introduction

  • 使用 Mockito 模拟依赖:https://flutter-io.cn/docs/cookbook/testing/unit/mocking

完整样例

import 'dart:async';
import 'dart:convert';

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

Future<Post> fetchPost() async {
  final response =
      await http.get('https://jsonplaceholder.typicode.com/posts/1');

  if (response.statusCode == 200) {
    // If the call to the server was successful, parse the JSON.
    return Post.fromJson(json.decode(response.body));
  } else {
    // If that call was not successful, throw an error.
    throw Exception('Failed to load post');
  }
}

class Post {
  final int userId;
  final int id;
  final String title;
  final String body;

  Post({this.userId, this.id, this.title, this.body});

  factory Post.fromJson(Map<String, dynamic> json) {
    return Post(
      userId: json['userId'],
      id: json['id'],
      title: json['title'],
      body: json['body'],
    );
  }
}

void main() => runApp(MyApp(post: fetchPost()));

class MyApp extends StatelessWidget {
  final Future<Post> post;

  MyApp({Key key, this.post}) : super(key: key);

  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      title: 'Fetch Data Example',
      theme: ThemeData(
        primarySwatch: Colors.blue,
      ),
      home: Scaffold(
        appBar: AppBar(
          title: Text('Fetch Data Example'),
        ),
        body: Center(
          child: FutureBuilder<Post>(
            future: post,
            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 CircularProgressIndicator();
            },
          ),
        ),
      ),
    );
  }
}

640?wx_fmt=png

  • 0
    点赞
  • 0
    评论
  • 2
    收藏
  • 一键三连
    一键三连
  • 扫一扫,分享海报

相关推荐
©️2020 CSDN 皮肤主题: Age of Ai 设计师:meimeiellie 返回首页
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、C币套餐、付费专栏及课程。

余额充值