mirror of
				https://github.com/AppFlowy-IO/AppFlowy.git
				synced 2025-10-30 17:38:40 +00:00 
			
		
		
		
	feat: open a mobile grid row as a page (#4000)
* chore: restore text cursor color * chore: open row as a card in mobile * refactor: clean up code * chore: code review * chore: restore c++ shared library
This commit is contained in:
		
							parent
							
								
									771dd9979f
								
							
						
					
					
						commit
						cac3acd553
					
				| @ -15,7 +15,6 @@ import 'package:appflowy_backend/protobuf/flowy-folder2/view.pb.dart'; | |||||||
| import 'package:flutter_bloc/flutter_bloc.dart'; | import 'package:flutter_bloc/flutter_bloc.dart'; | ||||||
| import 'package:flutter/material.dart'; | import 'package:flutter/material.dart'; | ||||||
| import 'package:linked_scroll_controller/linked_scroll_controller.dart'; | import 'package:linked_scroll_controller/linked_scroll_controller.dart'; | ||||||
| import '../../application/field/field_controller.dart'; |  | ||||||
| import '../../application/row/row_cache.dart'; | import '../../application/row/row_cache.dart'; | ||||||
| import '../../application/row/row_controller.dart'; | import '../../application/row/row_controller.dart'; | ||||||
| import '../application/grid_bloc.dart'; | import '../application/grid_bloc.dart'; | ||||||
| @ -297,11 +296,14 @@ class _GridRows extends StatelessWidget { | |||||||
|     final rowMeta = rowCache.getRow(rowId)?.rowMeta; |     final rowMeta = rowCache.getRow(rowId)?.rowMeta; | ||||||
| 
 | 
 | ||||||
|     /// Return placeholder widget if the rowMeta is null. |     /// Return placeholder widget if the rowMeta is null. | ||||||
|     if (rowMeta == null) return const SizedBox.shrink(); |     if (rowMeta == null) { | ||||||
|  |       Log.warn('RowMeta is null for rowId: $rowId'); | ||||||
|  |       return const SizedBox.shrink(); | ||||||
|  |     } | ||||||
| 
 | 
 | ||||||
|     final fieldController = |     final fieldController = | ||||||
|         context.read<GridBloc>().databaseController.fieldController; |         context.read<GridBloc>().databaseController.fieldController; | ||||||
|     final dataController = RowController( |     final rowController = RowController( | ||||||
|       viewId: viewId, |       viewId: viewId, | ||||||
|       rowMeta: rowMeta, |       rowMeta: rowMeta, | ||||||
|       rowCache: rowCache, |       rowCache: rowCache, | ||||||
| @ -313,15 +315,18 @@ class _GridRows extends StatelessWidget { | |||||||
|       viewId: viewId, |       viewId: viewId, | ||||||
|       index: index, |       index: index, | ||||||
|       isDraggable: isDraggable, |       isDraggable: isDraggable, | ||||||
|       dataController: dataController, |       dataController: rowController, | ||||||
|       cellBuilder: GridCellBuilder(cellCache: dataController.cellCache), |       cellBuilder: GridCellBuilder(cellCache: rowController.cellCache), | ||||||
|       openDetailPage: (context, cellBuilder) { |       openDetailPage: (context, cellBuilder) { | ||||||
|         _openRowDetailPage( |         FlowyOverlay.show( | ||||||
|           context, |           context: context, | ||||||
|           rowId, |           builder: (BuildContext context) { | ||||||
|           fieldController, |             return RowDetailPage( | ||||||
|           rowCache, |               cellBuilder: cellBuilder, | ||||||
|           cellBuilder, |               rowController: rowController, | ||||||
|  |               fieldController: fieldController, | ||||||
|  |             ); | ||||||
|  |           }, | ||||||
|         ); |         ); | ||||||
|       }, |       }, | ||||||
|     ); |     ); | ||||||
| @ -335,37 +340,6 @@ class _GridRows extends StatelessWidget { | |||||||
| 
 | 
 | ||||||
|     return child; |     return child; | ||||||
|   } |   } | ||||||
| 
 |  | ||||||
|   void _openRowDetailPage( |  | ||||||
|     BuildContext context, |  | ||||||
|     RowId rowId, |  | ||||||
|     FieldController fieldController, |  | ||||||
|     RowCache rowCache, |  | ||||||
|     GridCellBuilder cellBuilder, |  | ||||||
|   ) { |  | ||||||
|     final rowMeta = rowCache.getRow(rowId)?.rowMeta; |  | ||||||
|     // Most of the cases, the rowMeta should not be null. |  | ||||||
|     if (rowMeta != null) { |  | ||||||
|       final dataController = RowController( |  | ||||||
|         viewId: viewId, |  | ||||||
|         rowMeta: rowMeta, |  | ||||||
|         rowCache: rowCache, |  | ||||||
|       ); |  | ||||||
| 
 |  | ||||||
|       FlowyOverlay.show( |  | ||||||
|         context: context, |  | ||||||
|         builder: (BuildContext context) { |  | ||||||
|           return RowDetailPage( |  | ||||||
|             cellBuilder: cellBuilder, |  | ||||||
|             rowController: dataController, |  | ||||||
|             fieldController: fieldController, |  | ||||||
|           ); |  | ||||||
|         }, |  | ||||||
|       ); |  | ||||||
|     } else { |  | ||||||
|       Log.warn('RowMeta is null for rowId: $rowId'); |  | ||||||
|     } |  | ||||||
|   } |  | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| class _WrapScrollView extends StatelessWidget { | class _WrapScrollView extends StatelessWidget { | ||||||
|  | |||||||
| @ -1,13 +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/mobile_card_detail_screen.dart'; | ||||||
| import 'package:appflowy/plugins/database_view/application/database_controller.dart'; | import 'package:appflowy/plugins/database_view/application/database_controller.dart'; | ||||||
| import 'package:appflowy/plugins/database_view/application/field/field_controller.dart'; |  | ||||||
| import 'package:appflowy/plugins/database_view/application/row/row_cache.dart'; | import 'package:appflowy/plugins/database_view/application/row/row_cache.dart'; | ||||||
| import 'package:appflowy/plugins/database_view/application/row/row_controller.dart'; | import 'package:appflowy/plugins/database_view/application/row/row_controller.dart'; | ||||||
| import 'package:appflowy/plugins/database_view/application/row/row_service.dart'; | import 'package:appflowy/plugins/database_view/application/row/row_service.dart'; | ||||||
| import 'package:appflowy/plugins/database_view/grid/application/grid_bloc.dart'; | import 'package:appflowy/plugins/database_view/grid/application/grid_bloc.dart'; | ||||||
| import 'package:appflowy/plugins/database_view/tab_bar/tab_bar_view.dart'; | import 'package:appflowy/plugins/database_view/tab_bar/tab_bar_view.dart'; | ||||||
| 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/row_detail.dart'; |  | ||||||
| import 'package:appflowy_backend/log.dart'; | import 'package:appflowy_backend/log.dart'; | ||||||
| import 'package:appflowy_backend/protobuf/flowy-folder2/protobuf.dart'; | import 'package:appflowy_backend/protobuf/flowy-folder2/protobuf.dart'; | ||||||
| import 'package:collection/collection.dart'; | import 'package:collection/collection.dart'; | ||||||
| @ -18,6 +17,7 @@ import 'package:flowy_infra_ui/style_widget/scrolling/styled_scrollview.dart'; | |||||||
| import 'package:flowy_infra_ui/widget/error_page.dart'; | import 'package:flowy_infra_ui/widget/error_page.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 'package:linked_scroll_controller/linked_scroll_controller.dart'; | import 'package:linked_scroll_controller/linked_scroll_controller.dart'; | ||||||
| 
 | 
 | ||||||
| import 'grid_page.dart'; | import 'grid_page.dart'; | ||||||
| @ -26,7 +26,7 @@ import 'layout/layout.dart'; | |||||||
| import 'layout/sizes.dart'; | import 'layout/sizes.dart'; | ||||||
| import 'widgets/footer/grid_footer.dart'; | import 'widgets/footer/grid_footer.dart'; | ||||||
| import 'widgets/header/grid_header.dart'; | import 'widgets/header/grid_header.dart'; | ||||||
| import 'widgets/row/row.dart'; | import 'widgets/row/mobile_row.dart'; | ||||||
| import 'widgets/shortcuts.dart'; | import 'widgets/shortcuts.dart'; | ||||||
| import '../../widgets/setting/mobile_database_settings_button.dart'; | import '../../widgets/setting/mobile_database_settings_button.dart'; | ||||||
| 
 | 
 | ||||||
| @ -299,32 +299,33 @@ class _GridRows extends StatelessWidget { | |||||||
|     final rowCache = context.read<GridBloc>().getRowCache(rowId); |     final rowCache = context.read<GridBloc>().getRowCache(rowId); | ||||||
|     final rowMeta = rowCache.getRow(rowId)?.rowMeta; |     final rowMeta = rowCache.getRow(rowId)?.rowMeta; | ||||||
| 
 | 
 | ||||||
|     /// Return placeholder widget if the rowMeta is null. |     if (rowMeta == null) { | ||||||
|     if (rowMeta == null) return const SizedBox.shrink(); |       Log.warn('RowMeta is null for rowId: $rowId'); | ||||||
|  |       return const SizedBox.shrink(); | ||||||
|  |     } | ||||||
| 
 | 
 | ||||||
|     final fieldController = |     final fieldController = | ||||||
|         context.read<GridBloc>().databaseController.fieldController; |         context.read<GridBloc>().databaseController.fieldController; | ||||||
|     final dataController = RowController( |     final rowController = RowController( | ||||||
|       viewId: viewId, |       viewId: viewId, | ||||||
|       rowMeta: rowMeta, |       rowMeta: rowMeta, | ||||||
|       rowCache: rowCache, |       rowCache: rowCache, | ||||||
|     ); |     ); | ||||||
| 
 | 
 | ||||||
|     final child = GridRow( |     final child = MobileGridRow( | ||||||
|       key: ValueKey(rowMeta.id), |       key: ValueKey(rowMeta.id), | ||||||
|       rowId: rowId, |       rowId: rowId, | ||||||
|       viewId: viewId, |       viewId: viewId, | ||||||
|       index: index, |  | ||||||
|       isDraggable: isDraggable, |       isDraggable: isDraggable, | ||||||
|       dataController: dataController, |       dataController: rowController, | ||||||
|       cellBuilder: GridCellBuilder(cellCache: dataController.cellCache), |       cellBuilder: GridCellBuilder(cellCache: rowController.cellCache), | ||||||
|       openDetailPage: (context, cellBuilder) { |       openDetailPage: (context, cellBuilder) { | ||||||
|         _openRowDetailPage( |         context.push( | ||||||
|           context, |           MobileCardDetailScreen.routeName, | ||||||
|           rowId, |           extra: { | ||||||
|           fieldController, |             MobileCardDetailScreen.argRowController: rowController, | ||||||
|           rowCache, |             MobileCardDetailScreen.argFieldController: fieldController, | ||||||
|           cellBuilder, |           }, | ||||||
|         ); |         ); | ||||||
|       }, |       }, | ||||||
|     ); |     ); | ||||||
| @ -338,37 +339,6 @@ class _GridRows extends StatelessWidget { | |||||||
| 
 | 
 | ||||||
|     return child; |     return child; | ||||||
|   } |   } | ||||||
| 
 |  | ||||||
|   void _openRowDetailPage( |  | ||||||
|     BuildContext context, |  | ||||||
|     RowId rowId, |  | ||||||
|     FieldController fieldController, |  | ||||||
|     RowCache rowCache, |  | ||||||
|     GridCellBuilder cellBuilder, |  | ||||||
|   ) { |  | ||||||
|     final rowMeta = rowCache.getRow(rowId)?.rowMeta; |  | ||||||
|     // Most of the cases, the rowMeta should not be null. |  | ||||||
|     if (rowMeta != null) { |  | ||||||
|       final dataController = RowController( |  | ||||||
|         viewId: viewId, |  | ||||||
|         rowMeta: rowMeta, |  | ||||||
|         rowCache: rowCache, |  | ||||||
|       ); |  | ||||||
| 
 |  | ||||||
|       FlowyOverlay.show( |  | ||||||
|         context: context, |  | ||||||
|         builder: (BuildContext context) { |  | ||||||
|           return RowDetailPage( |  | ||||||
|             cellBuilder: cellBuilder, |  | ||||||
|             rowController: dataController, |  | ||||||
|             fieldController: fieldController, |  | ||||||
|           ); |  | ||||||
|         }, |  | ||||||
|       ); |  | ||||||
|     } else { |  | ||||||
|       Log.warn('RowMeta is null for rowId: $rowId'); |  | ||||||
|     } |  | ||||||
|   } |  | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| class _WrapScrollView extends StatelessWidget { | class _WrapScrollView extends StatelessWidget { | ||||||
|  | |||||||
| @ -0,0 +1,190 @@ | |||||||
|  | import 'package:appflowy/generated/flowy_svgs.g.dart'; | ||||||
|  | import 'package:appflowy/plugins/database_view/application/cell/cell_service.dart'; | ||||||
|  | import 'package:appflowy/plugins/database_view/application/row/row_controller.dart'; | ||||||
|  | import 'package:appflowy/plugins/database_view/application/row/row_service.dart'; | ||||||
|  | import 'package:appflowy/plugins/database_view/grid/application/row/row_bloc.dart'; | ||||||
|  | import 'package:appflowy/plugins/database_view/widgets/row/accessory/cell_accessory.dart'; | ||||||
|  | import 'package:appflowy/plugins/database_view/widgets/row/cell_builder.dart'; | ||||||
|  | import 'package:appflowy/plugins/database_view/widgets/row/cells/mobile_cell_container.dart'; | ||||||
|  | import 'package:flowy_infra/theme_extension.dart'; | ||||||
|  | import 'package:flowy_infra_ui/flowy_infra_ui.dart'; | ||||||
|  | import 'package:flutter/foundation.dart'; | ||||||
|  | import 'package:flutter/material.dart'; | ||||||
|  | import 'package:flutter_bloc/flutter_bloc.dart'; | ||||||
|  | 
 | ||||||
|  | import '../../layout/sizes.dart'; | ||||||
|  | import "package:appflowy/generated/locale_keys.g.dart"; | ||||||
|  | import 'package:easy_localization/easy_localization.dart'; | ||||||
|  | 
 | ||||||
|  | class MobileGridRow extends StatefulWidget { | ||||||
|  |   final RowId viewId; | ||||||
|  |   final RowId rowId; | ||||||
|  |   final RowController dataController; | ||||||
|  |   final GridCellBuilder cellBuilder; | ||||||
|  |   final void Function(BuildContext, GridCellBuilder) openDetailPage; | ||||||
|  | 
 | ||||||
|  |   final bool isDraggable; | ||||||
|  | 
 | ||||||
|  |   const MobileGridRow({ | ||||||
|  |     super.key, | ||||||
|  |     required this.viewId, | ||||||
|  |     required this.rowId, | ||||||
|  |     required this.dataController, | ||||||
|  |     required this.cellBuilder, | ||||||
|  |     required this.openDetailPage, | ||||||
|  |     this.isDraggable = false, | ||||||
|  |   }); | ||||||
|  | 
 | ||||||
|  |   @override | ||||||
|  |   State<MobileGridRow> createState() => _MobileGridRowState(); | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | class _MobileGridRowState extends State<MobileGridRow> { | ||||||
|  |   late final RowBloc _rowBloc; | ||||||
|  | 
 | ||||||
|  |   @override | ||||||
|  |   void initState() { | ||||||
|  |     super.initState(); | ||||||
|  |     _rowBloc = RowBloc( | ||||||
|  |       rowId: widget.rowId, | ||||||
|  |       dataController: widget.dataController, | ||||||
|  |       viewId: widget.viewId, | ||||||
|  |     )..add(const RowEvent.initial()); | ||||||
|  |   } | ||||||
|  | 
 | ||||||
|  |   @override | ||||||
|  |   Widget build(BuildContext context) { | ||||||
|  |     return BlocProvider.value( | ||||||
|  |       value: _rowBloc, | ||||||
|  |       child: BlocBuilder<RowBloc, RowState>( | ||||||
|  |         // The row need to rebuild when the cell count changes. | ||||||
|  |         buildWhen: (p, c) => p.rowSource != c.rowSource, | ||||||
|  |         builder: (context, state) { | ||||||
|  |           return Row( | ||||||
|  |             children: [ | ||||||
|  |               SizedBox(width: GridSize.leadingHeaderPadding), | ||||||
|  |               Expanded( | ||||||
|  |                 child: RowContent( | ||||||
|  |                   builder: widget.cellBuilder, | ||||||
|  |                   onExpand: () => widget.openDetailPage( | ||||||
|  |                     context, | ||||||
|  |                     widget.cellBuilder, | ||||||
|  |                   ), | ||||||
|  |                 ), | ||||||
|  |               ), | ||||||
|  |             ], | ||||||
|  |           ); | ||||||
|  |         }, | ||||||
|  |       ), | ||||||
|  |     ); | ||||||
|  |   } | ||||||
|  | 
 | ||||||
|  |   @override | ||||||
|  |   Future<void> dispose() async { | ||||||
|  |     _rowBloc.close(); | ||||||
|  |     super.dispose(); | ||||||
|  |   } | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | class InsertRowButton extends StatelessWidget { | ||||||
|  |   const InsertRowButton({super.key}); | ||||||
|  | 
 | ||||||
|  |   @override | ||||||
|  |   Widget build(BuildContext context) { | ||||||
|  |     return FlowyIconButton( | ||||||
|  |       tooltipText: LocaleKeys.tooltip_addNewRow.tr(), | ||||||
|  |       hoverColor: AFThemeExtension.of(context).lightGreyHover, | ||||||
|  |       width: 20, | ||||||
|  |       height: 30, | ||||||
|  |       onPressed: () => context.read<RowBloc>().add(const RowEvent.createRow()), | ||||||
|  |       iconPadding: const EdgeInsets.all(3), | ||||||
|  |       icon: FlowySvg( | ||||||
|  |         FlowySvgs.add_s, | ||||||
|  |         color: Theme.of(context).colorScheme.tertiary, | ||||||
|  |       ), | ||||||
|  |     ); | ||||||
|  |   } | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | class RowContent extends StatelessWidget { | ||||||
|  |   final VoidCallback onExpand; | ||||||
|  |   final GridCellBuilder builder; | ||||||
|  |   const RowContent({ | ||||||
|  |     super.key, | ||||||
|  |     required this.builder, | ||||||
|  |     required this.onExpand, | ||||||
|  |   }); | ||||||
|  | 
 | ||||||
|  |   @override | ||||||
|  |   Widget build(BuildContext context) { | ||||||
|  |     return BlocBuilder<RowBloc, RowState>( | ||||||
|  |       buildWhen: (previous, current) => | ||||||
|  |           !listEquals(previous.cells, current.cells), | ||||||
|  |       builder: (context, state) { | ||||||
|  |         return IntrinsicHeight( | ||||||
|  |           child: Row( | ||||||
|  |             crossAxisAlignment: CrossAxisAlignment.stretch, | ||||||
|  |             children: [ | ||||||
|  |               ..._makeCells(context, state.cellByFieldId), | ||||||
|  |               _finalCellDecoration(context), | ||||||
|  |             ], | ||||||
|  |           ), | ||||||
|  |         ); | ||||||
|  |       }, | ||||||
|  |     ); | ||||||
|  |   } | ||||||
|  | 
 | ||||||
|  |   List<Widget> _makeCells( | ||||||
|  |     BuildContext context, | ||||||
|  |     CellContextByFieldId cellByFieldId, | ||||||
|  |   ) { | ||||||
|  |     return cellByFieldId.values.map( | ||||||
|  |       (cellId) { | ||||||
|  |         final GridCellWidget child = builder.build(cellId); | ||||||
|  | 
 | ||||||
|  |         return MobileCellContainer( | ||||||
|  |           width: cellId.fieldInfo.fieldSettings?.width.toDouble() ?? 140, | ||||||
|  |           isPrimary: cellId.fieldInfo.field.isPrimary, | ||||||
|  |           accessoryBuilder: (buildContext) { | ||||||
|  |             final builder = child.accessoryBuilder; | ||||||
|  |             final List<GridCellAccessoryBuilder> accessories = []; | ||||||
|  |             if (cellId.fieldInfo.field.isPrimary) { | ||||||
|  |               accessories.add( | ||||||
|  |                 GridCellAccessoryBuilder( | ||||||
|  |                   builder: (key) => PrimaryCellAccessory( | ||||||
|  |                     key: key, | ||||||
|  |                     onTapCallback: onExpand, | ||||||
|  |                     isCellEditing: buildContext.isCellEditing, | ||||||
|  |                   ), | ||||||
|  |                 ), | ||||||
|  |               ); | ||||||
|  |             } | ||||||
|  | 
 | ||||||
|  |             if (builder != null) { | ||||||
|  |               accessories.addAll(builder(buildContext)); | ||||||
|  |             } | ||||||
|  | 
 | ||||||
|  |             return accessories; | ||||||
|  |           }, | ||||||
|  |           child: child, | ||||||
|  |         ); | ||||||
|  |       }, | ||||||
|  |     ).toList(); | ||||||
|  |   } | ||||||
|  | 
 | ||||||
|  |   Widget _finalCellDecoration(BuildContext context) { | ||||||
|  |     return MouseRegion( | ||||||
|  |       cursor: SystemMouseCursors.basic, | ||||||
|  |       child: Container( | ||||||
|  |         width: GridSize.trailHeaderPadding, | ||||||
|  |         padding: GridSize.headerContentInsets, | ||||||
|  |         constraints: const BoxConstraints(minHeight: 46), | ||||||
|  |         decoration: BoxDecoration( | ||||||
|  |           border: Border( | ||||||
|  |             bottom: BorderSide(color: Theme.of(context).dividerColor), | ||||||
|  |           ), | ||||||
|  |         ), | ||||||
|  |       ), | ||||||
|  |     ); | ||||||
|  |   } | ||||||
|  | } | ||||||
| @ -0,0 +1,130 @@ | |||||||
|  | import 'package:appflowy/plugins/database_view/grid/presentation/layout/sizes.dart'; | ||||||
|  | import 'package:flutter/material.dart'; | ||||||
|  | import 'package:provider/provider.dart'; | ||||||
|  | import 'package:styled_widget/styled_widget.dart'; | ||||||
|  | 
 | ||||||
|  | import '../accessory/cell_accessory.dart'; | ||||||
|  | import '../accessory/cell_shortcuts.dart'; | ||||||
|  | import '../cell_builder.dart'; | ||||||
|  | import 'cell_container.dart'; | ||||||
|  | 
 | ||||||
|  | class MobileCellContainer extends StatelessWidget { | ||||||
|  |   final GridCellWidget child; | ||||||
|  |   final AccessoryBuilder? accessoryBuilder; | ||||||
|  |   final double width; | ||||||
|  |   final bool isPrimary; | ||||||
|  | 
 | ||||||
|  |   const MobileCellContainer({ | ||||||
|  |     super.key, | ||||||
|  |     required this.child, | ||||||
|  |     required this.width, | ||||||
|  |     required this.isPrimary, | ||||||
|  |     this.accessoryBuilder, | ||||||
|  |   }); | ||||||
|  | 
 | ||||||
|  |   @override | ||||||
|  |   Widget build(BuildContext context) { | ||||||
|  |     return ChangeNotifierProvider.value( | ||||||
|  |       value: child.cellContainerNotifier, | ||||||
|  |       child: Selector<CellContainerNotifier, bool>( | ||||||
|  |         selector: (context, notifier) => notifier.isFocus, | ||||||
|  |         builder: (providerContext, isFocus, _) { | ||||||
|  |           Widget container = Center(child: GridCellShortcuts(child: child)); | ||||||
|  | 
 | ||||||
|  |           if (accessoryBuilder != null) { | ||||||
|  |             final accessories = accessoryBuilder!.call( | ||||||
|  |               GridCellAccessoryBuildContext( | ||||||
|  |                 anchorContext: context, | ||||||
|  |                 isCellEditing: isFocus, | ||||||
|  |               ), | ||||||
|  |             ); | ||||||
|  | 
 | ||||||
|  |             if (accessories.isNotEmpty) { | ||||||
|  |               container = _GridCellEnterRegion( | ||||||
|  |                 accessories: accessories, | ||||||
|  |                 isPrimary: isPrimary, | ||||||
|  |                 child: container, | ||||||
|  |               ); | ||||||
|  |             } | ||||||
|  |           } | ||||||
|  | 
 | ||||||
|  |           return GestureDetector( | ||||||
|  |             behavior: HitTestBehavior.opaque, | ||||||
|  |             onTap: () { | ||||||
|  |               if (!isFocus) { | ||||||
|  |                 child.requestFocus.notify(); | ||||||
|  |               } | ||||||
|  |             }, | ||||||
|  |             child: Container( | ||||||
|  |               constraints: BoxConstraints(maxWidth: width, minHeight: 46), | ||||||
|  |               decoration: _makeBoxDecoration(context, isFocus), | ||||||
|  |               child: container, | ||||||
|  |             ), | ||||||
|  |           ); | ||||||
|  |         }, | ||||||
|  |       ), | ||||||
|  |     ); | ||||||
|  |   } | ||||||
|  | 
 | ||||||
|  |   BoxDecoration _makeBoxDecoration(BuildContext context, bool isFocus) { | ||||||
|  |     if (isFocus) { | ||||||
|  |       return BoxDecoration( | ||||||
|  |         border: Border.fromBorderSide( | ||||||
|  |           BorderSide( | ||||||
|  |             color: Theme.of(context).colorScheme.primary, | ||||||
|  |           ), | ||||||
|  |         ), | ||||||
|  |       ); | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     final borderSide = BorderSide(color: Theme.of(context).dividerColor); | ||||||
|  |     return BoxDecoration( | ||||||
|  |       border: Border(right: borderSide, bottom: borderSide), | ||||||
|  |     ); | ||||||
|  |   } | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | class _GridCellEnterRegion extends StatelessWidget { | ||||||
|  |   const _GridCellEnterRegion({ | ||||||
|  |     required this.child, | ||||||
|  |     required this.accessories, | ||||||
|  |     required this.isPrimary, | ||||||
|  |     Key? key, | ||||||
|  |   }) : super(key: key); | ||||||
|  | 
 | ||||||
|  |   final Widget child; | ||||||
|  |   final List<GridCellAccessoryBuilder> accessories; | ||||||
|  |   final bool isPrimary; | ||||||
|  | 
 | ||||||
|  |   @override | ||||||
|  |   Widget build(BuildContext context) { | ||||||
|  |     return Selector<CellContainerNotifier, bool>( | ||||||
|  |       selector: (context, cellNotifier) => | ||||||
|  |           !cellNotifier.isFocus && (cellNotifier.onEnter || isPrimary), | ||||||
|  |       builder: (context, showAccessory, _) { | ||||||
|  |         final List<Widget> children = [child]; | ||||||
|  | 
 | ||||||
|  |         if (showAccessory) { | ||||||
|  |           children.add( | ||||||
|  |             CellAccessoryContainer(accessories: accessories).positioned( | ||||||
|  |               right: GridSize.cellContentInsets.right, | ||||||
|  |             ), | ||||||
|  |           ); | ||||||
|  |         } | ||||||
|  | 
 | ||||||
|  |         return MouseRegion( | ||||||
|  |           cursor: SystemMouseCursors.click, | ||||||
|  |           onEnter: (p) => | ||||||
|  |               CellContainerNotifier.of(context, listen: false).onEnter = true, | ||||||
|  |           onExit: (p) => | ||||||
|  |               CellContainerNotifier.of(context, listen: false).onEnter = false, | ||||||
|  |           child: Stack( | ||||||
|  |             alignment: AlignmentDirectional.center, | ||||||
|  |             fit: StackFit.expand, | ||||||
|  |             children: children, | ||||||
|  |           ), | ||||||
|  |         ); | ||||||
|  |       }, | ||||||
|  |     ); | ||||||
|  |   } | ||||||
|  | } | ||||||
| @ -239,9 +239,6 @@ class MobileAppearance extends BaseAppearance { | |||||||
|       ), |       ), | ||||||
|       colorScheme: colorTheme, |       colorScheme: colorTheme, | ||||||
|       indicatorColor: Colors.blue, |       indicatorColor: Colors.blue, | ||||||
|       textSelectionTheme: TextSelectionThemeData( |  | ||||||
|         cursorColor: colorTheme.onBackground, |  | ||||||
|       ), |  | ||||||
|       extensions: [ |       extensions: [ | ||||||
|         AFThemeExtension( |         AFThemeExtension( | ||||||
|           warning: theme.yellow, |           warning: theme.yellow, | ||||||
|  | |||||||
		Loading…
	
	
			
			x
			
			
		
	
		Reference in New Issue
	
	Block a user
	 Richard Shiue
						Richard Shiue