import 'package:flutter/material.dart'; import 'package:oc_front/core/sections/header/menu.dart'; import 'package:oc_front/core/services/router.dart'; import 'package:oc_front/utils/arrow_clipper.dart'; class TooltipWidget extends StatefulWidget { int index = -1; Size? buttonSize; List labels; Offset? buttonPosition; TooltipWidget ({ Key? key, required this.labels, required this.index, required this.buttonSize, required this.buttonPosition }): super(key: key); @override TooltipWidgetState createState() => TooltipWidgetState(); } class TooltipWidgetState extends State { @override Widget build(BuildContext context) { double minimal = (widget.index < 0 ? 0 : 7 * widget.labels[widget.index].length).toDouble() + 30; return Positioned( top : ((widget.buttonPosition?.dy ?? 1 ) + 11) + (((widget.buttonSize?.height) ?? 1)* 1.5) * (widget.index + 1), left: (widget.buttonPosition?.dx ?? 1) - minimal, child: Container( width: widget.index < 0 ? 0 : (widget.index < 0 ? 0 : minimal), height: (widget.buttonSize?.height ?? 1) - 2, color: Colors.black, child: Text( widget.index < 0 ? "" : widget.labels[widget.index], style: const TextStyle( color: Colors.white, decoration: TextDecoration.none, fontSize: 14, fontWeight: FontWeight.w300), textAlign: TextAlign.center, overflow: TextOverflow.ellipsis ), )); } } GlobalKey headerMenuKey = GlobalKey(); class ClipperMenuWidget extends StatefulWidget { final BorderRadius borderRadius; final Color backgroundColor; final Color iconColor; int index = -1; ClipperMenuWidget({ required this.borderRadius, this.backgroundColor = const Color.fromRGBO(38, 166, 154, 1), this.iconColor = Colors.black, }) : super(key: headerMenuKey); @override // ignore: library_private_types_in_public_api ClipperMenuWidgetState createState() => ClipperMenuWidgetState(); } class ClipperMenuWidgetState extends State with SingleTickerProviderStateMixin { late GlobalKey _key; bool isMenuOpen = false; Offset? buttonPosition; Size? buttonSize; final GlobalKey _tooltipKey = GlobalKey(); OverlayEntry? _overlayEntry; BorderRadius? _borderRadius; AnimationController? _animationController; @override void initState() { _animationController = AnimationController( vsync: this, duration: const Duration(milliseconds: 250), ); _borderRadius = widget.borderRadius; // BorderRadius.circular(4) super.initState(); } @override void dispose() { _animationController?.dispose(); super.dispose(); } findButton() { if (_key.currentContext != null) { RenderBox renderBox = _key.currentContext?.findRenderObject()! as RenderBox; buttonSize = renderBox.size; buttonPosition = renderBox.localToGlobal(Offset.zero); } } void closeMenu() { try { _overlayEntry?.remove(); _animationController?.reverse(); isMenuOpen = false; } catch (e) { } } void openMenu() { findButton(); _animationController?.forward(); _overlayEntry = _overlayEntryBuilder(); if (_overlayEntry != null) { Overlay.of(context).insert(_overlayEntry!); } isMenuOpen = true; } @override Widget build(BuildContext context) { _key = GlobalKey(); headerMenuKey = widget.key as GlobalKey; return Container( key: _key, padding: const EdgeInsets.all(0), child: IconButton( splashRadius: 4, icon: _animationController == null ? const Icon( Icons.close, ) : AnimatedIcon( icon: AnimatedIcons.menu_close, progress: _animationController!, ), onPressed: () { if (isMenuOpen) { closeMenu(); } else { openMenu(); } }, ), ); } OverlayEntry _overlayEntryBuilder() { var routes = AppRouter.zones.where( (e) => e.path != AppRouter.currentRoute.path && e.label != null && e.icon != null).toList(); return OverlayEntry( builder: (context) { return Stack( children: [ TooltipWidget( key: _tooltipKey, labels: routes.map((e) => e.label!).toList(), buttonPosition: buttonPosition, buttonSize: buttonSize, index: widget.index ), Positioned( top: (buttonPosition?.dy ?? 1 ) + (buttonSize?.height ?? 1), left: buttonPosition?.dx, width: buttonSize?.width, child: Material( color: Colors.transparent, child: Stack( children: [ Align( alignment: Alignment.topCenter, child: ClipPath( clipper: ArrowClipper(), child: Container( width: 17, height: 17, color: widget.backgroundColor ?? Color(0xFFF), ), ), ), Padding( padding: const EdgeInsets.only(top: 15.0), child: Container( height: routes.length * ((buttonSize?.height ?? 1) * 1.5), decoration: BoxDecoration( boxShadow: const [BoxShadow(color: Colors.black54, spreadRadius: 1, blurRadius: 1, offset: Offset(0, 1))], color: widget.backgroundColor, borderRadius: _borderRadius, ), child: Theme( data: ThemeData( iconTheme: IconThemeData( color: widget.iconColor ), ), child: Column( mainAxisSize: MainAxisSize.min, children: List.generate(routes.length, (index) { return GestureDetector( onTap: () { if (index >= 0) { routes[index].go(context, {}); } closeMenu(); }, child: Container( decoration: index == (routes.length - 1) ? null : const BoxDecoration( border: Border( bottom: BorderSide(color: Colors.white ), ), ), width: (buttonSize?.width ?? 1) * 1.5, height: (buttonSize?.height ?? 1) * 1.5, child: MouseRegion( cursor: SystemMouseCursors.click, onEnter: (state) { _tooltipKey.currentState?.setState(() { _tooltipKey.currentState?.widget.index = index; }); }, onExit: (state) { _tooltipKey.currentState?.setState(() { _tooltipKey.currentState?.widget.index = -1; }); }, child: Icon( routes[index].icon, size: 19) ), ), ); }), ), ), ), ), ], ), ), ) ]); }, ); } }