Flutter 从相关的小部件类更改状态

Posted

技术标签:

【中文标题】Flutter 从相关的小部件类更改状态【英文标题】:Flutter change state from related widget class 【发布时间】:2021-05-28 13:48:11 【问题描述】:

让我们假设一个类“SpecialButton”及其状态类“SpecialButtonState”

class SpecialButton extends StatefulWidget 
  bool active = false;
  SpecialButton(Key key) : super(key: key);
  @override
  SpecialButtonState createState() => SpecialButtonState();


class SpecialButtonState extends State<SpecialButton> 
  @override
  void initState() 
    super.initState();
  

  @override
  Widget build(BuildContext context) 
    return Container(
        decoration:
            BoxDecoration(color: this.widget.active ? COLOR_1 : COLOR_2),
        child: null);
  

在父窗口小部件中,我管理其中的几个按钮。因此,我想为他们分配一个状态。我尝试的解决方案是在 SpecialButton 类中引入一个“活动”标志,我可以轻松地从父小部件将其设置为 true 或 false。然后我可以在状态类的构建函数中使用它来为按钮着色。不幸的是,这并不完全有效,因为它不会立即更新按钮(它需要某种状态更新,例如通过将鼠标悬停在元素上)。

我的第二个想法是将此标志作为 SpecialButtonState 类的固有状态引入

class SpecialButton extends StatefulWidget 
  SpecialButton(Key key) : super(key: key);
  @override
  SpecialButtonState createState() => SpecialButtonState();


class SpecialButtonState extends State<SpecialButton> 
  bool active;

  @override
  void initState() 
    super.initState();
    this.active = false;
  

  activate() 
    this.setState(() 
      active = true;
    );
  

  deactivate() 
    this.setState(() 
      active = false;
    );
  

  @override
  Widget build(BuildContext context) 
    return Container(
        decoration: BoxDecoration(color: this.active ? COLOR_1 : COLOR_2),
        child: null);
  

据我了解,这将是使用颤振的正确方法,但似乎我无法从 SpecialButton 类或包含小部件的父类访问“激活”或“停用”功能。

所以我的问题是:我如何(直接或间接通过函数)从相应的 StatefulWidget 类或包含它的父 Widget 修改状态?

在 Stack Overflow 上已经有一些类似的问题,我可以在其中找到使用或不使用全局键的提示,我发现这种行为具有误导性。此外,由于颤振的快速发展,它们可能已经过时,所以我再次就这个确切的用例提出这个(类似的)问题。

编辑:我忘了提到在创建后更改此标志至关重要,因此它将在其生存期间多次更改。这需要重新绘制小部件。

【问题讨论】:

你的第一个想法是正确的,它甚至可以是一个无状态的小部件。也许你可以用它来描述你的问题。您的第二个解决方案不是 Flutter 是如何做到的。 【参考方案1】:

您不必为SpecialButton 使用有状态小部件。您可以使用无状态小部件和键处理 active 标志。示例代码:


class SomeParent extends StatefulWidget 
  const SomeParent(Key key) : super(key: key);

  @override
  State<SomeParent> createState() => SomeParentState();


class SomeParentState extends State<SomeParent> 
  bool _button1IsActive = false;
  bool _button2IsActive = false;

  @override
  Widget build(BuildContext context) 
    return Scaffold(
      body: Column(
        children: [
          SpecialButton(
            key: UniqueKey(),
            active: _button1IsActive,
          ),
          SizedBox(height: 8),
          SpecialButton(
            key: UniqueKey(),
            active: _button2IsActive,
          ),
          SizedBox(height: 16),
          TextButton(
            child: Text('Toggle button 1'),
            onPressed: () 
              setState(() 
                _button1IsActive = !_button1IsActive;
              );
            ,
          ),
          SizedBox(height: 8),
          TextButton(
            child: Text('Toggle button 2'),
            onPressed: () 
              setState(() 
                _button2IsActive = !_button2IsActive;
              );
            ,
          ),
        ],
      ),
    );
  


class SpecialButton extends StatelessWidget 
  final bool active;

  const SpecialButton(Key key, this.active = false) : super(key: key);

  @override
  Widget build(BuildContext context) 
    return Container(
      height: 40,
      width: 40,
      decoration: BoxDecoration(color: active ? Colors.red : Colors.blue),
    );
  


SomeParent 是我的幻想,例如。不知道你的父母是什么。 键在这里很重要。它们告诉小部件树何时应该重建具有相同类型的特定小部件(例如SpecialButton)。

请尝试这种方法,它应该可以工作。

【讨论】:

我明白了,所以我确实错过了那些键。不幸的是,我不知道父级将包含多少个特殊按钮,因此我创建了一个列表。 ValueKey 函数似乎不适用于数组:Duplicate Keys found: If multiple keyed nodes exist as children of another node, they must have unique keys。因此,我在父级中创建了一个布尔值列表,并尝试通过key: ValueKey(this.btnActive[i]) 为每个 SpecialButton 设置参数...有解决方法吗? 哦,我的错,你不应该在这里使用布尔值键。解决方法非常简单 - 使用 UniqueKey()。我更新了我的答案。【参考方案2】:

正如 nvoigt 所说,您的按钮甚至可以是无状态小部件,但它们的父级应该是有状态的,您应该为它们提供相应的值。例如:

import 'package:flutter/material.dart';

class Parent extends StatefulWidget 
  @override
  _ParentState createState() => _ParentState();


class _ParentState extends State<Parent> 
  bool isEnabled = false;
  @override
  Widget build(BuildContext context) 
    return Column(
      children: [
        StateLessButton1(isEnabled: isEnabled),
        StateLessButton1(isEnabled: !isEnabled),
        FloatingActionButton(onPressed: ()
          setState(() 
                      isEnabled = !isEnabled;
                    );
        )
      ],
      
    );
  

现在它只取决于您何时想要更改该值。如果你想在你的按钮中改变它,我建议你使用一个带有 ChangeNotifier 的类和一个里面的函数来改变值。否则我建议不要将你的树分成多个文件

【讨论】:

以上是关于Flutter 从相关的小部件类更改状态的主要内容,如果未能解决你的问题,请参考以下文章

如何从有状态的小部件类传递变量? - 颤动[重复]

如何从有状态小部件类 Flutter 访问小部件状态

Flutter/Dart:我的小部件树中可以有多个带有状态的小部件吗?

Flutter:何时创建无状态或有状态页面?

在小部件类 Flutter 中实现基类

用于从 REST API 获取状态并自动刷新的小部件的 Flutter 食谱?