Flutter开发实战之网络请求与数据处理

发布于:2025-07-27 ⋅ 阅读:(22) ⋅ 点赞:(0)

第6章:网络请求与数据处理

“数据是应用的血液,网络是连接世界的桥梁。”

在移动应用开发中,与服务器进行数据交互是必不可少的功能。无论是获取用户信息、提交表单数据,还是上传图片、下载文件,都离不开网络请求。本章将带你深入掌握Flutter中的网络编程技巧。

6.1 网络请求基础概念

6.1.1 什么是HTTP请求?

想象一下,你走进一家餐厅点餐的过程:

  1. 你告诉服务员想要什么(发送请求)
  2. 服务员把你的需求传达给厨房(请求到达服务器)
  3. 厨房准备你的餐点(服务器处理请求)
  4. 服务员把餐点端给你(接收响应)

HTTP请求就是这样一个过程,只不过是应用与服务器之间的"点餐"过程。

6.1.2 常见的HTTP方法

// GET:获取数据,就像询问菜单
// POST:提交数据,就像下订单
// PUT:更新数据,就像修改订单
// DELETE:删除数据,就像取消订单
// PATCH:部分更新,就像只修改订单中的某个菜品

6.2 HTTP请求的封装与配置

6.2.1 使用原生http库

Flutter提供了基础的http库,但直接使用会让代码变得复杂:

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

class BasicHttpClient {
   
   
  static const String baseUrl = 'https://api.example.com';
  
  // 基础GET请求
  static Future<Map<String, dynamic>> get(String endpoint) async {
   
   
    try {
   
   
      final response = await http.get(
        Uri.parse('$baseUrl$endpoint'),
        headers: {
   
   
          'Content-Type': 'application/json',
          'Accept': 'application/json',
        },
      );
      
      if (response.statusCode == 200) {
   
   
        return json.decode(response.body);
      } else {
   
   
        throw Exception('请求失败: ${response.statusCode}');
      }
    } catch (e) {
   
   
      throw Exception('网络错误: $e');
    }
  }
}

6.2.2 为什么选择dio库?

dio库就像是一个功能强大的"网络请求管家",它帮我们处理了很多繁琐的工作:

  • 更简洁的API:写更少的代码做更多的事
  • 强大的拦截器:统一处理请求和响应
  • 自动错误处理:智能的错误重试机制
  • 文件操作支持:轻松上传下载文件
  • 请求取消:避免内存泄漏
  • 缓存支持:提升用户体验

6.3 dio库的高级用法

6.3.1 dio的基本配置

import 'package:dio/dio.dart';

class HttpClient {
   
   
  static late Dio _dio;
  
  // 初始化dio实例
  static void init() {
   
   
    _dio = Dio(BaseOptions(
      baseUrl: 'https://api.example.com',
      connectTimeout: const Duration(seconds: 10),
      receiveTimeout: const Duration(seconds: 10),
      sendTimeout: const Duration(seconds: 10),
      headers: {
   
   
        'Content-Type': 'application/json',
        'Accept': 'application/json',
      },
    ));
    
    _setupInterceptors();
  }
  
  static Dio get dio => _dio;
}

6.3.2 创建一个优雅的网络请求封装类

class ApiClient {
   
   
  late Dio _dio;
  
  ApiClient() {
   
   
    _dio = Dio();
    _setupDio();
  }
  
  void _setupDio() {
   
   
    _dio.options = BaseOptions(
      baseUrl: 'https://jsonplaceholder.typicode.com',
      connectTimeout: const Duration(seconds: 10),
      receiveTimeout: const Duration(seconds: 10),
      headers: {
   
   
        'Content-Type': 'application/json',
      },
    );
  }
  
  // 通用请求方法
  Future<T> request<T>(
    String path, {
   
   
    String method = 'GET',
    Map<String, dynamic>? queryParameters,
    dynamic data,
    Map<String, dynamic>? headers,
    required T Function(dynamic) fromJson,
  }) async {
   
   
    try {
   
   
      final options = Options(
        method: method,
        headers: headers,
      );
      
      final response = await _dio.request(
        path,
        queryParameters: queryParameters,
        data: data,
        options: options,
      );
      
      return fromJson(response.data);
    } on DioException catch (e) {
   
   
      throw _handleDioError(e);
    }
  }
  
