You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

AutomaticKeepAliveClientMixin与CupertinoSwitch搭配时状态无法保持问题

问题描述

我开发了一个通过ListView构建包含CupertinoSwitch组件的页面,已为NewsPageRegistr、CheckWidget及PageView主组件添加AutomaticKeepAliveClientMixin并设置wantKeepAlive为true,但在PageView中切换页面时,NewsPageRegistr的状态仍会被销毁。

相关代码

NewsPageRegistr 代码

class NewsPageRegistr extends StatefulWidget {
  NewsPageRegistr({Key key, this.model}) : super(key: key);
  RegistrationModel model;
  @override
  _NewsPageRegistrState createState() => _NewsPageRegistrState();
}

class _NewsPageRegistrState extends State<NewsPageRegistr>
    with AutomaticKeepAliveClientMixin<NewsPageRegistr> {
  @override
  bool get wantKeepAlive => true;

  var responseV;
  List<DigitalNews> digitalNews = [];
  @override
  void dispose() {
    
    super.dispose();
  }

  @override
  void initState() {
    fitchData();
    super.initState();
  }


  fitchData() async {
    responseV = await apiRegistrationDataList(DropMenuMode.news);
    for (var values in responseV) {
      digitalNews.add(DigitalNews.fromJson(values));
    }
  }



  @override
  Widget build(BuildContext context) {
    super.build(context);
    return SingleChildScrollView(
      child: SafeArea(
        minimum: const EdgeInsets.fromLTRB(25, 0, 25, 0),
        child: Column(
          mainAxisSize: MainAxisSize.max,
          children: <Widget>[
            SizedBox(
              width: 400,
              height: 85,
              child: Text(
                "Ich möchte folgende Newsletter abonnieren",
                textAlign: TextAlign.left,
                style: GeneralStyles.titleStyle,
              ),
            ),
            ListView.builder(
                //padding: const EdgeInsets.symmetric(vertical: 8),
                physics: const NeverScrollableScrollPhysics(),
                shrinkWrap: true,
                itemCount: digitalNews.length,
                itemBuilder: (context, index) {
                  return CheckWidget(
                    title: digitalNews[index].displayName,
                    subtitle: digitalNews[index].displayDescription,
                    registrationName: digitalNews[index].name,
                    index: index,
                    model: widget.model,
                  );
                }),
            Padding(
              padding: const EdgeInsets.symmetric(vertical: 8.0),
              child: Row(
                mainAxisAlignment: MainAxisAlignment.end,
                children: [
                  Container(
                    child: Row(
                      mainAxisAlignment: MainAxisAlignment.end,
                      children: [
                        OutlinedButton(
                            style: OutlinedButton.styleFrom(
                              backgroundColor: GeneralStyles.petrolColor,
                            ),
                            onPressed: () {
                              setState(() {});
                              widget.model.setCurrentPage(2);

                            },
                            child: const Text(
                              "Weiter",
                              style: TextStyle(
                                  color: Colors.white,
                                  fontWeight: FontWeight.w300,
                                  fontFamily: 'FiraSans'),
                            )),
                      ],
                    ),
                  ),
                ],
              ),
            ),
          ],
        ),
      ),
    );
  }

}

CheckWidget 代码

class CheckWidget extends StatefulWidget {

  RegistrationModel model;
  bool checker = false;
  final Function(String) onChanged;
  final String title;
  final String subtitle;
  final String registrationName;
  final int index;

  CheckWidget({Key key, this.onChanged, this.title, this.subtitle, this.registrationName, this.index,this.model}) : super(key: key);

  @override
  State<CheckWidget> createState() => _CheckWidgetState();
}

class _CheckWidgetState extends State<CheckWidget> {

