flutter网络http请求入门教程-dio

Dio HTTP 请求完整教程

目录

  1. [什么是 Dio](#什么是 Dio)
  2. 环境安装
  3. [创建 Dio 实例](#创建 Dio 实例)
  4. 基础请求方法
  5. 请求配置
  6. 响应处理
  7. 拦截器
  8. 错误处理
  9. 取消请求
  10. 文件上传与下载
  11. [与 Riverpod 集成](#与 Riverpod 集成)
  12. 最佳实践

什么是 Dio

Dio 是一个强大的 Dart HTTP 客户端,专为 Flutter 设计。它提供了比 Dart 官方 http 包更丰富的功能:

  • 支持全局配置和拦截器
  • 支持请求/响应拦截、错误统一处理
  • 支持文件上传、下载进度监听
  • 支持请求取消
  • 支持自定义适配器、转换器

环境安装

在 pubspec.yaml 中添加依赖:

yaml 复制代码
dependencies:
  dio: ^5.5.0

然后执行:

bash 复制代码
flutter pub get

创建 Dio 实例

推荐将 Dio 实例封装为单例,便于全局统一配置。

dart 复制代码
import 'package:dio/dio.dart';

class DioClient {
  static final DioClient _instance = DioClient._internal();
  late final Dio dio;

  factory DioClient() => _instance;

  DioClient._internal() {
    dio = Dio(
      BaseOptions(
        baseUrl: 'https://api.example.com/v1',
        connectTimeout: const Duration(seconds: 10),
        receiveTimeout: const Duration(seconds: 10),
        headers: {
          'Accept': 'application/json',
          'Content-Type': 'application/json',
        },
      ),
    );
  }
}

使用:

dart 复制代码
final response = await DioClient().dio.get('/users');

基础请求方法

GET 请求

dart 复制代码
Future<List<User>> fetchUsers() async {
  final response = await DioClient().dio.get('/users');
  final List<dynamic> data = response.data;
  return data.map((json) => User.fromJson(json)).toList();
}

带查询参数

dart 复制代码
Future<List<User>> searchUsers(String keyword) async {
  final response = await DioClient().dio.get(
    '/users',
    queryParameters: {'q': keyword, 'page': 1, 'limit': 20},
  );
  return (response.data as List).map((e) => User.fromJson(e)).toList();
}

POST 请求

dart 复制代码
Future<User> createUser(String name, String email) async {
  final response = await DioClient().dio.post(
    '/users',
    data: {'name': name, 'email': email},
  );
  return User.fromJson(response.data);
}

PUT / PATCH / DELETE

dart 复制代码
// 更新全部字段
await DioClient().dio.put('/users/1', data: {'name': 'Tom'});

// 更新部分字段
await DioClient().dio.patch('/users/1', data: {'name': 'Tom'});

// 删除
await DioClient().dio.delete('/users/1');

请求配置

单请求覆盖配置

dart 复制代码
final response = await DioClient().dio.get(
  '/slow-endpoint',
  options: Options(
    receiveTimeout: const Duration(seconds: 30),
    headers: {'X-Custom-Header': 'value'},
  ),
);

常用配置项

dart 复制代码
BaseOptions(
  baseUrl: 'https://api.example.com',
  connectTimeout: const Duration(seconds: 5),
  receiveTimeout: const Duration(seconds: 5),
  sendTimeout: const Duration(seconds: 5),
  headers: {'Authorization': 'Bearer $token'},
  responseType: ResponseType.json,
  validateStatus: (status) => status != null && status < 500,
)

响应处理

Dio 的 Response 对象包含:

  • data:响应体
  • statusCode:HTTP 状态码
  • statusMessage:状态信息
  • headers:响应头
  • requestOptions:请求配置
dart 复制代码
final response = await DioClient().dio.get('/users/1');

if (response.statusCode == 200) {
  final user = User.fromJson(response.data);
}

拦截器

拦截器常用于:统一添加 token、打印日志、处理 401 重试等。

dart 复制代码
DioClient._internal() {
  dio = Dio(BaseOptions(baseUrl: 'https://api.example.com'));

  dio.interceptors.add(
    InterceptorsWrapper(
      onRequest: (options, handler) async {
        // 添加 Token
        final token = await _getToken();
        options.headers['Authorization'] = 'Bearer $token';
        return handler.next(options);
      },
      onResponse: (response, handler) {
        // 统一处理响应
        debugPrint('Response: ${response.data}');
        return handler.next(response);
      },
      onError: (DioException e, handler) {
        // 统一处理错误
        if (e.response?.statusCode == 401) {
          // 跳转登录或刷新 token
        }
        return handler.next(e);
      },
    ),
  );
}

日志拦截器

dart 复制代码
import 'package:dio/io.dart';

dio.interceptors.add(LogInterceptor(
  requestBody: true,
  responseBody: true,
));

错误处理

Dio 请求失败时会抛出 DioException。

dart 复制代码
Future<User?> fetchUser(String id) async {
  try {
    final response = await DioClient().dio.get('/users/$id');
    return User.fromJson(response.data);
  } on DioException catch (e) {
    switch (e.type) {
      case DioExceptionType.connectionTimeout:
        debugPrint('连接超时');
        break;
      case DioExceptionType.badResponse:
        debugPrint('服务器错误: ${e.response?.statusCode}');
        break;
      case DioExceptionType.cancel:
        debugPrint('请求被取消');
        break;
      default:
        debugPrint('请求失败: ${e.message}');
    }
    return null;
  } catch (e) {
    debugPrint('未知错误: $e');
    return null;
  }
}

封装 Result 类型

dart 复制代码
sealed class Result<T> {}

class Success<T> extends Result<T> {
  Success(this.data);
  final T data;
}

class Failure<T> extends Result<T> {
  Failure(this.message);
  final String message;
}

Future<Result<User>> fetchUserSafe(String id) async {
  try {
    final response = await DioClient().dio.get('/users/$id');
    return Success(User.fromJson(response.data));
  } on DioException catch (e) {
    return Failure(e.message ?? '请求失败');
  }
}

取消请求

使用 CancelToken 取消未完成的请求。

dart 复制代码
class UserRepository {
  CancelToken? _cancelToken;

  Future<void> fetchUsers() async {
    _cancelToken = CancelToken();
    try {
      final response = await DioClient().dio.get(
        '/users',
        cancelToken: _cancelToken,
      );
      // 处理数据
    } on DioException catch (e) {
      if (CancelToken.isCancel(e)) {
        debugPrint('请求已取消');
      }
    }
  }

  void cancel() {
    _cancelToken?.cancel();
    _cancelToken = null;
  }
}

文件上传与下载

文件上传

dart 复制代码
Future<void> uploadAvatar(String filePath) async {
  final formData = FormData.fromMap({
    'file': await MultipartFile.fromFile(filePath, filename: 'avatar.jpg'),
  });

  final response = await DioClient().dio.post(
    '/upload',
    data: formData,
    onSendProgress: (sent, total) {
      debugPrint('上传进度: ${(sent / total * 100).toStringAsFixed(0)}%');
    },
  );
}

文件下载

dart 复制代码
Future<void> downloadFile(String url, String savePath) async {
  await DioClient().dio.download(
    url,
    savePath,
    onReceiveProgress: (received, total) {
      if (total != -1) {
        debugPrint('下载进度: ${(received / total * 100).toStringAsFixed(0)}%');
      }
    },
  );
}

与 Riverpod 集成

将 Dio 封装为 Provider,可在 Riverpod 中统一管理。

dart 复制代码
import 'package:dio/dio.dart';
import 'package:riverpod_annotation/riverpod_annotation.dart';

part 'dio_provider.g.dart';

@riverpod
Dio dio(DioRef ref) {
  final dio = Dio(
    BaseOptions(
      baseUrl: 'https://api.example.com/v1',
      connectTimeout: const Duration(seconds: 10),
    ),
  );

  dio.interceptors.add(LogInterceptor(requestBody: true, responseBody: true));

  return dio;
}

在 Repository 中注入:

dart 复制代码
@riverpod
Future<User> user(UserRef ref, String id) async {
  final dio = ref.watch(dioProvider);
  final response = await dio.get('/users/$id');
  return User.fromJson(response.data);
}

在 Widget 中使用:

dart 复制代码
final asyncUser = ref.watch(userProvider('123'));

return asyncUser.when(
  data: (user) => Text(user.name),
  loading: () => const CircularProgressIndicator(),
  error: (err, _) => Text('错误: $err'),
);

最佳实践

  1. 单例封装:全局使用同一个 Dio 实例,统一 baseUrl、超时、拦截器。
  2. 统一错误处理:在拦截器或 Repository 层处理常见错误,Widget 只负责展示。
  3. 模型化数据 :将 response.data 转换为 Dart 模型类,避免直接使用 Map。
  4. 使用 CancelToken:页面销毁或用户刷新时取消旧请求,防止内存泄漏和状态异常。
  5. 环境隔离:通过环境变量切换 baseUrl(开发/测试/生产)。
  6. 与 Riverpod 结合:将 Dio 和 API 方法声明为 Provider,便于测试和依赖替换。
  7. 避免在 UI 层直接调用 Dio:由 Repository/Service 层封装网络请求,UI 只调用 Riverpod 状态。

结语

Dio 是 Flutter 网络请求的事实标准。掌握 Dio 的基础请求、拦截器、错误处理、取消请求、文件操作以及与 Riverpod 的结合,可以覆盖绝大多数移动应用的网络层需求。建议将 Dio 实例、API 方法、数据模型分层管理,保持代码清晰可维护。

相关推荐
码林鼠2 小时前
flutter BLoC 单向数据流完整教程
flutter
Lsetea2 小时前
curl访问HTTPS返回304却没有正文:条件请求、缓存与证书验收排查
linux·http·https·ssl证书·curl
zxanz14 小时前
免费 SSL 证书的隐性成本:续期压力、稳定性与商业风险评估
网络·网络协议·ssl
zzu123zsw4 小时前
面对多 RPC 扇入超时崩溃:深入 asyncTool 核心 DAG 编排与线程池隔离防护
java·网络·spring boot·网络协议·rpc·并发编程
福娃筱欢4 小时前
金仓数据库配置SSL双向认证
数据库·网络协议·ssl
K成长日志4 小时前
BLE Host层L2CAP -- 状态机
物联网·网络协议·无线通信·蓝牙·iot·ble
honsor5 小时前
ModbusTCP、SNMP、HTTP 怎么选?以太网温湿度变送器协议选型实战
网络·物联网·网络协议·tcp/ip·http
启博网络5 小时前
智慧档案库房工业组网:MR-1280 边缘大脑,实现温湿度、漏水、防火防盗设备智能联动教程
运维·网络·物联网·网络协议·网络安全·信息与通信·sdwan
wdfk_prog5 小时前
lwip教程 31:SLIP vs PPPoS——串口 IP Framing、错误检测、协商与工程边界
网络协议·tcp/ip·php