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

Flutter:FutureBuilder内setState不生效,无法按年份筛选文档

问题分析与解决方案

问题根源

  1. 选中状态无法持久化:selectedYear列表定义在build方法内部,每次调用build(包括setState触发后)都会重新初始化,导致用户选择的年份状态被重置。
  2. 无持久化的文档数据源:仅在FutureBuilder的builder回调中临时生成文档列表,未将原始数据保存到状态变量,无法在筛选时复用。
  3. 缺失年份筛选逻辑:ToggleButton点击后仅更新选中状态,未根据选中年份对文档列表进行过滤,页面内容自然不会变化。

修复方案

步骤1:调整状态变量至类级别

将选中状态、原始文档列表、过滤后列表定义为State类的成员变量:

class _ListOfDocumentsState extends State<ListOfDocuments> {
  final HttpService httpService = HttpService();
  Future<List<Document>>? listadoDocumentos;
  String myLocale = Intl.getCurrentLocale();

  // 类级别状态变量
  final List<String> yearValues = ['all', '2005', '2006', '2007', '2008', '2009', '2010', '2011', '2012', '2013'];
  List<bool> selectedYear = <bool>[true, false, false, false, false, false, false, false, false, false];
  List<Document>? _allDocuments;
  List<Document>? _filteredDocuments;

