mirror of
https://github.com/AppFlowy-IO/AppFlowy.git
synced 2024-08-30 18:12:39 +00:00
feat: open the row page on mobile (#5975)
* chore: add dart dependency validator
* feat: open the row page on mobile
* Revert "chore: add dart dependency validator"
This reverts commit c81e5ef0ed
.
* chore: update translations
* feat: preload row page to reduce open time
* chore: don't add orphan doc into recent records
* fix: bloc error
* fix: migrate the row page title to latest design
* chore: optimize database mobile UI
This commit is contained in:
parent
88cc0caab7
commit
6283649a6b
@ -2,20 +2,23 @@ import 'dart:async';
|
||||
import 'dart:convert';
|
||||
|
||||
import 'package:appflowy/mobile/presentation/chat/mobile_chat_screen.dart';
|
||||
import 'package:appflowy/workspace/presentation/home/menu/menu_shared_state.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
|
||||
import 'package:appflowy/mobile/presentation/database/board/mobile_board_screen.dart';
|
||||
import 'package:appflowy/mobile/presentation/database/mobile_calendar_screen.dart';
|
||||
import 'package:appflowy/mobile/presentation/database/mobile_grid_screen.dart';
|
||||
import 'package:appflowy/mobile/presentation/presentation.dart';
|
||||
import 'package:appflowy/startup/startup.dart';
|
||||
import 'package:appflowy/workspace/application/recent/cached_recent_service.dart';
|
||||
import 'package:appflowy/workspace/presentation/home/menu/menu_shared_state.dart';
|
||||
import 'package:appflowy_backend/protobuf/flowy-folder/view.pb.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:go_router/go_router.dart';
|
||||
|
||||
extension MobileRouter on BuildContext {
|
||||
Future<void> pushView(ViewPB view, [Map<String, dynamic>? arguments]) async {
|
||||
Future<void> pushView(
|
||||
ViewPB view, {
|
||||
Map<String, dynamic>? arguments,
|
||||
bool addInRecent = true,
|
||||
}) async {
|
||||
// set the current view before pushing the new view
|
||||
getIt<MenuSharedState>().latestOpenView = view;
|
||||
unawaited(getIt<CachedRecentService>().updateRecentViews([view.id], true));
|
||||
|
@ -10,7 +10,7 @@ enum FlowyAppBarLeadingType {
|
||||
Widget getWidget(VoidCallback? onTap) {
|
||||
switch (this) {
|
||||
case FlowyAppBarLeadingType.back:
|
||||
return AppBarBackButton(onTap: onTap);
|
||||
return AppBarImmersiveBackButton(onTap: onTap);
|
||||
case FlowyAppBarLeadingType.close:
|
||||
return AppBarCloseButton(onTap: onTap);
|
||||
case FlowyAppBarLeadingType.cancel:
|
||||
|
@ -26,6 +26,31 @@ class AppBarBackButton extends StatelessWidget {
|
||||
}
|
||||
}
|
||||
|
||||
class AppBarImmersiveBackButton extends StatelessWidget {
|
||||
const AppBarImmersiveBackButton({
|
||||
super.key,
|
||||
this.onTap,
|
||||
});
|
||||
|
||||
final VoidCallback? onTap;
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return AppBarButton(
|
||||
onTap: (_) => (onTap ?? () => Navigator.pop(context)).call(),
|
||||
padding: const EdgeInsets.only(
|
||||
left: 12.0,
|
||||
top: 8.0,
|
||||
bottom: 8.0,
|
||||
right: 4.0,
|
||||
),
|
||||
child: const FlowySvg(
|
||||
FlowySvgs.m_app_bar_back_s,
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
class AppBarCloseButton extends StatelessWidget {
|
||||
const AppBarCloseButton({
|
||||
super.key,
|
||||
|
@ -4,7 +4,6 @@ import 'package:appflowy/mobile/application/page_style/document_page_style_bloc.
|
||||
import 'package:appflowy/mobile/presentation/base/app_bar/app_bar.dart';
|
||||
import 'package:appflowy/mobile/presentation/base/view_page/app_bar_buttons.dart';
|
||||
import 'package:appflowy/mobile/presentation/widgets/flowy_mobile_state_container.dart';
|
||||
import 'package:appflowy/plugins/base/emoji/emoji_text.dart';
|
||||
import 'package:appflowy/plugins/document/presentation/document_collaborators.dart';
|
||||
import 'package:appflowy/shared/feature_flags.dart';
|
||||
import 'package:appflowy/startup/plugin/plugin.dart';
|
||||
@ -232,19 +231,20 @@ class _MobileViewPageState extends State<MobileViewPage> {
|
||||
return Row(
|
||||
mainAxisSize: MainAxisSize.min,
|
||||
children: [
|
||||
if (icon != null && icon.isNotEmpty)
|
||||
ConstrainedBox(
|
||||
constraints: const BoxConstraints.tightFor(width: 34.0),
|
||||
child: EmojiText(
|
||||
emoji: '$icon ',
|
||||
fontSize: 22.0,
|
||||
),
|
||||
if (icon != null && icon.isNotEmpty) ...[
|
||||
FlowyText.emoji(
|
||||
icon,
|
||||
fontSize: 15.0,
|
||||
figmaLineHeight: 18.0,
|
||||
),
|
||||
const HSpace(4),
|
||||
],
|
||||
Expanded(
|
||||
child: FlowyText.medium(
|
||||
view?.name ?? widget.title ?? '',
|
||||
fontSize: 15.0,
|
||||
overflow: TextOverflow.ellipsis,
|
||||
figmaLineHeight: 18.0,
|
||||
),
|
||||
),
|
||||
],
|
||||
|
@ -52,6 +52,7 @@ class _MobileBottomSheetRenameWidgetState
|
||||
height: 42.0,
|
||||
child: FlowyTextField(
|
||||
controller: controller,
|
||||
textStyle: Theme.of(context).textTheme.bodyMedium,
|
||||
keyboardType: TextInputType.text,
|
||||
onSubmitted: (text) => widget.onRename(text),
|
||||
),
|
||||
|
@ -3,6 +3,7 @@ import 'package:appflowy/generated/locale_keys.g.dart';
|
||||
import 'package:appflowy/mobile/presentation/base/app_bar/app_bar.dart';
|
||||
import 'package:appflowy/mobile/presentation/base/app_bar/app_bar_actions.dart';
|
||||
import 'package:appflowy/mobile/presentation/bottom_sheet/bottom_sheet.dart';
|
||||
import 'package:appflowy/mobile/presentation/database/card/card_detail/widgets/row_page_button.dart';
|
||||
import 'package:appflowy/mobile/presentation/widgets/flowy_mobile_quick_action_button.dart';
|
||||
import 'package:appflowy/plugins/database/application/cell/bloc/text_cell_bloc.dart';
|
||||
import 'package:appflowy/plugins/database/application/cell/cell_controller.dart';
|
||||
@ -366,6 +367,9 @@ class MobileRowDetailPageContentState
|
||||
if (rowDetailState.numHiddenFields != 0) ...[
|
||||
const ToggleHiddenFieldsVisibilityButton(),
|
||||
],
|
||||
OpenRowPageButton(
|
||||
documentId: rowController.rowMeta.documentId,
|
||||
),
|
||||
MobileRowDetailCreateFieldButton(
|
||||
viewId: viewId,
|
||||
fieldController: fieldController,
|
||||
|
@ -0,0 +1,116 @@
|
||||
import 'dart:async';
|
||||
|
||||
import 'package:appflowy/generated/flowy_svgs.g.dart';
|
||||
import 'package:appflowy/generated/locale_keys.g.dart';
|
||||
import 'package:appflowy/mobile/application/mobile_router.dart';
|
||||
import 'package:appflowy/plugins/database/grid/presentation/layout/sizes.dart';
|
||||
import 'package:appflowy/workspace/application/view/prelude.dart';
|
||||
import 'package:appflowy/workspace/presentation/widgets/dialogs.dart';
|
||||
import 'package:appflowy_backend/log.dart';
|
||||
import 'package:appflowy_backend/protobuf/flowy-folder/view.pb.dart';
|
||||
import 'package:easy_localization/easy_localization.dart';
|
||||
import 'package:flowy_infra_ui/flowy_infra_ui.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
|
||||
class OpenRowPageButton extends StatefulWidget {
|
||||
const OpenRowPageButton({
|
||||
super.key,
|
||||
required this.documentId,
|
||||
});
|
||||
|
||||
final String documentId;
|
||||
|
||||
@override
|
||||
State<OpenRowPageButton> createState() => _OpenRowPageButtonState();
|
||||
}
|
||||
|
||||
class _OpenRowPageButtonState extends State<OpenRowPageButton> {
|
||||
ViewPB? view;
|
||||
|
||||
@override
|
||||
void initState() {
|
||||
super.initState();
|
||||
|
||||
_preloadView(context, createDocumentIfMissed: true);
|
||||
}
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return ConstrainedBox(
|
||||
constraints: BoxConstraints(
|
||||
minWidth: double.infinity,
|
||||
minHeight: GridSize.headerHeight,
|
||||
),
|
||||
child: TextButton.icon(
|
||||
style: Theme.of(context).textButtonTheme.style?.copyWith(
|
||||
shape: WidgetStateProperty.all<RoundedRectangleBorder>(
|
||||
RoundedRectangleBorder(
|
||||
borderRadius: BorderRadius.circular(12.0),
|
||||
),
|
||||
),
|
||||
overlayColor: WidgetStateProperty.all<Color>(
|
||||
Theme.of(context).hoverColor,
|
||||
),
|
||||
alignment: AlignmentDirectional.centerStart,
|
||||
splashFactory: NoSplash.splashFactory,
|
||||
padding: const WidgetStatePropertyAll(
|
||||
EdgeInsets.symmetric(vertical: 14, horizontal: 6),
|
||||
),
|
||||
),
|
||||
label: FlowyText.medium(
|
||||
LocaleKeys.grid_field_openRowDocument.tr(),
|
||||
fontSize: 15,
|
||||
),
|
||||
icon: const Padding(
|
||||
padding: EdgeInsets.all(4.0),
|
||||
child: FlowySvg(
|
||||
FlowySvgs.full_view_s,
|
||||
size: Size.square(16.0),
|
||||
),
|
||||
),
|
||||
onPressed: () => _openRowPage(context),
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
Future<void> _openRowPage(BuildContext context) async {
|
||||
Log.info('Open row page(${widget.documentId})');
|
||||
|
||||
if (view == null) {
|
||||
showToastNotification(context, message: 'Failed to open row page');
|
||||
// reload the view again
|
||||
unawaited(_preloadView(context));
|
||||
Log.error('Failed to open row page(${widget.documentId})');
|
||||
return;
|
||||
}
|
||||
|
||||
if (context.mounted) {
|
||||
// the document in row is an orphan document, so we don't add it to recent
|
||||
await context.pushView(
|
||||
view!,
|
||||
addInRecent: false,
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
// preload view to reduce the time to open the view
|
||||
Future<void> _preloadView(
|
||||
BuildContext context, {
|
||||
bool createDocumentIfMissed = false,
|
||||
}) async {
|
||||
Log.info('Preload row page(${widget.documentId})');
|
||||
final result = await ViewBackendService.getView(widget.documentId);
|
||||
view = result.fold((s) => s, (f) => null);
|
||||
|
||||
if (view == null && createDocumentIfMissed) {
|
||||
// create view if not exists
|
||||
Log.info('Create row page(${widget.documentId})');
|
||||
final result = await ViewBackendService.createOrphanView(
|
||||
name: LocaleKeys.menuAppHeader_defaultNewPageName.tr(),
|
||||
viewId: widget.documentId,
|
||||
layoutType: ViewLayoutPB.Document,
|
||||
);
|
||||
view = result.fold((s) => s, (f) => null);
|
||||
}
|
||||
}
|
||||
}
|
@ -39,11 +39,13 @@ class CalculationsBloc extends Bloc<CalculationsEvent, CalculationsState> {
|
||||
_startListening();
|
||||
await _getAllCalculations();
|
||||
|
||||
if (!isClosed) {
|
||||
add(
|
||||
CalculationsEvent.didReceiveFieldUpdate(
|
||||
_fieldController.fieldInfos,
|
||||
),
|
||||
);
|
||||
}
|
||||
},
|
||||
didReceiveFieldUpdate: (fields) async {
|
||||
emit(
|
||||
@ -131,6 +133,10 @@ class CalculationsBloc extends Bloc<CalculationsEvent, CalculationsState> {
|
||||
Future<void> _getAllCalculations() async {
|
||||
final calculationsOrFailure = await _calculationsService.getCalculations();
|
||||
|
||||
if (isClosed) {
|
||||
return;
|
||||
}
|
||||
|
||||
final RepeatedCalculationsPB? calculations =
|
||||
calculationsOrFailure.fold((s) => s, (e) => null);
|
||||
if (calculations != null) {
|
||||
|
@ -1,15 +1,12 @@
|
||||
import 'package:appflowy/generated/flowy_svgs.g.dart';
|
||||
import 'package:appflowy/generated/locale_keys.g.dart';
|
||||
import 'package:appflowy/plugins/base/emoji/emoji_text.dart';
|
||||
import 'package:appflowy/plugins/database/application/cell/bloc/text_cell_bloc.dart';
|
||||
import 'package:appflowy/plugins/database/application/cell/cell_controller.dart';
|
||||
import 'package:appflowy/plugins/database/widgets/cell/editable_cell_builder.dart';
|
||||
import 'package:appflowy/plugins/database/widgets/cell/editable_cell_skeleton/text.dart';
|
||||
import 'package:appflowy/plugins/database/widgets/row/cells/cell_container.dart';
|
||||
import 'package:appflowy/plugins/document/presentation/editor_plugins/base/emoji_picker_button.dart';
|
||||
import 'package:appflowy/startup/tasks/app_window_size_manager.dart';
|
||||
import 'package:appflowy/workspace/application/tabs/tabs_bloc.dart';
|
||||
import 'package:appflowy/workspace/application/view/view_listener.dart';
|
||||
import 'package:appflowy/workspace/presentation/widgets/view_title_bar.dart';
|
||||
import 'package:appflowy_backend/protobuf/flowy-folder/view.pb.dart';
|
||||
import 'package:appflowy_popover/appflowy_popover.dart';
|
||||
import 'package:collection/collection.dart';
|
||||
@ -47,20 +44,16 @@ class ViewTitleBarWithRow extends StatelessWidget {
|
||||
if (state.ancestors.isEmpty) {
|
||||
return const SizedBox.shrink();
|
||||
}
|
||||
const maxWidth = WindowSizeManager.minWindowWidth - 200;
|
||||
return LayoutBuilder(
|
||||
builder: (context, constraints) {
|
||||
return Visibility(
|
||||
visible: maxWidth < constraints.maxWidth,
|
||||
// if the width is too small, only show one view title bar without the ancestors
|
||||
replacement: _buildRowName(),
|
||||
return SingleChildScrollView(
|
||||
scrollDirection: Axis.horizontal,
|
||||
child: SizedBox(
|
||||
height: 24,
|
||||
child: Row(
|
||||
// refresh the view title bar when the ancestors changed
|
||||
key: ValueKey(state.ancestors.hashCode),
|
||||
children: _buildViewTitles(state.ancestors),
|
||||
),
|
||||
);
|
||||
},
|
||||
),
|
||||
);
|
||||
},
|
||||
),
|
||||
@ -71,16 +64,22 @@ class ViewTitleBarWithRow extends StatelessWidget {
|
||||
// if the level is too deep, only show the root view, the database view and the row
|
||||
return views.length > 2
|
||||
? [
|
||||
_buildViewButton(views.first),
|
||||
const FlowyText.regular('/'),
|
||||
const FlowyText.regular(' ... /'),
|
||||
_buildViewButton(views[1]),
|
||||
const FlowySvg(FlowySvgs.title_bar_divider_s),
|
||||
const FlowyText.regular(' ... '),
|
||||
const FlowySvg(FlowySvgs.title_bar_divider_s),
|
||||
_buildViewButton(views.last),
|
||||
const FlowyText.regular('/'),
|
||||
const FlowySvg(FlowySvgs.title_bar_divider_s),
|
||||
_buildRowName(),
|
||||
]
|
||||
: [
|
||||
...views
|
||||
.map((e) => [_buildViewButton(e), const FlowyText.regular('/')])
|
||||
.map(
|
||||
(e) => [
|
||||
_buildViewButton(e),
|
||||
const FlowySvg(FlowySvgs.title_bar_divider_s),
|
||||
],
|
||||
)
|
||||
.flattened,
|
||||
_buildRowName(),
|
||||
];
|
||||
@ -89,9 +88,9 @@ class ViewTitleBarWithRow extends StatelessWidget {
|
||||
Widget _buildViewButton(ViewPB view) {
|
||||
return FlowyTooltip(
|
||||
message: view.name,
|
||||
child: _ViewTitle(
|
||||
child: ViewTitle(
|
||||
view: view,
|
||||
behavior: _ViewTitleBehavior.uneditable,
|
||||
behavior: ViewTitleBehavior.uneditable,
|
||||
onUpdated: () {},
|
||||
),
|
||||
);
|
||||
@ -180,11 +179,14 @@ class _TitleSkin extends IEditableTextCellSkin {
|
||||
onTap: () {},
|
||||
text: Row(
|
||||
children: [
|
||||
EmojiText(
|
||||
emoji: state.icon ?? "",
|
||||
fontSize: 18.0,
|
||||
if (state.icon != null) ...[
|
||||
FlowyText.emoji(
|
||||
state.icon!,
|
||||
fontSize: 14.0,
|
||||
figmaLineHeight: 18.0,
|
||||
),
|
||||
const HSpace(2.0),
|
||||
const HSpace(4.0),
|
||||
],
|
||||
ConstrainedBox(
|
||||
constraints: const BoxConstraints(maxWidth: 180),
|
||||
child: FlowyText.regular(
|
||||
@ -204,106 +206,6 @@ class _TitleSkin extends IEditableTextCellSkin {
|
||||
}
|
||||
}
|
||||
|
||||
enum _ViewTitleBehavior {
|
||||
editable,
|
||||
uneditable,
|
||||
}
|
||||
|
||||
class _ViewTitle extends StatefulWidget {
|
||||
const _ViewTitle({
|
||||
required this.view,
|
||||
this.behavior = _ViewTitleBehavior.editable,
|
||||
required this.onUpdated,
|
||||
}) : maxTitleWidth = 180;
|
||||
|
||||
final ViewPB view;
|
||||
final _ViewTitleBehavior behavior;
|
||||
final double maxTitleWidth;
|
||||
final VoidCallback onUpdated;
|
||||
|
||||
@override
|
||||
State<_ViewTitle> createState() => _ViewTitleState();
|
||||
}
|
||||
|
||||
class _ViewTitleState extends State<_ViewTitle> {
|
||||
late final viewListener = ViewListener(viewId: widget.view.id);
|
||||
|
||||
String name = '';
|
||||
String icon = '';
|
||||
|
||||
@override
|
||||
void initState() {
|
||||
super.initState();
|
||||
|
||||
name = widget.view.name.isEmpty
|
||||
? LocaleKeys.document_title_placeholder.tr()
|
||||
: widget.view.name;
|
||||
icon = widget.view.icon.value;
|
||||
|
||||
viewListener.start(
|
||||
onViewUpdated: (view) {
|
||||
if (name != view.name || icon != view.icon.value) {
|
||||
widget.onUpdated();
|
||||
}
|
||||
setState(() {
|
||||
name = view.name.isEmpty
|
||||
? LocaleKeys.document_title_placeholder.tr()
|
||||
: view.name;
|
||||
icon = view.icon.value;
|
||||
});
|
||||
},
|
||||
);
|
||||
}
|
||||
|
||||
@override
|
||||
void dispose() {
|
||||
viewListener.stop();
|
||||
|
||||
super.dispose();
|
||||
}
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
// root view
|
||||
if (widget.view.parentViewId.isEmpty) {
|
||||
return Row(
|
||||
children: [
|
||||
FlowyText.regular(name),
|
||||
const HSpace(4.0),
|
||||
],
|
||||
);
|
||||
}
|
||||
|
||||
final child = Row(
|
||||
children: [
|
||||
EmojiText(
|
||||
emoji: icon,
|
||||
fontSize: 18.0,
|
||||
),
|
||||
const HSpace(2.0),
|
||||
ConstrainedBox(
|
||||
constraints: BoxConstraints(
|
||||
maxWidth: widget.maxTitleWidth,
|
||||
),
|
||||
child: FlowyText.regular(
|
||||
name,
|
||||
overflow: TextOverflow.ellipsis,
|
||||
),
|
||||
),
|
||||
],
|
||||
);
|
||||
|
||||
return Listener(
|
||||
onPointerDown: (_) => context.read<TabsBloc>().openPlugin(widget.view),
|
||||
child: FlowyButton(
|
||||
useIntrinsicWidth: true,
|
||||
onTap: () {},
|
||||
text: child,
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
class RenameRowPopover extends StatefulWidget {
|
||||
const RenameRowPopover({
|
||||
super.key,
|
||||
|
@ -39,6 +39,10 @@ class DocumentCollaboratorsBloc
|
||||
if (userProfile != null) {
|
||||
_listener.start(
|
||||
onDocAwarenessUpdate: (states) {
|
||||
if (isClosed) {
|
||||
return;
|
||||
}
|
||||
|
||||
add(
|
||||
DocumentCollaboratorsEvent.update(
|
||||
userProfile,
|
||||
|
@ -190,9 +190,12 @@ class _ApplicationWidgetState extends State<ApplicationWidget> {
|
||||
if (view != null) {
|
||||
final view = action.arguments?[ActionArgumentKeys.view];
|
||||
final rowId = action.arguments?[ActionArgumentKeys.rowId];
|
||||
AppGlobals.rootNavKey.currentContext?.pushView(view, {
|
||||
AppGlobals.rootNavKey.currentContext?.pushView(
|
||||
view,
|
||||
arguments: {
|
||||
PluginArgumentKeys.rowId: rowId,
|
||||
});
|
||||
},
|
||||
);
|
||||
}
|
||||
}
|
||||
});
|
||||
|
@ -79,11 +79,11 @@ class ViewTitleBar extends StatelessWidget {
|
||||
final child = FlowyTooltip(
|
||||
key: ValueKey(view.id),
|
||||
message: view.name,
|
||||
child: _ViewTitle(
|
||||
child: ViewTitle(
|
||||
view: view,
|
||||
behavior: i == views.length - 1
|
||||
? _ViewTitleBehavior.editable // only the last one is editable
|
||||
: _ViewTitleBehavior.uneditable, // others are not editable
|
||||
? ViewTitleBehavior.editable // only the last one is editable
|
||||
: ViewTitleBehavior.uneditable, // others are not editable
|
||||
onUpdated: () {
|
||||
context
|
||||
.read<ViewTitleBarBloc>()
|
||||
@ -103,27 +103,28 @@ class ViewTitleBar extends StatelessWidget {
|
||||
}
|
||||
}
|
||||
|
||||
enum _ViewTitleBehavior {
|
||||
enum ViewTitleBehavior {
|
||||
editable,
|
||||
uneditable,
|
||||
}
|
||||
|
||||
class _ViewTitle extends StatefulWidget {
|
||||
const _ViewTitle({
|
||||
class ViewTitle extends StatefulWidget {
|
||||
const ViewTitle({
|
||||
super.key,
|
||||
required this.view,
|
||||
this.behavior = _ViewTitleBehavior.editable,
|
||||
this.behavior = ViewTitleBehavior.editable,
|
||||
required this.onUpdated,
|
||||
});
|
||||
|
||||
final ViewPB view;
|
||||
final _ViewTitleBehavior behavior;
|
||||
final ViewTitleBehavior behavior;
|
||||
final VoidCallback onUpdated;
|
||||
|
||||
@override
|
||||
State<_ViewTitle> createState() => _ViewTitleState();
|
||||
State<ViewTitle> createState() => _ViewTitleState();
|
||||
}
|
||||
|
||||
class _ViewTitleState extends State<_ViewTitle> {
|
||||
class _ViewTitleState extends State<ViewTitle> {
|
||||
final popoverController = PopoverController();
|
||||
final textEditingController = TextEditingController();
|
||||
|
||||
@ -137,7 +138,7 @@ class _ViewTitleState extends State<_ViewTitle> {
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
final isEditable = widget.behavior == _ViewTitleBehavior.editable;
|
||||
final isEditable = widget.behavior == ViewTitleBehavior.editable;
|
||||
|
||||
return BlocProvider(
|
||||
create: (_) =>
|
||||
|
@ -1327,6 +1327,7 @@
|
||||
"addOption": "Add option",
|
||||
"editProperty": "Edit property",
|
||||
"newProperty": "New property",
|
||||
"openRowDocument": "Open document",
|
||||
"deleteFieldPromptMessage": "Are you sure? This property will be deleted",
|
||||
"clearFieldPromptMessage": "Are you sure? All cells in this column will be emptied",
|
||||
"newColumn": "New Column",
|
||||
|
Loading…
Reference in New Issue
Block a user