如何在Flutter中使用HTTP请求
介绍
应用程序经常需要执行 POST 和 GET 等 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 Map 的 fromJson 方法返回一个新的 Post。
注意: 在生产应用程序中,可以使用像 json_serializable 这样的包来自动处理序列化。
JSON 占位符返回的 Post 将由 userId、id、title 和 body 组成。
第 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.hasData 为 false,则显示 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}"),
),
],
),
),
],
),
),
)
);
}
}
此代码将显示 title、id、body 和 userId。
为了观察到目前为止的情况,您需要修改 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 单个帖子。
post、put、patch等类似的操作也是可以的。 更多信息请参考【X12X】官方文档【X38X】。