📚 目录
概述
Flutter 应用需要与后端服务交互来获取和发送数据。主要涉及:
- 网络请求:使用 HTTP 协议与服务器通信
- 数据序列化:将 JSON 数据转换为 Dart 对象
- 数据持久化:本地存储数据(SharedPreferences、SQLite、文件等)
- 状态管理:管理异步数据的状态
- 错误处理:处理网络错误和异常
网络请求(HTTP)
1. 使用 http 包
安装:
dependencies:
http: ^1.1.0
基本用法:
import 'package:http/http.dart' as http;
import 'dart:convert';
// GET 请求
Future<void> fetchData() async {
final response = await http.get(
Uri.parse('https://api.example.com/data'),
);
if (response.statusCode == 200) {
// 请求成功
final data = jsonDecode(response.body);
print(data);
} else {
// 请求失败
print('请求失败: ${response.statusCode}');
}
}
// POST 请求
Future<void> sendData() async {
final response = await http.post(
Uri.parse('https://api.example.com/data'),
headers: {'Content-Type': 'application/json'},
body: jsonEncode({
'name': 'John',
'email': 'john@example.com',
}),
);
if (response.statusCode == 201) {
print('数据发送成功');
}
}
2. 完整的 HTTP 服务类
import 'package:http/http.dart' as http;
import 'dart:convert';
class ApiService {
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',
},
);
return _handleResponse(response);
} catch (e) {
throw Exception('网络请求失败: $e');
}
}
// POST 请求
static Future<Map<String, dynamic>> post(
String endpoint,
Map<String, dynamic> data,
) async {
try {
final response = await http.post(
Uri.parse('$baseUrl$endpoint'),
headers: {
'Content-Type': 'application/json',
'Accept': 'application/json',
},
body: jsonEncode(data),
);
return _handleResponse(response);
} catch (e) {
throw Exception('网络请求失败: $e');
}
}
// PUT 请求
static Future<Map<String, dynamic>> put(
String endpoint,
Map<String, dynamic> data,
) async {
try {
final response = await http.put(
Uri.parse('$baseUrl$endpoint'),
headers: {
'Content-Type': 'application/json',
'Accept': 'application/json',
},
body: jsonEncode(data),
);
return _handleResponse(response);
} catch (e) {
throw Exception('网络请求失败: $e');
}
}
// DELETE 请求
static Future<void> delete(String endpoint) async {
try {
final response = await http.delete(
Uri.parse('$baseUrl$endpoint'),
headers: {
'Content-Type': 'application/json',
},
);
_handleResponse(response);
} catch (e) {
throw Exception('网络请求失败: $e');
}
}
// 处理响应
static Map<String, dynamic> _handleResponse(http.Response response) {
if (response.statusCode >= 200 && response.statusCode < 300) {
if (response.body.isEmpty) {
return {};
}
return jsonDecode(response.body) as Map<String, dynamic>;
} else {
throw HttpException(
'请求失败: ${response.statusCode}',
statusCode: response.statusCode,
);
}
}
}
// 自定义异常
class HttpException implements Exception {
final String message;
final int? statusCode;
HttpException(this.message, {this.statusCode});
@override
String toString() => message;
}
3. 带认证的请求
class AuthenticatedApiService {
static String? _token;
static void setToken(String token) {
_token = token;
}
static Map<String, String> _getHeaders() {
final headers = {
'Content-Type': 'application/json',
'Accept': 'application/json',
};
if (_token != null) {
headers['Authorization'] = 'Bearer $_token';
}
return headers;
}
static Future<Map<String, dynamic>> get(String endpoint) async {
final response = await http.get(
Uri.parse('https://api.example.com$endpoint'),
headers: _getHeaders(),
);
return _handleResponse(response);
}
// ... 其他方法类似
}
4. 使用 dio 包(推荐)
dio 是一个功能更强大的 HTTP 客户端库。
安装:
dependencies:
dio: ^5.4.0
基本用法:
import 'package:dio/dio.dart';
class DioService {
late Dio _dio;
DioService() {
_dio = Dio(
BaseOptions(
baseUrl: 'https://api.example.com',
connectTimeout: const Duration(seconds: 5),
receiveTimeout: const Duration(seconds: 3),
headers: {
'Content-Type': 'application/json',
},
),
);
// 添加拦截器
_dio.interceptors.add(LogInterceptor(
requestBody: true,
responseBody: true,
));
}
// GET 请求
Future<Response> get(String path, {Map<String, dynamic>? queryParameters}) async {
try {
return await _dio.get(path, queryParameters: queryParameters);
} on DioException catch (e) {
throw _handleError(e);
}
}
// POST 请求
Future<Response> post(
String path,
Map<String, dynamic>? data,
) async {
try {
return await _dio.post(path, data: data);
} on DioException catch (e) {
throw _handleError(e);
}
}
// 错误处理
String _handleError(DioException error) {
switch (error.type) {
case DioExceptionType.connectionTimeout:
return '连接超时';
case DioExceptionType.sendTimeout:
return '发送超时';
case DioExceptionType.receiveTimeout:
return '接收超时';
case DioExceptionType.badResponse:
return '服务器错误: ${error.response?.statusCode}';
case DioExceptionType.cancel:
return '请求已取消';
default:
return '网络错误: ${error.message}';
}
}
}
JSON 序列化和反序列化
1. 手动序列化
class User {
final int id;
final String name;
final String email;
User({
required this.id,
required this.name,
required this.email,
});
// 从 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,
);
}
// 转换为 JSON
Map<String, dynamic> toJson() {
return {
'id': id,
'name': name,
'email': email,
};
}
}
// 使用
void example() {
// JSON 字符串
final jsonString = '{"id": 1, "name": "John", "email": "john@example.com"}';
// 解析 JSON
final json = jsonDecode(jsonString) as Map<String, dynamic>;
final user = User.fromJson(json);
// 转换为 JSON
final userJson = user.toJson();
final jsonString2 = jsonEncode(userJson);
}
2. 使用 json_serializable(推荐)
自动生成序列化代码,减少样板代码。
安装:
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';
@JsonSerializable()
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) => _$UserFromJson(json);
Map<String, dynamic> toJson() => _$UserToJson(this);
}
生成代码:
flutter pub run build_runner build
3. 处理嵌套对象
@JsonSerializable()
class Address {
final String street;
final String city;
Address({required this.street, required this.city});
factory Address.fromJson(Map<String, dynamic> json) => _$AddressFromJson(json);
Map<String, dynamic> toJson() => _$AddressToJson(this);
}
@JsonSerializable()
class User {
final int id;
final String name;
final Address address;
User({
required this.id,
required this.name,
required this.address,
});
factory User.fromJson(Map<String, dynamic> json) => _$UserFromJson(json);
Map<String, dynamic> toJson() => _$UserToJson(this);
}
4. 处理列表
// 解析 JSON 数组
final jsonString = '''
[
{"id": 1, "name": "John"},
{"id": 2, "name": "Jane"}
]
''';
final List<dynamic> jsonList = jsonDecode(jsonString);
final List<User> users = jsonList
.map((json) => User.fromJson(json as Map<String, dynamic>))
.toList();
数据持久化
1. SharedPreferences - 键值对存储
安装:
dependencies:
shared_preferences: ^2.2.2
使用:
import 'package:shared_preferences/shared_preferences.dart';
class PreferencesService {
static SharedPreferences? _prefs;
// 初始化
static Future<void> init() async {
_prefs = await SharedPreferences.getInstance();
}
// 保存字符串
static Future<bool> saveString(String key, String value) async {
return await _prefs?.setString(key, value) ?? false;
}
// 读取字符串
static String? getString(String key) {
return _prefs?.getString(key);
}
// 保存整数
static Future<bool> saveInt(String key, int value) async {
return await _prefs?.setInt(key, value) ?? false;
}
// 读取整数
static int? getInt(String key) {
return _prefs?.getInt(key);
}
// 保存布尔值
static Future<bool> saveBool(String key, bool value) async {
return await _prefs?.setBool(key, value) ?? false;
}
// 读取布尔值
static bool? getBool(String key) {
return _prefs?.getBool(key);
}
// 删除
static Future<bool> remove(String key) async {
return await _prefs?.remove(key) ?? false;
}
// 清空所有
static Future<bool> clear() async {
return await _prefs?.clear() ?? false;
}
}
// 使用示例
void example() async {
await PreferencesService.init();
// 保存数据
await PreferencesService.saveString('username', 'John');
await PreferencesService.saveInt('age', 25);
// 读取数据
final username = PreferencesService.getString('username');
final age = PreferencesService.getInt('age');
}
2. SQLite - 关系型数据库
安装:
dependencies:
sqflite: ^2.3.0
path: ^1.8.3
使用:
import 'package:sqflite/sqflite.dart';
import 'package:path/path.dart';
class DatabaseHelper {
static final DatabaseHelper instance = DatabaseHelper._init();
static Database? _database;
DatabaseHelper._init();
Future<Database> get database async {
if (_database != null) return _database!;
_database = await _initDB('app.db');
return _database!;
}
Future<Database> _initDB(String filePath) async {
final dbPath = await getDatabasesPath();
final path = join(dbPath, filePath);
return await openDatabase(
path,
version: 1,
onCreate: _createDB,
);
}
Future<void> _createDB(Database db, int version) async {
await db.execute('''
CREATE TABLE users (
id INTEGER PRIMARY KEY AUTOINCREMENT,
name TEXT NOT NULL,
email TEXT NOT NULL
)
''');
}
// 插入数据
Future<int> insertUser(Map<String, dynamic> user) async {
final db = await database;
return await db.insert('users', user);
}
// 查询所有
Future<List<Map<String, dynamic>>> getAllUsers() async {
final db = await database;
return await db.query('users');
}
// 查询单个
Future<Map<String, dynamic>?> getUser(int id) async {
final db = await database;
final results = await db.query(
'users',
where: 'id = ?',
whereArgs: [id],
);
if (results.isNotEmpty) {
return results.first;
}
return null;
}
// 更新
Future<int> updateUser(int id, Map<String, dynamic> user) async {
final db = await database;
return await db.update(
'users',
user,
where: 'id = ?',
whereArgs: [id],
);
}
// 删除
Future<int> deleteUser(int id) async {
final db = await database;
return await db.delete(
'users',
where: 'id = ?',
whereArgs: [id],
);
}
// 关闭数据库
Future<void> close() async {
final db = await database;
await db.close();
}
}
3. 文件存储
import 'dart:io';
import 'package:path_provider/path_provider.dart';
class FileService {
// 获取应用文档目录
static Future<File> getLocalFile(String filename) async {
final directory = await getApplicationDocumentsDirectory();
final path = directory.path;
return File('$path/$filename');
}
// 写入文件
static Future<File> writeFile(String filename, String content) async {
final file = await getLocalFile(filename);
return await file.writeAsString(content);
}
// 读取文件
static Future<String> readFile(String filename) async {
try {
final file = await getLocalFile(filename);
return await file.readAsString();
} catch (e) {
return '';
}
}
// 删除文件
static Future<void> deleteFile(String filename) async {
try {
final file = await getLocalFile(filename);
await file.delete();
} catch (e) {
print('删除文件失败: $e');
}
}
}
状态管理与数据
1. 使用 Provider 管理数据状态
import 'package:flutter/material.dart';
import 'package:provider/provider.dart';
// 数据模型
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 UserProvider extends ChangeNotifier {
List<User> _users = [];
bool _isLoading = false;
String? _error;
List<User> get users => _users;
bool get isLoading => _isLoading;
String? get error => _error;
// 获取用户列表
Future<void> fetchUsers() async {
_isLoading = true;
_error = null;
notifyListeners();
try {
final response = await http.get(
Uri.parse('https://api.example.com/users'),
);
if (response.statusCode == 200) {
final List<dynamic> jsonList = jsonDecode(response.body);
_users = jsonList
.map((json) => User.fromJson(json as Map<String, dynamic>))
.toList();
} else {
_error = '获取数据失败';
}
} catch (e) {
_error = '网络错误: $e';
} finally {
_isLoading = false;
notifyListeners();
}
}
// 添加用户
Future<void> addUser(User user) async {
try {
final response = await http.post(
Uri.parse('https://api.example.com/users'),
headers: {'Content-Type': 'application/json'},
body: jsonEncode(user.toJson()),
);
if (response.statusCode == 201) {
_users.add(user);
notifyListeners();
}
} catch (e) {
_error = '添加用户失败: $e';
notifyListeners();
}
}
}
// 使用
class UserListScreen extends StatelessWidget {
const UserListScreen({super.key});
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(title: const Text('用户列表')),
body: Consumer<UserProvider>(
builder: (context, userProvider, child) {
if (userProvider.isLoading) {
return const Center(child: CircularProgressIndicator());
}
if (userProvider.error != null) {
return Center(child: Text('错误: ${userProvider.error}'));
}
return ListView.builder(
itemCount: userProvider.users.length,
itemBuilder: (context, index) {
final user = userProvider.users[index];
return ListTile(
title: Text(user.name),
subtitle: Text(user.email),
);
},
);
},
),
floatingActionButton: FloatingActionButton(
onPressed: () {
context.read<UserProvider>().fetchUsers();
},
child: const Icon(Icons.refresh),
),
);
}
}
2. 使用 FutureBuilder
class UserListScreen extends StatelessWidget {
const UserListScreen({super.key});
Future<List<User>> fetchUsers() async {
final response = await http.get(
Uri.parse('https://api.example.com/users'),
);
if (response.statusCode == 200) {
final List<dynamic> jsonList = jsonDecode(response.body);
return jsonList
.map((json) => User.fromJson(json as Map<String, dynamic>))
.toList();
} else {
throw Exception('获取数据失败');
}
}
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(title: const Text('用户列表')),
body: FutureBuilder<List<User>>(
future: fetchUsers(),
builder: (context, snapshot) {
if (snapshot.connectionState == ConnectionState.waiting) {
return const Center(child: CircularProgressIndicator());
}
if (snapshot.hasError) {
return Center(child: Text('错误: ${snapshot.error}'));
}
if (!snapshot.hasData) {
return const Center(child: Text('没有数据'));
}
final users = snapshot.data!;
return ListView.builder(
itemCount: users.length,
itemBuilder: (context, index) {
final user = users[index];
return ListTile(
title: Text(user.name),
subtitle: Text(user.email),
);
},
);
},
),
);
}
}
错误处理
1. 网络错误处理
class ApiException implements Exception {
final String message;
final int? statusCode;
ApiException(this.message, {this.statusCode});
@override
String toString() => message;
}
class ApiService {
static Future<Map<String, dynamic>> get(String endpoint) async {
try {
final response = await http.get(
Uri.parse('https://api.example.com$endpoint'),
);
if (response.statusCode == 200) {
return jsonDecode(response.body) as Map<String, dynamic>;
} else {
throw ApiException(
'请求失败',
statusCode: response.statusCode,
);
}
} on SocketException {
throw ApiException('网络连接失败,请检查网络');
} on TimeoutException {
throw ApiException('请求超时,请重试');
} on FormatException {
throw ApiException('数据格式错误');
} catch (e) {
throw ApiException('未知错误: $e');
}
}
}
2. 重试机制
Future<T> retryRequest<T>(
Future<T> Function() request, {
int maxRetries = 3,
Duration delay = const Duration(seconds: 1),
}) async {
int attempts = 0;
while (attempts < maxRetries) {
try {
return await request();
} catch (e) {
attempts++;
if (attempts >= maxRetries) {
rethrow;
}
await Future.delayed(delay);
}
}
throw Exception('重试失败');
}
缓存策略
1. 简单的内存缓存
class CacheService {
static final Map<String, CacheItem> _cache = {};
static void set(String key, dynamic value, {Duration? expiry}) {
_cache[key] = CacheItem(
value: value,
expiry: expiry != null ? DateTime.now().add(expiry) : null,
);
}
static dynamic get(String key) {
final item = _cache[key];
if (item == null) return null;
if (item.expiry != null && DateTime.now().isAfter(item.expiry!)) {
_cache.remove(key);
return null;
}
return item.value;
}
static void clear() {
_cache.clear();
}
}
class CacheItem {
final dynamic value;
final DateTime? expiry;
CacheItem({required this.value, this.expiry});
}
2. 使用 cached_network_image
安装:
dependencies:
cached_network_image: ^3.3.0
使用:
CachedNetworkImage(
imageUrl: 'https://example.com/image.jpg',
placeholder: (context, url) => const CircularProgressIndicator(),
errorWidget: (context, url, error) => const Icon(Icons.error),
)
与后端 API 交互
1. RESTful API 示例
class UserApi {
static const String baseUrl = 'https://api.example.com';
// 获取所有用户
static Future<List<User>> getUsers() async {
final response = await http.get(Uri.parse('$baseUrl/users'));
// ... 处理响应
}
// 获取单个用户
static Future<User> getUser(int id) async {
final response = await http.get(Uri.parse('$baseUrl/users/$id'));
// ... 处理响应
}
// 创建用户
static Future<User> createUser(User user) async {
final response = await http.post(
Uri.parse('$baseUrl/users'),
headers: {'Content-Type': 'application/json'},
body: jsonEncode(user.toJson()),
);
// ... 处理响应
}
// 更新用户
static Future<User> updateUser(int id, User user) async {
final response = await http.put(
Uri.parse('$baseUrl/users/$id'),
headers: {'Content-Type': 'application/json'},
body: jsonEncode(user.toJson()),
);
// ... 处理响应
}
// 删除用户
static Future<void> deleteUser(int id) async {
final response = await http.delete(
Uri.parse('$baseUrl/users/$id'),
);
// ... 处理响应
}
}
2. WebSocket 通信
安装:
dependencies:
web_socket_channel: ^2.4.0
使用:
import 'package:web_socket_channel/web_socket_channel.dart';
class WebSocketService {
WebSocketChannel? _channel;
void connect(String url) {
_channel = WebSocketChannel.connect(Uri.parse(url));
}
void sendMessage(String message) {
_channel?.sink.add(message);
}
Stream<dynamic> get stream => _channel?.stream ?? const Stream.empty();
void disconnect() {
_channel?.sink.close();
}
}
最佳实践
1. 代码组织
lib/
models/
user.dart
product.dart
services/
api_service.dart
database_service.dart
cache_service.dart
providers/
user_provider.dart
product_provider.dart
screens/
user_list_screen.dart
user_detail_screen.dart
2. 使用 Repository 模式
abstract class UserRepository {
Future<List<User>> getUsers();
Future<User> getUser(int id);
Future<User> createUser(User user);
Future<User> updateUser(int id, User user);
Future<void> deleteUser(int id);
}
class UserRepositoryImpl implements UserRepository {
final ApiService _apiService;
final DatabaseHelper _databaseHelper;
UserRepositoryImpl(this._apiService, this._databaseHelper);
@override
Future<List<User>> getUsers() async {
try {
// 先尝试从网络获取
final users = await _apiService.getUsers();
// 保存到本地数据库
await _databaseHelper.saveUsers(users);
return users;
} catch (e) {
// 网络失败,从本地获取
return await _databaseHelper.getUsers();
}
}
// ... 其他方法
}
3. 环境配置
class AppConfig {
static const String apiBaseUrl = String.fromEnvironment(
'API_BASE_URL',
defaultValue: 'https://api.example.com',
);
static const bool enableLogging = bool.fromEnvironment(
'ENABLE_LOGGING',
defaultValue: false,
);
}
常见问题
1. CORS 错误(Web)
在 Web 开发中,如果遇到 CORS 错误,需要在后端配置 CORS 头。
2. 证书错误(Android)
在 android/app/src/main/res/xml/network_security_config.xml 中配置:
<?xml version="1.0" encoding="utf-8"?>
<network-security-config>
<base-config cleartextTrafficPermitted="true">
<trust-anchors>
<certificates src="system" />
</trust-anchors>
</base-config>
</network-security-config>
3. 网络权限(Android)
在 AndroidManifest.xml 中添加:
<uses-permission android:name="android.permission.INTERNET"/>
总结
Flutter 的数据调用和后端交互涉及多个方面:
-
网络请求:使用
http或dio包 -
JSON 序列化:手动或使用
json_serializable - 数据持久化:SharedPreferences、SQLite、文件存储
- 状态管理:Provider、FutureBuilder 等
- 错误处理:完善的异常处理机制
- 缓存策略:提高性能和用户体验