第6章:網絡請求與數據處理
“數據是應用的血液,網絡是連接世界的橋梁。”
在移動應用開發中,與服務器進行數據交互是必不可少的功能。無論是獲取用戶信息、提交表單數據,還是上傳圖片、下載文件,都離不開網絡請求。本章將帶你深入掌握Flutter中的網絡編程技巧。
6.1 網絡請求基礎概念
6.1.1 什么是HTTP請求?
想象一下,你走進一家餐廳點餐的過程:
- 你告訴服務員想要什么(發送請求)
- 服務員把你的需求傳達給廚房(請求到達服務器)
- 廚房準備你的餐點(服務器處理請求)
- 服務員把餐點端給你(接收響應)
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.1dev_dependencies:json_serializable: ^6.7.1build_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) =&