【问题标题】:Flutter nested widgets setState does not work as intendedFlutter 嵌套小部件 setState 无法按预期工作
【发布时间】:2021-11-17 13:35:43
【问题描述】:

我有以下结构:

  • 主块。主状态小部件。包含两个小部件 BlockABlockB
  • 块A。包含一个文本和一个按钮。
  • 块 B。包含另一个小部件 BlockBCard,它将被使用多次(在本例中为两次)。

什么按预期工作?当我点击 BlockA 中的按钮时,BlockA 中的文本字段的内容根据需要更换 BlockBCard

现在是我的问题:

块B中。为了使用setState,我将BlockB 更改为StatefulWidget。 单击 BlockBCard 中的按钮,根据需要更改两个 BlockBCard 中的文本字段。 但 BlockA 中文本字段的内容并没有改变。

如何实现以下功能: 点击其中一个BlockBCard中的按钮,BlockA中的文本字段和BlockBCard中的两个文本字段都改变了吗?

单击 BlockBCard 之一中的按钮,BlockA 中的文本字段发生变化,BlockBCard 中的文本字段发生变化,但第二个 BlockBCard 中的文本字段没有改变。

示例代码:

import 'package:flutter/material.dart';

void main() => runApp(MyApp());
int testCounter = 0;

class MyApp extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      home: MainBlock(),
    );
  }
}
// ------------------------------------ Stateless Widget <<<

class MainBlock extends StatefulWidget {
  @override
  _MainBlockState createState() => _MainBlockState();
}

class _MainBlockState extends State<MainBlock> {
  @override
  Widget build(BuildContext context) {
    return Scaffold(
      body: Container(
        margin: EdgeInsets.all(30.0),
        color: Color(0xFF122C39),
        child: Column(
          crossAxisAlignment: CrossAxisAlignment.stretch,
          children: [
            Expanded(
              child: BlockA(
                counter: testCounter,
                button: () {
                  setState(() {
                    testCounter++;
                  });
                },
              ),
            ),
            Expanded(
              child: BlockB(),
            ),
          ],
        ),
      ),
    );
  }
}

class BlockA extends StatelessWidget {
  final int counter;
  final Function button;

  BlockA({this.counter, this.button});

  @override
  Widget build(BuildContext context) {
    return Container(
      margin: EdgeInsets.all(10.0),
      color: Color(0xFF265672),
      child: Column(
        mainAxisAlignment: MainAxisAlignment.spaceEvenly,
        children: [
          Center(
            child: Text(
              counter.toString(),
              style: TextStyle(
                color: Color(0xFFFFFFFF),
                fontSize: 22.0,
              ),
            ),
          ),
          Center(
            child: GestureDetector(
              onTap: button,
              child: Text(
                'Button',
                style: TextStyle(
                  color: Color(0xFFFFFFFF),
                  fontSize: 22.0,
                ),
              ),
            ),
          ),
        ],
      ),
    );
  }
}

class BlockB extends StatefulWidget {
  @override
  _BlockBState createState() => _BlockBState();
}

class _BlockBState extends State<BlockB> {
  @override
  Widget build(BuildContext context) {
    return Container(
      margin: EdgeInsets.all(10.0),
      color: Color(0xFF265672),
      child: Column(
        mainAxisAlignment: MainAxisAlignment.center,
        children: [
          Expanded(
            child: BlockBCard(
              counter: testCounter,
              button: () {
                setState(() {
                  testCounter++;
                });
              },
            ),
          ),
          Expanded(
            child: BlockBCard(
              counter: testCounter,
            ),
          ),
        ],
      ),
    );
  }
}

class BlockBCard extends StatelessWidget {
  final int counter;
  final Function button;

  BlockBCard({this.counter, this.button});

  @override
  Widget build(BuildContext context) {
    return Container(
      margin: EdgeInsets.all(30.0),
      color: Color(0xFF4C93C7),
      child: Column(
        mainAxisAlignment: MainAxisAlignment.spaceEvenly,
        children: [
          Center(
            child: Text(
              counter.toString(),
              style: TextStyle(
                color: Color(0xFFFFFFFF),
                fontSize: 22.0,
              ),
            ),
          ),
          Center(
            child: GestureDetector(
              onTap: button,
              child: Text(
                'Button',
                style: TextStyle(
                  color: Color(0xFFFFFFFF),
                  fontSize: 22.0,
                ),
              ),
            ),
          ),
        ],
      ),
    );
  }
}

好的,我现在已经改进了我的代码,以便 BlockBCard's 中的按钮调用一个函数 myBrain 来执行计算,其结果在 中输出>块A

现在我想将 BlockBCard 的 中的文本字段增加 1,但仅在按下按钮的相应卡片中,而不是在所有卡片中。使用我当前的代码,所有卡片都错误地增加了 1。

