diff --git a/frontend/appflowy_flutter/lib/mobile/presentation/base/app_bar_actions.dart b/frontend/appflowy_flutter/lib/mobile/presentation/base/app_bar_actions.dart index 95647cc460..8e71db8768 100644 --- a/frontend/appflowy_flutter/lib/mobile/presentation/base/app_bar_actions.dart +++ b/frontend/appflowy_flutter/lib/mobile/presentation/base/app_bar_actions.dart @@ -85,6 +85,39 @@ class AppBarDoneButton extends StatelessWidget { } } +class AppBarFilledDoneButton extends StatelessWidget { + const AppBarFilledDoneButton({super.key, required this.onTap}); + + final VoidCallback onTap; + + @override + Widget build(BuildContext context) { + return Padding( + padding: const EdgeInsets.fromLTRB(8, 4, 8, 8), + child: TextButton( + style: TextButton.styleFrom( + padding: const EdgeInsets.symmetric(horizontal: 12, vertical: 5), + shape: RoundedRectangleBorder( + borderRadius: BorderRadius.circular(10), + ), + elevation: 0, + visualDensity: VisualDensity.compact, + tapTargetSize: MaterialTapTargetSize.shrinkWrap, + enableFeedback: true, + backgroundColor: Theme.of(context).primaryColor, + ), + onPressed: onTap, + child: FlowyText.medium( + LocaleKeys.button_done.tr(), + fontSize: 16, + color: Theme.of(context).colorScheme.onPrimary, + overflow: TextOverflow.ellipsis, + ), + ), + ); + } +} + class AppBarMoreButton extends StatelessWidget { const AppBarMoreButton({ super.key, diff --git a/frontend/appflowy_flutter/lib/mobile/presentation/bottom_sheet/bottom_sheet.dart b/frontend/appflowy_flutter/lib/mobile/presentation/bottom_sheet/bottom_sheet.dart index dee8578f3c..909fd81cbc 100644 --- a/frontend/appflowy_flutter/lib/mobile/presentation/bottom_sheet/bottom_sheet.dart +++ b/frontend/appflowy_flutter/lib/mobile/presentation/bottom_sheet/bottom_sheet.dart @@ -4,7 +4,6 @@ export 'bottom_sheet_drag_handler.dart'; export 'bottom_sheet_rename_widget.dart'; export 'bottom_sheet_view_item.dart'; export 'bottom_sheet_view_item_body.dart'; -export 'bottom_sheet_view_item_header.dart'; export 'bottom_sheet_view_page.dart'; export 'default_mobile_action_pane.dart'; export 'show_mobile_bottom_sheet.dart'; diff --git a/frontend/appflowy_flutter/lib/mobile/presentation/bottom_sheet/bottom_sheet_view_item_header.dart b/frontend/appflowy_flutter/lib/mobile/presentation/bottom_sheet/bottom_sheet_view_item_header.dart deleted file mode 100644 index 80889f4e58..0000000000 --- a/frontend/appflowy_flutter/lib/mobile/presentation/bottom_sheet/bottom_sheet_view_item_header.dart +++ /dev/null @@ -1,59 +0,0 @@ -import 'package:appflowy_backend/protobuf/flowy-folder/protobuf.dart'; -import 'package:flowy_infra_ui/flowy_infra_ui.dart'; -import 'package:flutter/material.dart'; -import 'package:go_router/go_router.dart'; - -class MobileViewItemBottomSheetHeader extends StatelessWidget { - const MobileViewItemBottomSheetHeader({ - super.key, - required this.view, - required this.showBackButton, - required this.onBack, - }); - - final ViewPB view; - final bool showBackButton; - final VoidCallback onBack; - - @override - Widget build(BuildContext context) { - return Row( - mainAxisAlignment: MainAxisAlignment.spaceBetween, - children: [ - // back button, - showBackButton - ? InkWell( - onTap: onBack, - child: const Padding( - padding: EdgeInsets.symmetric(horizontal: 8.0), - child: Icon( - Icons.arrow_back_ios_new_rounded, - size: 24.0, - ), - ), - ) - : FlowyButton( - useIntrinsicWidth: true, - text: const Icon( - Icons.close, - ), - margin: EdgeInsets.zero, - onTap: () { - context.pop(); - }, - ), - // title - ConstrainedBox( - constraints: BoxConstraints( - maxWidth: MediaQuery.of(context).size.width * 0.6, - ), - child: FlowyText.medium( - view.name, - overflow: TextOverflow.ellipsis, - ), - ), - const HSpace(24.0), - ], - ); - } -} diff --git a/frontend/appflowy_flutter/lib/mobile/presentation/bottom_sheet/show_mobile_bottom_sheet.dart b/frontend/appflowy_flutter/lib/mobile/presentation/bottom_sheet/show_mobile_bottom_sheet.dart index 34b0bb26a3..3767f1cadf 100644 --- a/frontend/appflowy_flutter/lib/mobile/presentation/bottom_sheet/show_mobile_bottom_sheet.dart +++ b/frontend/appflowy_flutter/lib/mobile/presentation/bottom_sheet/show_mobile_bottom_sheet.dart @@ -7,10 +7,12 @@ import 'package:flutter/material.dart'; Future showMobileBottomSheet( BuildContext context, { required WidgetBuilder builder, + bool useSafeArea = true, bool isDragEnabled = true, bool showDragHandle = false, bool showHeader = false, // this field is only used if showHeader is true + bool showBackButton = false, bool showCloseButton = false, // this field is only used if showHeader is true String title = '', @@ -20,7 +22,7 @@ Future showMobileBottomSheet( bool useRootNavigator = false, ShapeBorder? shape, // the padding of the content, the padding of the header area is fixed - EdgeInsets padding = const EdgeInsets.all(0.0), + EdgeInsets padding = EdgeInsets.zero, Color? backgroundColor, BoxConstraints? constraints, Color? barrierColor, @@ -32,10 +34,11 @@ Future showMobileBottomSheet( double maxChildSize = 0.8, double initialChildSize = 0.51, }) async { - assert(() { - if (showCloseButton || title.isNotEmpty) assert(showHeader); - return true; - }()); + assert( + showHeader || + title.isEmpty && !showCloseButton && !showBackButton && !showDoneButton, + ); + assert(!(showCloseButton && showBackButton)); shape ??= const RoundedRectangleBorder( borderRadius: BorderRadius.vertical( @@ -83,6 +86,7 @@ Future showMobileBottomSheet( children.add( _Header( showCloseButton: showCloseButton, + showBackButton: showBackButton, showDoneButton: showDoneButton, title: title, ), @@ -147,23 +151,30 @@ Future showMobileBottomSheet( VSpace(MediaQuery.of(context).padding.bottom == 0 ? 28.0 : 16.0), ); - return SafeArea( - child: Column( - mainAxisSize: MainAxisSize.min, - children: children, - ), - ); + return useSafeArea + ? SafeArea( + child: Column( + mainAxisSize: MainAxisSize.min, + children: children, + ), + ) + : Column( + mainAxisSize: MainAxisSize.min, + children: children, + ); }, ); } class _Header extends StatelessWidget { const _Header({ + required this.showBackButton, required this.showCloseButton, required this.title, required this.showDoneButton, }); + final bool showBackButton; final bool showCloseButton; final String title; final bool showDoneButton; @@ -176,6 +187,11 @@ class _Header extends StatelessWidget { height: 44.0, // the height of the header area is fixed child: Stack( children: [ + if (showBackButton) + const Align( + alignment: Alignment.centerLeft, + child: AppBarBackButton(), + ), if (showCloseButton) const Align( alignment: Alignment.centerLeft, diff --git a/frontend/appflowy_flutter/lib/mobile/presentation/database/field/mobile_field_bottom_sheets.dart b/frontend/appflowy_flutter/lib/mobile/presentation/database/field/mobile_field_bottom_sheets.dart index 2388d771cc..84872ff296 100644 --- a/frontend/appflowy_flutter/lib/mobile/presentation/database/field/mobile_field_bottom_sheets.dart +++ b/frontend/appflowy_flutter/lib/mobile/presentation/database/field/mobile_field_bottom_sheets.dart @@ -128,14 +128,17 @@ void showQuickEditField( /// Display a list of fields in the current database that users can choose from. Future showFieldPicker( BuildContext context, + String title, String? selectedFieldId, FieldController fieldController, bool Function(FieldInfo fieldInfo) filterBy, ) { return showMobileBottomSheet( context, + showDivider: false, builder: (context) { return MobileFieldPickerList( + title: title, selectedFieldId: selectedFieldId, fieldController: fieldController, filterBy: filterBy, diff --git a/frontend/appflowy_flutter/lib/mobile/presentation/database/field/mobile_field_picker_list.dart b/frontend/appflowy_flutter/lib/mobile/presentation/database/field/mobile_field_picker_list.dart index eaf12c0ad8..471b1a4205 100644 --- a/frontend/appflowy_flutter/lib/mobile/presentation/database/field/mobile_field_picker_list.dart +++ b/frontend/appflowy_flutter/lib/mobile/presentation/database/field/mobile_field_picker_list.dart @@ -1,11 +1,10 @@ import 'package:appflowy/generated/flowy_svgs.g.dart'; -import 'package:appflowy/generated/locale_keys.g.dart'; +import 'package:appflowy/mobile/presentation/base/app_bar_actions.dart'; import 'package:appflowy/mobile/presentation/widgets/flowy_option_tile.dart'; import 'package:appflowy/plugins/base/drag_handler.dart'; import 'package:appflowy/plugins/database/application/field/field_controller.dart'; import 'package:appflowy/plugins/database/application/field/field_info.dart'; import 'package:appflowy/util/field_type_extension.dart'; -import 'package:easy_localization/easy_localization.dart'; import 'package:flowy_infra_ui/flowy_infra_ui.dart'; import 'package:flutter/material.dart'; import 'package:go_router/go_router.dart'; @@ -13,11 +12,13 @@ import 'package:go_router/go_router.dart'; class MobileFieldPickerList extends StatefulWidget { MobileFieldPickerList({ super.key, + required this.title, required this.selectedFieldId, required FieldController fieldController, required bool Function(FieldInfo fieldInfo) filterBy, }) : fields = fieldController.fieldInfos.where(filterBy).toList(); + final String title; final String? selectedFieldId; final List fields; @@ -36,87 +37,79 @@ class _MobileFieldPickerListState extends State { @override Widget build(BuildContext context) { - return Column( - children: [ - const Center(child: DragHandler()), - _Header(newFieldId: newFieldId), - Expanded( - child: ListView.builder( - itemCount: widget.fields.length, - itemBuilder: (context, index) => _FieldButton( - field: widget.fields[index], - showTopBorder: index == 0, - isSelected: widget.fields[index].id == newFieldId, - onSelect: (fieldId) => setState(() => newFieldId = fieldId), - ), - ), - ), - ], - ); - } -} - -class _Header extends StatelessWidget { - const _Header({required this.newFieldId}); - - final String? newFieldId; - - @override - Widget build(BuildContext context) { - return Stack( - alignment: Alignment.center, - children: [ - Row( - mainAxisAlignment: MainAxisAlignment.spaceBetween, + return DraggableScrollableSheet( + expand: false, + snap: true, + initialChildSize: 0.98, + minChildSize: 0.98, + maxChildSize: 0.98, + builder: (context, scrollController) { + return Column( + mainAxisSize: MainAxisSize.min, children: [ - SizedBox.square( - dimension: 36, - child: IconButton( - highlightColor: Colors.transparent, - splashColor: Colors.transparent, - padding: EdgeInsets.zero, - onPressed: () => context.pop(), - icon: const FlowySvg( - FlowySvgs.arrow_left_s, - size: Size.square(20), - ), - ), + const DragHandler(), + _Header( + title: widget.title, + onDone: (context) => context.pop(newFieldId), ), - Padding( - padding: const EdgeInsets.fromLTRB(8, 4, 8, 8), - child: TextButton( - style: TextButton.styleFrom( - padding: const EdgeInsets.symmetric( - horizontal: 12, - vertical: 5, - ), - shape: RoundedRectangleBorder( - borderRadius: BorderRadius.circular(10), - ), - elevation: 0, - visualDensity: VisualDensity.compact, - tapTargetSize: MaterialTapTargetSize.shrinkWrap, - enableFeedback: true, - backgroundColor: Theme.of(context).primaryColor, - ), - onPressed: () => context.pop(newFieldId), - child: FlowyText.medium( - LocaleKeys.button_done.tr(), - fontSize: 16, - color: Theme.of(context).colorScheme.onPrimary, - overflow: TextOverflow.ellipsis, + SingleChildScrollView( + controller: scrollController, + child: ListView.builder( + shrinkWrap: true, + itemCount: widget.fields.length, + itemBuilder: (context, index) => _FieldButton( + field: widget.fields[index], + showTopBorder: index == 0, + isSelected: widget.fields[index].id == newFieldId, + onSelect: (fieldId) => setState(() => newFieldId = fieldId), ), ), ), ], + ); + }, + ); + } +} + +/// Same header as the one in showMobileBottomSheet, but allows popping the +/// sheet with a value. +class _Header extends StatelessWidget { + const _Header({ + required this.title, + required this.onDone, + }); + + final String title; + final void Function(BuildContext context) onDone; + + @override + Widget build(BuildContext context) { + return Padding( + padding: const EdgeInsets.only(bottom: 4.0), + child: SizedBox( + height: 44.0, + child: Stack( + children: [ + const Align( + alignment: Alignment.centerLeft, + child: AppBarBackButton(), + ), + Align( + child: FlowyText.medium( + title, + fontSize: 16.0, + ), + ), + Align( + alignment: Alignment.centerRight, + child: AppBarDoneButton( + onTap: () => onDone(context), + ), + ), + ], ), - Center( - child: FlowyText.medium( - LocaleKeys.calendar_settings_changeLayoutDateField.tr(), - fontSize: 16, - ), - ), - ], + ), ); } } diff --git a/frontend/appflowy_flutter/lib/mobile/presentation/database/view/database_field_list.dart b/frontend/appflowy_flutter/lib/mobile/presentation/database/view/database_field_list.dart index b33a65dd02..d1322d6b90 100644 --- a/frontend/appflowy_flutter/lib/mobile/presentation/database/view/database_field_list.dart +++ b/frontend/appflowy_flutter/lib/mobile/presentation/database/view/database_field_list.dart @@ -3,7 +3,6 @@ import 'dart:ui'; import 'package:appflowy/generated/flowy_svgs.g.dart'; import 'package:appflowy/generated/locale_keys.g.dart'; import 'package:appflowy/mobile/presentation/widgets/flowy_option_tile.dart'; -import 'package:appflowy/plugins/base/drag_handler.dart'; import 'package:appflowy/plugins/database/application/database_controller.dart'; import 'package:appflowy/plugins/database/application/field/field_controller.dart'; import 'package:appflowy/plugins/database/application/field/field_info.dart'; @@ -11,7 +10,6 @@ import 'package:appflowy/plugins/database/application/setting/property_bloc.dart import 'package:appflowy/plugins/database/widgets/setting/field_visibility_extension.dart'; import 'package:appflowy/util/field_type_extension.dart'; import 'package:appflowy/workspace/application/view/view_bloc.dart'; -import 'package:appflowy_backend/protobuf/flowy-folder/protobuf.dart'; import 'package:collection/collection.dart'; import 'package:easy_localization/easy_localization.dart'; import 'package:flowy_infra_ui/flowy_infra_ui.dart'; @@ -30,61 +28,9 @@ class MobileDatabaseFieldList extends StatelessWidget { @override Widget build(BuildContext context) { - return DraggableScrollableSheet( - expand: false, - snap: true, - initialChildSize: 1.0, - minChildSize: 0.0, - builder: (context, controller) { - return Material( - child: Column( - children: [ - const Center(child: DragHandler()), - const _MobileDatabaseFieldListHeader(), - Expanded( - child: SingleChildScrollView( - child: _MobileDatabaseFieldListBody( - databaseController: databaseController, - view: context.read().state.view, - ), - ), - ), - ], - ), - ); - }, - ); - } -} - -class _MobileDatabaseFieldListHeader extends StatelessWidget { - const _MobileDatabaseFieldListHeader(); - - @override - Widget build(BuildContext context) { - const iconWidth = 30.0; - return Padding( - padding: const EdgeInsets.fromLTRB(8, 4, 8, 12), - child: Stack( - children: [ - Align( - alignment: Alignment.centerLeft, - child: FlowyIconButton( - icon: const FlowySvg( - FlowySvgs.arrow_left_m, - size: Size.square(iconWidth), - ), - onPressed: () => Navigator.of(context).maybePop(), - ), - ), - Align( - child: FlowyText.medium( - LocaleKeys.grid_settings_properties.tr(), - fontSize: 16, - ), - ), - ], - ), + return _MobileDatabaseFieldListBody( + databaseController: databaseController, + viewId: context.read().state.view.id, ); } } @@ -92,17 +38,17 @@ class _MobileDatabaseFieldListHeader extends StatelessWidget { class _MobileDatabaseFieldListBody extends StatelessWidget { const _MobileDatabaseFieldListBody({ required this.databaseController, - required this.view, + required this.viewId, }); final DatabaseController databaseController; - final ViewPB view; + final String viewId; @override Widget build(BuildContext context) { return BlocProvider( create: (_) => DatabasePropertyBloc( - viewId: view.id, + viewId: viewId, fieldController: databaseController.fieldController, )..add(const DatabasePropertyEvent.initial()), child: BlocBuilder( @@ -114,7 +60,7 @@ class _MobileDatabaseFieldListBody extends StatelessWidget { final firstField = fields.removeAt(0); final firstCell = DatabaseFieldListTile( key: ValueKey(firstField.id), - viewId: view.id, + viewId: viewId, fieldController: databaseController.fieldController, fieldInfo: firstField, showTopBorder: true, @@ -123,7 +69,7 @@ class _MobileDatabaseFieldListBody extends StatelessWidget { .mapIndexed( (index, field) => DatabaseFieldListTile( key: ValueKey(field.id), - viewId: view.id, + viewId: viewId, fieldController: databaseController.fieldController, fieldInfo: field, index: index, @@ -133,6 +79,7 @@ class _MobileDatabaseFieldListBody extends StatelessWidget { .toList(); return ReorderableListView.builder( + padding: EdgeInsets.zero, proxyDecorator: (_, index, anim) { final field = fields[index]; return AnimatedBuilder( @@ -146,7 +93,7 @@ class _MobileDatabaseFieldListBody extends StatelessWidget { child: Material( child: DatabaseFieldListTile( key: ValueKey(field.id), - viewId: view.id, + viewId: viewId, fieldController: databaseController.fieldController, fieldInfo: field, index: index, @@ -170,7 +117,7 @@ class _MobileDatabaseFieldListBody extends StatelessWidget { mainAxisSize: MainAxisSize.min, children: [ _divider(), - _NewDatabaseFieldTile(viewId: view.id), + _NewDatabaseFieldTile(viewId: viewId), ], ), itemCount: cells.length, diff --git a/frontend/appflowy_flutter/lib/mobile/presentation/database/view/database_view_layout.dart b/frontend/appflowy_flutter/lib/mobile/presentation/database/view/database_view_layout.dart index 5961a3faf7..fdce3d6d21 100644 --- a/frontend/appflowy_flutter/lib/mobile/presentation/database/view/database_view_layout.dart +++ b/frontend/appflowy_flutter/lib/mobile/presentation/database/view/database_view_layout.dart @@ -168,6 +168,7 @@ class _CalendarLayoutField extends StatelessWidget { onTap: () async { final newFieldId = await showFieldPicker( context, + LocaleKeys.calendar_settings_changeLayoutDateField.tr(), selectedFieldId, databaseController.fieldController, (field) => field.fieldType == FieldType.DateTime, diff --git a/frontend/appflowy_flutter/lib/mobile/presentation/database/view/database_view_list.dart b/frontend/appflowy_flutter/lib/mobile/presentation/database/view/database_view_list.dart index 8e6fcfcb7c..5beb4bd7b2 100644 --- a/frontend/appflowy_flutter/lib/mobile/presentation/database/view/database_view_list.dart +++ b/frontend/appflowy_flutter/lib/mobile/presentation/database/view/database_view_list.dart @@ -1,5 +1,6 @@ import 'package:appflowy/generated/flowy_svgs.g.dart'; import 'package:appflowy/generated/locale_keys.g.dart'; +import 'package:appflowy/mobile/presentation/base/app_bar_actions.dart'; import 'package:appflowy/mobile/presentation/bottom_sheet/bottom_sheet.dart'; import 'package:appflowy/mobile/presentation/widgets/widgets.dart'; import 'package:appflowy/plugins/base/drag_handler.dart'; @@ -26,28 +27,110 @@ class MobileDatabaseViewList extends StatelessWidget { @override Widget build(BuildContext context) { - return BlocBuilder( - builder: (context, state) { - final views = [state.view, ...state.view.childViews]; - final children = [ - ...views.mapIndexed( - (index, view) => MobileDatabaseViewListButton( - view: view, - showTopBorder: index == 0, - ), - ), - const VSpace(20), - const MobileNewDatabaseViewButton(), - ]; + return DraggableScrollableSheet( + expand: false, + snap: true, + initialChildSize: 0.98, + minChildSize: 0.98, + maxChildSize: 0.98, + builder: (context, scrollController) { + return BlocBuilder( + builder: (context, state) { + final views = [state.view, ...state.view.childViews]; - return Column( - children: children, + return Column( + children: [ + const DragHandler(), + _Header( + title: LocaleKeys.grid_settings_viewList.plural( + context.watch().state.tabBars.length, + namedArgs: { + 'count': + '${context.watch().state.tabBars.length}', + }, + ), + showBackButton: false, + useFilledDoneButton: false, + onDone: (context) => Navigator.pop(context), + ), + SingleChildScrollView( + controller: scrollController, + child: Column( + children: [ + ...views.mapIndexed( + (index, view) => MobileDatabaseViewListButton( + view: view, + showTopBorder: index == 0, + ), + ), + const VSpace(20), + const MobileNewDatabaseViewButton(), + ], + ), + ), + ], + ); + }, ); }, ); } } +/// Same header as the one in showMobileBottomSheet, but allows popping the +/// sheet with a value. +class _Header extends StatelessWidget { + const _Header({ + required this.title, + required this.showBackButton, + required this.useFilledDoneButton, + required this.onDone, + }); + + final String title; + final bool showBackButton; + final bool useFilledDoneButton; + final void Function(BuildContext context) onDone; + + @override + Widget build(BuildContext context) { + return Padding( + padding: const EdgeInsets.only(bottom: 4.0), + child: SizedBox( + height: 44.0, + child: Stack( + children: [ + if (showBackButton) + const Align( + alignment: Alignment.centerLeft, + child: AppBarBackButton(), + ), + Align( + child: FlowyText.medium( + title, + fontSize: 16.0, + ), + ), + useFilledDoneButton + ? Align( + alignment: Alignment.centerRight, + child: AppBarFilledDoneButton( + onTap: () => onDone(context), + ), + ) + : Align( + alignment: Alignment.centerRight, + child: AppBarDoneButton( + onTap: () => onDone(context), + ), + ), + ], + ), + ), + ); + } +} + @visibleForTesting class MobileDatabaseViewListButton extends StatelessWidget { const MobileDatabaseViewListButton({ @@ -155,6 +238,7 @@ class MobileNewDatabaseViewButton extends StatelessWidget { onTap: () async { final result = await showMobileBottomSheet<(DatabaseLayoutPB, String)>( context, + showDragHandle: true, builder: (_) { return const MobileCreateDatabaseView(); }, @@ -199,12 +283,13 @@ class _MobileCreateDatabaseViewState extends State { Widget build(BuildContext context) { return Column( children: [ - const Center(child: DragHandler()), - _CreateViewHeader( - textController: controller, - selectedLayout: layoutType, + _Header( + title: LocaleKeys.grid_settings_createView.tr(), + showBackButton: true, + useFilledDoneButton: true, + onDone: (context) => + context.pop((layoutType, controller.text.trim())), ), - const VSpace(4.0), FlowyOptionTile.textField( autofocus: true, controller: controller, @@ -220,74 +305,3 @@ class _MobileCreateDatabaseViewState extends State { ); } } - -class _CreateViewHeader extends StatelessWidget { - const _CreateViewHeader({ - required this.textController, - required this.selectedLayout, - }); - - final TextEditingController textController; - final DatabaseLayoutPB selectedLayout; - - @override - Widget build(BuildContext context) { - return Stack( - alignment: Alignment.center, - children: [ - Row( - mainAxisAlignment: MainAxisAlignment.spaceBetween, - children: [ - SizedBox.square( - dimension: 36, - child: IconButton( - highlightColor: Colors.transparent, - splashColor: Colors.transparent, - padding: EdgeInsets.zero, - onPressed: () => context.pop(), - icon: const FlowySvg( - FlowySvgs.arrow_left_s, - size: Size.square(24), - ), - ), - ), - Padding( - padding: const EdgeInsets.fromLTRB(8, 4, 8, 8), - child: TextButton( - style: TextButton.styleFrom( - padding: const EdgeInsets.symmetric( - horizontal: 12, - vertical: 5, - ), - shape: RoundedRectangleBorder( - borderRadius: BorderRadius.circular(10), - ), - elevation: 0, - visualDensity: VisualDensity.compact, - tapTargetSize: MaterialTapTargetSize.shrinkWrap, - enableFeedback: true, - backgroundColor: Theme.of(context).primaryColor, - ), - onPressed: () { - context.pop((selectedLayout, textController.text.trim())); - }, - child: FlowyText.medium( - LocaleKeys.button_done.tr(), - fontSize: 16, - color: Theme.of(context).colorScheme.onPrimary, - overflow: TextOverflow.ellipsis, - ), - ), - ), - ], - ), - Center( - child: FlowyText.medium( - LocaleKeys.grid_settings_createView.tr(), - fontSize: 16, - ), - ), - ], - ); - } -} diff --git a/frontend/appflowy_flutter/lib/mobile/presentation/database/view/database_view_quick_actions.dart b/frontend/appflowy_flutter/lib/mobile/presentation/database/view/database_view_quick_actions.dart index 08c006e7c5..468730e2f1 100644 --- a/frontend/appflowy_flutter/lib/mobile/presentation/database/view/database_view_quick_actions.dart +++ b/frontend/appflowy_flutter/lib/mobile/presentation/database/view/database_view_quick_actions.dart @@ -1,5 +1,6 @@ import 'package:appflowy/generated/flowy_svgs.g.dart'; import 'package:appflowy/generated/locale_keys.g.dart'; +import 'package:appflowy/mobile/presentation/bottom_sheet/bottom_sheet.dart'; import 'package:appflowy/mobile/presentation/widgets/flowy_mobile_quick_action_button.dart'; import 'package:appflowy/plugins/database/application/database_controller.dart'; import 'package:appflowy/workspace/application/view/view_bloc.dart'; @@ -13,7 +14,7 @@ import 'edit_database_view_screen.dart'; /// [MobileDatabaseViewQuickActions] is gives users to quickly edit a database /// view from the [MobileDatabaseViewList] -class MobileDatabaseViewQuickActions extends StatefulWidget { +class MobileDatabaseViewQuickActions extends StatelessWidget { const MobileDatabaseViewQuickActions({ super.key, required this.view, @@ -23,49 +24,46 @@ class MobileDatabaseViewQuickActions extends StatefulWidget { final ViewPB view; final DatabaseController databaseController; - @override - State createState() => - _MobileDatabaseViewQuickActionsState(); -} - -class _MobileDatabaseViewQuickActionsState - extends State { - bool isEditing = false; - @override Widget build(BuildContext context) { - return isEditing - ? MobileEditDatabaseViewScreen( - databaseController: widget.databaseController, - ) - : _quickActions(context, widget.view); - } - - Widget _quickActions(BuildContext context, ViewPB view) { final isInline = view.childViews.isNotEmpty; - return Padding( - padding: const EdgeInsets.only(top: 8), - child: Column( - mainAxisSize: MainAxisSize.min, - children: [ - _actionButton(context, _Action.edit, () { - setState(() => isEditing = true); + return Column( + mainAxisSize: MainAxisSize.min, + children: [ + _actionButton(context, _Action.edit, () { + final bloc = context.read(); + context.pop(); + showMobileBottomSheet( + context, + showHeader: true, + showDoneButton: true, + title: LocaleKeys.grid_settings_editView.tr(), + enableDraggableScrollable: true, + initialChildSize: 0.98, + minChildSize: 0.98, + maxChildSize: 0.98, + builder: (_) => BlocProvider.value( + value: bloc, + child: MobileEditDatabaseViewScreen( + databaseController: databaseController, + ), + ), + ); + }), + if (!isInline) ...[ + _divider(), + _actionButton(context, _Action.duplicate, () { + context.read().add(const ViewEvent.duplicate()); + context.pop(); }), - if (!isInline) ...[ - _divider(), - _actionButton(context, _Action.duplicate, () { - context.read().add(const ViewEvent.duplicate()); - context.pop(); - }), - _divider(), - _actionButton(context, _Action.delete, () { - context.read().add(const ViewEvent.delete()); - context.pop(); - }), - _divider(), - ], + _divider(), + _actionButton(context, _Action.delete, () { + context.read().add(const ViewEvent.delete()); + context.pop(); + }), + _divider(), ], - ), + ], ); } diff --git a/frontend/appflowy_flutter/lib/mobile/presentation/database/view/edit_database_view_cubit.dart b/frontend/appflowy_flutter/lib/mobile/presentation/database/view/edit_database_view_cubit.dart deleted file mode 100644 index 6c098bff9c..0000000000 --- a/frontend/appflowy_flutter/lib/mobile/presentation/database/view/edit_database_view_cubit.dart +++ /dev/null @@ -1,35 +0,0 @@ -import 'package:bloc/bloc.dart'; -import 'package:flutter/foundation.dart'; -import 'package:freezed_annotation/freezed_annotation.dart'; - -part 'edit_database_view_cubit.freezed.dart'; - -class MobileEditDatabaseViewCubit extends Cubit { - MobileEditDatabaseViewCubit() - : super( - MobileDatabaseViewEditorState.initial(), - ); - - void changePage(MobileEditDatabaseViewPageEnum newPage) { - emit(MobileDatabaseViewEditorState(currentPage: newPage)); - } -} - -@freezed -class MobileDatabaseViewEditorState with _$MobileDatabaseViewEditorState { - factory MobileDatabaseViewEditorState({ - required MobileEditDatabaseViewPageEnum currentPage, - }) = _MobileDatabaseViewEditorState; - - factory MobileDatabaseViewEditorState.initial() => - MobileDatabaseViewEditorState( - currentPage: MobileEditDatabaseViewPageEnum.main, - ); -} - -enum MobileEditDatabaseViewPageEnum { - main, - fields, - filter, - sort, -} diff --git a/frontend/appflowy_flutter/lib/mobile/presentation/database/view/edit_database_view_screen.dart b/frontend/appflowy_flutter/lib/mobile/presentation/database/view/edit_database_view_screen.dart index b91ab80976..2c1c7859bb 100644 --- a/frontend/appflowy_flutter/lib/mobile/presentation/database/view/edit_database_view_screen.dart +++ b/frontend/appflowy_flutter/lib/mobile/presentation/database/view/edit_database_view_screen.dart @@ -2,7 +2,6 @@ import 'package:appflowy/generated/flowy_svgs.g.dart'; import 'package:appflowy/generated/locale_keys.g.dart'; import 'package:appflowy/mobile/presentation/bottom_sheet/bottom_sheet.dart'; import 'package:appflowy/mobile/presentation/widgets/flowy_option_tile.dart'; -import 'package:appflowy/plugins/base/drag_handler.dart'; import 'package:appflowy/plugins/database/application/database_controller.dart'; import 'package:appflowy/plugins/database/application/database_view_service.dart'; import 'package:appflowy/plugins/database/application/layout/layout_service.dart'; @@ -18,12 +17,11 @@ import 'package:go_router/go_router.dart'; import 'database_field_list.dart'; import 'database_view_layout.dart'; -import 'edit_database_view_cubit.dart'; /// [MobileEditDatabaseViewScreen] is the main widget used to edit a database /// view. It contains multiple sub-pages, and the current page is managed by /// [MobileEditDatabaseViewCubit] -class MobileEditDatabaseViewScreen extends StatefulWidget { +class MobileEditDatabaseViewScreen extends StatelessWidget { const MobileEditDatabaseViewScreen({ super.key, required this.databaseController, @@ -31,130 +29,12 @@ class MobileEditDatabaseViewScreen extends StatefulWidget { final DatabaseController databaseController; - @override - State createState() => - _MobileEditDatabaseViewScreenState(); -} - -class _MobileEditDatabaseViewScreenState - extends State { - @override - Widget build(BuildContext context) { - return BlocProvider( - create: (context) => MobileEditDatabaseViewCubit(), - child: BlocBuilder( - builder: (context, state) { - return switch (state.currentPage) { - MobileEditDatabaseViewPageEnum.main => _EditDatabaseViewMainPage( - databaseController: widget.databaseController, - ), - MobileEditDatabaseViewPageEnum.fields => _wrapSubPage( - context, - MobileDatabaseFieldList( - databaseController: widget.databaseController, - ), - ), - _ => const SizedBox.shrink(), - }; - }, - ), - ); - } - - Widget _wrapSubPage(BuildContext context, Widget child) { - return PopScope( - canPop: false, - child: child, - onPopInvoked: (_) { - context - .read() - .changePage(MobileEditDatabaseViewPageEnum.main); - }, - ); - } -} - -class _EditDatabaseViewMainPage extends StatelessWidget { - const _EditDatabaseViewMainPage({ - required this.databaseController, - }); - - final DatabaseController databaseController; - - @override - Widget build(BuildContext context) { - return DraggableScrollableSheet( - expand: false, - snap: true, - initialChildSize: 1.0, - minChildSize: 0.0, - builder: (context, controller) { - return Material( - child: Column( - children: [ - const Center(child: DragHandler()), - const _EditDatabaseViewHeader(), - Expanded( - child: SingleChildScrollView( - child: _EditDatabaseViewBody( - databaseController: databaseController, - ), - ), - ), - ], - ), - ); - }, - ); - } -} - -class _EditDatabaseViewHeader extends StatelessWidget { - const _EditDatabaseViewHeader(); - - @override - Widget build(BuildContext context) { - const iconWidth = 30.0; - return Padding( - padding: const EdgeInsets.fromLTRB(8, 4, 8, 12), - child: Stack( - children: [ - Align( - alignment: Alignment.centerLeft, - child: FlowyIconButton( - icon: const FlowySvg( - FlowySvgs.close_s, - size: Size.square(iconWidth), - ), - onPressed: () => context.pop(), - ), - ), - Align( - child: FlowyText.medium( - LocaleKeys.grid_settings_editView.tr(), - fontSize: 16, - ), - ), - ], - ), - ); - } -} - -class _EditDatabaseViewBody extends StatelessWidget { - const _EditDatabaseViewBody({ - required this.databaseController, - }); - - final DatabaseController databaseController; - @override Widget build(BuildContext context) { return BlocBuilder( builder: (context, state) { - return Column( - mainAxisSize: MainAxisSize.min, + return ListView( + shrinkWrap: true, children: [ _NameAndIcon(view: state.view), _divider(), @@ -207,6 +87,7 @@ class _NameAndIconState extends State<_NameAndIcon> { Widget build(BuildContext context) { return FlowyOptionTile.textField( autofocus: true, + showTopBorder: false, controller: textEditingController, onTextChanged: (text) { context.read().add(ViewEvent.rename(text)); @@ -250,15 +131,6 @@ enum DatabaseViewSettings { delete => FlowySvgs.delete_s, }; } - - MobileEditDatabaseViewPageEnum? get subPage { - return switch (this) { - fields => MobileEditDatabaseViewPageEnum.fields, - filter => MobileEditDatabaseViewPageEnum.filter, - sort => MobileEditDatabaseViewPageEnum.sort, - _ => null, - }; - } } class DatabaseViewSettingTile extends StatelessWidget { @@ -325,25 +197,18 @@ class DatabaseViewSettingTile extends StatelessWidget { } void _onTap(BuildContext context) async { - final subPage = setting.subPage; - - if (subPage != null) { - context.read().changePage(subPage); - return; - } - if (setting == DatabaseViewSettings.layout) { final databaseLayout = databaseLayoutFromViewLayout(view.layout); final newLayout = await showMobileBottomSheet( context, - resizeToAvoidBottomInset: false, showDragHandle: true, + showHeader: true, + showDivider: false, + title: LocaleKeys.grid_settings_layout.tr(), builder: (context) { return DatabaseViewLayoutPicker( selectedLayout: databaseLayout, - onSelect: (layout) { - Navigator.of(context).pop(layout); - }, + onSelect: (layout) => Navigator.of(context).pop(layout), ); }, ); @@ -356,6 +221,32 @@ class DatabaseViewSettingTile extends StatelessWidget { return; } + if (setting == DatabaseViewSettings.fields) { + await showMobileBottomSheet( + context, + useSafeArea: false, + resizeToAvoidBottomInset: false, + showDragHandle: true, + showHeader: true, + showBackButton: true, + title: LocaleKeys.grid_settings_properties.tr(), + showDivider: true, + enableDraggableScrollable: true, + initialChildSize: 0.98, + minChildSize: 0.98, + maxChildSize: 0.98, + builder: (_) { + return BlocProvider.value( + value: context.read(), + child: MobileDatabaseFieldList( + databaseController: databaseController, + ), + ); + }, + ); + return; + } + if (setting == DatabaseViewSettings.board) { await showMobileBottomSheet( context, @@ -375,13 +266,13 @@ class DatabaseViewSettingTile extends StatelessWidget { if (setting == DatabaseViewSettings.calendar) { await showMobileBottomSheet( context, - resizeToAvoidBottomInset: false, + showDragHandle: true, + showHeader: true, + showDivider: false, + title: LocaleKeys.calendar_settings_name.tr(), builder: (context) { - return Padding( - padding: const EdgeInsets.only(top: 24, bottom: 46), - child: MobileCalendarViewLayoutSettings( - databaseController: databaseController, - ), + return MobileCalendarViewLayoutSettings( + databaseController: databaseController, ); }, ); diff --git a/frontend/appflowy_flutter/lib/plugins/database/widgets/setting/mobile_database_controls.dart b/frontend/appflowy_flutter/lib/plugins/database/widgets/setting/mobile_database_controls.dart index 5f5e164271..37f3816d03 100644 --- a/frontend/appflowy_flutter/lib/plugins/database/widgets/setting/mobile_database_controls.dart +++ b/frontend/appflowy_flutter/lib/plugins/database/widgets/setting/mobile_database_controls.dart @@ -65,7 +65,13 @@ class MobileDatabaseControls extends StatelessWidget { onTap: () { showMobileBottomSheet( context, - padding: EdgeInsets.zero, + showHeader: true, + showDoneButton: true, + title: LocaleKeys.grid_settings_editView.tr(), + enableDraggableScrollable: true, + initialChildSize: 0.98, + minChildSize: 0.98, + maxChildSize: 0.98, builder: (_) { return BlocProvider( create: (_) { @@ -90,11 +96,7 @@ class MobileDatabaseControls extends StatelessWidget { onTap: () { showMobileBottomSheet( context, - showHeader: true, - showCloseButton: true, - showDragHandle: true, showDivider: false, - title: LocaleKeys.grid_settings_viewList.tr(), builder: (_) { return MultiBlocProvider( providers: [ diff --git a/frontend/resources/translations/en.json b/frontend/resources/translations/en.json index 7c1a1ef750..aa98f8c5ae 100644 --- a/frontend/resources/translations/en.json +++ b/frontend/resources/translations/en.json @@ -480,7 +480,11 @@ "typeAValue": "Type a value...", "layout": "Layout", "databaseLayout": "Layout", - "viewList": "Database Views", + "viewList": { + "zero": "0 views", + "one": "{count} view", + "other": "{count} views" + }, "editView": "Edit View", "boardSettings": "Board settings", "calendarSettings": "Calendar settings", @@ -1250,4 +1254,4 @@ "userIcon": "User icon" }, "noLogFiles": "There're no log files" -} +} \ No newline at end of file