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:
Yijing Huang 2023-12-02 04:46:02 -07:00 committed by GitHub
parent 5ff6405f7f
commit 9824d5980a
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
5 changed files with 162 additions and 39 deletions

View File

@ -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,27 +47,31 @@ class _NumberCellState extends State<MobileTextCardCell> {
child: BlocBuilder<TextCellBloc, TextCellState>(
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(),
),
);
},
),
);

View File

@ -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,
);

View File

@ -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<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),
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;
},
);
}

View File

@ -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'),

View File

@ -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