Flutter网络请求高级技巧

核心概念

网络请求基础

Flutter中常用的网络请求库包括httpdiohttp是Flutter官方提供的基础网络请求库,而dio是一个功能更强大的第三方网络请求库。

HTTP方法

  • GET:获取资源
  • POST:创建资源
  • PUT:更新资源
  • DELETE:删除资源
  • PATCH:部分更新资源

高级技巧

使用dio库

import 'package:dio/dio.dart';

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

// 基本GET请求
Future<void> getRequest() async {
  try {
    final response = await dio.get('https://api.example.com/users');
    print(response.data);
  } catch (e) {
    print(e);
  }
}

// 基本POST请求
Future<void> postRequest() async {
  try {
    final response = await dio.post('https://api.example.com/users', data: {
      'name': 'John Doe',
      'email': 'john@example.com'
    });
    print(response.data);
  } catch (e) {
    print(e);
  }
}

拦截器

// 添加请求拦截器
dio.interceptors.add(InterceptorsWrapper(
  onRequest: (options, handler) {
    // 在发送请求之前做一些处理
    print('请求路径: ${options.path}');
    print('请求方法: ${options.method}');
    print('请求参数: ${options.data}');
    
    // 添加请求头
    options.headers['Authorization'] = 'Bearer token';
    
    return handler.next(options);
  },
  onResponse: (response, handler) {
    // 在收到响应后做一些处理
    print('响应状态码: ${response.statusCode}');
    print('响应数据: ${response.data}');
    
    return handler.next(response);
  },
  onError: (DioError e, handler) {
    // 处理错误
    print('错误信息: ${e.message}');
    print('错误响应: ${e.response?.data}');
    
    return handler.next(e);
  },
));

错误处理

Future<void> handleError() async {
  try {
    final response = await dio.get('https://api.example.com/users');
    print(response.data);
  } on DioError catch (e) {
    if (e.response != null) {
      // 服务器返回错误
      print('状态码: ${e.response?.statusCode}');
      print('错误数据: ${e.response?.data}');
      
      switch (e.response?.statusCode) {
        case 400:
          print('请求参数错误');
          break;
        case 401:
          print('未授权,请重新登录');
          break;
        case 403:
          print('拒绝访问');
          break;
        case 404:
          print('资源不存在');
          break;
        case 500:
          print('服务器内部错误');
          break;
        default:
          print('未知错误');
      }
    } else {
      // 网络错误
      print('网络错误: ${e.message}');
    }
  } catch (e) {
    print('其他错误: $e');
  }
}

超时设置

// 设置全局超时
dio.options = BaseOptions(
  connectTimeout: Duration(seconds: 10),
  receiveTimeout: Duration(seconds: 10),
  sendTimeout: Duration(seconds: 10),
);

// 为单个请求设置超时
Future<void> requestWithTimeout() async {
  try {
    final response = await dio.get(
      'https://api.example.com/users',
      options: Options(
        connectTimeout: Duration(seconds: 5),
        receiveTimeout: Duration(seconds: 5),
      ),
    );
    print(response.data);
  } catch (e) {
    print(e);
  }
}

缓存策略

// 使用 dio_cache_interceptor 库
import 'package:dio_cache_interceptor/dio_cache_interceptor.dart';
import 'package:dio_cache_interceptor_hive_store/dio_cache_interceptor_hive_store.dart';
import 'package:path_provider/path_provider.dart';

Future<void> setupCache() async {
  // 获取缓存目录
  final dir = await getTemporaryDirectory();
  final cacheStore = HiveCacheStore(dir.path);
  
  // 配置缓存选项
  final cacheOptions = CacheOptions(
    store: cacheStore,
    policy: CachePolicy.request, // 缓存策略
    hitCacheOnErrorExcept: [401, 403], // 错误时缓存
    maxStale: Duration(days: 7), // 最大缓存时间
  );
  
  // 创建带缓存的 dio 实例
  final dio = Dio()..interceptors.add(DioCacheInterceptor(options: cacheOptions));
  
  // 使用缓存请求
  final response = await dio.get('https://api.example.com/users');
  print(response.data);
}

文件上传

// 上传单个文件
Future<void> uploadFile() async {
  try {
    final formData = FormData.fromMap({
      'name': 'John Doe',
      'file': await MultipartFile.fromFile('path/to/file.jpg', filename: 'file.jpg'),
    });
    
    final response = await dio.post('https://api.example.com/upload', data: formData);
    print(response.data);
  } catch (e) {
    print(e);
  }
}

