mirror of
https://github.com/AppFlowy-IO/AppFlowy.git
synced 2024-08-30 18:12:39 +00:00
feat: date picker ui revamp (#4044)
* feat: date picker editor * feat: refactor date picker on mobile * chore: update background color * feat: date picker UI revamp * feat: optimize the scroll behavior * chore: remove unused code
This commit is contained in:
@ -202,4 +202,4 @@ SPEC CHECKSUMS:
|
|||||||
|
|
||||||
PODFILE CHECKSUM: 8c681999c7764593c94846b2a64b44d86f7a27ac
|
PODFILE CHECKSUM: 8c681999c7764593c94846b2a64b44d86f7a27ac
|
||||||
|
|
||||||
COCOAPODS: 1.11.3
|
COCOAPODS: 1.12.1
|
||||||
|
@ -2,6 +2,7 @@ import 'package:appflowy/mobile/presentation/bottom_sheet/bottom_sheet.dart';
|
|||||||
import 'package:appflowy/workspace/application/favorite/favorite_bloc.dart';
|
import 'package:appflowy/workspace/application/favorite/favorite_bloc.dart';
|
||||||
import 'package:appflowy/workspace/application/view/view_bloc.dart';
|
import 'package:appflowy/workspace/application/view/view_bloc.dart';
|
||||||
import 'package:appflowy_backend/protobuf/flowy-folder2/protobuf.dart';
|
import 'package:appflowy_backend/protobuf/flowy-folder2/protobuf.dart';
|
||||||
|
import 'package:flowy_infra/size.dart';
|
||||||
import 'package:flowy_infra_ui/flowy_infra_ui.dart' hide WidgetBuilder;
|
import 'package:flowy_infra_ui/flowy_infra_ui.dart' hide WidgetBuilder;
|
||||||
import 'package:flutter/material.dart';
|
import 'package:flutter/material.dart';
|
||||||
import 'package:flutter_bloc/flutter_bloc.dart';
|
import 'package:flutter_bloc/flutter_bloc.dart';
|
||||||
@ -18,7 +19,13 @@ Future<void> showMobileBottomSheet({
|
|||||||
isScrollControlled: true,
|
isScrollControlled: true,
|
||||||
enableDrag: isDragEnabled,
|
enableDrag: isDragEnabled,
|
||||||
useSafeArea: true,
|
useSafeArea: true,
|
||||||
shape: shape,
|
clipBehavior: Clip.antiAlias,
|
||||||
|
shape: shape ??
|
||||||
|
const RoundedRectangleBorder(
|
||||||
|
borderRadius: BorderRadius.vertical(
|
||||||
|
top: Corners.s12Radius,
|
||||||
|
),
|
||||||
|
),
|
||||||
builder: builder,
|
builder: builder,
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
@ -10,7 +10,6 @@ import 'package:appflowy/plugins/database_view/widgets/row/accessory/cell_access
|
|||||||
import 'package:appflowy/plugins/database_view/widgets/row/cell_builder.dart';
|
import 'package:appflowy/plugins/database_view/widgets/row/cell_builder.dart';
|
||||||
import 'package:appflowy/plugins/database_view/widgets/row/cells/cells.dart';
|
import 'package:appflowy/plugins/database_view/widgets/row/cells/cells.dart';
|
||||||
import 'package:appflowy/plugins/database_view/widgets/row/row_property.dart';
|
import 'package:appflowy/plugins/database_view/widgets/row/row_property.dart';
|
||||||
|
|
||||||
import 'package:appflowy_backend/protobuf/flowy-database2/field_entities.pb.dart';
|
import 'package:appflowy_backend/protobuf/flowy-database2/field_entities.pb.dart';
|
||||||
import 'package:easy_localization/easy_localization.dart';
|
import 'package:easy_localization/easy_localization.dart';
|
||||||
import 'package:flowy_infra_ui/flowy_infra_ui.dart';
|
import 'package:flowy_infra_ui/flowy_infra_ui.dart';
|
||||||
|
@ -1,7 +1,5 @@
|
|||||||
export 'mobile_text_cell.dart';
|
|
||||||
export 'mobile_number_cell.dart';
|
|
||||||
export 'mobile_timestamp_cell.dart';
|
|
||||||
export 'mobile_checkbox_cell.dart';
|
export 'mobile_checkbox_cell.dart';
|
||||||
|
export 'mobile_number_cell.dart';
|
||||||
|
export 'mobile_text_cell.dart';
|
||||||
|
export 'mobile_timestamp_cell.dart';
|
||||||
export 'mobile_url_cell.dart';
|
export 'mobile_url_cell.dart';
|
||||||
export 'date_cell/mobile_date_cell.dart';
|
|
||||||
export 'date_cell/mobile_date_cell_edit_screen.dart';
|
|
||||||
|
@ -1,103 +0,0 @@
|
|||||||
import 'package:appflowy/plugins/database_view/application/cell/cell_controller_builder.dart';
|
|
||||||
import 'package:appflowy/plugins/database_view/widgets/row/cell_builder.dart';
|
|
||||||
import 'package:appflowy/plugins/database_view/widgets/row/cells/date_cell/date_cell_bloc.dart';
|
|
||||||
import 'package:flutter/material.dart';
|
|
||||||
import 'package:flutter_bloc/flutter_bloc.dart';
|
|
||||||
import 'package:go_router/go_router.dart';
|
|
||||||
|
|
||||||
import 'mobile_date_cell_edit_screen.dart';
|
|
||||||
|
|
||||||
class MobileDateCell extends GridCellWidget {
|
|
||||||
MobileDateCell({
|
|
||||||
super.key,
|
|
||||||
required this.cellControllerBuilder,
|
|
||||||
required this.hintText,
|
|
||||||
});
|
|
||||||
|
|
||||||
final CellControllerBuilder cellControllerBuilder;
|
|
||||||
final String? hintText;
|
|
||||||
|
|
||||||
@override
|
|
||||||
GridCellState<MobileDateCell> createState() => _DateCellState();
|
|
||||||
}
|
|
||||||
|
|
||||||
class _DateCellState extends GridCellState<MobileDateCell> {
|
|
||||||
late final DateCellBloc _cellBloc;
|
|
||||||
|
|
||||||
@override
|
|
||||||
void initState() {
|
|
||||||
final cellController =
|
|
||||||
widget.cellControllerBuilder.build() as DateCellController;
|
|
||||||
_cellBloc = DateCellBloc(cellController: cellController)
|
|
||||||
..add(const DateCellEvent.initial());
|
|
||||||
super.initState();
|
|
||||||
}
|
|
||||||
|
|
||||||
@override
|
|
||||||
Widget build(BuildContext context) {
|
|
||||||
return BlocProvider.value(
|
|
||||||
value: _cellBloc,
|
|
||||||
child: BlocBuilder<DateCellBloc, DateCellState>(
|
|
||||||
builder: (context, state) {
|
|
||||||
// full screen show the date edit screen
|
|
||||||
return GestureDetector(
|
|
||||||
onTap: () => context.push(
|
|
||||||
MobileDateCellEditScreen.routeName,
|
|
||||||
extra: {
|
|
||||||
MobileDateCellEditScreen.argCellController:
|
|
||||||
widget.cellControllerBuilder.build() as DateCellController,
|
|
||||||
},
|
|
||||||
),
|
|
||||||
child: SizedBox(
|
|
||||||
width: double.infinity,
|
|
||||||
child: MobileDateCellText(
|
|
||||||
dateStr: state.dateStr,
|
|
||||||
placeholder: widget.hintText ?? "",
|
|
||||||
),
|
|
||||||
),
|
|
||||||
);
|
|
||||||
},
|
|
||||||
),
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
@override
|
|
||||||
Future<void> dispose() async {
|
|
||||||
_cellBloc.close();
|
|
||||||
super.dispose();
|
|
||||||
}
|
|
||||||
|
|
||||||
@override
|
|
||||||
void requestBeginFocus() {}
|
|
||||||
|
|
||||||
@override
|
|
||||||
String? onCopy() => _cellBloc.state.dateStr;
|
|
||||||
}
|
|
||||||
|
|
||||||
class MobileDateCellText extends StatelessWidget {
|
|
||||||
const MobileDateCellText({
|
|
||||||
super.key,
|
|
||||||
required this.dateStr,
|
|
||||||
required this.placeholder,
|
|
||||||
});
|
|
||||||
|
|
||||||
final String dateStr;
|
|
||||||
final String placeholder;
|
|
||||||
|
|
||||||
@override
|
|
||||||
Widget build(BuildContext context) {
|
|
||||||
final isPlaceholder = dateStr.isEmpty;
|
|
||||||
final text = isPlaceholder ? placeholder : dateStr;
|
|
||||||
return Align(
|
|
||||||
alignment: Alignment.centerLeft,
|
|
||||||
child: Text(
|
|
||||||
text,
|
|
||||||
style: Theme.of(context).textTheme.titleMedium?.copyWith(
|
|
||||||
color: isPlaceholder
|
|
||||||
? Theme.of(context).hintColor
|
|
||||||
: Theme.of(context).colorScheme.onBackground,
|
|
||||||
),
|
|
||||||
),
|
|
||||||
);
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,10 +1,11 @@
|
|||||||
|
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/widgets/widgets.dart';
|
import 'package:appflowy/mobile/presentation/widgets/widgets.dart';
|
||||||
|
import 'package:appflowy/plugins/base/drag_handler.dart';
|
||||||
import 'package:appflowy/plugins/database_view/application/cell/cell_controller_builder.dart';
|
import 'package:appflowy/plugins/database_view/application/cell/cell_controller_builder.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/presentation/widgets/header/type_option/date.dart';
|
|
||||||
import 'package:appflowy/plugins/database_view/widgets/row/cells/date_cell/date_cal_bloc.dart';
|
import 'package:appflowy/plugins/database_view/widgets/row/cells/date_cell/date_cal_bloc.dart';
|
||||||
import 'package:appflowy/plugins/database_view/widgets/row/cells/date_cell/date_editor.dart';
|
import 'package:appflowy/plugins/database_view/widgets/row/cells/date_cell/mobile_date_editor.dart';
|
||||||
import 'package:appflowy_backend/log.dart';
|
import 'package:appflowy_backend/log.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-error/errors.pb.dart';
|
import 'package:appflowy_backend/protobuf/flowy-error/errors.pb.dart';
|
||||||
@ -13,19 +14,24 @@ import 'package:easy_localization/easy_localization.dart';
|
|||||||
import 'package:flowy_infra_ui/flowy_infra_ui.dart';
|
import 'package:flowy_infra_ui/flowy_infra_ui.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';
|
||||||
|
import 'package:go_router/go_router.dart';
|
||||||
import 'widgets/widgets.dart';
|
|
||||||
|
|
||||||
class MobileDateCellEditScreen extends StatefulWidget {
|
class MobileDateCellEditScreen extends StatefulWidget {
|
||||||
static const routeName = '/MobileDateCellEditScreen';
|
static const routeName = '/edit_date_cell';
|
||||||
static const argCellController = 'cellController';
|
|
||||||
|
|
||||||
const MobileDateCellEditScreen(
|
// the type is DateCellController
|
||||||
this.cellController, {
|
static const dateCellController = 'date_cell_controller';
|
||||||
|
// bool value, default is true
|
||||||
|
static const fullScreen = 'full_screen';
|
||||||
|
|
||||||
|
const MobileDateCellEditScreen({
|
||||||
super.key,
|
super.key,
|
||||||
|
required this.controller,
|
||||||
|
this.showAsFullScreen = true,
|
||||||
});
|
});
|
||||||
|
|
||||||
final DateCellController cellController;
|
final DateCellController controller;
|
||||||
|
final bool showAsFullScreen;
|
||||||
|
|
||||||
@override
|
@override
|
||||||
State<MobileDateCellEditScreen> createState() =>
|
State<MobileDateCellEditScreen> createState() =>
|
||||||
@ -33,42 +39,116 @@ class MobileDateCellEditScreen extends StatefulWidget {
|
|||||||
}
|
}
|
||||||
|
|
||||||
class _MobileDateCellEditScreenState extends State<MobileDateCellEditScreen> {
|
class _MobileDateCellEditScreenState extends State<MobileDateCellEditScreen> {
|
||||||
|
late final Future<Either<dynamic, FlowyError>> typeOptionFuture;
|
||||||
|
|
||||||
|
@override
|
||||||
|
void initState() {
|
||||||
|
super.initState();
|
||||||
|
|
||||||
|
typeOptionFuture = widget.controller.getTypeOption(
|
||||||
|
DateTypeOptionDataParser(),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
@override
|
@override
|
||||||
Widget build(BuildContext context) {
|
Widget build(BuildContext context) {
|
||||||
|
return widget.showAsFullScreen ? _buildFullScreen() : _buildNotFullScreen();
|
||||||
|
}
|
||||||
|
|
||||||
|
Widget _buildFullScreen() {
|
||||||
return Scaffold(
|
return Scaffold(
|
||||||
appBar: AppBar(
|
appBar: AppBar(
|
||||||
title: Text(LocaleKeys.button_edit.tr()),
|
title: Text(LocaleKeys.titleBar_date.tr()),
|
||||||
),
|
),
|
||||||
body: FutureBuilder<Either<dynamic, FlowyError>>(
|
body: _buildBody(),
|
||||||
future: widget.cellController.getTypeOption(
|
);
|
||||||
DateTypeOptionDataParser(),
|
}
|
||||||
|
|
||||||
|
Widget _buildNotFullScreen() {
|
||||||
|
return DraggableScrollableSheet(
|
||||||
|
expand: false,
|
||||||
|
snap: true,
|
||||||
|
initialChildSize: 0.6,
|
||||||
|
minChildSize: 0.6,
|
||||||
|
builder: (_, controller) => Material(
|
||||||
|
child: SingleChildScrollView(
|
||||||
|
controller: controller,
|
||||||
|
child: Column(
|
||||||
|
children: [
|
||||||
|
const DragHandler(),
|
||||||
|
_buildHeader(),
|
||||||
|
const Divider(),
|
||||||
|
_buildBody(),
|
||||||
|
],
|
||||||
|
),
|
||||||
),
|
),
|
||||||
builder: (BuildContext context, snapshot) {
|
),
|
||||||
if (snapshot.hasData) {
|
);
|
||||||
return snapshot.data!.fold(
|
}
|
||||||
(dateTypeOptionPB) {
|
|
||||||
return _DateCellEditBody(
|
Widget _buildBody() {
|
||||||
dateCellController: widget.cellController,
|
return FutureBuilder<Either<dynamic, FlowyError>>(
|
||||||
dateTypeOptionPB: dateTypeOptionPB,
|
future: typeOptionFuture,
|
||||||
);
|
builder: (context, snapshot) {
|
||||||
},
|
final data = snapshot.data;
|
||||||
(err) {
|
if (data == null) {
|
||||||
Log.error(err);
|
return const Center(
|
||||||
return FlowyMobileStateContainer.error(
|
child: CircularProgressIndicator.adaptive(),
|
||||||
title: LocaleKeys.grid_field_failedToLoadDate.tr(),
|
);
|
||||||
errorMsg: err.toString(),
|
}
|
||||||
);
|
|
||||||
},
|
return data.fold(
|
||||||
|
(dateTypeOptionPB) {
|
||||||
|
return _DateCellEditBody(
|
||||||
|
dateCellController: widget.controller,
|
||||||
|
dateTypeOptionPB: dateTypeOptionPB,
|
||||||
);
|
);
|
||||||
}
|
},
|
||||||
return const Center(child: CircularProgressIndicator.adaptive());
|
(err) {
|
||||||
},
|
Log.error(err);
|
||||||
|
return FlowyMobileStateContainer.error(
|
||||||
|
title: LocaleKeys.grid_field_failedToLoadDate.tr(),
|
||||||
|
errorMsg: err.toString(),
|
||||||
|
);
|
||||||
|
},
|
||||||
|
);
|
||||||
|
},
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
Widget _buildHeader() {
|
||||||
|
const iconWidth = 36.0;
|
||||||
|
const height = 44.0;
|
||||||
|
return Padding(
|
||||||
|
padding: const EdgeInsets.symmetric(horizontal: 8.0),
|
||||||
|
child: Stack(
|
||||||
|
children: [
|
||||||
|
Align(
|
||||||
|
alignment: Alignment.centerLeft,
|
||||||
|
child: FlowyIconButton(
|
||||||
|
icon: const FlowySvg(
|
||||||
|
FlowySvgs.close_s,
|
||||||
|
size: Size.square(iconWidth),
|
||||||
|
),
|
||||||
|
width: iconWidth,
|
||||||
|
iconPadding: EdgeInsets.zero,
|
||||||
|
onPressed: () => context.pop(),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
Align(
|
||||||
|
alignment: Alignment.center,
|
||||||
|
child: FlowyText.medium(
|
||||||
|
LocaleKeys.grid_field_dateFieldName.tr(),
|
||||||
|
fontSize: 18,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
].map((e) => SizedBox(height: height, child: e)).toList(),
|
||||||
),
|
),
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
class _DateCellEditBody extends StatefulWidget {
|
class _DateCellEditBody extends StatelessWidget {
|
||||||
const _DateCellEditBody({
|
const _DateCellEditBody({
|
||||||
required this.dateCellController,
|
required this.dateCellController,
|
||||||
required this.dateTypeOptionPB,
|
required this.dateTypeOptionPB,
|
||||||
@ -77,48 +157,48 @@ class _DateCellEditBody extends StatefulWidget {
|
|||||||
final DateCellController dateCellController;
|
final DateCellController dateCellController;
|
||||||
final DateTypeOptionPB dateTypeOptionPB;
|
final DateTypeOptionPB dateTypeOptionPB;
|
||||||
|
|
||||||
@override
|
|
||||||
State<_DateCellEditBody> createState() => _DateCellEditBodyState();
|
|
||||||
}
|
|
||||||
|
|
||||||
class _DateCellEditBodyState extends State<_DateCellEditBody> {
|
|
||||||
@override
|
@override
|
||||||
Widget build(BuildContext context) {
|
Widget build(BuildContext context) {
|
||||||
return BlocProvider(
|
return BlocProvider(
|
||||||
create: (context) => DateCellCalendarBloc(
|
create: (context) => DateCellCalendarBloc(
|
||||||
dateTypeOptionPB: widget.dateTypeOptionPB,
|
dateTypeOptionPB: dateTypeOptionPB,
|
||||||
cellData: widget.dateCellController.getCellData(),
|
cellData: dateCellController.getCellData(),
|
||||||
cellController: widget.dateCellController,
|
cellController: dateCellController,
|
||||||
)..add(const DateCellCalendarEvent.initial()),
|
)..add(const DateCellCalendarEvent.initial()),
|
||||||
child: BlocBuilder<DateCellCalendarBloc, DateCellCalendarState>(
|
child: const Column(
|
||||||
builder: (context, state) {
|
children: [
|
||||||
final widgetsList = [
|
FlowyOptionDecorateBox(
|
||||||
DateAndTimeDisplay(state),
|
showTopBorder: false,
|
||||||
const DatePicker(),
|
child: MobileDatePicker(),
|
||||||
const _EndDateSwitch(),
|
),
|
||||||
const _IncludeTimeSwitch(),
|
_ColoredDivider(),
|
||||||
const _StartDayTime(),
|
_EndDateSwitch(),
|
||||||
const _EndDayTime(),
|
_IncludeTimeSwitch(),
|
||||||
const Divider(),
|
_StartDayTime(),
|
||||||
const _DateFormatOption(),
|
_EndDayTime(),
|
||||||
const _TimeFormatOption(),
|
_ColoredDivider(),
|
||||||
const Divider(),
|
_DateFormatOption(),
|
||||||
const _ClearDateButton(),
|
_TimeFormatOption(),
|
||||||
];
|
_ClearDateButton(),
|
||||||
return Padding(
|
_ColoredDivider(),
|
||||||
padding: const EdgeInsets.all(16),
|
],
|
||||||
child: ListView.separated(
|
|
||||||
itemBuilder: (context, index) => widgetsList[index],
|
|
||||||
separatorBuilder: (_, __) => const VSpace(8),
|
|
||||||
itemCount: widgetsList.length,
|
|
||||||
),
|
|
||||||
);
|
|
||||||
},
|
|
||||||
),
|
),
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
class _ColoredDivider extends StatelessWidget {
|
||||||
|
const _ColoredDivider();
|
||||||
|
|
||||||
|
@override
|
||||||
|
Widget build(BuildContext context) {
|
||||||
|
return VSpace(
|
||||||
|
20.0,
|
||||||
|
color: Theme.of(context).colorScheme.secondaryContainer,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
class _EndDateSwitch extends StatelessWidget {
|
class _EndDateSwitch extends StatelessWidget {
|
||||||
const _EndDateSwitch();
|
const _EndDateSwitch();
|
||||||
|
|
||||||
@ -127,23 +207,17 @@ class _EndDateSwitch extends StatelessWidget {
|
|||||||
return BlocSelector<DateCellCalendarBloc, DateCellCalendarState, bool>(
|
return BlocSelector<DateCellCalendarBloc, DateCellCalendarState, bool>(
|
||||||
selector: (state) => state.isRange,
|
selector: (state) => state.isRange,
|
||||||
builder: (context, isRange) {
|
builder: (context, isRange) {
|
||||||
return Row(
|
return FlowyOptionTile(
|
||||||
children: [
|
text: LocaleKeys.grid_field_isRange.tr(),
|
||||||
Text(
|
leftIcon: const FlowySvg(FlowySvgs.date_s),
|
||||||
LocaleKeys.grid_field_isRange.tr(),
|
leading: _Switcher(
|
||||||
style: Theme.of(context).textTheme.titleMedium,
|
value: isRange,
|
||||||
),
|
onChanged: (value) {
|
||||||
const Spacer(),
|
context
|
||||||
Switch.adaptive(
|
.read<DateCellCalendarBloc>()
|
||||||
value: isRange,
|
.add(DateCellCalendarEvent.setIsRange(value));
|
||||||
activeColor: Theme.of(context).colorScheme.primary,
|
},
|
||||||
onChanged: (value) {
|
),
|
||||||
context
|
|
||||||
.read<DateCellCalendarBloc>()
|
|
||||||
.add(DateCellCalendarEvent.setIsRange(value));
|
|
||||||
},
|
|
||||||
),
|
|
||||||
],
|
|
||||||
);
|
);
|
||||||
},
|
},
|
||||||
);
|
);
|
||||||
@ -158,13 +232,18 @@ class _IncludeTimeSwitch extends StatelessWidget {
|
|||||||
return BlocSelector<DateCellCalendarBloc, DateCellCalendarState, bool>(
|
return BlocSelector<DateCellCalendarBloc, DateCellCalendarState, bool>(
|
||||||
selector: (state) => state.includeTime,
|
selector: (state) => state.includeTime,
|
||||||
builder: (context, includeTime) {
|
builder: (context, includeTime) {
|
||||||
return IncludeTimeSwitch(
|
return FlowyOptionTile(
|
||||||
switchValue: includeTime,
|
showTopBorder: false,
|
||||||
onChanged: (value) {
|
text: LocaleKeys.grid_field_includeTime.tr(),
|
||||||
context
|
leftIcon: const FlowySvg(FlowySvgs.clock_alarm_s),
|
||||||
.read<DateCellCalendarBloc>()
|
leading: _Switcher(
|
||||||
.add(DateCellCalendarEvent.setIncludeTime(value));
|
value: includeTime,
|
||||||
},
|
onChanged: (value) {
|
||||||
|
context
|
||||||
|
.read<DateCellCalendarBloc>()
|
||||||
|
.add(DateCellCalendarEvent.setIncludeTime(value));
|
||||||
|
},
|
||||||
|
),
|
||||||
);
|
);
|
||||||
},
|
},
|
||||||
);
|
);
|
||||||
@ -300,14 +379,9 @@ class _ClearDateButton extends StatelessWidget {
|
|||||||
|
|
||||||
@override
|
@override
|
||||||
Widget build(BuildContext context) {
|
Widget build(BuildContext context) {
|
||||||
return GestureDetector(
|
return FlowyOptionTile(
|
||||||
child: Align(
|
showTopBorder: false,
|
||||||
alignment: Alignment.centerLeft,
|
text: LocaleKeys.grid_field_clearDate.tr(),
|
||||||
child: Text(
|
|
||||||
LocaleKeys.grid_field_clearDate.tr(),
|
|
||||||
style: Theme.of(context).textTheme.titleMedium,
|
|
||||||
),
|
|
||||||
),
|
|
||||||
onTap: () => context
|
onTap: () => context
|
||||||
.read<DateCellCalendarBloc>()
|
.read<DateCellCalendarBloc>()
|
||||||
.add(const DateCellCalendarEvent.clearDate()),
|
.add(const DateCellCalendarEvent.clearDate()),
|
||||||
@ -324,20 +398,25 @@ class _TimeFormatOption extends StatelessWidget {
|
|||||||
TimeFormatPB>(
|
TimeFormatPB>(
|
||||||
selector: (state) => state.dateTypeOptionPB.timeFormat,
|
selector: (state) => state.dateTypeOptionPB.timeFormat,
|
||||||
builder: (context, state) {
|
builder: (context, state) {
|
||||||
return TimeFormatListTile(
|
return FlowyOptionTile(
|
||||||
currentFormatStr: state.title(),
|
showTopBorder: false,
|
||||||
groupValue: context
|
text: LocaleKeys.settings_appearance_timeFormat_label.tr(),
|
||||||
.watch<DateCellCalendarBloc>()
|
leftIcon: const FlowySvg(FlowySvgs.time_s),
|
||||||
.state
|
|
||||||
.dateTypeOptionPB
|
|
||||||
.timeFormat,
|
|
||||||
onChanged: (newFormat) {
|
|
||||||
if (newFormat == null) return;
|
|
||||||
context
|
|
||||||
.read<DateCellCalendarBloc>()
|
|
||||||
.add(DateCellCalendarEvent.setTimeFormat(newFormat));
|
|
||||||
},
|
|
||||||
);
|
);
|
||||||
|
// TimeFormatListTile(
|
||||||
|
// currentFormatStr: state.title(),
|
||||||
|
// groupValue: context
|
||||||
|
// .watch<DateCellCalendarBloc>()
|
||||||
|
// .state
|
||||||
|
// .dateTypeOptionPB
|
||||||
|
// .timeFormat,
|
||||||
|
// onChanged: (newFormat) {
|
||||||
|
// if (newFormat == null) return;
|
||||||
|
// context
|
||||||
|
// .read<DateCellCalendarBloc>()
|
||||||
|
// .add(DateCellCalendarEvent.setTimeFormat(newFormat));
|
||||||
|
// },
|
||||||
|
// );
|
||||||
},
|
},
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
@ -352,21 +431,50 @@ class _DateFormatOption extends StatelessWidget {
|
|||||||
DateFormatPB>(
|
DateFormatPB>(
|
||||||
selector: (state) => state.dateTypeOptionPB.dateFormat,
|
selector: (state) => state.dateTypeOptionPB.dateFormat,
|
||||||
builder: (context, state) {
|
builder: (context, state) {
|
||||||
return DateFormatListTile(
|
return FlowyOptionTile(
|
||||||
currentFormatStr: state.title(),
|
text: LocaleKeys.settings_appearance_dateFormat_label.tr(),
|
||||||
groupValue: context
|
leftIcon: const FlowySvg(FlowySvgs.clock_alarm_s),
|
||||||
.watch<DateCellCalendarBloc>()
|
|
||||||
.state
|
|
||||||
.dateTypeOptionPB
|
|
||||||
.dateFormat,
|
|
||||||
onChanged: (newFormat) {
|
|
||||||
if (newFormat == null) return;
|
|
||||||
context
|
|
||||||
.read<DateCellCalendarBloc>()
|
|
||||||
.add(DateCellCalendarEvent.setDateFormat(newFormat));
|
|
||||||
},
|
|
||||||
);
|
);
|
||||||
|
// DateFormatListTile(
|
||||||
|
// currentFormatStr: state.title(),
|
||||||
|
// groupValue: context
|
||||||
|
// .watch<DateCellCalendarBloc>()
|
||||||
|
// .state
|
||||||
|
// .dateTypeOptionPB
|
||||||
|
// .dateFormat,
|
||||||
|
// onChanged: (newFormat) {
|
||||||
|
// if (newFormat == null) return;
|
||||||
|
// context
|
||||||
|
// .read<DateCellCalendarBloc>()
|
||||||
|
// .add(DateCellCalendarEvent.setDateFormat(newFormat));
|
||||||
|
// },
|
||||||
|
// );
|
||||||
},
|
},
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
class _Switcher extends StatelessWidget {
|
||||||
|
const _Switcher({
|
||||||
|
required this.value,
|
||||||
|
required this.onChanged,
|
||||||
|
});
|
||||||
|
|
||||||
|
final bool value;
|
||||||
|
final void Function(bool value) onChanged;
|
||||||
|
|
||||||
|
@override
|
||||||
|
Widget build(BuildContext context) {
|
||||||
|
return SizedBox(
|
||||||
|
width: 48,
|
||||||
|
child: FittedBox(
|
||||||
|
fit: BoxFit.fill,
|
||||||
|
child: Switch.adaptive(
|
||||||
|
value: value,
|
||||||
|
activeColor: const Color(0xFF00BCF0),
|
||||||
|
onChanged: onChanged,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,35 @@
|
|||||||
|
import 'package:flutter/material.dart';
|
||||||
|
|
||||||
|
class FlowyOptionDecorateBox extends StatelessWidget {
|
||||||
|
const FlowyOptionDecorateBox({
|
||||||
|
super.key,
|
||||||
|
this.showTopBorder = true,
|
||||||
|
this.showBottomBorder = true,
|
||||||
|
required this.child,
|
||||||
|
});
|
||||||
|
|
||||||
|
final bool showTopBorder;
|
||||||
|
final bool showBottomBorder;
|
||||||
|
final Widget child;
|
||||||
|
|
||||||
|
@override
|
||||||
|
Widget build(BuildContext context) {
|
||||||
|
return DecoratedBox(
|
||||||
|
decoration: BoxDecoration(
|
||||||
|
border: Border(
|
||||||
|
top: showTopBorder
|
||||||
|
? BorderSide(
|
||||||
|
color: Theme.of(context).dividerColor,
|
||||||
|
)
|
||||||
|
: BorderSide.none,
|
||||||
|
bottom: showBottomBorder
|
||||||
|
? BorderSide(
|
||||||
|
color: Theme.of(context).dividerColor,
|
||||||
|
)
|
||||||
|
: BorderSide.none,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
child: child,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,53 @@
|
|||||||
|
import 'package:appflowy/mobile/presentation/widgets/widgets.dart';
|
||||||
|
import 'package:flowy_infra_ui/flowy_infra_ui.dart';
|
||||||
|
import 'package:flutter/material.dart';
|
||||||
|
|
||||||
|
// used in cell editor
|
||||||
|
class FlowyOptionTile extends StatelessWidget {
|
||||||
|
const FlowyOptionTile({
|
||||||
|
super.key,
|
||||||
|
this.showTopBorder = true,
|
||||||
|
this.showBottomBorder = true,
|
||||||
|
required this.text,
|
||||||
|
this.leftIcon,
|
||||||
|
this.onTap,
|
||||||
|
this.leading,
|
||||||
|
});
|
||||||
|
|
||||||
|
final bool showTopBorder;
|
||||||
|
final bool showBottomBorder;
|
||||||
|
final String text;
|
||||||
|
final void Function()? onTap;
|
||||||
|
final Widget? leftIcon;
|
||||||
|
final Widget? leading;
|
||||||
|
|
||||||
|
@override
|
||||||
|
Widget build(BuildContext context) {
|
||||||
|
return FlowyOptionDecorateBox(
|
||||||
|
showTopBorder: showTopBorder,
|
||||||
|
showBottomBorder: showBottomBorder,
|
||||||
|
child: Row(
|
||||||
|
children: [
|
||||||
|
FlowyButton(
|
||||||
|
useIntrinsicWidth: true,
|
||||||
|
text: FlowyText(
|
||||||
|
text,
|
||||||
|
fontSize: 16.0,
|
||||||
|
),
|
||||||
|
margin: const EdgeInsets.symmetric(
|
||||||
|
horizontal: 12.0,
|
||||||
|
vertical: 16.0,
|
||||||
|
),
|
||||||
|
leftIcon: leftIcon,
|
||||||
|
leftIconSize: const Size.square(24.0),
|
||||||
|
iconPadding: 8.0,
|
||||||
|
onTap: onTap,
|
||||||
|
),
|
||||||
|
const Spacer(),
|
||||||
|
leading ?? const SizedBox.shrink(),
|
||||||
|
const HSpace(12.0),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
@ -1,3 +1,5 @@
|
|||||||
export 'show_flowy_mobile_confirm_dialog.dart';
|
export 'flowy_mobile_option_decorate_box.dart';
|
||||||
export 'show_flowy_mobile_bottom_sheet.dart';
|
|
||||||
export 'flowy_mobile_state_container.dart';
|
export 'flowy_mobile_state_container.dart';
|
||||||
|
export 'flowy_option_tile.dart';
|
||||||
|
export 'show_flowy_mobile_bottom_sheet.dart';
|
||||||
|
export 'show_flowy_mobile_confirm_dialog.dart';
|
||||||
|
@ -155,10 +155,9 @@ GridCellWidget _getMobileCardCellWidget(
|
|||||||
);
|
);
|
||||||
case FieldType.DateTime:
|
case FieldType.DateTime:
|
||||||
style as DateCellStyle?;
|
style as DateCellStyle?;
|
||||||
return MobileDateCell(
|
return GridDateCell(
|
||||||
cellControllerBuilder: cellControllerBuilder,
|
cellControllerBuilder: cellControllerBuilder,
|
||||||
hintText: style?.placeholder,
|
style: style,
|
||||||
key: key,
|
|
||||||
);
|
);
|
||||||
case FieldType.URL:
|
case FieldType.URL:
|
||||||
style as GridURLCellStyle?;
|
style as GridURLCellStyle?;
|
||||||
@ -167,7 +166,6 @@ GridCellWidget _getMobileCardCellWidget(
|
|||||||
hintText: style?.placeholder,
|
hintText: style?.placeholder,
|
||||||
key: key,
|
key: key,
|
||||||
);
|
);
|
||||||
// TODO(yijing): implement the following mobile select option cell
|
|
||||||
case FieldType.SingleSelect:
|
case FieldType.SingleSelect:
|
||||||
return GridSingleSelectCell(
|
return GridSingleSelectCell(
|
||||||
cellControllerBuilder: cellControllerBuilder,
|
cellControllerBuilder: cellControllerBuilder,
|
||||||
|
@ -1,4 +1,7 @@
|
|||||||
|
import 'package:appflowy/mobile/presentation/bottom_sheet/bottom_sheet.dart';
|
||||||
|
import 'package:appflowy/mobile/presentation/database/date_picker/mobile_date_picker_screen.dart';
|
||||||
import 'package:appflowy/plugins/database_view/application/cell/cell_controller_builder.dart';
|
import 'package:appflowy/plugins/database_view/application/cell/cell_controller_builder.dart';
|
||||||
|
import 'package:appflowy_editor/appflowy_editor.dart';
|
||||||
import 'package:appflowy_popover/appflowy_popover.dart';
|
import 'package:appflowy_popover/appflowy_popover.dart';
|
||||||
import 'package:flowy_infra/theme_extension.dart';
|
import 'package:flowy_infra/theme_extension.dart';
|
||||||
import 'package:flowy_infra_ui/flowy_infra_ui.dart';
|
import 'package:flowy_infra_ui/flowy_infra_ui.dart';
|
||||||
@ -66,34 +69,55 @@ class _DateCellState extends GridCellState<GridDateCell> {
|
|||||||
final alignment = widget.cellStyle != null
|
final alignment = widget.cellStyle != null
|
||||||
? widget.cellStyle!.alignment
|
? widget.cellStyle!.alignment
|
||||||
: Alignment.centerLeft;
|
: Alignment.centerLeft;
|
||||||
|
|
||||||
return BlocProvider.value(
|
return BlocProvider.value(
|
||||||
value: _cellBloc,
|
value: _cellBloc,
|
||||||
child: BlocBuilder<DateCellBloc, DateCellState>(
|
child: BlocBuilder<DateCellBloc, DateCellState>(
|
||||||
builder: (context, state) {
|
builder: (context, state) {
|
||||||
return AppFlowyPopover(
|
final child = GridDateCellText(
|
||||||
controller: _popover,
|
dateStr: state.dateStr,
|
||||||
triggerActions: PopoverTriggerFlags.none,
|
placeholder: widget.cellStyle?.placeholder ?? "",
|
||||||
direction: PopoverDirection.bottomWithLeftAligned,
|
alignment: alignment,
|
||||||
constraints: BoxConstraints.loose(const Size(260, 620)),
|
cellPadding:
|
||||||
margin: EdgeInsets.zero,
|
widget.cellStyle?.cellPadding ?? GridSize.cellContentInsets,
|
||||||
child: GridDateCellText(
|
|
||||||
dateStr: state.dateStr,
|
|
||||||
placeholder: widget.cellStyle?.placeholder ?? "",
|
|
||||||
alignment: alignment,
|
|
||||||
cellPadding:
|
|
||||||
widget.cellStyle?.cellPadding ?? GridSize.cellContentInsets,
|
|
||||||
),
|
|
||||||
popupBuilder: (BuildContext popoverContent) {
|
|
||||||
return DateCellEditor(
|
|
||||||
cellController:
|
|
||||||
widget.cellControllerBuilder.build() as DateCellController,
|
|
||||||
onDismissed: () => widget.cellContainerNotifier.isFocus = false,
|
|
||||||
);
|
|
||||||
},
|
|
||||||
onClose: () {
|
|
||||||
widget.cellContainerNotifier.isFocus = false;
|
|
||||||
},
|
|
||||||
);
|
);
|
||||||
|
if (PlatformExtension.isDesktopOrWeb) {
|
||||||
|
return AppFlowyPopover(
|
||||||
|
controller: _popover,
|
||||||
|
triggerActions: PopoverTriggerFlags.none,
|
||||||
|
direction: PopoverDirection.bottomWithLeftAligned,
|
||||||
|
constraints: BoxConstraints.loose(const Size(260, 620)),
|
||||||
|
margin: EdgeInsets.zero,
|
||||||
|
child: child,
|
||||||
|
popupBuilder: (BuildContext popoverContent) {
|
||||||
|
return DateCellEditor(
|
||||||
|
cellController: widget.cellControllerBuilder.build()
|
||||||
|
as DateCellController,
|
||||||
|
onDismissed: () =>
|
||||||
|
widget.cellContainerNotifier.isFocus = false,
|
||||||
|
);
|
||||||
|
},
|
||||||
|
onClose: () {
|
||||||
|
widget.cellContainerNotifier.isFocus = false;
|
||||||
|
},
|
||||||
|
);
|
||||||
|
} else {
|
||||||
|
return FlowyButton(
|
||||||
|
text: child,
|
||||||
|
onTap: () {
|
||||||
|
showMobileBottomSheet(
|
||||||
|
context: context,
|
||||||
|
builder: (context) {
|
||||||
|
return MobileDateCellEditScreen(
|
||||||
|
controller: widget.cellControllerBuilder.build()
|
||||||
|
as DateCellController,
|
||||||
|
showAsFullScreen: false,
|
||||||
|
);
|
||||||
|
},
|
||||||
|
);
|
||||||
|
},
|
||||||
|
);
|
||||||
|
}
|
||||||
},
|
},
|
||||||
),
|
),
|
||||||
);
|
);
|
||||||
|
@ -0,0 +1,193 @@
|
|||||||
|
import 'package:appflowy/generated/flowy_svgs.g.dart';
|
||||||
|
import 'package:appflowy/workspace/presentation/widgets/date_picker/appflowy_calendar.dart';
|
||||||
|
import 'package:easy_localization/easy_localization.dart';
|
||||||
|
import 'package:flowy_infra_ui/flowy_infra_ui.dart';
|
||||||
|
import 'package:flutter/material.dart';
|
||||||
|
import 'package:flutter_bloc/flutter_bloc.dart';
|
||||||
|
import 'package:table_calendar/table_calendar.dart';
|
||||||
|
|
||||||
|
import 'date_cal_bloc.dart';
|
||||||
|
|
||||||
|
class MobileDatePicker extends StatefulWidget {
|
||||||
|
const MobileDatePicker({
|
||||||
|
super.key,
|
||||||
|
});
|
||||||
|
|
||||||
|
@override
|
||||||
|
State<MobileDatePicker> createState() => _MobileDatePickerState();
|
||||||
|
}
|
||||||
|
|
||||||
|
class _MobileDatePickerState extends State<MobileDatePicker> {
|
||||||
|
DateTime _focusedDay = DateTime.now();
|
||||||
|
CalendarFormat _calendarFormat = CalendarFormat.month;
|
||||||
|
|
||||||
|
final ValueNotifier<(DateTime, dynamic)> _currentDateNotifier = ValueNotifier(
|
||||||
|
(DateTime.now(), null),
|
||||||
|
);
|
||||||
|
PageController? _pageController;
|
||||||
|
|
||||||
|
@override
|
||||||
|
Widget build(BuildContext context) {
|
||||||
|
return Column(
|
||||||
|
children: [
|
||||||
|
const VSpace(8.0),
|
||||||
|
_buildHeader(context),
|
||||||
|
const VSpace(8.0),
|
||||||
|
_buildCalendar(context),
|
||||||
|
const VSpace(16.0),
|
||||||
|
],
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
Widget _buildCalendar(BuildContext context) {
|
||||||
|
const selectedColor = Color(0xFF00BCF0);
|
||||||
|
final textStyle = Theme.of(context).textTheme.bodyMedium!.copyWith(
|
||||||
|
fontSize: 16.0,
|
||||||
|
);
|
||||||
|
const boxDecoration = BoxDecoration(
|
||||||
|
shape: BoxShape.circle,
|
||||||
|
);
|
||||||
|
|
||||||
|
return Padding(
|
||||||
|
padding: const EdgeInsets.symmetric(horizontal: 8.0),
|
||||||
|
child: BlocBuilder<DateCellCalendarBloc, DateCellCalendarState>(
|
||||||
|
builder: (context, state) {
|
||||||
|
return TableCalendar(
|
||||||
|
firstDay: kFirstDay,
|
||||||
|
lastDay: kLastDay,
|
||||||
|
focusedDay: _focusedDay,
|
||||||
|
rowHeight: 48.0,
|
||||||
|
calendarFormat: _calendarFormat,
|
||||||
|
daysOfWeekHeight: 48.0,
|
||||||
|
rangeSelectionMode: state.isRange
|
||||||
|
? RangeSelectionMode.enforced
|
||||||
|
: RangeSelectionMode.disabled,
|
||||||
|
rangeStartDay: state.isRange ? state.startDay : null,
|
||||||
|
rangeEndDay: state.isRange ? state.endDay : null,
|
||||||
|
onCalendarCreated: (pageController) =>
|
||||||
|
_pageController = pageController,
|
||||||
|
headerVisible: false,
|
||||||
|
availableGestures: AvailableGestures.horizontalSwipe,
|
||||||
|
calendarStyle: CalendarStyle(
|
||||||
|
cellMargin: const EdgeInsets.all(3.5),
|
||||||
|
defaultDecoration: boxDecoration,
|
||||||
|
selectedDecoration: boxDecoration.copyWith(
|
||||||
|
color: selectedColor,
|
||||||
|
),
|
||||||
|
todayDecoration: boxDecoration.copyWith(
|
||||||
|
color: Colors.transparent,
|
||||||
|
border: Border.all(color: selectedColor),
|
||||||
|
),
|
||||||
|
weekendDecoration: boxDecoration,
|
||||||
|
outsideDecoration: boxDecoration,
|
||||||
|
rangeStartDecoration: boxDecoration.copyWith(
|
||||||
|
color: selectedColor,
|
||||||
|
),
|
||||||
|
rangeEndDecoration: boxDecoration.copyWith(
|
||||||
|
color: selectedColor,
|
||||||
|
),
|
||||||
|
defaultTextStyle: textStyle,
|
||||||
|
weekendTextStyle: textStyle,
|
||||||
|
selectedTextStyle: textStyle.copyWith(
|
||||||
|
color: Theme.of(context).colorScheme.surface,
|
||||||
|
),
|
||||||
|
rangeStartTextStyle: textStyle.copyWith(
|
||||||
|
color: Theme.of(context).colorScheme.surface,
|
||||||
|
),
|
||||||
|
rangeEndTextStyle: textStyle.copyWith(
|
||||||
|
color: Theme.of(context).colorScheme.surface,
|
||||||
|
),
|
||||||
|
todayTextStyle: textStyle,
|
||||||
|
outsideTextStyle: textStyle.copyWith(
|
||||||
|
color: Theme.of(context).disabledColor,
|
||||||
|
),
|
||||||
|
rangeHighlightColor:
|
||||||
|
Theme.of(context).colorScheme.secondaryContainer,
|
||||||
|
),
|
||||||
|
calendarBuilders: CalendarBuilders(
|
||||||
|
dowBuilder: (context, day) {
|
||||||
|
final locale = context.locale.toLanguageTag();
|
||||||
|
final label = DateFormat.E(locale).format(day).substring(0, 2);
|
||||||
|
return Padding(
|
||||||
|
padding: const EdgeInsets.only(bottom: 8.0),
|
||||||
|
child: Center(
|
||||||
|
child: Text(
|
||||||
|
label,
|
||||||
|
style: textStyle.copyWith(
|
||||||
|
color: Theme.of(context).hintColor,
|
||||||
|
fontSize: 14.0,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
);
|
||||||
|
},
|
||||||
|
),
|
||||||
|
selectedDayPredicate: (day) =>
|
||||||
|
state.isRange ? false : isSameDay(state.dateTime, day),
|
||||||
|
onDaySelected: (selectedDay, focusedDay) {
|
||||||
|
context.read<DateCellCalendarBloc>().add(
|
||||||
|
DateCellCalendarEvent.selectDay(selectedDay),
|
||||||
|
);
|
||||||
|
},
|
||||||
|
onRangeSelected: (start, end, focusedDay) {
|
||||||
|
context.read<DateCellCalendarBloc>().add(
|
||||||
|
DateCellCalendarEvent.selectDateRange(start, end),
|
||||||
|
);
|
||||||
|
},
|
||||||
|
onFormatChanged: (calendarFormat) => setState(() {
|
||||||
|
_calendarFormat = calendarFormat;
|
||||||
|
}),
|
||||||
|
onPageChanged: (focusedDay) => setState(() {
|
||||||
|
_focusedDay = focusedDay;
|
||||||
|
_currentDateNotifier.value = (focusedDay, null);
|
||||||
|
}),
|
||||||
|
);
|
||||||
|
},
|
||||||
|
),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
Widget _buildHeader(BuildContext context) {
|
||||||
|
return Row(
|
||||||
|
children: [
|
||||||
|
const HSpace(16.0),
|
||||||
|
ValueListenableBuilder(
|
||||||
|
valueListenable: _currentDateNotifier,
|
||||||
|
builder: (_, value, ___) {
|
||||||
|
return FlowyText(
|
||||||
|
DateFormat.yMMMM(value.$2).format(value.$1),
|
||||||
|
fontSize: 16.0,
|
||||||
|
);
|
||||||
|
},
|
||||||
|
),
|
||||||
|
const Spacer(),
|
||||||
|
FlowyButton(
|
||||||
|
useIntrinsicWidth: true,
|
||||||
|
text: FlowySvg(
|
||||||
|
FlowySvgs.arrow_left_s,
|
||||||
|
color: Theme.of(context).iconTheme.color,
|
||||||
|
size: const Size.square(24.0),
|
||||||
|
),
|
||||||
|
onTap: () => _pageController?.previousPage(
|
||||||
|
duration: const Duration(milliseconds: 300),
|
||||||
|
curve: Curves.easeOut,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
const HSpace(24.0),
|
||||||
|
FlowyButton(
|
||||||
|
useIntrinsicWidth: true,
|
||||||
|
text: FlowySvg(
|
||||||
|
FlowySvgs.arrow_right_s,
|
||||||
|
color: Theme.of(context).iconTheme.color,
|
||||||
|
size: const Size.square(24.0),
|
||||||
|
),
|
||||||
|
onTap: () => _pageController?.nextPage(
|
||||||
|
duration: const Duration(milliseconds: 300),
|
||||||
|
curve: Curves.easeOut,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
const HSpace(8.0),
|
||||||
|
],
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
@ -1,5 +1,6 @@
|
|||||||
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/widgets/widgets.dart';
|
||||||
import 'package:appflowy/plugins/base/drag_handler.dart';
|
import 'package:appflowy/plugins/base/drag_handler.dart';
|
||||||
import 'package:appflowy/plugins/database_view/application/cell/cell_controller_builder.dart';
|
import 'package:appflowy/plugins/database_view/application/cell/cell_controller_builder.dart';
|
||||||
import 'package:appflowy/plugins/database_view/grid/presentation/layout/sizes.dart';
|
import 'package:appflowy/plugins/database_view/grid/presentation/layout/sizes.dart';
|
||||||
@ -49,7 +50,7 @@ class _MobileSelectOptionEditorState extends State<MobileSelectOptionEditor> {
|
|||||||
|
|
||||||
@override
|
@override
|
||||||
Widget build(BuildContext context) {
|
Widget build(BuildContext context) {
|
||||||
return Container(
|
return ConstrainedBox(
|
||||||
constraints: const BoxConstraints.tightFor(height: 420),
|
constraints: const BoxConstraints.tightFor(height: 420),
|
||||||
child: BlocProvider(
|
child: BlocProvider(
|
||||||
create: (context) => SelectOptionCellEditorBloc(
|
create: (context) => SelectOptionCellEditorBloc(
|
||||||
@ -504,23 +505,36 @@ class _MoreOptions extends StatelessWidget {
|
|||||||
|
|
||||||
@override
|
@override
|
||||||
Widget build(BuildContext context) {
|
Widget build(BuildContext context) {
|
||||||
|
final color = Theme.of(context).colorScheme.secondaryContainer;
|
||||||
return SingleChildScrollView(
|
return SingleChildScrollView(
|
||||||
child: Column(
|
child: Column(
|
||||||
crossAxisAlignment: CrossAxisAlignment.start,
|
crossAxisAlignment: CrossAxisAlignment.start,
|
||||||
children: [
|
children: [
|
||||||
const VSpace(8.0),
|
VSpace(8.0, color: color),
|
||||||
_buildRenameTextField(context),
|
_buildRenameTextField(context),
|
||||||
const VSpace(16.0),
|
VSpace(
|
||||||
|
16.0,
|
||||||
|
color: color,
|
||||||
|
),
|
||||||
_buildDeleteButton(context),
|
_buildDeleteButton(context),
|
||||||
const VSpace(16.0),
|
VSpace(
|
||||||
|
16.0,
|
||||||
|
color: color,
|
||||||
|
),
|
||||||
Padding(
|
Padding(
|
||||||
padding: const EdgeInsets.only(left: 12.0),
|
padding: const EdgeInsets.only(left: 12.0),
|
||||||
child: FlowyText(
|
child: ColoredBox(
|
||||||
LocaleKeys.grid_field_optionTitle.tr(),
|
color: color,
|
||||||
color: Theme.of(context).hintColor,
|
child: FlowyText(
|
||||||
|
LocaleKeys.grid_field_optionTitle.tr(),
|
||||||
|
color: Theme.of(context).hintColor,
|
||||||
|
),
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
const VSpace(4.0),
|
VSpace(
|
||||||
|
4.0,
|
||||||
|
color: color,
|
||||||
|
),
|
||||||
_buildColorOptions(context),
|
_buildColorOptions(context),
|
||||||
],
|
],
|
||||||
),
|
),
|
||||||
@ -530,7 +544,7 @@ class _MoreOptions extends StatelessWidget {
|
|||||||
Widget _buildRenameTextField(BuildContext context) {
|
Widget _buildRenameTextField(BuildContext context) {
|
||||||
return ConstrainedBox(
|
return ConstrainedBox(
|
||||||
constraints: const BoxConstraints.tightFor(height: 52.0),
|
constraints: const BoxConstraints.tightFor(height: 52.0),
|
||||||
child: _DefaultDecorateBox(
|
child: FlowyOptionDecorateBox(
|
||||||
showTopBorder: true,
|
showTopBorder: true,
|
||||||
showBottomBorder: true,
|
showBottomBorder: true,
|
||||||
child: TextField(
|
child: TextField(
|
||||||
@ -549,28 +563,15 @@ class _MoreOptions extends StatelessWidget {
|
|||||||
}
|
}
|
||||||
|
|
||||||
Widget _buildDeleteButton(BuildContext context) {
|
Widget _buildDeleteButton(BuildContext context) {
|
||||||
return _DefaultDecorateBox(
|
return FlowyOptionTile(
|
||||||
showTopBorder: true,
|
text: LocaleKeys.button_delete.tr(),
|
||||||
showBottomBorder: true,
|
leftIcon: const FlowySvg(FlowySvgs.delete_s),
|
||||||
child: FlowyButton(
|
onTap: onDelete,
|
||||||
text: FlowyText(
|
|
||||||
LocaleKeys.button_delete.tr(),
|
|
||||||
fontSize: 16.0,
|
|
||||||
),
|
|
||||||
margin: const EdgeInsets.symmetric(
|
|
||||||
horizontal: 12.0,
|
|
||||||
vertical: 16.0,
|
|
||||||
),
|
|
||||||
leftIcon: const FlowySvg(FlowySvgs.delete_s),
|
|
||||||
leftIconSize: const Size.square(24.0),
|
|
||||||
iconPadding: 8.0,
|
|
||||||
onTap: onDelete,
|
|
||||||
),
|
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
Widget _buildColorOptions(BuildContext context) {
|
Widget _buildColorOptions(BuildContext context) {
|
||||||
return _DefaultDecorateBox(
|
return FlowyOptionDecorateBox(
|
||||||
showTopBorder: true,
|
showTopBorder: true,
|
||||||
showBottomBorder: true,
|
showBottomBorder: true,
|
||||||
child: Padding(
|
child: Padding(
|
||||||
@ -606,33 +607,3 @@ class _MoreOptions extends StatelessWidget {
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
class _DefaultDecorateBox extends StatelessWidget {
|
|
||||||
const _DefaultDecorateBox({
|
|
||||||
this.showTopBorder = true,
|
|
||||||
this.showBottomBorder = true,
|
|
||||||
required this.child,
|
|
||||||
});
|
|
||||||
|
|
||||||
final bool showTopBorder;
|
|
||||||
final bool showBottomBorder;
|
|
||||||
final Widget child;
|
|
||||||
|
|
||||||
@override
|
|
||||||
Widget build(BuildContext context) {
|
|
||||||
return DecoratedBox(
|
|
||||||
decoration: BoxDecoration(
|
|
||||||
color: Theme.of(context).colorScheme.secondaryContainer,
|
|
||||||
border: Border(
|
|
||||||
top: BorderSide(
|
|
||||||
color: Theme.of(context).dividerColor,
|
|
||||||
),
|
|
||||||
bottom: BorderSide(
|
|
||||||
color: Theme.of(context).dividerColor,
|
|
||||||
),
|
|
||||||
),
|
|
||||||
),
|
|
||||||
child: child,
|
|
||||||
);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
@ -4,7 +4,6 @@ import 'package:appflowy/plugins/database_view/widgets/row/cells/select_option_c
|
|||||||
import 'package:appflowy_backend/protobuf/flowy-database2/select_option.pb.dart';
|
import 'package:appflowy_backend/protobuf/flowy-database2/select_option.pb.dart';
|
||||||
import 'package:appflowy_editor/appflowy_editor.dart';
|
import 'package:appflowy_editor/appflowy_editor.dart';
|
||||||
import 'package:appflowy_popover/appflowy_popover.dart';
|
import 'package:appflowy_popover/appflowy_popover.dart';
|
||||||
import 'package:flowy_infra/size.dart';
|
|
||||||
import 'package:flowy_infra_ui/flowy_infra_ui.dart';
|
import 'package:flowy_infra_ui/flowy_infra_ui.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';
|
||||||
@ -209,11 +208,6 @@ class _SelectOptionWrapState extends State<SelectOptionWrap> {
|
|||||||
onTap: () {
|
onTap: () {
|
||||||
showMobileBottomSheet(
|
showMobileBottomSheet(
|
||||||
context: context,
|
context: context,
|
||||||
shape: const RoundedRectangleBorder(
|
|
||||||
borderRadius: BorderRadius.vertical(
|
|
||||||
top: Corners.s12Radius,
|
|
||||||
),
|
|
||||||
),
|
|
||||||
builder: (context) {
|
builder: (context) {
|
||||||
return MobileSelectOptionEditor(
|
return MobileSelectOptionEditor(
|
||||||
cellController: cellController,
|
cellController: cellController,
|
||||||
|
@ -1,7 +1,7 @@
|
|||||||
import 'package:appflowy/mobile/presentation/database/card/card.dart';
|
import 'package:appflowy/mobile/presentation/database/card/card.dart';
|
||||||
import 'package:appflowy/mobile/presentation/database/card/card_detail/mobile_create_row_field_screen.dart';
|
import 'package:appflowy/mobile/presentation/database/card/card_detail/mobile_create_row_field_screen.dart';
|
||||||
import 'package:appflowy/mobile/presentation/database/card/card_property_edit/card_property_edit_screen.dart';
|
import 'package:appflowy/mobile/presentation/database/card/card_property_edit/card_property_edit_screen.dart';
|
||||||
import 'package:appflowy/mobile/presentation/database/card/row/cells/cells.dart';
|
import 'package:appflowy/mobile/presentation/database/date_picker/mobile_date_picker_screen.dart';
|
||||||
import 'package:appflowy/mobile/presentation/database/mobile_board_screen.dart';
|
import 'package:appflowy/mobile/presentation/database/mobile_board_screen.dart';
|
||||||
import 'package:appflowy/mobile/presentation/database/mobile_calendar_events_screen.dart';
|
import 'package:appflowy/mobile/presentation/database/mobile_calendar_events_screen.dart';
|
||||||
import 'package:appflowy/mobile/presentation/database/mobile_calendar_screen.dart';
|
import 'package:appflowy/mobile/presentation/database/mobile_calendar_screen.dart';
|
||||||
@ -539,11 +539,18 @@ GoRoute _mobileDateCellEditScreenRoute() {
|
|||||||
path: MobileDateCellEditScreen.routeName,
|
path: MobileDateCellEditScreen.routeName,
|
||||||
pageBuilder: (context, state) {
|
pageBuilder: (context, state) {
|
||||||
final args = state.extra as Map<String, dynamic>;
|
final args = state.extra as Map<String, dynamic>;
|
||||||
final cellController = args[MobileDateCellEditScreen.argCellController];
|
final controller = args[MobileDateCellEditScreen.dateCellController];
|
||||||
|
final fullScreen = args[MobileDateCellEditScreen.fullScreen];
|
||||||
return MaterialPage(
|
return CustomTransitionPage(
|
||||||
child: MobileDateCellEditScreen(cellController),
|
transitionsBuilder: (_, __, ___, child) => child,
|
||||||
fullscreenDialog: true,
|
fullscreenDialog: true,
|
||||||
|
opaque: false,
|
||||||
|
barrierDismissible: true,
|
||||||
|
barrierColor: Theme.of(context).bottomSheetTheme.modalBarrierColor,
|
||||||
|
child: MobileDateCellEditScreen(
|
||||||
|
controller: controller,
|
||||||
|
showAsFullScreen: fullScreen ?? true,
|
||||||
|
),
|
||||||
);
|
);
|
||||||
},
|
},
|
||||||
);
|
);
|
||||||
|
@ -62,6 +62,7 @@ class FlowyButton extends StatelessWidget {
|
|||||||
return InkWell(
|
return InkWell(
|
||||||
onTap: disable ? null : onTap,
|
onTap: disable ? null : onTap,
|
||||||
onSecondaryTap: disable ? null : onSecondaryTap,
|
onSecondaryTap: disable ? null : onSecondaryTap,
|
||||||
|
borderRadius: radius ?? Corners.s6Border,
|
||||||
child: _render(context),
|
child: _render(context),
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
@ -11,12 +11,29 @@ class Space extends StatelessWidget {
|
|||||||
}
|
}
|
||||||
|
|
||||||
class VSpace extends StatelessWidget {
|
class VSpace extends StatelessWidget {
|
||||||
final double size;
|
const VSpace(
|
||||||
|
this.size, {
|
||||||
|
super.key,
|
||||||
|
this.color,
|
||||||
|
});
|
||||||
|
|
||||||
const VSpace(this.size, {Key? key}) : super(key: key);
|
final double size;
|
||||||
|
final Color? color;
|
||||||
|
|
||||||
@override
|
@override
|
||||||
Widget build(BuildContext context) => Space(0, size);
|
Widget build(BuildContext context) {
|
||||||
|
if (color != null) {
|
||||||
|
return SizedBox(
|
||||||
|
height: size,
|
||||||
|
width: double.infinity,
|
||||||
|
child: ColoredBox(
|
||||||
|
color: color!,
|
||||||
|
),
|
||||||
|
);
|
||||||
|
} else {
|
||||||
|
return Space(0, size);
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
class HSpace extends StatelessWidget {
|
class HSpace extends StatelessWidget {
|
||||||
|
@ -1124,6 +1124,7 @@
|
|||||||
"pageIcon": "Page icon",
|
"pageIcon": "Page icon",
|
||||||
"language": "Language",
|
"language": "Language",
|
||||||
"font": "Font",
|
"font": "Font",
|
||||||
"actions": "Actions"
|
"actions": "Actions",
|
||||||
|
"date": "Date"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
Reference in New Issue
Block a user