在这个例子中只有两张卡,但在实现中会有多张卡。

这是我当前的代码。为了简化示例,我删除了 BlockB 并将 BlockBCard's 直接放入 MainBlock:

如何将 BlockBCard 中的文本字段增加 1,但仅在按下按钮的相应卡片中而不是在所有卡片中?

import 'package:flutter/material.dart';

void main() => runApp(MyApp());

class MyApp extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      home: MainBlock(),
    );
  }
}

// OOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOO MainBlock >>>
class MainBlock extends StatefulWidget {
  @override
  _MainBlockState createState() => _MainBlockState();
}

class _MainBlockState extends State<MainBlock> {
  int totalCounter = 0;
  int singleCounter = 0;

  void myBrain() {
    totalCounter = totalCounter + 5;
  }

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      body: Container(
        margin: EdgeInsets.all(30.0),
        color: Color(0xFF122C39),
        child: Column(
          crossAxisAlignment: CrossAxisAlignment.stretch,
          children: [
            Expanded(
              child: BlockA(
                counter: totalCounter,
                button: () {
                  setState(() {
                    totalCounter = 0;
                  });
                },
              ),
            ),
            Expanded(
              child: BlockBCard(
                counter: singleCounter,
                button: () {
                  setState(() {
                    myBrain();
                    singleCounter++;
                  });
                },
              ),
            ),
            Expanded(
              child: BlockBCard(
                counter: singleCounter,
                button: () {
                  setState(() {
                    myBrain();
                    singleCounter++;
                  });
                },
              ),
            ),
          ],
        ),
      ),
    );
  }
}
// OOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOO MainBlock <<<

// OOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOO BlockA >>>
class BlockA extends StatelessWidget {
  final int counter;
  final Function button;

  BlockA({this.counter, this.button});

  @override
  Widget build(BuildContext context) {
    return Container(
      margin: EdgeInsets.all(10.0),
      color: Color(0xFF265672),
      child: Column(
        mainAxisAlignment: MainAxisAlignment.spaceEvenly,
        children: [
          Center(
            child: Text(
              counter.toString(),
              style: TextStyle(
                color: Color(0xFFFFFFFF),
                fontSize: 22.0,
              ),
            ),
          ),
          Center(
            child: GestureDetector(
              onTap: button,
              child: Text(
                'Button',
                style: TextStyle(
                  color: Color(0xFFFFFFFF),
                  fontSize: 22.0,
                ),
              ),
            ),
          ),
        ],
      ),
    );
  }
}

// OOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOO BlockA <<<
// OOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOO BlockBCard >>>

class BlockBCard extends StatelessWidget {
  final int counter;
  final Function button;

  BlockBCard({this.counter, this.button});

  @override
  Widget build(BuildContext context) {
    return Container(
      margin: EdgeInsets.all(30.0),
      color: Color(0xFF4C93C7),
      child: Column(
        mainAxisAlignment: MainAxisAlignment.spaceEvenly,
        children: [
          Center(
            child: Text(
              counter.toString(),
              style: TextStyle(
                color: Color(0xFFFFFFFF),
                fontSize: 22.0,
              ),
            ),
          ),
          Center(
            child: GestureDetector(
              onTap: button,
              child: Text(
                'Button',
                style: TextStyle(
                  color: Color(0xFFFFFFFF),
                  fontSize: 22.0,
                ),
              ),
            ),
          ),
        ],
      ),
    );
  }
}
// OOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOO BlockBCard <<<

