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

Flutter网络请求封装:高效、灵活、易用的Dio工具类

在Flutter开发中,网络请求是必不可少的功能。为了简化代码、提高开发效率,我们通常会封装一个网络请求工具类。本文基于Dio库,详细介绍如何封装一个高效、灵活、易用的网络请求工具类,支持以下功能:

  1. 单例模式:确保全局只有一个Dio实例,避免资源浪费。
  2. 动态配置:支持运行时动态修改baseUrlheaders等配置。
  3. 拦截器:内置日志拦截器,并支持添加自定义拦截器。
  4. 错误处理:提供详细的错误信息,支持自定义错误处理逻辑。
  5. 文件上传和下载:封装了文件上传和下载功能。
  6. 取消请求:支持取消正在进行的请求。
  7. 模块化设计:代码结构清晰,便于维护和扩展。

通过本文,你将学会如何封装一个功能强大的网络请求工具类,并直接应用到你的Flutter项目中。


代码实现

import 'package:dio/dio.dart';
import 'package:flutter/foundation.dart';

class HttpUtil {
  // 单例模式
  static final HttpUtil _instance = HttpUtil._internal();
  factory HttpUtil() => _instance;
  HttpUtil._internal() {
    _init();
  }

  late Dio _dio;
  final List<Interceptor> _interceptors = []; // 自定义拦截器列表
  CancelToken _cancelToken = CancelToken(); // 用于取消请求

  // 初始化
  void _init() {
    _dio = Dio(BaseOptions(
      baseUrl: 'https://your-api-url.com', // 默认基础地址
      connectTimeout: const Duration(seconds: 5), // 连接超时时间
      receiveTimeout: const Duration(seconds: 5), // 接收数据超时时间
      headers: {
        'Content-Type': 'application/json; charset=UTF-8',
      },
    ));

    // 添加默认拦截器
    _dio.interceptors.add(InterceptorsWrapper(
      onRequest: (options, handler) {
        if (kDebugMode) {
          print('Request: ${options.method} ${options.path}');
        }
        return handler.next(options);
      },
      onResponse: (response, handler) {
        if (kDebugMode) {
          print('Response: ${response.statusCode} ${response.data}');
        }
        return handler.next(response);
      },
      onError: (DioException e, handler) {
        if (kDebugMode) {
          print('Error: ${e.message}');
        }
        return handler.next(e);
      },
    ));

    // 添加自定义拦截器
    for (var interceptor in _interceptors) {
      _dio.interceptors.add(interceptor);
    }
  }

  // 添加自定义拦截器
  void addInterceptor(Interceptor interceptor) {
    _interceptors.add(interceptor);
    _dio.interceptors.add(interceptor);
  }

  // 动态更新基础配置
  void updateBaseConfig({
    String? baseUrl,
    Duration? connectTimeout,
    Duration? receiveTimeout,
    Map<String, dynamic>? headers,
  }) {
    _dio.options.baseUrl = baseUrl ?? _dio.options.baseUrl;
    _dio.options.connectTimeout = connectTimeout ?? _dio.options.connectTimeout;
    _dio.options.receiveTimeout = receiveTimeout ?? _dio.options.receiveTimeout;
    _dio.options.headers = headers ?? _dio.options.headers;
  }

  // GET请求
  Future<Response> get(
    String path, {
    Map<String, dynamic>? queryParameters,
    Options? options,
    CancelToken? cancelToken,
  }) async {
    return _request(
      path,
      method: 'GET',
      queryParameters: queryParameters,
      options: options,
      cancelToken: cancelToken,
    );
  }

  // POST请求
  Future<Response> post(
    String path, {
    dynamic data,
    Map<String, dynamic>? queryParameters,
    Options? options,
    CancelToken? cancelToken,
  }) async {
    return _request(
      path,
      method: 'POST',
      data: data,
      queryParameters: queryParameters,
      options: options,
      cancelToken: cancelToken,
    );
  }

  // PUT请求
  Future<Response> put(
    String path, {
    dynamic data,
    Map<String, dynamic>? queryParameters,
    Options? options,
    CancelToken? cancelToken,
  }) async {
    return _request(
      path,
      method: 'PUT',
      data: data,
      queryParameters: queryParameters,
      options: options,
      cancelToken: cancelToken,
    );
  }

  // DELETE请求
  Future<Response> delete(
    String path, {
    dynamic data,
    Map<String, dynamic>? queryParameters,
    Options? options,
    CancelToken? cancelToken,
  }) async {
    return _request(
      path,
      method: 'DELETE',
      data: data,
      queryParameters: queryParameters,
      options: options,
      cancelToken: cancelToken,
    );
  }

