skip to Main Content

I have a simple widget called a LineBox that changes the background color of a Container when the mouse hovers over it using a MouseRegion. However, this appears to be very inefficient as it requires Flutter to rebuild this widget as well as each of its children whenever the hovered state is changed, which will be a problem in the future as I intend to have many of these together in a Column. The main issue with this approach arises when I place another stateful widget inside of the LineBox – everything resets its state if the mouse moves outside of the box. My code is below:

class LineBox extends StatefulWidget {
  final Widget? child;
  final double indent;

  const LineBox({
    super.key,
    this.child,
    this.indent = 0,
  });

  @override
  State<LineBox> createState() => _LineBoxState();
}

class _LineBoxState extends State<LineBox> {
  var _hovered = false;

  @override
  Widget build(BuildContext context) {
    return MouseRegion(
      onEnter: (event) => setState((){
        _hovered = true;
      }),
      onExit: (event) => setState((){
        _hovered = false;
      }),
      child: Container(
        color: _hovered ? Colors.red : null,
        height: 40.0,
        padding: EdgeInsets.only(left: widget.indent),
        child: widget.child,
      ),
    );
  }
}

So far, I’ve tried stacking the LineBox below its child with a Stack, but this means the MouseRegion triggers the onExit event whenever the mouse moves over the child. I also tried wrapping the child widget in a KeyedSubtree with a UniqueKey, which does nothing.

2

Answers


  1. You can try to separate the background container from its children in this way:

    class _LineBoxState extends State<LineBox> {
      final ValueNotifier<bool> _hovered = ValueNotifier<bool>(false);
    
      @override
      Widget build(BuildContext context) {
        return Stack(
          children: [
            Positioned.fill(
              child: ValueListenableBuilder<bool>(
                builder: (BuildContext context, bool value, Widget? child) {
                  return Container(
                    color: value ? Colors.red : null,
                  );
                },
                valueListenable: _hovered,
              ),
            ),
            MouseRegion(
              onEnter: (event) => _hovered.value = true,
              onExit: (event) => _hovered.value = false,
              child: widget.child,
            ),
          ],
        );
      }
    }
    
    Login or Signup to reply.
  2. I think, and it could be wrong, but there is just one case when LineBox will be rebuild and child not:

    LineBox(
      child: const SomeChildWidget(),
    )..
    

    As child is const, there will no rebuild for this child widget.

    Login or Signup to reply.
Please signup or login to give your own answer.
Back To Top
Search