flutterdartriverpodstate-managementmousehover

How to Display Only One Edit Icon for Selected Container in Flutter?


I'm trying to implement a feature where an edit icon appears only for the container the user is hovering over. Currently, when I hover over one container, edit icons appear for all four containers. Here's what I'm experiencing:

enter image description here

This is the desired result:

enter image description here

Current Implementation

I have a row of four AddProyectName widgets, each representing a project container. The edit icon visibility is controlled by a single _isEditIconVisible state variable in the parent widget.

Here's a simplified version of the relevant code:

add_projects.dart


class AddProyectName extends ConsumerStatefulWidget {
  final Color color;
  final Icon? icon;
  final int index;
  final bool isEditable;
  final bool isEditIconVisible;

  final Function(PointerEnterEvent)? onEnterContainerHover;
  final Function(PointerExitEvent)? onExitContainerHover;


  const AddProyectName(this.color,
      {required this.index,
      this.icon,
      this.isEditable = false,
      super.key,
      required this.isEditIconVisible,
      this.onEnterContainerHover,
      this.onExitContainerHover});

  @override
  ConsumerState<ConsumerStatefulWidget> createState() => _AddProyectNameState();
}

class _AddProyectNameState extends ConsumerState<AddProyectName> {
  bool _isHovered = false;

