一、为什么需要优化Flutter网络请求

在移动应用开发中,网络请求是必不可少的功能。Flutter作为跨平台框架,虽然提供了http库用于基础网络请求,但在实际项目中,我们往往需要更强大的功能,比如请求拦截、错误统一处理、文件上传下载、超时配置等。这时候,Dio库就派上用场了。

Dio是一个强大的Dart HTTP客户端,支持拦截器、全局配置、FormData、文件上传等高级功能。但如果不合理配置,可能会导致请求失败、性能下降、错误处理混乱等问题。因此,我们需要掌握如何优化Dio的使用方式。

二、Dio库的基本配置

在开始优化之前,我们先看看如何正确初始化Dio实例并进行基础配置。

import 'package:dio/dio.dart';

void main() {
  // 创建Dio实例
  final dio = Dio();

  // 基础配置
  dio.options.baseUrl = 'https://api.example.com'; // 设置基础URL
  dio.options.connectTimeout = Duration(seconds: 5); // 连接超时时间
  dio.options.receiveTimeout = Duration(seconds: 10); // 接收数据超时时间
  dio.options.headers = {
    'Content-Type': 'application/json', // 默认请求头
  };

  // 发起GET请求示例
  Future<void> fetchData() async {
    try {
      final response = await dio.get('/users');
      print(response.data);
    } catch (e) {
      print('请求失败: $e');
    }
  }
}

代码注释说明:

  1. baseUrl 设置请求的基础URL,后续请求可以只写路径部分。
  2. connectTimeoutreceiveTimeout 分别控制连接和接收数据的超时时间。
  3. headers 可以设置默认请求头,比如Content-Type

三、高级配置:拦截器与错误处理

Dio的强大之处在于它的拦截器机制,我们可以在请求发出前或响应返回后统一处理逻辑。

1. 请求拦截器

dio.interceptors.add(InterceptorsWrapper(
  onRequest: (RequestOptions options, RequestInterceptorHandler handler) {
    // 在请求发出前添加Token
    options.headers['Authorization'] = 'Bearer your_token_here';
    return handler.next(options); // 继续请求
  },
));

2. 响应拦截器

dio.interceptors.add(InterceptorsWrapper(
  onResponse: (Response response, ResponseInterceptorHandler handler) {
    // 统一处理响应数据
    if (response.statusCode == 200) {
      return handler.next(response);
    } else {
      // 可以在这里抛出自定义错误
      throw DioError(
        requestOptions: response.requestOptions,
        error: '服务器返回异常',
      );
    }
  },
));

3. 错误拦截器

dio.interceptors.add(InterceptorsWrapper(
  onError: (DioError e, ErrorInterceptorHandler handler) {
    // 统一处理错误
    if (e.response?.statusCode == 401) {
      print('用户未授权,跳转登录页');
    } else if (e.type == DioErrorType.connectTimeout) {
      print('网络连接超时');
    }
    return handler.next(e); // 继续传递错误
  },
));

拦截器的作用:

  • 请求拦截器:适合添加Token、修改请求参数等。
  • 响应拦截器:可以统一处理返回数据,比如解析JSON、判断状态码。
  • 错误拦截器:集中处理各种网络错误,比如超时、401未授权等。

四、文件上传与下载优化

Dio对文件操作的支持非常友好,我们可以轻松实现文件上传和下载功能。

1. 文件上传

Future<void> uploadFile(String filePath) async {
  final formData = FormData.fromMap({
    'file': await MultipartFile.fromFile(filePath),
    'description': '这是一个测试文件',
  });

  try {
    final response = await dio.post('/upload', data: formData);
    print('上传成功: ${response.data}');
  } catch (e) {
    print('上传失败: $e');
  }
}

2. 文件下载

Future<void> downloadFile(String url, String savePath) async {
  try {
    await dio.download(url, savePath,
        onReceiveProgress: (received, total) {
      // 显示下载进度
      print('已下载: ${(received / total * 100).toStringAsFixed(2)}%');
    });
    print('下载完成');
  } catch (e) {
    print('下载失败: $e');
  }
}

优化建议:

  • 大文件上传或下载时,建议显示进度条提升用户体验。
  • 可以使用CancelToken来支持取消操作,避免资源浪费。

五、实战:封装一个健壮的Dio客户端

在实际项目中,我们通常会把Dio封装成一个单独的类,方便统一管理。

class ApiClient {
  final Dio _dio = Dio();

  ApiClient() {
    // 基础配置
    _dio.options.baseUrl = 'https://api.example.com';
    _dio.options.connectTimeout = Duration(seconds: 5);

    // 添加拦截器
    _dio.interceptors.add(InterceptorsWrapper(
      onRequest: _addAuthHeader,
      onError: _handleError,
    ));
  }

  // 添加认证头
  void _addAuthHeader(RequestOptions options, RequestInterceptorHandler handler) {
    options.headers['Authorization'] = 'Bearer your_token_here';
    handler.next(options);
  }

  // 统一错误处理
  void _handleError(DioError e, ErrorInterceptorHandler handler) {
    if (e.response?.statusCode == 401) {
      // 跳转登录
    }
    handler.next(e);
  }

  // 封装GET请求
  Future<Response> get(String path, {Map<String, dynamic>? params}) async {
    try {
      return await _dio.get(path, queryParameters: params);
    } catch (e) {
      rethrow;
    }
  }

  // 封装POST请求
  Future<Response> post(String path, dynamic data) async {
    try {
      return await _dio.post(path, data: data);
    } catch (e) {
      rethrow;
    }
  }
}

封装的好处:

  1. 统一管理Dio配置,避免重复代码。
  2. 拦截器和错误处理集中维护,便于修改。
  3. 提供简洁的API,如get()post(),降低使用复杂度。

六、总结与最佳实践

通过本文的介绍,我们了解了如何优化Dio库的使用方式,包括基础配置、拦截器、文件操作和封装实践。

最佳实践建议:

  1. 合理设置超时时间:避免因网络波动导致长时间等待。
  2. 使用拦截器统一处理逻辑:比如Token管理、错误处理。
  3. 封装Dio客户端:提升代码复用性和可维护性。
  4. 注意异常捕获:避免未处理的错误导致应用崩溃。

Dio是Flutter网络请求的利器,掌握它的高级用法可以大幅提升开发效率和用户体验。希望本文能帮助你在实际项目中更好地使用Dio