简体   繁体   中英

Flutter change state from related widget class

Lets assume a class "SpecialButton" and its State-Class "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);
  }
}

In the parent widget, I manage a couple of these buttons. Therefore, I want to assign a state to them. The solution I tried was to introduce a flag "active" in the SpecialButton class which I can easily set to either true or false from the parent widget. I can then use this in the build function of the state class to colorize the button. Unfortunately, this does not work completely as it does not update the button immediately (it needs some kind of state update eg by hovering over the element).

My second idea was to introduce this flag as a propper state of the SpecialButtonState class

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);
  }
}

As far as I understood, this would be the correct way to work with flutter but it seems that I can't access the functions "activate" or "deactivate" from either the SpecialButton Class or the Parent Class containing the widget.

So my question is: How can I (directly or indirectly through functions) modify a State from the corresponding StatefulWidget Class or the Parent Widget containing it?

There are already some similar questions about this on here on Stack Overflow where I could find hints both to use or not to use global keys for such behavior which i found misleading. Also, due to the rapid ongoing development of flutter, they are probably outdated so I ask this (similar) question again in relation to this exact use case.

EDIT: I forgot to mention that it is crucial that this flag will be changed after creation therefore It will be changed multiple times during its livetime. This requires the widget to redraw.

It is not neсessary to use stateful widget for SpecialButton is you case. You can handle active flag with stateless widget and keys. Example code:


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 is my fantasy, just for example. Don't know what your parent is. Keys are significant here. They tell widget tree when specific widgets with the same type (such as SpecialButton ) should be rebuild.

Please try this approach, it should work.

As nvoigt says, your buttons could even be stateless widget, but their parent should be statefull and you should provide them with the corresponding value. eg:

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;
                    });
        })
      ],
      
    );
  }
}

Now it just depends on when you want to change that value. If you want to change it inside your buttons, I would recommend you to use a class with ChangeNotifier and a function inside it that changes the value. Otherwise I would recommend not to separate your tree into multiple files

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM