浏览代码

chore: cache cell data

appflowy 3 年之前
父节点
当前提交
b0f5af679c
共有 23 个文件被更改,包括 249 次插入135 次删除
  1. 14 14
      frontend/app_flowy/lib/startup/deps_resolver.dart
  2. 91 3
      frontend/app_flowy/lib/workspace/application/grid/cell/cell_service.dart
  3. 3 4
      frontend/app_flowy/lib/workspace/application/grid/cell/checkbox_cell_bloc.dart
  4. 4 5
      frontend/app_flowy/lib/workspace/application/grid/cell/date_cell_bloc.dart
  5. 4 5
      frontend/app_flowy/lib/workspace/application/grid/cell/number_cell_bloc.dart
  6. 5 5
      frontend/app_flowy/lib/workspace/application/grid/cell/selection_cell_bloc.dart
  7. 1 1
      frontend/app_flowy/lib/workspace/application/grid/cell/selection_editor_bloc.dart
  8. 3 4
      frontend/app_flowy/lib/workspace/application/grid/cell/text_cell_bloc.dart
  9. 8 1
      frontend/app_flowy/lib/workspace/application/grid/grid_bloc.dart
  10. 13 24
      frontend/app_flowy/lib/workspace/application/grid/grid_service.dart
  11. 1 1
      frontend/app_flowy/lib/workspace/application/grid/row/row_bloc.dart
  12. 1 0
      frontend/app_flowy/lib/workspace/application/grid/row/row_detail_bloc.dart
  13. 33 25
      frontend/app_flowy/lib/workspace/application/grid/row/row_service.dart
  14. 2 0
      frontend/app_flowy/lib/workspace/presentation/plugins/grid/src/grid_page.dart
  15. 11 10
      frontend/app_flowy/lib/workspace/presentation/plugins/grid/src/widgets/cell/cell_builder.dart
  16. 3 3
      frontend/app_flowy/lib/workspace/presentation/plugins/grid/src/widgets/cell/checkbox_cell.dart
  17. 3 3
      frontend/app_flowy/lib/workspace/presentation/plugins/grid/src/widgets/cell/date_cell.dart
  18. 3 3
      frontend/app_flowy/lib/workspace/presentation/plugins/grid/src/widgets/cell/number_cell.dart
  19. 6 6
      frontend/app_flowy/lib/workspace/presentation/plugins/grid/src/widgets/cell/selection_cell/selection_cell.dart
  20. 1 0
      frontend/app_flowy/lib/workspace/presentation/plugins/grid/src/widgets/cell/selection_cell/selection_editor.dart
  21. 3 3
      frontend/app_flowy/lib/workspace/presentation/plugins/grid/src/widgets/cell/text_cell.dart
  22. 16 5
      frontend/app_flowy/lib/workspace/presentation/plugins/grid/src/widgets/row/grid_row.dart
  23. 20 10
      frontend/app_flowy/lib/workspace/presentation/plugins/grid/src/widgets/row/row_detail.dart

+ 14 - 14
frontend/app_flowy/lib/startup/deps_resolver.dart

@@ -168,34 +168,34 @@ void _resolveGridDeps(GetIt getIt) {
     ),
   );
 
