Flutter Provider添加交易后需手动刷新分类信息问题求助
记账应用分类详情自动更新问题
我正在开发一款记账应用,希望实现添加交易后,分类详情能根据交易所属分类自动更新。目前用随机数做测试,逻辑后续再完善,但现在的问题是添加记录后必须手动刷新才能看到分类详情变化。
现有代码
SqfliteProvider类代码
class SqfliteProvider with ChangeNotifier { Database? _database; Future<Database> get getdatabase async { _database = await openDatabase( join(await getDatabasesPath(), 'expensedb34.db'), version: 1, onCreate: (db, version) { db.execute( "CREATE TABLE categorytb(title TEXT NOT NULL,entries INTEGER NOT NULL,totalamount STRING NOT NULL)"); db.execute( "CREATE TABLE expensetb(id TEXT NOT NULL,title TEXT NOT NULL,amount STRING NOT NULL,category STRING NOT NULL,date STRING NOT NULL)"); //add some default categories for (int x = 0; x < icons.length; x++) { db.insert('categorytb', { 'title': icons.keys.toList()[x], 'entries': 0, 'totalamount': (0.0).toString() }); } }); return _database!; } Future<List<CategoryModel>?> fetchcategory() async { final db = await getdatabase; List<CategoryModel> templist = []; List<Map<String, dynamic>> maplist = await db.query('categorytb'); if (maplist.isEmpty) return null; templist = maplist.map((e) => CategoryModel.frommap(e)).toList(); return templist; } Future<void> addTransaction(TransactionModel transactionModel) async { final db = await getdatabase; //insert in database await db.insert('expensetb', transactionModel.tomap(), conflictAlgorithm: ConflictAlgorithm.replace); notifyListeners(); await db.update('categorytb', { 'entries':Random().nextInt(500) },where: 'title==?',whereArgs:['Travel']).then((value) => print('Updated')); notifyListeners(); //update category information } }
分类详情展示Widget代码
class CategoryFetcher extends StatelessWidget { @override Widget build(BuildContext context) { final provider=Provider.of<SqfliteProvider>(context,listen: false); return FutureBuilder( future: provider.fetchcategory(), builder: (context,AsyncSnapshot<List<CategoryModel>?>snapshot){ if (snapshot.connectionState == ConnectionState.waiting) { return Center(child: WaitWidget()); } else if (snapshot.hasError) { return Center( child: Text(snapshot.error.toString()), ); } else if (snapshot.hasData) { if (snapshot.data != null) { return ListView.builder( itemCount: snapshot.data!.length, itemBuilder: (context, index) { CategoryModel t = snapshot.data![index]; return CategoryCard(categorymodel: t); }); } else { return Text('Something wrong'); } } else { return Text('Last if'); } }, ); } }
问题原因
CategoryFetcher中设置Provider.of(..., listen: false),关闭了状态监听,导致notifyListeners()触发时Widget不会重建。FutureBuilder的future参数仅在Widget首次构建时执行一次fetchcategory(),后续Provider状态变化不会重新触发数据库查询,无法获取最新分类数据。addTransaction中先调用notifyListeners()再更新数据库,会导致Widget提前用旧数据重建,逻辑顺序错误。
修复方案
方案一:维护分类数据缓存(推荐)
修改SqfliteProvider,添加缓存变量存储分类数据,更新数据库后清空缓存并通知状态变化:
class SqfliteProvider with ChangeNotifier { Database? _database; List<CategoryModel>? _categories; // 新增缓存变量 Future<Database> get getdatabase async { // 原代码保持不变 _database = await openDatabase( join(await getDatabasesPath(), 'expensedb34.db'), version: 1, onCreate: (db, version) { db.execute( "CREATE TABLE categorytb(title TEXT NOT NULL,entries INTEGER NOT NULL,totalamount STRING NOT NULL)"); db.execute( "CREATE TABLE expensetb(id TEXT NOT NULL,title TEXT NOT NULL,amount STRING NOT NULL,category STRING NOT NULL,date STRING NOT NULL)"); for (int x = 0; x < icons.length; x++) { db.insert('categorytb', { 'title': icons.keys.toList()[x], 'entries': 0, 'totalamount': (0.0).toString() }); } }); return _database!; } // 新增:优先返回缓存数据,无缓存则从数据库加载 Future<List<CategoryModel>?> get categories async { if (_categories == null) { _categories = await fetchcategory(); } return _categories; } Future<List<CategoryModel>?> fetchcategory() async { final db = await getdatabase; List<Map<String, dynamic>> maplist = await db.query('categorytb'); if (maplist.isEmpty) return null; return maplist.map((e) => CategoryModel.frommap(e)).toList(); } Future<void> addTransaction(TransactionModel transactionModel) async { final db = await getdatabase; // 1. 插入交易记录 await db.insert('expensetb', transactionModel.tomap(), conflictAlgorithm: ConflictAlgorithm.replace); // 2. 更新分类数据 await db.update('categorytb', { 'entries': Random().nextInt(500) }, where: 'title==?', whereArgs: ['Travel']); // 3. 清空缓存,下次获取时加载最新数据 _categories = null; // 4. 通知状态变化,触发Widget重建 notifyListeners(); } }
修改CategoryFetcher,监听Provider状态并使用缓存的分类数据:
class CategoryFetcher extends StatelessWidget { @override Widget build(BuildContext context) { return FutureBuilder( future: Provider.of<SqfliteProvider>(context).categories, builder: (context, AsyncSnapshot<List<CategoryModel>?> snapshot) { if (snapshot.connectionState == ConnectionState.waiting) { return const Center(child: WaitWidget()); } else if (snapshot.hasError) { return Center(child: Text(snapshot.error.toString())); } else if (snapshot.hasData && snapshot.data != null) { return ListView.builder( itemCount: snapshot.data!.length, itemBuilder: (context, index) { CategoryModel t = snapshot.data![index]; return CategoryCard(categorymodel: t); }, ); } else { return const Text('暂无分类数据'); } }, ); } }
方案二:使用Consumer触发重新查询
如果不想维护缓存,可以用Consumer包裹FutureBuilder,每次Provider状态变化时重新执行数据库查询:
class CategoryFetcher extends StatelessWidget { @override Widget build(BuildContext context) { return Consumer<SqfliteProvider>( builder: (context, provider, child) { return FutureBuilder( future: provider.fetchcategory(), builder: (context, AsyncSnapshot<List<CategoryModel>?> snapshot) { if (snapshot.connectionState == ConnectionState.waiting) { return const Center(child: WaitWidget()); } else if (snapshot.hasError) { return Center(child: Text(snapshot.error.toString())); } else if (snapshot.hasData && snapshot.data != null) { return ListView.builder( itemCount: snapshot.data!.length, itemBuilder: (context, index) { CategoryModel t = snapshot.data![index]; return CategoryCard(categorymodel: t); }, ); } else { return const Text('暂无分类数据'); } }, ); }, ); } }
同时修改addTransaction中的notifyListeners()位置,确保数据库更新完成后再通知:
Future<void> addTransaction(TransactionModel transactionModel) async { final db = await getdatabase; await db.insert('expensetb', transactionModel.tomap(), conflictAlgorithm: ConflictAlgorithm.replace); await db.update('categorytb', { 'entries': Random().nextInt(500) }, where: 'title==?', whereArgs: ['Travel']); // 只在所有数据库操作完成后通知一次 notifyListeners(); }
内容的提问来源于stack exchange,提问作者Irfan Ganatra
相关产品推荐
相关产品推荐

