Flutter: Dio + Retrofit 入门(面向 Android 开发者)
目标:用 Android Retrofit 的思维快速上手 Dart/Flutter 的 Dio + Retrofit 组合,从依赖引入 → 初始化 → 接口定义 → 代码生成 → 调用与排错。
1. 依赖引入(pubspec.yaml)
你项目已包含部分依赖,可按需校对版本(以下为示例,建议与现有项目版本一致):
yaml
dependencies:
dio: ^5.x.x
retrofit: ^4.x.x
json_annotation: ^4.x.x
dev_dependencies:
build_runner: ^2.x.x
retrofit_generator: ^8.x.x
json_serializable: ^6.x.x
执行:
bash
flutter pub get
2. 初始化 Dio(拦截器/超时/日志)
dart
// lib/network/base_dio.dart
import 'package:dio/dio.dart';
import 'package:flutter/foundation.dart';
class BaseDio {
static Dio create({
required String baseUrl,
Map<String, dynamic>? defaultHeaders,
bool enableLog = kDebugMode,
}) {
final dio = Dio(BaseOptions(
baseUrl: baseUrl,
connectTimeout: const Duration(seconds: 10),
receiveTimeout: const Duration(seconds: 20),
headers: {
'Accept': 'application/json',
if (defaultHeaders != null) ...defaultHeaders,
},
));
if (enableLog) {
dio.interceptors.add(LogInterceptor(
request: true,
requestHeader: true,
requestBody: true,
responseHeader: false,
responseBody: true,
error: true,
));
}
// 自定义拦截器(token 注入、统一错误处理等)
dio.interceptors.add(InterceptorsWrapper(
onRequest: (options, handler) {
// options.headers['Authorization'] = 'Bearer xxx';
handler.next(options);
},
onResponse: (response, handler) => handler.next(response),
onError: (e, handler) => handler.next(e),
));
return dio;
}
}
3. 通用响应模型(可选,推荐配合 json_serializable)
dart
// lib/network/api_response.dart
import 'package:json_annotation/json_annotation.dart';
part 'api_response.g.dart';
@JsonSerializable(genericArgumentFactories: true, explicitToJson: true)
class ApiResponse<T> {
final int? code; // 0 / 200 -> success
final String? message; // 或 err_msg
final String? errMsg;
final T? data;
const ApiResponse({this.code, this.message, this.errMsg, this.data});
bool get isSuccess => code == 0 || code == 200;
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);
}
生成(首次或模型调整后):
bash
flutter pub run build_runner build --delete-conflicting-outputs
4. Retrofit 接口定义(与 Android Retrofit 思路一致)
dart
// lib/network/api_service.dart
import 'package:dio/dio.dart';
import 'package:retrofit/retrofit.dart';
import 'api_response.dart';
part 'api_service.g.dart';
@RestApi()
abstract class ApiService {
factory ApiService(Dio dio, {String baseUrl}) = _ApiService;
// GET 示例(回声服务)
@GET('/get')
Future<HttpResponse<dynamic>> httpBinGet();
// 业务示例:登录(返回强类型)
@POST('/login')
Future<ApiResponse<LoginData>> login(@Body() Map<String, dynamic> body);
}
// 模型示例
// lib/network/login_data.dart
import 'package:json_annotation/json_annotation.dart';
part 'login_data.g.dart';
@JsonSerializable()
class LoginData {
final int userId;
final String token;
LoginData({required this.userId, required this.token});
factory LoginData.fromJson(Map<String, dynamic> json) => _$LoginDataFromJson(json);
Map<String, dynamic> toJson() => _$LoginDataToJson(this);
}
生成接口桩文件:
bash
flutter pub run build_runner build --delete-conflicting-outputs
5. 组装与调用
dart
import 'package:dio/dio.dart';
import 'base_dio.dart';
import 'api_service.dart';
final dio = BaseDio.create(baseUrl: 'https://httpbin.org');
final api = ApiService(dio, baseUrl: 'https://httpbin.org');
Future<void> ping() async {
try {
final resp = await api.httpBinGet();
// resp.response 为 Dio 的 Response;resp.data 为 decoded body
print('status=${resp.response.statusCode}, url=${resp.data['url']}');
} on DioException catch (e) {
print('dio error: ${e.message}');
}
}
Future<void> doLogin() async {
final loginApi = ApiService(BaseDio.create(baseUrl: 'https://api.example.com'),
baseUrl: 'https://api.example.com');
try {
final res = await loginApi.login({'accountNumber': 'a', 'password': 'b'});
if (res.isSuccess) {
print('token=${res.data?.token}');
} else {
print('login fail code=${res.code} msg=${res.message ?? res.errMsg}');
}
} on DioException catch (e) {
print('network error: ${e.message}');
}
}
6. 进阶要点
- 返回类型选择:
Future<HttpResponse<T>>需要底层Response细节(headers/status)时用;Future<T>够用时返回强类型模型;- 响应结构不稳定时可用
dynamic/Map<String, dynamic>临时兜底。
- 错误处理:Dio v5 抛
DioException,可通过e.type区分超时、响应错误等。 - 拦截器:统一 Header(如 Token)、统一错误码处理、重试策略等。
- 多环境:用不同
baseUrl或注入不同Dio实例(上层配置)。 - 代码生成:所有
part '*.g.dart'文件禁止手改;模型与接口调整后重新生成。
7. 快速排错清单
- "Target of URI doesn't exist" → 未生成
*.g.dart或part路径不匹配文件名大小写。 - "Map.fromJson 调用错误" → Retrofit 返回泛型为
Map<String, dynamic>时,优先改为dynamic或定义强类型模型。 - 运行时 4xx/5xx → 用
HttpResponse检查response.statusCode与response.data并结合 LogInterceptor。 - iOS/Android 真机无网络 → 检查代理/网络权限/域名可达性(可先用
https://httpbin.org/get连通性测试)。
8. 最小连通性测试(推荐先跑通)
dart
final api = ApiService(BaseDio.create(baseUrl: 'https://httpbin.org'), baseUrl: 'https://httpbin.org');
final resp = await api.httpBinGet();
print(resp.data['url']); // 期望输出: https://httpbin.org/get