title: How to breakdown a widget and switch between his image and the real widget?
body: <p><a href="https://i.stack.imgur.com/39lj9.gif" rel="nofollow noreferrer"><img src="https://i.stack.imgur.com/39lj9.gif" alt="enter image description here" /></a></p>
  <p>This is a menu that I have created with Overlay and Flow Widget.
  Base on this menu I want each DropdowItem have a piece of(image) a bigger widget. Once the animation ending, the real widget take place.
  What I've done so far:</p>
  <pre><code>Future&lt;Uint8List?&gt; capturePng(GlobalKey globalKey) async {
    if(globalKey.currentContext == null){
      return null;
    }
    RenderRepaintBoundary boundary =
        globalKey.currentContext!.findRenderObject() as RenderRepaintBoundary ;
  
    if (boundary.debugNeedsPaint) {
  
      Timer(const Duration(seconds: 1), () =&gt; capturePng(globalKey));
      return null;
    }
    final image = await boundary.toImage(pixelRatio: 3.0);
    final ByteData? byteData =
        await image.toByteData(format: ui.ImageByteFormat.png);
    var pngBytes = byteData?.buffer.asUint8List();
  
    return pngBytes ;
  }
  </code></pre>
  <p>How to get the height and width of the unint8list?</p>
  <pre><code>    final decodedImage = decodeImage(uint8list);
      if(decodedImage != null){
        final img = copyCrop(decodedImage, 0, (i)*height, width, height);
        menuImage.add(Uint8List.fromList(encodePng(img)));
      }
  </code></pre>
  <p>If you want to know how I made the menu:</p>
  <pre><code>Row(
              mainAxisAlignment: MainAxisAlignment.center,
              children: menuList
                  .map(
                    (e) =&gt; TitleDropdown(
                      text: e.title,
                      key: GlobalKey(),
                      menu: e,
                    ),
                  )
                  .toList(),
            ),
  ------------
  class TitleDropdown extends HookConsumerWidget {
    final String text;
    final Menu menu;
  
    const TitleDropdown(
        {required Key key, required this.text, required this.menu})
        : super(key: key);
  
    @override
    Widget build(BuildContext context,WidgetRef ref) {
      final theme = Theme.of(context);
      final animationController = useAnimationController(duration: const Duration(milliseconds: 400));
      Animation&lt;double&gt; animTween = Tween&lt;double&gt;(
          begin: 0.0, end: 100) //32 le padding
          .animate(CurvedAnimation(
          parent: animationController, curve: Curves.easeInOut));
      return MouseRegion(
        onHover: (onHover) async {
  
          // await Future.delayed(const Duration(milliseconds: 10));
          // ref
          //     .read(menuProvider)
          //     .setOverlayChange(context, menu, text);
        },
        onEnter: (onEnter){
          print(&quot;onEnter&quot;);
          ref
              .read(menuProvider)
              .setOnEnterTitle(context, menu, text);
        },
        onExit: (onExit){
          print(&quot;onExit&quot;);
          // await Future.delayed(const Duration(milliseconds: 10));
          print(onExit.delta);
          if(onExit.delta.dy &gt;0){
            return;
          }
          ref
              .read(menuProvider)
              .setOnExitTitle(context, menu, text);
        },
        // onTap: () {},
        child: Consumer(builder: (context, ref, _) {
          final toggle = ref.watch(menuProvider.select((value) =&gt; value.isHighlight)) &amp;&amp;
              ref.watch(menuProvider.select((value) =&gt; value.onGoingMenu)) == text;
  
          WidgetsBinding.instance!.addPostFrameCallback((_) {
            animTween = Tween&lt;double&gt;(
                begin: 0.0, end: context.size!.width - 32) //32 le padding
                .animate(CurvedAnimation(
                parent: animationController, curve: Curves.easeInOut));
          });
  
          if (toggle) {
            animationController.forward();
          } else {
            animationController.reverse();
          }
  
          return Padding(
            padding: const EdgeInsets.symmetric(horizontal: 16, vertical: 8),
            child: Column(
              children: [
                Text(
                  text,
                  style: theme.textTheme.headline6,
                ),
                AnimatedBuilder(
                  animation: animTween,
                  builder: (context, _) {
                    return Container(
                      color: theme.colorScheme.onBackground,
                      height: 1,
                      width: animTween.value,
                    );
                  },
                )
              ],
            ),
          );
        }),
      );
    }
  }
  
  </code></pre>
  <p>DropDown class</p>
  <pre><code>class DropDown extends HookConsumerWidget {
    final double itemHeight;
    final Menu menu;
  
    const DropDown(
        {required Key key, required this.itemHeight, required this.menu})
        : super(key: key);
  
    @override
    Widget build(BuildContext context, WidgetRef ref) {
      final l = menu.subTitle.length;
      final h = heightOfOneItem * l.toDouble();
  
      final theme = Theme.of(context);
      final anim = useAnimationController(
        duration: const Duration(milliseconds: 250),
      );
      anim.forward();
  
      final primaryColor = Theme.of(context).colorScheme.primaryVariant;
      return MouseRegion(
        onEnter: (PointerEnterEvent pointerEnterEvent) {
          ref.read(menuProvider).isEnterDropDown = true;
        },
        onExit: (PointerExitEvent pointerExitEvent) {
          ref.read(menuProvider).closeDropDown();
        },
        child: SizedBox(
          height: h,
          width: MediaQuery.of(context).size.width,
          child: AnimatedAppear(
            children: &lt;Widget&gt;[
  
              for(int i = 0 ; i&lt;menu.subTitle.length;i++)
                Row(
                  children: [
                    Expanded(
                      child: Material(
                        color: Colors.transparent,
                        child: SizedBox(
                          height: heightOfOneItem,
                          child: DropDownItem(
                            text: menu.subTitle[i].title,
                            key: GlobalKey(),
                            isFirstItem: menu.subTitle.first == menu.subTitle[i],
                            isLastItem: menu.subTitle.last == menu.subTitle[i],
                            iconData: Icons.person_outline, index: i,
                          ),
                        ),
                      ),
                    ),
                  ],
                ),
  
  
            ],
          ),
        ),
      );
    }
  }
  </code></pre>
  <p>DropDownItem class</p>
  <pre><code>class DropDownItem extends StatefulWidget {
    final String text;
    final int index;
    final IconData iconData;
    final bool isFirstItem;
    final bool isLastItem;
  
    const DropDownItem(
        {Key? key,
        required this.text,
        required this.iconData,
        this.isFirstItem = false,
        this.isLastItem = false, required this.index})
        : super(key: key);
  
    @override
    _DropDownItemState createState() =&gt; _DropDownItemState();
  }
  
  class _DropDownItemState extends State&lt;DropDownItem&gt; {
    late bool isSelected;
  
    @override
    void initState() {
      super.initState();
      isSelected = false;
    }
  
    @override
    Widget build(BuildContext context) {
      final theme = Theme.of(context);
      return InkWell(
        onTap: () {},
        onHover: (onHover) {
          setState(() {
            isSelected = !isSelected;
          });
        },
        child: Container(
          decoration: BoxDecoration(
            borderRadius: BorderRadius.only(
              topRight:
                  widget.isFirstItem ? const Radius.circular(8) : Radius.zero,
              bottomLeft:
                  widget.isLastItem ? const Radius.circular(8) : Radius.zero,
              bottomRight:
                  widget.isLastItem ? const Radius.circular(8) : Radius.zero,
            ),
            color: isSelected
                ? theme.colorScheme.primary
                : theme.colorScheme.primaryVariant,
          ),
  
          child: Consumer(
            builder: (context, ref,_) {
              return FittedBox(
                fit: BoxFit.contain,
                alignment: Alignment.centerLeft,
                child: Row(
                  mainAxisSize: MainAxisSize.min,
                  children: &lt;Widget&gt;[
                    Text(
                      widget.text,
                      style: theme.textTheme.headline5,
                    ),
                    Icon(
                      widget.iconData,
                      color: theme.colorScheme.onPrimary,
                    ),
                  ],
                ),
                // Image.memory(ref.watch(menuProvider).menuImage[widget.index]),
              );
            }
          ),
        ),
      );
    }
  }
  </code></pre>
  <p>AnimatedAppear class</p>
  <pre><code>class AnimatedAppear extends HookWidget {
    final List&lt;Widget&gt; children;
    final bool isForward;
  
    const AnimatedAppear(
        {required this.children, this.isForward = true, Key? key})
        : super(key: key);
  
    @override
    Widget build(BuildContext context) {
      final animationController = useAnimationController(
        duration: const Duration(milliseconds: 1000),
      );
      if (isForward) {
        animationController.forward();
      } else {
        animationController.reverse();
      }
  
      return Flow(
        delegate: FlowMenuDelegate(animationController: animationController),
        children: children,
      );
    }
  }
  
  class FlowMenuDelegate extends FlowDelegate {
    final AnimationController animationController;
  
    FlowMenuDelegate({required this.animationController})
        : super(repaint: animationController);
  
    @override
    void paintChildren(FlowPaintingContext context) {
      final size = context.size;
  
      final xStart = size.width / 2;
      final yStart = -size.height /2 + arrowHeight/2;
  
  
      const margin = 0;
      final animation = Tween(begin: 0.0, end: 1.0).animate(
          CurvedAnimation(parent: animationController, curve: Curves.elasticOut));
      for (int i = context.childCount - 1; i &gt;= 0; i--) {
        final childSize = context.getChildSize(i);
  
        if (childSize == null) {
          continue;
        }
  
        final dy = yStart + (margin + heightOfOneItem) * (i) * animation.value;
  
        context.paintChild(i,
            transform: Matrix4.translationValues(
                xStart - childSize.width / 2, dy.toDouble(), 0),
            opacity: animation.value);
      }
    }
  
    @override
    bool shouldRepaint(covariant FlowDelegate oldDelegate) =&gt; false;
  }
  
  </code></pre>
  <p>MenuController class</p>
  <pre><code>class MenuController extends ChangeNotifier {
    bool isEnterDropDown = false;
    OverlayEntry? floatingDropdown;
    bool isDropdownOpened = false;
    bool isHighlight = false;
    late double height, width, xPosition, yPosition;
    String onGoingMenu = '';
    List&lt;GlobalKey&gt; globalKey = [];
    List&lt;Uint8List&gt; menuImage = [];
    List&lt;Image&gt; myImage = [];
  
  
  
    void setOnEnterTitle(BuildContext context, Menu menu, String text) {
      print(&quot;!!setOnEnterTitle!!&quot;);
      if (!isDropdownOpened) {
        onGoingMenu = text;
        if (menu.subTitle.isEmpty) {
          isHighlight = !isHighlight; //Juste pour changer Title
  
          notifyListeners();
          return;
        }
  
        findDropdownData(context);
  
        floatingDropdown = _createFloatingDropdown(menu);
  
        Overlay.of(context)!.insert(floatingDropdown!);
  
        isDropdownOpened = true;
        isHighlight = true;
        isEnterDropDown = false;
        notifyListeners();
      } else if (isEnterDropDown) {
        isEnterDropDown = false;
        notifyListeners();
      }
  
    }
  
    void setOnExitTitle(BuildContext context, Menu menu, String text) {
      if (isEnterDropDown) {
        return;
      }
      if (!isEnterDropDown &amp;&amp; isDropdownOpened &amp;&amp; floatingDropdown != null) {
        if (menu.subTitle.isEmpty) {
          isHighlight = false; //Juste pour changer Title
  
          notifyListeners();
          return;
        }
        floatingDropdown!.remove();
        isHighlight = false;
        isDropdownOpened = false;
        onGoingMenu = '';
      }
      notifyListeners();
    }
    void findDropdownData(BuildContext context) {
      final RenderBox renderBox = context.findRenderObject()! as RenderBox;
  
      height = renderBox.size.height + 3;
      width = renderBox.size.width;
      final Offset offset = renderBox.localToGlobal(Offset.zero);
      // xPosition = offset.dx;
      xPosition = 0;
      yPosition = offset.dy;
    }
  
    OverlayEntry _createFloatingDropdown(Menu menu) {
      return OverlayEntry(builder: (context) {
        return Stack(
          children: [
            Positioned(
              top: yPosition + height - 4,
              child: IgnorePointer(
                child: Container(
                  color: Colors.black45,
                  width: MediaQuery.of(context).size.width,
                  height: MediaQuery.of(context).size.height,
                ),
              ),
            ),
            Positioned(
              right: kPaddingHorizontal,
              left: kPaddingHorizontal,
              top: yPosition + height - 4,
              //-4 pour que le curseur sois dans le dropdown
              child: DropDown(
                itemHeight: height,
                key: GlobalKey(),
                menu: menu,
              ),
            )
          ],
        );
      });
    }
  
    void closeDropDown() {
      if (floatingDropdown == null) {
        return;
      }
      if (isDropdownOpened &amp;&amp; isEnterDropDown) {
        floatingDropdown!.remove();
        isDropdownOpened = false;
        isHighlight = false;
        isEnterDropDown = false;
        notifyListeners();
      }
    }
  }
  </code></pre>