如何在 Flutter 中使用 HTTP 请求

简介

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

Ffltter提供了支持发起Http请求的HTTP

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

前提条件

要完成本教程,您需要:

  • 下载并安装Flutter
  • 下载并安装Android Studio Visual Studio Code
  • 建议为您的代码编辑器安装插件:
    • 为Android Studio安装的FlutterDart插件。
    • [Flutter](https:marketplace.visualstudio.com/items? itemName=Dart-Code.flutter)扩展。

本教程已通过Flutter v1.22.2、Android SDK v30.0.2和Android Studio v4.1验证。

第一步-设置项目

为了按照设置进行操作,您将创建一个示例颤动应用程序。

将环境设置为颤动后,可以运行以下命令来创建新应用程序:

1flutter create flutter_http_example

导航到新项目目录:

1cd flutter_http_example

使用fltter create将生成一个演示应用程序,其中将显示按钮被点击的次数。

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

1[label pubspec.yaml]
2dependencies:
3  flutter:
4    sdk: flutter
5
6  http: ^0.12.0+2

这是一个由dart.dev发布的官方扑翼插件,它有100%健康得分,),因此,你可以相信这个插件的可靠性。

第二步-处理GET请求

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

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

 1[label lib/http_service.dart]
 2import 'dart:convert';
 3import 'package:http/http.dart';
 4import 'post_model.dart';
 5
 6class HttpService {
 7  final String postsURL = "https://jsonplaceholder.typicode.com/posts";
 8
 9  Future<List<Post>> getPosts() async {
10    Response res = await get(postsURL);
11
12    if (res.statusCode == 200) {
13      List<dynamic> body = jsonDecode(res.body);
14
15      List<Post> posts = body
16        .map(
17          (dynamic item) => Post.fromJson(item),
18        )
19        .toList();
20
21      return posts;
22    } else {
23      throw "Unable to retrieve posts.";
24    }
25  }
26}

在本例中,您将连接到JSON Placeholder。这段代码使用http包的getpostsURL字符串。

如果请求成功,此代码将使用Post.from Json返回List<Post>。否则,将抛出一条错误消息。

<$>[备注] 注意: http状态code用于判断请求是否成功。状态码200表示HTTP请求成功。 <$>

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

 1[label lib/post_model.dart]
 2import 'package:flutter/foundation.dart';
 3
 4class Post {
 5  final int userId;
 6  final int id;
 7  final String title;
 8  final String body;
 9
10  Post({
11    @required this.userId,
12    @required this.id,
13    @required this.title,
14    @required this.body,
15  });
16
17  factory Post.fromJson(Map<String, dynamic> json) {
18    return Post(
19      userId: json['userId'] as int,
20      id: json['id'] as int,
21      title: json['title'] as String,
22      body: json['body'] as String,
23    );
24  }
25}

为了序列化来自JSON占位符的响应,此代码将返回一个新的Post,该Post带有基于JSONMapFromJson方法。

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

JSON占位符返回的PostuserIdidtilebody组成。

第三步-显示Posts

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

 1[label lib/posts.dart]
 2import 'package:flutter/material.dart';
 3import 'http_service.dart';
 4import 'post_model.dart';
 5
 6class PostsPage extends StatelessWidget {
 7  final HttpService httpService = HttpService();
 8
 9  @override
10  Widget build(BuildContext context) {
11    return Scaffold(
12      appBar: AppBar(
13        title: Text("Posts"),
14      ),
15      body: FutureBuilder(
16        future: httpService.getPosts(),
17        builder: (BuildContext context, AsyncSnapshot<List<Post>> snapshot) {
18          if (snapshot.hasData) {
19            List<Post> posts = snapshot.data;
20            return ListView(
21              children: posts
22                  .map(
23                    (Post post) => ListTile(
24                      title: Text(post.title),
25                      subtitle: Text("${post.userId}"),
26                    ),
27                  )
28                  .toList(),
29            );
30          } else {
31            return Center(child: CircularProgressIndicator());
32          }
33        },
34      ),
35    );
36  }
37}

此代码使用FutureBuilder小部件与getPosts()函数交互。这允许代码确定List何时准备就绪并相应地执行操作。

如果Snaphot.hasDataFalse,则显示CircularProgressIndicator。否则,显示带有帖子信息的ListTile

为了观察到目前为止的情况,您需要替换main.dart中的代码。

在代码编辑器中打开lib/main.dart,修改为使用PostsPage

 1[label lib/main.dart]
 2import 'package:flutter/material.dart';
 3import 'posts.dart';
 4
 5void main() {
 6  runApp(MyApp());
 7}
 8
 9class MyApp extends StatelessWidget {
10  @override
11  Widget build(BuildContext context) {
12    return MaterialApp(
13      title: 'HTTP',
14      debugShowCheckedModeBanner: false,
15      theme: ThemeData(
16        primarySwatch: Colors.blue,
17        visualDensity: VisualDensity.adaptivePlatformDensity,
18      ),
19      home: PostsPage(),
20    );
21  }
22}

