英文:
Riverpod state change methods not changing the state as intended
问题
以下是您要翻译的代码部分:
所以,我有一个在 riverpod 中的状态,它保存了一个集合的列表,然后我有两个方法,一个将其转换为数据库中所有集合的列表,另一个将状态仅限于今天的集合,如下所示:
u/riverpod
class SetsProvider extends _$SetsProvider {
u/override
List<Set> build() {
return [];
}
void refresh() async {
final data = await SQLHelper.getAllSets();
state = data.map((e) => Set.fromJson(e)).toList();
}
void getTodaySets() async {
final data = await SQLHelper.getTodayItems();
state = data.map((e) => Set.fromJson(e)).toList();
}
}
当我调用
final list = ref.watch(setsProviderProvider);
它完美地工作
但是当我尝试更改状态以仅返回今天的项目时,它就无法工作:
ref.read(setsProviderProvider.notifier).getTodaySets();
之后,当我观察 setProvider 时,它只会使我的列表为空。我可能做错了什么?
以下是我的 dbhelper 类:
import 'package:flutter/material.dart';
import 'package:sqflite/sqflite.dart' as sql;
import 'set_model.dart';
import 'package:intl/intl.dart';
class SQLHelper {
//创建表的方法
static Future<void> createTables(sql.Database database) async {
await database.execute("""CREATE TABLE sets(
id INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL,
exerciseName TEXT,
totalWeight TEXT,
totalReps TEXT,
date TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP,
isFavorited INTEGER
)
""");
print('数据库已创建');
}
//打开名为 db 的数据库的方法
static Future<sql.Database> db() async {
return sql.openDatabase('dbsets.db', version: 1,
onCreate: (sql.Database database, int version) async {
await createTables(database); //调用上面的创建表方法
});
}
static Future<int> insertSet(Set set) async {
final db = await SQLHelper.db(); //打开数据库
DateTime now = DateTime.now();
String formattedDate = DateFormat('yyyy-MM-dd').format(now);
//将日期更改为当前日期,这意味着我将在 Set 对象中将日期设置为非必需
set.date == formattedDate;
final id = await db.insert('sets', set.toJson(), //toJson 调用 map
conflictAlgorithm: sql.ConflictAlgorithm.replace);
return id;
}
static Future<List<Map<String, dynamic>>> getAllSets() async {
final db = await SQLHelper.db();
return db.query('sets', orderBy: 'id');
}
static Future<List<Map<String, dynamic>>> getTodayItems() async {
final db = await SQLHelper.db(); //获取连接
DateTime now = DateTime.now();
String formattedDate = DateFormat('yyyy-MM-dd').format(now);
final results =
db.rawQuery('SELECT * FROM sets WHERE date = ?', [formattedDate]);
print(results.toString());
return results;
}
}
以及我尝试在 Main.dart 中显示集合的地方:
child: ListView.builder(
scrollDirection: Axis.vertical,
shrinkWrap: true,
itemCount: ref.watch(setsProviderProvider).length,
itemBuilder: ((_, index) {
ref.read(setsProviderProvider.notifier).getTodaySets();
final list = ref.watch(setsProviderProvider);
var set = list[index];
return ListTile(
title: Text(set.exerciseName.toString()),
subtitle: Column(
children: [
Text(
set.totalWeight.toString(),
),
Text(
set.totalReps.toString(),
)
],
),
);
}),
),
英文:
So I have a state in riverpod which holds a List of sets then I have two methods that make it a List of all the sets in the database and another that makes the state only today sets as follows:
u/riverpod
class SetsProvider extends _$SetsProvider {
u/override
List<Set> build() {
return [];
}
void refresh() async {
final data = await SQLHelper.getAllSets();
state = data.map((e) => Set.fromJson(e)).toList();
}
void getTodaySets() async {
final data = await SQLHelper.getTodayItems();
state = data.map((e) => Set.fromJson(e)).toList();
}
When I call
final list = ref.watch(setsProviderProvider);
it works perfectly
But when I try to change the state to return only todays items it just doesn't work:
ref.read(setsProviderProvider.notifier).getTodaySets();
it just makes my list empty when I watch the setProvider after that. What could I be doing wrong?
here is my dbhelper class:
import 'package:flutter/material.dart';
import 'package:sqflite/sqflite.dart' as sql;
import 'set_model.dart';
import 'package:intl/intl.dart';
class SQLHelper {
//method to create table
static Future<void> createTables(sql.Database database) async {
await database.execute("""CREATE TABLE sets(
id INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL,
exerciseName TEXT,
totalWeight TEXT,
totalReps TEXT,
date TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP,
isFavorited INTEGER
)
""");
print('Database created');
}
//method to open the database called db
static Future<sql.Database> db() async {
return sql.openDatabase('dbsets.db', version: 1,
onCreate: (sql.Database database, int version) async {
await createTables(database); //calling that create table method above
});
}
static Future<int> insertSet(Set set) async {
final db = await SQLHelper.db(); //opening the database
DateTime now = DateTime.now();
String formattedDate = DateFormat('yyyy-MM-dd').format(now);
//Changing the Date to now meaning ill make the date not required in a Set object
set.date == formattedDate;
final id = await db.insert('sets', set.toJson(), //toJson calls the map
conflictAlgorithm: sql.ConflictAlgorithm.replace);
return id;
}
static Future<List<Map<String, dynamic>>> getAllSets() async {
final db = await SQLHelper.db();
return db.query('sets', orderBy: 'id');
}
static Future<List<Map<String, dynamic>>> getTodayItems() async {
final db = await SQLHelper.db(); //get connection
DateTime now = DateTime.now();
String formattedDate = DateFormat('yyyy-MM-dd').format(now);
final results =
db.rawQuery('SELECT * FROM sets WHERE date = ?', [formattedDate]);
print(results.toString());
return results;
}
}
And where I am trying to show the sets in Main.dart:
child: ListView.builder(
scrollDirection: Axis.vertical,
shrinkWrap: true,
itemCount: ref.watch(setsProviderProvider).length,
itemBuilder: ((_, index) {
ref.read(setsProviderProvider.notifier).getTodaySets();
final list = ref.watch(setsProviderProvider);
var set = list[index];
return ListTile(
title: Text(set.exerciseName.toString()),
subtitle: Column(
children: [
Text(
set.totalWeight.toString(),
),
Text(
set.totalReps.toString(),
)
],
),
);
}),
),
答案1
得分: 0
首先,不要在 build
方法中进行数据库调用,因为它可能会运行多次。你可以将它放在诸如 initState
或 main()
这样的地方。
列表没有更新,因为初始列表是空的,itemCount
为 0,所以 ListView
的项构建器从未被调用。
更新:我的意思是 Widget.build
,而不是 provider
的 build
。最佳做法是在 provider
的 build
中调用它,但这也会将 provider
更改为异步提供程序。
itemBuilder: ((_, index) {
ref.read(setsProviderProvider.notifier).getTodaySets(); // <- 这不应该在这里/
final list = ref.watch(setsProviderProvider);
英文:
First don't put database calls in during build
, it might run multiple time.
You could put it in places such as initState
or main()
.
The list didn't update because the initial list is empty, itemCount
is 0, so item builder of ListView
is never called.
Update: I meant Widget.build
, not provider build
. The best practice is to call it in provider's build, but this would also change the provider to async provider.
itemBuilder: ((_, index) {
ref.read(setsProviderProvider.notifier).getTodaySets(); // <- This shouldn't be here/
final list = ref.watch(setsProviderProvider);
</details>
# 答案2
**得分**: 0
你应该让筛选提供程序*监视*完整列表提供程序,同时将筛选参数作为家庭键。然后,当你更新完整列表提供程序(无论是来自本地变更还是观察数据库流),筛选提供程序将具有正确的值,并在被监视时进行缓存。
<details>
<summary>英文:</summary>
You should have the filter provider *watch* the full-list provider, as well as take a filter argument as a family key. Then, as you update the full-list provider (either from local mutations, or from watching a database stream), the filter provider(s) will have the proper value, caching as long as they are being watched.
</details>
通过集体智慧和协作来改善编程学习和解决问题的方式。致力于成为全球开发者共同参与的知识库,让每个人都能够通过互相帮助和分享经验来进步。
评论