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

相关推荐
走在路上的菜鸟1 天前
Android学Flutter学习笔记 第五节 Android视角认知Flutter(插件plugins)
android·学习·flutter
奋斗的小青年!!1 天前
Flutter开发OpenHarmony打卡进度环组件:实现与跨平台兼容性实践
flutter·harmonyos·鸿蒙
消失的旧时光-19431 天前
Flutter 列表 + Riverpod 架构实战 —— 从 setState 到状态驱动列表的工程落地
flutter
消失的旧时光-19431 天前
GetX 从 0 开始:理解 Flutter 的“对象级响应式系统”
flutter
奋斗的小青年!!1 天前
Flutter跨平台开发鸿蒙应用:表情选择器组件的深度实践
flutter·harmonyos·鸿蒙
消失的旧时光-19431 天前
Flutter ListView 全解:从 RecyclerView 到声明式列表
flutter
恋猫de小郭1 天前
Compose Multiplatform 1.10 Interop views 新特性:Overlay 和 Autosizing
android·flutter·macos·kotlin·github·objective-c·cocoa
世人万千丶1 天前
鸿蒙跨端框架Flutter学习day 1、变量与基本类型-智能家居监控模型
学习·flutter·ui·智能家居·harmonyos·鸿蒙·鸿蒙系统
小白阿龙1 天前
flutter 与鸿蒙融合开发实战:构建跨平台应用的新范式
flutter·华为·harmonyos
世人万千丶1 天前
鸿蒙跨端框架Flutter学习day 1、变量与基本类型-咖啡店点餐逻辑
学习·flutter·ui·交互·鸿蒙·鸿蒙系统