Probe into Flutter State Management

2019-11-01
4 min read

Recently, I learned a cross-platform solution based on an APP that wants to implement a collection of commonly used personal functions. Then began to study Flutter, through the article to record the learning gains.

The main content of this article is to introduce the way of state management in Flutter and the choice of different ways.

This article was originally written in Chinese and translated into English by a translation software without proofreading. So please understand the possible inaccuracies and rigor in the article.

State management

There are three common state management methods, which are managed by the Widget itself, managed by the parent Widget and mixedly managed by the Widget itself and the parent Widget.

When analyzing three common state management methods, we defined three Tapboxes, each of which can indicate activation and inactivation. This Tapbox is a stateful Widget.

Managed by Widget itself

//------------------------- TapboxA ----------------------------------

class TapboxA extends StatefulWidget {
  TapboxA({Key key}) : super(key: key);

  @override
  _TapboxAState createState() => _TapboxAState();
}

class _TapboxAState extends State<TapboxA> {
  bool _active = false;

  void _handleTap() {
    setState(() {
      _active = !_active;
    });
  }

  Widget build(BuildContext context) {
    return GestureDetector(
      onTap: _handleTap,
      child: Container(
        child: Center(
          child: Text(
            _active ? 'Active' : 'Inactive',
            style: TextStyle(fontSize: 32.0, color: Colors.white),
          ),
        ),
        width: 200.0,
        height: 200.0,
        decoration: BoxDecoration(
          color: _active ? Colors.lightGreen[700] : Colors.grey[600],
        ),
      ),
    );
  }
}

The state management by the Widget itself is very simple, and the state management by TapboxA’s state class. Use _active to determine the current value and color and use _handleTap to change its value.

Managed by parent Widget

//------------------------ ParentWidget --------------------------------

class ParentWidget extends StatefulWidget {
  @override
  _ParentWidgetState createState() => _ParentWidgetState();
}

class _ParentWidgetState extends State<ParentWidget> {
  bool _active = false;

  void _handleTapboxChanged(bool newValue) {
    setState(() {
      _active = newValue;
    });
  }

  @override
  Widget build(BuildContext context) {
    return Container(
      child: TapboxB(
        active: _active,
        onChanged: _handleTapboxChanged,
      ),
    );
  }
}

//------------------------- TapboxB ----------------------------------

class TapboxB extends StatelessWidget {
  TapboxB({Key key, this.active: false, @required this.onChanged})
      : super(key: key);

  final bool active;
  final ValueChanged<bool> onChanged;

  void _handleTap() {
    onChanged(!active);
  }

  Widget build(BuildContext context) {
    return GestureDetector(
      onTap: _handleTap,
      child: Container(
        child: Center(
          child: Text(
            active ? 'Active' : 'Inactive',
            style: TextStyle(fontSize: 32.0, color: Colors.white),
          ),
        ),
        width: 200.0,
        height: 200.0,
        decoration: BoxDecoration(
          color: active ? Colors.lightGreen[700] : Colors.grey[600],
        ),
      ),
    );
  }
}

Mainly managed by the parent Widget are:

  • The parent widget manages TapboxB’s _active state.

  • Implement _handleTapboxChanged () in the parent Widget’s status class.

The main features of sub-Widgets are:

  • Inherit the StatelessWidget class because its state is managed by the parent Widget.

  • Notify the parent Widget when it is clicked instead of managing its own status.

Mixed management by Widget itself and parent Widget

//---------------------------- ParentWidget ----------------------------

class ParentWidget extends StatefulWidget {
  @override
  _ParentWidgetState createState() => _ParentWidgetState();
}

class _ParentWidgetState extends State<ParentWidget> {
  bool _active = false;

  void _handleTapboxChanged(bool newValue) {
    setState(() {
      _active = newValue;
    });
  }

  @override
  Widget build(BuildContext context) {
    return Container(
      child: TapboxC(
        active: _active,
        onChanged: _handleTapboxChanged,
      ),
    );
  }
}

//----------------------------- TapboxC ------------------------------

class TapboxC extends StatefulWidget {
  TapboxC({Key key, this.active: false, @required this.onChanged})
      : super(key: key);

  final bool active;
  final ValueChanged<bool> onChanged;

  _TapboxCState createState() => _TapboxCState();
}

class _TapboxCState extends State<TapboxC> {
  bool _highlight = false;

  void _handleTapDown(TapDownDetails details) {
    setState(() {
      _highlight = true;
    });
  }

  void _handleTapUp(TapUpDetails details) {
    setState(() {
      _highlight = false;
    });
  }

  void _handleTapCancel() {
    setState(() {
      _highlight = false;
    });
  }

  void _handleTap() {
    widget.onChanged(!widget.active);
  }

  Widget build(BuildContext context) {
    // This example adds a green border on tap down.
    // On tap up, the square changes to the opposite state.
    return GestureDetector(
      onTapDown: _handleTapDown, // Handle the tap events in the order that
      onTapUp: _handleTapUp, // they occur: down, up, tap, cancel
      onTap: _handleTap,
      onTapCancel: _handleTapCancel,
      child: Container(
        child: Center(
          child: Text(widget.active ? 'Active' : 'Inactive',
              style: TextStyle(fontSize: 32.0, color: Colors.white)),
        ),
        width: 200.0,
        height: 200.0,
        decoration: BoxDecoration(
          color:
              widget.active ? Colors.lightGreen[700] : Colors.grey[600],
          border: _highlight
              ? Border.all(
                  color: Colors.teal[700],
                  width: 10.0,
                )
              : null,
        ),
      ),
    );
  }
}

It can be seen that this model only combines the first two models. In the example, the state of adding a border on the edge of the Tapbox is increased by _highlight, which is managed by the Widget itself, while the _active state is managed by the parent Widget.

Choice of state management methods

The selection of state management methods can be summarized in the following ways:

  • If the state in question is user data, such as the checked or unchecked mode of the check box, or the position of the slider, it is best to manage the state by the parent widget.

  • If the state in question is a state such as animation, it is better to manage the state by the Widget itself.

This post was originally published on my personal blog wangchucheng.com.
Original link: https://wangchucheng.com/en/posts/flutter-state-management/
Posts in this blog are original unless otherwise stated, and are licensed under a CC BY-NC-SA 4.0 License. Use beyond the lincense please contact the author for authorization.

Avatar

WANG Chucheng

He is an authentic Tianjin native, but not proficient in any of the four classic skills in Xiangsheng (Speaking, Imitating, Teasing, Singing).