Flutter添加数据后ListView未实时更新,仅热重载生效问题求助
问题:添加数据后ListView无法实时更新,需热重载才能显示
我尝试从BottomSheet获取数据存入ProductInfos模型,再在ListView中展示相关数据。目前流程看似正常,但添加新数据后无法实时显示在ListView中,必须热重载应用才能看到新增数据,且每次添加都需要重复热重载,尚未找到解决办法。
主页面代码
bool? isChecked = false; final boy = TextEditingController(); final adet = TextEditingController(); final kilo = TextEditingController(); final firma = TextEditingController(); final kalit = TextEditingController(); final kalinlik = TextEditingController(); final en = TextEditingController(); class MyHomePage extends ConsumerWidget { const MyHomePage({super.key, required this.title}); final String title; @override Widget build(BuildContext context, WidgetRef ref) { var size = MediaQuery.of(context).size; final productInfoProvider = ref.watch(productProvider); ProductInfos product1 = ProductInfos( firmAdi: firma.text, kalite: kalit.text, kalinlik: kalinlik.text, en: en.text, boy: boy.text, adet: adet.text, kilo: kilo.text, pvc: isChecked); ProductInfos product2 = ProductInfos( firmAdi: "firma.text", kalite: "kalit.text", kalinlik: "kalinlik.text", en: "en.text", boy: "boy.text", adet: "adet.text", kilo: "kilo.text", pvc: true); return Scaffold( appBar: AppBar( title: Text(title), ), body: Column( children: [ SizedBox( height: size.height * 0.5, child: ListView.separated( separatorBuilder: (context, index) { return const Divider( height: 5, thickness: 2, ); }, itemCount: productInfoProvider.products.length, itemBuilder: (BuildContext context, int index) { return Container( color: Colors.white, child: ListTile( leading: Text(productInfoProvider .products[index].firmAdi .toString()), subtitle: Text( productInfoProvider.products[index].en.toString()), title: Text( "${productInfoProvider.products[index].kalite.toString()} kalite"), ), ); }, )), Center( child: SizedBox( height: size.height * 0.2, child: ElevatedButton( style: ElevatedButton.styleFrom( backgroundColor: Colors.white, foregroundColor: Colors.black), onPressed: () { print(productInfoProvider.products.length); showModalBottomSheet( context: context, builder: ((context) { return StatefulBuilder( //bottomSheet de statefulbuilder kullanmazsak state yenilenmiyor builder: (BuildContext context, StateSetter myState) { return SizedBox( height: size.height * 0.5, child: Padding( padding: const EdgeInsets.all(10.0), child: Column( children: [ Row( mainAxisAlignment: MainAxisAlignment.spaceBetween, children: [ solBottomSheet(size, myState), sagBottomSheet(size, myState), ], ), Align( alignment: Alignment.bottomCenter, child: ElevatedButton( onPressed: (() { myState(() { ref .read(productProvider) .addProduct(product1); inspect(productInfoProvider .products); }); }), style: ElevatedButton.styleFrom( backgroundColor: const Color(0xff74a6cc)), child: const Text("ekle"), )), ], ), ), ); }); })); }, child: const Text("BottomSheet"), )), ), ], )); } SizedBox sagBottomSheet(Size size, StateSetter myState) { return SizedBox( width: size.width * 0.4, child: Column( children: [ CustomTextField( textController: boy, t: TextInputType.number, hintText: "Boy", ic: const Icon(Icons.numbers_outlined), dgonly: [FilteringTextInputFormatter.digitsOnly], ), space(), CustomTextField( textController: adet, t: TextInputType.number, hintText: "Adet", ic: const Icon(Icons.numbers_outlined), dgonly: [FilteringTextInputFormatter.digitsOnly], ), space(), CustomTextField( textController: kilo, t: TextInputType.number, hintText: "Kilo", ic: const Icon(Icons.numbers_outlined), dgonly: [FilteringTextInputFormatter.digitsOnly], ), space(), CheckboxListTile( title: const Text( "PVC", style: TextStyle( color: Color(0xff74a6cc), fontWeight: FontWeight.w800), ), value: isChecked, onChanged: ((value) { myState( () { isChecked = value; }, ); })), ], )); } SizedBox solBottomSheet(Size size, StateSetter myState) { return SizedBox( width: size.width * 0.4, child: Column( children: [ CustomTextField( textController: firma, t: TextInputType.name, hintText: "Firma", ic: const Icon(Icons.home), ), space(), CustomTextField( textController: kalit, t: TextInputType.number, hintText: "Kalite", ic: const Icon(Icons.high_quality_outlined), dgonly: [FilteringTextInputFormatter.digitsOnly], ), space(), CustomTextField( textController: kalinlik, t: TextInputType.number, hintText: "Kalınlık", ic: const Icon(Icons.high_quality_outlined), dgonly: [FilteringTextInputFormatter.digitsOnly], ), space(), CustomTextField( textController: en, t: TextInputType.number, hintText: "En", ic: const Icon(Icons.numbers_outlined), dgonly: [FilteringTextInputFormatter.digitsOnly], ), ], ), ); } SizedBox space() { return const SizedBox( height: 10, ); } }
Provider代码
//this is provider code class ProductInfoRepo extends ChangeNotifier { List<ProductInfos> products = [ ProductInfos( firmAdi: "firm1", kalite: "430", kalinlik: "0.50", en: "750", boy: "1000", adet: "500", kilo: "1500", pvc: true), ]; addProduct(ProductInfos product) { products.add(product); notifyListeners(); } } final productProvider = ChangeNotifierProvider((((ref) { return ProductInfoRepo(); })));
解决方案
问题根源
- 全局变量导致状态不一致:
isChecked和所有TextEditingController都是全局变量,脱离了Widget的生命周期管理,输入值更新无法同步到后续创建的ProductInfos实例。 product1初始化时机错误:product1在build方法中创建,此时获取的是控制器的初始空值;用户在BottomSheet输入内容后,product1不会自动更新为最新输入值,添加的是空数据,热重载后build重新执行才会读取当前输入值,造成“热重载才显示”的假象。
修正后的主页面代码
class MyHomePage extends ConsumerWidget { const MyHomePage({super.key, required this.title}); final String title; @override Widget build(BuildContext context, WidgetRef ref) { var size = MediaQuery.of(context).size; final productInfoProvider = ref.watch(productProvider); // 将控制器和复选框状态移至build内部,跟随Widget生命周期管理 final boy = TextEditingController(); final adet = TextEditingController(); final kilo = TextEditingController(); final firma = TextEditingController(); final kalit = TextEditingController(); final kalinlik = TextEditingController(); final en = TextEditingController(); bool? isChecked = false; return Scaffold( appBar: AppBar( title: Text(title), ), body: Column( children: [ SizedBox( height: size.height * 0.5, child: ListView.separated( separatorBuilder: (context, index) { return const Divider( height: 5, thickness: 2, ); }, itemCount: productInfoProvider.products.length, itemBuilder: (BuildContext context, int index) { return Container( color: Colors.white, child: ListTile( leading: Text(productInfoProvider .products[index].firmAdi .toString()), subtitle: Text( productInfoProvider.products[index].en.toString()), title: Text( "${productInfoProvider.products[index].kalite.toString()} kalite"), ), ); }, )), Center( child: SizedBox( height: size.height * 0.2, child: ElevatedButton( style: ElevatedButton.styleFrom( backgroundColor: Colors.white, foregroundColor: Colors.black), onPressed: () { print(productInfoProvider.products.length); showModalBottomSheet( context: context, builder: ((context) { return StatefulBuilder( builder: (BuildContext context, StateSetter myState) { return SizedBox( height: size.height * 0.5, child: Padding( padding: const EdgeInsets.all(10.0), child: Column( children: [ Row( mainAxisAlignment: MainAxisAlignment.spaceBetween, children: [ // 传递控制器和状态更新回调 solBottomSheet(size, firma, kalit, kalinlik, en), sagBottomSheet(size, boy, adet, kilo, (value) { myState(() { isChecked = value; }); }, isChecked), ], ), Align( alignment: Alignment.bottomCenter, child: ElevatedButton( onPressed: (() { // 点击提交时才创建ProductInfos实例,获取最新输入值 final newProduct = ProductInfos( firmAdi: firma.text, kalite: kalit.text, kalinlik: kalinlik.text, en: en.text, boy: boy.text, adet: adet.text, kilo: kilo.text, pvc: isChecked, ); ref.read(productProvider).addProduct(newProduct); Navigator.pop(context); // 关闭BottomSheet,提升体验 }), style: ElevatedButton.styleFrom( backgroundColor: const Color(0xff74a6cc)), child: const Text("ekle"), )), ], ), ), ); }); })); }, child: const Text("BottomSheet"), )), ), ], )); } SizedBox sagBottomSheet(Size size, TextEditingController boy, TextEditingController adet, TextEditingController kilo, Function(bool?) onCheckboxChanged, bool? isChecked) { return SizedBox( width: size.width * 0.4, child: Column( children: [ CustomTextField( textController: boy, t: TextInputType.number, hintText: "Boy", ic: const Icon(Icons.numbers_outlined), dgonly: [FilteringTextInputFormatter.digitsOnly], ), space(), CustomTextField( textController: adet, t: TextInputType.number, hintText: "Adet", ic: const Icon(Icons.numbers_outlined), dgonly: [FilteringTextInputFormatter.digitsOnly], ), space(), CustomTextField( textController: kilo, t: TextInputType.number, hintText: "Kilo", ic: const Icon(Icons.numbers_outlined), dgonly: [FilteringTextInputFormatter.digitsOnly], ), space(), CheckboxListTile( title: const Text( "PVC", style: TextStyle( color: Color(0xff74a6cc), fontWeight: FontWeight.w800), ), value: isChecked, onChanged: onCheckboxChanged), ], )); } SizedBox solBottomSheet(Size size, TextEditingController firma, TextEditingController kalit, TextEditingController kalinlik, TextEditingController en) { return SizedBox( width: size.width * 0.4, child: Column( children: [ CustomTextField( textController: firma, t: TextInputType.name, hintText: "Firma", ic: const Icon(Icons.home), ), space(), CustomTextField( textController: kalit, t: TextInputType.number, hintText: "Kalite", ic: const Icon(Icons.high_quality_outlined), dgonly: [FilteringTextInputFormatter.digitsOnly], ), space(), CustomTextField( textController: kalinlik, t: TextInputType.number, hintText: "Kalınlık", ic: const Icon(Icons.high_quality_outlined), dgonly: [FilteringTextInputFormatter.digitsOnly], ), space(), CustomTextField( textController: en, t: TextInputType.number, hintText: "En", ic: const Icon(Icons.numbers_outlined), dgonly: [FilteringTextInputFormatter.digitsOnly], ), ], ), ); } SizedBox space() { return const SizedBox( height: 10, ); } }
核心修正点
- 将TextEditingController和
isChecked移至build方法内部,跟随Widget生命周期管理,避免全局变量的状态混乱。 - 点击"ekle"按钮时才创建
ProductInfos实例,确保获取的是用户输入后的最新值。 - 提交数据后关闭BottomSheet,优化用户交互体验。
- 将控制器和复选框状态回调作为参数传递给BottomSheet的子组件,保证状态更新同步。
内容的提问来源于stack exchange,提问作者Tolunay Kutlu
相关产品推荐
相关产品推荐

