mirror of
https://github.com/AppFlowy-IO/AppFlowy.git
synced 2024-08-30 18:12:39 +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;
|
final CellControllerBuilder cellControllerBuilder;
|
||||||
|
|
||||||
@override
|
@override
|
||||||
State<MobileTextCardCell> createState() => _NumberCellState();
|
State<MobileTextCardCell> createState() => _MobileTextCardCellState();
|
||||||
}
|
}
|
||||||
|
|
||||||
class _NumberCellState extends State<MobileTextCardCell> {
|
class _MobileTextCardCellState extends State<MobileTextCardCell> {
|
||||||
late final TextCellBloc _cellBloc;
|
late final TextCellBloc _cellBloc;
|
||||||
|
|
||||||
@override
|
@override
|
||||||
@ -47,9 +47,8 @@ class _NumberCellState extends State<MobileTextCardCell> {
|
|||||||
child: BlocBuilder<TextCellBloc, TextCellState>(
|
child: BlocBuilder<TextCellBloc, TextCellState>(
|
||||||
buildWhen: (previous, current) => previous.content != current.content,
|
buildWhen: (previous, current) => previous.content != current.content,
|
||||||
builder: (context, state) {
|
builder: (context, state) {
|
||||||
if (state.content.isEmpty) {
|
// return custom widget if render hook is provided(for example, the title of the card on board view)
|
||||||
return const SizedBox();
|
// if widget.cardData.isEmpty means there is no data for this cell
|
||||||
} else {
|
|
||||||
final Widget? custom = widget.renderHook?.call(
|
final Widget? custom = widget.renderHook?.call(
|
||||||
state.content,
|
state.content,
|
||||||
widget.cardData,
|
widget.cardData,
|
||||||
@ -59,6 +58,12 @@ class _NumberCellState extends State<MobileTextCardCell> {
|
|||||||
return custom;
|
return custom;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// if there is no render hook
|
||||||
|
// the empty text cell will be hidden
|
||||||
|
if (state.content.isEmpty) {
|
||||||
|
return const SizedBox();
|
||||||
|
}
|
||||||
|
|
||||||
return Container(
|
return Container(
|
||||||
alignment: Alignment.centerLeft,
|
alignment: Alignment.centerLeft,
|
||||||
padding: cellStyle.padding,
|
padding: cellStyle.padding,
|
||||||
@ -67,7 +72,6 @@ class _NumberCellState extends State<MobileTextCardCell> {
|
|||||||
style: cellStyle.primaryTextStyle(),
|
style: cellStyle.primaryTextStyle(),
|
||||||
),
|
),
|
||||||
);
|
);
|
||||||
}
|
|
||||||
},
|
},
|
||||||
),
|
),
|
||||||
);
|
);
|
||||||
|
@ -72,16 +72,17 @@ class MobileCardContent<CustomCardData> extends StatelessWidget {
|
|||||||
DatabaseCellContext cellContext,
|
DatabaseCellContext cellContext,
|
||||||
) {
|
) {
|
||||||
final renderHook = RowCardRenderHook<String>();
|
final renderHook = RowCardRenderHook<String>();
|
||||||
renderHook.addTextCellHook((cellData, _, __) {
|
renderHook.addTextCellHook((cellData, cardData, __) {
|
||||||
return BlocBuilder<TextCellBloc, TextCellState>(
|
return BlocBuilder<TextCellBloc, TextCellState>(
|
||||||
builder: (context, state) {
|
builder: (context, state) {
|
||||||
final text = cellData.isEmpty
|
final cardDataIsEmpty = cardData == null;
|
||||||
|
final text = cardDataIsEmpty
|
||||||
? LocaleKeys.grid_row_titlePlaceholder.tr()
|
? LocaleKeys.grid_row_titlePlaceholder.tr()
|
||||||
: cellData;
|
: cellData;
|
||||||
|
|
||||||
final textStyle = Theme.of(context).textTheme.bodyMedium?.copyWith(
|
final textStyle = Theme.of(context).textTheme.bodyMedium?.copyWith(
|
||||||
color: cellData.isEmpty
|
color: cardDataIsEmpty
|
||||||
? Theme.of(context).colorScheme.onSecondary
|
? Theme.of(context).hintColor
|
||||||
: Theme.of(context).colorScheme.onBackground,
|
: Theme.of(context).colorScheme.onBackground,
|
||||||
fontSize: 20,
|
fontSize: 20,
|
||||||
);
|
);
|
||||||
|
@ -1,3 +1,4 @@
|
|||||||
|
import 'package:flowy_infra/size.dart';
|
||||||
import 'package:flutter/material.dart';
|
import 'package:flutter/material.dart';
|
||||||
import 'package:go_router/go_router.dart';
|
import 'package:go_router/go_router.dart';
|
||||||
|
|
||||||
@ -5,13 +6,22 @@ Future<T?> showFlowyMobileBottomSheet<T>(
|
|||||||
BuildContext context, {
|
BuildContext context, {
|
||||||
required String title,
|
required String title,
|
||||||
required Widget Function(BuildContext) builder,
|
required Widget Function(BuildContext) builder,
|
||||||
|
bool resizeToAvoidBottomInset = true,
|
||||||
bool isScrollControlled = false,
|
bool isScrollControlled = false,
|
||||||
}) async {
|
}) async {
|
||||||
return showModalBottomSheet(
|
return showModalBottomSheet(
|
||||||
context: context,
|
context: context,
|
||||||
isScrollControlled: isScrollControlled,
|
isScrollControlled: isScrollControlled,
|
||||||
builder: (context) => Padding(
|
shape: const RoundedRectangleBorder(
|
||||||
padding: const EdgeInsets.all(16.0),
|
borderRadius: BorderRadius.vertical(
|
||||||
|
top: Corners.s12Radius,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
builder: (context) {
|
||||||
|
const padding = EdgeInsets.fromLTRB(16, 16, 16, 48);
|
||||||
|
|
||||||
|
final child = Padding(
|
||||||
|
padding: padding,
|
||||||
child: Column(
|
child: Column(
|
||||||
mainAxisSize: MainAxisSize.min,
|
mainAxisSize: MainAxisSize.min,
|
||||||
children: [
|
children: [
|
||||||
@ -22,7 +32,19 @@ Future<T?> showFlowyMobileBottomSheet<T>(
|
|||||||
builder(context),
|
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/generated/locale_keys.g.dart';
|
||||||
import 'package:appflowy/user/application/sign_in_bloc.dart';
|
import 'package:appflowy/user/application/sign_in_bloc.dart';
|
||||||
import 'package:appflowy/user/presentation/presentation.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:appflowy/workspace/application/settings/appearance/appearance_cubit.dart';
|
||||||
import 'package:easy_localization/easy_localization.dart';
|
import 'package:easy_localization/easy_localization.dart';
|
||||||
import 'package:flowy_infra/size.dart';
|
import 'package:flowy_infra/size.dart';
|
||||||
@ -76,9 +77,40 @@ class _ThirdPartySignInButton extends StatelessWidget {
|
|||||||
|
|
||||||
final VoidCallback onPressed;
|
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
|
@override
|
||||||
Widget build(BuildContext context) {
|
Widget build(BuildContext context) {
|
||||||
final style = Theme.of(context);
|
final style = Theme.of(context);
|
||||||
|
// In desktop, the width of button is limited by [AuthFormContainer]
|
||||||
return SizedBox(
|
return SizedBox(
|
||||||
height: 48,
|
height: 48,
|
||||||
width: AuthFormContainer.width,
|
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) {
|
void _signInWithGoogle(BuildContext context) {
|
||||||
context.read<SignInBloc>().add(
|
context.read<SignInBloc>().add(
|
||||||
const SignInEvent.signedInWithOAuth('google'),
|
const SignInEvent.signedInWithOAuth('google'),
|
||||||
|
@ -10,6 +10,7 @@ class MobileAppearance extends BaseAppearance {
|
|||||||
static const _onBackgroundColor = Color(0xff2F3030); // text/title color
|
static const _onBackgroundColor = Color(0xff2F3030); // text/title color
|
||||||
static const _onSurfaceColor = Color(0xff676666); // text/body color
|
static const _onSurfaceColor = Color(0xff676666); // text/body color
|
||||||
static const _onSecondaryColor = Color(0xFFC5C7CB); // text/body2 color
|
static const _onSecondaryColor = Color(0xFFC5C7CB); // text/body2 color
|
||||||
|
static const _hintColorInDarkMode = Color(0xff626262); // hint color
|
||||||
|
|
||||||
@override
|
@override
|
||||||
ThemeData getThemeData(
|
ThemeData getThemeData(
|
||||||
@ -60,13 +61,14 @@ class MobileAppearance extends BaseAppearance {
|
|||||||
primary: _primaryColor,
|
primary: _primaryColor,
|
||||||
onPrimary: Colors.white,
|
onPrimary: Colors.white,
|
||||||
// TODO(yijing): add color later
|
// TODO(yijing): add color later
|
||||||
secondary: Colors.black,
|
secondary: const Color(0xff2d2d2d), //temp
|
||||||
onSecondary: Colors.white,
|
onSecondary: Colors.white,
|
||||||
|
tertiary: const Color(0xff858585), // temp
|
||||||
error: const Color(0xffFB006D),
|
error: const Color(0xffFB006D),
|
||||||
onError: const Color(0xffFB006D),
|
onError: const Color(0xffFB006D),
|
||||||
background: const Color(0xff1C1C1E), // BG/Secondary color
|
background: const Color(0xff121212), // temp
|
||||||
onBackground: Colors.white,
|
onBackground: Colors.white,
|
||||||
outline: const Color(0xff96989C), //caption
|
outline: _hintColorInDarkMode,
|
||||||
outlineVariant: Colors.black,
|
outlineVariant: Colors.black,
|
||||||
//Snack bar
|
//Snack bar
|
||||||
surface: const Color(0xff2F3030),
|
surface: const Color(0xff2F3030),
|
||||||
@ -75,7 +77,7 @@ class MobileAppearance extends BaseAppearance {
|
|||||||
|
|
||||||
final hintColor = brightness == Brightness.light
|
final hintColor = brightness == Brightness.light
|
||||||
? const Color(0xff89909B)
|
? const Color(0xff89909B)
|
||||||
: const Color(0xff96989C);
|
: _hintColorInDarkMode;
|
||||||
|
|
||||||
return ThemeData(
|
return ThemeData(
|
||||||
// color
|
// color
|
||||||
|
Loading…
x
Reference in New Issue
Block a user