From 06ac3bc29ebf46a595573925f20d8075f13421a6 Mon Sep 17 00:00:00 2001 From: Jaylen Bian Date: Mon, 26 Jul 2021 18:37:27 +0800 Subject: [PATCH] [infra_ui][overlay] Impl new overlay page and route skeleton --- .../src/overlay/overlay_layout_delegate.dart | 45 ++++----- .../lib/src/overlay/overlay_manager.dart | 19 ++++ .../lib/src/overlay/overlay_pannel.dart | 31 ++++-- .../lib/src/overlay/overlay_route.dart | 96 +++++++++++++++++++ 4 files changed, 160 insertions(+), 31 deletions(-) create mode 100644 app_flowy/packages/flowy_infra_ui/lib/src/overlay/overlay_route.dart diff --git a/app_flowy/packages/flowy_infra_ui/lib/src/overlay/overlay_layout_delegate.dart b/app_flowy/packages/flowy_infra_ui/lib/src/overlay/overlay_layout_delegate.dart index 19ec056f70..bd08ec7011 100644 --- a/app_flowy/packages/flowy_infra_ui/lib/src/overlay/overlay_layout_delegate.dart +++ b/app_flowy/packages/flowy_infra_ui/lib/src/overlay/overlay_layout_delegate.dart @@ -1,50 +1,47 @@ +import 'dart:math' as math; import 'dart:ui'; import 'package:flutter/material.dart'; + +import 'overlay_route.dart'; import 'overlay_basis.dart'; class OverlayLayoutDelegate extends SingleChildLayoutDelegate { OverlayLayoutDelegate({ - required this.anchorRect, - required this.targetRect, + required this.route, + required this.padding, + required this.anchorPosition, required this.anchorDirection, - required this.safeAreaEnabled, - required this.insets, }); + final OverlayPannelRoute route; + final EdgeInsets padding; final AnchorDirection anchorDirection; - final bool safeAreaEnabled; - final EdgeInsets insets; - final Rect anchorRect; - final Rect targetRect; + final Offset anchorPosition; @override bool shouldRelayout(OverlayLayoutDelegate oldDelegate) { - return anchorRect != oldDelegate.anchorRect || - insets != oldDelegate.insets || - safeAreaEnabled != oldDelegate.safeAreaEnabled || - anchorDirection != oldDelegate.anchorDirection; + return anchorPosition != oldDelegate.anchorPosition || anchorDirection != oldDelegate.anchorDirection; } @override Offset getPositionForChild(Size size, Size childSize) { - // calculate the pannel maximum available rect - var pannelRect = Rect.fromLTWH(0, 0, size.width, size.height); - pannelRect = insets.deflateRect(pannelRect); - // apply safearea - if (safeAreaEnabled) { - final safeArea = MediaQueryData.fromWindow(window).padding; - pannelRect = safeArea.deflateRect(pannelRect); - } - - // clip pannel rect - // TODO: junlin - calculate child position return Offset.zero; } @override BoxConstraints getConstraintsForChild(BoxConstraints constraints) { - return constraints.loosen(); + double maxHeight = math.max( + 0.0, + math.min(route.maxHeight, constraints.maxHeight - padding.top - padding.bottom), + ); + double width = math.min(route.maxWidth, constraints.maxWidth); + return BoxConstraints( + minHeight: 0.0, + maxHeight: maxHeight, + minWidth: width, + maxWidth: width, + ); } } diff --git a/app_flowy/packages/flowy_infra_ui/lib/src/overlay/overlay_manager.dart b/app_flowy/packages/flowy_infra_ui/lib/src/overlay/overlay_manager.dart index b49bbf5cc3..99a0f81bac 100644 --- a/app_flowy/packages/flowy_infra_ui/lib/src/overlay/overlay_manager.dart +++ b/app_flowy/packages/flowy_infra_ui/lib/src/overlay/overlay_manager.dart @@ -50,3 +50,22 @@ class OverlayManagerState extends State { return Container(); } } + + +// TODO: Impl show method + // void show(BuildContext context) { + // assert(_overlayRoute == null, 'Can\'t push single overlay twice.'); + // final NavigatorState navigator = Navigator.of(context); + // final RenderBox renderBox = context.findRenderObject()! as RenderBox; + + // _overlayRoute = OverlayPannelRoute( + // anchorDirection: widget.anchorDirection, + // barrierLabel: MaterialLocalizations.of(context).modalBarrierDismissLabel, + // anchorPosition: widget.anchorPosition, + // maxWidth: widget.maxWidth ?? renderBox.size.width, + // maxHeight: widget.maxHeight ?? renderBox.size.height, + // ); + // _createRouteAnimation(_overlayRoute!); + + // navigator.push(_overlayRoute!); + // } \ No newline at end of file diff --git a/app_flowy/packages/flowy_infra_ui/lib/src/overlay/overlay_pannel.dart b/app_flowy/packages/flowy_infra_ui/lib/src/overlay/overlay_pannel.dart index 2317d54dfb..a3b4b83331 100644 --- a/app_flowy/packages/flowy_infra_ui/lib/src/overlay/overlay_pannel.dart +++ b/app_flowy/packages/flowy_infra_ui/lib/src/overlay/overlay_pannel.dart @@ -1,17 +1,25 @@ import 'dart:ui' show window; +import 'package:flowy_infra_ui/src/overlay/overlay_route.dart'; import 'package:flutter/material.dart'; import 'overlay_basis.dart'; -import 'overlay_layout_delegate.dart'; class OverlayPannel extends StatefulWidget { const OverlayPannel({ Key? key, this.focusNode, + this.padding = EdgeInsets.zero, + this.anchorDirection = AnchorDirection.topRight, + required this.anchorPosition, + required this.route, }) : super(key: key); final FocusNode? focusNode; + final EdgeInsetsGeometry padding; + final AnchorDirection anchorDirection; + final Offset anchorPosition; + final OverlayPannelRoute route; @override _OverlayPannelState createState() => _OverlayPannelState(); @@ -22,10 +30,25 @@ class _OverlayPannelState extends State with WidgetsBindingObserv FocusNode? get focusNode => widget.focusNode ?? _internalNode; late FocusHighlightMode _focusHighlightMode; bool _hasPrimaryFocus = false; + late CurvedAnimation _fadeOpacity; + late CurvedAnimation _resize; + OverlayPannelRoute? _overlayRoute; @override void initState() { super.initState(); + _fadeOpacity = CurvedAnimation( + parent: widget.route.animation!, + curve: const Interval(0.0, 0.25), + reverseCurve: const Interval(0.75, 1.0), + ); + _resize = CurvedAnimation( + parent: widget.route.animation!, + curve: const Interval(0.25, 0.5), + reverseCurve: const Threshold(0.0), + ); + + // TODO: junlin - handle focus action or remove it if (widget.focusNode == null) { _internalNode ??= _createFocusNode(); } @@ -85,17 +108,11 @@ class _OverlayPannelState extends State with WidgetsBindingObserv }); } - void _removeOverlayRoute() { - // TODO: junlin - } - // MARK: Layout Orientation _getOrientation(BuildContext context) { Orientation? result = MediaQuery.maybeOf(context)?.orientation; if (result == null) { - // If there's no MediaQuery, then use the window aspect to determine - // orientation. final Size size = window.physicalSize; result = size.width > size.height ? Orientation.landscape : Orientation.portrait; } diff --git a/app_flowy/packages/flowy_infra_ui/lib/src/overlay/overlay_route.dart b/app_flowy/packages/flowy_infra_ui/lib/src/overlay/overlay_route.dart new file mode 100644 index 0000000000..6efe4fb74f --- /dev/null +++ b/app_flowy/packages/flowy_infra_ui/lib/src/overlay/overlay_route.dart @@ -0,0 +1,96 @@ +import 'package:flowy_infra_ui/src/overlay/overlay_pannel.dart'; +import 'package:flutter/material.dart'; + +import 'overlay_basis.dart'; +import 'overlay_layout_delegate.dart'; + +class _OverlayRouteResult {} + +const Duration _kOverlayDurationDuration = Duration(milliseconds: 500); + +class OverlayPannelRoute extends PopupRoute<_OverlayRouteResult> { + final EdgeInsetsGeometry padding; + final AnchorDirection anchorDirection; + final Offset anchorPosition; + final double maxWidth; + final double maxHeight; + + OverlayPannelRoute({ + this.padding = EdgeInsets.zero, + required this.anchorDirection, + this.barrierColor, + required this.barrierLabel, + required this.anchorPosition, + required this.maxWidth, + required this.maxHeight, + }); + + @override + bool get barrierDismissible => true; + + @override + Color? barrierColor; + + @override + String? barrierLabel; + + @override + Duration get transitionDuration => _kOverlayDurationDuration; + + @override + Widget buildPage(BuildContext context, Animation animation, Animation secondaryAnimation) { + return LayoutBuilder(builder: (context, contraints) { + return _OverlayRoutePage( + route: this, + anchorDirection: anchorDirection, + anchorPosition: anchorPosition, + ); + }); + } +} + +class _OverlayRoutePage extends StatelessWidget { + const _OverlayRoutePage({ + Key? key, + required this.route, + this.padding = EdgeInsets.zero, + required this.anchorDirection, + required this.anchorPosition, + }) : super(key: key); + + final OverlayPannelRoute route; + final EdgeInsetsGeometry padding; + final AnchorDirection anchorDirection; + final Offset anchorPosition; + + @override + Widget build(BuildContext context) { + assert(debugCheckHasDirectionality(context)); + final TextDirection? textDirection = Directionality.maybeOf(context); + final OverlayPannel overlayPannel = OverlayPannel( + route: route, + padding: padding, + anchorDirection: anchorDirection, + anchorPosition: anchorPosition, + ); + + return MediaQuery.removePadding( + context: context, + removeTop: true, + removeBottom: true, + removeLeft: true, + removeRight: true, + child: Builder( + builder: (context) => CustomSingleChildLayout( + delegate: OverlayLayoutDelegate( + route: route, + padding: padding.resolve(textDirection), + anchorPosition: anchorPosition, + anchorDirection: anchorDirection, + ), + child: overlayPannel, + ), + ), + ); + } +}