ViewModel 生成
使用 flu-cli add viewmodel 命令生成视图模型,管理页面状态和业务逻辑。
基本用法
bash
flu-cli add viewmodel <name> [options]
flu-cli add vm <name> [options]
flu-cli a vm <name> [options]参数
| 参数 | 说明 |
|---|---|
-f, --feature | 所属功能模块 |
生成的代码
dart
import 'package:flutter/foundation.dart';
class HomeViewModel extends ChangeNotifier {
// 状态
bool _isLoading = false;
bool get isLoading => _isLoading;
// 初始化
HomeViewModel() {
_init();
}
void _init() {
// 初始化逻辑
}
// 业务方法
Future<void> loadData() async {
_isLoading = true;
notifyListeners();
try {
// 加载数据
await Future.delayed(const Duration(seconds: 1));
} catch (e) {
debugPrint('Error: $e');
} finally {
_isLoading = false;
notifyListeners();
}
}
@override
void dispose() {
// 清理资源
super.dispose();
}
}ViewModel 职责
1. 状态管理
管理页面的所有状态:
dart
class UserListViewModel extends ChangeNotifier {
List<User> _users = [];
List<User> get users => _users;
bool _isLoading = false;
bool get isLoading => _isLoading;
String? _error;
String? get error => _error;
int _selectedIndex = 0;
int get selectedIndex => _selectedIndex;
}2. 业务逻辑
处理业务逻辑和数据操作:
dart
class UserListViewModel extends ChangeNotifier {
final UserService _service = UserService();
Future<void> loadUsers() async {
_isLoading = true;
_error = null;
notifyListeners();
try {
_users = await _service.getUsers();
} catch (e) {
_error = e.toString();
} finally {
_isLoading = false;
notifyListeners();
}
}
Future<void> deleteUser(String id) async {
try {
await _service.deleteUser(id);
_users.removeWhere((user) => user.id == id);
notifyListeners();
} catch (e) {
_error = e.toString();
notifyListeners();
}
}
}3. UI 交互
处理用户交互:
dart
class UserListViewModel extends ChangeNotifier {
void selectUser(int index) {
_selectedIndex = index;
notifyListeners();
}
void toggleFavorite(String userId) {
final index = _users.indexWhere((u) => u.id == userId);
if (index != -1) {
_users[index] = _users[index].copyWith(
isFavorite: !_users[index].isFavorite,
);
notifyListeners();
}
}
}使用 ViewModel
在 StatelessWidget 中
dart
class UserListPage extends StatelessWidget {
const UserListPage({super.key});
@override
Widget build(BuildContext context) {
return ChangeNotifierProvider(
create: (_) => UserListViewModel()..loadUsers(),
child: Consumer<UserListViewModel>(
builder: (context, viewModel, child) {
if (viewModel.isLoading) {
return const Center(child: CircularProgressIndicator());
}
return ListView.builder(
itemCount: viewModel.users.length,
itemBuilder: (context, index) {
final user = viewModel.users[index];
return ListTile(
title: Text(user.name),
onTap: () => viewModel.selectUser(index),
);
},
);
},
),
);
}
}在 StatefulWidget 中
dart
class UserListPage extends StatefulWidget {
const UserListPage({super.key});
@override
State<UserListPage> createState() => _UserListPageState();
}
class _UserListPageState extends State<UserListPage> {
late final UserListViewModel _viewModel;
@override
void initState() {
super.initState();
_viewModel = UserListViewModel();
_viewModel.addListener(_onViewModelChanged);
_viewModel.loadUsers();
}
void _onViewModelChanged() {
setState(() {});
}
@override
Widget build(BuildContext context) {
if (_viewModel.isLoading) {
return const Center(child: CircularProgressIndicator());
}
return ListView.builder(
itemCount: _viewModel.users.length,
itemBuilder: (context, index) {
final user = _viewModel.users[index];
return ListTile(
title: Text(user.name),
onTap: () => _viewModel.selectUser(index),
);
},
);
}
@override
void dispose() {
_viewModel.removeListener(_onViewModelChanged);
_viewModel.dispose();
super.dispose();
}
}最佳实践
1. 单一职责
每个 ViewModel 只负责一个页面或功能:
bash
# ✅ 好的做法
flu-cli a vm user_list -f user
flu-cli a vm user_detail -f user
flu-cli a vm user_edit -f user
# ❌ 避免
flu-cli a vm user # 太宽泛2. 状态管理
使用私有变量和公共 getter:
dart
class UserListViewModel extends ChangeNotifier {
// 私有状态
List<User> _users = [];
bool _isLoading = false;
String? _error;
// 公共 getter
List<User> get users => _users;
bool get isLoading => _isLoading;
String? get error => _error;
// 公共方法修改状态
Future<void> loadUsers() async {
_setLoading(true);
// ...
}
void _setLoading(bool value) {
_isLoading = value;
notifyListeners();
}
}3. 错误处理
统一的错误处理:
dart
class UserListViewModel extends ChangeNotifier {
String? _error;
String? get error => _error;
Future<void> loadUsers() async {
_error = null;
notifyListeners();
try {
_users = await _service.getUsers();
} catch (e) {
_error = '加载失败: ${e.toString()}';
debugPrint('Error loading users: $e');
} finally {
_isLoading = false;
notifyListeners();
}
}
void clearError() {
_error = null;
notifyListeners();
}
}4. 资源清理
在 dispose 中清理资源:
dart
class UserListViewModel extends ChangeNotifier {
final UserService _service = UserService();
Timer? _refreshTimer;
UserListViewModel() {
_startAutoRefresh();
}
void _startAutoRefresh() {
_refreshTimer = Timer.periodic(
const Duration(minutes: 5),
(_) => loadUsers(),
);
}
@override
void dispose() {
_refreshTimer?.cancel();
_service.dispose();
super.dispose();
}
}完整示例
创建 ViewModel
bash
flu-cli a vm user_list -f user完善 ViewModel
dart
import 'package:flutter/foundation.dart';
import '../models/user_model.dart';
import '../services/user_service.dart';
class UserListViewModel extends ChangeNotifier {
final UserService _service = UserService();
// 状态
List<User> _users = [];
List<User> get users => _users;
bool _isLoading = false;
bool get isLoading => _isLoading;
String? _error;
String? get error => _error;
String _searchQuery = '';
String get searchQuery => _searchQuery;
// 计算属性
List<User> get filteredUsers {
if (_searchQuery.isEmpty) return _users;
return _users.where((user) =>
user.name.toLowerCase().contains(_searchQuery.toLowerCase())
).toList();
}
// 初始化
UserListViewModel() {
loadUsers();
}
// 加载用户
Future<void> loadUsers() async {
_setLoading(true);
_setError(null);
try {
_users = await _service.getUsers();
} catch (e) {
_setError('加载失败: ${e.toString()}');
debugPrint('Error loading users: $e');
} finally {
_setLoading(false);
}
}
// 搜索
void search(String query) {
_searchQuery = query;
notifyListeners();
}
// 删除用户
Future<void> deleteUser(String id) async {
try {
await _service.deleteUser(id);
_users.removeWhere((user) => user.id == id);
notifyListeners();
} catch (e) {
_setError('删除失败: ${e.toString()}');
}
}
// 刷新
Future<void> refresh() async {
await loadUsers();
}
// 私有方法
void _setLoading(bool value) {
_isLoading = value;
notifyListeners();
}
void _setError(String? value) {
_error = value;
notifyListeners();
}
@override
void dispose() {
_service.dispose();
super.dispose();
}
}