-  getIt.registerFactoryParam<TextCellBloc, GridCell, void>(
-    (cellData, _) => TextCellBloc(
-      cellData: cellData,
+  getIt.registerFactoryParam<TextCellBloc, GridCellDataContext, void>(
+    (context, _) => TextCellBloc(
+      cellDataContext: context,
     ),
   );
 
-  getIt.registerFactoryParam<SelectionCellBloc, GridCell, void>(
-    (cellData, _) => SelectionCellBloc(
-      cellData: cellData,
+  getIt.registerFactoryParam<SelectionCellBloc, GridCellDataContext, void>(
+    (context, _) => SelectionCellBloc(
+      cellDataContext: context,
     ),
   );
 
-  getIt.registerFactoryParam<NumberCellBloc, GridCell, void>(
-    (cellData, _) => NumberCellBloc(
-      cellData: cellData,
+  getIt.registerFactoryParam<NumberCellBloc, GridCellDataContext, void>(
+    (context, _) => NumberCellBloc(
+      cellDataContext: context,
     ),
   );
 
-  getIt.registerFactoryParam<DateCellBloc, GridCell, void>(
-    (cellData, _) => DateCellBloc(
-      cellData: cellData,
+  getIt.registerFactoryParam<DateCellBloc, GridCellDataContext, void>(
+    (context, _) => DateCellBloc(
+      cellDataContext: context,
     ),
   );
 
-  getIt.registerFactoryParam<CheckboxCellBloc, GridCell, void>(
+  getIt.registerFactoryParam<CheckboxCellBloc, GridCellDataContext, void>(
     (cellData, _) => CheckboxCellBloc(
       service: CellService(),
-      cellData: cellData,
+      cellDataContext: cellData,
     ),
   );
 

+ 91 - 3
frontend/app_flowy/lib/workspace/application/grid/cell/cell_service.dart

@@ -1,12 +1,90 @@
 import 'dart:collection';
 
-import 'package:app_flowy/workspace/application/grid/row/row_service.dart';
-import 'package:flowy_sdk/log.dart';
-import 'package:flowy_sdk/protobuf/flowy-error/errors.pb.dart';
 import 'package:dartz/dartz.dart';
 import 'package:flowy_sdk/dispatch/dispatch.dart';
+import 'package:flowy_sdk/log.dart';
+import 'package:flowy_sdk/protobuf/flowy-error/errors.pb.dart';
 import 'package:flowy_sdk/protobuf/flowy-grid-data-model/grid.pb.dart';
 import 'package:flowy_sdk/protobuf/flowy-grid/cell_entities.pb.dart';
+import 'package:flutter/foundation.dart';
+import 'package:freezed_annotation/freezed_annotation.dart';
+
+part 'cell_service.freezed.dart';
+
+class GridCellDataContext {
+  GridCell cellData;
+  GridCellCache cellCache;
+  GridCellDataContext({
+    required this.cellData,
+    required this.cellCache,
+  });
+
+  String get gridId => cellData.gridId;
+
+  String get cellId => cellData.rowId + (cellData.cell?.fieldId ?? "");
+
+  String get rowId => cellData.rowId;
+
+  String get fieldId => cellData.field.id;
+
+  FieldType get fieldType => cellData.field.fieldType;
+
+  Field get field => cellData.field;
+}
+
+// key: rowId
+typedef CellDataMap = LinkedHashMap<String, GridCell>;
+
+abstract class GridCellCacheData {
+  String get fieldId;
+  String get cacheKey;
+  dynamic get cacheData;
+}
+
+abstract class GridCellFieldDelegate {
+  void onFieldChanged(void Function(String) callback);
+}
+
+class GridCellCache {
+  final String gridId;
+  final GridCellFieldDelegate fieldDelegate;
+
+  /// fieldId: {cacheKey: cacheData}
+  final Map<String, Map<String, dynamic>> _cells = {};
+  GridCellCache({
+    required this.gridId,
+    required this.fieldDelegate,
+  }) {
+    fieldDelegate.onFieldChanged((fieldId) {
+      _cells.remove(fieldId);
+    });
+  }
+
+  void insert<T extends GridCellCacheData>(T cacheData) {
+    var map = _cells[cacheData.fieldId];
+    if (map == null) {
+      _cells[cacheData.fieldId] = {};
+      map = _cells[cacheData.fieldId];
+    }
+
+    map![cacheData.cacheKey] = cacheData.cacheData;
+  }
+
+  T? get<T>(String fieldId, String cacheKey) {
+    final map = _cells[fieldId];
+    if (map == null) {
+      return null;
+    } else {
+      final object = map[cacheKey];
+      if (object is T) {
+        return object;
+      } else {
+        Log.error("Cache data type does not match the cache data type");
+        return null;
+      }
+    }
+  }
+}
 
 class CellService {
   CellService();
@@ -73,3 +151,13 @@ class CellCache {
     return "${identifier.rowId}/${identifier.field.id}";
   }
 }
+
+@freezed
+class GridCell with _$GridCell {
+  const factory GridCell({
+    required String gridId,
+    required String rowId,
+    required Field field,
+    Cell? cell,
+  }) = _GridCell;
+}

+ 3 - 4
frontend/app_flowy/lib/workspace/application/grid/cell/checkbox_cell_bloc.dart

@@ -1,5 +1,4 @@
 import 'package:app_flowy/workspace/application/grid/cell/cell_listener.dart';
-import 'package:app_flowy/workspace/application/grid/row/row_service.dart';
 import 'package:flowy_sdk/log.dart';
 import 'package:flowy_sdk/protobuf/flowy-grid-data-model/grid.pb.dart' show Cell;
 import 'package:flutter_bloc/flutter_bloc.dart';
@@ -15,10 +14,10 @@ class CheckboxCellBloc extends Bloc<CheckboxCellEvent, CheckboxCellState> {
 
   CheckboxCellBloc({
     required CellService service,
-    required GridCell cellData,
+    required GridCellDataContext cellDataContext,
   })  : _service = service,
-        _cellListener = CellListener(rowId: cellData.rowId, fieldId: cellData.field.id),
-        super(CheckboxCellState.initial(cellData)) {
+        _cellListener = CellListener(rowId: cellDataContext.rowId, fieldId: cellDataContext.fieldId),
+        super(CheckboxCellState.initial(cellDataContext.cellData)) {
     on<CheckboxCellEvent>(
       (event, emit) async {
         await event.map(

+ 4 - 5
frontend/app_flowy/lib/workspace/application/grid/cell/date_cell_bloc.dart

@@ -1,6 +1,5 @@
 import 'package:app_flowy/workspace/application/grid/cell/cell_listener.dart';
 import 'package:app_flowy/workspace/application/grid/field/field_listener.dart';
-import 'package:app_flowy/workspace/application/grid/row/row_service.dart';
 import 'package:flowy_sdk/log.dart';
 import 'package:flowy_sdk/protobuf/flowy-grid-data-model/grid.pb.dart' show Cell, Field;
 import 'package:flutter_bloc/flutter_bloc.dart';
@@ -15,11 +14,11 @@ class DateCellBloc extends Bloc<DateCellEvent, DateCellState> {
   final CellListener _cellListener;
   final SingleFieldListener _fieldListener;
 
-  DateCellBloc({required GridCell cellData})
+  DateCellBloc({required GridCellDataContext cellDataContext})
       : _service = CellService(),
-        _cellListener = CellListener(rowId: cellData.rowId, fieldId: cellData.field.id),
-        _fieldListener = SingleFieldListener(fieldId: cellData.field.id),
-        super(DateCellState.initial(cellData)) {
+        _cellListener = CellListener(rowId: cellDataContext.rowId, fieldId: cellDataContext.fieldId),
+        _fieldListener = SingleFieldListener(fieldId: cellDataContext.fieldId),
+        super(DateCellState.initial(cellDataContext.cellData)) {
     on<DateCellEvent>(
       (event, emit) async {
         event.map(

+ 4 - 5
frontend/app_flowy/lib/workspace/application/grid/cell/number_cell_bloc.dart

@@ -1,6 +1,5 @@
 import 'package:app_flowy/workspace/application/grid/cell/cell_listener.dart';
 import 'package:app_flowy/workspace/application/grid/field/field_listener.dart';
-import 'package:app_flowy/workspace/application/grid/row/row_service.dart';
 import 'package:flowy_sdk/log.dart';
 import 'package:flowy_sdk/protobuf/flowy-grid-data-model/grid.pb.dart';
 import 'package:flutter_bloc/flutter_bloc.dart';
@@ -16,11 +15,11 @@ class NumberCellBloc extends Bloc<NumberCellEvent, NumberCellState> {
   final SingleFieldListener _fieldListener;
 
   NumberCellBloc({
-    required GridCell cellData,
+    required GridCellDataContext cellDataContext,
   })  : _service = CellService(),
-        _cellListener = CellListener(rowId: cellData.rowId, fieldId: cellData.field.id),
-        _fieldListener = SingleFieldListener(fieldId: cellData.field.id),
-        super(NumberCellState.initial(cellData)) {
+        _cellListener = CellListener(rowId: cellDataContext.rowId, fieldId: cellDataContext.fieldId),
+        _fieldListener = SingleFieldListener(fieldId: cellDataContext.fieldId),
+        super(NumberCellState.initial(cellDataContext.cellData)) {
     on<NumberCellEvent>(
       (event, emit) async {
         await event.map(

+ 5 - 5
frontend/app_flowy/lib/workspace/application/grid/cell/selection_cell_bloc.dart

@@ -1,7 +1,7 @@
 import 'package:app_flowy/workspace/application/grid/cell/cell_listener.dart';
+import 'package:app_flowy/workspace/application/grid/cell/cell_service.dart';
 import 'package:app_flowy/workspace/application/grid/cell/select_option_service.dart';
 import 'package:app_flowy/workspace/application/grid/field/field_listener.dart';
-import 'package:app_flowy/workspace/application/grid/row/row_service.dart';
 import 'package:flowy_sdk/log.dart';
 import 'package:flowy_sdk/protobuf/flowy-grid/selection_type_option.pb.dart';
 import 'package:flutter_bloc/flutter_bloc.dart';
@@ -16,11 +16,11 @@ class SelectionCellBloc extends Bloc<SelectionCellEvent, SelectionCellState> {
   final SingleFieldListener _fieldListener;
 
   SelectionCellBloc({
-    required GridCell cellData,
+    required GridCellDataContext cellDataContext,
   })  : _service = SelectOptionService(),
-        _cellListener = CellListener(rowId: cellData.rowId, fieldId: cellData.field.id),
-        _fieldListener = SingleFieldListener(fieldId: cellData.field.id),
-        super(SelectionCellState.initial(cellData)) {
+        _cellListener = CellListener(rowId: cellDataContext.rowId, fieldId: cellDataContext.fieldId),
+        _fieldListener = SingleFieldListener(fieldId: cellDataContext.fieldId),
+        super(SelectionCellState.initial(cellDataContext.cellData)) {
     on<SelectionCellEvent>(
       (event, emit) async {
         await event.map(

+ 1 - 1
frontend/app_flowy/lib/workspace/application/grid/cell/selection_editor_bloc.dart

@@ -1,6 +1,6 @@
 import 'package:app_flowy/workspace/application/grid/cell/cell_listener.dart';
+import 'package:app_flowy/workspace/application/grid/cell/cell_service.dart';
 import 'package:app_flowy/workspace/application/grid/field/field_listener.dart';
-import 'package:app_flowy/workspace/application/grid/row/row_service.dart';
 import 'package:flowy_sdk/log.dart';
 import 'package:flowy_sdk/protobuf/flowy-grid-data-model/grid.pb.dart';
 import 'package:flowy_sdk/protobuf/flowy-grid/selection_type_option.pb.dart';

+ 3 - 4
frontend/app_flowy/lib/workspace/application/grid/cell/text_cell_bloc.dart

@@ -1,4 +1,3 @@
-import 'package:app_flowy/workspace/application/grid/row/row_service.dart';
 import 'package:flowy_sdk/log.dart';
 import 'package:flowy_sdk/protobuf/flowy-grid-data-model/grid.pb.dart' show Cell;
 import 'package:flutter_bloc/flutter_bloc.dart';
@@ -14,10 +13,10 @@ class TextCellBloc extends Bloc<TextCellEvent, TextCellState> {
   final CellListener _cellListener;
 
   TextCellBloc({
-    required GridCell cellData,
+    required GridCellDataContext cellDataContext,
   })  : _service = CellService(),
-        _cellListener = CellListener(rowId: cellData.rowId, fieldId: cellData.field.id),
-        super(TextCellState.initial(cellData)) {
+        _cellListener = CellListener(rowId: cellDataContext.rowId, fieldId: cellDataContext.fieldId),
+        super(TextCellState.initial(cellDataContext.cellData)) {
     on<TextCellEvent>(
       (event, emit) async {
         await event.map(

+ 8 - 1
frontend/app_flowy/lib/workspace/application/grid/grid_bloc.dart

@@ -5,6 +5,7 @@ import 'package:flowy_sdk/protobuf/flowy-folder-data-model/view.pb.dart';
 import 'package:flowy_sdk/protobuf/flowy-grid-data-model/protobuf.dart';
 import 'package:flutter_bloc/flutter_bloc.dart';
 import 'package:freezed_annotation/freezed_annotation.dart';
+import 'cell/cell_service.dart';
 import 'grid_service.dart';
 import 'row/row_service.dart';
 
@@ -14,6 +15,7 @@ class GridBloc extends Bloc<GridEvent, GridState> {
   final GridService _gridService;
   final GridFieldCache fieldCache;
   late final GridRowCache rowCache;
+  late final GridCellCache cellCache;
 
   GridBloc({required View view})
       : _gridService = GridService(gridId: view.id),
@@ -21,7 +23,12 @@ class GridBloc extends Bloc<GridEvent, GridState> {
         super(GridState.initial(view.id)) {
     rowCache = GridRowCache(
       gridId: view.id,
-      dataDelegate: GridRowDataDelegateAdaptor(fieldCache),
+      fieldDelegate: GridRowCacheDelegateImpl(fieldCache),
+    );
+
+    cellCache = GridCellCache(
+      gridId: view.id,
+      fieldDelegate: GridCellCacheDelegateImpl(fieldCache),
     );
 
     on<GridEvent>(

+ 13 - 24
frontend/app_flowy/lib/workspace/application/grid/grid_service.dart

@@ -8,6 +8,7 @@ import 'package:flowy_sdk/protobuf/flowy-grid-data-model/grid.pb.dart';
 import 'package:flutter/foundation.dart';
 import 'package:freezed_annotation/freezed_annotation.dart';
 
+import 'cell/cell_service.dart';
 import 'row/row_service.dart';
 
 class GridService {
@@ -155,43 +156,31 @@ class GridFieldCache {
   }
 }
 
-class GridRowDataDelegateAdaptor extends GridRowDataDelegate {
+class GridRowCacheDelegateImpl extends GridRowFieldDelegate {
   final GridFieldCache _cache;
+  GridRowCacheDelegateImpl(GridFieldCache cache) : _cache = cache;
 
-  GridRowDataDelegateAdaptor(GridFieldCache cache) : _cache = cache;
   @override
   UnmodifiableListView<Field> get fields => _cache.unmodifiableFields;
 
-  @override
-  GridRow buildGridRow(RowOrder rowOrder) {
-    return GridRow(
-      gridId: _cache.gridId,
-      fields: _cache.unmodifiableFields,
-      rowId: rowOrder.rowId,
-      height: rowOrder.height.toDouble(),
-    );
-  }
-
   @override
   void onFieldChanged(FieldDidUpdateCallback callback) {
     _cache.addListener(listener: () {
       callback();
     });
   }
+}
+
+class GridCellCacheDelegateImpl extends GridCellFieldDelegate {
+  final GridFieldCache _cache;
+  GridCellCacheDelegateImpl(GridFieldCache cache) : _cache = cache;
 
   @override
-  CellDataMap buildCellDataMap(String rowId, Row? rowData) {
-    var map = CellDataMap.new();
-    for (final field in fields) {
-      if (field.visibility) {
-        map[field.id] = GridCell(
-          rowId: rowId,
-          gridId: _cache.gridId,
-          cell: rowData?.cellByFieldId[field.id],
-          field: field,
-        );
+  void onFieldChanged(void Function(String) callback) {
+    _cache.addListener(onChanged: (fields) {
+      for (final field in fields) {
+        callback(field.id);
       }
-    }
-    return map;
+    });
   }
 }

+ 1 - 1
frontend/app_flowy/lib/workspace/application/grid/row/row_bloc.dart

@@ -1,9 +1,9 @@
 import 'dart:collection';
+import 'package:app_flowy/workspace/application/grid/cell/cell_service.dart';
 import 'package:flutter_bloc/flutter_bloc.dart';
 import 'package:freezed_annotation/freezed_annotation.dart';
 import 'dart:async';
 import 'row_service.dart';
-import 'package:dartz/dartz.dart';
 
 part 'row_bloc.freezed.dart';
 

+ 1 - 0
frontend/app_flowy/lib/workspace/application/grid/row/row_detail_bloc.dart

@@ -1,3 +1,4 @@
+import 'package:app_flowy/workspace/application/grid/cell/cell_service.dart';
 import 'package:flutter_bloc/flutter_bloc.dart';
 import 'package:freezed_annotation/freezed_annotation.dart';
 import 'dart:async';

+ 33 - 25
frontend/app_flowy/lib/workspace/application/grid/row/row_service.dart

@@ -1,5 +1,6 @@
 import 'dart:collection';
 
+import 'package:app_flowy/workspace/application/grid/cell/cell_service.dart';
 import 'package:dartz/dartz.dart';
 import 'package:flowy_sdk/dispatch/dispatch.dart';
 import 'package:flowy_sdk/log.dart';
@@ -13,12 +14,9 @@ part 'row_service.freezed.dart';
 
 typedef RowUpdateCallback = void Function();
 typedef FieldDidUpdateCallback = void Function();
-typedef CellDataMap = LinkedHashMap<String, GridCell>;
 
-abstract class GridRowDataDelegate {
+abstract class GridRowFieldDelegate {
   UnmodifiableListView<Field> get fields;
-  GridRow buildGridRow(RowOrder rowOrder);
-  CellDataMap buildCellDataMap(String rowId, Row? rowData);
   void onFieldChanged(FieldDidUpdateCallback callback);
 }
 
@@ -26,16 +24,25 @@ class GridRowCache {
   final String gridId;
   final RowsNotifier _rowNotifier;
   final GridRowListener _rowsListener;
-  final GridRowDataDelegate _dataDelegate;
+  final GridRowFieldDelegate _fieldDelegate;
 
   List<GridRow> get clonedRows => _rowNotifier.clonedRows;
 
-  GridRowCache({required this.gridId, required GridRowDataDelegate dataDelegate})
-      : _rowNotifier = RowsNotifier(rowBuilder: dataDelegate.buildGridRow),
+  GridRowCache({required this.gridId, required GridRowFieldDelegate fieldDelegate})
+      : _rowNotifier = RowsNotifier(
+          rowBuilder: (rowOrder) {
+            return GridRow(
+              gridId: gridId,
+              fields: fieldDelegate.fields,
+              rowId: rowOrder.rowId,
+              height: rowOrder.height.toDouble(),
+            );
+          },
+        ),
         _rowsListener = GridRowListener(gridId: gridId),
-        _dataDelegate = dataDelegate {
+        _fieldDelegate = fieldDelegate {
     //
-    dataDelegate.onFieldChanged(() => _rowNotifier.fieldDidChange());
+    fieldDelegate.onFieldChanged(() => _rowNotifier.fieldDidChange());
 
     // listen on the row update
     _rowsListener.rowsUpdateNotifier.addPublishListener((result) {
@@ -92,7 +99,7 @@ class GridRowCache {
       notify() {
         final row = _rowNotifier.rowDataWithId(rowId);
         if (row != null) {
-          final cellDataMap = _dataDelegate.buildCellDataMap(rowId, row);
+          final CellDataMap cellDataMap = _makeCellDataMap(rowId, row);
           onUpdated(cellDataMap);
         }
       }
@@ -111,6 +118,21 @@ class GridRowCache {
     return listenrHandler;
   }
 
+  CellDataMap _makeCellDataMap(String rowId, Row? row) {
+    var cellDataMap = CellDataMap.new();
+    for (final field in _fieldDelegate.fields) {
+      if (field.visibility) {
+        cellDataMap[field.id] = GridCell(
+          rowId: rowId,
+          gridId: gridId,
+          cell: row?.cellByFieldId[field.id],
+          field: field,
+        );
+      }
+    }
+    return cellDataMap;
+  }
+
   void removeRowListener(VoidCallback callback) {
     _rowNotifier.removeListener(callback);
   }
@@ -130,7 +152,7 @@ class GridRowCache {
       });
     }
 
-    return _dataDelegate.buildCellDataMap(rowId, data);
+    return _makeCellDataMap(rowId, data);
   }
 
   void updateWithBlock(List<GridBlockOrder> blocks) {
@@ -322,20 +344,6 @@ class GridRow with _$GridRow {
   }) = _GridRow;
 }
 
-@freezed
-class GridCell with _$GridCell {
-  const factory GridCell({
-    required String gridId,
-    required String rowId,
-    required Field field,
-    Cell? cell,
-  }) = _GridCell;
-
-  ValueKey key() {
-    return ValueKey(rowId + (cell?.fieldId ?? ""));
-  }
-}
-
 typedef InsertedIndexs = List<InsertedIndex>;
 typedef DeletedIndexs = List<DeletedIndex>;
 typedef UpdatedIndexs = LinkedHashMap<String, UpdatedIndex>;

+ 2 - 0
frontend/app_flowy/lib/workspace/presentation/plugins/grid/src/grid_page.dart

@@ -227,11 +227,13 @@ class _GridRowsState extends State<_GridRows> {
     Animation<double> animation,
   ) {
     final rowCache = context.read<GridBloc>().rowCache;
+    final cellCache = context.read<GridBloc>().cellCache;
     return SizeTransition(
       sizeFactor: animation,
       child: GridRowWidget(
         rowData: rowData,
         rowCache: rowCache,
+        cellCache: cellCache,
         key: ValueKey(rowData.rowId),
       ),
     );

+ 11 - 10
frontend/app_flowy/lib/workspace/presentation/plugins/grid/src/widgets/cell/cell_builder.dart

@@ -1,4 +1,4 @@
-import 'package:app_flowy/workspace/application/grid/row/row_service.dart';
+import 'package:app_flowy/workspace/application/grid/cell/cell_service.dart';
 import 'package:flowy_infra_ui/style_widget/hover.dart';
 import 'package:flowy_sdk/protobuf/flowy-grid-data-model/grid.pb.dart' show FieldType;
 import 'package:flutter/widgets.dart';
@@ -8,21 +8,22 @@ import 'number_cell.dart';
 import 'selection_cell/selection_cell.dart';
 import 'text_cell.dart';
 
-GridCellWidget buildGridCell(GridCell cellData, {GridCellStyle? style}) {
-  final key = cellData.key();
-  switch (cellData.field.fieldType) {
+GridCellWidget buildGridCell(GridCellDataContext cellDataContext, {GridCellStyle? style}) {
+  final key = ValueKey(cellDataContext.cellId);
+  final fieldType = cellDataContext.cellData.field.fieldType;
+  switch (fieldType) {
     case FieldType.Checkbox:
-      return CheckboxCell(cellData: cellData, key: key);
+      return CheckboxCell(cellDataContext: cellDataContext, key: key);
     case FieldType.DateTime:
-      return DateCell(cellData: cellData, key: key);
+      return DateCell(cellDataContext: cellDataContext, key: key);
     case FieldType.MultiSelect:
-      return MultiSelectCell(cellData: cellData, style: style, key: key);
+      return MultiSelectCell(cellDataContext: cellDataContext, style: style, key: key);
     case FieldType.Number:
-      return NumberCell(cellData: cellData, key: key);
+      return NumberCell(cellDataContext: cellDataContext, key: key);
     case FieldType.RichText:
-      return GridTextCell(cellData: cellData, style: style, key: key);
+      return GridTextCell(cellDataContext: cellDataContext, style: style, key: key);
     case FieldType.SingleSelect:
-      return SingleSelectCell(cellData: cellData, style: style, key: key);
+      return SingleSelectCell(cellDataContext: cellDataContext, style: style, key: key);
     default:
       throw UnimplementedError;
   }

+ 3 - 3
frontend/app_flowy/lib/workspace/presentation/plugins/grid/src/widgets/cell/checkbox_cell.dart

@@ -7,10 +7,10 @@ import 'package:flutter_bloc/flutter_bloc.dart';
 import 'cell_builder.dart';
 
 class CheckboxCell extends GridCellWidget {
-  final GridCell cellData;
+  final GridCellDataContext cellDataContext;
 
   CheckboxCell({
-    required this.cellData,
+    required this.cellDataContext,
     Key? key,
   }) : super(key: key);
 
@@ -23,7 +23,7 @@ class _CheckboxCellState extends State<CheckboxCell> {
 
   @override
   void initState() {
-    _cellBloc = getIt<CheckboxCellBloc>(param1: widget.cellData)..add(const CheckboxCellEvent.initial());
+    _cellBloc = getIt<CheckboxCellBloc>(param1: widget.cellDataContext)..add(const CheckboxCellEvent.initial());
     super.initState();
   }
 

+ 3 - 3
frontend/app_flowy/lib/workspace/presentation/plugins/grid/src/widgets/cell/date_cell.dart

@@ -14,10 +14,10 @@ abstract class GridCellDelegate {
 }
 
 class DateCell extends GridCellWidget {
-  final GridCell cellData;
+  final GridCellDataContext cellDataContext;
 
   DateCell({
-    required this.cellData,
+    required this.cellDataContext,
     Key? key,
   }) : super(key: key);
 
@@ -30,7 +30,7 @@ class _DateCellState extends State<DateCell> {
 
   @override
   void initState() {
-    _cellBloc = getIt<DateCellBloc>(param1: widget.cellData)..add(const DateCellEvent.initial());
+    _cellBloc = getIt<DateCellBloc>(param1: widget.cellDataContext)..add(const DateCellEvent.initial());
     super.initState();
   }
 

+ 3 - 3
frontend/app_flowy/lib/workspace/presentation/plugins/grid/src/widgets/cell/number_cell.dart

@@ -8,10 +8,10 @@ import 'package:flutter_bloc/flutter_bloc.dart';
 import 'cell_builder.dart';
 
 class NumberCell extends GridCellWidget {
-  final GridCell cellData;
+  final GridCellDataContext cellDataContext;
 
   NumberCell({
-    required this.cellData,
+    required this.cellDataContext,
     Key? key,
   }) : super(key: key);
 
@@ -27,7 +27,7 @@ class _NumberCellState extends State<NumberCell> {
 
   @override
   void initState() {
-    _cellBloc = getIt<NumberCellBloc>(param1: widget.cellData)..add(const NumberCellEvent.initial());
+    _cellBloc = getIt<NumberCellBloc>(param1: widget.cellDataContext)..add(const NumberCellEvent.initial());
     _controller = TextEditingController(text: _cellBloc.state.content);
     _focusNode = FocusNode();
     _focusNode.addListener(() {

+ 6 - 6
frontend/app_flowy/lib/workspace/presentation/plugins/grid/src/widgets/cell/selection_cell/selection_cell.dart

@@ -18,11 +18,11 @@ class SelectOptionCellStyle extends GridCellStyle {
 }
 
 class SingleSelectCell extends GridCellWidget {
-  final GridCell cellData;
+  final GridCellDataContext cellDataContext;
   late final SelectOptionCellStyle? cellStyle;
 
   SingleSelectCell({
-    required this.cellData,
+    required this.cellDataContext,
     GridCellStyle? style,
     Key? key,
   }) : super(key: key) {
@@ -42,7 +42,7 @@ class _SingleSelectCellState extends State<SingleSelectCell> {
 
   @override
   void initState() {
-    _cellBloc = getIt<SelectionCellBloc>(param1: widget.cellData)..add(const SelectionCellEvent.initial());
+    _cellBloc = getIt<SelectionCellBloc>(param1: widget.cellDataContext)..add(const SelectionCellEvent.initial());
     super.initState();
   }
 
@@ -89,11 +89,11 @@ class _SingleSelectCellState extends State<SingleSelectCell> {
 
 //----------------------------------------------------------------
 class MultiSelectCell extends GridCellWidget {
-  final GridCell cellData;
+  final GridCellDataContext cellDataContext;
   late final SelectOptionCellStyle? cellStyle;
 
   MultiSelectCell({
-    required this.cellData,
+    required this.cellDataContext,
     GridCellStyle? style,
     Key? key,
   }) : super(key: key) {
@@ -113,7 +113,7 @@ class _MultiSelectCellState extends State<MultiSelectCell> {
 
   @override
   void initState() {
-    _cellBloc = getIt<SelectionCellBloc>(param1: widget.cellData)..add(const SelectionCellEvent.initial());
+    _cellBloc = getIt<SelectionCellBloc>(param1: widget.cellDataContext)..add(const SelectionCellEvent.initial());
     super.initState();
   }
 

+ 1 - 0
frontend/app_flowy/lib/workspace/presentation/plugins/grid/src/widgets/cell/selection_cell/selection_editor.dart

@@ -1,4 +1,5 @@
 import 'dart:collection';
+import 'package:app_flowy/workspace/application/grid/cell/cell_service.dart';
 import 'package:app_flowy/workspace/application/grid/cell/selection_editor_bloc.dart';
 import 'package:app_flowy/workspace/application/grid/row/row_service.dart';
 import 'package:app_flowy/workspace/presentation/plugins/grid/src/layout/sizes.dart';

+ 3 - 3
frontend/app_flowy/lib/workspace/presentation/plugins/grid/src/widgets/cell/text_cell.dart

@@ -14,10 +14,10 @@ class GridTextCellStyle extends GridCellStyle {
 }
 
 class GridTextCell extends GridCellWidget {
-  final GridCell cellData;
+  final GridCellDataContext cellDataContext;
   late final GridTextCellStyle? cellStyle;
   GridTextCell({
-    required this.cellData,
+    required this.cellDataContext,
     GridCellStyle? style,
     Key? key,
   }) : super(key: key) {
@@ -41,7 +41,7 @@ class _GridTextCellState extends State<GridTextCell> {
 
   @override
   void initState() {
-    _cellBloc = getIt<TextCellBloc>(param1: widget.cellData);
+    _cellBloc = getIt<TextCellBloc>(param1: widget.cellDataContext);
     _cellBloc.add(const TextCellEvent.initial());
     _controller = TextEditingController(text: _cellBloc.state.content);
     _focusNode = FocusNode();

+ 16 - 5
frontend/app_flowy/lib/workspace/presentation/plugins/grid/src/widgets/row/grid_row.dart

@@ -8,17 +8,18 @@ import 'package:flutter/material.dart';
 import 'package:flutter_bloc/flutter_bloc.dart';
 import 'package:provider/provider.dart';
 import 'row_action_sheet.dart';
-import 'package:dartz/dartz.dart' show Option;
 
 import 'row_detail.dart';
 
 class GridRowWidget extends StatefulWidget {
   final GridRow rowData;
   final GridRowCache rowCache;
+  final GridCellCache cellCache;
 
   const GridRowWidget({
     required this.rowData,
     required this.rowCache,
+    required this.cellCache,
     Key? key,
   }) : super(key: key);
 
@@ -49,7 +50,7 @@ class _GridRowWidgetState extends State<GridRowWidget> {
           builder: (context, state) {
             final children = [
               const _RowLeading(),
-              _RowCells(onExpand: () => onExpandCell(context)),
+              _RowCells(cellCache: widget.cellCache, onExpand: () => onExpandCell(context)),
               const _RowTrailing(),
             ];
 
@@ -73,7 +74,11 @@ class _GridRowWidgetState extends State<GridRowWidget> {
   }
 
   void onExpandCell(BuildContext context) {
-    final page = RowDetailPage(rowData: widget.rowData, rowCache: widget.rowCache);
+    final page = RowDetailPage(
+      rowData: widget.rowData,
+      rowCache: widget.rowCache,
+      cellCache: widget.cellCache,
+    );
     page.show(context);
   }
 }
@@ -147,8 +152,9 @@ class _DeleteRowButton extends StatelessWidget {
 }
 
 class _RowCells extends StatelessWidget {
+  final GridCellCache cellCache;
   final VoidCallback onExpand;
-  const _RowCells({required this.onExpand, Key? key}) : super(key: key);
+  const _RowCells({required this.cellCache, required this.onExpand, Key? key}) : super(key: key);
 
   @override
   Widget build(BuildContext context) {
@@ -172,9 +178,14 @@ class _RowCells extends StatelessWidget {
           expander = _CellExpander(onExpand: onExpand);
         }
 
+        final cellDataContext = GridCellDataContext(
+          cellData: cellData,
+          cellCache: cellCache,
+        );
+
         return CellContainer(
           width: cellData.field.width.toDouble(),
-          child: buildGridCell(cellData),
+          child: buildGridCell(cellDataContext),
           expander: expander,
         );
       },

+ 20 - 10
frontend/app_flowy/lib/workspace/presentation/plugins/grid/src/widgets/row/row_detail.dart

@@ -1,3 +1,4 @@
+import 'package:app_flowy/workspace/application/grid/cell/cell_service.dart';
 import 'package:app_flowy/workspace/application/grid/field/field_service.dart';
 import 'package:app_flowy/workspace/application/grid/row/row_detail_bloc.dart';
 import 'package:app_flowy/workspace/application/grid/row/row_service.dart';
@@ -20,10 +21,12 @@ import 'package:window_size/window_size.dart';
 class RowDetailPage extends StatefulWidget with FlowyOverlayDelegate {
   final GridRow rowData;
   final GridRowCache rowCache;
+  final GridCellCache cellCache;
 
   const RowDetailPage({
     required this.rowData,
     required this.rowCache,
+    required this.cellCache,
     Key? key,
   }) : super(key: key);
 
@@ -63,15 +66,17 @@ class _RowDetailPageState extends State<RowDetailPage> {
       },
       child: Padding(
         padding: const EdgeInsets.symmetric(horizontal: 80, vertical: 40),
-        child: _PropertyList(),
+        child: _PropertyList(cellCache: widget.cellCache),
       ),
     );
   }
 }
 
 class _PropertyList extends StatelessWidget {
+  final GridCellCache cellCache;
   final ScrollController _scrollController;
   _PropertyList({
+    required this.cellCache,
     Key? key,
   })  : _scrollController = ScrollController(),
         super(key: key);
@@ -89,7 +94,11 @@ class _PropertyList extends StatelessWidget {
             controller: _scrollController,
             itemCount: state.cellDatas.length,
             itemBuilder: (BuildContext context, int index) {
-              return _RowDetailCell(cellData: state.cellDatas[index]);
+              final cellDataContext = GridCellDataContext(
+                cellData: state.cellDatas[index],
+                cellCache: cellCache,
+              );
+              return _RowDetailCell(cellDataContext: cellDataContext);
             },
             separatorBuilder: (BuildContext context, int index) {
               return const VSpace(2);
@@ -102,15 +111,16 @@ class _PropertyList extends StatelessWidget {
 }
 
 class _RowDetailCell extends StatelessWidget {
-  final GridCell cellData;
-  const _RowDetailCell({required this.cellData, Key? key}) : super(key: key);
+  final GridCellDataContext cellDataContext;
+  const _RowDetailCell({required this.cellDataContext, Key? key}) : super(key: key);
 
   @override
   Widget build(BuildContext context) {
     final theme = context.watch<AppTheme>();
+
     final cell = buildGridCell(
-      cellData,
-      style: _buildCellStyle(theme, cellData.field.fieldType),
+      cellDataContext,
+      style: _buildCellStyle(theme, cellDataContext.fieldType),
     );
     return SizedBox(
       height: 36,
@@ -120,7 +130,7 @@ class _RowDetailCell extends StatelessWidget {
         children: [
           SizedBox(
             width: 150,
-            child: FieldCellButton(field: cellData.field, onTap: () => _showFieldEditor(context)),
+            child: FieldCellButton(field: cellDataContext.field, onTap: () => _showFieldEditor(context)),
           ),
           const HSpace(10),
           Expanded(
@@ -136,10 +146,10 @@ class _RowDetailCell extends StatelessWidget {
 
   void _showFieldEditor(BuildContext context) {
     FieldEditor(
-      gridId: cellData.gridId,
+      gridId: cellDataContext.gridId,
       fieldContextLoader: FieldContextLoaderAdaptor(
-        gridId: cellData.gridId,
-        field: cellData.field,
+        gridId: cellDataContext.gridId,
+        field: cellDataContext.field,
       ),
     ).show(context);
   }