如何在Flutter中使用HTTP请求

来自菜鸟教程
跳转至:导航、​搜索

介绍

应用程序经常需要执行 POSTGET 等 HTTP 请求。

Flutter 提供了一个 http 包,支持发出 HTTP 请求。

在本文中,您将创建一个示例 Flutter 应用程序,该应用程序使用 http 包执行 HTTP 请求以显示占位符信息。

先决条件

要完成本教程,您需要:

  • 下载并安装 Flutter
  • 下载并安装Android Studio Visual Studio Code
  • 建议为您的代码编辑器安装插件: 为 Android Studio 安装了 Flutter 和 Dart 插件。 为 Visual Studio Code 安装了 Flutter 扩展。

本教程已使用 Flutter v1.22.2、Android SDK v30.0.2 和 Android Studio v4.1 进行了验证。

第 1 步 — 设置项目

为了遵循设置,您将创建一个示例 Flutter 应用程序。

为 Flutter 设置好环境后,您可以运行以下命令来创建新应用程序:

flutter create flutter_http_example

导航到新的项目目录:

cd flutter_http_example

使用 flutter create 将生成一个演示应用程序,该应用程序将显示按钮被单击的次数。

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

发布规范.yaml

dependencies:
  flutter:
    sdk: flutter

  http: ^0.12.0+2

这是由 dart.dev 发布的官方 Flutter 插件,它具有 100% health 分数,因此,您可以信任该插件的可靠性。

第 2 步 - 处理 GET 请求

您的第一个任务是创建一个可用于与 API 交互的类。

打开您的代码编辑器并在 lib 目录中创建一个 http_service.dart 文件。 在这里,您将开发一个新的 HttpService 类并添加一个 getPosts 函数:

lib/http_service.dart

import 'dart:convert';
import 'package:http/http.dart';
import 'post_model.dart';

class HttpService {
  final String postsURL = "https://jsonplaceholder.typicode.com/posts";

  Future<List<Post>> getPosts() async {
    Response res = await get(postsURL);

    if (res.statusCode == 200) {
      List<dynamic> body = jsonDecode(res.body);

      List<Post> posts = body
        .map(
          (dynamic item) => Post.fromJson(item),
        )
        .toList();

      return posts;
    } else {
      throw "Unable to retrieve posts.";
    }
  }
}

在此示例中,您将连接到 JSON 占位符。 此代码在 postsURL 字符串上使用 http 包的 get

如果该请求成功,此代码将使用 Post.fromJson 返回 List<Post>。 否则,将引发错误消息。

笔记: HTTP 状态码 are used to determine if a request was successful or unsuccessful. A status code of 200 represents a successful HTTP request.


然后,使用您的代码编辑器在 lib 目录中创建一个 post_model.dart 文件。 在这里,您将开发一个新的 Post 类:

lib/post_model.dart

import 'package:flutter/foundation.dart';

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

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

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

为了序列化来自 JSON 占位符的响应,此代码将使用基于 JSON MapfromJson 方法返回一个新的 Post

注意: 在生产应用程序中,可以使用像 json_serializable 这样的包来自动处理序列化。


JSON 占位符返回的 Post 将由 userIdidtitlebody 组成。

第 3 步 — 显示 Posts

接下来,使用您的代码编辑器在 lib 目录中创建一个 posts.dart 文件。 在这里,您将创建一个 PostsPage 类,它将显示从 HTTP 请求返回到 JSON 占位符的 Posts

lib/posts.dart

import 'package:flutter/material.dart';
import 'http_service.dart';
import 'post_model.dart';

class PostsPage extends StatelessWidget {
  final HttpService httpService = HttpService();

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(
        title: Text("Posts"),
      ),
      body: FutureBuilder(
        future: httpService.getPosts(),
        builder: (BuildContext context, AsyncSnapshot<List<Post>> snapshot) {
          if (snapshot.hasData) {
            List<Post> posts = snapshot.data;
            return ListView(
              children: posts
                  .map(
                    (Post post) => ListTile(
                      title: Text(post.title),
                      subtitle: Text("${post.userId}"),
                    ),
                  )
                  .toList(),
            );
          } else {
            return Center(child: CircularProgressIndicator());
          }
        },
      ),
    );
  }
}

此代码使用 FutureBuilder 小部件与 getPosts() 函数进行交互。 这允许代码确定 List<Post> 何时准备就绪并采取相应措施。

如果 snapshot.hasDatafalse,则显示 CircularProgressIndicator。 否则,将显示带有发布信息的 ListTile

为了观察你到目前为止所拥有的,你需要替换 main.dart 中的代码。

在代码编辑器中打开 lib/main.dart 并修改它以使用 PostsPage

lib/main.dart

import 'package:flutter/material.dart';
import 'posts.dart';

void main() {
  runApp(MyApp());
}

class MyApp extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      title: 'HTTP',
      debugShowCheckedModeBanner: false,
      theme: ThemeData(
        primarySwatch: Colors.blue,
        visualDensity: VisualDensity.adaptivePlatformDensity,
      ),
      home: PostsPage(),
    );
  }
}