【问题讨论】:

    标签: flutter widget setstate


    【解决方案1】:

    您遇到了问题,因为当您更改 blockB 的值时,它无法重建 ta blockA,但如果您想从 blockB 更改 blockA,它会更改全局值,您应该像 blockA 一样传递。

    
    import 'package:flutter/material.dart';
    
    void main() => runApp(MyApp());
    int testCounter = 0;
    
    class MyApp extends StatelessWidget {
      @override
      Widget build(BuildContext context) {
        return MaterialApp(
          home: MainBlock(),
        );
      }
    }
    // ------------------------------------ Stateless Widget <<<
    
    class MainBlock extends StatefulWidget {
      @override
      _MainBlockState createState() => _MainBlockState();
    }
    
    class _MainBlockState extends State<MainBlock> {
      @override
      Widget build(BuildContext context) {
        return Scaffold(
          body: Container(
            margin: EdgeInsets.all(30.0),
            color: Color(0xFF122C39),
            child: Column(
              crossAxisAlignment: CrossAxisAlignment.stretch,
              children: [
                Expanded(
                  child: BlockA(
                    counter: testCounter,
                    button: () {
                      setState(() {
                        testCounter++;
                      });
                    },
                  ),
                ),
                Expanded(
                  child: BlockB(
                      counter: testCounter,
                      button: () {
                        setState(() {
                          testCounter++;
                        });
                      }),
                ),
              ],
            ),
          ),
        );
      }
    }
    
    class BlockA extends StatelessWidget {
      final int counter;
      final Function button;
    
      BlockA({this.counter, this.button});
    
      @override
      Widget build(BuildContext context) {
        return Container(
          margin: EdgeInsets.all(10.0),
          color: Color(0xFF265672),
          child: Column(
            mainAxisAlignment: MainAxisAlignment.spaceEvenly,
            children: [
              Center(
                child: Text(
                  counter.toString(),
                  style: TextStyle(
                    color: Color(0xFFFFFFFF),
                    fontSize: 22.0,
                  ),
                ),
              ),
              Center(
                child: GestureDetector(
                  onTap: button,
                  child: Text(
                    'Button',
                    style: TextStyle(
                      color: Color(0xFFFFFFFF),
                      fontSize: 22.0,
                    ),
                  ),
                ),
              ),
            ],
          ),
        );
      }
    }
    
    class BlockB extends StatefulWidget {
      final int counter;
      final Function button;
    
      BlockB({this.counter, this.button});
    
      @override
      _BlockBState createState() => _BlockBState();
    }
    
    class _BlockBState extends State<BlockB> {
      @override
      Widget build(BuildContext context) {
        return Container(
          margin: EdgeInsets.all(10.0),
          color: Color(0xFF265672),
          child: Column(
            mainAxisAlignment: MainAxisAlignment.center,
            children: [
              Expanded(
                child: BlockBCard(
                  counter: testCounter,
                  button:widget.button,
                ),
              ),
              Expanded(
                child: BlockBCard(
                  counter: testCounter,
                ),
              ),
            ],
          ),
        );
      }
    }
    
    class BlockBCard extends StatelessWidget {
      final int counter;
      final Function button;
    
      BlockBCard({this.counter, this.button});
    
      @override
      Widget build(BuildContext context) {
        return Container(
          margin: EdgeInsets.all(30.0),
          color: Color(0xFF4C93C7),
          child: Column(
            mainAxisAlignment: MainAxisAlignment.spaceEvenly,
            children: [
              Center(
                child: Text(
                  counter.toString(),
                  style: TextStyle(
                    color: Color(0xFFFFFFFF),
                    fontSize: 22.0,
                  ),
                ),
              ),
              Center(
                child: GestureDetector(
                  onTap: button,
                  child: Text(
                    'Button',
                    style: TextStyle(
                      color: Color(0xFFFFFFFF),
                      fontSize: 22.0,
                    ),
                  ),
                ),
              ),
            ],
          ),
        );
      }
    }
    
    

    【讨论】:

    • 啊哈你好再次;)我们大多做同样的事情但是我认为使用可变全局变量确实是这里犯的错误。当然你需要把它传递下去,但是如果你明白你永远不应该创建一个可变的全局变量,那么无论如何你都不得不把它传递下去
    • @Lulupointu yap 明白可变全局变量确实是个错误。
    【解决方案2】:

    正如@Jahidul Islam 所解释的,您必须将数据从MainBlock 传递到BlocB,就像从MainBlock 传递到BlocA 一样。

    通常,在 Flutter 中,正是出于这个原因,您希望避免使用可变全局变量。当一个变量更新时,flutter知道它。如果您致电setState,小部件树波纹管 将被重建。 这就是为什么您经常会听到“提升状态”的原因,因为状态必须包含在足够高的小部件中,以便它可以通过小部件树传递给每个需要它的小部件。在您的情况下,由于BlocA BlockBCard 需要counter,因此必须在较低的共同祖先中创建它:MainBlock

    这是具体的实现,但最重要的是理解上面解释的推理:

    import 'package:flutter/material.dart';
    
    void main() => runApp(MyApp());
    
    class MyApp extends StatelessWidget {
      @override
      Widget build(BuildContext context) {
        return MaterialApp(
          home: MainBlock(),
        );
      }
    }
    
    class MainBlock extends StatefulWidget {
      @override
      _MainBlockState createState() => _MainBlockState();
    }
    
    class _MainBlockState extends State<MainBlock> {
      int testCounter = 0;
    
      @override
      Widget build(BuildContext context) {
        return Scaffold(
          body: Container(
            margin: EdgeInsets.all(30.0),
            color: Color(0xFF122C39),
            child: Column(
              crossAxisAlignment: CrossAxisAlignment.stretch,
              children: [
                Expanded(
                  child: BlockA(
                    counter: testCounter,
                    button: () {
                      setState(() {
                        testCounter++;
                      });
                    },
                  ),
                ),
                Expanded(
                  child: BlockB(
                    counter: testCounter,
                    button: () {
                      setState(() {
                        testCounter++;
                      });
                    },
                  ),
                ),
              ],
            ),
          ),
        );
      }
    }
    
    class BlockA extends StatelessWidget {
      final int counter;
      final VoidCallback button;
    
      BlockA({required this.counter, required this.button});
    
      @override
      Widget build(BuildContext context) {
        return Container(
          margin: EdgeInsets.all(10.0),
          color: Color(0xFF265672),
          child: Column(
            mainAxisAlignment: MainAxisAlignment.spaceEvenly,
            children: [
              Center(
                child: Text(
                  counter.toString(),
                  style: TextStyle(
                    color: Color(0xFFFFFFFF),
                    fontSize: 22.0,
                  ),
                ),
              ),
              Center(
                child: GestureDetector(
                  onTap: button,
                  child: Text(
                    'Button',
                    style: TextStyle(
                      color: Color(0xFFFFFFFF),
                      fontSize: 22.0,
                    ),
                  ),
                ),
              ),
            ],
          ),
        );
      }
    }
    
    class BlockB extends StatelessWidget {
      final int counter;
      final VoidCallback button;
    
      BlockB({required this.counter, required this.button});
    
      @override
      Widget build(BuildContext context) {
        return Container(
          margin: EdgeInsets.all(10.0),
          color: Color(0xFF265672),
          child: Column(
            mainAxisAlignment: MainAxisAlignment.center,
            children: [
              Expanded(
                child: BlockBCard(
                  counter: counter,
                  button: button,
                ),
              ),
              Expanded(
                child: BlockBCard(
                  counter: counter,
                ),
              ),
            ],
          ),
        );
      }
    }
    
    class BlockBCard extends StatelessWidget {
      final int counter;
      final VoidCallback? button;
    
      BlockBCard({required this.counter, this.button});
    
      @override
      Widget build(BuildContext context) {
        return Container(
          margin: EdgeInsets.all(30.0),
          color: Color(0xFF4C93C7),
          child: Column(
            mainAxisAlignment: MainAxisAlignment.spaceEvenly,
            children: [
              Center(
                child: Text(
                  counter.toString(),
                  style: TextStyle(
                    color: Color(0xFFFFFFFF),
                    fontSize: 22.0,
                  ),
                ),
              ),
              Center(
                child: GestureDetector(
                  onTap: button ?? (() {}),
                  child: Text(
                    'Button',
                    style: TextStyle(
                      color: Color(0xFFFFFFFF),
                      fontSize: 22.0,
                    ),
                  ),
                ),
              ),
            ],
          ),
        );
      }
    }
    

    【讨论】:

    • 我通过小部件树传递状态状态来查看这里的逻辑。在我的编码水平上,为什么要避免使用可变全局变量尚不清楚。
    • 你亲眼看到全局变量产生了什么样的不良行为,你没有吗?
    • 是的,现在更清楚了,我改进了我的代码并理解了这种关系。但目前尚不清楚如何仅在按下按钮的相应卡片中而不是在所有其他卡片中将文本字段增加 1。
    【解决方案3】:

    如果我们刷新MainBlock,它的孩子也会得到更新的状态。我们可以使用回调。

    开启BlocKB

    
    class BlockB extends StatefulWidget {
      final VoidCallback ontap;
      const BlockB({
        Key? key,
        required this.ontap,
      }) : super(key: key);
      @override
      _BlockBState createState() => _BlockBState();
    }
    
    class _BlockBState extends State<BlockB> {
      @override
      Widget build(BuildContext context) {
        return Container(
          margin: EdgeInsets.all(10.0),
          color: Color(0xFF265672),
          child: Column(
            mainAxisAlignment: MainAxisAlignment.center,
            children: [
              Expanded(
                child: BlockBCard(
                  counter: testCounter,
                  button: () {
                    // setState(() { /// we dont need it while parent widget will refress it
                    testCounter++;
                    // });
    
                    widget.ontap();
                  },
                ),
              ),
              Expanded(
                child: BlockBCard(
                  counter: testCounter,
                  button: () {
                    /// you may also wanted to increment here
    
                    testCounter++;
    
                    widget.ontap();
                  },
                ),
              ),
            ],
          ),
        );
      }
    }
    
    

    在主块上

    
           Expanded(
                  child: BlockB(
                    ontap: () {
                      setState(() {});
                    },
                  ),
                ),
    
    

    【讨论】:

      猜你喜欢
      • 2021-07-10
      • 1970-01-01
      • 2018-10-13
      • 2015-02-20
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2016-10-11
      • 2020-08-04
      相关资源
      最近更新 更多