Flutter 应用网络通信全解析:从入门到高级实践
掌握 Flutter 中的网络请求技术
在现代跨平台应用开发中,与后端服务的数据交互是核心功能之一。Flutter 通过 Dart 语言强大的异步支持,为开发者提供了高效、灵活的网络通信能力。本文将系统介绍如何在 Flutter 项目中实现 HTTP 请求、处理响应数据,并结合实际场景构建健壮的网络层架构。
理解基础机制
Flutter 的网络操作依赖于 dart:io 包,底层封装了 Android 和 iOS 原生的网络栈(如 OkHttp 和 NSURLSession),支持标准的 HTTP/HTTPS 协议以及 WebSocket 实时通信。所有网络调用均为异步执行,避免阻塞主线程影响用户体验。
关键概念包括:
- Future 与 async/await:用于管理异步任务,确保 UI 流畅;
- 请求结构:包含 URL、方法(GET、POST 等)、头部信息和可选的请求体;
- 响应处理:根据状态码判断结果,常见如 200 表示成功,401 表示未授权,500 表示服务器错误;
- JSON 数据转换:服务器返回的数据通常为 JSON 格式,需映射为 Dart 模型类。
权限配置说明
Android 平台需要在 android/app/src/main/AndroidManifest.xml 文件中添加互联网访问权限:
<uses-permission android:name="android.permission.INTERNET" />
iOS 默认允许网络请求,无需额外配置,但若使用 HTTP 明文地址,需在 Info.plist 中配置 App Transport Security 例外。
主流网络库对比与实战
目前最常用的两种方案是官方 http 包和社区广泛采用的 Dio 库,各有适用场景。
1. 使用 http 包进行轻量级请求
该包由 Flutter 团队维护,API 简洁清晰,适合小型项目或学习阶段使用。
首先在 pubspec.yaml 添加依赖:
dependencies:
http: ^0.13.6
定义模型类以解析返回数据:
class Article {
final int id;
final String title;
final String content;
Article({required this.id, required this.title, required this.content});
factory Article.fromJson(Map<String, dynamic> json) {
return Article(
id: json['id'],
title: json['title'],
content: json['body'],
);
}
}
发起 GET 请求获取文章列表:
import 'package:http/http.dart' as http;
import 'dart:convert';
Future<List<Article>> loadArticles() async {
final uri = Uri.parse('https://jsonplaceholder.typicode.com/posts');
final response = await http.get(uri);
if (response.statusCode == 200) {
final List<dynamic> rawList = json.decode(response.body);
return rawList.map((item) => Article.fromJson(item)).toList();
} else {
throw Exception('Failed to fetch data: ${response.statusCode}');
}
}
提交数据使用 POST 方法:
Future<Article> submitArticle(String title, String content) async {
final uri = Uri.parse('https://jsonplaceholder.typicode.com/posts');
final body = json.encode({
'title': title,
'body': content,
'userId': 1
});
final response = await http.post(
uri,
headers: {'Content-Type': 'application/json'},
body: body,
);
if (response.statusCode == 201) {
return Article.fromJson(json.decode(response.body));
} else {
throw Exception('Submission failed');
}
}
2. Dio:构建企业级网络层
Dio 提供更丰富的特性,适用于复杂业务需求,如拦截器、文件传输、请求取消等。
添加依赖:
dependencies:
dio: ^5.4.0
初始化全局实例并设置默认参数:
import 'package:dio/dio.dart';
final Dio apiClient = Dio(
BaseOptions(
baseUrl: 'https://jsonplaceholder.typicode.com',
connectTimeout: const Duration(seconds: 10),
receiveTimeout: const Duration(seconds: 5),
headers: {'Content-Type': 'application/json'},
),
);
添加日志和认证拦截器:
// 日志输出
apiClient.interceptors.add(LogInterceptor(requestBody: true, responseBody: true));
// 自动附加 Token
apiClient.interceptors.add(InterceptorsWrapper(
onRequest: (options, handler) {
const String token = 'your-auth-token';
options.headers['Authorization'] = 'Bearer $token';
handler.next(options);
},
onError: (DioException error, handler) {
if (error.response?.statusCode == 401) {
// 处理登录过期,跳转至登录页
}
handler.reject(error);
},
));
简化请求调用:
Future<List<Article>> getArticles() async {
final res = await apiClient.get('/posts');
return List<dynamic>.from(res.data).map((e) => Article.fromJson(e)).toList();
}
Future<Article> createArticle(String title, String content) async {
final res = await apiClient.post('/posts', data: {
'title': title,
'body': content,
'userId': 1
});
return Article.fromJson(res.data);
}
支持文件上传与进度监控:
Future<String> uploadImage(String path) async {
final formData = FormData.fromMap({
'file': await MultipartFile.fromFile(path, filename: 'avatar.jpg')
});
final result = await apiClient.post('/upload', data: formData);
return result.data['url'];
}
// 下载文件并监听进度
Future<void> downloadFile(String url, String savePath) async {
await apiClient.download(
url,
savePath,
onReceiveProgress: (received, total) {
final percent = (received / total * 100).toStringAsFixed(1);
print('Progress: $percent%');
},
);
}
实现请求可取消机制:
final cancelToken = CancelToken();
// 发起带取消能力的请求
apiClient.get('/posts', cancelToken: cancelToken)
.then((res) => print(res.data))
.catchError((err) {
if (CancelToken.isCancel(err)) {
print('Request was canceled');
}
});
// 在页面销毁时调用
cancelToken.cancel();
构建可复用的服务层
为了提升代码组织性和可维护性,建议封装统一的 API 接口类:
class NetworkService {
static final Dio _client = Dio(BaseOptions(baseUrl: 'https://jsonplaceholder.typicode.com'));
static void setupInterceptors() {
_client.interceptors
..add(LogInterceptor(requestBody: true))
..add(AuthInterceptor());
}
static Future<T> get<T>(String endpoint, {Map<String, dynamic>? query}) async {
try {
final resp = await _client.get(endpoint, queryParameters: query);
return resp.data as T;
} on DioException catch (e) {
_handleFailure(e);
rethrow;
}
}
static Future<T> post<T>(String endpoint, {dynamic payload}) async {
try {
final resp = await _client.post(endpoint, data: payload);
return resp.data as T;
} on DioException catch (e) {
_handleFailure(e);
rethrow;
}
}
static void _handleFailure(DioException e) {
switch (e.type) {
case DioExceptionType.connectionTimeout:
throw TimeoutException('Network timeout');
case DioExceptionType.badResponse:
throw HttpException('Server error: ${e.response?.statusCode}');
case DioExceptionType.cancel:
throw CanceledException('Request canceled');
default:
throw NetworkException('Network error occurred');
}
}
}
// 自定义异常类
class NetworkException implements Exception {}
class CanceledException implements Exception {
final String message;
CanceledException(this.message);
}
class HttpException implements Exception {
final int? code;
HttpException(this.code);
}
class TimeoutException implements Exception {
final String message;
TimeoutException(this.message);
}
连接 UI 与数据流
利用 FutureBuilder 可直接在界面中响应异步状态变化:
FutureBuilder<List<Article>>(
future: NetworkService.get<List<dynamic>>('/posts').then((list) =>
list.map((e) => Article.fromJson(e)).toList()),
builder: (ctx, snapshot) {
if (snapshot.hasData) {
final articles = snapshot.data!;
return ListView.builder(
itemCount: articles.length,
itemBuilder: (ctx, index) => ListTile(
title: Text(articles[index].title),
subtitle: Text(articles[index].content),
),
);
} else if (snapshot.hasError) {
return Center(child: Text('Error: ${snapshot.error}'));
} else {
return const Center(child: CircularProgressIndicator());
}
},
)
性能优化与安全建议
- 启用 HTTPS:生产环境必须使用加密连接;
- 引入缓存:对静态资源使用
dio_cache_interceptor减少重复请求; - 设置重试策略:弱网环境下自动重试失败请求,可用
dio_retry插件; - 敏感信息保护:使用
flutter_secure_storage安全存储 Token; - 大文件分片处理:上传下载大文件时采用分块传输,防止内存溢出。