如何在Flutter中搭建可复用的API调用框架?
如何在Flutter中搭建通用API调用框架
要构建能复用的通用API框架,核心是分层解耦和统一抽象,下面是一套可落地的实现方案:
1. 核心依赖选择
优先用dio作为网络请求库,它自带拦截器、请求/响应拦截、全局配置等功能,比原生http包更适合构建通用框架。在pubspec.yaml中添加依赖:
dependencies: dio: ^5.4.0+1 json_annotation: ^4.8.1 dev_dependencies: build_runner: ^2.4.6 json_serializable: ^6.7.1
(用json_serializable统一处理JSON序列化,避免手动编写解析代码)
2. 基础架构分层
把框架拆成4个核心模块,每个模块职责单一:
- 网络客户端层:封装底层请求配置、拦截器
- 请求模型层:统一API请求的参数、路径、方法定义
- 响应解析层:统一处理服务器返回格式,解析成通用模型
- API服务层:业务侧调用的入口,封装具体API接口
3. 实现核心类
3.1 全局网络客户端配置
创建单例的ApiClient类,负责全局配置baseUrl、拦截器、超时等:
import 'package:dio/dio.dart'; class ApiClient { static final ApiClient _instance = ApiClient._internal(); late Dio dio; factory ApiClient() => _instance; ApiClient._internal() { dio = Dio(BaseOptions( baseUrl: _getBaseUrl(), connectTimeout: const Duration(seconds: 10), receiveTimeout: const Duration(seconds: 10), headers: {'Content-Type': 'application/json'}, )); // 添加请求拦截器(示例:注入token、打印日志) dio.interceptors.add(InterceptorsWrapper( onRequest: (options, handler) { final token = _getAuthToken(); if (token != null) { options.headers['Authorization'] = 'Bearer $token'; } return handler.next(options); }, onResponse: (response, handler) { print('Response: ${response.data}'); return handler.next(response); }, onError: (DioException e, handler) { print('Error: ${e.message}'); return handler.next(e); }, )); } // 根据环境切换baseUrl String _getBaseUrl() { const isProduction = bool.fromEnvironment('dart.vm.product'); return isProduction ? 'https://api.prod.example.com' : 'https://api.dev.example.com'; } // 从本地获取token(示例,实际用shared_preferences等存储) String? _getAuthToken() { return 'your_auth_token'; } }
3.2 统一响应模型
创建泛型ApiResponse类,适配后端统一返回格式(假设后端返回{code: int, message: String, data: T}):
import 'package:json_annotation/json_annotation.dart'; part 'api_response.g.dart'; @JsonSerializable(genericArgumentFactories: true) class ApiResponse<T> { final int code; final String message; final T? data; ApiResponse({required this.code, required this.message, this.data}); factory ApiResponse.fromJson( Map<String, dynamic> json, T Function(Object? json) fromJsonT, ) => _$ApiResponseFromJson(json, fromJsonT); Map<String, dynamic> toJson(Object? Function(T value) toJsonT) => _$ApiResponseToJson(this, toJsonT); // 根据后端规则判断请求是否成功(示例:code=0为成功) bool get isSuccess => code == 0; }
运行flutter pub run build_runner build生成序列化代码。
3.3 基础API服务类
创建BaseApiService作为所有API服务的父类,封装通用请求方法:
import 'package:dio/dio.dart'; import 'api_response.dart'; import 'api_client.dart'; class BaseApiService { final Dio _dio = ApiClient().dio; // 通用GET请求 Future<ApiResponse<T>> get<T>( String path, { Map<String, dynamic>? queryParameters, required T Function(dynamic) fromJson, }) async { try { final response = await _dio.get(path, queryParameters: queryParameters); return ApiResponse.fromJson(response.data as Map<String, dynamic>, fromJson); } on DioException catch (e) { return ApiResponse( code: e.response?.statusCode ?? -1, message: e.message ?? '网络请求失败', data: null, ); } } // 通用POST请求 Future<ApiResponse<T>> post<T>( String path, { dynamic data, required T Function(dynamic) fromJson, }) async { try { final response = await _dio.post(path, data: data); return ApiResponse.fromJson(response.data as Map<String, dynamic>, fromJson); } on DioException catch (e) { return ApiResponse( code: e.response?.statusCode ?? -1, message: e.message ?? '网络请求失败', data: null, ); } } // 可按需封装PUT、DELETE等方法 }
3.4 自定义错误处理(可选)
如果需要精细的错误分类,可创建枚举和错误处理类:
enum ApiErrorType { networkError, serverError, unauthorized, badRequest, unknown, } class ApiError { final ApiErrorType type; final String message; ApiError({required this.type, required this.message}); // 根据Dio错误生成自定义错误 factory ApiError.fromDioException(DioException e) { if ([DioExceptionType.connectionTimeout, DioExceptionType.connectionError] .contains(e.type)) { return ApiError(type: ApiErrorType.networkError, message: '网络连接异常'); } final statusCode = e.response?.statusCode; if (statusCode == 401) { return ApiError(type: ApiErrorType.unauthorized, message: '登录已过期,请重新登录'); } else if (statusCode == 400) { return ApiError(type: ApiErrorType.badRequest, message: '请求参数错误'); } else if (statusCode != null && statusCode >= 500) { return ApiError(type: ApiErrorType.serverError, message: '服务器内部错误'); } return ApiError(type: ApiErrorType.unknown, message: e.message ?? '未知错误'); } }
在BaseApiService的catch块中替换为该错误处理即可。
4. 业务侧使用示例
以用户相关API为例:
// 用户模型 import 'package:json_annotation/json_annotation.dart'; part 'user_model.g.dart'; @JsonSerializable() class User { final String id; final String name; final String email; User({required this.id, required this.name, required this.email}); factory User.fromJson(Map<String, dynamic> json) => _$UserFromJson(json); Map<String, dynamic> toJson() => _$UserToJson(json); } // 用户API服务 class UserApiService extends BaseApiService { // 获取用户信息 Future<ApiResponse<User>> getUserInfo(String userId) async { return get<User>( '/users/$userId', fromJson: (json) => User.fromJson(json as Map<String, dynamic>), ); } // 登录接口 Future<ApiResponse<User>> login(String email, String password) async { return post<User>( '/auth/login', data: {'email': email, 'password': password}, fromJson: (json) => User.fromJson(json as Map<String, dynamic>), ); } }
页面中调用:
final userApi = UserApiService(); void _login() async { final response = await userApi.login('test@example.com', '123456'); if (response.isSuccess) { print('登录成功:${response.data?.name}'); } else { print('登录失败:${response.message}'); } }
5. 框架扩展建议
- 多环境切换:通过环境变量或配置文件管理不同环境的baseUrl,避免硬编码
- 缓存策略:用
dio_cache_interceptor实现GET请求缓存 - 重试机制:在拦截器中添加网络错误自动重试逻辑
- 日志开关:根据环境控制日志输出,生产环境关闭日志
- Token刷新:在请求拦截器中处理Token过期,自动刷新并重发请求
内容的提问来源于stack exchange,提问作者Phanindra
相关产品推荐
相关产品推荐

