当前位置: 首页 > news >正文

Flutter实现Retrofit风格的网络请求封装

以下是一个基于 Android Retrofit 思路封装的 Flutter 网络请求框架示例。这个封装会借鉴 Retrofit 的设计模式,包括定义接口、使用注解、以及通过单例模式管理网络请求客户端。

1. 添加依赖

首先,在 pubspec.yaml 文件中添加必要的依赖:

dependencies:dio: ^5.0.0retrofit: ^4.0.0json_annotation: ^4.8.0

2. 定义数据模型

使用 json_annotation 来定义数据模型,这类似于 Retrofit 中的 Gson 转换器。

import 'package:json_annotation/json_annotation.dart';part 'user.g.dart';()
class User {(name: 'id')int id;(name: 'name')String name;(name: 'email')String email;User({required this.id, required this.name, required this.email});factory User.fromJson(Map<String, dynamic> json) => _$UserFromJson(json);
}

3. 定义 API 接口

定义一个接口来描述网络请求,使用 Retrofit 的注解风格。

import 'package:retrofit/retrofit.dart';
import 'package:dio/dio.dart';part 'api_service.g.dart';()
abstract class ApiService {factory ApiService(Dio dio, {String baseUrl}) = _ApiService;("/users/{id}")Future<User> getUser(("id") int userId);("/users")Future<User> createUser(() User user);
}

4. 创建 Retrofit 实例

创建一个单例类来管理 Retrofit 实例,类似于 Retrofit 的初始化。

import 'package:dio/dio.dart';
import 'api_service.dart';class RetrofitClient {static final RetrofitClient _instance = RetrofitClient._internal();late ApiService _apiService;factory RetrofitClient() {return _instance;}RetrofitClient._internal() {final dio = Dio(BaseOptions(baseUrl: "https://api.example.com/"));_apiService = ApiService(dio);}ApiService get apiService => _apiService;
}

5. 使用封装的网络请求

在你的 Flutter 应用中使用封装的网络请求。

import 'package:flutter/material.dart';
import 'retrofit_client.dart';void main() {runApp(MyApp());
}class MyApp extends StatelessWidget {Widget build(BuildContext context) {return MaterialApp(home: Scaffold(appBar: AppBar(title: Text('Retrofit Example')),body: Center(child: ElevatedButton(onPressed: () async {try {var user = await RetrofitClient().apiService.getUser(1);print(user.name);} catch (e) {print(e);}},child: Text('Get User'),),),),);}
}

6. 生成代码

由于使用了 retrofitjson_annotation,你需要运行以下命令来生成代码:

flutter pub run build_runner build

总结

通过以上步骤,你可以创建一个类似于 Retrofit 的网络请求框架。这个框架利用了 Retrofit 的注解和接口定义方式,同时结合了 Flutter 的 Dio 库来实现网络请求。这种方式不仅代码清晰,而且易于维护和扩展。

下面是一个优化后的 Flutter 网络请求框架,增加了环境切换功能,并进行了全面增强:

完整封装方案(带环境切换)