  void cargarDocumentos() async {
    listadoDocumentos = httpService.getDocList(
      myLocale.toString(),
      widget.sectionRowName,
      widget.singleItem,
    );
  }

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

步骤2:在FutureBuilder中保存原始数据

首次获取到文档数据后,将其存入状态变量并初始化过滤列表:

@override
Widget build(BuildContext context) {
  double screenWidth = MediaQuery.of(context).size.width;
  double screenHeight = MediaQuery.of(context).size.height;

  List<Widget> years = <Widget>[
    Padding(
      padding: const EdgeInsets.all(4.0),
      child: Text(S.of(context).all),
    ),
    ...yearValues.sublist(1).map((year) => Text(year)).toList(),
  ];

  return Scaffold(
    backgroundColor: kPrimaryColor,
    appBar: AppBar(
      backgroundColor: Colors.transparent,
      elevation: 0.0,
      centerTitle: true,
      title: getTitle(widget.sectionRowName, widget.singleItem.name, context),
    ),
    body: FutureBuilder(
      future: listadoDocumentos,
      builder: (BuildContext context, AsyncSnapshot snapshotFromList) {
        if (snapshotFromList.hasData) {
          // 仅首次加载时保存原始数据
          if (_allDocuments == null) {
            _allDocuments = List.generate(
              snapshotFromList.data.length,
              (index) => Document(
                id: snapshotFromList.data[index].id,
                fecha: snapshotFromList.data[index].fecha,
                titulo: snapshotFromList.data[index].titulo,
              ),
            );
            _filteredDocuments = List.from(_allDocuments!);
          }

          return Column(
            children: [
              SingleChildScrollView(
                scrollDirection: Axis.horizontal,
                child: Padding(
                  padding: const EdgeInsets.fromLTRB(10.0, 0.0, 0.0, 0.0),
                  child: ToggleButtons(
                    direction: Axis.horizontal,
                    textStyle: kAppBarStyle.copyWith(fontSize: 18.0),
                    borderColor: Colors.yellow,
                    borderWidth: 1.0,
                    selectedBorderColor: Colors.yellowAccent,
                    fillColor: Colors.yellowAccent.withOpacity(0.2),
                    color: Colors.white,
                    selectedColor: Colors.white,
                    isSelected: selectedYear,
                    borderRadius: const BorderRadius.all(Radius.circular(10.0)),
                    onPressed: (int index) {
                      setState(() {
                        // 更新选中状态
                        for (int i = 0; i < selectedYear.length; i++) {
                          selectedYear[i] = i == index;
                        }

                        // 执行筛选逻辑
                        if (index == 0) {
                          _filteredDocuments = List.from(_allDocuments!);
                        } else {
                          String targetYear = yearValues[index];
                          _filteredDocuments = _allDocuments!.where((doc) {
                            DateTime dateFormated = DateFormat('dd/MM/yyyy').parse(doc.fecha);
                            return DateFormat("yyyy").format(dateFormated) == targetYear;
                          }).toList();
                        }
                      });
                    },
                    children: years,
                  ),
                ),
              ),
              Expanded(
                child: Padding(
                  padding: EdgeInsets.fromLTRB(0.0, 0.0, screenWidth * 0.05, 0.0),
                  child: SingleChildScrollView(
                    scrollDirection: Axis.vertical,
                    child: ListView.separated(
                      itemCount: _filteredDocuments?.length ?? 0,
                      shrinkWrap: true,
                      physics: const NeverScrollableScrollPhysics(),
                      separatorBuilder: (BuildContext context, int index) {
                        return const Divider(
                          thickness: 2.0,
                          color: Colors.white10,
                          indent: 15.0,
                        );
                      },
                      itemBuilder: (context, index) {
                        final doc = _filteredDocuments![index];
                        DateTime dateFormated = DateFormat('dd/MM/yyyy').parse(doc.fecha);
                        return ListTile(
                          leading: Column(
                            crossAxisAlignment: CrossAxisAlignment.center,
                            mainAxisAlignment: MainAxisAlignment.center,
                            mainAxisSize: MainAxisSize.max,
                            children: [
                              Text(
                                DateFormat("dd").format(dateFormated),
                                style: kDocTitleInList.copyWith(
                                  color: Colors.white70,
                                ),
                              ),
                              Text(
                                DateFormat("MMM")
                                    .format(dateFormated)
                                    .toUpperCase(),
                                style: kDocTitleInList.copyWith(
                                  fontSize: 10.0,
                                  color: Colors.white70,
                                ),
                              ),
                              Text(
                                DateFormat("yyyy").format(dateFormated),
                                style: kDocTitleInList.copyWith(
                                  fontSize: 14.0,
                                  color: Colors.white70,
                                ),
                              ),
                            ],
                          ),
                          title: Text(
                            doc.titulo,
                            style: kDocTitleInList,
                          ),
                          trailing: const Icon(
                            Icons.arrow_forward_ios_rounded,
                            color: Colors.white,
                          ),
                          onTap: () {
                            showDialog(
                              context: context,
                              builder: (BuildContext context) {
                                return FutureBuilder(
                                    future: httpService.getDocument(
                                        snapshotFromList.data[index].id
                                            .toString()),
                                    builder: (BuildContext context,
                                        AsyncSnapshot snapshotFromDocument) {
                                      if (snapshotFromDocument.hasData) {
                                        return Dialog(
                                          backgroundColor: Colors.transparent,
                                          elevation: 0.0,
                                          child: Container(
                                            decoration: BoxDecoration(
                                              color: Colors.white,
                                              borderRadius:
                                                  BorderRadius.circular(30.0),
                                            ),
                                            child: Column(
                                              mainAxisAlignment:
                                                  MainAxisAlignment.center,
                                              mainAxisSize: MainAxisSize.min,
                                              children: <Widget>[
                                                Padding(
                                                  padding:
                                                      const EdgeInsets.all(
                                                          15.0),
                                                  child: Text(
                                                    snapshotFromList
                                                        .data[index].titulo,
                                                    style: kSubTitleStyle
                                                        .copyWith(
                                                            color:
                                                                Colors.black),
                                                  ),
                                                ),
                                                Padding(
                                                  padding:
                                                      const EdgeInsets.all(
                                                          15.0),
                                                  child: FlagChips(
                                                      documento:
                                                          snapshotFromDocument
                                                              .data),
                                                ),
                                                GestureDetector(
                                                  child: Text(
                                                    '- ${S.of(context).cerrarVentana} -',
                                                    style: kSubTitleStyle
                                                        .copyWith(
                                                            color:
                                                                Colors.black),
                                                  ),
                                                  onTap: () {
                                                    Navigator.of(context)
                                                        .pop();
                                                  },
                                                ),
                                                const SizedBox(
                                                  height: 20.0,
                                                ),
                                              ],
                                            ),
                                          ),
                                        );
                                      } else {
                                        return const Center(
                                          child: CircularProgressIndicator(
                                            color: Colors.white54,
                                          ),
                                        );
                                      }
                                    });
                              },
                            );
                          },
                        );
                      },
                    ),
                  ),
                ),
              ),
            ],
          );
        } else {
          return const Center(
            child: CircularProgressIndicator(
              color: Colors.white54,
            ),
          );
        }
      },
    ),
  );
}

关键修改说明

  • 将selectedYear移至类级别,确保setState后状态能持久化;
  • 新增_allDocuments保存原始数据,避免重复生成;
  • 添加_filteredDocuments存储筛选结果,作为ListView的数据源;
  • 在ToggleButton的onPressed回调中实现年份筛选逻辑,通过setState触发页面更新。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.05 00:00:56