Состояние изменения флаттера из связанного класса виджета

Предположим, что класс 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 или содержащего его родительского виджета?

Уже есть несколько похожих вопросов по этому поводу здесь, в Stack Overflow, где я мог бы найти подсказки как использовать, так и не использовать глобальные ключи для такого поведения, которое я нашел вводящим в заблуждение. Кроме того, из-за быстрого развития флаттера они, вероятно, устарели, поэтому я снова задаю этот (аналогичный) вопрос в отношении этого конкретного варианта использования.

РЕДАКТИРОВАТЬ: Я забыл упомянуть, что очень важно, чтобы этот флаг был изменен после создания, поэтому он будет изменен несколько раз во время его существования. Это требует, чтобы виджет перерисовался.


person hypnomaki    schedule 26.02.2021    source источник
comment
Ваша первая идея была правильной, на самом деле это может быть даже виджет без сохранения состояния. Может, вместо этого вы можете описать свои проблемы. Ваше второе решение не в том, как это делает Flutter.   -  person nvoigt    schedule 26.02.2021


Ответы (2)


В вашем случае не обязательно использовать виджет с отслеживанием состояния для 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) должны быть перестроены.

Пожалуйста, попробуйте этот подход, он должен работать.

person Mol0ko    schedule 26.02.2021
comment
Понятно, значит, я пропустил эти ключи. К сожалению, я не знаю, сколько SpecialButton будет содержать родительский элемент, поэтому я создал список. Похоже, что функция ValueKey не работает с массивами: Duplicate Keys found: If multiple keyed nodes exist as children of another node, they must have unique keys. Поэтому я создал список логических значений в родительском элементе и пытаюсь установить параметр для каждого SpecialButton через key: ValueKey(this.btnActive[i]) ... Есть ли обходной путь для этого? - person hypnomaki; 26.02.2021
comment
Ой, моя беда, конечно, вам не следует использовать здесь логические ValueKeys. Решение довольно простое - используйте UniqueKey(). Я обновил свой ответ. - person Mol0ko; 26.02.2021

Как говорит 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 и функцию внутри него, которая изменяет значение. В противном случае я бы рекомендовал не разделять ваше дерево на несколько файлов.

person user14624595    schedule 26.02.2021