Flutter中如何在StreamBuilder构建后更新AppBar标题的totalValue?
解决Flutter中StreamBuilder内调用setState导致的构建错误
错误原因
你遇到的setState() or markNeedsBuild() called during build错误,是因为在**StreamBuilder的builder函数(属于Widget构建流程环节)**中调用了setState。此时Flutter框架正处于构建Widget的阶段,不允许在这个过程中标记Widget需要重新构建,否则会打乱正常的构建流程。
解决方案
方案一:延迟执行setState到构建完成后
使用SchedulerBinding.addPostFrameCallback,将setState的执行延迟到当前Widget构建周期完成之后:
修改StreamBuilder中的计算逻辑部分:
// 替换原来直接赋值或注释的setState代码 SchedulerBinding.instance.addPostFrameCallback((_) { setState(() { totalValue = filteredDocuments.fold( 0.0, (num sum, doc) => sum + (doc['cost'] ?? 0.0)); }); });
方案二:用ValueNotifier实现无setState的状态更新(更优雅)
通过ValueNotifier和ValueListenableBuilder将数值计算与UI展示解耦,避免依赖setState触发全局重建:
- 在State类中定义ValueNotifier并处理资源释放:
class _FilterChipExampleState extends State<FilterChipExample> { // ... 其他原有代码 ValueNotifier<num> totalValueNotifier = ValueNotifier(0); // 新增 @override void dispose() { totalValueNotifier.dispose(); // 页面销毁时释放资源 super.dispose(); } // ... }
- 修改StreamBuilder中的数值计算逻辑:
// 替换原来的totalValue赋值代码 totalValueNotifier.value = filteredDocuments.fold( 0.0, (num sum, doc) => sum + (doc['cost'] ?? 0.0));
- 修改AppBar的title为ValueListenableBuilder:
appBar: AppBar( title: ValueListenableBuilder<num>( valueListenable: totalValueNotifier, builder: (context, value, child) { return Text('$value'); }, ), ),
修改后的完整代码(方案二示例)
import 'package:flutter/material.dart'; import 'package:cloud_firestore/cloud_firestore.dart'; import 'package:calc_tag/services/get_items_for_month.dart'; class FilterChipExample extends StatefulWidget { final int year; final int month; FilterChipExample({required this.year, required this.month}); @override _FilterChipExampleState createState() => _FilterChipExampleState(); } class _FilterChipExampleState extends State<FilterChipExample> { final List<String> categories = [ 'Costanera Norte', 'Vespucio Norte', 'Vespucio Sur', 'Autopista Central', ]; ValueNotifier<num> totalValueNotifier = ValueNotifier(0); String titulo = ''; List<String> selectedCategories = []; @override void dispose() { totalValueNotifier.dispose(); super.dispose(); } @override Widget build(BuildContext context) { return Scaffold( appBar: AppBar( title: ValueListenableBuilder<num>( valueListenable: totalValueNotifier, builder: (context, value, child) { return Text('$value'); }, ), ), body: Column( children: [ Container( padding: const EdgeInsets.all(8.0), margin: const EdgeInsets.all(8.0), child: Wrap( children: categories.map((category) { return FilterChip( selectedColor: Colors.grey, showCheckmark: false, visualDensity: const VisualDensity(vertical: -4, horizontal: -4), selected: selectedCategories.contains(category), label: Container( margin: const EdgeInsets.all(0.0), child: Text(category), ), onSelected: (selected) { setState(() { if (selected) { selectedCategories.add(category); } else { selectedCategories.remove(category); } }); }); }).toList(), ), ), Expanded( child: StreamBuilder<QuerySnapshot>( stream: getItemsForMonth(widget.year, widget.month), builder: (context, snapshot) { if (snapshot.hasError) { return Center(child: Text('Error: ${snapshot.error}')); } if (!snapshot.hasData) { return const Center(child: CircularProgressIndicator()); } final documents = snapshot.data!.docs; final filteredDocuments = documents.where((element) { return selectedCategories.contains(element['contract']); }).toList(); totalValueNotifier.value = filteredDocuments.fold( 0.0, (num sum, doc) => sum + (doc['cost'] ?? 0.0)); return ListView.builder( itemCount: filteredDocuments.length, itemBuilder: (context, index) { return ListTile( title: Text( '${filteredDocuments[index]['contract']} ${(filteredDocuments[index]['created'] as Timestamp).toDate()}'), subtitle: Row( mainAxisAlignment: MainAxisAlignment.start, children: [ Text( '\$ ${filteredDocuments[index]['cost'].toString()}'), const SizedBox( width: 30, ), Text( ' ${filteredDocuments[index]['category'].toString()} ', style: const TextStyle( color: Colors.white, fontWeight: FontWeight.bold, fontSize: 10), ), ], ), ); }, ); }, ), ), ], ), ); } }
内容的提问来源于stack exchange,提问作者Jaime Albornoz Soto
相关产品推荐
相关产品推荐

