【问题标题】:How to update widget state built inside FutureBuilder如何更新内置于 FutureBuilder 中的小部件状态
【发布时间】:2020-11-16 08:05:28
【问题描述】:

之前,我使用ListBuilder 生成一个包含 70 个数字的列表,它可以工作,但是将 70 个数字生成到自定义小部件中需要很长时间,而且当我点击一个数字只是为了更改背景颜色状态时,它在改变状态之前花了几毫秒。

现在我使用FutureBuilder 能够在等待生成的 70 个整数时加载屏幕。但是当我点击球号时,背景颜色没有更新......就像setState()在Future ListBuilder中不起作用。

这个问题:“Flutter - How to update state (or value?) of a Future/List used to build ListView (via FutureBuilder)”很相似,但是并没有解决我的问题。

这是我在构建方法中的代码

Flexible(
                child:FutureBuilder<List<Widget>>(
                  future: ballNumbers,
                  builder: (context, snapshot){
                    if(snapshot.connectionState != ConnectionState.done){
                      return Center(child: CircularProgressIndicator());
                    }
                    if(snapshot.hasError){
                      return Center(child: Text("An error has occured"));
                    }
                    List<Widget> balls = snapshot.data ?? [];
                    return GridView.count(
                      crossAxisCount: 9,
                      children: balls,
                    );
                  }
                )

这是我如何启动函数的状态:

Future<List<Widget>> ballNumbers;
List<int> picks = []; 

@override
void initState() {
    ballNumbers = getBallNumbers();
});

  Future<List<Widget>> getBallNumbers() async {
    return List.generate(limitBallNumber,(number){
      number = number + 1;
      return Padding(
        padding:EdgeInsets.all(2.5),
        child:Ball(
          number : number,
          size: ballWidth,
          textColor:(picks.contains(number)) ? Colors.black : Colors.white,
          ballColor: (picks.contains(number)) ? Style.selectedBallColor : Style.ballColor,
          onTap:(){
            setState((){
                picks.contains(number) ? picks.remove(number) : picks.add(number);
            });
          }
        )
      );
    });
  }

更新:这是Ball 小部件的类

class Ball extends StatelessWidget {
  final Color ballColor;
  final Color textColor;
  final double size;
  final double fontSize;
  final int number;
  final VoidCallback onTap;

  Ball({Key key, @required this.number, 
    this.textColor, 
    this.ballColor,
    this.onTap,
    this.size = 55.0,
    this.fontSize = 14,
  }) : super(key : key);

  @override
  Widget build(BuildContext context) {
    return Container(
      height: size,
      width: size,
      decoration: BoxDecoration(
        shape: BoxShape.circle,
        gradient: LinearGradient(
          colors: [
            Style.secondaryColor,
            ballColor != null ? ballColor : Style.ballColor,
          ],
          begin: Alignment.bottomLeft,
          end: Alignment.topRight
        )
      ),
      child: FlatButton(
        padding: EdgeInsets.all(0),
        child: Container(
          child: Text(
            number.toString().length > 1 ? number.toString() : "0" + number.toString(),
            style: TextStyle(
              fontSize: fontSize,
              color: textColor != null ? textColor : Colors.white
            ),
          ),
          padding: const EdgeInsets.all(4.0),
          decoration:BoxDecoration(
            color: Colors.transparent,
            border: Border.all(color: textColor != null ? textColor : Colors.white,  width: 1),
            borderRadius: BorderRadius.circular(32),
          )
        ),
        color: Colors.transparent,
        onPressed: onTap,
      ),
    );
  }
}

【问题讨论】:

    标签: flutter flutter-widget flutter-futurebuilder


    【解决方案1】:

    问题是getBallNumbersinitState 中只被调用一次,所以当picks 更新时,这并不重要,因为不会再次调用getBallNumbers 来更新传递给Ball 小部件。

    一个简单的解决方法是用future: getBallNumbers()build 中调用getBallNumbers,但这会导致CircularProgressIndicatorList 重新生成时在每次点击时显示。

    但是,理想情况下,您应该处理每个Ball 状态内的所有颜色变化,这样您就不必在每次点击时都重新构建List。并且要在父窗口小部件的State 中维护选定数字的List,您应该向每个球传递一个回调,以便从父窗口的List 中添加和删除它们的数字。


    粗略示例:

    Ball 类(修改为有状态并删除了不必要的参数;活动状态现在存储在 ball 中,而不是仅存储在父级中):

    class Ball extends StatefulWidget {
      final double size;
      final double fontSize;
      final int number;
      final VoidCallback toggleBall;
      final bool initialActiveState;
    
      Ball({Key key, @required this.number, 
        this.toggleBall,
        this.size = 55.0,
        this.fontSize = 14,
        this.initialActiveState,
      }) : super(key : key);
    
      _BallState createState() => _BallState();
    }
    
    class _BallState extends State<Ball> {
      bool isActive;
      
      @override
      void initState() {
        super.initState();
        isActive = widget.initialActiveState;
      }
      
      @override
      Widget build(BuildContext context) {
        return Container(
          height: widget.size,
          width: widget.size,
          decoration: BoxDecoration(
            shape: BoxShape.circle,
            gradient: LinearGradient(
              colors: [
                Style.secondaryColor,
                isActive ? Style.selectedBallColor : Style.ballColor,
              ],
              begin: Alignment.bottomLeft,
              end: Alignment.topRight
            )
          ),
          child: FlatButton(
            padding: EdgeInsets.all(0),
            child: Container(
              child: Text(
                widget.number.toString().length > 1 ? widget.number.toString() : "0" + widget.number.toString(),
                style: TextStyle(
                  fontSize: widget.fontSize,
                  color: isActive ? Colors.black : Colors.white,
                ),
              ),
              padding: const EdgeInsets.all(4.0),
              decoration:BoxDecoration(
                color: Colors.transparent,
                border: Border.all(color: isActive ? Colors.black : Colors.white,  width: 1),
                borderRadius: BorderRadius.circular(32),
              )
            ),
            color: Colors.transparent,
            onPressed: () {
              if(!isActive && widget.activeBallList.length >= 7) {
                return;
              }
              setState(() {
                isActive = !isActive;
              });
              widget.activeBallList.contains(widget.number) ? widget.activeBallList.remove(widget.number) : widget.activeBallList.add(widget.number);
            },
          ),
        );
      }
    }
    

    父类(唯一需要修改的是Ball的参数):

    Future<List<Widget>> getBallNumbers() async {
      return List.generate(limitBallNumber,(number){
        number = number + 1;
        return Padding(
          padding:EdgeInsets.all(2.5),
          child: Ball(
            number: number,
            size: ballWidth,
            initialActiveState: picks.contains(number),
            activeBallList: picks,
          )
        );
      });
    }
    

    【讨论】:

    • 你能根据你的解释举一个例子吗?
    • @Lemayzeur 对于“理想”的解决方案?另外你在哪里打电话setState
    • 请。我对 Flutter 有点陌生。但看来你的解决方案会奏效
    • 抱歉!代码中漏掉了,我在onTap函数回调中调用了
    • @Lemayzeur 是的,我可以制作样品。只是让你知道这需要一段时间。你也可以显示Ball 类吗?
    猜你喜欢
    • 2022-01-19
    • 2021-07-25
    • 2021-04-17
    • 2021-07-16
    • 2021-06-11
    • 1970-01-01
    • 2018-07-28
    • 2018-09-01
    • 2020-01-02
    相关资源
    最近更新 更多