如何在Flutter中消耗API

249 阅读6分钟

在Flutter中消耗API

应用编程接口(API)是一个通信门户,它允许两个或更多的应用程序连接以共享数据。它作为一个中介,向服务提供者传递请求并返回响应。鉴于使用预先存在的框架的便利性,API的使用在移动应用程序开发中得到了普遍的应用。程序员使用大多数API从网络服务器获取数据,并将其渲染到UI组件中。

本文将演示如何在Flutter应用程序中使用RESTful API从服务器获取和消费数据。根据维基百科,Representational State Transfer API是HTTP的一个架构子集,通常用于创建使用网络服务的交互式应用程序。

它允许程序员从服务器上获取和修改资源。REST API是首选,因为它支持大多数协议和数据格式。在本教程中,我们将使用HTTP和JSON数据格式

前提条件

  1. 对Flutter有一个基本的了解
  2. 在您的电脑上安装Flutter SDK
  3. 代码编辑器,最好是Android StudioVSCode
  4. 一个模拟器或一个移动设备来运行代码。

设置应用程序

首先,您需要通过在您的计算机上安装Flutter SDK来设置您的应用程序。安装完SDK后,我们现在需要设置我们的本地机器项目。如果您以前没有使用过flutter,以逐步了解创建flutter项目的情况。

API密钥和客户秘密

我们将在Github API的基础上建立我们的应用程序。因此,我们需要获得GitHubclient keysecret 来访问API。

整理文件夹

我们需要在Flutter项目中组织文件夹,而不是在单个文件上写代码,以便轻松地找到我们应用程序的文件和组件。这种做法可以让我们更容易找到bug。

此外,我们需要将视图文件与便于从API获取数据的文件分开,以避免这两个应用程序的组件之间的混淆。 最终的文件夹组织应该如下图所示。

lib
    ┣ models
    ┃ ┗ User.dart
    ┣ Providers
    ┃ ┗ UserProvider.dart
    ┣ Requests
    ┃ ┗ GithubRequest.dart
    ┣ Screens
    ┃ ┗ FollowersPage.dart
    ┗ main.dart

添加HTTP包

HTTP package ,其中包含了一组用于HTTP资源消耗的高级函数。要将该包添加到我们的应用程序中,请打开pubspec.yml ,在 "dependencies "下添加以下一行。

dependencies:
flutter:
    SDK: flutter
HTTP: ^0.12.2

接下来,我们将用下面这行代码把HTTP包导入我们的GithubRequest.dart 文件中。

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

下面的片段显示了我们将如何使用该包从API中获取一个给定的用户名的关注者。我们使用用户名是因为每个用户都有一个独特的用户名。

GithubRequest.dart

//importing HTTP package for fetching and consuming HTTP resources
import 'package:HTTP/HTTP.dart' as HTTP;

//Github request class
class Github {
  final String userName; // usernaname
  final String url = 'https://api.github.com/';
  static String clientId = 'CLIENT_ID'; //enter yout client id
  static String clientSecret = 'CLIENT_SECRET'; // insert your client secret

  //Github class constructor
  Github(this.userName);

  //Fetch a user with the username supplied in the form input
  Future<http.Response> fetchUser() {
    return http.get(url + 'users/' + userName);
  }
}

从JSON创建数据类

由于Flutter接受dart作为主要编程语言,我们需要将从URL中获取的JSON数据转换为dart类,以便在应用程序中使用。

我们可以使用Quicktype网站来完成,在那里我们传递JSON对象,并根据指定的语言返回该对象的类。将会以dart ,返回我们的类。

例如,我们代表用户的JSON如下所示。


//data json object
{
  "login": "jerimkaura",
  "avatar_url": "https://avatars.githubusercontent.com/u/50904889?v=4",
  "location": "Nairobi"
}

用户类

我对JSON进行了编辑,只捕获了应用程序上需要的属性。当我们将上述JSON传入Quicktype时,生成的用户类如下。

// To parse this JSON data, do 
final user = userFromJson(jsonString);

import 'dart:convert';

//the created user class
class User {

  String login; //username
  String avatarUrl; //profile picture
  String location; //location

