Flutter: Dio + Retrofit 入门(面向 Android 开发者)

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.dartpart 路径不匹配文件名大小写。
  • "Map.fromJson 调用错误" → Retrofit 返回泛型为 Map<String, dynamic> 时,优先改为 dynamic 或定义强类型模型。
  • 运行时 4xx/5xx → 用 HttpResponse 检查 response.statusCoderesponse.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

相关推荐
晚霞的不甘2 小时前
社区、标准与未来:共建 Flutter 与 OpenHarmony 融合生态的可持续发展路径
安全·flutter·ui·架构
走在路上的菜鸟2 小时前
Android学Dart学习笔记第十一节 错误处理
android·笔记·学习·flutter
QuantumLeap丶3 小时前
《Flutter全栈开发实战指南:从零到高级》- 22 -插件开发与原生交互
android·flutter·ios
kirk_wang3 小时前
鸿蒙UI组件与Flutter Widget混合开发:原理、实践与踩坑指南
flutter·移动开发·跨平台·arkts·鸿蒙
西西学代码4 小时前
flutter---进度条(2)
前端·javascript·flutter
QuantumLeap丶4 小时前
《Flutter全栈开发实战指南:从零到高级》- 21 -响应式设计与适配
android·javascript·flutter·ios·前端框架
晚霞的不甘4 小时前
实战精要:构建企业级 Flutter + OpenHarmony 工业物联网(IIoT)监控平台
物联网·flutter
等你等了那么久5 小时前
Flutter路由3分钟学会
flutter·dart
百分三十七5 小时前
最新的 Dart sdk 安装教程
flutter·dart