  @override
  Widget build(BuildContext context) {

    return  Container(
      child: Row(
          mainAxisAlignment: MainAxisAlignment.start,
          children: <Widget>[
            Transform.scale(
              alignment: Alignment.centerLeft,
              scale: 0.8,
              child: CupertinoSwitch(
                value: widget.checker,
                activeColor: GeneralStyles.petrolColor,
                onChanged: (bool value) {
                  setState(() {
                    widget.checker = value;
                    if(value == true) {
                    widget.model.newsPapers[widget.index] = widget.registrationName;
                    }else {
                      widget.model.newsPapers[widget.index] = '';
                    }
                  });
                },
              ),
            ),
            Expanded(
              child: GestureDetector(
                child: Column(
                  mainAxisAlignment: MainAxisAlignment.start,
                  crossAxisAlignment: CrossAxisAlignment.start,
                  children: [
                    Text(widget.title.replaceFirst("Newsletter", "").trim(), style:TextStyle(
                      color: GeneralStyles.blackColor,
                      fontSize: 15,
                      fontFamily: 'FiraSans',
                      fontWeight: FontWeight.w400,
                    )),
                    Text(widget.subtitle, style:TextStyle(
                      color: GeneralStyles.blackColor,
                      fontSize: 13,
                      fontFamily: 'FiraSans',
                      fontWeight: FontWeight.w300,
                    )),
                  ],
                ),
                onTap: (){
                  setState(() {

                    if(widget.checker == false) {
                      widget.checker = true;
                     widget.model.newsPapers[widget.index] = widget.registrationName;
                    }else {
                      widget.checker = false;
                      widget.model.newsPapers[widget.index] = '';
                    }
                  });
                },
              ),
            ),
          ]
      ),
    );
  }

}

解决方案

1. 修正CheckWidget的状态存储逻辑

StatefulWidget的成员变量属于Widget实例,是不可变的,直接修改会导致重建时状态丢失。需要将checker移到_CheckWidgetState中存储,并从RegistrationModel同步初始状态:

class CheckWidget extends StatefulWidget {
  final RegistrationModel model;
  final Function(String) onChanged;
  final String title;
  final String subtitle;
  final String registrationName;
  final int index;

  CheckWidget({Key key, this.onChanged, this.title, this.subtitle, this.registrationName, this.index,this.model}) : super(key: key);

  @override
  State<CheckWidget> createState() => _CheckWidgetState();
}

class _CheckWidgetState extends State<CheckWidget> with AutomaticKeepAliveClientMixin<CheckWidget> {
  bool checker = false;

  @override
  bool get wantKeepAlive => true;

  @override
  void initState() {
    super.initState();
    // 从model中同步初始选中状态
    checker = widget.model.newsPapers[widget.index] != '';
  }

  @override
  Widget build(BuildContext context) {
    super.build(context);
    return Container(
      child: Row(
          mainAxisAlignment: MainAxisAlignment.start,
          children: <Widget>[
            Transform.scale(
              alignment: Alignment.centerLeft,
              scale: 0.8,
              child: CupertinoSwitch(
                value: checker,
                activeColor: GeneralStyles.petrolColor,
                onChanged: (bool value) {
                  setState(() {
                    checker = value;
                    widget.model.newsPapers[widget.index] = value ? widget.registrationName : '';
                  });
                },
              ),
            ),
            Expanded(
              child: GestureDetector(
                child: Column(
                  mainAxisAlignment: MainAxisAlignment.start,
                  crossAxisAlignment: CrossAxisAlignment.start,
                  children: [
                    Text(widget.title.replaceFirst("Newsletter", "").trim(), style:TextStyle(
                      color: GeneralStyles.blackColor,
                      fontSize: 15,
                      fontFamily: 'FiraSans',
                      fontWeight: FontWeight.w400,
                    )),
                    Text(widget.subtitle, style:TextStyle(
                      color: GeneralStyles.blackColor,
                      fontSize: 13,
                      fontFamily: 'FiraSans',
                      fontWeight: FontWeight.w300,
                    )),
                  ],
                ),
                onTap: (){
                  setState(() {
                    checker = !checker;
                    widget.model.newsPapers[widget.index] = checker ? widget.registrationName : '';
                  });
                },
              ),
            ),
          ]
      ),
    );
  }
}

2. 扩大PageView的缓存范围

PageView默认仅缓存当前页面前后各1个页面,若切换跨度超过此范围,需设置cacheExtent参数扩大缓存:

PageView(
  cacheExtent: 3, // 根据实际页面数量调整,比如缓存3个页面
  children: [/* 你的页面列表 */],
  // 其他配置
)

3. 修复NewsPageRegistr的数据加载更新

在fitchData中获取数据后需调用setState更新UI,确保数据变化能触发ListView重建:

fitchData() async {
  responseV = await apiRegistrationDataList(DropMenuMode.news);
  setState(() {
    digitalNews.clear();
    for (var values in responseV) {
      digitalNews.add(DigitalNews.fromJson(values));
    }
  });
}

问题核心原因

  • 错误地将状态存储在StatefulWidget实例而非State类中,导致Widget重建时状态丢失;
  • PageView默认缓存范围有限,切换页面超出范围时会销毁页面状态;
  • 数据加载后未调用setState更新UI,页面重建时无法恢复数据。

内容的提问来源于stack exchange,提问作者Kouki

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.07 12:01:08