You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何在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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.10 08:20:33