关于 flutter-use-http-package
面向 Flutter 开发的 HTTP 网络编程技能,使用 `http` 包处理请求。内容涵盖平台权限、类型化 JSON 解析、错误处理和后台解析。
主要功能
- 01HTTP 配置与平台权限
- 02GET、POST、PUT 和 DELETE 请求
- 03类型化 JSON 反序列化
- 04使用 compute() 后台解析
- 05FutureBuilder 错误处理
适用场景
- 01为 Flutter 应用添加 API 请求
- 02在 UI 线程之外解析大型 JSON 响应
- 03在界面中呈现请求和解析错误
安装
复制下面的命令,在终端中运行。
$
npx skills add https:/ / github.com/ flutter/ agent-plugins --skill flutter-use-http-package安装后在 Claude Code 中输入/flutter-use-http-package确认已加载。
详细介绍
实现 Flutter 网络通信
目录
配置和权限
配置访问网络所需的环境和特定平台权限。
- 在终端中添加
http包依赖:flutter pub add http - 在 Dart 文件中导入该包:
import 'package:http/http.dart' as http; - 在
android/app/src/main/AndroidManifest.xml中添加 Internet 权限,以配置 Android 权限:<uses-permission android:name="android.permission.INTERNET" /> - 在
macos/Runner/DebugProfile.entitlements和macos/Runner/Release.entitlements中添加网络客户端键,以配置 macOS 权限:<key>com.apple.security.network.client</key> <true/>
请求执行和响应处理
执行 HTTP 操作,并将响应映射为强类型 Dart 对象。
- URI: 始终使用
Uri.parse('your_url')解析 URL 字符串。 - 标头: 通过
headers参数映射注入授权和内容类型标头。使用HttpHeaders.authorizationHeader设置身份验证令牌。 - 请求负载: 对于 POST 和 PUT 请求,使用
dart:convert中的jsonEncode()对请求正文进行编码。 - 状态验证: 检查
response.statusCode。将200 OK(GET/PUT/DELETE)和201 CREATED(POST)视为成功。 - 错误处理: 对非成功状态码抛出明确的异常。失败时绝不返回
null,否则会阻止FutureBuilder进入错误状态,并导致加载指示器无限显示。 - 反序列化: 使用
jsonDecode(response.body)解析原始字符串,并通过工厂构造函数(例如fromJson)将其映射为自定义 Dart 对象。
后台解析
将耗时的 JSON 解析任务转移到单独的 Isolate 中,以避免 UI 卡顿(帧丢失)。
- 导入
package:flutter/foundation.dart。 - 使用
compute()函数在后台 Isolate 中运行解析逻辑。 - 确保传递给
compute()的解析函数是顶层函数或静态方法,因为闭包或实例方法无法跨 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. 使用
FutureBuilder将Future集成到 UI 中。 - 6. 处理
snapshot.hasData和snapshot.hasError,默认显示CircularProgressIndicator。 - 7. 反馈循环: 运行应用 -> 触发网络请求 -> 检查控制台中未处理的异常 -> 修复解析或权限错误。
示例
高保真实现:在后台获取并解析
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. Top-level parsing function for 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. Network execution with background parsing
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) {
// Offload heavy parsing to a background isolate
return compute(parsePhotos, response.body);
} else {
throw Exception('Failed to load photos. Status: ${response.statusCode}');
}
}
// 3. Strongly typed model
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 Integration
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();
// Initialize Future once to prevent re-fetching on rebuilds
_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}'));
}
// Default loading state
return const Center(child: CircularProgressIndicator());
},
);
}
}
评论
支持 Markdown