// 上传多个文件
Future<void> uploadMultipleFiles() async {
  try {
    final formData = FormData.fromMap({
      'name': 'John Doe',
      'files': [
        await MultipartFile.fromFile('path/to/file1.jpg', filename: 'file1.jpg'),
        await MultipartFile.fromFile('path/to/file2.jpg', filename: 'file2.jpg'),
      ],
    });
    
    final response = await dio.post('https://api.example.com/upload', data: formData);
    print(response.data);
  } catch (e) {
    print(e);
  }
}

// 上传进度
Future<void> uploadWithProgress() async {
  try {
    final formData = FormData.fromMap({
      'file': await MultipartFile.fromFile('path/to/file.jpg', filename: 'file.jpg'),
    });
    
    final response = await dio.post(
      'https://api.example.com/upload',
      data: formData,
      onSendProgress: (int sent, int total) {
        print('上传进度: ${(sent / total * 100).toStringAsFixed(0)}%');
      },
    );
    print(response.data);
  } catch (e) {
    print(e);
  }
}

文件下载

// 基本下载
Future<void> downloadFile() async {
  try {
    final response = await dio.download(
      'https://example.com/file.jpg',
      'path/to/save/file.jpg',
    );
    print('下载完成');
  } catch (e) {
    print(e);
  }
}

// 下载进度
Future<void> downloadWithProgress() async {
  try {
    final response = await dio.download(
      'https://example.com/file.jpg',
      'path/to/save/file.jpg',
      onReceiveProgress: (int received, int total) {
        print('下载进度: ${(received / total * 100).toStringAsFixed(0)}%');
      },
    );
    print('下载完成');
  } catch (e) {
    print(e);
  }
}

// 断点续传
Future<void> resumeDownload() async {
  try {
    // 检查文件是否已存在
    final file = File('path/to/save/file.jpg');
    final fileLength = file.existsSync() ? file.lengthSync() : 0;
    
    final response = await dio.download(
      'https://example.com/file.jpg',
      'path/to/save/file.jpg',
      options: Options(
        headers: {'Range': 'bytes=$fileLength-'}, // 设置 Range 请求头
      ),
      onReceiveProgress: (int received, int total) {
        final actualReceived = received + fileLength;
        final actualTotal = total + fileLength;
        print('下载进度: ${(actualReceived / actualTotal * 100).toStringAsFixed(0)}%');
      },
    );
    print('下载完成');
  } catch (e) {
    print(e);
  }
}

最佳实践

网络状态管理

import 'package:connectivity_plus/connectivity_plus.dart';

// 检查网络状态
Future<void> checkNetworkStatus() async {
  final connectivityResult = await (Connectivity().checkConnectivity());
  if (connectivityResult == ConnectivityResult.none) {
    print('无网络连接');
  } else if (connectivityResult == ConnectivityResult.mobile) {
    print('移动网络');
  } else if (connectivityResult == ConnectivityResult.wifi) {
    print('WiFi网络');
  }
}

// 监听网络状态变化
void listenNetworkChanges() {
  Connectivity().onConnectivityChanged.listen((ConnectivityResult result) {
    if (result == ConnectivityResult.none) {
      print('无网络连接');
    } else if (result == ConnectivityResult.mobile) {
      print('移动网络');
    } else if (result == ConnectivityResult.wifi) {
      print('WiFi网络');
    }
  });
}

响应模型

// 定义响应模型
class User {
  final int id;
  final String name;
  final String email;

  User({required this.id, required this.name, required this.email});

  factory User.fromJson(Map<String, dynamic> json) {
    return User(
      id: json['id'],
      name: json['name'],
      email: json['email'],
    );
  }

  Map<String, dynamic> toJson() {
    return {
      'id': id,
      'name': name,
      'email': email,
    };
  }
}

// 使用响应模型
Future<void> getUser() async {
  try {
    final response = await dio.get('https://api.example.com/users/1');
    final user = User.fromJson(response.data);
    print('用户: ${user.name}');
  } catch (e) {
    print(e);
  }
}

网络请求封装

// 网络请求封装类
class ApiService {
  final Dio _dio;

  ApiService() : _dio = Dio() {
    // 配置基础选项
    _dio.options = BaseOptions(
      baseUrl: 'https://api.example.com',
      connectTimeout: Duration(seconds: 10),
      receiveTimeout: Duration(seconds: 10),
    );

    // 添加拦截器
    _dio.interceptors.add(InterceptorsWrapper(
      onRequest: (options, handler) {
        // 添加认证token
        final token = getToken();
        if (token != null) {
          options.headers['Authorization'] = 'Bearer $token';
        }
        return handler.next(options);
      },
      onError: (DioError e, handler) {
        // 处理401错误(未授权)
        if (e.response?.statusCode == 401) {
          // 跳转到登录页
          // Navigator.pushReplacementNamed(context, '/login');
        }
        return handler.next(e);
      },
    ));
  }

