SKILL.md
只读
名称
flutter-use-http-package
描述
使用 `http` 包发起 GET、POST、PUT 或 DELETE 请求。适用于需要向 REST API 请求或提交数据的场景。
Flutter 网络请求实践指南
目录
配置与权限
配置网络访问所需的环境与平台特定权限。
- 在终端中运行命令添加
http包依赖:flutter pub add http - 在 Dart 文件中引入该包:
import 'package:http/http.dart' as http; - 配置 Android 权限:在
android/app/src/main/AndroidManifest.xml中添加网络权限:<uses-permission android:name="android.permission.INTERNET" /> - 配置 macOS 权限:在
macos/Runner/DebugProfile.entitlements与macos/Runner/Release.entitlements中均添加网络客户端权限配置:<key>com.apple.security.network.client</key> <true/>
请求发送与响应处理
发送 HTTP 请求并将响应映射为强类型的 Dart 对象。
- URI 解析: 务必使用
Uri.parse('your_url')来解析 URL 字符串。 - 请求头(Headers): 通过
headers参数映射传入身份验证和内容类型头。推荐使用HttpHeaders.authorizationHeader设置 Auth Token。 - 请求体(Payloads): 对于 POST 和 PUT 请求,使用
dart:convert中的jsonEncode()对 body 进行编码。 - 状态码校验: 检查
response.statusCode。将200 OK(GET/PUT/DELETE)和201 CREATED(POST)视为请求成功。 - 错误处理: 状态码非成功状态时,显式抛出异常。请求失败时切勿返回
null,否则会导致FutureBuilder无法触发错误状态并卡在无限加载界面。 - 反序列化: 使用
jsonDecode(response.body)解析原始字符串,并通过工厂构造函数(如fromJson)将其映射为自定义的 Dart 对象。
后台异步解析
将耗时的 JSON 解析任务交由独立的 Isolate 执行,防止主线程卡顿掉帧(UI jank)。
- 引入
package:flutter/foundation.dart。 - 使用
compute()函数在后台 Isolate 中运行解析逻辑。 - 确保传给
compute()的解析函数是顶层函数(top-level function)或静态方法(static method),因为闭包和实例方法无法跨 Isolate 传递。
工作流:执行网络操作
请按以下清单依次落实与校验网络操作逻辑。
任务进度:
- [ ] 1. 定义强类型 Dart 数据模型,并提供
fromJson工厂构造函数。 - [ ] 2. 实现网络请求方法,返回
Future<Model>。 - [ ] 3. 根据请求类型应用分支逻辑:
- 获取数据(GET): 将查询参数追加拼接至 URI。
- 修改数据(POST/PUT): 设置
'Content-Type': 'application/json; charset=UTF-8'并挂载jsonEncode后的请求体。 - 删除数据(DELETE): 成功(
200 OK)时返回空模型实例。
- [ ] 4. 校验
statusCode,并在请求失败时抛出Exception。 - [ ] 5. 在 UI 中使用
FutureBuilder接入Future。 - [ ] 6. 正确处理
snapshot.hasData和snapshot.hasError状态,默认展示CircularProgressIndicator。 - [ ] 7. 反馈循环: 运行 App -> 触发网络请求 -> 检查控制台是否有未捕获的异常 -> 修复解析或权限报错。
示例代码
高质量完整实现:后台获取并解析数据
import 'dart:async';
import 'dart:convert';
import 'dart:io';
import 'package:flutter/foundation.dart';
import 'package:flutter/material.dart';
import 'package:http/http.dart' as http;
// 1. 用于后台 Isolate 的顶层解析函数
List<Photo> parsePhotos(String responseBody) {
final parsed = (jsonDecode(responseBody) as List<Object?>)
.cast<Map<String, Object?>>();
return parsed.map<Photo>(Photo.fromJson).toList();
}
// 2. 网络请求执行与后台解析
Future<List<Photo>> fetchPhotos() async {
final response = await http.get(
Uri.parse('https://jsonplaceholder.typicode.com/photos'),
headers: {
HttpHeaders.authorizationHeader: 'Bearer your_token_here',
HttpHeaders.acceptHeader: 'application/json',
},
);
if (response.statusCode == 200) {
// 将耗时的解析任务推给后台 Isolate 处理
return compute(parsePhotos, response.body);
} else {
throw Exception('Failed to load photos. Status: ${response.statusCode}');
}
}
// 3. 强类型数据模型
class Photo {
final int id;
final String title;
final String thumbnailUrl;
const Photo({
required this.id,
required this.title,
required this.thumbnailUrl,
});
factory Photo.fromJson(Map<String, dynamic> json) {
return Photo(
id: json['id'] as int,
title: json['title'] as String,
thumbnailUrl: json['thumbnailUrl'] as String,
);
}
}
// 4. UI 界面集成
class PhotoGallery extends StatefulWidget {
const PhotoGallery({super.key});
@override
State<PhotoGallery> createState() => _PhotoGalleryState();
}
class _PhotoGalleryState extends State<PhotoGallery> {
late Future<List<Photo>> _futurePhotos;
@override
void initState() {
super.initState();
// 只在初始化时请求一次,避免 Widget 重新构建(rebuild)时重复触发请求
_futurePhotos = fetchPhotos();
}
@override
Widget build(BuildContext context) {
return FutureBuilder<List<Photo>>(
future: _futurePhotos,
builder: (context, snapshot) {
if (snapshot.hasData) {
final photos = snapshot.data!;
return ListView.builder(
itemCount: photos.length,
itemBuilder: (context, index) => ListTile(
leading: Image.network(photos[index].thumbnailUrl),
title: Text(photos[index].title),
),
);
} else if (snapshot.hasError) {
return Center(child: Text('Error: ${snapshot.error}'));
}
// 默认加载状态
return const Center(child: CircularProgressIndicator());
},
);
}
}






