Flutter,如何在有状态小部件中正确声明和使用变量?

Flutter, how do I correctly declare and use variables in a stateful widget?

我有一个 StatefulWidget,其中包含一个 ListView() 和一个 ListView.builder()ListView.builder() 使用两个列表构建多个 CheckboxListTile(),一个用于标签,一个用于布尔值。但是它没有按预期工作。我试图创建一个问题的最小示例,它看起来像这样:

import 'package:flutter/material.dart';

class TestClass extends StatefulWidget {
  @override
  _TestClassState createState() => _TestClassState();
}

class _TestClassState extends State<TestClass> {
  @override
  Widget build(BuildContext context) {
    bool firstBoolean = false;
    bool secondBoolean = false;
    List labels = [
    "First Checkbox",
    "Second Checkbox",
    ];  
    List<bool> booleans = [
      firstBoolean,
      secondBoolean,
    ];    
    return ListView(
      children: [
        Container(
          height: MediaQuery.of(context).size.height * 0.5,
          child: ListView.builder(
              physics: AlwaysScrollableScrollPhysics(),
              shrinkWrap: true,
              itemCount: 2,
              itemBuilder: (context, index) {
                return CheckboxListTile(
                  title: Text("${labels[index]}"),
                  value: booleans[index],
                  onChanged: (bool newValue) {
                    setState(() {
                      secondBoolean = newValue;
                      print(
                          "firstBoolean, secondBoolean: [$firstBoolean, $secondBoolean]");
                    });
                  },
                );
              }),
            ),
          ],
        );
      }
    }

这不会更改复选框,也不会更改分配的布尔值。如果我替换这一行:

value: booleans[index]

value: firstBoolean

并将 bool firstBoolean = false; 移动到 _TestCassState@override 的上方 'works' 但由于所有复选框现在都分配了相同的布尔变量,因此它会改变所有他们同时。但是我不明白为什么这个 'works' 和上面的代码不一样。此外,如果我尝试在 @override 上方创建我需要的所有布尔值,然后尝试创建一个包含所有布尔值的列表,我会收到此错误:

The instance member 'firstBoolean' can't be accessed in an initializer. Try replacing the reference to the instance member with a different expression

如果我移动到带有 @override 上方标签的列表,它不会给我这个错误并正确分配标签

我觉得我不了解 StatefulWidgets 的一些基本知识。所以如果有人能给我解释如何正确解决这个问题以及为什么它没有按预期工作,我真的很感激

当您调用 setstate() 时,会调用构建方法,并在构建方法中声明您。

覆盖状态的 initState() 方法。

class _TestClassState extends State<TestClass> {
   bool firstBoolean,secondBoolean;
   List labels;
   List<bool> booleans;
  
  @override
   void initState(){
    super.initState();
       firstBoolean = false;
     secondBoolean = false;
     labels = [
    "First Checkbox",
    "Second Checkbox",
    ];  
    booleans = [
      firstBoolean,
      secondBoolean,
    ];    
  }
}

并从构建方法中删除变量声明

您甚至不需要单独的 bool 变量。

您基本上可以将 labels 放在 TestClass 中,因为它们不属于状态,因为它们永远不会改变。

接下来,您可以在 State class 中只包含 List<bool> booleans,因为您所有更改的内容就是这些 booleans 本身。

所以你的结构现在是这样的。

class TestClass extends StatefulWidget {
  final List labels = [
    "First Checkbox",
    "Second Checkbox",
  ];
  
  @override
  _TestClassState createState() => _TestClassState();
}

最好将它们放在 StatefulWidget class 中。然后,您可以像这样在 State class 中使用它们 - widget.labels[index].

因此,您的 State class 将是,

class _TestClassState extends State<TestClass> {
  List<bool> booleans = [false,false];

  @override
  Widget build(BuildContext context) {
    return ListView(
      children: [
        Container(
          height: MediaQuery.of(context).size.height * 0.5,
          child: ListView.builder(
            physics: AlwaysScrollableScrollPhysics(),
            shrinkWrap: true,
            itemCount: 2,
            itemBuilder: (context, index) {
              return CheckboxListTile(
                title: Text("${widget.labels[index]}"),
                value: booleans[index],
                onChanged: (bool newValue) {
                  setState(() {
                    // There was a logic error here in your code, so changed it to work correctly
                    booleans[index] = newValue;
                });
              },
            );
          }),
        ),
      ],
    );
  }
}

只要您调用 setState() 方法,就会调用构建函数并使用相同的旧值初始化变量。 解决问题的最简单方法是将变量保留在构建函数之外,这样它们就不会在您每次调用 setState() 方法时都被初始化。 这是你应该做的

    import 'package:flutter/material.dart';
class TestClass extends StatefulWidget {
  @override
  _TestClassState createState() => _TestClassState();
}

class _TestClassState extends State<TestClass> {
    // Declare the variables above build fucntion
    bool firstBoolean = false; 
    bool secondBoolean = false;
    List labels = [
    "First Checkbox",
    "Second Checkbox",
    ];  
    List<bool> booleans = [false,false];    
  @override
  Widget build(BuildContext context) {
    return ListView(
      children: [
        Container(
          height: MediaQuery.of(context).size.height * 0.5,
          child: ListView.builder(
              physics: AlwaysScrollableScrollPhysics(),
              shrinkWrap: true,
              itemCount: 2,
              itemBuilder: (context, index) {
                return CheckboxListTile(
                  title: Text("${labels[index]}"),
                  value: booleans[index],
                  onChanged: (bool newValue) {
                    setState(() {
                      secondBoolean = newValue;
                      print(
                          "firstBoolean, secondBoolean: [$firstBoolean, $secondBoolean]");
                    });
                  },
                );
              }),
            ),
          ],
        );
      }
    }

您在错误的地方声明了变量,您已经在构建函数中初始化了这些变量。因此,无论何时调用 setState,变量都将重新初始化为默认值。您也不需要使用 firstBooleansecondBoolean。将您的代码更改为:

class TestClass extends StatefulWidget {
  @override
  _TestClassState createState() => _TestClassState();
}

class _TestClassState extends State<TestClass> {

  List labels = [
    "First Checkbox",
    "Second Checkbox",
  ];
  List<bool> booleans = List.filled(2, false); // Initialising with default value

  @override
  Widget build(BuildContext context) {
    return ListView(
      children: [
        Container(
          height: MediaQuery.of(context).size.height * 0.5,
          child: ListView.builder(
            physics: AlwaysScrollableScrollPhysics(),
            shrinkWrap: true,
            itemCount: 2,
            itemBuilder: (context, index) {
              return CheckboxListTile(
                title: Text("${labels[index]}"),
                value: booleans[index],
                onChanged: (bool newValue) {
                  setState(() {
                    booleans[index] = newValue;
                    print(booleans[index]);
                  });
                },
              );
            },
          ),
        ),
      ],
    );
  }
}