  // 文件上传
  Future<Response> upload(
    String path, {
    required String filePath,
    Map<String, dynamic>? data,
    Map<String, dynamic>? queryParameters,
    Options? options,
    CancelToken? cancelToken,
  }) async {
    final formData = FormData.fromMap({
      ...data ?? {},
      'file': await MultipartFile.fromFile(filePath),
    });
    return _request(
      path,
      method: 'POST',
      data: formData,
      queryParameters: queryParameters,
      options: options,
      cancelToken: cancelToken,
    );
  }

  // 文件下载
  Future<Response> download(
    String urlPath,
    String savePath, {
    Map<String, dynamic>? queryParameters,
    Options? options,
    CancelToken? cancelToken,
  }) async {
    try {
      final response = await _dio.download(
        urlPath,
        savePath,
        queryParameters: queryParameters,
        options: options,
        cancelToken: cancelToken ?? _cancelToken,
      );
      return response;
    } on DioException catch (e) {
      throw _handleError(e);
    }
  }

  // 通用请求方法
  Future<Response> _request(
    String path, {
    required String method,
    dynamic data,
    Map<String, dynamic>? queryParameters,
    Options? options,
    CancelToken? cancelToken,
  }) async {
    try {
      final response = await _dio.request(
        path,
        data: data,
        queryParameters: queryParameters,
        options: Options(method: method),
        cancelToken: cancelToken ?? _cancelToken,
      );
      return response;
    } on DioException catch (e) {
      throw _handleError(e);
    }
  }

  // 取消请求
  void cancelRequests({CancelToken? cancelToken}) {
    if (cancelToken == null) {
      _cancelToken.cancel('Request cancelled');
      _cancelToken = CancelToken(); // 重置CancelToken
    } else {
      cancelToken.cancel('Request cancelled');
    }
  }

  // 错误处理
  String _handleError(DioException e) {
    switch (e.type) {
      case DioExceptionType.connectionTimeout:
        return '连接超时';
      case DioExceptionType.sendTimeout:
        return '发送请求超时';
      case DioExceptionType.receiveTimeout:
        return '接收数据超时';
      case DioExceptionType.badResponse:
        return '服务器返回错误: ${e.response?.statusCode}';
      case DioExceptionType.cancel:
        return '请求已取消';
      case DioExceptionType.unknown:
        return '未知错误: ${e.message}';
      default:
        return '网络错误: ${e.message}';
    }
  }
}

使用示例

void fetchData() async {
  try {
    final response = await HttpUtil().get('/api/data');
    print('Data: ${response.data}');
  } catch (e) {
    print('Error: $e');
  }
}

void uploadFile() async {
  try {
    final response = await HttpUtil().upload(
      '/api/upload',
      filePath: '/path/to/file',
    );
    print('Upload Response: ${response.data}');
  } catch (e) {
    print('Error: $e');
  }
}

void cancelRequest() {
  HttpUtil().cancelRequests();
}

总结

通过封装这样一个网络请求工具类,我们可以显著提高Flutter项目的开发效率,减少重复代码,同时增强代码的可维护性和扩展性。希望本文对你有所帮助

相关文章:

  • 第十三章:优化内存管理_《C++性能优化指南》_notes
  • 强大的AI网站推荐(第四集)—— Gamma
  • Pre-flash和Main flash
  • 机器学习——CatBoost、目标变量统计、排序提升算法
  • 《TCP/IP网络编程》学习笔记 | Chapter 22:重叠 I/O 模型
  • 重复出现的数字
  • ‌GraphRAG 知识图谱,设置适配阿里云百炼平台实战教程【上】
  • 【大模型基础_毛玉仁】4.3 参数选择方法
  • python笔记之判断月份有多少天
  • 【商城实战(72)】解锁用户评价与晒单功能开发秘籍
  • 【第2月_day10】Pandas数据查看与选择
  • MySQL查询语句的使用
  • TypeScript实现二分查找算法:原理剖析与最佳实践
  • 网页的性能优化
  • 一. 相机模组摆放原理
  • OpenAI深夜直播「偷袭」谷歌!GPT-4o原生图像生成:奥特曼带梗图,AGI战场再燃战火
  • 阶段二:面向对象编程
  • 生活电子常识——cmd不能使用anaconda的python环境,导致输入python打开应用商店
  • 文件上传绕过的小点总结(6)
  • Linux之 权限提升(Linux Privilege Escalation)
  • 特朗普称即将与伊朗达成核协议,外交部:中方愿继续发挥建设性作用
  • 中国情怀:时代记录与家国镜相|澎湃·镜相第三届非虚构写作大赛征稿启事
  • 刘国中将出席第78届世界卫生大会并顺访瑞士、访问白俄罗斯
  • 中国物流集团等10家央企11名领导人员职务任免
  • 德州国资欲退出三东筑工,后者大股东系当地房企东海集团
  • 龚正会见哥伦比亚总统佩特罗