feat: improve UI in MobileFieldEditor (#4042)

This commit is contained in:
Yijing Huang 2023-11-28 19:38:11 -07:00 committed by GitHub
parent 7da759c662
commit 551e012147
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
15 changed files with 219 additions and 138 deletions

View File

@ -1,4 +1,7 @@
import 'package:appflowy/generated/locale_keys.g.dart';
import 'package:appflowy/mobile/presentation/database/card/card_property_edit/widgets/widgets.dart';
import 'package:appflowy/plugins/database_view/application/field/field_editor_bloc.dart'; import 'package:appflowy/plugins/database_view/application/field/field_editor_bloc.dart';
import 'package:easy_localization/easy_localization.dart';
import 'package:flutter/material.dart'; import 'package:flutter/material.dart';
import 'package:flutter_bloc/flutter_bloc.dart'; import 'package:flutter_bloc/flutter_bloc.dart';
@ -33,13 +36,21 @@ class _MobileFieldNameTextFieldState extends State<MobileFieldNameTextField> {
@override @override
Widget build(BuildContext context) { Widget build(BuildContext context) {
return TextField( return PropertyEditContainer(
controller: controller, padding: EdgeInsets.zero,
onChanged: (newName) { child: TextField(
context controller: controller,
.read<FieldEditorBloc>() decoration: InputDecoration(
.add(FieldEditorEvent.renameField(newName)); hintText: LocaleKeys.board_propertyName.tr(),
}, enabledBorder: InputBorder.none,
focusedBorder: InputBorder.none,
),
onChanged: (newName) {
context
.read<FieldEditorBloc>()
.add(FieldEditorEvent.renameField(newName));
},
),
); );
} }
} }

View File