  String? getToken() {
    // 从本地存储获取token
    // 这里简化处理,实际应该使用shared_preferences或其他存储方案
    return 'your-token';
  }

  // GET请求
  Future<T> get<T>(String path, {Map<String, dynamic>? queryParameters, Options? options}) async {
    final response = await _dio.get(path, queryParameters: queryParameters, options: options);
    return response.data as T;
  }

  // POST请求
  Future<T> post<T>(String path, {dynamic data, Options? options}) async {
    final response = await _dio.post(path, data: data, options: options);
    return response.data as T;
  }

  // PUT请求
  Future<T> put<T>(String path, {dynamic data, Options? options}) async {
    final response = await _dio.put(path, data: data, options: options);
    return response.data as T;
  }

  // DELETE请求
  Future<T> delete<T>(String path, {Options? options}) async {
    final response = await _dio.delete(path, options: options);
    return response.data as T;
  }

  // 上传文件
  Future<T> upload<T>(String path, FormData formData, {void Function(int, int)? onSendProgress}) async {
    final response = await _dio.post(path, data: formData, onSendProgress: onSendProgress);
    return response.data as T;
  }

  // 下载文件
  Future<void> download(String url, String savePath, {void Function(int, int)? onReceiveProgress}) async {
    await _dio.download(url, savePath, onReceiveProgress: onReceiveProgress);
  }
}

// 使用示例
void main() async {
  final apiService = ApiService();
  
  // GET请求
  final users = await apiService.get<List<dynamic>>('/users');
  print(users);
  
  // POST请求
  final user = await apiService.post<Map<String, dynamic>>('/users', data: {
    'name': 'John Doe',
    'email': 'john@example.com'
  });
  print(user);
}

重试机制

// 重试机制
Future<T> retry<T>(Future<T> Function() fn, {int retryCount = 3, Duration delay = const Duration(seconds: 1)}) async {
  try {
    return await fn();
  } catch (e) {
    if (retryCount > 0) {
      print('请求失败,正在重试... ($retryCount)');
      await Future.delayed(delay);
      return retry(fn, retryCount: retryCount - 1, delay: delay);
    } else {
      rethrow;
    }
  }
}

// 使用重试机制
Future<void> fetchDataWithRetry() async {
  try {
    final response = await retry(() => dio.get('https://api.example.com/users'));
    print(response.data);
  } catch (e) {
    print('请求失败: $e');
  }
}

实用案例

完整的网络请求示例

import 'package:dio/dio.dart';
import 'package:connectivity_plus/connectivity_plus.dart';
import 'package:flutter/material.dart';

// 网络请求服务
class NetworkService {
  static final NetworkService _instance = NetworkService._internal();
  factory NetworkService() => _instance;
  NetworkService._internal();

  final Dio _dio = Dio();
  final Connectivity _connectivity = Connectivity();

  void init() {
    // 配置基础选项
    _dio.options = BaseOptions(
      baseUrl: 'https://api.example.com',
      connectTimeout: Duration(seconds: 10),
      receiveTimeout: Duration(seconds: 10),
    );

    // 添加拦截器
    _dio.interceptors.add(InterceptorsWrapper(
      onRequest: (options, handler) {
        print('请求: ${options.path}');
        return handler.next(options);
      },
      onResponse: (response, handler) {
        print('响应: ${response.statusCode}');
        return handler.next(response);
      },
      onError: (DioError e, handler) {
        print('错误: ${e.message}');
        return handler.next(e);
      },
    ));
  }

  // 检查网络连接
  Future<bool> isConnected() async {
    final result = await _connectivity.checkConnectivity();
    return result != ConnectivityResult.none;
  }

  // GET请求
  Future<T> get<T>(String path, {Map<String, dynamic>? queryParameters}) async {
    if (!await isConnected()) {
      throw Exception('无网络连接');
    }

    final response = await _dio.get(path, queryParameters: queryParameters);
    return response.data as T;
  }

  // POST请求
  Future<T> post<T>(String path, {dynamic data}) async {
    if (!await isConnected()) {
      throw Exception('无网络连接');
    }

    final response = await _dio.post(path, data: data);
    return response.data as T;
  }
}

// 数据模型
class User {
  final int id;
  final String name;
  final String email;

  User({required this.id, required this.name, required this.email});

