Flutter如何防止SliverList子组件滚动后重建丢失状态
点击勾选复选框后,将其滚动出可视区域再滚动回可视区域时,复选框会恢复为未勾选状态。出现该问题的原因是组件发生重建,导致_personalData变量被重置为false。原配置addAutomaticKeepAlives: true未达到阻止组件重建、保留复选框状态的预期,需要实现防止CheckboxWidget组件重建、保留状态的效果。
import 'package:flutter/material.dart'; void main() { runApp(const MyApp()); } class MyApp extends StatefulWidget { const MyApp({super.key}); @override State<MyApp> createState() => _MyAppState(); } class _MyAppState extends State<MyApp> { @override Widget build(BuildContext context) { return const MaterialApp( title: 'Welcome to Flutter', home: Scaffold( body: CustomScrollView( slivers: [Example()], ), )); } } class Example extends StatefulWidget { const Example({Key? key}) : super(key: key); @override State<Example> createState() => ExampleState(); } class ExampleState extends State<Example> with AutomaticKeepAliveClientMixin { @override Widget build(BuildContext context) { super.build(context); return SliverList( delegate: SliverChildListDelegate(const <Widget>[ SizedBox( height: 1400, ), CheckboxWidget() ], addAutomaticKeepAlives: true)); } @override bool get wantKeepAlive => true; } class CheckboxWidget extends StatefulWidget { const CheckboxWidget({Key? key}) : super(key: key); @override State<CheckboxWidget> createState() => _CheckboxWidgetState(); } class _CheckboxWidgetState extends State<CheckboxWidget> { late bool _personalData = false; @override Widget build(BuildContext context) { return Checkbox( checkColor: Colors.black, onChanged: (bool? value) { setState(() { _personalData = value!; }); }, value: _personalData, ); } }
SliverList这类可滚动组件默认会回收滚出可视区域的子组件以节省性能,addAutomaticKeepAlives: true只是开启了列表对子组件保活的支持,并不会主动给所有子组件做保活。当前代码仅给父组件Example混入了AutomaticKeepAliveClientMixin,保活范围仅覆盖Example本身,实际持有复选框状态的CheckboxWidget没有做保活配置,滚出可视区后会被直接销毁,内部存储的_personalData状态自然会被重置。
有两种可落地的方案,根据业务场景选择即可:
方案一:给目标子组件添加保活逻辑
直接给CheckboxWidget的State类混入AutomaticKeepAliveClientMixin,开启自身保活,修改后的代码如下:class _CheckboxWidgetState extends State<CheckboxWidget> with AutomaticKeepAliveClientMixin { late bool _personalData = false; @override Widget build(BuildContext context) { // 必须调用super.build触发保活逻辑 super.build(context); return Checkbox( checkColor: Colors.black, onChanged: (bool? value) { setState(() { _personalData = value!; }); }, value: _personalData, ); } // 开启保活标记 @override bool get wantKeepAlive => true; }这种方案适合组件状态仅自身使用、不需要和外部组件共享的场景,改动量最小。
方案二:状态上移到父组件
把复选框的状态存储到不会被销毁的父组件中,CheckboxWidget仅负责接收状态、触发变更回调,哪怕子组件被回收重建,状态始终存在父组件不会丢失,修改核心逻辑如下:// 父组件Example持有状态 class ExampleState extends State<Example> with AutomaticKeepAliveClientMixin { bool _personalData = false; @override Widget build(BuildContext context) { super.build(context); return SliverList( delegate: SliverChildListDelegate(<Widget>[ const SizedBox( height: 1400, ), CheckboxWidget( value: _personalData, onChanged: (val) { setState(() { _personalData = val; }); }, ) ], addAutomaticKeepAlives: true)); } @override bool get wantKeepAlive => true; } // CheckboxWidget不持有内部状态,接收外部传入的参数 class CheckboxWidget extends StatefulWidget { final bool value; final ValueChanged<bool> onChanged; const CheckboxWidget({Key? key, required this.value, required this.onChanged}) : super(key: key); @override State<CheckboxWidget> createState() => _CheckboxWidgetState(); } class _CheckboxWidgetState extends State<CheckboxWidget> { @override Widget build(BuildContext context) { return Checkbox( checkColor: Colors.black, onChanged: (bool? value) { widget.onChanged(value!); }, value: widget.value, ); } }这种方案是Flutter官方推荐的状态管理思路,适合复选框状态需要和其他组件共享、后续有业务扩展的场景,可维护性更强。
内容的提问来源于stack exchange,提问作者flutter