编译你的代码并让它在模拟器中运行:

您应该观察 JSON 占位符返回的帖子标题和用户 ID 列表。

注意: 标题将摘自Lorem Ipsum,它经常用作占位符文本。


下一步是在用户单击帖子标题时创建一个详细信息页面,其中包含有关该帖子的更多信息。

第 4 步 — 显示 PostDetail

如果用户点击帖子,您的应用程序应将用户导航到 PostDetail 页面。

使用您的代码编辑器在 lib 目录中创建一个 post_detail.dart 文件。 在这里,您将创建一个 PostDetail 类,它将显示一个单独的 Post

lib/post_detail.dart

import 'package:flutter/material.dart';
import 'post_model.dart';

class PostDetail extends StatelessWidget {
  final Post post;

  PostDetail({@required this.post});

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(
        title: Text(post.title),
      ),
      body: SingleChildScrollView(
        child: Padding(
          padding: const EdgeInsets.all(12.0),
          child: Column(
            children: <Widget>[
              Card(
                child: Column(
                  crossAxisAlignment: CrossAxisAlignment.center,
                  children: <Widget>[
                    ListTile(
                      title: Text("Title"),
                      subtitle: Text(post.title),
                    ),
                    ListTile(
                      title: Text("ID"),
                      subtitle: Text("${post.id}"),
                    ),
                    ListTile(
                      title: Text("Body"),
                      subtitle: Text(post.body),
                    ),
                    ListTile(
                      title: Text("User ID"),
                      subtitle: Text("${post.userId}"),
                    ),
                  ],
                ),
              ),
            ],
          ),
        ),
      )
    );
  }
}

此代码将显示 titleidbodyuserId

为了观察到目前为止的情况,您需要修改 posts.dart 以支持 post_detail.dart

lib/posts.dart

import 'package:flutter/material.dart';
import 'http_service.dart';
import 'post_detail.dart';
import 'post_model.dart';

class PostsPage extends StatelessWidget {
  final HttpService httpService = HttpService();

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(
        title: Text("Posts"),
      ),
      body: FutureBuilder(
        future: httpService.getPosts(),
        builder: (BuildContext context, AsyncSnapshot<List<Post>> snapshot) {
          if (snapshot.hasData) {
            List<Post> posts = snapshot.data;
            return ListView(
              children: posts
                  .map(
                    (Post post) => ListTile(
                      title: Text(post.title),
                      subtitle: Text("${post.userId}"),
                      onTap: () => Navigator.of(context).push(
                        MaterialPageRoute(
                          builder: (context) => PostDetail(
                            post: post,
                          ),
                        ),
                      ),
                    ),
                  )
                  .toList(),
            );
          } else {
            return Center(child: CircularProgressIndicator());
          }
        },
      ),
    );
  }
}

编译你的代码并让它在模拟器中运行:

下一步是添加通过删除帖子来删除帖子的功能。

第 5 步 - 处理 DELETE 请求

HTTP 请求的另一个示例是使用 DELETE 方法。

在代码编辑器中重新访问 http_service.dart 并创建一个 deletePost(int id) 方法:

lib/http_service.dart

import 'dart:convert';
import 'package:http/http.dart';
import 'post_model.dart';

class HttpService {
  final String postsURL = "https://jsonplaceholder.typicode.com/posts";

  // ...

  Future<void> deletePost(int id) async {
    Response res = await delete("$postsURL/$id");

    if (res.statusCode == 200) {
      print("DELETED");
    } else {
      throw "Unable to delete post.";
    }
  }
}

在代码编辑器中重新访问 post_detail.dart 并将 IconButton 添加到 AppBar 内的 actions 数组。 按下图标时,应删除关联的帖子:

lib/post_detail.dart

import 'package:flutter/material.dart';
import 'http_service.dart';
import 'post_model.dart';

class PostDetail extends StatelessWidget {
  final HttpService httpService = HttpService();
  final Post post;

  PostDetail({@required this.post});

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(
        title: Text(post.title),
        actions: <Widget>[
          IconButton(
            icon: Icon(Icons.delete),
            onPressed: () async {
              await httpService.deletePost(post.id);
              Navigator.of(context).pop();
            },
          )
        ],
      ),
      // ...
    );
  }
}

编译您的代码并让它在模拟器中运行。

当您访问帖子详情页面时,您会在 AppBar 中看到一个 Delete 图标按钮。 按下按钮将在控制台中打印一条消息:

Outputflutter: DELETED

这将代表一个删除请求。 由于 JSON 占位符和此示例应用程序的限制,该帖子实际上不会被删除。

结论

在本文中,您学习了如何与 Flutter http 包进行交互。 这使您可以 GET 帖子列表和 DELETE 单个帖子。

postputpatch等类似的操作也是可以的。 更多信息请参考【X12X】官方文档【X38X】。