Flutter desktop hover and SelectionArea menu reset

Viewed 26

I have written a simple hover widget to change background colors.

import 'package:flutter/material.dart';

class Hover extends StatefulWidget {
  final Color hoverColor;
  final Widget child;

  const Hover({required this.child, required this.hoverColor, Key? key})
      : super(key: key);

  @override
  State<Hover> createState() => _HoverState();
}

class _HoverState extends State<Hover> {
  Color? _bgColor;

  @override
  Widget build(BuildContext context) {
    return MouseRegion(
      onEnter: (_) {
        setState(() {
          _bgColor = widget.hoverColor;
        });
      },
      onExit: (_) {
        setState(() {
          _bgColor = null;
        });
      },
      child: Container(
        color: _bgColor,
        child: widget.child,
      ),
    );
  }
}

When putting this inside a SelectionArea() and the text is selectable but the right-click menu of the selected text disappears when trying to select an option there. I think the reason is that Hover() is rebuilding the entire tree and thus resetting the menu: (this is the main build method)

    return Scaffold(
      body: SelectionArea(
        child: Hover(
          hoverColor: Colors.grey,
          child: Container(
            width: double.infinity,
            padding: const EdgeInsets.all(20),
            child: Text(
                'Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat. Duis aute irure dolor in reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla pariatur. Excepteur sint occaecat cupidatat non proident, sunt in culpa qui officia deserunt mollit anim id est laborum.'),
          ),
        ),
      )
    );

How would you do this? Is it possible to make Hover() ignore rebuilding the child widget? (since its only goal is to change the background)

1 Answers

I managed to stop the rebuild with animation in the Hover class.

It can work with both AnimationBuilder or TweenAnimationBuilder. This is the state class of my hover:

class _HoverState extends State<Hover> {
  Color _firstColor = Colors.transparent;
  Color _secondColor = Colors.transparent;

  @override
  void initState() {
    super.initState();
  }

  @override
  Widget build(BuildContext context) {
    return MouseRegion(
      onEnter: (_) {
        _firstColor = Colors.transparent;
        _secondColor = widget.hoverColor;
        setState(() {});
      },
      onExit: (_) {
        _firstColor = widget.hoverColor;
        _secondColor = Colors.transparent;

        setState(() {});
      },
      child: TweenAnimationBuilder(
        duration: const Duration(milliseconds: 1),
        tween: ColorTween(begin: _firstColor, end: _secondColor),
        child: widget.child,
        builder: (BuildContext context, Color? value, Widget? child) {
          return Container(
            color: value,
            child: child,
          );
        },
      ),
    );
  }
}

The child parameter of the animation builders is not rebuilt and is isolated from the animation. So with this, I can animate the background color while keeping the content widget untouched.

Related