first commit

This commit is contained in:
2025-09-24 14:08:54 +08:00
commit 40784642cf
83 changed files with 37832 additions and 0 deletions

504
basic_utils/base_mvvm.md Normal file
View File

@@ -0,0 +1,504 @@
# Base MVVM - 基础MVVM架构模块文档
## 模块概述
`base_mvvm` 是 OneApp 基础工具模块群中的MVVM架构基础模块提供了完整的Model-View-ViewModel架构模式实现。该模块封装了状态管理、数据绑定、Provider模式等核心功能为应用提供了统一的架构规范和基础组件。
### 基本信息
- **模块名称**: base_mvvm
- **模块路径**: oneapp_basic_utils/base_mvvm
- **类型**: Flutter Package Module
- **主要功能**: MVVM架构基础、状态管理、数据绑定
### 核心特性
- **状态管理**: 基于Provider的响应式状态管理
- **视图状态**: 统一的页面状态管理(idle/busy/empty/error)
- **列表刷新**: 封装下拉刷新和上拉加载更多
- **数据绑定**: 双向数据绑定支持
- **错误处理**: 统一的错误类型和处理机制
- **日志工具**: 集成日志记录功能
## 目录结构
```
base_mvvm/
├── lib/
│ ├── base_mvvm.dart # 模块入口文件
│ ├── provider/ # Provider相关
│ │ ├── provider_widget.dart # Provider封装组件
│ │ ├── view_state_model.dart # 视图状态模型
│ │ ├── view_state_list_model.dart # 列表状态模型
│ │ ├── view_state_refresh_list_model.dart # 刷新列表模型
│ │ └── view_state.dart # 状态枚举定义
│ ├── utils/ # 工具类
│ │ ├── logs/ # 日志工具
│ │ │ └── log_utils.dart
│ │ └── rxbus.dart # 事件总线
│ └── widgets/ # 基础组件
│ ├── glides/ # 图片组件
│ │ └── glide_image_view.dart
│ └── refresh/ # 刷新组件
│ └── base_easy_refresh.dart
└── pubspec.yaml # 依赖配置
```
## 核心架构组件
### 1. 视图状态枚举 (ViewState)
定义了应用中通用的页面状态:
```dart
/// 页面状态类型
enum ViewState {
idle, // 空闲状态
busy, // 加载中
empty, // 无数据
error, // 加载失败
}
/// 错误类型
enum ViewStateErrorType {
none,
defaultError,
networkTimeOutError, // 网络超时
disconnectException, // 断网
noNetworkSignal, // 无网络信号
}
```
### 2. 基础视图状态模型 (ViewStateModel)
所有ViewModel的基础类提供统一的状态管理
```dart
class ViewStateModel with ChangeNotifier {
/// 防止页面销毁后异步任务才完成导致报错
bool _disposed = false;
/// 当前页面状态默认为idle
ViewState _viewState = ViewState.idle;
/// 错误类型
ViewStateErrorType _viewStateError = ViewStateErrorType.none;
/// 构造函数,可指定初始状态
ViewStateModel({ViewState? viewState})
: _viewState = viewState ?? ViewState.idle;
// 状态获取器
ViewState get viewState => _viewState;
ViewStateErrorType get viewStateError => _viewStateError;
// 状态判断方法
bool get isBusy => viewState == ViewState.busy;
bool get isIdle => viewState == ViewState.idle;
bool get isEmpty => viewState == ViewState.empty;
bool get isError => viewState == ViewState.error;
// 状态设置方法
void setIdle() => viewState = ViewState.idle;
void setBusy() => viewState = ViewState.busy;
void setEmpty() => viewState = ViewState.empty;
@override
void notifyListeners() {
if (!_disposed) {
super.notifyListeners();
}
}
@override
void dispose() {
_disposed = true;
super.dispose();
}
}
```
### 3. Provider封装组件 (ProviderWidget)
简化Provider使用的封装组件
```dart
class ProviderWidget<T extends ChangeNotifier> extends StatefulWidget {
final ValueWidgetBuilder<T> builder;
final T model;
final Widget? child;
final Function(T model)? onModelReady;
final bool autoDispose;
const ProviderWidget({
super.key,
required this.builder,
required this.model,
this.child,
this.onModelReady,
this.autoDispose = true
});
@override
_ProviderWidgetState<T> createState() => _ProviderWidgetState<T>();
}
class _ProviderWidgetState<T extends ChangeNotifier>
extends State<ProviderWidget<T>> {
late T model;
@override
void initState() {
model = widget.model;
widget.onModelReady?.call(model);
super.initState();
}
@override
void dispose() {
if (widget.autoDispose) {
model.dispose();
}
super.dispose();
}
@override
Widget build(BuildContext context) {
return ChangeNotifierProvider<T>.value(
value: model,
child: Consumer<T>(builder: widget.builder, child: widget.child)
);
}
}
```
### 4. 多Provider支持 (ProviderWidget2)
支持同时管理两个Model的组件
```dart
class ProviderWidget2<A extends ChangeNotifier, B extends ChangeNotifier>
extends StatefulWidget {
final Widget Function(BuildContext context, A model1, B model2, Widget? child) builder;
final A model1;
final B model2;
final Widget? child;
final Function(A model1, B model2)? onModelReady;
final bool autoDispose;
// ... 实现与ProviderWidget类似支持双Model管理
}
```
### 5. 刷新列表状态模型 (ViewStateRefreshListModel)
专门用于处理列表数据的刷新和加载更多功能:
```dart
abstract class ViewStateRefreshListModel<T> extends ViewStateListModel<T> {
/// 分页配置
static const int pageNumFirst = 1;
static const int pageSize = 10;
/// 列表刷新控制器
final EasyRefreshController _refreshController;
EasyRefreshController get refreshController => _refreshController;
/// 当前页码
int _currentPageNum = pageNumFirst;
ViewStateRefreshListModel({super.viewState, bool isMore = true})
: _refreshController = EasyRefreshController(
controlFinishLoad: isMore,
controlFinishRefresh: true);
/// 下拉刷新
@override
Future<List<T>> refresh({bool init = false}) async {
try {
_currentPageNum = pageNumFirst;
var data = await loadData(pageNum: pageNumFirst);
refreshController.finishRefresh();
if (data.isEmpty) {
refreshController.finishLoad(IndicatorResult.none);
list.clear();
setEmpty();
} else {
onCompleted(data);
list.clear();
list.addAll(data);
// 小于分页数量时禁止上拉加载更多
if (data.length < pageSize) {
Future.delayed(const Duration(milliseconds: 100)).then((value) {
refreshController.finishLoad(IndicatorResult.noMore);
});
}
setIdle();
}
return data;
} catch (e) {
if (init) list.clear();
refreshController.finishLoad(IndicatorResult.fail);
setEmpty();
return [];
}
}
/// 上拉加载更多
Future<List<T>> loadMore() async {
try {
var data = await loadData(pageNum: ++_currentPageNum);
refreshController.finishRefresh();
if (data.isEmpty) {
_currentPageNum--;
refreshController.finishLoad(IndicatorResult.noMore);
} else {
onCompleted(data);
list.addAll(data);
if (data.length < pageSize) {
refreshController.finishLoad(IndicatorResult.noMore);
} else {
refreshController.finishLoad();
}
notifyListeners();
}
return data;
} catch (e) {
_currentPageNum--;
refreshController.finishLoad(IndicatorResult.fail);
return [];
}
}
/// 抽象方法:加载数据
@override
Future<List<T>> loadData({int? pageNum});
@override
void dispose() {
_refreshController.dispose();
super.dispose();
}
}
```
## 使用指南
### 1. 基础ViewModel示例
创建一个继承ViewStateModel的ViewModel
```dart
class UserProfileViewModel extends ViewStateModel {
UserInfo? _userInfo;
UserInfo? get userInfo => _userInfo;
Future<void> loadUserProfile(String userId) async {
setBusy(); // 设置加载状态
try {
_userInfo = await userRepository.getUserProfile(userId);
setIdle(); // 设置空闲状态
} catch (e) {
setError(); // 设置错误状态
}
}
}
```
### 2. 在UI中使用ProviderWidget
```dart
class UserProfilePage extends StatelessWidget {
@override
Widget build(BuildContext context) {
return ProviderWidget<UserProfileViewModel>(
model: UserProfileViewModel(),
onModelReady: (model) => model.loadUserProfile("123"),
builder: (context, model, child) {
if (model.isBusy) {
return Center(child: CircularProgressIndicator());
}
if (model.isEmpty) {
return Center(child: Text('暂无数据'));
}
if (model.isError) {
return Center(child: Text('加载失败'));
}
return Column(
children: [
Text(model.userInfo?.name ?? ''),
Text(model.userInfo?.email ?? ''),
],
);
},
);
}
}
```
### 3. 列表刷新示例
```dart
class NewsListViewModel extends ViewStateRefreshListModel<NewsItem> {
final NewsRepository _repository = NewsRepository();
@override
Future<List<NewsItem>> loadData({int? pageNum}) async {
return await _repository.getNewsList(
page: pageNum ?? 1,
pageSize: ViewStateRefreshListModel.pageSize,
);
}
}
// UI使用
class NewsListPage extends StatelessWidget {
@override
Widget build(BuildContext context) {
return ProviderWidget<NewsListViewModel>(
model: NewsListViewModel(),
onModelReady: (model) => model.refresh(init: true),
builder: (context, model, child) {
return EasyRefresh(
controller: model.refreshController,
onRefresh: () => model.refresh(),
onLoad: () => model.loadMore(),
child: ListView.builder(
itemCount: model.list.length,
itemBuilder: (context, index) {
final item = model.list[index];
return ListTile(
title: Text(item.title),
subtitle: Text(item.summary),
);
},
),
);
},
);
}
}
```
### 4. 双Model管理示例
```dart
class DashboardPage extends StatelessWidget {
@override
Widget build(BuildContext context) {
return ProviderWidget2<UserViewModel, NotificationViewModel>(
model1: UserViewModel(),
model2: NotificationViewModel(),
onModelReady: (userModel, notificationModel) {
userModel.loadUserInfo();
notificationModel.loadNotifications();
},
builder: (context, userModel, notificationModel, child) {
return Scaffold(
body: Column(
children: [
// 用户信息区域
if (userModel.isBusy)
CircularProgressIndicator()
else
UserInfoWidget(user: userModel.currentUser),
// 通知区域
if (notificationModel.isBusy)
CircularProgressIndicator()
else
NotificationListWidget(notifications: notificationModel.notifications),
],
),
);
},
);
}
}
```
## 依赖配置
### pubspec.yaml 关键依赖
```yaml
dependencies:
flutter:
sdk: flutter
# 状态管理
provider: ^6.0.0
# 列表刷新
easy_refresh: ^3.0.0
# 日志记录
# (自定义日志工具类)
dev_dependencies:
flutter_test:
sdk: flutter
```
## 最佳实践
### 1. ViewModel设计原则
- 继承ViewStateModel获得基础状态管理能力
- 将业务逻辑封装在ViewModel中保持View的简洁
- 合理使用状态枚举,提供良好的用户体验反馈
- 及时释放资源,避免内存泄漏
### 2. 错误处理策略
- 使用ViewStateErrorType枚举区分不同错误类型
- 在UI层根据错误类型提供相应的用户提示
- 网络错误提供重试机制
### 3. 列表优化建议
- 使用ViewStateRefreshListModel处理列表数据
- 合理设置分页大小,平衡性能和用户体验
- 实现适当的缓存策略减少不必要的网络请求
### 4. 内存管理
- ProviderWidget默认开启autoDispose自动管理Model生命周期
- 在Model的dispose方法中清理定时器、流订阅等资源
- 避免在已销毁的Model上调用notifyListeners
## 扩展开发
### 1. 自定义状态类型
可以扩展ViewState枚举添加业务特定的状态
```dart
enum CustomViewState {
idle,
busy,
empty,
error,
networkError, // 自定义网络错误状态
authRequired, // 自定义需要认证状态
}
```
### 2. 自定义刷新组件
基于base_mvvm的基础组件可以创建适合特定业务场景的刷新组件。
### 3. 状态持久化
结合SharedPreferences等持久化方案实现ViewState的持久化存储。
## 问题排查
### 常见问题
1. **Model未正确释放**: 检查ProviderWidget的autoDispose设置
2. **状态更新无效**: 确认notifyListeners调用时机
3. **列表刷新异常**: 检查EasyRefreshController的状态管理
### 调试技巧
- 使用LogUtils记录关键状态变更
- 通过ViewState枚举值判断当前页面状态
- 利用Flutter Inspector查看Provider状态