@ -1,14 +1,12 @@
import 'package:appflowy/generated/locale_keys.g.dart'; import 'package:appflowy/generated/locale_keys.g.dart';
import 'package:appflowy/mobile/presentation/database/card/card_detail/widgets/widgets.dart'; import 'package:appflowy/mobile/presentation/database/card/card_detail/widgets/widgets.dart';
import 'package:appflowy/mobile/presentation/database/card/card_property_edit/mobile_field_type_option_editor.dart'; import 'package:appflowy/mobile/presentation/database/card/card_property_edit/mobile_field_type_option_editor.dart';
import 'package:appflowy/mobile/presentation/database/card/card_property_edit/widgets/property_title.dart'; import 'package:appflowy/mobile/presentation/database/card/card_property_edit/widgets/widgets.dart';
import 'package:appflowy/plugins/database_view/application/field/field_controller.dart'; import 'package:appflowy/plugins/database_view/application/field/field_controller.dart';
import 'package:appflowy/plugins/database_view/application/field/field_editor_bloc.dart'; import 'package:appflowy/plugins/database_view/application/field/field_editor_bloc.dart';
import 'package:appflowy/plugins/database_view/application/field/type_option/type_option_context.dart'; import 'package:appflowy/plugins/database_view/application/field/type_option/type_option_context.dart';
import 'package:appflowy/plugins/database_view/grid/application/row/row_detail_bloc.dart'; import 'package:appflowy/plugins/database_view/grid/application/row/row_detail_bloc.dart';
import 'package:appflowy/plugins/database_view/widgets/setting/field_visibility_extension.dart'; import 'package:appflowy/plugins/database_view/widgets/setting/field_visibility_extension.dart';
import 'package:appflowy/workspace/presentation/widgets/toggle/toggle.dart';
import 'package:appflowy/workspace/presentation/widgets/toggle/toggle_style.dart';
import 'package:appflowy_backend/protobuf/flowy-database2/protobuf.dart'; import 'package:appflowy_backend/protobuf/flowy-database2/protobuf.dart';
import 'package:easy_localization/easy_localization.dart'; import 'package:easy_localization/easy_localization.dart';
import 'package:flowy_infra_ui/widget/spacing.dart'; import 'package:flowy_infra_ui/widget/spacing.dart';
@ -51,38 +49,42 @@ class MobileFieldEditor extends StatelessWidget {
context.read<FieldEditorBloc>().typeOptionController; context.read<FieldEditorBloc>().typeOptionController;
final fieldInfoVisibility = final fieldInfoVisibility =
fieldController.getField(field.id)?.visibility; fieldController.getField(field.id)?.visibility;
return Padding( return Container(
padding: const EdgeInsets.all(16), padding: const EdgeInsets.all(16),
color: Theme.of(context).colorScheme.secondary,
child: Column( child: Column(
crossAxisAlignment: CrossAxisAlignment.start, crossAxisAlignment: CrossAxisAlignment.start,
children: [ children: [
// TODO(yijing): improve hint text PropertyEditGroupTitle(LocaleKeys.settings_user_name.tr()),
PropertyTitle(LocaleKeys.settings_user_name.tr()),
BlocSelector<FieldEditorBloc, FieldEditorState, String>( BlocSelector<FieldEditorBloc, FieldEditorState, String>(
selector: (state) => state.field.name, selector: (state) => state.field.name,
builder: (context, fieldName) => builder: (context, fieldName) =>
MobileFieldNameTextField(text: fieldName), MobileFieldNameTextField(text: fieldName),
), ),
Row( const VSpace(16),
children: [ PropertyEditGroupTitle(LocaleKeys.grid_field_visibility.tr()),
Expanded( PropertyEditContainer(
child: PropertyTitle( child: Row(
LocaleKeys.grid_field_visibility.tr(), mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: [
PropertyTitle(
LocaleKeys.board_showOnCard.tr(),
), ),
), VisibilitySwitch(
VisibilitySwitch( isFieldHidden: !(fieldInfoVisibility != null
isFieldHidden: !(fieldInfoVisibility != null ? fieldInfoVisibility.isVisibleState()
? fieldInfoVisibility.isVisibleState() : field.visibility),
: field.visibility), onChanged: () => context.read<RowDetailBloc>().add(
onChanged: () => context.read<RowDetailBloc>().add( RowDetailEvent.toggleFieldVisibility(
RowDetailEvent.toggleFieldVisibility( state.field.id,
state.field.id, ),
), ),
), ),
), ],
], ),
), ),
const VSpace(8), const VSpace(16),
PropertyEditGroupTitle(LocaleKeys.board_setting.tr()),
// edit property type and settings // edit property type and settings
if (!typeOptionLoader.field.isPrimary) if (!typeOptionLoader.field.isPrimary)
MobileFieldTypeOptionEditor(dataController: dataController), MobileFieldTypeOptionEditor(dataController: dataController),
@ -114,11 +116,10 @@ class _VisibilitySwitchState extends State<VisibilitySwitch> {
@override @override
Widget build(BuildContext context) { Widget build(BuildContext context) {
return Toggle( return Switch.adaptive(
padding: EdgeInsets.zero,
value: !_isFieldHidden, value: !_isFieldHidden,
style: ToggleStyle.mobile, activeColor: Theme.of(context).colorScheme.primary,
onChanged: (_) { onChanged: (value) {
setState(() { setState(() {
_isFieldHidden = !_isFieldHidden; _isFieldHidden = !_isFieldHidden;
widget.onChanged?.call(); widget.onChanged?.call();

View File

@ -1,6 +1,7 @@
import 'package:appflowy/generated/flowy_svgs.g.dart'; 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/mobile/presentation/database/card/card_property_edit/type_option_widget_builder/type_option_widget_builder.dart'; import 'package:appflowy/mobile/presentation/database/card/card_property_edit/type_option_widget_builder/type_option_widget_builder.dart';
import 'package:appflowy/mobile/presentation/database/card/card_property_edit/widgets/widgets.dart';
import 'package:appflowy/mobile/presentation/widgets/show_flowy_mobile_bottom_sheet.dart'; import 'package:appflowy/mobile/presentation/widgets/show_flowy_mobile_bottom_sheet.dart';
import 'package:appflowy/plugins/database_view/application/field/field_type_option_edit_bloc.dart'; import 'package:appflowy/plugins/database_view/application/field/field_type_option_edit_bloc.dart';
import 'package:appflowy/plugins/database_view/application/field/type_option/type_option_data_controller.dart'; import 'package:appflowy/plugins/database_view/application/field/type_option/type_option_data_controller.dart';
@ -59,24 +60,25 @@ class _MobileSwitchFieldButton extends StatelessWidget {
(FieldTypeOptionEditBloc bloc) => bloc.state.field.fieldType, (FieldTypeOptionEditBloc bloc) => bloc.state.field.fieldType,
); );
return GestureDetector( return GestureDetector(
child: Row( child: PropertyEditContainer(
children: [ child: Row(
Text( children: [
LocaleKeys.grid_field_propertyType.tr(), PropertyTitle(
style: Theme.of(context).textTheme.titleMedium, LocaleKeys.grid_field_propertyType.tr(),
), ),
const Spacer(), const Spacer(),
FlowySvg(fieldType.icon()), FlowySvg(fieldType.icon()),
const HSpace(4), const HSpace(4),
Text( Text(
fieldType.title(), fieldType.title(),
style: Theme.of(context).textTheme.titleMedium, style: Theme.of(context).textTheme.titleMedium,
), ),
Icon( Icon(
Icons.arrow_forward_ios_sharp, Icons.arrow_forward_ios_sharp,
color: Theme.of(context).hintColor, color: Theme.of(context).hintColor,
), ),
], ],
),
), ),
onTap: () => showFlowyMobileBottomSheet( onTap: () => showFlowyMobileBottomSheet(
context, context,

View File

@ -1,3 +1,4 @@
import 'package:appflowy/mobile/presentation/database/card/card_property_edit/widgets/widgets.dart';
import 'package:appflowy/mobile/presentation/database/card/row/cells/date_cell/widgets/widgets.dart'; import 'package:appflowy/mobile/presentation/database/card/row/cells/date_cell/widgets/widgets.dart';
import 'package:appflowy/plugins/database_view/application/field/type_option/date_bloc.dart'; import 'package:appflowy/plugins/database_view/application/field/type_option/date_bloc.dart';
import 'package:appflowy/plugins/database_view/application/field/type_option/type_option_context.dart'; import 'package:appflowy/plugins/database_view/application/field/type_option/type_option_context.dart';
@ -38,39 +39,43 @@ class DateTypeOptionMobileWidget extends TypeOptionWidget {
typeOptionContext.typeOption = state.typeOption, typeOptionContext.typeOption = state.typeOption,
builder: (context, state) { builder: (context, state) {
final List<Widget> children = [ final List<Widget> children = [
DateFormatListTile( PropertyEditContainer(
currentFormatStr: state.typeOption.dateFormat.title(), child: DateFormatListTile(
groupValue: context currentFormatStr: state.typeOption.dateFormat.title(),
.watch<DateTypeOptionBloc>() groupValue: context
.state .watch<DateTypeOptionBloc>()
.typeOption .state
.dateFormat, .typeOption
onChanged: (newFormat) { .dateFormat,
if (newFormat != null) { onChanged: (newFormat) {
context.read<DateTypeOptionBloc>().add( if (newFormat != null) {
DateTypeOptionEvent.didSelectDateFormat( context.read<DateTypeOptionBloc>().add(
newFormat, DateTypeOptionEvent.didSelectDateFormat(
), newFormat,
); ),
} );
}, }
},
),
), ),
TimeFormatListTile( PropertyEditContainer(
currentFormatStr: state.typeOption.timeFormat.title(), child: TimeFormatListTile(
groupValue: context currentFormatStr: state.typeOption.timeFormat.title(),
.watch<DateTypeOptionBloc>() groupValue: context
.state .watch<DateTypeOptionBloc>()
.typeOption .state
.timeFormat, .typeOption
onChanged: (newFormat) { .timeFormat,
if (newFormat != null) { onChanged: (newFormat) {
context.read<DateTypeOptionBloc>().add( if (newFormat != null) {
DateTypeOptionEvent.didSelectTimeFormat( context.read<DateTypeOptionBloc>().add(
newFormat, DateTypeOptionEvent.didSelectTimeFormat(
), newFormat,
); ),
} );
}, }
},
),
), ),
]; ];

View File

@ -1,4 +1,4 @@
import 'package:appflowy/mobile/presentation/database/card/card_property_edit/widgets/property_title.dart'; import 'package:appflowy/mobile/presentation/database/card/card_property_edit/widgets/widgets.dart';
import 'package:appflowy/mobile/presentation/widgets/widgets.dart'; import 'package:appflowy/mobile/presentation/widgets/widgets.dart';
import 'package:appflowy/plugins/database_view/application/field/type_option/number_bloc.dart'; import 'package:appflowy/plugins/database_view/application/field/type_option/number_bloc.dart';
import 'package:appflowy/plugins/database_view/application/field/type_option/number_format_bloc.dart'; import 'package:appflowy/plugins/database_view/application/field/type_option/number_format_bloc.dart';
@ -46,20 +46,22 @@ class NumberTypeOptionMobileWidget extends TypeOptionWidget {
typeOptionContext.typeOption = state.typeOption, typeOptionContext.typeOption = state.typeOption,
builder: (context, state) { builder: (context, state) {
return GestureDetector( return GestureDetector(
child: Row( child: PropertyEditContainer(
children: [ child: Row(
PropertyTitle(LocaleKeys.grid_field_numberFormat.tr()), children: [
const Spacer(), PropertyTitle(LocaleKeys.grid_field_numberFormat.tr()),
const HSpace(4), const Spacer(),
Text( const HSpace(4),
state.typeOption.format.title(), Text(
style: Theme.of(context).textTheme.titleMedium, state.typeOption.format.title(),
), style: Theme.of(context).textTheme.titleMedium,
Icon( ),
Icons.arrow_forward_ios, Icon(
color: Theme.of(context).hintColor, Icons.arrow_forward_ios,
), color: Theme.of(context).hintColor,
], ),
],
),
), ),
onTap: () => showFlowyMobileBottomSheet( onTap: () => showFlowyMobileBottomSheet(
context, context,

View File

@ -1,3 +1,4 @@
import 'package:appflowy/mobile/presentation/database/card/card_property_edit/widgets/widgets.dart';
import 'package:appflowy/mobile/presentation/database/card/row/cells/date_cell/widgets/widgets.dart'; import 'package:appflowy/mobile/presentation/database/card/row/cells/date_cell/widgets/widgets.dart';
import 'package:appflowy/plugins/database_view/application/field/type_option/timestamp_bloc.dart'; import 'package:appflowy/plugins/database_view/application/field/type_option/timestamp_bloc.dart';
import 'package:appflowy/plugins/database_view/application/field/type_option/type_option_context.dart'; import 'package:appflowy/plugins/database_view/application/field/type_option/type_option_context.dart';
@ -39,49 +40,53 @@ class TimestampTypeOptionMobileWidget extends TypeOptionWidget {
typeOptionContext.typeOption = state.typeOption, typeOptionContext.typeOption = state.typeOption,
builder: (context, state) { builder: (context, state) {
final List<Widget> children = [ final List<Widget> children = [
// used to add a separator padding at the top PropertyEditContainer(
const SizedBox.shrink(), child: DateFormatListTile(
DateFormatListTile( currentFormatStr: state.typeOption.dateFormat.title(),
currentFormatStr: state.typeOption.dateFormat.title(),
groupValue: context
.watch<TimestampTypeOptionBloc>()
.state
.typeOption
.dateFormat,
onChanged: (newFormat) {
if (newFormat != null) {
context.read<TimestampTypeOptionBloc>().add(
TimestampTypeOptionEvent.didSelectDateFormat(
newFormat,
),
);
}
},
),
IncludeTimeSwitch(
switchValue: state.typeOption.includeTime,
onChanged: (value) => context
.read<TimestampTypeOptionBloc>()
.add(TimestampTypeOptionEvent.includeTime(value)),
),
if (state.typeOption.includeTime)
TimeFormatListTile(
currentFormatStr: state.typeOption.timeFormat.title(),
groupValue: context groupValue: context
.watch<TimestampTypeOptionBloc>() .watch<TimestampTypeOptionBloc>()
.state .state
.typeOption .typeOption
.timeFormat, .dateFormat,
onChanged: (newFormat) { onChanged: (newFormat) {
if (newFormat != null) { if (newFormat != null) {
context.read<TimestampTypeOptionBloc>().add( context.read<TimestampTypeOptionBloc>().add(
TimestampTypeOptionEvent.didSelectTimeFormat( TimestampTypeOptionEvent.didSelectDateFormat(
newFormat, newFormat,
), ),
); );
} }
}, },
), ),
),
PropertyEditContainer(
child: IncludeTimeSwitch(
switchValue: state.typeOption.includeTime,
onChanged: (value) => context
.read<TimestampTypeOptionBloc>()
.add(TimestampTypeOptionEvent.includeTime(value)),
),
),
if (state.typeOption.includeTime)
PropertyEditContainer(
child: TimeFormatListTile(
currentFormatStr: state.typeOption.timeFormat.title(),
groupValue: context
.watch<TimestampTypeOptionBloc>()
.state
.typeOption
.timeFormat,
onChanged: (newFormat) {
if (newFormat != null) {
context.read<TimestampTypeOptionBloc>().add(
TimestampTypeOptionEvent.didSelectTimeFormat(
newFormat,
),
);
}
},
),
),
]; ];
return ListView.separated( return ListView.separated(

View File

@ -0,0 +1,26 @@
import 'package:flutter/material.dart';
class PropertyEditContainer extends StatelessWidget {
const PropertyEditContainer({
super.key,
required this.child,
this.padding,
});
final Widget child;
final EdgeInsets? padding;
@override
Widget build(BuildContext context) {
return Container(
height: 50,
decoration: BoxDecoration(
color: Theme.of(context).colorScheme.surface,
borderRadius: BorderRadius.circular(6),
),
alignment: Alignment.centerLeft,
padding: padding ?? const EdgeInsets.symmetric(horizontal: 8),
child: child,
);
}
}

View File

@ -0,0 +1,20 @@
import 'package:flutter/material.dart';
class PropertyEditGroupTitle extends StatelessWidget {
const PropertyEditGroupTitle(this.name, {super.key});
final String name;
@override
Widget build(BuildContext context) {
return Padding(
padding: const EdgeInsets.symmetric(vertical: 8),
child: Text(
name,
style: Theme.of(context).textTheme.bodyMedium?.copyWith(
color: Theme.of(context).colorScheme.onSurface,
),
),
);
}
}

View File

@ -11,7 +11,10 @@ class PropertyTitle extends StatelessWidget {
padding: const EdgeInsets.symmetric(vertical: 8), padding: const EdgeInsets.symmetric(vertical: 8),
child: Text( child: Text(
name, name,
style: Theme.of(context).textTheme.titleMedium, style: Theme.of(context).textTheme.bodyMedium?.copyWith(
color: Theme.of(context).colorScheme.onBackground,
fontSize: 16,
),
), ),
); );
} }

View File

@ -0,0 +1,3 @@
export 'property_edit_group_title.dart';
export 'property_title.dart';
export 'property_edit_container.dart';

View File

@ -1,4 +1,5 @@
import 'package:appflowy/generated/locale_keys.g.dart'; import 'package:appflowy/generated/locale_keys.g.dart';
import 'package:appflowy/mobile/presentation/database/card/card_property_edit/widgets/widgets.dart';
import 'package:appflowy/mobile/presentation/widgets/widgets.dart'; import 'package:appflowy/mobile/presentation/widgets/widgets.dart';
import 'package:appflowy_backend/protobuf/flowy-database2/date_entities.pb.dart'; import 'package:appflowy_backend/protobuf/flowy-database2/date_entities.pb.dart';
import 'package:easy_localization/easy_localization.dart'; import 'package:easy_localization/easy_localization.dart';
@ -27,9 +28,8 @@ class DateFormatListTile extends StatelessWidget {
final style = Theme.of(context); final style = Theme.of(context);
return Row( return Row(
children: [ children: [
Text( PropertyTitle(
LocaleKeys.grid_field_dateFormat.tr(), LocaleKeys.grid_field_dateFormat.tr(),
style: style.textTheme.titleMedium,
), ),
const Spacer(), const Spacer(),
GestureDetector( GestureDetector(

View File

@ -1,4 +1,5 @@
import 'package:appflowy/generated/locale_keys.g.dart'; import 'package:appflowy/generated/locale_keys.g.dart';
import 'package:appflowy/mobile/presentation/database/card/card_property_edit/widgets/property_title.dart';
import 'package:easy_localization/easy_localization.dart'; import 'package:easy_localization/easy_localization.dart';
import 'package:flutter/material.dart'; import 'package:flutter/material.dart';
@ -16,9 +17,8 @@ class IncludeTimeSwitch extends StatelessWidget {
Widget build(BuildContext context) { Widget build(BuildContext context) {
return Row( return Row(
children: [ children: [
Text( PropertyTitle(
LocaleKeys.grid_field_includeTime.tr(), LocaleKeys.grid_field_includeTime.tr(),
style: Theme.of(context).textTheme.titleMedium,
), ),
const Spacer(), const Spacer(),
Switch.adaptive( Switch.adaptive(

View File

@ -1,4 +1,5 @@
import 'package:appflowy/generated/locale_keys.g.dart'; import 'package:appflowy/generated/locale_keys.g.dart';
import 'package:appflowy/mobile/presentation/database/card/card_property_edit/widgets/widgets.dart';
import 'package:appflowy/mobile/presentation/widgets/widgets.dart'; import 'package:appflowy/mobile/presentation/widgets/widgets.dart';
import 'package:appflowy_backend/protobuf/flowy-database2/date_entities.pb.dart'; import 'package:appflowy_backend/protobuf/flowy-database2/date_entities.pb.dart';
import 'package:appflowy_backend/protobuf/flowy-database2/date_entities.pbenum.dart'; import 'package:appflowy_backend/protobuf/flowy-database2/date_entities.pbenum.dart';
@ -28,9 +29,8 @@ class TimeFormatListTile extends StatelessWidget {
final style = Theme.of(context); final style = Theme.of(context);
return Row( return Row(
children: [ children: [
Text( PropertyTitle(
LocaleKeys.grid_field_timeFormat.tr(), LocaleKeys.grid_field_timeFormat.tr(),
style: style.textTheme.titleMedium,
), ),
const Spacer(), const Spacer(),
GestureDetector( GestureDetector(

View File

@ -37,8 +37,8 @@ class MobileAppearance extends BaseAppearance {
brightness: brightness, brightness: brightness,
primary: _primaryColor, primary: _primaryColor,
onPrimary: Colors.white, onPrimary: Colors.white,
// TODO(yijing): add color later // group card & property edit background color
secondary: Colors.white, secondary: const Color(0xfff7f8fc), // shade 10
onSecondary: _onSecondaryColor, onSecondary: _onSecondaryColor,
error: const Color(0xffFB006D), error: const Color(0xffFB006D),
onError: const Color(0xffFB006D), onError: const Color(0xffFB006D),

View File

@ -825,6 +825,9 @@
"cardActions": "Card Actions", "cardActions": "Card Actions",
"cardDuplicated": "Card has been duplicated", "cardDuplicated": "Card has been duplicated",
"cardDeleted": "Card has been deleted", "cardDeleted": "Card has been deleted",
"showOnCard": "Show on card detail",
"setting": "Setting",
"propertyName": "Property name",
"menuName": "Board", "menuName": "Board",
"showUngrouped": "Show ungrouped items", "showUngrouped": "Show ungrouped items",
"ungroupedButtonText": "Ungrouped", "ungroupedButtonText": "Ungrouped",