01. http 包发起请求
Flutter 最基础的网络请求用 http 包,http.get 发 GET,http.post 发 POST。返回的 Response 有 statusCode 和 body(字符串)。需要反序列化 JSON 用 dart:convert 的 jsonDecode。虽然简单但也够用,小项目完全可以不引入第三方网络库。
dart
import 'package:http/http.dart' as http;
import 'dart:convert';
Future<List<User>> fetchUsers() async {
final response = await http.get(
Uri.parse('https://api.example.com/users'),
headers: {'Authorization': 'Bearer $token'},
);
if (response.statusCode == 200) {
final List<dynamic> jsonList = jsonDecode(response.body);
return jsonList.map((json) => User.fromJson(json)).toList();
} else {
throw Exception('请求失败: ${response.statusCode}');
}
}
Future<void> createUser(User user) async {
final response = await http.post(
Uri.parse('https://api.example.com/users'),
headers: {'Content-Type': 'application/json'},
body: jsonEncode(user.toJson()),
);
if (response.statusCode != 201) {
throw Exception('创建失败');
}
}jsonDecode 默认返回 dynamic,不安全。用 Map<String, dynamic> 显式声明类型更好。
02. JSON 序列化
Dart 没有像 Gson 那样的反射序列化,需要手写 fromJson 和 toJson 方法。json_serializable 库用代码生成自动写序列化代码,需要运行 build_runner。手动写虽然麻烦但简单可控,小项目手写够用。fromJson 是工厂构造函数,toJson 返回 Map。
dart
class User {
final int id;
final String name;
final String? email;
User({required this.id, required this.name, this.email});
factory User.fromJson(Map<String, dynamic> json) {
return User(
id: json['id'] as int,
name: json['name'] as String,
email: json['email'] as String?,
);
}
Map<String, dynamic> toJson() {
return {
'id': id,
'name': name,
'email': email,
};
}
}
// 使用 json_serializable 自动生成
// @JsonSerializable()
// class User {
// final int id;
// final String name;
// factory User.fromJson(Map<String, dynamic> json) => _$UserFromJson(json);
// Map<String, dynamic> toJson() => _$UserToJson(this);
// }json_serializable 需要额外配置 build_runner,虽然省代码但多了构建步骤,项目初期手动写更简单。
03. Dio 高级网络库
Dio 是 Flutter 最流行的第三方网络库,比 http 包功能强大得多。支持拦截器(请求/响应/异常三方拦截)、拦截器链、取消请求、超时设置、文件下载上传进度、BaseUrl 配置、Cookie 管理等。拦截器最适合做 token 刷新和日志记录。
dart
import 'package:dio/dio.dart';
// 创建实例并配置
final dio = Dio(BaseOptions(
baseUrl: 'https://api.example.com',
connectTimeout: Duration(seconds: 5),
receiveTimeout: Duration(seconds: 3),
headers: {'Content-Type': 'application/json'},
));
// 添加拦截器
dio.interceptors.add(InterceptorsWrapper(
onRequest: (options, handler) {
options.headers['Authorization'] = 'Bearer $token';
print('请求: ${options.method} ${options.path}');
handler.next(options);
},
onResponse: (response, handler) {
print('响应: ${response.statusCode}');
handler.next(response);
},
onError: (error, handler) {
if (error.response?.statusCode == 401) {
// token 过期,刷新 token 后重试
refreshToken().then((_) => handler.resolve(retryRequest(error.requestOptions)));
return;
}
handler.next(error);
},
));
// 使用
final response = await dio.get('/users', queryParameters: {'page': 1});
final data = response.data;
// 表单上传
final formData = FormData.fromMap({
'file': await MultipartFile.fromFile('path/to/file.jpg'),
'name': 'avatar',
});
await dio.post('/upload', data: formData);Dio 拦截器链按添加顺序执行,Request 拦截器正序,Response 和 Error 拦截器倒序。
04. 网络异常处理和加载状态
网络请求必须处理各种异常:超时、无网络、服务器错误、JSON 解析失败。用 try-catch 包裹 await 调用,分别 catch DioException 和通用 Exception。UI 层通常有三种状态:loading 显示加载动画、error 显示错误重试按钮、loaded 显示数据。
dart
class UserProvider extends ChangeNotifier {
List<User> _users = [];
bool _isLoading = false;
String? _error;
List<User> get users => _users;
bool get isLoading => _isLoading;
String? get error => _error;
Future<void> fetchUsers() async {
_isLoading = true;
_error = null;
notifyListeners();
try {
final response = await dio.get('/users');
_users = (response.data as List)
.map((json) => User.fromJson(json))
.toList();
} on DioException catch (e) {
switch (e.type) {
case DioExceptionType.connectionTimeout:
_error = '连接超时,请检查网络';
break;
case DioExceptionType.receiveTimeout:
_error = '服务器响应超时';
break;
case DioExceptionType.badResponse:
_error = '服务器错误: ${e.response?.statusCode}';
break;
default:
_error = '网络异常: ${e.message}';
}
} catch (e) {
_error = '数据解析失败';
} finally {
_isLoading = false;
notifyListeners();
}
}
}网络状态的 loading/error/data 三态管理是几乎所有 App 页面都需要的,可以封装成通用组件。
05. WebSocket 实时通信
WebSocket 适合需要服务端主动推送的场景:即时通讯、股票行情、在线协作。dart:io 内置 WebSocket,连接后通过 Stream 监听消息。web_socket_channel 包提供了更友好的 API。onMessage 处理收到的消息,sink.add 发送消息。记得在 dispose 时关闭连接。
dart
import 'package:web_socket_channel/web_socket_channel.dart';
class ChatService {
late WebSocketChannel _channel;
void connect(String token) {
_channel = WebSocketChannel.connect(
Uri.parse('wss://chat.example.com/ws?token=$token'),
);
}
Stream get messages => _channel.stream;
void sendMessage(String text) {
_channel.sink.add(jsonEncode({
'type': 'message',
'content': text,
}));
}
void disconnect() {
_channel.sink.close();
}
}
// Widget 中使用
class ChatPage extends StatefulWidget {
@override
_ChatPageState createState() => _ChatPageState();
}
class _ChatPageState extends State<ChatPage> {
final _service = ChatService();
@override
void initState() {
super.initState();
_service.connect('token');
}
@override
Widget build(BuildContext context) {
return StreamBuilder(
stream: _service.messages,
builder: (context, snapshot) {
if (snapshot.hasData) {
return Text('收到: ${snapshot.data}');
}
return Text('等待消息...');
},
);
}
@override
void dispose() {
_service.disconnect();
super.dispose();
}
}客户端 WebSocket 注意断线重连和心跳保活,生产环境要加上重连逻辑。
知识测验
第 1/4 题正确 0
Flutter 中 http 包获取数据后如何解析 JSON?