Flutter状态异常:全选复选框点击后状态不更新且报空指针
问题排查与解决方案
问题原因
- 空指针错误:
MessageCustomerHeader的listOfCustomers参数未被正确赋值,始终为null,调用forEach时触发空指针异常。 - 状态不同步:两个组件各自维护独立的
listOfCustomers和checkAll状态,修改Header的全选状态后,ListView组件无法感知到列表项选中状态的变化,不会触发重绘。 - 单向状态传递失效:
InactiveCustomersListView的checkAll参数仅在初始化时接收值,后续Header的状态变化无法同步到ListView。
解决方案
1. 统一数据源,传递同一个列表实例
将listOfCustomers的创建移到父组件,同时传递给MessageCustomerHeader和InactiveCustomersListView,确保两个组件操作的是同一个列表对象。
2. 同步全选状态与列表项状态
- 让
checkAll状态依赖列表项的选中状态,而非各自维护。 - 修改列表项状态后,触发对应组件的重绘。
3. 修复空指针问题
确保MessageCustomerHeader接收的listOfCustomers不为null,通过构造函数默认值或父组件初始化时传递非空列表。
修改后的代码示例
父组件(示例)
class CustomerPage extends StatefulWidget { @override _CustomerPageState createState() => _CustomerPageState(); } class _CustomerPageState extends State<CustomerPage> { List<CheckBoxModel> listOfCustomers = [ CheckBoxModel(selected: false), // 可添加更多客户数据 ]; bool checkAll = false; @override Widget build(BuildContext context) { return Column( children: [ MessageCustomerHeader( listOfCustomers: listOfCustomers, checkAll: checkAll, onCheckAllChanged: (value) { setState(() { checkAll = value; listOfCustomers.forEach((element) { element.selected = value; }); }); }, ), Expanded( child: InactiveCustomersListView( listOfCustomers: listOfCustomers, checkAll: checkAll, onItemChecked: () { setState(() { checkAll = listOfCustomers.every((e) => e.selected); }); }, ), ), ], ); } }
修改后的InactiveCustomersListView
class InactiveCustomersListView extends StatefulWidget { final List<CheckBoxModel> listOfCustomers; final bool checkAll; final VoidCallback onItemChecked; const InactiveCustomersListView({ Key key, @required this.listOfCustomers, this.checkAll, this.onItemChecked, }) : super(key: key); @override State<InactiveCustomersListView> createState() => _InactiveCustomersListViewState(); } class _InactiveCustomersListViewState extends State<InactiveCustomersListView> { @override Widget build(BuildContext context) { final controller = Get.find<CustomersController>(); // 用find避免重复创建Controller return ListView.builder( scrollDirection: Axis.vertical, shrinkWrap: true, itemCount: widget.listOfCustomers.length, itemBuilder: (context, index) { return Obx(() { return Container( color: Color(0xffFFFFFF), child: ListTile( contentPadding: controller.isCheboxVisible.isTrue ? EdgeInsets.only(left: 0, right: 22, top: 10, bottom: 10) : EdgeInsets.only(left: 22, right: 22, top: 10, bottom: 10), horizontalTitleGap: 5, leading: controller.isCheboxVisible.isTrue ? Visibility( visible: controller.isCheboxVisible.value, child: Transform.scale( scale: 1.3, child: Checkbox( side: MaterialStateBorderSide.resolveWith( (Set<MaterialState> states) { if (states.contains(MaterialState.selected)) { return const BorderSide( width: 2, color: Color(0xff34495E)); } return const BorderSide( width: 1, color: Color(0xffB0BEC1)); }, ), shape: RoundedRectangleBorder( borderRadius: BorderRadius.circular(5)), activeColor: Color(0xff34495E), value: widget.listOfCustomers[index].selected, onChanged: (value) { setState(() { widget.listOfCustomers[index].selected = value; }); widget.onItemChecked?.call(); }, ), ), ) : null, title: Row( children: [ SizedBox( width: 60, height: 60, child: ClipRRect( borderRadius: BorderRadius.all(Radius.circular(80)), child: CachedNetworkImage( height: 100, width: double.infinity, fit: BoxFit.cover, imageUrl: Get.find<AuthService>().user.value.avatar.thumb, placeholder: (context, url) => Image.asset( 'assets/img/loading.gif', fit: BoxFit.cover, width: double.infinity, height: 80, ), errorWidget: (context, url, error) => Icon(Icons.error_outline), ), ), ), SizedBox(width: 10), Expanded( child: Column( crossAxisAlignment: CrossAxisAlignment.start, children: [ Row( mainAxisAlignment: MainAxisAlignment.spaceBetween, children: [ Text( 'John Cletus', style: GoogleFonts.poppins( fontSize: 14, fontWeight: FontWeight.w500, color: Color(0xff151515)), ), Container( decoration: BoxDecoration( color: Color(0xffECF0F1), borderRadius: BorderRadius.circular(20), ), child: Padding( padding: const EdgeInsets.only( left: 10.0, right: 10.0, top: 5.0, bottom: 5.0), child: Text( 'Inactive', style: GoogleFonts.poppins( fontSize: 10, fontWeight: FontWeight.w400, color: Color(0xff7F8D90)), ), ), ), ], ), SizedBox(height: 10), Row( mainAxisAlignment: MainAxisAlignment.spaceBetween, children: [ Row( children: [ Text( '2', style: GoogleFonts.poppins( fontSize: 14, fontWeight: FontWeight.w500, color: Color(0xff151515)), ), SizedBox(width: 5), Text( 'jobs', style: GoogleFonts.poppins( fontSize: 12, fontWeight: FontWeight.w400, color: Color(0xff151515)), ), ], ), Text( 'Last job 5 days ago', style: GoogleFonts.poppins( fontSize: 9, fontStyle: FontStyle.italic, fontWeight: FontWeight.w400, color: Color(0xff151515)), ), ], ), ], ), ), ], ), ), ); }); }); } } class CheckBoxModel { bool selected; CheckBoxModel({this.selected = false}); // 添加默认值避免null }
修改后的MessageCustomerHeader
class MessageCustomerHeader extends StatefulWidget { final List<CheckBoxModel> listOfCustomers; final bool checkAll; final ValueChanged<bool> onCheckAllChanged; const MessageCustomerHeader({ Key key, @required this.listOfCustomers, this.checkAll, this.onCheckAllChanged, }) : super(key: key); @override State<MessageCustomerHeader> createState() => _MessageCustomerHeaderState(); } class _MessageCustomerHeaderState extends State<MessageCustomerHeader> { @override Widget build(BuildContext context) { return Container( color: Colors.white, child: Padding( padding: const EdgeInsets.all(20.0), child: Column( crossAxisAlignment: CrossAxisAlignment.start, children: [ Text( 'Select customers you would like to message', style: GoogleFonts.poppins( color: Color(0xff596780).withOpacity(0.8), fontSize: 14, fontWeight: FontWeight.w500), ), SizedBox(height: 10), Row( mainAxisAlignment: MainAxisAlignment.start, children: [ Transform.scale( scale: 1.3, child: Checkbox( side: MaterialStateBorderSide.resolveWith( (Set<MaterialState> states) { if (states.contains(MaterialState.selected)) { return const BorderSide( width: 2, color: Color(0xff34495E)); } return const BorderSide( width: 1, color: Color(0xffB0BEC1)); }, ), shape: RoundedRectangleBorder( borderRadius: BorderRadius.circular(5)), activeColor: Color(0xff34495E), materialTapTargetSize: MaterialTapTargetSize.shrinkWrap, visualDensity: VisualDensity(horizontal: -4, vertical: -4), value: widget.checkAll, onChanged: widget.onCheckAllChanged, ), ), SizedBox(width: 10), Text( 'Select all', style: GoogleFonts.poppins( color: Colors.black87, fontSize: 12, fontWeight: FontWeight.w400), ), ], ), ], ), ), ); } }
内容的提问来源于stack exchange,提问作者tesseract
相关产品推荐
相关产品推荐

