Skip to content

网络请求

Flutter 应用经常需要与后端 API 交互。本章介绍两种网络请求方案:基础的 http 包和功能更强大的 dio 包。

http — 基础网络请求

http 是 Dart 官方提供的 HTTP 客户端,简单易用。

安装

bash
flutter pub add http

GET 请求

dart
import 'dart:convert';
import 'package:http/http.dart' as http;

Future<void> fetchUsers() async {
  final response = await http.get(Uri.parse('https://api.example.com/users'));

  if (response.statusCode == 200) {
    final data = jsonDecode(response.body);
    // 解析 JSON
  } else {
    throw Exception('请求失败: ${response.statusCode}');
  }
}

POST 请求

dart
import 'dart:convert';
import 'package:http/http.dart' as http;

Future<void> createUser() async {
  final response = await http.post(
    Uri.parse('https://api.example.com/users'),
    headers: {'Content-Type': 'application/json'},
    body: jsonEncode({'name': '张三', 'age': 25}),
  );
}

常用方法

方法说明
http.get()GET 请求
http.post()POST 请求
http.put()PUT 请求
http.patch()PATCH 请求
http.delete()DELETE 请求

dio — 功能强大的网络请求

dio 是 Flutter 社区最流行的 HTTP 客户端,支持拦截器、取消请求、文件上传下载、超时重试等功能。

安装

bash
flutter pub add dio

基本用法

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

final dio = Dio();

// GET 请求
final response = await dio.get(
  'https://api.example.com/users',
  queryParameters: {'page': 1, 'size': 20},
);

// POST 请求
final response = await dio.post(
  'https://api.example.com/users',
  data: {'name': '张三', 'age': 25},
);

// 获取响应数据
final data = response.data;        // 自动解析(根据 responseType)
final statusCode = response.statusCode;
final headers = response.headers;

BaseOptions — 全局配置

dart
final dio = Dio(BaseOptions(
  baseUrl: 'https://api.example.com',
  connectTimeout: Duration(seconds: 10),
  receiveTimeout: Duration(seconds: 10),
  sendTimeout: Duration(seconds: 10),
  headers: {
    'Content-Type': 'application/json',
  },
));

拦截器

拦截器可以在请求前/后统一处理,如添加 Token、处理错误等:

dart
dio.interceptors.add(InterceptorsWrapper(
  onRequest: (options, handler) {
    // 请求前:添加 Token
    final token = StorageService.getToken();
    if (token != null) {
      options.headers['Authorization'] = 'Bearer $token';
    }
    handler.next(options);  // 继续请求
  },
  onResponse: (response, handler) {
    // 响应后:统一处理
    handler.next(response);
  },
  onError: (error, handler) {
    // 错误处理
    if (error.response?.statusCode == 401) {
      // Token 过期,跳转登录
    }
    handler.next(error);
  },
));

日志拦截器

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

取消请求

dart
final cancelToken = CancelToken();

// 发起请求时传入
dio.get('/users', cancelToken: cancelToken);

// 取消请求
cancelToken.cancel('用户取消了请求');

文件下载

dart
await dio.download(
  'https://example.com/file.pdf',
  '/path/to/save/file.pdf',
  onReceiveProgress: (received, total) {
    if (total != -1) {
      print('下载进度: ${(received / total * 100).toStringAsFixed(0)}%');
    }
  },
);

文件上传

dart
final formData = FormData.fromMap({
  'name': '张三',
  'file': await MultipartFile.fromFile(
    '/path/to/file.jpg',
    filename: 'photo.jpg',
  ),
});

final response = await dio.post('/upload', data: formData);

超时与重试

dart
try {
  final response = await dio.get('/users');
} on DioException catch (e) {
  switch (e.type) {
    case DioExceptionType.connectionTimeout:
      print('连接超时');
      break;
    case DioExceptionType.sendTimeout:
      print('发送超时');
      break;
    case DioExceptionType.receiveTimeout:
      print('接收超时');
      break;
    case DioExceptionType.connectionError:
      print('网络连接失败');
      break;
    default:
      print('其他错误: ${e.message}');
  }
}

封装 Dio 单例

dart
class HttpService {
  static final HttpService _instance = HttpService._();
  factory HttpService() => _instance;
  HttpService._();

  late final Dio _dio;

  void init() {
    _dio = Dio(BaseOptions(
      baseUrl: 'https://api.example.com',
      connectTimeout: Duration(seconds: 10),
      receiveTimeout: Duration(seconds: 10),
    ));

    _dio.interceptors.add(InterceptorsWrapper(
      onRequest: (options, handler) {
        final token = StorageService.getToken();
        if (token != null) {
          options.headers['Authorization'] = 'Bearer $token';
        }
        handler.next(options);
      },
      onError: (error, handler) async {
        if (error.response?.statusCode == 401) {
          // Token 过期处理
        }
        handler.next(error);
      },
    ));
  }

  Future<Response> get(String path, {Map<String, dynamic>? queryParameters}) {
    return _dio.get(path, queryParameters: queryParameters);
  }

  Future<Response> post(String path, {dynamic data}) {
    return _dio.post(path, data: data);
  }
}

http vs dio 选择

特性httpdio
学习成本
拦截器不支持支持
取消请求不支持支持
文件上传下载手动处理内置支持
超时重试手动处理内置支持
全局配置不支持支持
适用简单项目正式项目(推荐)

下一步

基于 Flutter 官方文档整理