我是新来的颤振。我实现了这个示例应用程序来了解 SQLite 和提供程序状态管理。这是一个简单的任务管理系统。我使用 MOOR 来处理 SQLite 数据库。我遇到的问题是添加任务后任务列表没有更新。如果我重新启动应用程序,我可以看到任务已成功保存在数据库中。
Moor 查询实现
Future<List<Task>> getAllTasks() => select(tasks).get();
Stream<List<Task>> watchAllTasks() => select(tasks).watch();
Future<int> insertTask(TasksCompanion task) => into(tasks).insert(task);
任务库
在这里,我将粘贴整个课程以了解我的实现。
class TaskRepository{
Stream<List<DisplayTaskData>> getAllTasks(){
var watchAllTasks = AppDatabase().watchAllTasks();
final formattedTasks = List<DisplayTaskData>();
return watchAllTasks.map((tasks) {
tasks.forEach((element) {
var date = element.dueDate;
String dueDateInString;
if(date != null){
dueDateInString = ""
"${date.year.toString()}-"
"${date.month.toString().padLeft(2,'0')}-"
"${date.day.toString().padLeft(2,'0')}";
}
var displayTaskData = DisplayTaskData(task : element.task, dueDate: dueDateInString);
formattedTasks.add(displayTaskData);
});
return formattedTasks;
});
}
Future<Resource<int>> insertTask(TasksCompanion task) async{
try {
int insertTaskId = await AppDatabase().insertTask(task);
return Resource(DataProcessingStatus.SUCCESS, insertTaskId);
}on InvalidDataException catch (e) {
var displayMessage = DisplayMessage(
title : "Data Insertion Error",
description : "Something went wrong please try again"
);
return Resource.displayConstructor(DataProcessingStatus.PROCESSING_ERROR,displayMessage);
}
}
}
我有一个视图模型层来将值推送到 UI 端
基础视图模型
class BaseViewModel extends ChangeNotifier {
ViewState _state = ViewState.IDLE;
ViewState get state => _state;
void setState(ViewState viewState) {
_state = viewState;
notifyListeners();
}
}
任务视图模型
class TaskViewModel extends BaseViewModel{
final TaskRepository _repository = TaskRepository();
DateTime _deuDate;
Stream<List<DisplayTaskData>> getAllTasks(){
return _repository.getAllTasks().map((formattedTasks) => formattedTasks);
}
Future<void> insertTask() async {
setState(ViewState.PROCESSING);
var tasksCompanion = TasksCompanion(task: Value(_taskValidation.value),dueDate: Value(_deuDate));
insertTaskStatus = await _repository.insertTask(tasksCompanion);
setState(ViewState.IDLE);
}
}
主功能
void main() {
Stetho.initialize();
final taskViewModel = TaskViewModel();
runApp(
MultiProvider(
providers: [
ChangeNotifierProvider(create: (_) => taskViewModel),
StreamProvider(create:(context) => taskViewModel.getAllTasks())
],
child: MyApp(),
),
);
}
任务视图
class TaskView extends StatelessWidget {
DateTime selectedDate = DateTime.now();
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: Text('Tasks'),
),
body : ListView.builder(
itemCount: context.watch<List<DisplayTaskData>>()?.length,
itemBuilder: (context, index) => _taskListView(context, index),
),
floatingActionButton: FloatingActionButton(
onPressed: () {
showMaterialModalBottomSheet(
context: context,
builder: (context, scrollController) =>
Container(
child: bottomSheet(context),
),
);
},
child: Icon(
Icons.add,
color: Colors.white,
),
backgroundColor: Colors.blueAccent,
));
}
Widget _taskListView(BuildContext context, int index) {
var task = context.watch<List<DisplayTaskData>>()[index];
return Row(
children: <Widget>[
Align(
alignment: Alignment.topLeft,
child: Column(children: <Widget>[
Text(task.task),
SizedBox(height: 8),
Text("hardcoded value")
],),
)
],
);
}
}
我期望发生的是当我插入一个任务getAllTasks()
流时应该流式传输新添加的任务并且列表应该得到自动更新。
粘贴存储库和 ViewModel 的整个代码的原因是为了表明我在插入和检索任务中使用了相同的类。我将它们结合在一起,因为这两个操作都与任务相关,并且计划在同一个类中也有更新和删除功能。我之所以强调这一点,是因为我怀疑 main 函数中的实现是否正确。但是,我仍然使用相同的对象ChangeNotifierProvider
和StreamProvider
更新
首先,对于两次粘贴 TaskRepository 类代码,我深表歉意。在那个地方,我的 ViewModel 类应该已经出现了,我已经在上面更正了。
我从原始代码中改变的东西很少。首先,AppDatabase
该类不是单例类,因此即使应用程序没有崩溃,Logcat 也会出现错误。
正如我之前提到的,当应用程序重新启动时,所有数据库值都被加载到 listview 中。但是通过放置一个调试点,我注意到第一次_taskListView
方法触发结果列表为空,并且它给出了错误,因为我访问了该索引。我不知道原因,但我添加了一个空检查并解决了这个问题。
完成这些更改后,我尝试插入一个任务,看看会发生什么。所以在插入重新编码时,Logcat 给出了以下日志。
2020-10-28 21:02:29.845 4258-4294/com.example.sqlite_test I/flutter: Moor: Sent INSERT INTO tasks (task, due_date) VALUES (?, ?) with args [Task 8, 1604082600]
2020-10-28 21:02:29.905 4258-4294/com.example.sqlite_test I/flutter: Moor: Sent SELECT * FROM tasks; with args []
因此,根据此日志watchAllTasks()
在插入重新编码后触发。但是 UI 没有得到更新。getAllTasks()
即使在视图模型中触发,我也会通过放置调试点来检查。这似乎是一个 UI 错误。并且渲染的 List 在任务名称之前也有一个边距。列表视图不适合手机屏幕。看看下面的截图。
我做了一些关于在 Flutter 中处理流的研究。这个例子出现了。根据this我的语法getAllTasks()
在存储库中是错误的。所以我根据下面的内容更改了功能。
Stream<List<DisplayTaskData>> getAllTasks() async*{
var watchAllTasks = AppDatabase().watchAllTasks();
final formattedTasks = List<DisplayTaskData>();
watchAllTasks.map((tasks) {
tasks.forEach((element) {
var date = element.dueDate;
String dueDateInString;
if(date != null){
dueDateInString = ""
"${date.year.toString()}-"
"${date.month.toString().padLeft(2,'0')}-"
"${date.day.toString().padLeft(2,'0')}";
}
var displayTaskData = DisplayTaskData(task : element.task, dueDate: dueDateInString);
formattedTasks.add(displayTaskData);
});
});
yield formattedTasks;
}
通过此更改,列表甚至不会在应用程序重新启动时加载。在这一点上,我不清楚错误在哪里。我试图缩小范围。我希望这些更新的信息将帮助某人查明此代码中的问题并帮助我。
感谢所有的答案。