网络请求
使用基础的 http 包
需要配置权限
- 安卓:在
AndroidManifest.xml文件里面添加权限<uses-permission android:name="android.permission.INTERNET" />
安装
flutter pub add http
使用
// get
Future<http.Response> get(String path) async {
return await http.get(Uri.parse(path));
}
// post
Future<http.Response> post(String path, {Map<String, String>? headers, body}) async {
return await http.post(Uri.parse(path), headers: headers, body: body);
}
数据转换
flutter 中需要手动编写数据转换类,将 http.Response 转换成 dart 对象。示例如下
import 'dart:convert';
class Album {
final int userId;
final int id;
final String title;
const Album({
required this.userId,
required this.id,
required this.title,
});
factory Album.fromJson(Map<String, dynamic> json) {
return Album(
userId: json['userId'],
id: json['id'],
title: json['title'],
);
}
}
Future<Album> getAlbum() async {
final response await http.get(Uri.parse('url'));
if(response.statusCode == 200) {
return Album.fromJson(jsonDecode(response.body));
}else{
throw Exception('failed to load album');
}
}
获取数据
在组件的 initState() 或 didChangeDependencies() 中调用
注意,仅 StatefulWidget 组件有效,若想在 StatelessWidget 使用数据,相当于从父组件调用接口获取数据,然后再传入 StatelessWidget 子组件
class _MyAppState extends State<MyApp> {
late Future<Album> futureAlbum;
void initState() {
super.initState();
futureAlbum = getAlbum();
}
}
显示数据
官网的示例中,初始化 futureAlbum 是使用了 late 且并未赋值,是在 initState() 中获取数据的,若不在 initState() 中获取数据(如通过点击事件获取),则需要为 futureAlbum 赋值(通常赋空值),并且在使用时判断 futureAlbum 是否为空
使用 FutureBuilder 组件
这是一个 fltter 内置的专门用于处理异步数据的组件
class _MyAppState extends State<MyApp> {
Future<Album>? futureAlbum;
void _handleTap() {
setState((){
futureAlbum = getAlbum();
});
}
Widget build(BuildContext context) {
return FutureBuilder<Album>(
future: futureAlbum,
builder: (context, snapshot) {
if (snapshot.hasData) {
return Text(snapshot.data!.title);
}else if (snapshot.hasError) {
return Text('${snapshot.error}');
}
// 默认状态下的显示
return const CircularProgressIndicator();
}
)
}
}
自定义使用数据
将数据值当作普通变量使用,自行处理数据
class _MyAppState extends State<MyApp> {
Album? album;
void _handleTap() {
getAlbum(_counter).then((value) {
setState(() {
album = value;
_counter++;
});
});
}
Widget build(BuildContext context) {
return Text(album == null ? 'loading...' : album!.title)
}
}
简单封装如下
需要注意请求头
- 若
Content-Type为application/json,则post发送的数据需要进行jsonEncode编码 - 若
Content-Type为application/x-www-form-urlencoded,则post发送的数据无需进行编码,直接发送Map<String, String>{}即可
// config.dart
const baseUrl = 'https://jsonplaceholder.typicode.com';
const baseHeaders = <String, String>{
'Content-Type': 'application/json; charset=UTF-8',
};
// http.dart
import 'package:flutter_framework/config.dart' as cfg;
var baseUrl = cfg.baseUrl;
var baseHeaders = cfg.baseHeaders;
String queryParams([Map<String, dynamic>? data]) {
const prefix = '?';
final result = <String>[];
data?.forEach((key, value) {
result.add('$key=$value');
});
return result.isNotEmpty ? prefix + result.join('&') : '';
}
Future requestInterceptors(
String url, String method, Map<String, dynamic>? params) async {
final token = await getToken();
final newHeaders = {'Authorization': 'Bearer $token'};
final Map<String, String> headers = {}
..addAll(baseHeaders)
..addAll(newHeaders);
var targetUrl = '$baseUrl$url';
if (method == 'get') {
targetUrl = targetUrl + queryParams(params);
}
return {'url': Uri.parse(targetUrl), 'headers': headers, 'body': params};
}
class DiyHttp {
static Future<http.Response> get(String path,
[Map<String, dynamic>? params]) async {
final map = await requestInterceptors(path, 'get', params);
return await http.get(map['url'],
headers: map['headers']);
}
static Future<http.Response> post(String path,
[Map<String, dynamic>? params, Map<String, String>? headers]) async {
final map = await requestInterceptors(path, 'post', params);
return await http.post(map['url'],
headers: map['headers'], body: map['body']);
}
}