编译代码并让其在模拟器中运行:

应用程序当前状态的屏幕截图,显示Json Placeholder返回的帖子标题和用户ID的列表

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

<$>[备注] 注: 标题摘录自lorem Ipsum,经常用作占位符文本。 <$>

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

第四步-展示PostDetail

如果用户点击了帖子,你的应用程序应该会将用户导航到一个PostDetail页面。

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

 1[label lib/post_detail.dart]
 2import 'package:flutter/material.dart';
 3import 'post_model.dart';
 4
 5class PostDetail extends StatelessWidget {
 6  final Post post;
 7
 8  PostDetail({@required this.post});
 9
10  @override
11  Widget build(BuildContext context) {
12    return Scaffold(
13      appBar: AppBar(
14        title: Text(post.title),
15      ),
16      body: SingleChildScrollView(
17        child: Padding(
18          padding: const EdgeInsets.all(12.0),
19          child: Column(
20            children: <Widget>[
21              Card(
22                child: Column(
23                  crossAxisAlignment: CrossAxisAlignment.center,
24                  children: <Widget>[
25                    ListTile(
26                      title: Text("Title"),
27                      subtitle: Text(post.title),
28                    ),
29                    ListTile(
30                      title: Text("ID"),
31                      subtitle: Text("${post.id}"),
32                    ),
33                    ListTile(
34                      title: Text("Body"),
35                      subtitle: Text(post.body),
36                    ),
37                    ListTile(
38                      title: Text("User ID"),
39                      subtitle: Text("${post.userId}"),
40                    ),
41                  ],
42                ),
43              ),
44            ],
45          ),
46        ),
47      )
48    );
49  }
50}

此代码将显示tileidbodyuserId

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

 1[label lib/posts.dart]
 2import 'package:flutter/material.dart';
 3import 'http_service.dart';
 4import 'post_detail.dart';
 5import 'post_model.dart';
 6
 7class PostsPage extends StatelessWidget {
 8  final HttpService httpService = HttpService();
 9
10  @override
11  Widget build(BuildContext context) {
12    return Scaffold(
13      appBar: AppBar(
14        title: Text("Posts"),
15      ),
16      body: FutureBuilder(
17        future: httpService.getPosts(),
18        builder: (BuildContext context, AsyncSnapshot<List<Post>> snapshot) {
19          if (snapshot.hasData) {
20            List<Post> posts = snapshot.data;
21            return ListView(
22              children: posts
23                  .map(
24                    (Post post) => ListTile(
25                      title: Text(post.title),
26                      subtitle: Text("${post.userId}"),
27                      onTap: () => Navigator.of(context).push(
28                        MaterialPageRoute(
29                          builder: (context) => PostDetail(
30                            post: post,
31                          ),
32                        ),
33                      ),
34                    ),
35                  )
36                  .toList(),
37            );
38          } else {
39            return Center(child: CircularProgressIndicator());
40          }
41        },
42      ),
43    );
44  }
45}

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

应用程序当前状态的屏幕截图,显示由Json Placeholder返回的单个帖子的详细信息

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

第五步-处理DELETE请求

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

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

 1[label lib/http_service.dart]
 2import 'dart:convert';
 3import 'package:http/http.dart';
 4import 'post_model.dart';
 5
 6class HttpService {
 7  final String postsURL = "https://jsonplaceholder.typicode.com/posts";
 8
 9  // ...
10
11  Future<void> deletePost(int id) async {
12    Response res = await delete("$postsURL/$id");
13
14    if (res.statusCode == 200) {
15      print("DELETED");
16    } else {
17      throw "Unable to delete post.";
18    }
19  }
20}

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

 1[label lib/post_detail.dart]
 2import 'package:flutter/material.dart';
 3import 'http_service.dart';
 4import 'post_model.dart';
 5
 6class PostDetail extends StatelessWidget {
 7  final HttpService httpService = HttpService();
 8  final Post post;
 9
10  PostDetail({@required this.post});
11
12  @override
13  Widget build(BuildContext context) {
14    return Scaffold(
15      appBar: AppBar(
16        title: Text(post.title),
17        actions: <Widget>[
18          IconButton(
19            icon: Icon(Icons.delete),
20            onPressed: () async {
21              await httpService.deletePost(post.id);
22              Navigator.of(context).pop();
23            },
24          )
25        ],
26      ),
27      // ...
28    );
29  }
30}

编译代码并让其在模拟器中运行。

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

1[secondary_label Output]
2flutter: DELETED

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

结论

在本文中,您学习了如何与Flutter http包进行交互。这允许你获取一个帖子列表和删除一个单独的帖子。

也支持postputpatch等类似操作。更多信息,请咨询官方documentation

Published At
Categories with 技术
Tagged with
comments powered by Disqus