  @override
  Widget build(BuildContext context) {
    final selectedContainerIndex = ref.watch(selectedProyectContainerProvider);
    final projectNames = ref.watch(projectStateNotifierProvider);

//... code

  return MouseRegion(
      onEnter: widget.onEnterContainerHover,
      onExit: widget.onExitContainerHover,
      cursor: SystemMouseCursors.click,
      child: Container(
        height: 66,
        width: 27,
        decoration: BoxDecoration(
          border: Border.all(
            color: Colors.transparent,
          ),
        ),

//... code

  child: Container(
                      width: 24,
                      height: 24,
                      decoration: BoxDecoration(
                        color: widget.color,
                        shape: BoxShape.circle,
                        border: selectedContainerIndex != null &&
                                selectedContainerIndex == widget.index
                            ? Border.all(color: Colors.white, width: 2.0)
                            : null,
                      ),
                      child: widget.icon != null
                          ? Center(child: widget.icon)
                          : null,
                    ),
                  ),
                  Visibility(
                    visible: widget.isEditIconVisible,
                    replacement: const SizedBox(
                      height: 40,
                      width: 25,
                    ),
                    child: MouseRegion(
                      onEnter: (_) => setState(() => _isHovered = true),
                      onExit: (_) => setState(() => _isHovered = false),
                      cursor: SystemMouseCursors.click,
                      child: Tooltip(
                        message: "Edit Project",
                        decoration: BoxDecoration(
                          color: Colors.white,
                          borderRadius: BorderRadius.circular(4.0),
                        ),
                        child: Container(
                          decoration: BoxDecoration(
                            shape: BoxShape.circle,
                            color: _isHovered
                                ? Colors.white30
                                : Colors.transparent,
                          ),
                          height: 40,
                          width: 25,
                          child: GestureDetector(
                            onTap: () {
                              _showDialog(context, 'Edit Project',
                                  'Please edit the project name');
                            },
                            child: const Icon(Icons.edit,
                                color: Colors.white, size: 20),
                          ),
                        ),
                      ),
                    ),
                  ),
               
               
//... code

task_card.dart


class TaskCard extends ConsumerStatefulWidget {
  const TaskCard({super.key});

  @override
  ConsumerState<ConsumerStatefulWidget> createState() => _TaskCardState();
}

class _TaskCardState extends ConsumerState<TaskCard> {
  bool _isEditIconVisible = false;


//... code


    return LayoutBuilder(builder: (context, constraints) {
      final currentWidth = MediaQuery.of(context).size.width;
      return Row(


//... code



       Row(
                        mainAxisAlignment: MainAxisAlignment.end,
                        children: [
                          Flexible(
                            child: GestureDetector(
                              behavior: HitTestBehavior.opaque,
                              onTap: () {
                                setState(() => _isEditIconVisible = false);
                              },
                              child: AddProyectName(
                                const Color(0xffDE6868),
                                index: 0,
                                isEditable: true,
                                isEditIconVisible: _isEditIconVisible,

                                onEnterContainerHover: (p0) {
                                  setState(() => _isEditIconVisible = true);
                                },
                                onExitContainerHover: (p0) {
                                  setState(() => _isEditIconVisible = false);
                                },
                              
                              ),
                            ),
                          ),
                      // ... similar code for other AddProyectName widgets

                        ],
                      ),
                    



The AddProyectName widget uses this isEditIconVisible prop to determine whether to show the edit icon:

Visibility(
  visible: widget.isEditIconVisible,
  replacement: const SizedBox(height: 40, width: 25),
  child: // ... edit icon widget
)

The Problem:

The issue is that the _isEditIconVisible state is shared among all AddProyectName widgets. When one container is hovered, it sets _isEditIconVisible to true, causing all containers to show their edit icons.

What I've Tried

I've attempted to use individual MouseRegion widgets for each container, but I'm still struggling to make the edit icon appear only for the hovered container.

Question

How can I modify my code to ensure that the edit icon appears only for the specific container being hovered over?


Solution

  • We need to separate "_isEditIconVisible" for each container.

    Like this:

    import 'package:flutter/material.dart';
    
    void main() async {
      runApp(const MyApp());
    }
    
    class MyApp extends StatelessWidget {
      const MyApp({super.key});
    
      @override
      Widget build(BuildContext context) {
        return MaterialApp(
          debugShowCheckedModeBanner: false,
          theme: ThemeData(useMaterial3: false),
          home: const TestApp(),
        );
      }
    }
    
    class TestApp extends StatefulWidget {
      const TestApp({super.key});
    
      @override
      State<TestApp> createState() => _TestAppState();
    }
    
    class _TestAppState extends State<TestApp> {
      @override
      Widget build(BuildContext context) {
        return Scaffold(
          backgroundColor: Colors.black,
          body: Center(
            child: Row(
              children: const [
                AddProyectName(
                  Color(0xffDE6868),
                  index: 0,
                  isEditable: true,
                ),
                SizedBox(width: 20),
                AddProyectName(
                  Colors.yellow,
                  index: 1,
                  isEditable: true,
                ),
                SizedBox(width: 20),
                AddProyectName(
                  Colors.blue,
                  index: 2,
                  isEditable: true,
                ),
                SizedBox(width: 20),
                AddProyectName(
                  Colors.green,
                  index: 3,
                  isEditable: true,
                ),
              ],
            ),
          ),
        );
      }
    }
    
    class AddProyectName extends StatefulWidget {
      final Color color;
      final int index;
      final bool isEditable;
    
      const AddProyectName(
        this.color, {
        required this.index,
        this.isEditable = false,
        super.key,
      });
    
      @override
      State<AddProyectName> createState() => _AddProyectNameState();
    }
    
    class _AddProyectNameState extends State<AddProyectName> {
      bool _isHovered = false;
    
      bool _isEditIconVisible = false;
    
      @override
      Widget build(BuildContext context) {
        return GestureDetector(
          behavior: HitTestBehavior.opaque,
          onTap: () {
            setState(() => _isEditIconVisible = false);
          },
          child: MouseRegion(
            onEnter: (event) {
              setState(() => _isEditIconVisible = true);
            },
            onExit: (event) {
              setState(() => _isEditIconVisible = false);
            },
            child: SizedBox(
              height: 100,
              child: Row(
                mainAxisAlignment: MainAxisAlignment.end,
                children: [
                  Column(
                    children: [
                      Container(
                        width: 50,
                        height: 50,
                        color: widget.color,
                      ),
                      Visibility(
                          visible: _isEditIconVisible,
                          replacement: const SizedBox(
                            height: 40,
                            width: 40,
                          ),
                          child: MouseRegion(
                            onEnter: (_) => setState(() => _isHovered = true),
                            onExit: (_) => setState(() => _isHovered = false),
                            cursor: SystemMouseCursors.click,
                            child: Tooltip(
                              message: "Edit Message",
                              decoration: BoxDecoration(
                                color: Colors.black,
                                borderRadius: BorderRadius.circular(4.0),
                              ),
                              child: Container(
                                decoration: BoxDecoration(
                                  shape: BoxShape.circle,
                                  color: _isHovered
                                      ? Colors.white30
                                      : Colors.transparent,
                                ),
                                height: 40,
                                width: 40,
                                child: const Icon(Icons.edit,
                                    color: Colors.white, size: 20),
                              ),
                            ),
                          )),
                    ],
                  ),
                ],
              ),
            ),
          ),
        );
      }
    }