// network/dio_client.dart
import 'package:dio/dio.dart';
import 'package:logger/logger.dart';class DioClient {static final DioClient _instance = DioClient._internal();late Dio _dio;final Logger _logger = Logger();String _baseUrl = "";Environment _currentEnv = Environment.dev;factory DioClient() => _instance;DioClient._internal() {_initDio();}void _initDio() {_dio = Dio(BaseOptions(baseUrl: _baseUrl,connectTimeout: const Duration(seconds: 30),receiveTimeout: const Duration(seconds: 30),));// 添加日志拦截器_dio.interceptors.add(InterceptorsWrapper(onRequest: (options, handler) {_logger.d("🚀 Request [${options.method}] ${options.uri}");_logger.d("Headers: ${options.headers}");if (options.data != null) {_logger.d("Body: ${options.data}");}return handler.next(options);},onResponse: (response, handler) {_logger.i("✅ Response [${response.statusCode}] ${response.requestOptions.uri}");_logger.i("Response: ${response.data}");return handler.next(response);},onError: (e, handler) {_logger.e("❌ Error [${e.response?.statusCode}] ${e.requestOptions.uri}");_logger.e("Error: ${e.message}\n${e.response?.data}");return handler.next(e);},));}// 环境切换void switchEnvironment(Environment env) {_currentEnv = env;_baseUrl = _getBaseUrlForEnvironment(env);_dio.options.baseUrl = _baseUrl;_logger.i("Environment switched to: ${env.name.toUpperCase()}");}String _getBaseUrlForEnvironment(Environment env) {switch (env) {case Environment.dev:return "https://dev.api.example.com/";case Environment.test:return "https://test.api.example.com/";case Environment.staging:return "https://staging.api.example.com/";case Environment.prod:return "https://api.example.com/";case Environment.custom:return "https://custom.api.example.com/";}}// 设置自定义域名(用于特殊场景)void setCustomBaseUrl(String url) {_baseUrl = url;_dio.options.baseUrl = url;_currentEnv = Environment.custom;_logger.i("Custom base URL set: $url");}// 获取当前环境Environment get currentEnvironment => _currentEnv;// 获取Dio实例Dio get dio => _dio;// 设置认证Tokenvoid setAuthToken(String token) {_dio.options.headers['Authorization'] = 'Bearer $token';_logger.i("Auth token set");}// 清除认证void clearAuth() {_dio.options.headers.remove('Authorization');_logger.i("Auth token cleared");}
}// 环境枚举
enum Environment {dev,     // 开发环境test,    // 测试环境staging, // 预发布环境prod,    // 生产环境custom,  // 自定义环境
}

环境配置管理器

// config/environment_manager.dart
import 'package:shared_preferences/shared_preferences.dart';
import '../network/dio_client.dart';class EnvironmentManager {static const String _envKey = 'current_environment';// 初始化环境配置static Future<void> init() async {final prefs = await SharedPreferences.getInstance();final envIndex = prefs.getInt(_envKey) ?? Environment.dev.index;DioClient().switchEnvironment(Environment.values[envIndex]);}// 切换并保存环境static Future<void> switchAndSave(Environment env) async {final prefs = await SharedPreferences.getInstance();await prefs.setInt(_envKey, env.index);DioClient().switchEnvironment(env);}// 获取当前环境名称static String get currentEnvName {final env = DioClient().currentEnvironment;return env.toString().split('.').last.toUpperCase();}
}

增强版 API 服务封装

// api/base_api_service.dart
import 'package:retrofit/retrofit.dart';
import 'package:dio/dio.dart';part 'base_api_service.g.dart';()
abstract class BaseApiService {factory BaseApiService(Dio dio) = _BaseApiService;// 添加统一请求头({'requireAuth': true})("{path}")Future<HttpResponse> getRequest(("path") String path,() Map<String, dynamic>? query,);({'requireAuth': true})("{path}")Future<HttpResponse> postRequest(("path") String path,() Map<String, dynamic> body,);// 文件上传({'requireAuth': true})("{path}")()Future<HttpResponse> uploadFile(("path") String path,() File file,() String description,);
}

带环境感知的 API 服务

// api/user_service.dart
import 'package:retrofit/retrofit.dart';
import 'package:dio/dio.dart';
import '../models/user_model.dart';part 'user_service.g.dart';()
abstract class UserService {factory UserService(Dio dio) = _UserService;// 带环境切换感知的API端点("users/{id}")Future<User> getUser(("id") int id,("env") String? envOverride,);("users")Future<User> createUser(() User user);
}// 使用示例
UserService(DioClient().dio).getUser(1, EnvironmentManager.currentEnvName);

增强版拦截器(带环境处理)

// network/environment_interceptor.dart
import 'package:dio/dio.dart';
import 'package:shared_preferences/shared_preferences.dart';
import 'dio_client.dart';
import '../config/environment_manager.dart';class EnvironmentInterceptor extends Interceptor {void onRequest(RequestOptions options, RequestInterceptorHandler handler) {// 自动添加环境标识if (!options.queryParameters.containsKey('env')) {options.queryParameters['env'] = EnvironmentManager.currentEnvName;}// 环境特定处理final env = DioClient().currentEnvironment;if (env == Environment.dev) {options.headers['X-Debug-Mode'] = 'true';}// 认证处理final requireAuth = options.extra['requireAuth'] ?? false;if (requireAuth) {// 这里添加认证逻辑}handler.next(options);}void onError(DioException err, ErrorInterceptorHandler handler) {// 环境特定的错误处理if (DioClient().currentEnvironment == Environment.dev) {err.error = "DEV ERROR: ${err.message}";}handler.next(err);}
}

初始化流程

// main.dart
void main() async {WidgetsFlutterBinding.ensureInitialized();// 初始化环境配置await EnvironmentManager.init();// 添加环境拦截器DioClient().dio.interceptors.add(EnvironmentInterceptor());runApp(MyApp());
}// 环境切换UI示例
class EnvironmentSwitcher extends StatelessWidget {Widget build(BuildContext context) {return PopupMenuButton<Environment>(onSelected: (env) => EnvironmentManager.switchAndSave(env),itemBuilder: (context) => [_buildMenuItem(Environment.dev, '开发环境'),_buildMenuItem(Environment.test, '测试环境'),_buildMenuItem(Environment.staging, '预发环境'),_buildMenuItem(Environment.prod, '生产环境'),],child: Chip(label: Text('Env: ${EnvironmentManager.currentEnvName}'),backgroundColor: _getEnvColor(DioClient().currentEnvironment),),);}Color _getEnvColor(Environment env) {switch (env) {case Environment.dev: return Colors.blue;case Environment.test: return Colors.orange;case Environment.staging: return Colors.purple;case Environment.prod: return Colors.green;default: return Colors.grey;}}PopupMenuItem<Environment> _buildMenuItem(Environment env, String text) {return PopupMenuItem(value: env,child: ListTile(leading: Icon(Icons.developer_mode, color: _getEnvColor(env)),title: Text(text),trailing: DioClient().currentEnvironment == env? Icon(Icons.check, color: Colors.green): null,),);}
}

环境切换架构设计

lib/
├── network/
│   ├── dio_client.dart         # Dio 核心封装
│   ├── environment.dart        # 环境枚举
│   ├── environment_interceptor.dart # 环境感知拦截器
│   ├── api_interceptor.dart    # 通用API拦截器
│   └── error_handler.dart      # 统一错误处理
├── config/
│   ├── environment_config.dart # 环境配置
│   └── environment_manager.dart# 环境管理器
├── api/
│   ├── base_api_service.dart   # 基础API服务
│   ├── user_service.dart       # 用户API服务
│   ├── product_service.dart    # 产品API服务
│   └── *.g.dart               # 生成的代码
├── models/                     # 数据模型
└── utils/├── debug_tools.dart        # 调试工具(含环境切换UI)└── env_banner.dart         # 环境标识组件

关键特性说明

  1. 多环境管理

    • 5种预设环境:开发/测试/预发/生产/自定义
    • 环境切换自动更新所有API端点
    • 持久化保存环境选择
  2. 环境感知拦截器

    • 自动添加环境标识参数
    • 环境特定的头部信息
    • 差异化的错误处理策略
  3. 可视化环境控制

    • 应用内环境切换面板
    • 环境标识水印
    • 不同环境的视觉区分
  4. 环境特定配置

    // config/environment_config.dart
    Map<Environment, Map<String, dynamic>> configs = {Environment.dev: {'apiKey': 'dev_123456','analytics': false,'logLevel': 'verbose'},Environment.prod: {'apiKey': 'prod_abcdef','analytics': true,'logLevel': 'error'}
    };dynamic getConfig(String key) {return configs[DioClient().currentEnvironment]?[key];
    }
    
  5. 调试工具集成

    // utils/debug_tools.dart
    void showDebugPanel(BuildContext context) {showModalBottomSheet(context: context,builder: (ctx) => Column(children: [EnvironmentSwitcher(),SwitchListTile(title: Text('Mock API'),value: _isMockEnabled,onChanged: (v) => DioClient().toggleMockMode(v),),// 更多调试选项...],),);
    }
    
  6. 环境标识组件

    // utils/env_banner.dart
    class EnvBanner extends StatelessWidget {Widget build(BuildContext context) {if (DioClient().currentEnvironment == Environment.prod) {return SizedBox.shrink();}return Banner(location: BannerLocation.topEnd,message: EnvironmentManager.currentEnvName,color: _getEnvColor(DioClient().currentEnvironment),textStyle: TextStyle(fontWeight: FontWeight.bold),);}
    }
    

使用示例

// 获取带环境感知的API服务
UserService userService = UserService(DioClient().dio);// 调用API
try {final user = await userService.getUser(1);print('User data: $user');// 创建用户final newUser = User(name: 'John', email: 'john@example.com');final createdUser = await userService.createUser(newUser);} on DioException catch (e) {// 统一错误处理showErrorDialog(e.toUiMessage());
}// 切换环境
void switchToProduction() {EnvironmentManager.switchAndSave(Environment.prod);
}// 设置自定义端点
void setCustomEndpoint() {DioClient().setCustomBaseUrl('https://custom.api.server/');
}

优势总结

  1. 灵活的环境管理

    • 一键切换多个预设环境
    • 支持自定义临时端点
    • 环境配置持久化存储
  2. 全面的环境感知

    • 自动添加环境标识
    • 差异化的请求处理
    • 环境特定的日志级别
  3. 增强的调试能力

    • 可视化环境切换面板
    • 环境标识水印
    • 集成Mock模式开关
  4. 生产就绪架构

    • 环境隔离的安全保障
    • 自动化的配置管理
    • 敏感操作的权限控制
  5. 扩展性强

    • 轻松添加新环境
    • 自定义环境配置
    • 拦截器链可扩展

这个框架完美融合了 Retrofit 的声明式 API 风格和环境切换能力,适合从开发到生产全生命周期的应用需求,特别适合需要频繁切换环境的团队协作场景。

http://www.dtcms.com/a/299158.html

相关文章:

  • Qt 线程同步机制:互斥锁、信号量等
  • VTK交互——ImageRegion
  • Mixture-of-Recursions: 混合递归模型,通过学习动态递归深度,以实现对自适应Token级计算的有效适配
  • RK3568笔记九十二:QT使用Opencv显示摄像头
  • 基于RK3588+国产实时系统的隧道掘进机智能操控终端应用
  • NOIP普及组|2009T1多项式输出
  • 20250726让荣品的PRO-RK3566开发板通过TF卡刷Buildroot系统
  • 详解力扣高频SQL50题之1141. 查询近30天活跃用户数【简单】
  • 工具 | 解决 VSCode 中的 Delete CR 问题
  • 黑屏运维OceanBase数据库的常见案例
  • Java中配置两个r2db连接不同的数据库
  • LeetCode 854:相似度为 K 的字符串
  • RabbitMQ面试精讲 Day 5:Virtual Host与权限控制
  • 力扣 hot100 Day56
  • 香港本地和国际金融科技应用
  • 比特币如何实现去中心化?技术架构与机制解析
  • 2025年7月26日训练日志
  • MySQL 基本查询
  • SGLang 核心技术详解
  • IPSec IKE端口理解
  • Bert项目--新闻标题文本分类
  • 备份一下我的 mac mini 的环境变量配置情况
  • Java基础-文件操作
  • 尝试几道算法题,提升python编程思维
  • JavaSE:初识数组
  • 7月26日星期六今日早报简报微语报早读
  • kafka的消息存储机制和查询机制
  • Android Data Binding 深度解析与实践指南
  • Ubuntu22.04提示找不到python命令的解决方案
  • 测试人员先写测试要点,还是 测试场景?