架构设计思路
- 分层设计
- 数据层:负责与数据源交互,例如通过HTTP请求获取后端数据、从本地数据库读取数据等。使用
Riverpod
的Provider
家族来创建数据相关的Provider
,如FutureProvider
用于异步数据获取。例如,创建一个FutureProvider
来获取企业的员工列表数据:
final employeeListProvider = FutureProvider<List<Employee>>((ref) async {
// 这里通过HTTP请求或本地数据库读取数据
final response = await http.get(Uri.parse('api/employees'));
return employeeListFromJson(response.body);
});
- **业务逻辑层**:处理数据的转换、计算和业务规则。基于数据层提供的数据,进行加工处理。例如,计算员工的平均工资。可以使用`Provider`或`NotifierProvider`来封装业务逻辑。如:
class EmployeeLogic extends Notifier<double> {
@override
double build() {
final employees = ref.watch(employeeListProvider).valueOrNull;
if (employees == null || employees.isEmpty) return 0;
final totalSalary = employees.fold(0, (sum, emp) => sum + emp.salary);
return totalSalary / employees.length;
}
}
final averageSalaryProvider = NotifierProvider<EmployeeLogic, double>(EmployeeLogic.new);
- **视图层**:根据业务逻辑层提供的数据进行UI渲染。通过`ConsumerWidget`或`ConsumerStatefulWidget`来监听`Provider`数据的变化,并更新UI。
class EmployeeListView extends ConsumerWidget {
@override
Widget build(BuildContext context, WidgetRef ref) {
final employees = ref.watch(employeeListProvider).valueOrNull;
if (employees == null) return const CircularProgressIndicator();
return ListView.builder(
itemCount: employees.length,
itemBuilder: (context, index) {
final employee = employees[index];
return ListTile(
title: Text(employee.name),
subtitle: Text('Salary: ${employee.salary}'),
);
},
);
}
}
- 模块化:将不同功能模块的状态管理分开,每个模块有自己独立的
Provider
集合。例如,将用户模块、订单模块、报表模块等的状态管理隔离,这样在添加新功能或模块时,只需要在对应的模块内进行修改,不会影响其他模块。
关键技术点
- Selector:使用
Selector
来优化UI更新。Selector
允许我们只在特定数据变化时更新UI,而不是在整个Provider
数据变化时都更新。例如,在一个包含员工信息和部门信息的页面,当员工信息变化时,只更新员工相关UI部分,而部门信息部分不更新:
class EmployeeAndDepartmentView extends ConsumerWidget {
@override
Widget build(BuildContext context, WidgetRef ref) {
final employee = ref.select(employeeProvider, (value) => value.employeeInfo);
final department = ref.watch(departmentProvider);
return Column(
children: [
// 显示员工信息UI
Text(employee.name),
// 显示部门信息UI
Text(department.name),
],
);
}
}
- AutoDispose:对于一些临时的、不需要长期存在的
Provider
,使用autoDispose
。例如,一个用于搜索功能的Provider
,当搜索结束或页面离开时,自动释放资源,防止内存泄漏:
final searchResultProvider = AutoDisposeFutureProvider<List<Employee>>((ref) async {
final searchText = ref.watch(searchTextProvider);
// 根据搜索文本进行数据查询
final response = await http.get(Uri.parse('api/employees?search=$searchText'));
return employeeListFromJson(response.body);
});
- Provider Scope:合理使用
ProviderScope
来控制Provider
的作用域。例如,在一个多页面应用中,某些Provider
只在特定页面或页面组内有效,可以将这些Provider
定义在对应的ProviderScope
内。
性能优化策略
- 缓存策略:对于不经常变化的数据,使用缓存。可以在数据层实现缓存逻辑,比如使用内存缓存或本地数据库缓存。当请求数据时,先检查缓存中是否有数据,如果有则直接返回,避免重复的网络请求或数据库查询。例如:
class EmployeeDataRepository {
final Map<int, Employee> _cache = {};
Future<Employee> getEmployee(int id) async {
if (_cache.containsKey(id)) {
return _cache[id]!;
}
final response = await http.get(Uri.parse('api/employees/$id'));
final employee = employeeFromJson(response.body);
_cache[id] = employee;
return employee;
}
}
- Debounce和Throttle:对于高频率的状态更新,如用户输入搜索框时触发的搜索,使用Debounce(防抖)或Throttle(节流)技术。Debounce可以在用户停止输入一段时间后再触发搜索,Throttle则可以限制一定时间内只触发一次搜索。例如,使用
Debounce
:
class SearchViewModel extends ChangeNotifier {
final Debouncer _debouncer = Debouncer(milliseconds: 500);
String _searchText = '';
String get searchText => _searchText;
set searchText(String value) {
_searchText = value;
_debouncer.run(() {
// 触发搜索逻辑
notifyListeners();
});
}
}
- UI Diffing:虽然Flutter本身已经有一定的UI Diffing机制,但通过合理的
Selector
使用和分层设计,可以进一步减少不必要的UI更新,提高渲染性能。确保只有真正变化的UI部分才进行重绘。