  // 错误处理
  String _handleDioError(DioException e) {
   
   
    switch (e.type) {
   
   
      case DioExceptionType.connectionTimeout:
        return '连接超时,请检查网络';
      case DioExceptionType.receiveTimeout:
        return '接收数据超时,请重试';
      case DioExceptionType.badResponse:
        return '服务器错误:${e.response?.statusCode}';
      case DioExceptionType.cancel:
        return '请求已取消';
      default:
        return '网络错误:${e.message}';
    }
  }
}

6.4 RESTful API接口调用

6.4.1 理解RESTful API

RESTful API就像是一套标准的"服务规范":

  • 资源导向:把数据看作资源,每个资源都有唯一的URL
  • HTTP方法语义化:用不同的HTTP方法表示不同的操作
  • 状态码标准化:用HTTP状态码表示操作结果

6.4.2 实现完整的CRUD操作

class UserService {
   
   
  final ApiClient _apiClient = ApiClient();
  
  // 获取用户列表 (GET)
  Future<List<User>> getUsers() async {
   
   
    return await _apiClient.request<List<User>>(
      '/users',
      fromJson: (data) => (data as List)
          .map((item) => User.fromJson(item))
          .toList(),
    );
  }
  
  // 获取单个用户 (GET)
  Future<User> getUser(int id) async {
   
   
    return await _apiClient.request<User>(
      '/users/$id',
      fromJson: (data) => User.fromJson(data),
    );
  }
  
  // 创建用户 (POST)
  Future<User> createUser(User user) async {
   
   
    return await _apiClient.request<User>(
      '/users',
      method: 'POST',
      data: user.toJson(),
      fromJson: (data) => User.fromJson(data),
    );
  }
  
  // 更新用户 (PUT)
  Future<User> updateUser(int id, User user) async {
   
   
    return await _apiClient.request<User>(
      '/users/$id',
      method: 'PUT',
      data: user.toJson(),
      fromJson: (data) => User.fromJson(data),
    );
  }
  
  // 删除用户 (DELETE)
  Future<void> deleteUser(int id) async {
   
   
    await _apiClient.request<void>(
      '/users/$id',
      method: 'DELETE',
      fromJson: (data) => null,
    );
  }
}

6.5 JSON数据序列化与反序列化

6.5.1 理解JSON序列化

JSON序列化就像是"翻译官"的工作:

  • 序列化:把Dart对象翻译成JSON字符串,方便网络传输
  • 反序列化:把JSON字符串翻译回Dart对象,方便程序使用

6.5.2 手动序列化(适合简单场景)

class User {
   
   
  final int id;
  final String name;
  final String email;
  final String? avatar;
  
  User({
   
   
    required this.id,
    required this.name,
    required this.email,
    this.avatar,
  });
  
  // 从JSON创建对象(反序列化)
  factory User.fromJson(Map<String, dynamic> json) {
   
   
    return User(
      id: json['id'] as int,
      name: json['name'] as String,
      email: json['email'] as String,
      avatar: json['avatar'] as String?,
    );
  }
  
  // 转换为JSON(序列化)
  Map<String, dynamic> toJson() {
   
   
    return {
   
   
      'id': id,
      'name': name,
      'email': email,
      if (avatar != null) 'avatar': avatar,
    };
  }
  
  
  String toString() {
   
   
    return 'User{id: $id, name: $name, email: $email}';
  }
}

6.5.3 使用json_annotation(推荐方式)

首先添加依赖:

dependencies:
  json_annotation: ^4.8.1

dev_dependencies:
  json_serializable: ^6.7.1
  build_runner: ^2.4.7

然后创建模型类:

import 'package:json_annotation/json_annotation.dart';

part 'user.g.dart';

()
class User {
   
   
  final int id;
  final String name;
  final String email;
  
  (name: 'avatar_url')
  final String? avatarUrl;
  
  (name: 'created_at')
  final DateTime? createdAt;
  
  User({
   
   
    required this.id,
    required this.name,
    required this.email,
    this.avatarUrl,
    this.createdAt,
  });
  
  factory User.fromJson(Map<String, dynamic> json) =&

网站公告

今日签到

点亮在社区的每一天
去签到