mirror of
https://github.com/AppFlowy-IO/AppFlowy.git
synced 2024-08-30 18:12:39 +00:00
[infra_ui][overlay] Impl new overlay page and route skeleton
This commit is contained in:
parent
440d2940c3
commit
06ac3bc29e
@ -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,
|
||||
);
|
||||
}
|
||||
}
|
||||
|
@ -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!);
|
||||
// }
|
@ -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;
|
||||
}
|
||||
|
@ -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,
|
||||
),
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
Loading…
Reference in New Issue
Block a user