feat: add popover overlay

This commit is contained in:
Vincent Chan
2022-08-26 13:47:49 +08:00
parent ddc394fcd7
commit 4fb760e44c
3 changed files with 303 additions and 22 deletions

View File

@ -114,16 +114,14 @@ class FieldEditorPopOver extends StatelessWidget {
@override @override
Widget build(BuildContext context) { Widget build(BuildContext context) {
return FlowyPopover( return FlowyPopover(
child: Container( builder: (BuildContext context) {
constraints: BoxConstraints.loose(const Size(280, 400)), return FieldEditor(
width: 280, gridId: gridId,
height: 400, fieldName: fieldName,
child: FieldEditor( typeOptionLoader: typeOptionLoader,
gridId: gridId, key: key);
fieldName: fieldName, },
typeOptionLoader: typeOptionLoader, );
key: key),
));
} }
static show( static show(

View File

@ -1,21 +1,23 @@
import 'package:flowy_infra_ui/flowy_infra_ui_web.dart';
import 'package:flowy_infra_ui/style_widget/decoration.dart';
import 'package:flowy_infra/theme.dart';
import 'package:provider/provider.dart';
import 'package:flutter/material.dart'; import 'package:flutter/material.dart';
import './flowy_popover_layout.dart';
const _overlayContainerPadding = EdgeInsets.all(12); const _overlayContainerPadding = EdgeInsets.all(12);
class FlowyPopover extends StatelessWidget { class FlowyPopover extends StatefulWidget {
final Widget child; final Widget Function(BuildContext context) builder;
final ShapeBorder? shape; final ShapeBorder? shape;
final EdgeInsets padding;
FlowyPopover({Key? key, required this.child, this.shape}) : super(key: key); FlowyPopover({
Key? key,
@override required this.builder,
Widget build(BuildContext context) { this.shape,
return SimpleDialog( this.padding = _overlayContainerPadding,
shape: shape ?? }) : super(key: key);
RoundedRectangleBorder(borderRadius: BorderRadius.circular(8)),
children: [Container(padding: _overlayContainerPadding, child: child)],
);
}
static show( static show(
BuildContext context, { BuildContext context, {
@ -24,4 +26,34 @@ class FlowyPopover extends StatelessWidget {
showDialog( showDialog(
barrierColor: Colors.transparent, context: context, builder: builder); barrierColor: Colors.transparent, context: context, builder: builder);
} }
@override
State<FlowyPopover> createState() => _FlowyPopoverState();
}
class _FlowyPopoverState extends State<FlowyPopover> {
final preRenderKey = GlobalKey();
Size? size;
@override
Widget build(BuildContext context) {
final theme =
context.watch<AppTheme?>() ?? AppTheme.fromType(ThemeType.light);
return Material(
type: MaterialType.transparency,
child: CustomSingleChildLayout(
delegate: PopoverLayoutDelegate(
anchorRect: const Rect.fromLTWH(0, 0, 280, 400),
anchorDirection: AnchorDirection.rightWithTopAligned,
overlapBehaviour: OverlapBehaviour.stretch,
),
child: Container(
padding: widget.padding,
constraints: BoxConstraints.loose(const Size(280, 400)),
decoration: FlowyDecoration.decoration(
theme.surface, theme.shadowColor.withOpacity(0.15)),
key: preRenderKey,
child: widget.builder(context),
)));
}
} }

View File

@ -0,0 +1,251 @@
import 'dart:math' as math;
import 'package:flutter/material.dart';
import 'flowy_overlay.dart';
class PopoverLayoutDelegate extends SingleChildLayoutDelegate {
PopoverLayoutDelegate({
required this.anchorRect,
required this.anchorDirection,
required this.overlapBehaviour,
});
final Rect anchorRect;
final AnchorDirection anchorDirection;
final OverlapBehaviour overlapBehaviour;
@override
bool shouldRelayout(PopoverLayoutDelegate oldDelegate) {
return anchorRect != oldDelegate.anchorRect ||
anchorDirection != oldDelegate.anchorDirection ||
overlapBehaviour != oldDelegate.overlapBehaviour;
}
@override
BoxConstraints getConstraintsForChild(BoxConstraints constraints) {
switch (overlapBehaviour) {
case OverlapBehaviour.none:
return constraints.loosen();
case OverlapBehaviour.stretch:
BoxConstraints childConstraints;
switch (anchorDirection) {
case AnchorDirection.topLeft:
childConstraints = BoxConstraints.loose(Size(
anchorRect.left,
anchorRect.top,
));
break;
case AnchorDirection.topRight:
childConstraints = BoxConstraints.loose(Size(
constraints.maxWidth - anchorRect.right,
anchorRect.top,
));
break;
case AnchorDirection.bottomLeft:
childConstraints = BoxConstraints.loose(Size(
anchorRect.left,
constraints.maxHeight - anchorRect.bottom,
));
break;
case AnchorDirection.bottomRight:
childConstraints = BoxConstraints.loose(Size(
constraints.maxWidth - anchorRect.right,
constraints.maxHeight - anchorRect.bottom,
));
break;
case AnchorDirection.center:
childConstraints = BoxConstraints.loose(Size(
constraints.maxWidth,
constraints.maxHeight,
));
break;
case AnchorDirection.topWithLeftAligned:
childConstraints = BoxConstraints.loose(Size(
constraints.maxWidth - anchorRect.left,
anchorRect.top,
));
break;
case AnchorDirection.topWithCenterAligned:
childConstraints = BoxConstraints.loose(Size(
constraints.maxWidth,
anchorRect.top,
));
break;
case AnchorDirection.topWithRightAligned:
childConstraints = BoxConstraints.loose(Size(
anchorRect.right,
anchorRect.top,
));
break;
case AnchorDirection.rightWithTopAligned:
childConstraints = BoxConstraints.loose(Size(
constraints.maxWidth - anchorRect.right,
constraints.maxHeight - anchorRect.top,
));
break;
case AnchorDirection.rightWithCenterAligned:
childConstraints = BoxConstraints.loose(Size(
constraints.maxWidth - anchorRect.right,
constraints.maxHeight,
));
break;
case AnchorDirection.rightWithBottomAligned:
childConstraints = BoxConstraints.loose(Size(
constraints.maxWidth - anchorRect.right,
anchorRect.bottom,
));
break;
case AnchorDirection.bottomWithLeftAligned:
childConstraints = BoxConstraints.loose(Size(
anchorRect.left,
constraints.maxHeight - anchorRect.bottom,
));
break;
case AnchorDirection.bottomWithCenterAligned:
childConstraints = BoxConstraints.loose(Size(
constraints.maxWidth,
constraints.maxHeight - anchorRect.bottom,
));
break;
case AnchorDirection.bottomWithRightAligned:
childConstraints = BoxConstraints.loose(Size(
anchorRect.right,
constraints.maxHeight - anchorRect.bottom,
));
break;
case AnchorDirection.leftWithTopAligned:
childConstraints = BoxConstraints.loose(Size(
anchorRect.left,
constraints.maxHeight - anchorRect.top,
));
break;
case AnchorDirection.leftWithCenterAligned:
childConstraints = BoxConstraints.loose(Size(
anchorRect.left,
constraints.maxHeight,
));
break;
case AnchorDirection.leftWithBottomAligned:
childConstraints = BoxConstraints.loose(Size(
anchorRect.left,
anchorRect.bottom,
));
break;
case AnchorDirection.custom:
childConstraints = constraints.loosen();
break;
default:
throw UnimplementedError();
}
return childConstraints;
}
}
@override
Offset getPositionForChild(Size size, Size childSize) {
Offset position;
switch (anchorDirection) {
case AnchorDirection.topLeft:
position = Offset(
anchorRect.left - childSize.width,
anchorRect.top - childSize.height,
);
break;
case AnchorDirection.topRight:
position = Offset(
anchorRect.right,
anchorRect.top - childSize.height,
);
break;
case AnchorDirection.bottomLeft:
position = Offset(
anchorRect.left - childSize.width,
anchorRect.bottom,
);
break;
case AnchorDirection.bottomRight:
position = Offset(
anchorRect.right,
anchorRect.bottom,
);
break;
case AnchorDirection.center:
position = anchorRect.center;
break;
case AnchorDirection.topWithLeftAligned:
position = Offset(
anchorRect.left,
anchorRect.top - childSize.height,
);
break;
case AnchorDirection.topWithCenterAligned:
position = Offset(
anchorRect.left + anchorRect.width / 2.0 - childSize.width / 2.0,
anchorRect.top - childSize.height,
);
break;
case AnchorDirection.topWithRightAligned:
position = Offset(
anchorRect.right - childSize.width,
anchorRect.top - childSize.height,
);
break;
case AnchorDirection.rightWithTopAligned:
position = Offset(anchorRect.right, anchorRect.top);
break;
case AnchorDirection.rightWithCenterAligned:
position = Offset(
anchorRect.right,
anchorRect.top + anchorRect.height / 2.0 - childSize.height / 2.0,
);
break;
case AnchorDirection.rightWithBottomAligned:
position = Offset(
anchorRect.right,
anchorRect.bottom - childSize.height,
);
break;
case AnchorDirection.bottomWithLeftAligned:
position = Offset(
anchorRect.left,
anchorRect.bottom,
);
break;
case AnchorDirection.bottomWithCenterAligned:
position = Offset(
anchorRect.left + anchorRect.width / 2.0 - childSize.width / 2.0,
anchorRect.bottom,
);
break;
case AnchorDirection.bottomWithRightAligned:
position = Offset(
anchorRect.right - childSize.width,
anchorRect.bottom,
);
break;
case AnchorDirection.leftWithTopAligned:
position = Offset(
anchorRect.left - childSize.width,
anchorRect.top,
);
break;
case AnchorDirection.leftWithCenterAligned:
position = Offset(
anchorRect.left - childSize.width,
anchorRect.top + anchorRect.height / 2.0 - childSize.height / 2.0,
);
break;
case AnchorDirection.leftWithBottomAligned:
position = Offset(
anchorRect.left - childSize.width,
anchorRect.bottom - childSize.height,
);
break;
default:
throw UnimplementedError();
}
return Offset(
math.max(0.0, math.min(size.width - childSize.width, position.dx)),
math.max(0.0, math.min(size.height - childSize.height, position.dy)),
);
}
}