From 4afbe62475ebc649d8b4473e9ba96f8a9075468b Mon Sep 17 00:00:00 2001 From: appflowy Date: Mon, 11 Oct 2021 16:40:08 +0800 Subject: [PATCH] [flutter]:enable add doc with FlowyOverlay --- .../lib/startup/tasks/application_task.dart | 5 +- .../widgets/menu/widget/app/header.dart | 106 +++++++++++++----- .../lib/src/flowy_overlay/flowy_overlay.dart | 58 ++++------ ...erlay_layout_delegate.dart => layout.dart} | 0 .../lib/src/flowy_overlay/list_overlay.dart | 39 +++---- .../lib/style_widget/icon_button.dart | 17 --- 6 files changed, 128 insertions(+), 97 deletions(-) rename app_flowy/packages/flowy_infra_ui/lib/src/flowy_overlay/{overlay_layout_delegate.dart => layout.dart} (100%) diff --git a/app_flowy/lib/startup/tasks/application_task.dart b/app_flowy/lib/startup/tasks/application_task.dart index ba58fcbf74..71aae466e2 100644 --- a/app_flowy/lib/startup/tasks/application_task.dart +++ b/app_flowy/lib/startup/tasks/application_task.dart @@ -20,6 +20,8 @@ class AppWidgetTask extends LaunchTask { } } +final GlobalKey _key = GlobalKey(); + class ApplicationWidget extends StatelessWidget { final Widget child; const ApplicationWidget({ @@ -36,10 +38,11 @@ class ApplicationWidget extends StatelessWidget { setWindowFrame(const Rect.fromLTRB(0, 0, launchWidth, launchWidth / ratio)); final theme = AppTheme.fromType(ThemeType.light); + FlowyOverlayConfig config = FlowyOverlayConfig(barrierColor: theme.bg3.withOpacity(0.3)); return Provider.value( value: theme, child: MaterialApp( - builder: overlayManagerBuilder(), + builder: overlayManagerBuilder(config: config), debugShowCheckedModeBanner: false, theme: theme.themeData, navigatorKey: AppGlobals.rootNavKey, diff --git a/app_flowy/lib/workspace/presentation/widgets/menu/widget/app/header.dart b/app_flowy/lib/workspace/presentation/widgets/menu/widget/app/header.dart index a159fa65f5..c36208fa00 100644 --- a/app_flowy/lib/workspace/presentation/widgets/menu/widget/app/header.dart +++ b/app_flowy/lib/workspace/presentation/widgets/menu/widget/app/header.dart @@ -1,16 +1,19 @@ -import 'package:app_flowy/workspace/application/app/app_bloc.dart'; import 'package:expandable/expandable.dart'; +import 'package:flowy_infra/flowy_icon_data_icons.dart'; +import 'package:flowy_infra/image.dart'; import 'package:flowy_infra/theme.dart'; +import 'package:flowy_infra_ui/flowy_infra_ui.dart'; import 'package:flowy_infra_ui/style_widget/icon_button.dart'; import 'package:flowy_infra_ui/style_widget/text.dart'; import 'package:flowy_infra_ui/widget/spacing.dart'; -import 'package:flowy_infra/flowy_icon_data_icons.dart'; import 'package:flowy_sdk/protobuf/flowy-workspace/app_create.pb.dart'; import 'package:flowy_sdk/protobuf/flowy-workspace/view_create.pb.dart'; import 'package:flutter/material.dart'; import 'package:flutter_bloc/flutter_bloc.dart'; import 'package:styled_widget/styled_widget.dart'; +import 'package:app_flowy/workspace/application/app/app_bloc.dart'; + import 'menu_app.dart'; class MenuAppHeader extends StatelessWidget { @@ -55,37 +58,88 @@ class MenuAppHeader extends StatelessWidget { fontSize: 12, ), )), - - ViewAddButton( - onPressed: () { - debugPrint('add view'); - // FlowyOverlay.of(context) - // .insert(widget: Text('test'), identifier: 'identifier'); + DisclosureButton( + onSelected: (viewType) { + context.read().add(AppEvent.createView("New view", "", viewType)); }, ).padding(right: MenuAppSizes.expandedIconPadding), - // PopupMenuButton( - // iconSize: 16, - // tooltip: 'create new view', - // icon: svg("home/add"), - // padding: EdgeInsets.zero, - // onSelected: (viewType) => _createView(viewType as ViewType, context), - // itemBuilder: (context) => menuItemBuilder()) ], ), ); } +} - List menuItemBuilder() { - return ViewType.values.where((element) => element != ViewType.Blank).map((ty) { - return PopupMenuItem( - value: ty, - child: Row( - children: [Text(ty.name)], - )); - }).toList(); - } +class DisclosureButton extends StatelessWidget { + final Function(ViewType) onSelected; + const DisclosureButton({ + Key? key, + required this.onSelected, + }) : super(key: key); - void _createView(ViewType viewType, BuildContext context) { - context.read().add(AppEvent.createView("New view", "", viewType)); + @override + Widget build(BuildContext context) { + return FlowyIconButton( + width: 16, + onPressed: () { + DisclosureButtonActionList( + anchorContext: context, + onSelected: onSelected, + ).show(context); + }, + icon: svg("home/add"), + ); + } +} + +class DisclosureButtonActionList { + final Function(ViewType) onSelected; + final BuildContext anchorContext; + final String _identifier = 'DisclosureButtonActionList'; + + const DisclosureButtonActionList({required this.anchorContext, required this.onSelected}); + + void show(BuildContext buildContext) { + final items = ViewType.values.where((element) => element != ViewType.Blank).map((ty) { + return CreateItem( + viewType: ty, + onSelected: (viewType) { + FlowyOverlay.of(buildContext).remove(_identifier); + onSelected(viewType); + }); + }).toList(); + + ListOverlay.showWithAnchor( + buildContext, + identifier: _identifier, + itemCount: items.length, + itemBuilder: (context, index) => items[index], + anchorContext: anchorContext, + anchorDirection: AnchorDirection.bottomRight, + maxWidth: 120, + maxHeight: 80, + ); + } +} + +class CreateItem extends StatelessWidget { + final ViewType viewType; + final Function(ViewType) onSelected; + const CreateItem({ + Key? key, + required this.viewType, + required this.onSelected, + }) : super(key: key); + + @override + Widget build(BuildContext context) { + return GestureDetector( + onTap: () { + onSelected(viewType); + }, + child: FlowyText.medium( + viewType.name, + fontSize: 12, + ).padding(horizontal: 10, vertical: 10), + ); } } diff --git a/app_flowy/packages/flowy_infra_ui/lib/src/flowy_overlay/flowy_overlay.dart b/app_flowy/packages/flowy_infra_ui/lib/src/flowy_overlay/flowy_overlay.dart index 992976c88a..2e0486f7cc 100644 --- a/app_flowy/packages/flowy_infra_ui/lib/src/flowy_overlay/flowy_overlay.dart +++ b/app_flowy/packages/flowy_infra_ui/lib/src/flowy_overlay/flowy_overlay.dart @@ -1,7 +1,7 @@ // ignore_for_file: unused_element import 'package:dartz/dartz.dart' show Tuple3; -import 'package:flowy_infra_ui/src/flowy_overlay/overlay_layout_delegate.dart'; +import 'package:flowy_infra_ui/src/flowy_overlay/layout.dart'; import 'package:flutter/material.dart'; /// Specifies how overlay are anchored to the SourceWidget @@ -55,13 +55,21 @@ enum OnBackBehavior { dismiss, } +class FlowyOverlayConfig { + final Color barrierColor; + + FlowyOverlayConfig({required this.barrierColor}); + + const FlowyOverlayConfig.defualt() : barrierColor = Colors.transparent; +} + final GlobalKey _key = GlobalKey(); /// Invoke this method in app generation process -TransitionBuilder overlayManagerBuilder() { +TransitionBuilder overlayManagerBuilder({FlowyOverlayConfig config = const FlowyOverlayConfig.defualt()}) { return (context, child) { assert(child != null, 'Child can\'t be null.'); - return FlowyOverlay(key: _key, child: child!); + return FlowyOverlay(key: _key, child: child!, config: config); }; } @@ -70,50 +78,33 @@ abstract class FlowyOverlayDelegate { } class FlowyOverlay extends StatefulWidget { - const FlowyOverlay({ - Key? key, - required this.child, - this.barrierColor = Colors.transparent, - }) : super(key: key); + const FlowyOverlay({Key? key, required this.child, required this.config}) : super(key: key); final Widget child; - final Color? barrierColor; - - static FlowyOverlayState of( - BuildContext context, { - bool rootOverlay = false, - }) { - FlowyOverlayState? overlayManager; - if (rootOverlay) { - overlayManager = context.findRootAncestorStateOfType() ?? overlayManager; - } else { - overlayManager = overlayManager ?? context.findAncestorStateOfType(); - } + final FlowyOverlayConfig config; + static FlowyOverlayState of(BuildContext context, {bool rootOverlay = false}) { + FlowyOverlayState? state = maybeOf(context, rootOverlay: rootOverlay); assert(() { - if (overlayManager == null) { + if (state == null) { throw FlutterError( 'Can\'t find overlay manager in current context, please check if already wrapped by overlay manager.', ); } return true; }()); - return overlayManager!; + return state!; } - static FlowyOverlayState? maybeOf( - BuildContext context, { - bool rootOverlay = false, - }) { - FlowyOverlayState? overlayManager; + static FlowyOverlayState? maybeOf(BuildContext context, {bool rootOverlay = false}) { + FlowyOverlayState? state; if (rootOverlay) { - overlayManager = context.findRootAncestorStateOfType() ?? overlayManager; + state = context.findRootAncestorStateOfType(); } else { - overlayManager = overlayManager ?? context.findAncestorStateOfType(); + state = context.findAncestorStateOfType(); } - - return overlayManager; + return state; } @override @@ -239,8 +230,7 @@ class FlowyOverlayState extends State { overlay = CustomSingleChildLayout( delegate: OverlayLayoutDelegate( anchorRect: anchorRect, - anchorDirection: - shouldAnchor ? anchorDirection ?? AnchorDirection.rightWithTopAligned : AnchorDirection.custom, + anchorDirection: anchorDirection ?? AnchorDirection.rightWithTopAligned, overlapBehaviour: overlapBehaviour ?? OverlapBehaviour.stretch, ), child: widget, @@ -259,7 +249,7 @@ class FlowyOverlayState extends State { widget.child, if (overlays.isNotEmpty) Container( - color: widget.barrierColor, + color: widget.config.barrierColor, child: GestureDetector( behavior: HitTestBehavior.opaque, onTap: _handleTapOnBackground, diff --git a/app_flowy/packages/flowy_infra_ui/lib/src/flowy_overlay/overlay_layout_delegate.dart b/app_flowy/packages/flowy_infra_ui/lib/src/flowy_overlay/layout.dart similarity index 100% rename from app_flowy/packages/flowy_infra_ui/lib/src/flowy_overlay/overlay_layout_delegate.dart rename to app_flowy/packages/flowy_infra_ui/lib/src/flowy_overlay/layout.dart diff --git a/app_flowy/packages/flowy_infra_ui/lib/src/flowy_overlay/list_overlay.dart b/app_flowy/packages/flowy_infra_ui/lib/src/flowy_overlay/list_overlay.dart index 61d7ec8fe4..1a654b0d92 100644 --- a/app_flowy/packages/flowy_infra_ui/lib/src/flowy_overlay/list_overlay.dart +++ b/app_flowy/packages/flowy_infra_ui/lib/src/flowy_overlay/list_overlay.dart @@ -17,6 +17,26 @@ class ListOverlay extends StatelessWidget { final double maxWidth; final double maxHeight; + @override + Widget build(BuildContext context) { + return Container( + constraints: BoxConstraints.tight(Size(maxWidth, maxHeight)), + decoration: BoxDecoration( + color: Colors.white, + borderRadius: const BorderRadius.all(Radius.circular(6)), + boxShadow: [ + BoxShadow(color: Colors.black.withOpacity(0.1), spreadRadius: 1, blurRadius: 20.0), + ], + ), + child: ListView.builder( + shrinkWrap: true, + itemBuilder: itemBuilder, + itemCount: itemCount, + controller: controller, + ), + ); + } + static void showWithAnchor( BuildContext context, { required String identifier, @@ -77,23 +97,4 @@ class ListOverlay extends StatelessWidget { overlapBehaviour: overlapBehaviour, ); } - - @override - Widget build(BuildContext context) { - return Container( - constraints: BoxConstraints.tight(Size(maxWidth, maxHeight)), - decoration: BoxDecoration( - color: Colors.white, - borderRadius: const BorderRadius.all(Radius.circular(6)), - boxShadow: [ - BoxShadow(color: Colors.black.withOpacity(0.1), spreadRadius: 1, blurRadius: 20.0), - ], - ), - child: ListView.builder( - shrinkWrap: true, - itemBuilder: itemBuilder, - itemCount: itemCount, - ), - ); - } } diff --git a/app_flowy/packages/flowy_infra_ui/lib/style_widget/icon_button.dart b/app_flowy/packages/flowy_infra_ui/lib/style_widget/icon_button.dart index 9d5753b073..0fdaadabe9 100644 --- a/app_flowy/packages/flowy_infra_ui/lib/style_widget/icon_button.dart +++ b/app_flowy/packages/flowy_infra_ui/lib/style_widget/icon_button.dart @@ -48,23 +48,6 @@ class FlowyDropdownButton extends StatelessWidget { } } -class ViewAddButton extends StatelessWidget { - final VoidCallback? onPressed; - const ViewAddButton({ - Key? key, - this.onPressed, - }) : super(key: key); - - @override - Widget build(BuildContext context) { - return FlowyIconButton( - width: 16, - onPressed: onPressed, - icon: svg("home/add"), - ); - } -} - class ViewMoreButton extends StatelessWidget { final VoidCallback? onPressed; const ViewMoreButton({