Flutter网络请求完全指南:从dio到RESTful API
·
引言
网络请求是现代应用的核心功能之一。Flutter提供了多种方式来处理网络请求,从原生的http包到功能强大的dio库。本文将深入探讨Flutter网络请求的最佳实践,帮助你构建健壮的网络层。
一、网络请求基础
1.1 选择合适的库
| 库 | 特点 | 适用场景 |
|---|---|---|
| http | 官方包,轻量级 | 简单请求 |
| dio | 功能强大,支持拦截器 | 复杂场景 |
| retrofit | 类型安全,代码生成 | 大型项目 |
1.2 基本HTTP请求
import 'package:http/http.dart' as http;
Future<void> fetchData() async {
final response = await http.get(
Uri.parse('https://api.example.com/data'),
headers: {'Authorization': 'Bearer token'},
);
if (response.statusCode == 200) {
print(response.body);
} else {
throw Exception('请求失败');
}
}
1.3 POST请求
Future<void> postData() async {
final response = await http.post(
Uri.parse('https://api.example.com/data'),
headers: {'Content-Type': 'application/json'},
body: jsonEncode({'name': 'John', 'age': 30}),
);
}
二、dio库详解
2.1 安装与配置
dependencies:
dio: ^5.0.0
import 'package:dio/dio.dart';
final dio = Dio();
2.2 基本配置
final dio = Dio(
BaseOptions(
baseUrl: 'https://api.example.com',
connectTimeout: const Duration(seconds: 5),
receiveTimeout: const Duration(seconds: 3),
headers: {
'Content-Type': 'application/json',
},
),
);
2.3 GET请求
Future<User> getUser(String id) async {
try {
final response = await dio.get('/users/$id');
return User.fromJson(response.data);
} catch (e) {
throw Exception('获取用户失败: $e');
}
}
2.4 POST请求
Future<User> createUser(User user) async {
final response = await dio.post(
'/users',
data: user.toJson(),
);
return User.fromJson(response.data);
}
三、拦截器
3.1 请求拦截器
dio.interceptors.add(InterceptorsWrapper(
onRequest: (options, handler) {
options.headers['Authorization'] = 'Bearer ${AuthService.token}';
return handler.next(options);
},
));
3.2 响应拦截器
dio.interceptors.add(InterceptorsWrapper(
onResponse: (response, handler) {
if (response.statusCode == 401) {
AuthService.refreshToken();
}
return handler.next(response);
},
));
3.3 错误拦截器
dio.interceptors.add(InterceptorsWrapper(
onError: (error, handler) {
if (error.response?.statusCode == 500) {
showError('服务器错误');
}
return handler.next(error);
},
));
3.4 日志拦截器
dio.interceptors.add(LogInterceptor(
requestBody: true,
responseBody: true,
));
四、错误处理
4.1 统一错误处理
class ApiException implements Exception {
final String message;
final int? statusCode;
ApiException(this.message, {this.statusCode});
@override
String toString() => 'ApiException: $message';
}
Future<T> safeApiCall<T>(Future<T> Function() apiCall) async {
try {
return await apiCall();
} on DioError catch (e) {
throw ApiException(
e.response?.data['message'] ?? '网络请求失败',
statusCode: e.response?.statusCode,
);
} catch (e) {
throw ApiException(e.toString());
}
}
4.2 重试机制
dio.interceptors.add(RetryInterceptor(
retries: 3,
retryDelay: const Duration(seconds: 1),
retryEvaluator: (error) => error.response?.statusCode == 500,
));
五、请求取消
5.1 取消单个请求
CancelToken cancelToken = CancelToken();
dio.get('/data', cancelToken: cancelToken);
// 取消请求
cancelToken.cancel('请求已取消');
5.2 取消多个请求
final cancelToken = CancelToken();
// 多个请求使用同一个cancelToken
dio.get('/data1', cancelToken: cancelToken);
dio.get('/data2', cancelToken: cancelToken);
// 取消所有请求
cancelToken.cancel('全部取消');
六、文件上传下载
6.1 文件上传
Future<void> uploadFile(File file) async {
final formData = FormData.fromMap({
'file': await MultipartFile.fromFile(file.path),
'name': 'test.jpg',
});
await dio.post('/upload', data: formData);
}
6.2 文件下载
Future<void> downloadFile(String url, String savePath) async {
await dio.download(
url,
savePath,
onReceiveProgress: (received, total) {
final progress = (received / total * 100).toStringAsFixed(0);
print('下载进度: $progress%');
},
);
}
七、实战案例:API服务
7.1 创建API服务类
class ApiService {
final Dio _dio;
ApiService(this._dio);
Future<User> getUser(String id) async {
final response = await _dio.get('/users/$id');
return User.fromJson(response.data);
}
Future<List<User>> getUsers() async {
final response = await _dio.get('/users');
return (response.data as List).map((e) => User.fromJson(e)).toList();
}
Future<User> createUser(User user) async {
final response = await _dio.post('/users', data: user.toJson());
return User.fromJson(response.data);
}
Future<void> updateUser(String id, User user) async {
await _dio.put('/users/$id', data: user.toJson());
}
Future<void> deleteUser(String id) async {
await _dio.delete('/users/$id');
}
}
7.2 依赖注入
// 使用GetIt
final getIt = GetIt.instance;
void setupLocator() {
getIt.registerSingleton<Dio>(Dio(
BaseOptions(baseUrl: 'https://api.example.com'),
));
getIt.registerSingleton<ApiService>(ApiService(getIt()));
}
7.3 配合状态管理
class UserViewModel extends ChangeNotifier {
final ApiService _apiService;
User? _user;
bool _isLoading = false;
String? _error;
UserViewModel(this._apiService);
Future<void> loadUser(String id) async {
_isLoading = true;
_error = null;
notifyListeners();
try {
_user = await _apiService.getUser(id);
} catch (e) {
_error = e.toString();
}
_isLoading = false;
notifyListeners();
}
}
八、性能优化
8.1 请求缓存
final cache = <String, dynamic>{};
Future<T> cachedRequest<T>(String key, Future<T> Function() request) async {
if (cache.containsKey(key)) {
return cache[key] as T;
}
final result = await request();
cache[key] = result;
return result;
}
8.2 请求合并
Future<List<User>> fetchUsers(List<String> ids) async {
final futures = ids.map((id) => _apiService.getUser(id));
return await Future.wait(futures);
}
8.3 连接池
final dio = Dio(BaseOptions(
connectTimeout: const Duration(seconds: 5),
receiveTimeout: const Duration(seconds: 3),
maxConnectionsPerHost: 5,
));
九、最佳实践
9.1 环境配置
enum Environment { development, staging, production }
class Config {
static String baseUrl(Environment env) {
switch (env) {
case Environment.development:
return 'https://dev.api.example.com';
case Environment.staging:
return 'https://staging.api.example.com';
case Environment.production:
return 'https://api.example.com';
}
}
}
9.2 日志记录
dio.interceptors.add(InterceptorsWrapper(
onRequest: (options, handler) {
print('请求: ${options.method} ${options.path}');
return handler.next(options);
},
));
9.3 测试网络层
void main() {
test('getUser returns user', () async {
final dio = Dio(BaseOptions(baseUrl: 'https://api.example.com'));
final service = ApiService(dio);
final user = await service.getUser('1');
expect(user.id, '1');
});
}
十、总结
Flutter网络请求是应用开发的核心技能,通过合理使用dio库和良好的架构设计,可以构建健壮的网络层。
关键要点:
- 使用dio进行网络请求
- 使用拦截器处理请求/响应
- 实现统一错误处理
- 支持请求取消和重试
- 考虑性能优化和缓存
掌握网络请求,将使你的Flutter应用更加稳定和高效。
更多推荐
所有评论(0)