mirror of
				https://github.com/AppFlowy-IO/AppFlowy.git
				synced 2025-11-04 12:03:28 +00:00 
			
		
		
		
	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
This commit is contained in:
		
							parent
							
								
									5ff6405f7f
								
							
						
					
					
						commit
						9824d5980a
					
				@ -17,10 +17,10 @@ class MobileTextCardCell<CustomCardData> extends CardCell {
 | 
			
		||||
  final CellControllerBuilder cellControllerBuilder;
 | 
			
		||||
 | 
			
		||||
  @override
 | 
			
		||||
  State<MobileTextCardCell> createState() => _NumberCellState();
 | 
			
		||||
  State<MobileTextCardCell> createState() => _MobileTextCardCellState();
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
class _NumberCellState extends State<MobileTextCardCell> {
 | 
			
		||||
class _MobileTextCardCellState extends State<MobileTextCardCell> {
 | 
			
		||||
  late final TextCellBloc _cellBloc;
 | 
			
		||||
 | 
			
		||||
  @override
 | 
			
		||||
@ -47,9 +47,8 @@ class _NumberCellState extends State<MobileTextCardCell> {
 | 
			
		||||
      child: BlocBuilder<TextCellBloc, TextCellState>(
 | 
			
		||||
        buildWhen: (previous, current) => previous.content != current.content,
 | 
			
		||||
        builder: (context, state) {
 | 
			
		||||
          if (state.content.isEmpty) {
 | 
			
		||||
            return const SizedBox();
 | 
			
		||||
          } else {
 | 
			
		||||
          // 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,
 | 
			
		||||
@ -59,6 +58,12 @@ class _NumberCellState extends State<MobileTextCardCell> {
 | 
			
		||||
            return custom;
 | 
			
		||||
          }
 | 
			
		||||
 | 
			
		||||
          // if there is no render hook
 | 
			
		||||
          // the empty text cell will be hidden
 | 
			
		||||
          if (state.content.isEmpty) {
 | 
			
		||||
            return const SizedBox();
 | 
			
		||||
          }
 | 
			
		||||
 | 
			
		||||
          return Container(
 | 
			
		||||
            alignment: Alignment.centerLeft,
 | 
			
		||||
            padding: cellStyle.padding,
 | 
			
		||||
@ -67,7 +72,6 @@ class _NumberCellState extends State<MobileTextCardCell> {
 | 
			
		||||
              style: cellStyle.primaryTextStyle(),
 | 
			
		||||
            ),
 | 
			
		||||
          );
 | 
			
		||||
          }
 | 
			
		||||
        },
 | 
			
		||||
      ),
 | 
			
		||||
    );
 | 
			
		||||
 | 
			
		||||
@ -72,16 +72,17 @@ class MobileCardContent<CustomCardData> extends StatelessWidget {
 | 
			
		||||
    DatabaseCellContext cellContext,
 | 
			
		||||
  ) {
 | 
			
		||||
    final renderHook = RowCardRenderHook<String>();
 | 
			
		||||
    renderHook.addTextCellHook((cellData, _, __) {
 | 
			
		||||
    renderHook.addTextCellHook((cellData, cardData, __) {
 | 
			
		||||
      return BlocBuilder<TextCellBloc, TextCellState>(
 | 
			
		||||
        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,
 | 
			
		||||
              );
 | 
			
		||||
 | 
			
		||||
@ -1,3 +1,4 @@
 | 
			
		||||
import 'package:flowy_infra/size.dart';
 | 
			
		||||
import 'package:flutter/material.dart';
 | 
			
		||||
import 'package:go_router/go_router.dart';
 | 
			
		||||
 | 
			
		||||
@ -5,13 +6,22 @@ Future<T?> showFlowyMobileBottomSheet<T>(
 | 
			
		||||
  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),
 | 
			
		||||
    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: [
 | 
			
		||||
@ -22,7 +32,19 @@ Future<T?> showFlowyMobileBottomSheet<T>(
 | 
			
		||||
            builder(context),
 | 
			
		||||
          ],
 | 
			
		||||
        ),
 | 
			
		||||
      );
 | 
			
		||||
      if (resizeToAvoidBottomInset) {
 | 
			
		||||
        return AnimatedPadding(
 | 
			
		||||
          padding: padding +
 | 
			
		||||
              EdgeInsets.only(
 | 
			
		||||
                bottom: MediaQuery.of(context).viewInsets.bottom,
 | 
			
		||||
              ),
 | 
			
		||||
          duration: Duration.zero,
 | 
			
		||||
          child: child,
 | 
			
		||||
        );
 | 
			
		||||
      }
 | 
			
		||||
      return child;
 | 
			
		||||
    },
 | 
			
		||||
  );
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
@ -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<SignInBloc>().add(
 | 
			
		||||
        const SignInEvent.signedInWithOAuth('google'),
 | 
			
		||||
 | 
			
		||||
@ -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
 | 
			
		||||
 | 
			
		||||
		Loading…
	
	
			
			x
			
			
		
	
		Reference in New Issue
	
	Block a user