如何在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】。