从互联网上获取数据

从大多数应用程序都需要从互联网上获取数据,Dart和Flutter提供了相关工具!

注:本篇示例官方使用的是用http package发起简单的网络请求,但是http package功能较弱,很多常用功能都不支持。我们建议您使用dio 来发起网络请求,它是一个强大易用的dart http请求库,支持Restful API、FormData、拦截器、请求取消、Cookie管理、文件上传/下载……详情请查看github dio .

步骤

1. 添加`http` package依赖
2. 使用`http` package发出网络请求
3. 将响应转为自定义的Dart对象
4. 获取并显示数据

1. 添加http package

http package提供了从互联网获取数据的最简单方法。

dependencies:
  http: <latest_version>

2. 发起网络请求

在这个例子中,我们将使用http.getJSONPlaceholder REST API中获取示例文章。

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

http.get方法返回一个包含一个ResponseFuture

  • Future是与异步操作一起工作的核心Dart类。它用于表示未来某个时间可能会出现的可用值或错误。
  • http.Response类包含一个成功的HTTP请求接收到的数据

3. 将响应转换为自定义Dart对象

虽然发出网络请求很简单,但如果要使用原始的Future<http.Response>并不简单。为了让我们可以开开心心的写代码,我们可以将http.Response转换成我们自己的Dart对象。

创建一个Post

首先,我们需要创建一个Post类,它包含我们网络请求的数据。它还将包括一个工厂构造函数,它允许我们可以通过json创建一个Post对象。

手动转换JSON只是一种选择。有关更多信息,请参阅关于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 new Post(
      userId: json['userId'],
      id: json['id'],
      title: json['title'],
      body: json['body'],
    );
  }
}

http.Response 转换成一个Post对象

现在,我们将更新fetchPost函数以返回一个Future<Post>。为此,我们需要:

  1. 使用dart:convert package将响应内容转化为一个json Map
  2. 使用fromJson工厂函数,将json Map 转化为一个Post对象。
Future<Post> fetchPost() async {
  final response = await http.get('https://jsonplaceholder.typicode.com/posts/1');
  final json = JSON.decode(response.body); 
  
  return new Post.fromJson(json); 
}

Hooray! 现在我们有一个函数,我们可以调用它从互联网上获取一篇文章!

4. 获取并显示数据

为了获取数据并将其显示在屏幕上,我们可以使用FutureBuilder widget!FutureBuilder Widget可以很容易与异步数据源一起工作。

我们需要提供两个参数:

  1. future参数是一个异步的网络请求,在这个例子中,我们传递调用fetchPost()函数的返回值(一个future)。

  2. 一个 builder 函数,这告诉Flutter在future执行的不同阶段应该如何渲染界面。

    new FutureBuilder<Post>(
      future: fetchPost(),
      builder: (context, snapshot) {
        if (snapshot.hasData) {
          return new Text(snapshot.data.title);
        } else if (snapshot.hasError) {
          return new Text("${snapshot.error}");
        }
       
        // By default, show a loading spinner
        return new CircularProgressIndicator();
      },
    );
    

    译者注:其中snapshot.data即为Future的结果。

完整的例子

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');
  final responseJson = json.decode(response.body);

  return new Post.fromJson(responseJson);
}

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 new Post(
      userId: json['userId'],
      id: json['id'],
      title: json['title'],
      body: json['body'],
    );
  }
}

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

class MyApp extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return new MaterialApp(
      title: 'Fetch Data Example',
      theme: new ThemeData(
        primarySwatch: Colors.blue,
      ),
      home: new Scaffold(
        appBar: new AppBar(
          title: new Text('Fetch Data Example'),
        ),
        body: new Center(
          child: new FutureBuilder<Post>(
            future: fetchPost(),
            builder: (context, snapshot) {
              if (snapshot.hasData) {
                return new Text(snapshot.data.title);
              } else if (snapshot.hasError) {
                return new Text("${snapshot.error}");
              }

              // By default, show a loading spinner
              return new CircularProgressIndicator();
            },
          ),
        ),
      ),
    );
  }
}