欢迎大家加入开源鸿蒙跨平台开发者社区,一起共建开源鸿蒙跨平台生态。
Flutter 后端联动详解
Flutter 作为跨平台开发框架,通常需要与后端服务进行数据交互。后端联动涉及 API 设计、数据传输、状态管理等多个方面。以下从多个角度详细说明 Flutter 如何与后端联动,并提供代码案例。
后端 API 设计
RESTful API 设计原则
后端通常使用 RESTful API 或 GraphQL 提供服务。RESTful API 是目前最常用的方式,设计时需遵循以下核心原则:
- 资源导向:将数据抽象为资源,如用户、商品等
- HTTP 方法语义化 :
- GET:获取资源
- POST:创建资源
- PUT:更新完整资源
- PATCH:部分更新资源
- DELETE:删除资源
- 无状态:每个请求应包含完成操作所需的所有信息
- URI 规范化:使用名词复数形式表示资源集合
典型 API 示例
一个完整的用户管理系统 API 设计可能如下:
GET /users:获取用户列表(支持分页参数?page=1&limit=10)POST /users:创建用户(请求体包含用户信息)GET /users/{id}:获取特定用户详情PUT /users/{id}:更新用户完整信息PATCH /users/{id}:部分更新用户信息(如仅更新邮箱)DELETE /users/{id}:删除用户GET /users/search?name=xxx:搜索用户
后端实现技术
后端可以使用多种技术实现:
- Node.js + Express:
javascript
const express = require('express');
const app = express();
app.get('/users', (req, res) => {
// 获取用户逻辑
});
app.listen(3000);
- Spring Boot (Java):
java
@RestController
@RequestMapping("/api/users")
public class UserController {
@Autowired
private UserService userService;
@GetMapping
public ResponseEntity<Page<User>> getUsers(
@RequestParam(defaultValue = "0") int page,
@RequestParam(defaultValue = "10") int size) {
return ResponseEntity.ok(userService.getUsers(page, size));
}
}
- Django (Python):
python
from rest_framework import viewsets
class UserViewSet(viewsets.ModelViewSet):
queryset = User.objects.all()
serializer_class = UserSerializer
Flutter 网络请求实现
网络请求库选择
Flutter 中常用的网络请求库:
- http:官方基础库,功能简单
- dio :功能强大,支持:
- 拦截器
- FormData
- 请求取消
- 文件上传/下载
- 超时设置
- Chopper:基于 http 的声明式客户端
dio 详细使用示例
1. 基础配置
yaml
dependencies:
dio: ^5.3.0
logger: ^2.0.0 # 可选,用于日志记录
2. 完整 API 服务类实现
dart
import 'package:dio/dio.dart';
import 'package:logger/logger.dart';
class ApiService {
final Dio _dio;
final Logger _logger = Logger();
// 单例模式
static final ApiService _instance = ApiService._internal();
factory ApiService() => _instance;
ApiService._internal() {
_dio = Dio(BaseOptions(
baseUrl: 'https://your-api.com/api',
connectTimeout: const Duration(seconds: 10),
receiveTimeout: const Duration(seconds: 10),
headers: {
'Content-Type': 'application/json',
'Accept': 'application/json',
},
));
// 添加拦截器
_dio.interceptors.add(InterceptorsWrapper(
onRequest: (options, handler) {
_logger.d('Request: ${options.method} ${options.path}');
return handler.next(options);
},
onError: (error, handler) {
_logger.e('Error: ${error.message}');
return handler.next(error);
},
));
}
// 获取用户列表
Future<List<User>> getUsers({int page = 1, int limit = 10}) async {
try {
final response = await _dio.get(
'/users',
queryParameters: {'page': page, 'limit': limit},
);
return (response.data['data'] as List)
.map((e) => User.fromJson(e))
.toList();
} on DioException catch (e) {
_handleError(e);
rethrow;
}
}
// 错误处理
void _handleError(DioException e) {
if (e.response != null) {
_logger.e('Status code: ${e.response?.statusCode}');
_logger.e('Response data: ${e.response?.data}');
}
}
}
3. 高级功能示例
文件上传:
dart
Future<String> uploadAvatar(File image) async {
FormData formData = FormData.fromMap({
'avatar': await MultipartFile.fromFile(image.path),
});
final response = await _dio.post('/users/avatar', data: formData);
return response.data['url'];
}
下载文件:
dart
Future<File> downloadFile(String url) async {
final savePath = '${(await getTemporaryDirectory()).path}/file.pdf';
await _dio.download(url, savePath);
return File(savePath);
}
数据模型与序列化
JSON 序列化方案
- 手动序列化:适用于简单模型
- json_serializable:自动生成序列化代码(推荐)
- freezed:不可变模型 + 序列化
完整用户模型示例
- 添加依赖:
yaml
dependencies:
json_annotation: ^4.8.1
dev_dependencies:
json_serializable: ^6.7.1
build_runner: ^2.4.6
- 创建模型类:
dart
import 'package:json_annotation/json_annotation.dart';
part 'user.g.dart';
@JsonSerializable()
class User {
final String id;
final String name;
final String email;
@JsonKey(name: 'created_at')
final DateTime createdAt;
User({
required this.id,
required this.name,
required this.email,
required this.createdAt,
});
factory User.fromJson(Map<String, dynamic> json) => _$UserFromJson(json);
Map<String, dynamic> toJson() => _$UserToJson(this);
}
- 生成序列化代码:
bash
flutter pub run build_runner build
复杂模型处理
嵌套对象:
dart
@JsonSerializable()
class Order {
final String id;
final User user; // 嵌套User对象
final List<Product> products;
// ...其他代码
}
枚举处理:
dart
enum UserRole {
@JsonValue('admin')
admin,
@JsonValue('user')
user,
}
状态管理与缓存
常见状态管理方案
- Provider:简单场景
- Riverpod:Provider 的改进版
- Bloc:复杂业务逻辑
- GetX:轻量级方案
数据缓存策略
- shared_preferences:简单键值存储
- hive:轻量级 NoSQL 数据库
- sqflite:SQLite 实现
dart
// 使用 hive 缓存用户数据示例
class UserRepository {
final ApiService api;
final Box<User> userBox;
Future<List<User>> getUsers(bool refresh) async {
if (!refresh && userBox.isNotEmpty) {
return userBox.values.toList();
}
final users = await api.getUsers();
await userBox.clear();
await userBox.addAll(users);
return users;
}
}
安全与认证
JWT 认证实现
dart
class AuthService {
final Dio dio;
final SharedPreferences prefs;
Future<void> login(String email, String password) async {
final response = await dio.post('/auth/login', data: {
'email': email,
'password': password,
});
final token = response.data['token'];
await prefs.setString('jwt_token', token);
dio.options.headers['Authorization'] = 'Bearer $token';
}
Future<void> autoLogin() async {
final token = prefs.getString('jwt_token');
if (token != null) {
dio.options.headers['Authorization'] = 'Bearer $token';
}
}
}
HTTPS 与证书校验
dart
(dio.httpClientAdapter as DefaultHttpClientAdapter).onHttpClientCreate =
(client) {
final securityContext = SecurityContext();
// 添加自签名证书
securityContext.setTrustedCertificates('path/to/cert.pem');
return HttpClient(context: securityContext);
};
性能优化
请求优化技巧
- 分页加载:避免一次性加载大量数据
- 数据压缩:启用 gzip
- 缓存控制:合理使用 ETag/Last-Modified
- 请求合并:GraphQL 或自定义端点
dart
// 分页加载示例
class UserListView extends StatefulWidget {
@override
_UserListViewState createState() => _UserListViewState();
}
class _UserListViewState extends State<UserListView> {
final ScrollController _controller = ScrollController();
final List<User> _users = [];
int _page = 1;
bool _isLoading = false;
@override
void initState() {
super.initState();
_loadMore();
_controller.addListener(() {
if (_controller.position.pixels ==
_controller.position.maxScrollExtent) {
_loadMore();
}
});
}
Future<void> _loadMore() async {
if (_isLoading) return;
setState(() => _isLoading = true);
final newUsers = await ApiService().getUsers(page: _page);
setState(() {
_users.addAll(newUsers);
_page++;
_isLoading = false;
});
}
// ...构建列表的代码
}
错误处理与监控
统一错误处理
dart
class AppErrorHandler {
static void handleError(BuildContext context, dynamic error) {
if (error is DioException) {
final message = error.response?.data['message'] ?? error.message;
ScaffoldMessenger.of(context).showSnackBar(
SnackBar(content: Text('Error: $message')),
);
} else {
ScaffoldMessenger.of(context).showSnackBar(
SnackBar(content: Text('Unexpected error occurred')),
);
}
// 上报错误到监控平台
FirebaseCrashlytics.instance.recordError(error, StackTrace.current);
}
}
监控方案集成
- Firebase Crashlytics:崩溃报告
- Sentry:错误监控
- Prometheus + Grafana:性能监控
dart
// 初始化监控
await FirebaseCrashlytics.instance.setCrashlyticsCollectionEnabled(true);
FlutterError.onError = FirebaseCrashlytics.instance.recordFlutterError;
- 添加依赖:
yaml
dependencies:
json_annotation: ^4.8.1
dev_dependencies:
json_serializable: ^6.7.1
build_runner: ^2.4.4
- 创建模型类:
dart
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生成user.g.dart文件。
状态管理
Flutter 中常用 provider 或 riverpod 管理后端数据状态。以下是使用 riverpod 的示例:
- 添加依赖:
yaml
dependencies:
flutter_riverpod: ^2.4.9
- 创建状态管理类:
dart
final userProvider = FutureProvider<List<User>>((ref) async {
final apiService = ref.read(apiServiceProvider);
return await apiService.getUsers();
});
final apiServiceProvider = Provider((ref) => ApiService());
- 在 UI 中使用:
dart
class UserList extends ConsumerWidget {
@override
Widget build(BuildContext context, WidgetRef ref) {
final usersAsync = ref.watch(userProvider);
return usersAsync.when(
loading: () => const CircularProgressIndicator(),
error: (error, stack) => Text('Error: $error'),
data: (users) => ListView.builder(
itemCount: users.length,
itemBuilder: (context, index) => ListTile(
title: Text(users[index].name),
subtitle: Text(users[index].email),
),
),
);
}
}
认证与安全
后端联动通常需要认证。JWT(JSON Web Token)是常用方案。以下是在 Flutter 中处理 JWT 的示例:
- 添加依赖:
yaml
dependencies:
shared_preferences: ^2.2.2
- 存储和获取 Token:
dart
class AuthService {
static const _tokenKey = 'auth_token';
Future<void> saveToken(String token) async {
final prefs = await SharedPreferences.getInstance();
await prefs.setString(_tokenKey, token);
}
Future<String?> getToken() async {
final prefs = await SharedPreferences.getInstance();
return prefs.getString(_tokenKey);
}
Future<void> clearToken() async {
final prefs = await SharedPreferences.getInstance();
await prefs.remove(_tokenKey);
}
}
- 在 Dio 拦截器中添加 Token:
dart
class ApiService {
final Dio _dio;
final AuthService _authService;
ApiService(this._authService) : _dio = Dio(BaseOptions(
baseUrl: 'https://your-api.com/api',
)) {
_dio.interceptors.add(InterceptorsWrapper(
onRequest: (options, handler) async {
final token = await _authService.getToken();
if (token != null) {
options.headers['Authorization'] = 'Bearer $token';
}
return handler.next(options);
},
));
}
}
错误处理
网络请求可能失败,需统一处理错误。以下是扩展的错误处理示例:
dart
class ApiError {
final String message;
final int? statusCode;
ApiError(this.message, {this.statusCode});
@override
String toString() => message;
}
class ApiService {
// ...
Future<T> _handleRequest<T>(Future<Response> request) async {
try {
final response = await request;
return response.data;
} on DioException catch (e) {
if (e.response != null) {
throw ApiError(
e.response?.data['message'] ?? 'Unknown error',
statusCode: e.response?.statusCode,
);
} else {
throw ApiError(e.message ?? 'Network error');
}
}
}
Future<List<User>> getUsers() async {
return _handleRequest(_dio.get('/users'));
}
}
文件上传
后端联动常涉及文件上传。以下是使用 dio 上传文件的示例:
dart
Future<String> uploadImage(File image) async {
final formData = FormData.fromMap({
'file': await MultipartFile.fromFile(image.path, filename: 'profile.jpg'),
});
final response = await _dio.post('/upload', data: formData);
return response.data['url'];
}
WebSocket 实时通信
对于实时应用,可以使用 WebSocket。以下是 Flutter 中 WebSocket 的示例:
dart
final channel = IOWebSocketChannel.connect('ws://your-api.com/ws');
// 发送消息
channel.sink.add(jsonEncode({'event': 'message', 'data': 'Hello'}));
// 接收消息
StreamBuilder(
stream: channel.stream,
builder: (context, snapshot) {
if (snapshot.hasData) {
final message = jsonDecode(snapshot.data);
return Text('Received: ${message['data']}');
}
return const CircularProgressIndicator();
},
);
性能优化
后端联动需注意性能优化:
- 使用分页加载大量数据:
dart
Future<List<User>> getUsers(int page, int limit) async {
final response = await _dio.get('/users', queryParameters: {
'page': page,
'limit': limit,
});
return (response.data as List).map((e) => User.fromJson(e)).toList();
}
- 缓存响应数据:
dart
final _dio = Dio()
..interceptors.add(
DioCacheInterceptor(
options: CacheOptions(
store: MemCacheStore(),
policy: CachePolicy.forceCache,
hitCacheOnErrorExcept: [401, 403],
),
),
);
测试
测试是保证后端联动可靠性的关键。以下是单元测试示例:
dart
void main() {
test('getUsers returns list of users', () async {
final dio = DioMock();
when(dio.get('/users')).thenAnswer(
(_) async => Response(
data: [
{'id': 1, 'name': 'Alice', 'email': 'alice@example.com'},
],
statusCode: 200,
requestOptions: RequestOptions(path: '/users'),
),
);
final apiService = ApiService(dio);
final users = await apiService.getUsers();
expect(users.length, 1);
expect(users[0].name, 'Alice');
});
}
通过以上方法,Flutter 可以高效稳定地与后端联动。实际开发中需根据项目需求调整架构和技术选型。
欢迎大家加入开源鸿蒙跨平台开发者社区,一起共建开源鸿蒙跨平台生态。