Flutter中的Http请求和数据转换

发布于3/1/2020 来自:「前端知否」微信公众号

大部分的应用开发中都离不开Http请求,因此,我们将研究如何在Flutter中使用http插件。

创建一个新的Flutter项目

为了进行设置,我们将创建一个Flutter应用示例。假设您已经安装了Flutter和Dart SDK,请在终端中运行以下命令:

# 创建Flutter应用
$ flutter create flutter_http

# 在vscode中打开
$ cd flutter_http && code .

添加HTTP插件

切换到您的pubspec.yaml,并添加以下插件:

dependencies:
flutter:
sdk: flutter

http: ^0.12.0+2

这是dart.dev发布的官方Flutter插件,因此,我们可以相信它的可靠性。

HTTP请求

我们的第一个任务是创建一个可用于与API交互的类。我们将在lib/http_service.dart文件中创建一个名为HttpService的新类,并添加一个getPosts函数:

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 "Can't get posts.";
}
}
}

从getPosts函数可以看出,我们首先调用http包上的get方法请求postsURL

接下来,如果该请求成功,我们将解析响应并使用Post.fromJson返回List <Post>。让我们继续在lib/posts_model.dart中创建Post类:

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 Map使用fromJson方法返回一个新的帖子。在生产应用程序中,我建议使用json_serializable之类的东西来自动处理序列化。

展示文章

让我们在lib/posts.dart中创建一个名为PostsPage的新页面:

import 'package:flutter/material.dart';
import 'package:flut_http/http_service.dart';
import 'package:flut_http/post_detail.dart';
import 'package:flut_http/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());
}
},
),
);
}
}

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

如果snapshot.hasData为false,则显示的是CircularProgressIndicator,否则显示的是带有各种发布信息的ListTile

确保把main.dart中主页的home属性设置为PostsPage

import 'package:flutter/material.dart';
import 'package:flut_http/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.deepPurple,
),

home: PostsPage(),
);
}
}

xxx

文章详情

如果用户点击该帖子,我们希望将其导航到PostDetail页面。让我们来构建它:

import 'package:flutter/material.dart';
import 'package:flut_http/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}"),
),
],
),
),
],
),
),
));
}
}

虽然此页面没有任何HTTP数据,但我选择了一些字段数据用来模拟显示完整的Post。

xxx

删除

HTTP请求的另一个示例是delete方法的使用。例如,在我们的HttpService内部,我们可以创建一个deletePost(int id)方法:

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

if (res.statusCode == 200) {
print("DELETED");
} else {
throw "Can't delete post.";
}
}

我们可以在AppBar的actions数组中添加一个IconButton,每当点击它时,都会删除一个Post:

final HttpService httpService = HttpService();

@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();
},
)
],
),
//
)
}

最后

在本文中,我们研究了如何与Flutter http软件包进行交互。这使我们能够获取文章列表,以及删除单个文章。也可以使用其他操作,如发布,修改等。