  factory User.fromJson(Map<String, dynamic> json) {
    return User(
      id: json['id'],
      name: json['name'],
      email: json['email'],
    );
  }
}

// 数据仓库
class UserRepository {
  final NetworkService _networkService = NetworkService();

  Future<List<User>> getUsers() async {
    final data = await _networkService.get<List<dynamic>>('/users');
    return data.map((item) => User.fromJson(item)).toList();
  }

  Future<User> getUser(int id) async {
    final data = await _networkService.get<Map<String, dynamic>>('/users/$id');
    return User.fromJson(data);
  }

  Future<User> createUser(User user) async {
    final data = await _networkService.post<Map<String, dynamic>>('/users', data: user.toJson());
    return User.fromJson(data);
  }
}

// 使用示例
void main() async {
  // 初始化网络服务
  NetworkService().init();

  // 创建用户仓库
  final userRepository = UserRepository();

  try {
    // 获取用户列表
    final users = await userRepository.getUsers();
    print('用户列表: ${users.map((user) => user.name).toList()}');

    // 获取单个用户
    final user = await userRepository.getUser(1);
    print('用户: ${user.name}');

    // 创建用户
    final newUser = User(id: 0, name: 'John Doe', email: 'john@example.com');
    final createdUser = await userRepository.createUser(newUser);
    print('创建的用户: ${createdUser.name}');
  } catch (e) {
    print('错误: $e');
  }
}

网络请求状态管理

import 'package:flutter/material.dart';

// 网络请求状态
enum RequestStatus {
  initial,
  loading,
  success,
  error,
}

// 网络请求状态管理
class RequestState<T> {
  final RequestStatus status;
  final T? data;
  final String? error;

  RequestState({
    required this.status,
    this.data,
    this.error,
  });

  factory RequestState.initial() {
    return RequestState(status: RequestStatus.initial);
  }

  factory RequestState.loading() {
    return RequestState(status: RequestStatus.loading);
  }

  factory RequestState.success(T data) {
    return RequestState(status: RequestStatus.success, data: data);
  }

  factory RequestState.error(String error) {
    return RequestState(status: RequestStatus.error, error: error);
  }
}

// 使用示例
class UserListScreen extends StatefulWidget {
  @override
  _UserListScreenState createState() => _UserListScreenState();
}

class _UserListScreenState extends State<UserListScreen> {
  final UserRepository _userRepository = UserRepository();
  RequestState<List<User>> _state = RequestState.initial();

  @override
  void initState() {
    super.initState();
    fetchUsers();
  }

  Future<void> fetchUsers() async {
    setState(() {
      _state = RequestState.loading();
    });

    try {
      final users = await _userRepository.getUsers();
      setState(() {
        _state = RequestState.success(users);
      });
    } catch (e) {
      setState(() {
        _state = RequestState.error(e.toString());
      });
    }
  }

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(title: Text('用户列表')),
      body: Center(
        child: _state.status == RequestStatus.loading
            ? CircularProgressIndicator()
            : _state.status == RequestStatus.error
                ? Column(
                    mainAxisAlignment: MainAxisAlignment.center,
                    children: [
                      Text('错误: ${_state.error}'),
                      ElevatedButton(
                        onPressed: fetchUsers,
                        child: Text('重试'),
                      ),
                    ],
                  )
                : _state.status == RequestStatus.success
                    ? ListView.builder(
                        itemCount: _state.data!.length,
                        itemBuilder: (context, index) {
                          final user = _state.data![index];
                          return ListTile(
                            title: Text(user.name),
                            subtitle: Text(user.email),
                          );
                        },
                      )
                    : SizedBox(),
      ),
    );
  }
}

总结

Flutter网络请求是应用开发中的重要组成部分,通过掌握这些高级技巧,你可以创建更加健壮、高效的网络请求系统。

关键要点

  1. 选择合适的库:根据需求选择httpdio库。
  2. 使用拦截器:处理请求头、响应数据和错误。
  3. 错误处理:妥善处理各种网络错误。
  4. 超时设置:合理设置超时时间,避免请求无响应。
  5. 缓存策略:使用缓存提高性能和用户体验。
  6. 文件上传下载:支持文件的上传和下载,包括进度监听。
  7. 网络状态管理:检查和监听网络状态。
  8. 响应模型:使用强类型的响应模型,提高代码可维护性。
  9. 网络请求封装:封装网络请求,提高代码复用性。
  10. 重试机制:处理临时网络问题,提高请求成功率。

通过掌握这些高级技巧,你可以创建更加可靠、高效的网络请求系统,提升你的Flutter应用的用户体验。

更多推荐