【问题标题】:Why is setState not rebuilding my GridView.builder? How do I fix it?为什么 setState 不重建我的 GridView.builder?我如何解决它?
【发布时间】:2022-07-24 04:25:52
【问题描述】:

我正在尝试使用 setState 更新 GridView.builder,当触发更改时,实际状态会在后台更新,但 GridView.builder 在我手动刷新应用程序之前不会更改。

setState 函数触发,我对此进行了测试。代码如下:

import 'package:flutter/material.dart';
import 'image_display_card.dart';

void main() {
  runApp(MaterialApp(
    home: ChooseScreen(),
  ));
}


class ChooseScreen extends StatefulWidget {
  @override
  State<ChooseScreen> createState() => _ChooseScreenState();
}

class _ChooseScreenState extends State<ChooseScreen> {
  DisplayCard? currentSelectedCard;

  // The List of image cards
  List<DisplayCard> baseDisplayList = [];

  // These are dummy images I added
  List<Image> listOfInitialImages = const [
    Image(image: AssetImage('images/testing_stock/stickFigureMale.png')),
    Image(image: AssetImage('images/testing_stock/stickFigureFemale.png')),
    Image(image: AssetImage('images/testing_stock/gown.png')),
    Image(image: AssetImage('images/testing_stock/hat1.png')),
  ];

  @override
  void initState() {
    super.initState();
    baseDisplayList = [
      for (int i = 0; i < listOfInitialImages.length; i++)
        DisplayCard(
          picture: listOfInitialImages[i],
          onCardSelected: () {
            setCardToSelected(i);
          },
        ),
    ];
  }

  /// unselect the previous selected card and
  /// set currentSelectedCard to the new selected card.
  setCardToSelected(int index) {
    if (currentSelectedCard != null) {
      currentSelectedCard!.selectOrUnselect(false);
    }
    print('triggered');
    // set the new selected card.
    currentSelectedCard = baseDisplayList[index];
    currentSelectedCard!.selectOrUnselect(true);
    print('triggered again');
  }

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(
        title: Text('Choose picture'),
      ),
      body: GridView.builder(
        gridDelegate: SliverGridDelegateWithFixedCrossAxisCount(
          crossAxisCount: 2,
        ),
        itemCount: baseDisplayList.length,
        itemBuilder: (BuildContext context, int index) {
          return baseDisplayList[index];
        },
      ),
    );
  }
}

这是display_card.dart

import 'package:flutter/material.dart';

class DisplayCard extends StatelessWidget {
  final Image picture;
  final onCardSelected;
  bool isSelected;

  // TODO: Implement color change on selected picture
  DisplayCard({
    this.isSelected = false,
    this.onCardSelected,
    this.picture = const Image(
        image: AssetImage('images/testing_stock/stickFigureMale.png')),
  });

  selectOrUnselect(bool choice) {
    isSelected = choice;
  }

  @override
  Widget build(BuildContext context) {
    return GestureDetector(
      onTap: onCardSelected,
      child: Container(
        height: 200,
        margin: const EdgeInsets.all(5),
        decoration: BoxDecoration(
          color: Colors.grey[300],
          image: DecorationImage(
            image: picture.image,
            colorFilter: ColorFilter.mode(
                Colors.black.withOpacity(isSelected ? 0.4 : 0.0),
                BlendMode.srcOver
            ),
            fit: BoxFit.cover,
          ),
        ),
      ),
    );
  }
}

我已对其进行了编辑,使其仅包含复制错误所需的代码。之前很抱歉。

baseDisplayList 的状态已更新,但 GridView.builder 未重建。

关于我如何知道 GridView.builder 没有重建,我尝试在 setCardToSelected 函数中更改脚手架的 backgroundColor 并且背景颜色发生了变化,但 GridView 没有改变。但是当我之后立即刷新应用程序时,GridView 更新了。

请问,当setCardToSelected 被调用时,如何更新GridView.builder

谢谢。感谢您的帮助。

【问题讨论】:

  • 可以发一下SelectedCard DisplayCard类的代码吗?
  • 你怎么知道GridBuilder没有重建?
  • 请将更新后的商品重新设置为列表。
  • 你在哪里使用currentSelectedCard
  • 查看更多关于minimal-reproducible-example

标签: flutter dart setstate gridview.builder


【解决方案1】:

我尝试使用提供程序包,但仍然遇到同样的问题。

看来您必须在 GridView.builder 中构建每个单独的项目,以便在有任何更改时重新构建。

这是我找到的问题的解决方案。

这是main.dart


import 'image_display_card.dart';

void main() {
  runApp(MaterialApp(
    home: ChooseScreen(),
  ));
}

class ChooseScreen extends StatefulWidget {
  @override
  State<ChooseScreen> createState() => _ChooseScreenState();
}