  //class constructor
  User({
    this.login, //username
    this.avatarUrl, //profile picture
    this.location, //location
  });  

  //JSON serialization: return the value from json
  factory User.fromRawJson(String str) => User.fromJson(json.decode(str));

  //encode data to json format
  String toRawJson() => json.encode(toJson());

  //creating a dart user object from the json object
  factory User.fromJson(Map<String, dynamic> json) => User(
    login: json["login"],
    avatarUrl: json["avatar_url"],
    location: json["location"],
  );

  Map<String, dynamic> toJson() => {
    "login": login,
    "avatar_url": avatarUrl,
    "location": location,
  };
}

添加提供者

提供者将拥有获取API的用户数据和提供响应所需的功能。我们将在Providers 文件夹下创建一个名为UserProvider.dart 的文件。

ChangeNotifier 类将在多一个变量变化时通知我们的视图。当我们的代码继续执行时,我们使用async 函数来等待从API中获取用户的数据。


class UserProvider with ChangeNotifier {
  User user; //an instance of a user
  String errorMessage; //error message
  bool loading = false; //loading the page

  Future<bool> fetchUser(username) async {
    setLoading(true);
    // fetch user from the input supplied in the form
    await Github(username).fetchUser().then((data) {
      setLoading(false);
      if (data.statusCode == 200) {
        //incase of success
        setUser(User.fromJson(json.decode(data.body)));
      } else {
        Map<String, dynamic> result = json.decode(data.body);
        setMessage(result['message']); // error message
      }
    });
    return isUser(); //returns the fetched user
  }

  bool isLoading() {
    return loading; //return true if the app is loading the data
  }

  void setLoading(value) {
    loading = value;
    notifyListeners(); //This method is called when the objects is changed
  }

  void setUser(value) {
    user = value;
    notifyListeners(); //alert listeners that user's value changed
  }

  User getUser() {
    return user; //returns the fetched user
  }

  void setMessage(value) {
    errorMessage = value;
    notifyListeners(); // alert listeners that the error message changed
  }

  String getMessage() {
    return errorMessage; // get the error message
  }

  bool isUser() {
    return user != null ? true : false; // returns true if user is not null, anf false otherwise
  }
}

消耗数据

随着我们的模型和提供者准备就绪,我们的应用程序将通过提供者获取数据,并使用用户模型的方法将JSON结果转换为dart类。

接下来我们要做的是在移动屏幕上消费数据。在这个过程中,我们将做三件主要的事情。

实例化用户类。

我们将有两个用户类的实例;一个是用户的实例,另一个是给定用户的关注者列表。

User user; //instantiate a user
List< User> followers; // instantiate a list of users as a placeholder for the followers.

使用setState()来获取数据

setState方法通知应用程序,应用程序的内部状态已经被改变,并且这种改变可能会影响到视图。我们将在我们的FollowersPage.dart 文件中添加这段代码,就在打开scaffold() widget之前。

setState(() {
//This function gets a user from the username supplied in the input
  user = Provider.of<UserProvider>(context).getUser();
  
  // this method returns followers of the username supplied in the input as a list
  Github(user.login).fetchFollowers().then((following) {
    Iterable list = json.decode(following.body);
    setState(() {
      followers = list.map((e) => User.fromJson(e)).toList();
    });
  });
});

在用户界面上渲染数据

数据消耗的最后一件事是将动态输出渲染到我们的用户界面上。

下面的代码块显示了如何在移动屏幕上消耗数据。

// username
Text(followers[index].login,style:TextStyle(fontSize: 20, fontWeight: FontWeight.w500, color: Colors.grey[700]),)

// User avartar
child: CircleAvatar(backgroundImage: NetworkImage(followers[index].avatarUrl),),

// User location
Text(followers[index].location, style: TextStyle(color: Colors.blue, fontWeight: FontWeight.w700),)

结论

在这篇文章中,我们学习了如何从RESTful API中获取和消费数据,以GitHub的REST API为例。总结一下。

  • 我们从GitHub的API中获取了一个用户并显示了他的关注者。
  • 我们使用Quicktype从JSON中自动生成dart类。
  • 我们在构建一个实际的应用程序时实现了Flutter文件夹组织。