Sfoglia il codice sorgente

[infra_ui][overlay] Impl new overlay page and route skeleton

Jaylen Bian 3 anni fa
parent
commit
06ac3bc29e

+ 21 - 24
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,
+    );
   }
 }

+ 19 - 0
app_flowy/packages/flowy_infra_ui/lib/src/overlay/overlay_manager.dart

@@ -50,3 +50,22 @@ class OverlayManagerState extends State<OverlayManager> {
     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!);
+  // }

+ 24 - 7
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<OverlayPannel> 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<OverlayPannel> 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;
     }

+ 96 - 0
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<double> animation, Animation<double> 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,
+        ),
+      ),
+    );
+  }
+}