diff --git a/example/lib/main.dart b/example/lib/main.dart index 9cfeb5d..b64bb31 100644 --- a/example/lib/main.dart +++ b/example/lib/main.dart @@ -31,7 +31,7 @@ class MyHomePage extends StatefulWidget { } class _MyHomePageState extends State { - final OverlayPortalController _controller = OverlayPortalController(); + final ListenableOverlayPortalController _controller = ListenableOverlayPortalController(); MenuPosition position = MenuPosition.bottomStart; bool dismissOnTapOutside = true; bool useButtonSize = true; diff --git a/lib/src/widget/raw.dart b/lib/src/widget/raw.dart index 70576a9..231bf79 100644 --- a/lib/src/widget/raw.dart +++ b/lib/src/widget/raw.dart @@ -1,5 +1,3 @@ -import 'dart:developer'; - import 'package:flutter/material.dart'; typedef ButtonBuilder = Widget Function( @@ -32,7 +30,7 @@ class RawFlexDropDown extends StatefulWidget { this.dismissOnTapOutside = true, }); - final OverlayPortalController controller; + final ListenableOverlayPortalController controller; final ButtonBuilder buttonBuilder; final MenuBuilder menuBuilder; @@ -43,40 +41,75 @@ class RawFlexDropDown extends StatefulWidget { State createState() => _RawFlexDropDownState(); } -class _RawFlexDropDownState extends State { +class _RawFlexDropDownState extends State with SingleTickerProviderStateMixin { final _link = LayerLink(); /// width of the button after the widget rendered double? _buttonWidth; + late AnimationController _animationController; + late Animation _fadeAnimation; + + @override + void initState() { + super.initState(); + _animationController = AnimationController( + vsync: this, + duration: const Duration(milliseconds: 200), + ); + _fadeAnimation = CurvedAnimation( + parent: _animationController, + curve: Curves.easeIn, + ); + + widget.controller.addListener((isVisible) { + if (isVisible) { + _animationController.forward(); + } else { + _animationController.reverse(); + } + }); + } + + @override + void dispose() { + _animationController.dispose(); + super.dispose(); + } + @override Widget build(BuildContext context) { final direction = Directionality.of(context); return CompositedTransformTarget( link: _link, - child: OverlayPortal( + child: OverlayPortal.targetsRootOverlay( controller: widget.controller, overlayChildBuilder: (BuildContext context) { Widget menu = widget.menuBuilder(context, _buttonWidth); - - if (widget.dismissOnTapOutside) { - log('dismissOnTapOutside'); - menu = TapRegion( - onTapOutside: (event) { - widget.controller.hide(); - }, - child: menu, - ); - } - - return CompositedTransformFollower( - link: _link, - targetAnchor: _createTargetAnchor(direction), - followerAnchor: _createFollowerAnchor(direction), - showWhenUnlinked: false, - child: Align( - alignment: _createAlignment(), - child: menu, + return FadeTransition( + opacity: _fadeAnimation, + child: Stack( + children: [ + if (widget.dismissOnTapOutside) + Positioned.fill( + child: ModalBarrier( + color: Colors.transparent, + dismissible: true, + barrierSemanticsDismissible: true, + onDismiss: widget.controller.hide, + ), + ), + CompositedTransformFollower( + link: _link, + targetAnchor: _createTargetAnchor(direction), + followerAnchor: _createFollowerAnchor(direction), + showWhenUnlinked: false, + child: Align( + alignment: _createAlignment(), + child: menu, + ), + ), + ], ), ); }, @@ -132,3 +165,43 @@ class _RawFlexDropDownState extends State { widget.controller.toggle(); } } + +typedef OverlayVisibilityChangedCallback = void Function(bool isShowing); + +class ListenableOverlayPortalController extends OverlayPortalController { + ListenableOverlayPortalController(); + + final _listeners = []; + + void addListener(OverlayVisibilityChangedCallback listener) { + _listeners.add(listener); + } + + void removeListener(OverlayVisibilityChangedCallback listener) { + _listeners.remove(listener); + } + + void _notifyListeners() { + for (final listener in _listeners) { + listener(super.isShowing); + } + } + + @override + void hide() { + super.hide(); + _notifyListeners(); + } + + @override + void show() { + super.show(); + _notifyListeners(); + } + + @override + void toggle() { + super.toggle(); + _notifyListeners(); + } +} \ No newline at end of file