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 方法、数据模型分层管理,保持代码清晰可维护。

相关推荐
Dream Cosmos32 分钟前
传输层协议之UDP
网络·网络协议·udp
梦到啥说啥3 小时前
Linux网络编程:UDP网络协议
linux·网络协议·udp
FungLeo5 小时前
成为全栈·Vue 管理后台篇·用 fetch 建一层可控的请求内核
前端·javascript·vue.js·http·fetch·formdata·接口封装
张小姐的猫6 小时前
【基于 WebSocket 的多用户网页五子棋】—— 项目结构设计与实用工具类模块
网络·websocket·网络协议
wuyk55512 小时前
Spider-第03讲-HTTP速通与读懂响应
网络·爬虫·网络协议·http
恋猫de小郭12 小时前
Flutter 3.50 & Dart 3.14 发布,快来看看有什么更新吧
android·前端·flutter
梦到啥说啥1 天前
Linux网络编程:TCP网络协议
网络·网络协议·tcp/ip
喵个咪1 天前
GoWind CMS 拆解:Flutter 设计系统内核——双态 token、四档断点与自适应壳层
前端·flutter·交互设计
Jeremy_WW1 天前
802.3协议解读 06:117章节 RS 和 200GMII / 400GMII I
网络·网络协议·信息与通信·光模块·802.3协议