From 9824d5980a91c2a2f1feaf08431e5a36e4d560f7 Mon Sep 17 00:00:00 2001 From: Yijing Huang Date: Sat, 2 Dec 2023 04:46:02 -0700 Subject: [PATCH] fix: mobile bugs fix (#4059) * fix: add missing tap gesture * fix: unify bottom sheet shape and padding * fix: fail to pop the detail page when add new card * fix: show card title placeholder when it is empty * refactor: ThirdPartySignInButtons * chore: add resizeToAvoidBottomInset in showFlowyMobileBottomSheet * chore: refactor code --- .../card/card_content/card_cells/text.dart | 44 +++++---- .../card_content/mobile_card_content.dart | 9 +- .../show_flowy_mobile_bottom_sheet.dart | 44 ++++++--- .../widgets/third_party_sign_in_buttons.dart | 94 +++++++++++++++++++ .../appearance/mobile_appearance.dart | 10 +- 5 files changed, 162 insertions(+), 39 deletions(-) diff --git a/frontend/appflowy_flutter/lib/mobile/presentation/database/card/card_content/card_cells/text.dart b/frontend/appflowy_flutter/lib/mobile/presentation/database/card/card_content/card_cells/text.dart index 6be4c74cfa..563b5f5fd7 100644 --- a/frontend/appflowy_flutter/lib/mobile/presentation/database/card/card_content/card_cells/text.dart +++ b/frontend/appflowy_flutter/lib/mobile/presentation/database/card/card_content/card_cells/text.dart @@ -17,10 +17,10 @@ class MobileTextCardCell extends CardCell { final CellControllerBuilder cellControllerBuilder; @override - State createState() => _NumberCellState(); + State createState() => _MobileTextCardCellState(); } -class _NumberCellState extends State { +class _MobileTextCardCellState extends State { late final TextCellBloc _cellBloc; @override @@ -47,27 +47,31 @@ class _NumberCellState extends State { child: BlocBuilder( buildWhen: (previous, current) => previous.content != current.content, builder: (context, state) { + // return custom widget if render hook is provided(for example, the title of the card on board view) + // if widget.cardData.isEmpty means there is no data for this cell + final Widget? custom = widget.renderHook?.call( + state.content, + widget.cardData, + context, + ); + if (custom != null) { + return custom; + } + + // if there is no render hook + // the empty text cell will be hidden if (state.content.isEmpty) { return const SizedBox(); - } else { - final Widget? custom = widget.renderHook?.call( - state.content, - widget.cardData, - context, - ); - if (custom != null) { - return custom; - } - - return Container( - alignment: Alignment.centerLeft, - padding: cellStyle.padding, - child: Text( - state.content, - style: cellStyle.primaryTextStyle(), - ), - ); } + + return Container( + alignment: Alignment.centerLeft, + padding: cellStyle.padding, + child: Text( + state.content, + style: cellStyle.primaryTextStyle(), + ), + ); }, ), ); diff --git a/frontend/appflowy_flutter/lib/mobile/presentation/database/card/card_content/mobile_card_content.dart b/frontend/appflowy_flutter/lib/mobile/presentation/database/card/card_content/mobile_card_content.dart index 2aaed1e585..4ebc1bd477 100644 --- a/frontend/appflowy_flutter/lib/mobile/presentation/database/card/card_content/mobile_card_content.dart +++ b/frontend/appflowy_flutter/lib/mobile/presentation/database/card/card_content/mobile_card_content.dart @@ -72,16 +72,17 @@ class MobileCardContent extends StatelessWidget { DatabaseCellContext cellContext, ) { final renderHook = RowCardRenderHook(); - renderHook.addTextCellHook((cellData, _, __) { + renderHook.addTextCellHook((cellData, cardData, __) { return BlocBuilder( builder: (context, state) { - final text = cellData.isEmpty + final cardDataIsEmpty = cardData == null; + final text = cardDataIsEmpty ? LocaleKeys.grid_row_titlePlaceholder.tr() : cellData; final textStyle = Theme.of(context).textTheme.bodyMedium?.copyWith( - color: cellData.isEmpty - ? Theme.of(context).colorScheme.onSecondary + color: cardDataIsEmpty + ? Theme.of(context).hintColor : Theme.of(context).colorScheme.onBackground, fontSize: 20, ); diff --git a/frontend/appflowy_flutter/lib/mobile/presentation/widgets/show_flowy_mobile_bottom_sheet.dart b/frontend/appflowy_flutter/lib/mobile/presentation/widgets/show_flowy_mobile_bottom_sheet.dart index 2728102328..fd1ca38160 100644 --- a/frontend/appflowy_flutter/lib/mobile/presentation/widgets/show_flowy_mobile_bottom_sheet.dart +++ b/frontend/appflowy_flutter/lib/mobile/presentation/widgets/show_flowy_mobile_bottom_sheet.dart @@ -1,3 +1,4 @@ +import 'package:flowy_infra/size.dart'; import 'package:flutter/material.dart'; import 'package:go_router/go_router.dart'; @@ -5,24 +6,45 @@ Future showFlowyMobileBottomSheet( BuildContext context, { required String title, required Widget Function(BuildContext) builder, + bool resizeToAvoidBottomInset = true, bool isScrollControlled = false, }) async { return showModalBottomSheet( context: context, isScrollControlled: isScrollControlled, - builder: (context) => Padding( - padding: const EdgeInsets.all(16.0), - child: Column( - mainAxisSize: MainAxisSize.min, - children: [ - _BottomSheetTitle(title), - const SizedBox( - height: 16, - ), - builder(context), - ], + shape: const RoundedRectangleBorder( + borderRadius: BorderRadius.vertical( + top: Corners.s12Radius, ), ), + builder: (context) { + const padding = EdgeInsets.fromLTRB(16, 16, 16, 48); + + final child = Padding( + padding: padding, + child: Column( + mainAxisSize: MainAxisSize.min, + children: [ + _BottomSheetTitle(title), + const SizedBox( + height: 16, + ), + builder(context), + ], + ), + ); + if (resizeToAvoidBottomInset) { + return AnimatedPadding( + padding: padding + + EdgeInsets.only( + bottom: MediaQuery.of(context).viewInsets.bottom, + ), + duration: Duration.zero, + child: child, + ); + } + return child; + }, ); } diff --git a/frontend/appflowy_flutter/lib/user/presentation/screens/sign_in_screen/widgets/third_party_sign_in_buttons.dart b/frontend/appflowy_flutter/lib/user/presentation/screens/sign_in_screen/widgets/third_party_sign_in_buttons.dart index 9876b33698..42fe578c4b 100644 --- a/frontend/appflowy_flutter/lib/user/presentation/screens/sign_in_screen/widgets/third_party_sign_in_buttons.dart +++ b/frontend/appflowy_flutter/lib/user/presentation/screens/sign_in_screen/widgets/third_party_sign_in_buttons.dart @@ -2,6 +2,7 @@ import 'package:appflowy/generated/flowy_svgs.g.dart'; import 'package:appflowy/generated/locale_keys.g.dart'; import 'package:appflowy/user/application/sign_in_bloc.dart'; import 'package:appflowy/user/presentation/presentation.dart'; +import 'package:appflowy/util/platform_extension.dart'; import 'package:appflowy/workspace/application/settings/appearance/appearance_cubit.dart'; import 'package:easy_localization/easy_localization.dart'; import 'package:flowy_infra/size.dart'; @@ -76,9 +77,40 @@ class _ThirdPartySignInButton extends StatelessWidget { final VoidCallback onPressed; + @override + Widget build(BuildContext context) { + if (PlatformExtension.isMobile) { + return _MobileSignInButton( + icon: icon, + labelText: labelText, + onPressed: onPressed, + ); + } else { + return _DesktopSignInButton( + icon: icon, + labelText: labelText, + onPressed: onPressed, + ); + } + } +} + +class _DesktopSignInButton extends StatelessWidget { + const _DesktopSignInButton({ + required this.icon, + required this.labelText, + required this.onPressed, + }); + + final FlowySvgData icon; + final String labelText; + + final VoidCallback onPressed; + @override Widget build(BuildContext context) { final style = Theme.of(context); + // In desktop, the width of button is limited by [AuthFormContainer] return SizedBox( height: 48, width: AuthFormContainer.width, @@ -130,6 +162,68 @@ class _ThirdPartySignInButton extends StatelessWidget { } } +class _MobileSignInButton extends StatelessWidget { + const _MobileSignInButton({ + required this.icon, + required this.labelText, + required this.onPressed, + }); + + final FlowySvgData icon; + final String labelText; + final VoidCallback onPressed; + + @override + Widget build(BuildContext context) { + final style = Theme.of(context); + return GestureDetector( + onTap: onPressed, + child: Container( + height: 48, + decoration: BoxDecoration( + borderRadius: const BorderRadius.all( + Radius.circular(4), + ), + border: Border.all( + color: style.colorScheme.outline, + width: 0.5, + ), + ), + child: Center( + child: Row( + mainAxisSize: MainAxisSize.min, + children: [ + SizedBox( + // The icon could be in different height as original aspect ratio, we use a fixed sizebox to wrap it to make sure they all occupy the same space. + width: 30, + height: 30, + child: Center( + child: SizedBox( + width: 24, + child: FlowySvg( + icon, + blendMode: null, + ), + ), + ), + ), + const HSpace(8), + SizedBox( + // To fit the longest label 'Log in with Discord' + width: 135, + child: Text( + labelText, + style: Theme.of(context).textTheme.titleSmall, + ), + ), + ], + ), + ), + ), + ); + } +} + void _signInWithGoogle(BuildContext context) { context.read().add( const SignInEvent.signedInWithOAuth('google'), diff --git a/frontend/appflowy_flutter/lib/workspace/application/settings/appearance/mobile_appearance.dart b/frontend/appflowy_flutter/lib/workspace/application/settings/appearance/mobile_appearance.dart index 5339ef6dca..44a90b8d9b 100644 --- a/frontend/appflowy_flutter/lib/workspace/application/settings/appearance/mobile_appearance.dart +++ b/frontend/appflowy_flutter/lib/workspace/application/settings/appearance/mobile_appearance.dart @@ -10,6 +10,7 @@ class MobileAppearance extends BaseAppearance { static const _onBackgroundColor = Color(0xff2F3030); // text/title color static const _onSurfaceColor = Color(0xff676666); // text/body color static const _onSecondaryColor = Color(0xFFC5C7CB); // text/body2 color + static const _hintColorInDarkMode = Color(0xff626262); // hint color @override ThemeData getThemeData( @@ -60,13 +61,14 @@ class MobileAppearance extends BaseAppearance { primary: _primaryColor, onPrimary: Colors.white, // TODO(yijing): add color later - secondary: Colors.black, + secondary: const Color(0xff2d2d2d), //temp onSecondary: Colors.white, + tertiary: const Color(0xff858585), // temp error: const Color(0xffFB006D), onError: const Color(0xffFB006D), - background: const Color(0xff1C1C1E), // BG/Secondary color + background: const Color(0xff121212), // temp onBackground: Colors.white, - outline: const Color(0xff96989C), //caption + outline: _hintColorInDarkMode, outlineVariant: Colors.black, //Snack bar surface: const Color(0xff2F3030), @@ -75,7 +77,7 @@ class MobileAppearance extends BaseAppearance { final hintColor = brightness == Brightness.light ? const Color(0xff89909B) - : const Color(0xff96989C); + : _hintColorInDarkMode; return ThemeData( // color