mirror of
https://github.com/AppFlowy-IO/AppFlowy.git
synced 2024-08-30 18:12:39 +00:00
feat: add popover overlay
This commit is contained in:
parent
ddc394fcd7
commit
4fb760e44c
@ -114,16 +114,14 @@ class FieldEditorPopOver extends StatelessWidget {
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return FlowyPopover(
|
||||
child: Container(
|
||||
constraints: BoxConstraints.loose(const Size(280, 400)),
|
||||
width: 280,
|
||||
height: 400,
|
||||
child: FieldEditor(
|
||||
gridId: gridId,
|
||||
fieldName: fieldName,
|
||||
typeOptionLoader: typeOptionLoader,
|
||||
key: key),
|
||||
));
|
||||
builder: (BuildContext context) {
|
||||
return FieldEditor(
|
||||
gridId: gridId,
|
||||
fieldName: fieldName,
|
||||
typeOptionLoader: typeOptionLoader,
|
||||
key: key);
|
||||
},
|
||||
);
|
||||
}
|
||||
|
||||
static show(
|
||||
|
@ -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 './flowy_popover_layout.dart';
|
||||
|
||||
const _overlayContainerPadding = EdgeInsets.all(12);
|
||||
|
||||
class FlowyPopover extends StatelessWidget {
|
||||
final Widget child;
|
||||
class FlowyPopover extends StatefulWidget {
|
||||
final Widget Function(BuildContext context) builder;
|
||||
final ShapeBorder? shape;
|
||||
final EdgeInsets padding;
|
||||
|
||||
FlowyPopover({Key? key, required this.child, this.shape}) : super(key: key);
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return SimpleDialog(
|
||||
shape: shape ??
|
||||
RoundedRectangleBorder(borderRadius: BorderRadius.circular(8)),
|
||||
children: [Container(padding: _overlayContainerPadding, child: child)],
|
||||
);
|
||||
}
|
||||
FlowyPopover({
|
||||
Key? key,
|
||||
required this.builder,
|
||||
this.shape,
|
||||
this.padding = _overlayContainerPadding,
|
||||
}) : super(key: key);
|
||||
|
||||
static show(
|
||||
BuildContext context, {
|
||||
@ -24,4 +26,34 @@ class FlowyPopover extends StatelessWidget {
|
||||
showDialog(
|
||||
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),
|
||||
)));
|
||||
}
|
||||
}
|
||||
|
@ -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)),
|
||||
);
|
||||
}
|
||||
}
|
Loading…
Reference in New Issue
Block a user