class _ChooseScreenState extends State<ChooseScreen> {
  Image? currentlySelectedImage;

  // The List of image cards
  List<Image> baseImageList = [];
  // The list of image card states
  List<bool> baseStateList = [];

  // These are dummy images I added
  List<Image> listOfInitialImages = const [
    Image(image: AssetImage('images/testing_stock/stickFigureMale.png')),
    Image(image: AssetImage('images/testing_stock/stickFigureFemale.png')),
    Image(image: AssetImage('images/testing_stock/gown.png')),
    Image(image: AssetImage('images/testing_stock/hat1.png')),
  ];

  initialize() {
    for (int i = 0; i < listOfInitialImages.length; i++) {
      baseImageList.add(listOfInitialImages[i]);
      baseStateList.add(false);
    }
  }

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

  /// Changes the currentlySelectedImage and the color of
  /// the chosen card by setting all values in the baseStatesList
  /// to false, then assigning only the chosen one to true.
  setToSelectedImage(int index) {
    currentlySelectedImage = baseImageList[index];
    setState(() {
      baseStateList.setAll(
          0, [for (int i = 0; i < baseStateList.length; i++) false]);
      baseStateList[index] = true;
    });
  }

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      body: GridView.builder(
        gridDelegate: SliverGridDelegateWithFixedCrossAxisCount(
          crossAxisCount: 2,
        ),
        itemCount: baseImageList.length,
        itemBuilder: (BuildContext context, int index) {
          return DisplayCard(
            picture: baseImageList[index],
            isSelected: baseStateList[index],
            onCardSelected: () {
              setToSelectedImage(index);
            },
          );
        },
      ),
    );
  }
}

DisplayCard 类保持不变,只是不再需要 selectOrUnselect 方法。

如果gridview 内容的状态是在gridview 中单独定义的,那么它们的状态实际上会使用setState 进行更新。 这种方法也更短。谢谢你们的帮助,伙计们。

【讨论】:

    【解决方案2】:

    在我的例子中,我使用了一个 DataRecord 类型列表,然后使用 setState 将 lstData 分配给我模型中的 DataRecords 列表。 GridView 使用 itembuilder 接收 lstData 作为参数来构建 GridTiles。

    class DataRecord {
      String WordValue;
      String State;
      int Index;
      DataRecord(this.WordValue, this.State, this.Index);
    }
    
    class _Test_SinglePageState extends State<Test_SinglePage> {
      List<DataRecord> lstData = [];
    
    Widget _itemBuilder(BuildContext context, int index,
          ConcentrationMonitor model, List<DataRecord> lstData) {
        model.lstData = lstData;
        return MouseRegion(
            cursor: SystemMouseCursors.click,
            child: GestureDetector(
                onTap: () {
                  if (StaticVar.index1 != 0 && StaticVar.index2 != 0) {
                    _stopWatchTimer.onExecute.add(StopWatchExecute.reset);
                    _stopWatchTimer.onExecute.add(StopWatchExecute.start);
                  }
                  model.checkForMatch(index);
                  setState(() {
                    lstData = model.lstData;
                  });
                }
                ,
                child: Container(
                    color: Colors.grey,
                    child: GridTile(
                        child: Visibility(
                            visible: model.lstData[index].State == "Show" ||
                                    model.lstData[index].State == "TempShow"
                                ? true
                                : false,
                            child: Center(
                                child: Column(children: [
                              Text(
                                model.lstData[index].WordValue,
                                style: TextStyle(
                                    fontSize: 20,
                                    color: model.lstData[index].State == "Show"
                                        ? Colors.purple
                                        : Colors.black),
                              ),
                              Text(model.lstData[index].Index.toString(),
                                  style: Theme.of(context).textTheme.headline5)
                              //Text(model.lstData[index].State,
                              //style: Theme.of(context).textTheme.headline5)
                            ])))))));
      }
    
    Expanded(
                        child: SizedBox(
                            height: 400,
                            width: MediaQuery.of(context).size.width,
                            child: GridView.builder(
                                gridDelegate:
                                    const SliverGridDelegateWithMaxCrossAxisExtent(
                                        maxCrossAxisExtent: 120,
                                        childAspectRatio: 3 / 2,
                                        crossAxisSpacing: 20,
                                        mainAxisSpacing: 20),
                                itemCount: lstData.length,
                                itemBuilder: (ctxt, Index) =>
                                    _itemBuilder(ctxt, Index, model, lstData))))
    

    【讨论】:

      猜你喜欢
      • 2016-08-06
      • 2017-05-26
      • 1970-01-01
      • 2016-01-03
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2020-09-14
      • 1970-01-01
      相关资源
      最近更新 更多