mirror of
https://github.com/AppFlowy-IO/AppFlowy.git
synced 2025-04-24 22:57:12 -04:00
[infra_ui][overlay] (WIP) Temp submit before refactor
This commit is contained in:
parent
ae45e64244
commit
7efbf03030
11 changed files with 192 additions and 52 deletions
|
@ -1,4 +1,6 @@
|
||||||
import 'package:flutter/material.dart';
|
import 'package:flutter/material.dart';
|
||||||
|
|
||||||
|
import '../overlay/overlay_screen.dart';
|
||||||
import '../keyboard/keyboard_screen.dart';
|
import '../keyboard/keyboard_screen.dart';
|
||||||
import 'demo_item.dart';
|
import 'demo_item.dart';
|
||||||
|
|
||||||
|
@ -8,6 +10,7 @@ class HomeScreen extends StatelessWidget {
|
||||||
static List<ListItem> items = [
|
static List<ListItem> items = [
|
||||||
SectionHeaderItem('Widget Demos'),
|
SectionHeaderItem('Widget Demos'),
|
||||||
KeyboardItem(),
|
KeyboardItem(),
|
||||||
|
OverlayItem(),
|
||||||
];
|
];
|
||||||
|
|
||||||
@override
|
@override
|
||||||
|
|
|
@ -0,0 +1,51 @@
|
||||||
|
import 'package:flutter/material.dart';
|
||||||
|
|
||||||
|
import '../home/demo_item.dart';
|
||||||
|
|
||||||
|
class OverlayItem extends DemoItem {
|
||||||
|
@override
|
||||||
|
String buildTitle() => 'Overlay';
|
||||||
|
|
||||||
|
@override
|
||||||
|
void handleTap(BuildContext context) {
|
||||||
|
Navigator.of(context).push(
|
||||||
|
MaterialPageRoute(
|
||||||
|
builder: (context) {
|
||||||
|
return const OverlayScreen();
|
||||||
|
},
|
||||||
|
),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
class OverlayScreen extends StatelessWidget {
|
||||||
|
const OverlayScreen({Key? key}) : super(key: key);
|
||||||
|
|
||||||
|
@override
|
||||||
|
Widget build(BuildContext context) {
|
||||||
|
return Scaffold(
|
||||||
|
appBar: AppBar(
|
||||||
|
title: const Text('Overlay Demo'),
|
||||||
|
),
|
||||||
|
body: Column(
|
||||||
|
children: [
|
||||||
|
Flexible(
|
||||||
|
child: Padding(
|
||||||
|
padding: const EdgeInsets.all(16.0),
|
||||||
|
child: Container(
|
||||||
|
height: 300.0,
|
||||||
|
decoration: BoxDecoration(
|
||||||
|
borderRadius: BorderRadius.circular(15.0),
|
||||||
|
color: Colors.grey[200],
|
||||||
|
),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
ElevatedButton(
|
||||||
|
onPressed: () {},
|
||||||
|
child: const Text('Show Overlay'),
|
||||||
|
),
|
||||||
|
],
|
||||||
|
));
|
||||||
|
}
|
||||||
|
}
|
5
app_flowy/packages/flowy_infra_ui/lib/basis.dart
Normal file
5
app_flowy/packages/flowy_infra_ui/lib/basis.dart
Normal file
|
@ -0,0 +1,5 @@
|
||||||
|
import 'package:flutter/material.dart';
|
||||||
|
|
||||||
|
// MARK: - Shared Builder
|
||||||
|
|
||||||
|
typedef WidgetBuilder = Widget Function();
|
|
@ -1,2 +1,5 @@
|
||||||
|
// Basis
|
||||||
|
export 'basis.dart';
|
||||||
|
|
||||||
// Keyboard
|
// Keyboard
|
||||||
export 'src/keyboard/keyboard_visibility_detector.dart';
|
export 'src/keyboard/keyboard_visibility_detector.dart';
|
||||||
|
|
|
@ -1,2 +1,5 @@
|
||||||
|
// Basis
|
||||||
|
export 'basis.dart';
|
||||||
|
|
||||||
// Keyboard
|
// Keyboard
|
||||||
export 'src/keyboard/keyboard_visibility_detector.dart';
|
export 'src/keyboard/keyboard_visibility_detector.dart';
|
||||||
|
|
|
@ -1,5 +1,3 @@
|
||||||
import 'package:flutter/material.dart';
|
|
||||||
|
|
||||||
/// Specifies how overlay are anchored to the SourceWidget
|
/// Specifies how overlay are anchored to the SourceWidget
|
||||||
enum AnchorDirection {
|
enum AnchorDirection {
|
||||||
// Corner aligned with a corner of the SourceWidget
|
// Corner aligned with a corner of the SourceWidget
|
||||||
|
|
|
@ -0,0 +1,50 @@
|
||||||
|
import 'dart:ui';
|
||||||
|
|
||||||
|
import 'package:flutter/material.dart';
|
||||||
|
import 'overlay_basis.dart';
|
||||||
|
|
||||||
|
class OverlayLayoutDelegate extends SingleChildLayoutDelegate {
|
||||||
|
OverlayLayoutDelegate({
|
||||||
|
required this.anchorRect,
|
||||||
|
required this.targetRect,
|
||||||
|
required this.anchorDirection,
|
||||||
|
required this.safeAreaEnabled,
|
||||||
|
required this.insets,
|
||||||
|
});
|
||||||
|
|
||||||
|
final AnchorDirection anchorDirection;
|
||||||
|
final bool safeAreaEnabled;
|
||||||
|
final EdgeInsets insets;
|
||||||
|
final Rect anchorRect;
|
||||||
|
final Rect targetRect;
|
||||||
|
|
||||||
|
@override
|
||||||
|
bool shouldRelayout(OverlayLayoutDelegate oldDelegate) {
|
||||||
|
return anchorRect != oldDelegate.anchorRect ||
|
||||||
|
insets != oldDelegate.insets ||
|
||||||
|
safeAreaEnabled != oldDelegate.safeAreaEnabled ||
|
||||||
|
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();
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,52 @@
|
||||||
|
import 'package:flutter/material.dart';
|
||||||
|
|
||||||
|
class OverlayManager extends StatefulWidget {
|
||||||
|
const OverlayManager({Key? key}) : super(key: key);
|
||||||
|
|
||||||
|
static OverlayManagerState of(
|
||||||
|
BuildContext context, {
|
||||||
|
bool rootOverlay = false,
|
||||||
|
}) {
|
||||||
|
OverlayManagerState? overlayManager;
|
||||||
|
if (rootOverlay) {
|
||||||
|
overlayManager = context.findRootAncestorStateOfType<OverlayManagerState>() ?? overlayManager;
|
||||||
|
} else {
|
||||||
|
overlayManager = overlayManager ?? context.findAncestorStateOfType<OverlayManagerState>();
|
||||||
|
}
|
||||||
|
|
||||||
|
assert(() {
|
||||||
|
if (overlayManager == null) {
|
||||||
|
throw FlutterError(
|
||||||
|
'Can\'t find overlay manager in current context, please check if already wrapped by overlay manager.',
|
||||||
|
);
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
}());
|
||||||
|
return overlayManager!;
|
||||||
|
}
|
||||||
|
|
||||||
|
static OverlayManagerState? maybeOf(
|
||||||
|
BuildContext context, {
|
||||||
|
bool rootOverlay = false,
|
||||||
|
}) {
|
||||||
|
OverlayManagerState? overlayManager;
|
||||||
|
if (rootOverlay) {
|
||||||
|
overlayManager = context.findRootAncestorStateOfType<OverlayManagerState>() ?? overlayManager;
|
||||||
|
} else {
|
||||||
|
overlayManager = overlayManager ?? context.findAncestorStateOfType<OverlayManagerState>();
|
||||||
|
}
|
||||||
|
|
||||||
|
return overlayManager;
|
||||||
|
}
|
||||||
|
|
||||||
|
@override
|
||||||
|
OverlayManagerState createState() => OverlayManagerState();
|
||||||
|
}
|
||||||
|
|
||||||
|
class OverlayManagerState extends State<OverlayManager> {
|
||||||
|
@override
|
||||||
|
Widget build(BuildContext context) {
|
||||||
|
Navigator.of(context, rootNavigator: true);
|
||||||
|
return Container();
|
||||||
|
}
|
||||||
|
}
|
|
@ -1,43 +1,37 @@
|
||||||
import 'dart:ui';
|
|
||||||
|
|
||||||
import 'package:flutter/material.dart';
|
import 'package:flutter/material.dart';
|
||||||
import 'overlay_basis.dart';
|
|
||||||
|
|
||||||
class OverlayPannel extends SingleChildLayoutDelegate {
|
import 'overlay_basis.dart';
|
||||||
OverlayPannel({
|
import 'overlay_layout_delegate.dart';
|
||||||
|
|
||||||
|
class OverlayPannel extends StatelessWidget {
|
||||||
|
const OverlayPannel({
|
||||||
|
Key? key,
|
||||||
|
required this.child,
|
||||||
required this.targetRect,
|
required this.targetRect,
|
||||||
|
required this.anchorRect,
|
||||||
|
this.safeAreaEnabled = true,
|
||||||
this.anchorDirection = AnchorDirection.topRight,
|
this.anchorDirection = AnchorDirection.topRight,
|
||||||
this.safeAreaEnabled = false,
|
|
||||||
this.insets = EdgeInsets.zero,
|
this.insets = EdgeInsets.zero,
|
||||||
});
|
}) : super(key: key);
|
||||||
|
|
||||||
final AnchorDirection anchorDirection;
|
final AnchorDirection anchorDirection;
|
||||||
final bool safeAreaEnabled;
|
final bool safeAreaEnabled;
|
||||||
final EdgeInsets insets;
|
final EdgeInsets insets;
|
||||||
final Rect targetRect;
|
final Rect targetRect;
|
||||||
|
final Rect anchorRect;
|
||||||
|
final Widget child;
|
||||||
|
|
||||||
@override
|
@override
|
||||||
bool shouldRelayout(OverlayPannel oldDelegate) {
|
Widget build(BuildContext context) {
|
||||||
return targetRect != oldDelegate.targetRect ||
|
return CustomSingleChildLayout(
|
||||||
insets != oldDelegate.insets ||
|
delegate: OverlayLayoutDelegate(
|
||||||
safeAreaEnabled != oldDelegate.safeAreaEnabled ||
|
targetRect: targetRect,
|
||||||
anchorDirection != oldDelegate.anchorDirection;
|
anchorRect: anchorRect,
|
||||||
}
|
safeAreaEnabled: safeAreaEnabled,
|
||||||
|
anchorDirection: anchorDirection,
|
||||||
@override
|
insets: insets,
|
||||||
Offset getPositionForChild(Size size, Size childSize) {
|
),
|
||||||
var pannelRect = targetRect;
|
child: child,
|
||||||
if (safeAreaEnabled) {
|
);
|
||||||
final safeArea = MediaQueryData.fromWindow(window).padding;
|
|
||||||
pannelRect = safeArea.deflateRect(pannelRect);
|
|
||||||
}
|
|
||||||
|
|
||||||
// TODO: junlin - calculate child position
|
|
||||||
return Offset.zero;
|
|
||||||
}
|
|
||||||
|
|
||||||
@override
|
|
||||||
BoxConstraints getConstraintsForChild(BoxConstraints constraints) {
|
|
||||||
return constraints.loosen();
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,15 +0,0 @@
|
||||||
import 'package:flutter/material.dart';
|
|
||||||
|
|
||||||
class Overlay extends StatelessWidget {
|
|
||||||
const Overlay({
|
|
||||||
Key? key,
|
|
||||||
this.safeAreaEnabled = true,
|
|
||||||
}) : super(key: key);
|
|
||||||
|
|
||||||
final bool safeAreaEnabled;
|
|
||||||
|
|
||||||
@override
|
|
||||||
Widget build(BuildContext context) {
|
|
||||||
return Container();
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -6,9 +6,7 @@ typedef HoverBuilder = Widget Function(BuildContext context, bool isHovering);
|
||||||
class MouseHoverBuilder extends StatefulWidget {
|
class MouseHoverBuilder extends StatefulWidget {
|
||||||
final bool isClickable;
|
final bool isClickable;
|
||||||
|
|
||||||
const MouseHoverBuilder(
|
const MouseHoverBuilder({Key? key, required this.builder, this.isClickable = false}) : super(key: key);
|
||||||
{Key? key, required this.builder, this.isClickable = false})
|
|
||||||
: super(key: key);
|
|
||||||
|
|
||||||
final HoverBuilder builder;
|
final HoverBuilder builder;
|
||||||
|
|
||||||
|
@ -22,9 +20,7 @@ class _MouseHoverBuilderState extends State<MouseHoverBuilder> {
|
||||||
@override
|
@override
|
||||||
Widget build(BuildContext context) {
|
Widget build(BuildContext context) {
|
||||||
return MouseRegion(
|
return MouseRegion(
|
||||||
cursor: widget.isClickable
|
cursor: widget.isClickable ? SystemMouseCursors.click : SystemMouseCursors.basic,
|
||||||
? SystemMouseCursors.click
|
|
||||||
: SystemMouseCursors.basic,
|
|
||||||
onEnter: (p) => setOver(true),
|
onEnter: (p) => setOver(true),
|
||||||
onExit: (p) => setOver(false),
|
onExit: (p) => setOver(false),
|
||||||
child: widget.builder(context, isOver),
|
child: widget.builder(context, isOver),
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue