瀏覽代碼

Merge branch 'main' into feat/insert_cell_test

appflowy 2 年之前
父節點
當前提交
cca577f967
共有 71 個文件被更改,包括 1045 次插入3010 次删除
  1. 5 1
      .gitignore
  2. 5 5
      frontend/app_flowy/lib/startup/deps_resolver.dart
  3. 7 6
      frontend/app_flowy/lib/workspace/application/grid/block/block_cache.dart
  4. 0 109
      frontend/app_flowy/lib/workspace/application/grid/cell/cell_service/cache.dart
  5. 70 0
      frontend/app_flowy/lib/workspace/application/grid/cell/cell_service/cell_cache.dart
  6. 6 61
      frontend/app_flowy/lib/workspace/application/grid/cell/cell_service/cell_data_loader.dart
  7. 15 18
      frontend/app_flowy/lib/workspace/application/grid/cell/cell_service/cell_data_persistence.dart
  8. 60 0
      frontend/app_flowy/lib/workspace/application/grid/cell/cell_service/cell_field_notifier.dart
  9. 25 20
      frontend/app_flowy/lib/workspace/application/grid/cell/cell_service/cell_service.dart
  10. 180 98
      frontend/app_flowy/lib/workspace/application/grid/cell/cell_service/context_builder.dart
  11. 2 2
      frontend/app_flowy/lib/workspace/application/grid/cell/checkbox_cell_bloc.dart
  12. 1 1
      frontend/app_flowy/lib/workspace/application/grid/cell/date_cal_bloc.dart
  13. 2 2
      frontend/app_flowy/lib/workspace/application/grid/cell/date_cell_bloc.dart
  14. 2 2
      frontend/app_flowy/lib/workspace/application/grid/cell/number_cell_bloc.dart
  15. 2 2
      frontend/app_flowy/lib/workspace/application/grid/cell/select_option_cell_bloc.dart
  16. 11 28
      frontend/app_flowy/lib/workspace/application/grid/cell/select_option_editor_bloc.dart
  17. 5 5
      frontend/app_flowy/lib/workspace/application/grid/cell/select_option_service.dart
  18. 2 2
      frontend/app_flowy/lib/workspace/application/grid/cell/text_cell_bloc.dart
  19. 2 2
      frontend/app_flowy/lib/workspace/application/grid/cell/url_cell_bloc.dart
  20. 2 2
      frontend/app_flowy/lib/workspace/application/grid/cell/url_cell_editor_bloc.dart
  21. 23 12
      frontend/app_flowy/lib/workspace/application/grid/field/field_editor_bloc.dart
  22. 0 57
      frontend/app_flowy/lib/workspace/application/grid/field/field_editor_pannel_bloc.dart
  23. 19 22
      frontend/app_flowy/lib/workspace/application/grid/field/field_service.dart
  24. 57 0
      frontend/app_flowy/lib/workspace/application/grid/field/field_type_option_edit_bloc.dart
  25. 2 2
      frontend/app_flowy/lib/workspace/application/grid/field/type_option/date_bloc.dart
  26. 8 7
      frontend/app_flowy/lib/workspace/application/grid/field/type_option/multi_select_type_option.dart
  27. 2 2
      frontend/app_flowy/lib/workspace/application/grid/field/type_option/number_bloc.dart
  28. 5 5
      frontend/app_flowy/lib/workspace/application/grid/field/type_option/single_select_type_option.dart
  29. 13 13
      frontend/app_flowy/lib/workspace/application/grid/field/type_option/type_option_service.dart
  30. 5 5
      frontend/app_flowy/lib/workspace/application/grid/grid_bloc.dart
  31. 5 6
      frontend/app_flowy/lib/workspace/application/grid/grid_service.dart
  32. 1 1
      frontend/app_flowy/lib/workspace/application/grid/prelude.dart
  33. 1 1
      frontend/app_flowy/lib/workspace/application/grid/row/row_action_sheet_bloc.dart
  34. 5 5
      frontend/app_flowy/lib/workspace/application/grid/row/row_bloc.dart
  35. 6 6
      frontend/app_flowy/lib/workspace/application/grid/row/row_detail_bloc.dart
  36. 60 49
      frontend/app_flowy/lib/workspace/application/grid/row/row_service.dart
  37. 4 2
      frontend/app_flowy/lib/workspace/presentation/plugins/grid/src/grid_page.dart
  38. 34 22
      frontend/app_flowy/lib/workspace/presentation/plugins/grid/src/widgets/cell/cell_builder.dart
  39. 3 3
      frontend/app_flowy/lib/workspace/presentation/plugins/grid/src/widgets/cell/checkbox_cell.dart
  40. 4 4
      frontend/app_flowy/lib/workspace/presentation/plugins/grid/src/widgets/cell/date_cell/date_cell.dart
  41. 6 6
      frontend/app_flowy/lib/workspace/presentation/plugins/grid/src/widgets/cell/date_cell/date_editor.dart
  42. 3 3
      frontend/app_flowy/lib/workspace/presentation/plugins/grid/src/widgets/cell/number_cell.dart
  43. 11 11
      frontend/app_flowy/lib/workspace/presentation/plugins/grid/src/widgets/cell/select_option_cell/select_option_cell.dart
  44. 5 5
      frontend/app_flowy/lib/workspace/presentation/plugins/grid/src/widgets/cell/select_option_cell/select_option_editor.dart
  45. 3 3
      frontend/app_flowy/lib/workspace/presentation/plugins/grid/src/widgets/cell/text_cell.dart
  46. 5 5
      frontend/app_flowy/lib/workspace/presentation/plugins/grid/src/widgets/cell/url_cell/cell_editor.dart
  47. 9 9
      frontend/app_flowy/lib/workspace/presentation/plugins/grid/src/widgets/cell/url_cell/url_cell.dart
  48. 1 1
      frontend/app_flowy/lib/workspace/presentation/plugins/grid/src/widgets/header/field_cell.dart
  49. 16 13
      frontend/app_flowy/lib/workspace/presentation/plugins/grid/src/widgets/header/field_editor.dart
  50. 0 243
      frontend/app_flowy/lib/workspace/presentation/plugins/grid/src/widgets/header/field_editor_pannel.dart
  51. 127 0
      frontend/app_flowy/lib/workspace/presentation/plugins/grid/src/widgets/header/field_type_option_editor.dart
  52. 1 1
      frontend/app_flowy/lib/workspace/presentation/plugins/grid/src/widgets/header/grid_header.dart
  53. 108 0
      frontend/app_flowy/lib/workspace/presentation/plugins/grid/src/widgets/header/type_option/builder.dart
  54. 6 6
      frontend/app_flowy/lib/workspace/presentation/plugins/grid/src/widgets/header/type_option/checkbox.dart
  55. 7 4
      frontend/app_flowy/lib/workspace/presentation/plugins/grid/src/widgets/header/type_option/date.dart
  56. 5 4
      frontend/app_flowy/lib/workspace/presentation/plugins/grid/src/widgets/header/type_option/multi_select.dart
  57. 6 4
      frontend/app_flowy/lib/workspace/presentation/plugins/grid/src/widgets/header/type_option/number.dart
  58. 6 7
      frontend/app_flowy/lib/workspace/presentation/plugins/grid/src/widgets/header/type_option/rich_text.dart
  59. 1 1
      frontend/app_flowy/lib/workspace/presentation/plugins/grid/src/widgets/header/type_option/select_option.dart
  60. 5 4
      frontend/app_flowy/lib/workspace/presentation/plugins/grid/src/widgets/header/type_option/single_select.dart
  61. 6 6
      frontend/app_flowy/lib/workspace/presentation/plugins/grid/src/widgets/header/type_option/url.dart
  62. 26 12
      frontend/app_flowy/lib/workspace/presentation/plugins/grid/src/widgets/row/grid_row.dart
  63. 20 18
      frontend/app_flowy/lib/workspace/presentation/plugins/grid/src/widgets/row/row_detail.dart
  64. 1 1
      frontend/app_flowy/lib/workspace/presentation/plugins/grid/src/widgets/toolbar/grid_property.dart
  65. 0 231
      frontend/app_flowy/packages/flowy_infra/pubspec.lock
  66. 0 334
      frontend/app_flowy/packages/flowy_infra_ui/example/pubspec.lock
  67. 0 168
      frontend/app_flowy/packages/flowy_infra_ui/flowy_infra_ui_platform_interface/pubspec.lock
  68. 0 187
      frontend/app_flowy/packages/flowy_infra_ui/flowy_infra_ui_web/pubspec.lock
  69. 0 320
      frontend/app_flowy/packages/flowy_infra_ui/pubspec.lock
  70. 0 309
      frontend/app_flowy/packages/flowy_sdk/example/pubspec.lock
  71. 0 504
      frontend/app_flowy/packages/flowy_sdk/pubspec.lock

+ 5 - 1
.gitignore

@@ -27,4 +27,8 @@ frontend/.vscode/*
 !frontend/.vscode/tasks.json
 !frontend/.vscode/launch.json
 !frontend/.vscode/extensions.json
-!frontend/.vscode/*.code-snippets
+!frontend/.vscode/*.code-snippets
+
+# Commit the highest level pubspec.lock, but ignore the others
+pubspec.lock
+!frontend/app_flowy/pubspec.lock

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

@@ -153,31 +153,31 @@ void _resolveGridDeps(GetIt getIt) {
     ),
   );
 
-  getIt.registerFactoryParam<TextCellBloc, GridCellContext, void>(
+  getIt.registerFactoryParam<TextCellBloc, GridCellController, void>(
     (context, _) => TextCellBloc(
       cellContext: context,
     ),
   );
 
-  getIt.registerFactoryParam<SelectOptionCellBloc, GridSelectOptionCellContext, void>(
+  getIt.registerFactoryParam<SelectOptionCellBloc, GridSelectOptionCellController, void>(
     (context, _) => SelectOptionCellBloc(
       cellContext: context,
     ),
   );
 
-  getIt.registerFactoryParam<NumberCellBloc, GridCellContext, void>(
+  getIt.registerFactoryParam<NumberCellBloc, GridCellController, void>(
     (context, _) => NumberCellBloc(
       cellContext: context,
     ),
   );
 
-  getIt.registerFactoryParam<DateCellBloc, GridDateCellContext, void>(
+  getIt.registerFactoryParam<DateCellBloc, GridDateCellController, void>(
     (context, _) => DateCellBloc(
       cellContext: context,
     ),
   );
 
-  getIt.registerFactoryParam<CheckboxCellBloc, GridCellContext, void>(
+  getIt.registerFactoryParam<CheckboxCellBloc, GridCellController, void>(
     (cellData, _) => CheckboxCellBloc(
       service: CellService(),
       cellContext: cellData,

+ 7 - 6
frontend/app_flowy/lib/workspace/application/grid/block/block_service.dart → frontend/app_flowy/lib/workspace/application/grid/block/block_cache.dart

@@ -6,24 +6,25 @@ import 'package:flowy_sdk/protobuf/flowy-grid/block_entities.pb.dart';
 
 import 'block_listener.dart';
 
-class GridBlockCacheService {
+/// Read https://appflowy.gitbook.io/docs/essential-documentation/contribute-to-appflowy/architecture/frontend/grid for more information
+class GridBlockCache {
   final String gridId;
   final GridBlock block;
-  late GridRowCacheService _rowCache;
+  late GridRowCache _rowCache;
   late GridBlockListener _listener;
 
   List<GridRow> get rows => _rowCache.rows;
-  GridRowCacheService get rowCache => _rowCache;
+  GridRowCache get rowCache => _rowCache;
 
-  GridBlockCacheService({
+  GridBlockCache({
     required this.gridId,
     required this.block,
     required GridFieldCache fieldCache,
   }) {
-    _rowCache = GridRowCacheService(
+    _rowCache = GridRowCache(
       gridId: gridId,
       block: block,
-      delegate: GridRowCacheDelegateImpl(fieldCache),
+      notifier: GridRowCacheFieldNotifierImpl(fieldCache),
     );
 
     _listener = GridBlockListener(blockId: block.id);

+ 0 - 109
frontend/app_flowy/lib/workspace/application/grid/cell/cell_service/cache.dart

@@ -1,109 +0,0 @@
-part of 'cell_service.dart';
-
-typedef GridCellMap = LinkedHashMap<String, GridCell>;
-
-class _GridCellCacheObject {
-  _GridCellCacheKey key;
-  dynamic object;
-  _GridCellCacheObject({
-    required this.key,
-    required this.object,
-  });
-}
-
-class _GridCellCacheKey {
-  final String fieldId;
-  final String rowId;
-  _GridCellCacheKey({
-    required this.fieldId,
-    required this.rowId,
-  });
-}
-
-abstract class GridCellCacheDelegate {
-  void onFieldUpdated(void Function(Field) callback);
-}
-
-class GridCellCacheService {
-  final String gridId;
-  final GridCellCacheDelegate delegate;
-
-  /// fieldId: {objectId: callback}
-  final Map<String, Map<String, List<VoidCallback>>> _fieldListenerByFieldId = {};
-
-  /// fieldId: {cacheKey: cacheData}
-  final Map<String, Map<String, dynamic>> _cellDataByFieldId = {};
-  GridCellCacheService({
-    required this.gridId,
-    required this.delegate,
-  }) {
-    delegate.onFieldUpdated((field) {
-      _cellDataByFieldId.remove(field.id);
-      final map = _fieldListenerByFieldId[field.id];
-      if (map != null) {
-        for (final callbacks in map.values) {
-          for (final callback in callbacks) {
-            callback();
-          }
-        }
-      }
-    });
-  }
-
-  void addFieldListener(_GridCellCacheKey cacheKey, VoidCallback onFieldChanged) {
-    var map = _fieldListenerByFieldId[cacheKey.fieldId];
-    if (map == null) {
-      _fieldListenerByFieldId[cacheKey.fieldId] = {};
-      map = _fieldListenerByFieldId[cacheKey.fieldId];
-      map![cacheKey.rowId] = [onFieldChanged];
-    } else {
-      var objects = map[cacheKey.rowId];
-      if (objects == null) {
-        map[cacheKey.rowId] = [onFieldChanged];
-      } else {
-        objects.add(onFieldChanged);
-      }
-    }
-  }
-
-  void removeFieldListener(_GridCellCacheKey cacheKey, VoidCallback fn) {
-    var callbacks = _fieldListenerByFieldId[cacheKey.fieldId]?[cacheKey.rowId];
-    final index = callbacks?.indexWhere((callback) => callback == fn);
-    if (index != null && index != -1) {
-      callbacks?.removeAt(index);
-    }
-  }
-
-  void insert<T extends _GridCellCacheObject>(T item) {
-    var map = _cellDataByFieldId[item.key.fieldId];
-    if (map == null) {
-      _cellDataByFieldId[item.key.fieldId] = {};
-      map = _cellDataByFieldId[item.key.fieldId];
-    }
-
-    map![item.key.rowId] = item.object;
-  }
-
-  T? get<T>(_GridCellCacheKey key) {
-    final map = _cellDataByFieldId[key.fieldId];
-    if (map == null) {
-      return null;
-    } else {
-      final object = map[key.rowId];
-      if (object is T) {
-        return object;
-      } else {
-        if (object != null) {
-          Log.error("Cache data type does not match the cache data type");
-        }
-
-        return null;
-      }
-    }
-  }
-
-  Future<void> dispose() async {
-    _fieldListenerByFieldId.clear();
-    _cellDataByFieldId.clear();
-  }
-}

+ 70 - 0
frontend/app_flowy/lib/workspace/application/grid/cell/cell_service/cell_cache.dart

@@ -0,0 +1,70 @@
+part of 'cell_service.dart';
+
+typedef GridCellMap = LinkedHashMap<String, GridCellIdentifier>;
+
+class GridCell {
+  dynamic object;
+  GridCell({
+    required this.object,
+  });
+}
+
+/// Use to index the cell in the grid.
+/// We use [fieldId + rowId] to identify the cell.
+class GridCellCacheKey {
+  final String fieldId;
+  final String rowId;
+  GridCellCacheKey({
+    required this.fieldId,
+    required this.rowId,
+  });
+}
+
+/// GridCellCache is used to cache cell data of each block.
+/// We use GridCellCacheKey to index the cell in the cache.
+/// Read https://appflowy.gitbook.io/docs/essential-documentation/contribute-to-appflowy/architecture/frontend/grid
+/// for more information
+class GridCellCache {
+  final String gridId;
+
+  /// fieldId: {cacheKey: GridCell}
+  final Map<String, Map<String, dynamic>> _cellDataByFieldId = {};
+  GridCellCache({
+    required this.gridId,
+  });
+
+  void remove(String fieldId) {
+    _cellDataByFieldId.remove(fieldId);
+  }
+
+  void insert<T extends GridCell>(GridCellCacheKey key, T value) {
+    var map = _cellDataByFieldId[key.fieldId];
+    if (map == null) {
+      _cellDataByFieldId[key.fieldId] = {};
+      map = _cellDataByFieldId[key.fieldId];
+    }
+
+    map![key.rowId] = value.object;
+  }
+
+  T? get<T>(GridCellCacheKey key) {
+    final map = _cellDataByFieldId[key.fieldId];
+    if (map == null) {
+      return null;
+    } else {
+      final value = map[key.rowId];
+      if (value is T) {
+        return value;
+      } else {
+        if (value != null) {
+          Log.error("Expected value type: $T, but receive $value");
+        }
+        return null;
+      }
+    }
+  }
+
+  Future<void> dispose() async {
+    _cellDataByFieldId.clear();
+  }
+}

+ 6 - 61
frontend/app_flowy/lib/workspace/application/grid/cell/cell_service/cell_data_loader.dart

@@ -3,58 +3,26 @@ part of 'cell_service.dart';
 abstract class IGridCellDataConfig {
   // The cell data will reload if it receives the field's change notification.
   bool get reloadOnFieldChanged;
-
-  // When the reloadOnCellChanged is true, it will load the cell data after user input.
-  // For example: The number cell reload the cell data that carries the format
-  // user input: 12
-  // cell display: $12
-  bool get reloadOnCellChanged;
-}
-
-class GridCellDataConfig implements IGridCellDataConfig {
-  @override
-  final bool reloadOnCellChanged;
-
-  @override
-  final bool reloadOnFieldChanged;
-
-  const GridCellDataConfig({
-    this.reloadOnCellChanged = false,
-    this.reloadOnFieldChanged = false,
-  });
-}
-
-abstract class IGridCellDataLoader<T> {
-  Future<T?> loadData();
-
-  IGridCellDataConfig get config;
 }
 
 abstract class ICellDataParser<T> {
   T? parserData(List<int> data);
 }
 
-class GridCellDataLoader<T> extends IGridCellDataLoader<T> {
+class GridCellDataLoader<T> {
   final CellService service = CellService();
-  final GridCell gridCell;
+  final GridCellIdentifier cellId;
   final ICellDataParser<T> parser;
-
-  @override
-  final IGridCellDataConfig config;
+  final bool reloadOnFieldChanged;
 
   GridCellDataLoader({
-    required this.gridCell,
+    required this.cellId,
     required this.parser,
-    this.config = const GridCellDataConfig(),
+    this.reloadOnFieldChanged = false,
   });
 
-  @override
   Future<T?> loadData() {
-    final fut = service.getCell(
-      gridId: gridCell.gridId,
-      fieldId: gridCell.field.id,
-      rowId: gridCell.rowId,
-    );
+    final fut = service.getCell(cellId: cellId);
     return fut.then(
       (result) => result.fold((Cell cell) {
         try {
@@ -72,29 +40,6 @@ class GridCellDataLoader<T> extends IGridCellDataLoader<T> {
   }
 }
 
-class SelectOptionCellDataLoader extends IGridCellDataLoader<SelectOptionCellData> {
-  final SelectOptionService service;
-  final GridCell gridCell;
-  SelectOptionCellDataLoader({
-    required this.gridCell,
-  }) : service = SelectOptionService(gridCell: gridCell);
-  @override
-  Future<SelectOptionCellData?> loadData() async {
-    return service.getOpitonContext().then((result) {
-      return result.fold(
-        (data) => data,
-        (err) {
-          Log.error(err);
-          return null;
-        },
-      );
-    });
-  }
-
-  @override
-  IGridCellDataConfig get config => const GridCellDataConfig(reloadOnFieldChanged: true);
-}
-
 class StringCellDataParser implements ICellDataParser<String> {
   @override
   String? parserData(List<int> data) {

+ 15 - 18
frontend/app_flowy/lib/workspace/application/grid/cell/cell_service/cell_data_persistence.dart

@@ -1,25 +1,22 @@
 part of 'cell_service.dart';
 
-abstract class _GridCellDataPersistence<D> {
+/// Save the cell data to disk
+/// You can extend this class to do custom operations. For example, the DateCellDataPersistence.
+abstract class IGridCellDataPersistence<D> {
   Future<Option<FlowyError>> save(D data);
 }
 
-class CellDataPersistence implements _GridCellDataPersistence<String> {
-  final GridCell gridCell;
+class CellDataPersistence implements IGridCellDataPersistence<String> {
+  final GridCellIdentifier cellId;
 
   CellDataPersistence({
-    required this.gridCell,
+    required this.cellId,
   });
   final CellService _cellService = CellService();
 
   @override
   Future<Option<FlowyError>> save(String data) async {
-    final fut = _cellService.updateCell(
-      gridId: gridCell.gridId,
-      fieldId: gridCell.field.id,
-      rowId: gridCell.rowId,
-      data: data,
-    );
+    final fut = _cellService.updateCell(cellId: cellId, data: data);
 
     return fut.then((result) {
       return result.fold(
@@ -35,15 +32,15 @@ class CalendarData with _$CalendarData {
   const factory CalendarData({required DateTime date, String? time}) = _CalendarData;
 }
 
-class DateCellDataPersistence implements _GridCellDataPersistence<CalendarData> {
-  final GridCell gridCell;
+class DateCellDataPersistence implements IGridCellDataPersistence<CalendarData> {
+  final GridCellIdentifier cellId;
   DateCellDataPersistence({
-    required this.gridCell,
+    required this.cellId,
   });
 
   @override
   Future<Option<FlowyError>> save(CalendarData data) {
-    var payload = DateChangesetPayload.create()..cellIdentifier = _cellIdentifier(gridCell);
+    var payload = DateChangesetPayload.create()..cellIdentifier = _makeCellIdPayload(cellId);
 
     final date = (data.date.millisecondsSinceEpoch ~/ 1000).toString();
     payload.date = date;
@@ -61,9 +58,9 @@ class DateCellDataPersistence implements _GridCellDataPersistence<CalendarData>
   }
 }
 
-CellIdentifierPayload _cellIdentifier(GridCell gridCell) {
+CellIdentifierPayload _makeCellIdPayload(GridCellIdentifier cellId) {
   return CellIdentifierPayload.create()
-    ..gridId = gridCell.gridId
-    ..fieldId = gridCell.field.id
-    ..rowId = gridCell.rowId;
+    ..gridId = cellId.gridId
+    ..fieldId = cellId.fieldId
+    ..rowId = cellId.rowId;
 }

+ 60 - 0
frontend/app_flowy/lib/workspace/application/grid/cell/cell_service/cell_field_notifier.dart

@@ -0,0 +1,60 @@
+import 'package:flowy_sdk/protobuf/flowy-grid/field_entities.pb.dart';
+import 'package:flutter/foundation.dart';
+
+import 'cell_service.dart';
+
+abstract class GridFieldChangedNotifier {
+  void onFieldChanged(void Function(Field) callback);
+  void dispose();
+}
+
+/// Grid's cell helper wrapper that enables each cell will get notified when the corresponding field was changed.
+/// You Register an onFieldChanged callback to listen to the cell changes, and unregister if you don't want to listen.
+class GridCellFieldNotifier {
+  /// fieldId: {objectId: callback}
+  final Map<String, Map<String, List<VoidCallback>>> _fieldListenerByFieldId = {};
+
+  GridCellFieldNotifier({required GridFieldChangedNotifier notifier}) {
+    notifier.onFieldChanged(
+      (field) {
+        final map = _fieldListenerByFieldId[field.id];
+        if (map != null) {
+          for (final callbacks in map.values) {
+            for (final callback in callbacks) {
+              callback();
+            }
+          }
+        }
+      },
+    );
+  }
+
+  ///
+  void register(GridCellCacheKey cacheKey, VoidCallback onFieldChanged) {
+    var map = _fieldListenerByFieldId[cacheKey.fieldId];
+    if (map == null) {
+      _fieldListenerByFieldId[cacheKey.fieldId] = {};
+      map = _fieldListenerByFieldId[cacheKey.fieldId];
+      map![cacheKey.rowId] = [onFieldChanged];
+    } else {
+      var objects = map[cacheKey.rowId];
+      if (objects == null) {
+        map[cacheKey.rowId] = [onFieldChanged];
+      } else {
+        objects.add(onFieldChanged);
+      }
+    }
+  }
+
+  void unregister(GridCellCacheKey cacheKey, VoidCallback fn) {
+    var callbacks = _fieldListenerByFieldId[cacheKey.fieldId]?[cacheKey.rowId];
+    final index = callbacks?.indexWhere((callback) => callback == fn);
+    if (index != null && index != -1) {
+      callbacks?.removeAt(index);
+    }
+  }
+
+  Future<void> dispose() async {
+    _fieldListenerByFieldId.clear();
+  }
+}

+ 25 - 20
frontend/app_flowy/lib/workspace/application/grid/cell/cell_service/cell_service.dart

@@ -1,6 +1,7 @@
 import 'dart:async';
 import 'dart:collection';
 
+import 'package:app_flowy/workspace/application/grid/grid_service.dart';
 import 'package:dartz/dartz.dart';
 import 'package:equatable/equatable.dart';
 import 'package:flowy_sdk/dispatch/dispatch.dart';
@@ -14,13 +15,15 @@ import 'package:flowy_sdk/protobuf/flowy-grid/url_type_option_entities.pb.dart';
 import 'package:flutter/foundation.dart';
 import 'package:freezed_annotation/freezed_annotation.dart';
 import 'package:app_flowy/workspace/application/grid/cell/cell_listener.dart';
-import 'package:app_flowy/workspace/application/grid/cell/select_option_service.dart';
 import 'package:app_flowy/workspace/application/grid/field/field_service.dart';
 import 'dart:convert' show utf8;
+
+import '../../field/type_option/type_option_service.dart';
+import 'cell_field_notifier.dart';
 part 'cell_service.freezed.dart';
 part 'cell_data_loader.dart';
 part 'context_builder.dart';
-part 'cache.dart';
+part 'cell_cache.dart';
 part 'cell_data_persistence.dart';
 
 // key: rowId
@@ -29,44 +32,46 @@ class CellService {
   CellService();
 
   Future<Either<void, FlowyError>> updateCell({
-    required String gridId,
-    required String fieldId,
-    required String rowId,
+    required GridCellIdentifier cellId,
     required String data,
   }) {
     final payload = CellChangeset.create()
-      ..gridId = gridId
-      ..fieldId = fieldId
-      ..rowId = rowId
+      ..gridId = cellId.gridId
+      ..fieldId = cellId.fieldId
+      ..rowId = cellId.rowId
       ..content = data;
     return GridEventUpdateCell(payload).send();
   }
 
   Future<Either<Cell, FlowyError>> getCell({
-    required String gridId,
-    required String fieldId,
-    required String rowId,
+    required GridCellIdentifier cellId,
   }) {
     final payload = CellIdentifierPayload.create()
-      ..gridId = gridId
-      ..fieldId = fieldId
-      ..rowId = rowId;
+      ..gridId = cellId.gridId
+      ..fieldId = cellId.fieldId
+      ..rowId = cellId.rowId;
     return GridEventGetCell(payload).send();
   }
 }
 
+/// Id of the cell
+/// We can locate the cell by using gridId + rowId + field.id.
 @freezed
-class GridCell with _$GridCell {
-  const factory GridCell({
+class GridCellIdentifier with _$GridCellIdentifier {
+  const factory GridCellIdentifier({
     required String gridId,
     required String rowId,
     required Field field,
-  }) = _GridCell;
+  }) = _GridCellIdentifier;
 
   // ignore: unused_element
-  const GridCell._();
+  const GridCellIdentifier._();
+
+  String get fieldId => field.id;
+
+  FieldType get fieldType => field.fieldType;
 
-  String cellId() {
-    return rowId + field.id + "${field.fieldType}";
+  ValueKey key() {
+    return ValueKey(rowId + fieldId + "${field.fieldType}");
   }
 }

+ 180 - 98
frontend/app_flowy/lib/workspace/application/grid/cell/cell_service/context_builder.dart

@@ -1,154 +1,185 @@
 part of 'cell_service.dart';
 
-typedef GridCellContext = _GridCellContext<String, String>;
-typedef GridSelectOptionCellContext = _GridCellContext<SelectOptionCellData, String>;
-typedef GridDateCellContext = _GridCellContext<DateCellData, CalendarData>;
-typedef GridURLCellContext = _GridCellContext<URLCellData, String>;
-
-class GridCellContextBuilder {
-  final GridCellCacheService _cellCache;
-  final GridCell _gridCell;
-  GridCellContextBuilder({
-    required GridCellCacheService cellCache,
-    required GridCell gridCell,
+typedef GridCellController = IGridCellController<String, String>;
+typedef GridSelectOptionCellController = IGridCellController<SelectOptionCellData, String>;
+typedef GridDateCellController = IGridCellController<DateCellData, CalendarData>;
+typedef GridURLCellController = IGridCellController<URLCellData, String>;
+
+class GridCellControllerBuilder {
+  final GridCellIdentifier _cellId;
+  final GridCellCache _cellCache;
+  final GridFieldCache _fieldCache;
+
+  GridCellControllerBuilder({
+    required GridCellIdentifier cellId,
+    required GridCellCache cellCache,
+    required GridFieldCache fieldCache,
   })  : _cellCache = cellCache,
-        _gridCell = gridCell;
+        _fieldCache = fieldCache,
+        _cellId = cellId;
+
+  IGridCellController build() {
+    final cellFieldNotifier = GridCellFieldNotifier(notifier: _GridFieldChangedNotifierImpl(_fieldCache));
 
-  _GridCellContext build() {
-    switch (_gridCell.field.fieldType) {
+    switch (_cellId.fieldType) {
       case FieldType.Checkbox:
         final cellDataLoader = GridCellDataLoader(
-          gridCell: _gridCell,
+          cellId: _cellId,
           parser: StringCellDataParser(),
         );
-        return GridCellContext(
-          gridCell: _gridCell,
+        return GridCellController(
+          cellId: _cellId,
           cellCache: _cellCache,
           cellDataLoader: cellDataLoader,
-          cellDataPersistence: CellDataPersistence(gridCell: _gridCell),
+          fieldNotifier: cellFieldNotifier,
+          cellDataPersistence: CellDataPersistence(cellId: _cellId),
         );
       case FieldType.DateTime:
         final cellDataLoader = GridCellDataLoader(
-          gridCell: _gridCell,
+          cellId: _cellId,
           parser: DateCellDataParser(),
-          config: const GridCellDataConfig(reloadOnFieldChanged: true),
+          reloadOnFieldChanged: true,
         );
 
-        return GridDateCellContext(
-          gridCell: _gridCell,
+        return GridDateCellController(
+          cellId: _cellId,
           cellCache: _cellCache,
           cellDataLoader: cellDataLoader,
-          cellDataPersistence: DateCellDataPersistence(gridCell: _gridCell),
+          fieldNotifier: cellFieldNotifier,
+          cellDataPersistence: DateCellDataPersistence(cellId: _cellId),
         );
       case FieldType.Number:
         final cellDataLoader = GridCellDataLoader(
-          gridCell: _gridCell,
+          cellId: _cellId,
           parser: StringCellDataParser(),
-          config: const GridCellDataConfig(reloadOnCellChanged: true, reloadOnFieldChanged: true),
         );
-        return GridCellContext(
-          gridCell: _gridCell,
+        return GridCellController(
+          cellId: _cellId,
           cellCache: _cellCache,
           cellDataLoader: cellDataLoader,
-          cellDataPersistence: CellDataPersistence(gridCell: _gridCell),
+          fieldNotifier: cellFieldNotifier,
+          cellDataPersistence: CellDataPersistence(cellId: _cellId),
         );
       case FieldType.RichText:
         final cellDataLoader = GridCellDataLoader(
-          gridCell: _gridCell,
+          cellId: _cellId,
           parser: StringCellDataParser(),
         );
-        return GridCellContext(
-          gridCell: _gridCell,
+        return GridCellController(
+          cellId: _cellId,
           cellCache: _cellCache,
           cellDataLoader: cellDataLoader,
-          cellDataPersistence: CellDataPersistence(gridCell: _gridCell),
+          fieldNotifier: cellFieldNotifier,
+          cellDataPersistence: CellDataPersistence(cellId: _cellId),
         );
       case FieldType.MultiSelect:
       case FieldType.SingleSelect:
         final cellDataLoader = GridCellDataLoader(
-          gridCell: _gridCell,
+          cellId: _cellId,
           parser: SelectOptionCellDataParser(),
-          config: const GridCellDataConfig(reloadOnFieldChanged: true),
+          reloadOnFieldChanged: true,
         );
 
-        return GridSelectOptionCellContext(
-          gridCell: _gridCell,
+        return GridSelectOptionCellController(
+          cellId: _cellId,
           cellCache: _cellCache,
           cellDataLoader: cellDataLoader,
-          cellDataPersistence: CellDataPersistence(gridCell: _gridCell),
+          fieldNotifier: cellFieldNotifier,
+          cellDataPersistence: CellDataPersistence(cellId: _cellId),
         );
 
       case FieldType.URL:
         final cellDataLoader = GridCellDataLoader(
-          gridCell: _gridCell,
+          cellId: _cellId,
           parser: URLCellDataParser(),
         );
-        return GridURLCellContext(
-          gridCell: _gridCell,
+        return GridURLCellController(
+          cellId: _cellId,
           cellCache: _cellCache,
           cellDataLoader: cellDataLoader,
-          cellDataPersistence: CellDataPersistence(gridCell: _gridCell),
+          fieldNotifier: cellFieldNotifier,
+          cellDataPersistence: CellDataPersistence(cellId: _cellId),
         );
     }
     throw UnimplementedError;
   }
 }
 
-// T: the type of the CellData
-// D: the type of the data that will be save to disk
+/// IGridCellController is used to manipulate the cell and receive notifications.
+/// * Read/Write cell data
+/// * Listen on field/cell notifications.
+///
+/// Generic T represents the type of the cell data.
+/// Generic D represents the type of data that will be saved to the disk
+///
 // ignore: must_be_immutable
-class _GridCellContext<T, D> extends Equatable {
-  final GridCell gridCell;
-  final GridCellCacheService cellCache;
-  final _GridCellCacheKey _cacheKey;
-  final IGridCellDataLoader<T> cellDataLoader;
-  final _GridCellDataPersistence<D> cellDataPersistence;
+class IGridCellController<T, D> extends Equatable {
+  final GridCellIdentifier cellId;
+  final GridCellCache _cellsCache;
+  final GridCellCacheKey _cacheKey;
   final FieldService _fieldService;
+  final GridCellFieldNotifier _fieldNotifier;
+  final GridCellDataLoader<T> _cellDataLoader;
+  final IGridCellDataPersistence<D> _cellDataPersistence;
 
   late final CellListener _cellListener;
-  late final ValueNotifier<T?>? _cellDataNotifier;
+  ValueNotifier<T?>? _cellDataNotifier;
+
   bool isListening = false;
   VoidCallback? _onFieldChangedFn;
   Timer? _loadDataOperation;
   Timer? _saveDataOperation;
+  bool _isDispose = false;
 
-  _GridCellContext({
-    required this.gridCell,
-    required this.cellCache,
-    required this.cellDataLoader,
-    required this.cellDataPersistence,
-  })  : _fieldService = FieldService(gridId: gridCell.gridId, fieldId: gridCell.field.id),
-        _cacheKey = _GridCellCacheKey(rowId: gridCell.rowId, fieldId: gridCell.field.id);
-
-  _GridCellContext<T, D> clone() {
-    return _GridCellContext(
-        gridCell: gridCell,
-        cellDataLoader: cellDataLoader,
-        cellCache: cellCache,
-        cellDataPersistence: cellDataPersistence);
-  }
+  IGridCellController({
+    required this.cellId,
+    required GridCellCache cellCache,
+    required GridCellFieldNotifier fieldNotifier,
+    required GridCellDataLoader<T> cellDataLoader,
+    required IGridCellDataPersistence<D> cellDataPersistence,
+  })  : _cellsCache = cellCache,
+        _cellDataLoader = cellDataLoader,
+        _cellDataPersistence = cellDataPersistence,
+        _fieldNotifier = fieldNotifier,
+        _fieldService = FieldService(gridId: cellId.gridId, fieldId: cellId.field.id),
+        _cacheKey = GridCellCacheKey(rowId: cellId.rowId, fieldId: cellId.field.id);
 
-  String get gridId => gridCell.gridId;
+  IGridCellController<T, D> clone() {
+    return IGridCellController(
+        cellId: cellId,
+        cellDataLoader: _cellDataLoader,
+        cellCache: _cellsCache,
+        fieldNotifier: _fieldNotifier,
+        cellDataPersistence: _cellDataPersistence);
+  }
 
-  String get rowId => gridCell.rowId;
+  String get gridId => cellId.gridId;
 
-  String get cellId => gridCell.rowId + gridCell.field.id;
+  String get rowId => cellId.rowId;
 
-  String get fieldId => gridCell.field.id;
+  String get fieldId => cellId.field.id;
 
-  Field get field => gridCell.field;
+  Field get field => cellId.field;
 
-  FieldType get fieldType => gridCell.field.fieldType;
+  FieldType get fieldType => cellId.field.fieldType;
 
-  VoidCallback? startListening({required void Function(T?) onCellChanged}) {
+  VoidCallback? startListening({required void Function(T?) onCellChanged, VoidCallback? onCellFieldChanged}) {
     if (isListening) {
       Log.error("Already started. It seems like you should call clone first");
       return null;
     }
-
     isListening = true;
-    _cellDataNotifier = ValueNotifier(cellCache.get(_cacheKey));
-    _cellListener = CellListener(rowId: gridCell.rowId, fieldId: gridCell.field.id);
+
+    /// The cell data will be changed by two reasons:
+    /// 1. User edit the cell
+    /// 2. User edit the field
+    ///   For example: The number cell reload the cell data that carries the format
+    ///   user input: 12
+    ///   cell display: $12
+    _cellDataNotifier = ValueNotifier(_cellsCache.get(_cacheKey));
+    _cellListener = CellListener(rowId: cellId.rowId, fieldId: cellId.field.id);
+
+    /// 1.Listen on user edit event and load the new cell data if needed.
     _cellListener.start(onCellChanged: (result) {
       result.fold(
         (_) => _loadData(),
@@ -156,22 +187,24 @@ class _GridCellContext<T, D> extends Equatable {
       );
     });
 
-    if (cellDataLoader.config.reloadOnFieldChanged) {
-      _onFieldChangedFn = () {
-        _loadData();
-      };
-      cellCache.addFieldListener(_cacheKey, _onFieldChangedFn!);
-    }
-
-    onCellChangedFn() {
-      onCellChanged(_cellDataNotifier?.value);
+    /// 2.Listen on the field event and load the cell data if needed.
+    _onFieldChangedFn = () {
+      if (onCellFieldChanged != null) {
+        onCellFieldChanged();
+      }
 
-      if (cellDataLoader.config.reloadOnCellChanged) {
+      if (_cellDataLoader.reloadOnFieldChanged) {
         _loadData();
       }
-    }
+    };
+
+    _fieldNotifier.register(_cacheKey, _onFieldChangedFn!);
 
+    /// Notify the listener, the cell data was changed.
+    onCellChangedFn() => onCellChanged(_cellDataNotifier?.value);
     _cellDataNotifier?.addListener(onCellChangedFn);
+
+    // Return the function pointer that can be used when calling removeListener.
     return onCellChangedFn;
   }
 
@@ -179,29 +212,45 @@ class _GridCellContext<T, D> extends Equatable {
     _cellDataNotifier?.removeListener(fn);
   }
 
-  T? getCellData({bool loadIfNoCache = true}) {
-    final data = cellCache.get(_cacheKey);
-    if (data == null && loadIfNoCache) {
+  /// Return the cell data.
+  /// The cell data will be read from the Cache first, and load from disk if it does not exist.
+  /// You can set [loadIfNotExist] to false (default is true) to disable loading the cell data.
+  T? getCellData({bool loadIfNotExist = true}) {
+    final data = _cellsCache.get(_cacheKey);
+    if (data == null && loadIfNotExist) {
       _loadData();
     }
     return data;
   }
 
-  Future<Either<FieldTypeOptionData, FlowyError>> getTypeOptionData() {
-    return _fieldService.getFieldTypeOptionData(fieldType: fieldType);
+  /// Return the FieldTypeOptionData that can be parsed into corresponding class using the [parser].
+  /// [PD] is the type that the parser return.
+  Future<Either<PD, FlowyError>> getFieldTypeOption<PD, P extends TypeOptionDataParser>(P parser) {
+    return _fieldService.getFieldTypeOptionData(fieldType: fieldType).then((result) {
+      return result.fold(
+        (data) => parser.fromBuffer(data.typeOptionData),
+        (err) => right(err),
+      );
+    });
   }
 
+  /// Save the cell data to disk
+  /// You can set [dedeplicate] to true (default is false) to reduce the save operation.
+  /// It's useful when you call this method when user editing the [TextField].
+  /// The default debounce interval is 300 milliseconds.
   void saveCellData(D data, {bool deduplicate = false, void Function(Option<FlowyError>)? resultCallback}) async {
     if (deduplicate) {
       _loadDataOperation?.cancel();
-      _loadDataOperation = Timer(const Duration(milliseconds: 300), () async {
-        final result = await cellDataPersistence.save(data);
+
+      _saveDataOperation?.cancel();
+      _saveDataOperation = Timer(const Duration(milliseconds: 300), () async {
+        final result = await _cellDataPersistence.save(data);
         if (resultCallback != null) {
           resultCallback(result);
         }
       });
     } else {
-      final result = await cellDataPersistence.save(data);
+      final result = await _cellDataPersistence.save(data);
       if (resultCallback != null) {
         resultCallback(result);
       }
@@ -209,26 +258,59 @@ class _GridCellContext<T, D> extends Equatable {
   }
 
   void _loadData() {
+    _saveDataOperation?.cancel();
+
     _loadDataOperation?.cancel();
     _loadDataOperation = Timer(const Duration(milliseconds: 10), () {
-      cellDataLoader.loadData().then((data) {
+      _cellDataLoader.loadData().then((data) {
         _cellDataNotifier?.value = data;
-        cellCache.insert(_GridCellCacheObject(key: _cacheKey, object: data));
+        _cellsCache.insert(_cacheKey, GridCell(object: data));
       });
     });
   }
 
   void dispose() {
+    if (_isDispose) {
+      Log.error("$this should only dispose once");
+      return;
+    }
+    _isDispose = true;
     _cellListener.stop();
     _loadDataOperation?.cancel();
     _saveDataOperation?.cancel();
+    _cellDataNotifier = null;
 
     if (_onFieldChangedFn != null) {
-      cellCache.removeFieldListener(_cacheKey, _onFieldChangedFn!);
+      _fieldNotifier.unregister(_cacheKey, _onFieldChangedFn!);
       _onFieldChangedFn = null;
     }
   }
 
   @override
-  List<Object> get props => [cellCache.get(_cacheKey) ?? "", cellId];
+  List<Object> get props => [_cellsCache.get(_cacheKey) ?? "", cellId.rowId + cellId.field.id];
+}
+
+class _GridFieldChangedNotifierImpl extends GridFieldChangedNotifier {
+  final GridFieldCache _cache;
+  FieldChangesetCallback? _onChangesetFn;
+
+  _GridFieldChangedNotifierImpl(GridFieldCache cache) : _cache = cache;
+
+  @override
+  void dispose() {
+    if (_onChangesetFn != null) {
+      _cache.removeListener(onChangsetListener: _onChangesetFn!);
+      _onChangesetFn = null;
+    }
+  }
+
+  @override
+  void onFieldChanged(void Function(Field p1) callback) {
+    _onChangesetFn = (GridFieldChangeset changeset) {
+      for (final updatedField in changeset.updatedFields) {
+        callback(updatedField);
+      }
+    };
+    _cache.addListener(onChangeset: _onChangesetFn);
+  }
 }

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

@@ -6,7 +6,7 @@ import 'cell_service/cell_service.dart';
 part 'checkbox_cell_bloc.freezed.dart';
 
 class CheckboxCellBloc extends Bloc<CheckboxCellEvent, CheckboxCellState> {
-  final GridCellContext cellContext;
+  final GridCellController cellContext;
   void Function()? _onCellChangedFn;
 
   CheckboxCellBloc({
@@ -67,7 +67,7 @@ class CheckboxCellState with _$CheckboxCellState {
     required bool isSelected,
   }) = _CheckboxCellState;
 
-  factory CheckboxCellState.initial(GridCellContext context) {
+  factory CheckboxCellState.initial(GridCellController context) {
     return CheckboxCellState(isSelected: _isSelected(context.getCellData()));
   }
 }

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

@@ -17,7 +17,7 @@ import 'package:fixnum/fixnum.dart' as $fixnum;
 part 'date_cal_bloc.freezed.dart';
 
 class DateCalBloc extends Bloc<DateCalEvent, DateCalState> {
-  final GridDateCellContext cellContext;
+  final GridDateCellController cellContext;
   void Function()? _onCellChangedFn;
 
   DateCalBloc({

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

@@ -7,7 +7,7 @@ import 'cell_service/cell_service.dart';
 part 'date_cell_bloc.freezed.dart';
 
 class DateCellBloc extends Bloc<DateCellEvent, DateCellState> {
-  final GridDateCellContext cellContext;
+  final GridDateCellController cellContext;
   void Function()? _onCellChangedFn;
 
   DateCellBloc({required this.cellContext}) : super(DateCellState.initial(cellContext)) {
@@ -60,7 +60,7 @@ class DateCellState with _$DateCellState {
     required Field field,
   }) = _DateCellState;
 
-  factory DateCellState.initial(GridDateCellContext context) {
+  factory DateCellState.initial(GridDateCellController context) {
     final cellData = context.getCellData();
 
     return DateCellState(

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

@@ -8,7 +8,7 @@ import 'cell_service/cell_service.dart';
 part 'number_cell_bloc.freezed.dart';
 
 class NumberCellBloc extends Bloc<NumberCellEvent, NumberCellState> {
-  final GridCellContext cellContext;
+  final GridCellController cellContext;
   void Function()? _onCellChangedFn;
 
   NumberCellBloc({
@@ -72,7 +72,7 @@ class NumberCellState with _$NumberCellState {
     required Either<String, FlowyError> content,
   }) = _NumberCellState;
 
-  factory NumberCellState.initial(GridCellContext context) {
+  factory NumberCellState.initial(GridCellController context) {
     final cellContent = context.getCellData() ?? "";
     return NumberCellState(
       content: left(cellContent),

+ 2 - 2
frontend/app_flowy/lib/workspace/application/grid/cell/select_option_cell_bloc.dart

@@ -7,7 +7,7 @@ import 'package:app_flowy/workspace/application/grid/cell/cell_service/cell_serv
 part 'select_option_cell_bloc.freezed.dart';
 
 class SelectOptionCellBloc extends Bloc<SelectOptionCellEvent, SelectOptionCellState> {
-  final GridSelectOptionCellContext cellContext;
+  final GridSelectOptionCellController cellContext;
   void Function()? _onCellChangedFn;
 
   SelectOptionCellBloc({
@@ -66,7 +66,7 @@ class SelectOptionCellState with _$SelectOptionCellState {
     required List<SelectOption> selectedOptions,
   }) = _SelectOptionCellState;
 
-  factory SelectOptionCellState.initial(GridSelectOptionCellContext context) {
+  factory SelectOptionCellState.initial(GridSelectOptionCellController context) {
     final data = context.getCellData();
 
     return SelectOptionCellState(

+ 11 - 28
frontend/app_flowy/lib/workspace/application/grid/cell/select_option_editor_bloc.dart

@@ -1,5 +1,4 @@
 import 'dart:async';
-import 'package:app_flowy/workspace/application/grid/field/grid_listenr.dart';
 import 'package:dartz/dartz.dart';
 import 'package:flowy_sdk/log.dart';
 import 'package:flowy_sdk/protobuf/flowy-grid/select_option.pb.dart';
@@ -13,16 +12,13 @@ part 'select_option_editor_bloc.freezed.dart';
 
 class SelectOptionCellEditorBloc extends Bloc<SelectOptionEditorEvent, SelectOptionEditorState> {
   final SelectOptionService _selectOptionService;
-  final GridSelectOptionCellContext cellContext;
-  late final GridFieldsListener _fieldListener;
-  void Function()? _onCellChangedFn;
+  final GridSelectOptionCellController cellController;
   Timer? _delayOperation;
 
   SelectOptionCellEditorBloc({
-    required this.cellContext,
-  })  : _selectOptionService = SelectOptionService(gridCell: cellContext.gridCell),
-        _fieldListener = GridFieldsListener(gridId: cellContext.gridId),
-        super(SelectOptionEditorState.initial(cellContext)) {
+    required this.cellController,
+  })  : _selectOptionService = SelectOptionService(cellId: cellController.cellId),
+        super(SelectOptionEditorState.initial(cellController)) {
     on<SelectOptionEditorEvent>(
       (event, emit) async {
         await event.map(
@@ -64,13 +60,8 @@ class SelectOptionCellEditorBloc extends Bloc<SelectOptionEditorEvent, SelectOpt
 
   @override
   Future<void> close() async {
-    if (_onCellChangedFn != null) {
-      cellContext.removeListener(_onCellChangedFn!);
-      _onCellChangedFn = null;
-    }
     _delayOperation?.cancel();
-    await _fieldListener.stop();
-    cellContext.dispose();
+    cellController.dispose();
     return super.close();
   }
 
@@ -157,24 +148,16 @@ class SelectOptionCellEditorBloc extends Bloc<SelectOptionEditorEvent, SelectOpt
   }
 
   void _startListening() {
-    _onCellChangedFn = cellContext.startListening(
+    cellController.startListening(
       onCellChanged: ((selectOptionContext) {
         if (!isClosed) {
           _loadOptions();
         }
       }),
+      onCellFieldChanged: () {
+        _loadOptions();
+      },
     );
-
-    _fieldListener.start(onFieldsChanged: (result) {
-      result.fold(
-        (changeset) {
-          if (changeset.updatedFields.isNotEmpty) {
-            _loadOptions();
-          }
-        },
-        (err) => Log.error(err),
-      );
-    });
   }
 }
 
@@ -200,8 +183,8 @@ class SelectOptionEditorState with _$SelectOptionEditorState {
     required Option<String> filter,
   }) = _SelectOptionEditorState;
 
-  factory SelectOptionEditorState.initial(GridSelectOptionCellContext context) {
-    final data = context.getCellData(loadIfNoCache: false);
+  factory SelectOptionEditorState.initial(GridSelectOptionCellController context) {
+    final data = context.getCellData(loadIfNotExist: false);
     return SelectOptionEditorState(
       options: data?.options ?? [],
       allOptions: data?.options ?? [],

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

@@ -7,12 +7,12 @@ import 'package:flowy_sdk/protobuf/flowy-grid/select_option.pb.dart';
 import 'cell_service/cell_service.dart';
 
 class SelectOptionService {
-  final GridCell gridCell;
-  SelectOptionService({required this.gridCell});
+  final GridCellIdentifier cellId;
+  SelectOptionService({required this.cellId});
 
-  String get gridId => gridCell.gridId;
-  String get fieldId => gridCell.field.id;
-  String get rowId => gridCell.rowId;
+  String get gridId => cellId.gridId;
+  String get fieldId => cellId.field.id;
+  String get rowId => cellId.rowId;
 
   Future<Either<Unit, FlowyError>> create({required String name}) {
     return TypeOptionService(gridId: gridId, fieldId: fieldId).newOption(name: name).then(

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

@@ -6,7 +6,7 @@ import 'cell_service/cell_service.dart';
 part 'text_cell_bloc.freezed.dart';
 
 class TextCellBloc extends Bloc<TextCellEvent, TextCellState> {
-  final GridCellContext cellContext;
+  final GridCellController cellContext;
   void Function()? _onCellChangedFn;
   TextCellBloc({
     required this.cellContext,
@@ -63,7 +63,7 @@ class TextCellState with _$TextCellState {
     required String content,
   }) = _TextCellState;
 
-  factory TextCellState.initial(GridCellContext context) => TextCellState(
+  factory TextCellState.initial(GridCellController context) => TextCellState(
         content: context.getCellData() ?? "",
       );
 }

+ 2 - 2
frontend/app_flowy/lib/workspace/application/grid/cell/url_cell_bloc.dart

@@ -7,7 +7,7 @@ import 'cell_service/cell_service.dart';
 part 'url_cell_bloc.freezed.dart';
 
 class URLCellBloc extends Bloc<URLCellEvent, URLCellState> {
-  final GridURLCellContext cellContext;
+  final GridURLCellController cellContext;
   void Function()? _onCellChangedFn;
   URLCellBloc({
     required this.cellContext,
@@ -67,7 +67,7 @@ class URLCellState with _$URLCellState {
     required String url,
   }) = _URLCellState;
 
-  factory URLCellState.initial(GridURLCellContext context) {
+  factory URLCellState.initial(GridURLCellController context) {
     final cellData = context.getCellData();
     return URLCellState(
       content: cellData?.content ?? "",

+ 2 - 2
frontend/app_flowy/lib/workspace/application/grid/cell/url_cell_editor_bloc.dart

@@ -7,7 +7,7 @@ import 'cell_service/cell_service.dart';
 part 'url_cell_editor_bloc.freezed.dart';
 
 class URLCellEditorBloc extends Bloc<URLCellEditorEvent, URLCellEditorState> {
-  final GridURLCellContext cellContext;
+  final GridURLCellController cellContext;
   void Function()? _onCellChangedFn;
   URLCellEditorBloc({
     required this.cellContext,
@@ -64,7 +64,7 @@ class URLCellEditorState with _$URLCellEditorState {
     required String content,
   }) = _URLCellEditorState;
 
-  factory URLCellEditorState.initial(GridURLCellContext context) {
+  factory URLCellEditorState.initial(GridURLCellController context) {
     final cellData = context.getCellData();
     return URLCellEditorState(
       content: cellData?.content ?? "",

+ 23 - 12
frontend/app_flowy/lib/workspace/application/grid/field/field_editor_bloc.dart

@@ -1,3 +1,4 @@
+import 'package:flowy_sdk/protobuf/flowy-grid/field_entities.pb.dart';
 import 'package:flutter_bloc/flutter_bloc.dart';
 import 'package:freezed_annotation/freezed_annotation.dart';
 import 'dart:async';
@@ -6,27 +7,32 @@ import 'package:dartz/dartz.dart';
 part 'field_editor_bloc.freezed.dart';
 
 class FieldEditorBloc extends Bloc<FieldEditorEvent, FieldEditorState> {
+  final TypeOptionDataController dataController;
+
   FieldEditorBloc({
     required String gridId,
     required String fieldName,
-    required IFieldContextLoader fieldContextLoader,
-  }) : super(FieldEditorState.initial(gridId, fieldName, fieldContextLoader)) {
+    required IFieldTypeOptionLoader loader,
+  })  : dataController = TypeOptionDataController(gridId: gridId, loader: loader),
+        super(FieldEditorState.initial(gridId, fieldName)) {
     on<FieldEditorEvent>(
       (event, emit) async {
         await event.when(
           initial: () async {
-            final fieldContext = GridFieldContext(gridId: gridId, loader: fieldContextLoader);
-            await fieldContext.loadData().then((result) {
-              result.fold(
-                (l) => emit(state.copyWith(fieldContext: Some(fieldContext), name: fieldContext.field.name)),
-                (r) => null,
-              );
+            dataController.addFieldListener((field) {
+              if (!isClosed) {
+                add(FieldEditorEvent.didReceiveFieldChanged(field));
+              }
             });
+            await dataController.loadData();
           },
           updateName: (name) {
-            state.fieldContext.fold(() => null, (fieldContext) => fieldContext.fieldName = name);
+            dataController.fieldName = name;
             emit(state.copyWith(name: name));
           },
+          didReceiveFieldChanged: (Field field) {
+            emit(state.copyWith(field: Some(field)));
+          },
         );
       },
     );
@@ -42,6 +48,7 @@ class FieldEditorBloc extends Bloc<FieldEditorEvent, FieldEditorState> {
 class FieldEditorEvent with _$FieldEditorEvent {
   const factory FieldEditorEvent.initial() = _InitialField;
   const factory FieldEditorEvent.updateName(String name) = _UpdateName;
+  const factory FieldEditorEvent.didReceiveFieldChanged(Field field) = _DidReceiveFieldChanged;
 }
 
 @freezed
@@ -50,13 +57,17 @@ class FieldEditorState with _$FieldEditorState {
     required String gridId,
     required String errorText,
     required String name,
-    required Option<GridFieldContext> fieldContext,
+    required Option<Field> field,
   }) = _FieldEditorState;
 
-  factory FieldEditorState.initial(String gridId, String fieldName, IFieldContextLoader loader) => FieldEditorState(
+  factory FieldEditorState.initial(
+    String gridId,
+    String fieldName,
+  ) =>
+      FieldEditorState(
         gridId: gridId,
-        fieldContext: none(),
         errorText: '',
+        field: none(),
         name: fieldName,
       );
 }

+ 0 - 57
frontend/app_flowy/lib/workspace/application/grid/field/field_editor_pannel_bloc.dart

@@ -1,57 +0,0 @@
-import 'package:flowy_sdk/protobuf/flowy-grid/field_entities.pb.dart';
-import 'package:flutter_bloc/flutter_bloc.dart';
-import 'package:freezed_annotation/freezed_annotation.dart';
-import 'dart:async';
-
-import 'field_service.dart';
-
-part 'field_editor_pannel_bloc.freezed.dart';
-
-class FieldEditorPannelBloc extends Bloc<FieldEditorPannelEvent, FieldEditorPannelState> {
-  final GridFieldContext _fieldContext;
-  void Function()? _fieldListenFn;
-
-  FieldEditorPannelBloc(GridFieldContext fieldContext)
-      : _fieldContext = fieldContext,
-        super(FieldEditorPannelState.initial(fieldContext)) {
-    on<FieldEditorPannelEvent>(
-      (event, emit) async {
-        event.when(
-          initial: () {
-            _fieldListenFn = fieldContext.addFieldListener((field) {
-              add(FieldEditorPannelEvent.didReceiveFieldUpdated(field));
-            });
-          },
-          didReceiveFieldUpdated: (field) {
-            emit(state.copyWith(field: field));
-          },
-        );
-      },
-    );
-  }
-
-  @override
-  Future<void> close() async {
-    if (_fieldListenFn != null) {
-      _fieldContext.removeFieldListener(_fieldListenFn!);
-    }
-    return super.close();
-  }
-}
-
-@freezed
-class FieldEditorPannelEvent with _$FieldEditorPannelEvent {
-  const factory FieldEditorPannelEvent.initial() = _Initial;
-  const factory FieldEditorPannelEvent.didReceiveFieldUpdated(Field field) = _DidReceiveFieldUpdated;
-}
-
-@freezed
-class FieldEditorPannelState with _$FieldEditorPannelState {
-  const factory FieldEditorPannelState({
-    required Field field,
-  }) = _FieldEditorPannelState;
-
-  factory FieldEditorPannelState.initial(GridFieldContext fieldContext) => FieldEditorPannelState(
-        field: fieldContext.field,
-      );
-}

+ 19 - 22
frontend/app_flowy/lib/workspace/application/grid/field/field_service.dart

@@ -1,4 +1,5 @@
 import 'package:dartz/dartz.dart';
+import 'package:flowy_infra/notifier.dart';
 import 'package:flowy_sdk/dispatch/dispatch.dart';
 import 'package:flowy_sdk/log.dart';
 import 'package:flowy_sdk/protobuf/flowy-error/errors.pb.dart';
@@ -9,6 +10,10 @@ import 'package:freezed_annotation/freezed_annotation.dart';
 import 'package:protobuf/protobuf.dart';
 part 'field_service.freezed.dart';
 
+/// FieldService consists of lots of event functions. We define the events in the backend(Rust),
+/// you can find the corresponding event implementation in event_map.rs of the corresponding crate.
+///
+/// You could check out the rust-lib/flowy-grid/event_map.rs for more information.
 class FieldService {
   final String gridId;
   final String fieldId;
@@ -137,7 +142,7 @@ class GridFieldCellContext with _$GridFieldCellContext {
   }) = _GridFieldCellContext;
 }
 
-abstract class IFieldContextLoader {
+abstract class IFieldTypeOptionLoader {
   String get gridId;
   Future<Either<FieldTypeOptionData, FlowyError>> load();
 
@@ -151,10 +156,10 @@ abstract class IFieldContextLoader {
   }
 }
 
-class NewFieldContextLoader extends IFieldContextLoader {
+class NewFieldTypeOptionLoader extends IFieldTypeOptionLoader {
   @override
   final String gridId;
-  NewFieldContextLoader({
+  NewFieldTypeOptionLoader({
     required this.gridId,
   });
 
@@ -168,12 +173,12 @@ class NewFieldContextLoader extends IFieldContextLoader {
   }
 }
 
-class FieldContextLoader extends IFieldContextLoader {
+class FieldTypeOptionLoader extends IFieldTypeOptionLoader {
   @override
   final String gridId;
   final Field field;
 
-  FieldContextLoader({
+  FieldTypeOptionLoader({
     required this.gridId,
     required this.field,
   });
@@ -189,16 +194,16 @@ class FieldContextLoader extends IFieldContextLoader {
   }
 }
 
-class GridFieldContext {
+class TypeOptionDataController {
   final String gridId;
-  final IFieldContextLoader _loader;
+  final IFieldTypeOptionLoader _loader;
 
   late FieldTypeOptionData _data;
-  ValueNotifier<Field>? _fieldNotifier;
+  final PublishNotifier<Field> _fieldNotifier = PublishNotifier();
 
-  GridFieldContext({
+  TypeOptionDataController({
     required this.gridId,
-    required IFieldContextLoader loader,
+    required IFieldTypeOptionLoader loader,
   }) : _loader = loader;
 
   Future<Either<Unit, FlowyError>> loadData() async {
@@ -207,13 +212,7 @@ class GridFieldContext {
       (data) {
         data.freeze();
         _data = data;
-
-        if (_fieldNotifier == null) {
-          _fieldNotifier = ValueNotifier(data.field_2);
-        } else {
-          _fieldNotifier?.value = data.field_2;
-        }
-
+        _fieldNotifier.value = data.field_2;
         return left(unit);
       },
       (err) {
@@ -256,9 +255,7 @@ class GridFieldContext {
       }
     });
 
-    if (_data.field_2 != _fieldNotifier?.value) {
-      _fieldNotifier?.value = _data.field_2;
-    }
+    _fieldNotifier.value = _data.field_2;
 
     FieldService.insertField(
       gridId: gridId,
@@ -288,11 +285,11 @@ class GridFieldContext {
       callback(field);
     }
 
-    _fieldNotifier?.addListener(listener);
+    _fieldNotifier.addListener(listener);
     return listener;
   }
 
   void removeFieldListener(void Function() listener) {
-    _fieldNotifier?.removeListener(listener);
+    _fieldNotifier.removeListener(listener);
   }
 }

+ 57 - 0
frontend/app_flowy/lib/workspace/application/grid/field/field_type_option_edit_bloc.dart

@@ -0,0 +1,57 @@
+import 'package:flowy_sdk/protobuf/flowy-grid/field_entities.pb.dart';
+import 'package:flutter_bloc/flutter_bloc.dart';
+import 'package:freezed_annotation/freezed_annotation.dart';
+import 'dart:async';
+
+import 'field_service.dart';
+
+part 'field_type_option_edit_bloc.freezed.dart';
+
+class FieldTypeOptionEditBloc extends Bloc<FieldTypeOptionEditEvent, FieldTypeOptionEditState> {
+  final TypeOptionDataController _dataController;
+  void Function()? _fieldListenFn;
+
+  FieldTypeOptionEditBloc(TypeOptionDataController dataController)
+      : _dataController = dataController,
+        super(FieldTypeOptionEditState.initial(dataController)) {
+    on<FieldTypeOptionEditEvent>(
+      (event, emit) async {
+        event.when(
+          initial: () {
+            _fieldListenFn = dataController.addFieldListener((field) {
+              add(FieldTypeOptionEditEvent.didReceiveFieldUpdated(field));
+            });
+          },
+          didReceiveFieldUpdated: (field) {
+            emit(state.copyWith(field: field));
+          },
+        );
+      },
+    );
+  }
+
+  @override
+  Future<void> close() async {
+    if (_fieldListenFn != null) {
+      _dataController.removeFieldListener(_fieldListenFn!);
+    }
+    return super.close();
+  }
+}
+
+@freezed
+class FieldTypeOptionEditEvent with _$FieldTypeOptionEditEvent {
+  const factory FieldTypeOptionEditEvent.initial() = _Initial;
+  const factory FieldTypeOptionEditEvent.didReceiveFieldUpdated(Field field) = _DidReceiveFieldUpdated;
+}
+
+@freezed
+class FieldTypeOptionEditState with _$FieldTypeOptionEditState {
+  const factory FieldTypeOptionEditState({
+    required Field field,
+  }) = _FieldTypeOptionEditState;
+
+  factory FieldTypeOptionEditState.initial(TypeOptionDataController fieldContext) => FieldTypeOptionEditState(
+        field: fieldContext.field,
+      );
+}

+ 2 - 2
frontend/app_flowy/lib/workspace/application/grid/field/type_option/date_bloc.dart

@@ -7,9 +7,9 @@ import 'dart:async';
 import 'package:protobuf/protobuf.dart';
 part 'date_bloc.freezed.dart';
 
-typedef DateTypeOptionContext = TypeOptionContext<DateTypeOption>;
+typedef DateTypeOptionContext = TypeOptionWidgetContext<DateTypeOption>;
 
-class DateTypeOptionDataBuilder extends TypeOptionDataBuilder<DateTypeOption> {
+class DateTypeOptionDataParser extends TypeOptionDataParser<DateTypeOption> {
   @override
   DateTypeOption fromBuffer(List<int> buffer) {
     return DateTypeOption.fromBuffer(buffer);

+ 8 - 7
frontend/app_flowy/lib/workspace/application/grid/field/type_option/multi_select_type_option.dart

@@ -7,17 +7,18 @@ import 'package:protobuf/protobuf.dart';
 import 'select_option_type_option_bloc.dart';
 import 'type_option_service.dart';
 
-class MultiSelectTypeOptionContext extends TypeOptionContext<MultiSelectTypeOption> with SelectOptionTypeOptionAction {
+class MultiSelectTypeOptionContext extends TypeOptionWidgetContext<MultiSelectTypeOption>
+    with SelectOptionTypeOptionAction {
   final TypeOptionService service;
 
   MultiSelectTypeOptionContext({
-    required MultiSelectTypeOptionDataBuilder dataBuilder,
-    required GridFieldContext fieldContext,
+    required MultiSelectTypeOptionWidgetDataParser dataBuilder,
+    required TypeOptionDataController dataController,
   })  : service = TypeOptionService(
-          gridId: fieldContext.gridId,
-          fieldId: fieldContext.field.id,
+          gridId: dataController.gridId,
+          fieldId: dataController.field.id,
         ),
-        super(dataBuilder: dataBuilder, fieldContext: fieldContext);
+        super(dataParser: dataBuilder, dataController: dataController);
 
   @override
   List<SelectOption> Function(SelectOption) get deleteOption {
@@ -70,7 +71,7 @@ class MultiSelectTypeOptionContext extends TypeOptionContext<MultiSelectTypeOpti
   }
 }
 
-class MultiSelectTypeOptionDataBuilder extends TypeOptionDataBuilder<MultiSelectTypeOption> {
+class MultiSelectTypeOptionWidgetDataParser extends TypeOptionDataParser<MultiSelectTypeOption> {
   @override
   MultiSelectTypeOption fromBuffer(List<int> buffer) {
     return MultiSelectTypeOption.fromBuffer(buffer);

+ 2 - 2
frontend/app_flowy/lib/workspace/application/grid/field/type_option/number_bloc.dart

@@ -8,9 +8,9 @@ import 'package:protobuf/protobuf.dart';
 
 part 'number_bloc.freezed.dart';
 
-typedef NumberTypeOptionContext = TypeOptionContext<NumberTypeOption>;
+typedef NumberTypeOptionContext = TypeOptionWidgetContext<NumberTypeOption>;
 
-class NumberTypeOptionDataBuilder extends TypeOptionDataBuilder<NumberTypeOption> {
+class NumberTypeOptionWidgetDataParser extends TypeOptionDataParser<NumberTypeOption> {
   @override
   NumberTypeOption fromBuffer(List<int> buffer) {
     return NumberTypeOption.fromBuffer(buffer);

+ 5 - 5
frontend/app_flowy/lib/workspace/application/grid/field/type_option/single_select_type_option.dart

@@ -7,18 +7,18 @@ import 'package:protobuf/protobuf.dart';
 import 'select_option_type_option_bloc.dart';
 import 'type_option_service.dart';
 
-class SingleSelectTypeOptionContext extends TypeOptionContext<SingleSelectTypeOption>
+class SingleSelectTypeOptionContext extends TypeOptionWidgetContext<SingleSelectTypeOption>
     with SelectOptionTypeOptionAction {
   final TypeOptionService service;
 
   SingleSelectTypeOptionContext({
-    required SingleSelectTypeOptionDataBuilder dataBuilder,
-    required GridFieldContext fieldContext,
+    required SingleSelectTypeOptionWidgetDataParser dataBuilder,
+    required TypeOptionDataController fieldContext,
   })  : service = TypeOptionService(
           gridId: fieldContext.gridId,
           fieldId: fieldContext.field.id,
         ),
-        super(dataBuilder: dataBuilder, fieldContext: fieldContext);
+        super(dataParser: dataBuilder, dataController: fieldContext);
 
   @override
   List<SelectOption> Function(SelectOption) get deleteOption {
@@ -71,7 +71,7 @@ class SingleSelectTypeOptionContext extends TypeOptionContext<SingleSelectTypeOp
   }
 }
 
-class SingleSelectTypeOptionDataBuilder extends TypeOptionDataBuilder<SingleSelectTypeOption> {
+class SingleSelectTypeOptionWidgetDataParser extends TypeOptionDataParser<SingleSelectTypeOption> {
   @override
   SingleSelectTypeOption fromBuffer(List<int> buffer) {
     return SingleSelectTypeOption.fromBuffer(buffer);

+ 13 - 13
frontend/app_flowy/lib/workspace/application/grid/field/type_option/type_option_service.dart

@@ -33,36 +33,36 @@ class TypeOptionService {
   }
 }
 
-abstract class TypeOptionDataBuilder<T> {
+abstract class TypeOptionDataParser<T> {
   T fromBuffer(List<int> buffer);
 }
 
-class TypeOptionContext<T extends GeneratedMessage> {
+class TypeOptionWidgetContext<T extends GeneratedMessage> {
   T? _typeOptionObject;
-  final GridFieldContext _fieldContext;
-  final TypeOptionDataBuilder<T> dataBuilder;
+  final TypeOptionDataController _dataController;
+  final TypeOptionDataParser<T> dataParser;
 
-  TypeOptionContext({
-    required this.dataBuilder,
-    required GridFieldContext fieldContext,
-  }) : _fieldContext = fieldContext;
+  TypeOptionWidgetContext({
+    required this.dataParser,
+    required TypeOptionDataController dataController,
+  }) : _dataController = dataController;
 
-  String get gridId => _fieldContext.gridId;
+  String get gridId => _dataController.gridId;
 
-  Field get field => _fieldContext.field;
+  Field get field => _dataController.field;
 
   T get typeOption {
     if (_typeOptionObject != null) {
       return _typeOptionObject!;
     }
 
-    final T object = dataBuilder.fromBuffer(_fieldContext.typeOptionData);
+    final T object = dataParser.fromBuffer(_dataController.typeOptionData);
     _typeOptionObject = object;
     return object;
   }
 
   set typeOption(T typeOption) {
-    _fieldContext.typeOptionData = typeOption.writeToBuffer();
+    _dataController.typeOptionData = typeOption.writeToBuffer();
     _typeOptionObject = typeOption;
   }
 }
@@ -77,7 +77,7 @@ class TypeOptionContext2<T> {
   final Field field;
   final FieldService _fieldService;
   T? _data;
-  final TypeOptionDataBuilder dataBuilder;
+  final TypeOptionDataParser dataBuilder;
 
   TypeOptionContext2({
     required this.gridId,

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

@@ -7,7 +7,7 @@ import 'package:flowy_sdk/protobuf/flowy-folder/view.pb.dart';
 import 'package:flowy_sdk/protobuf/flowy-grid/protobuf.dart';
 import 'package:flutter_bloc/flutter_bloc.dart';
 import 'package:freezed_annotation/freezed_annotation.dart';
-import 'block/block_service.dart';
+import 'block/block_cache.dart';
 import 'grid_service.dart';
 import 'row/row_service.dart';
 import 'dart:collection';
@@ -20,7 +20,7 @@ class GridBloc extends Bloc<GridEvent, GridState> {
   final GridFieldCache fieldCache;
 
   // key: the block id
-  final LinkedHashMap<String, GridBlockCacheService> _blocks;
+  final LinkedHashMap<String, GridBlockCache> _blocks;
 
   List<GridRow> get rows {
     final List<GridRow> rows = [];
@@ -68,8 +68,8 @@ class GridBloc extends Bloc<GridEvent, GridState> {
     return super.close();
   }
 
-  GridRowCacheService? getRowCache(String blockId, String rowId) {
-    final GridBlockCacheService? blockCache = _blocks[blockId];
+  GridRowCache? getRowCache(String blockId, String rowId) {
+    final GridBlockCache? blockCache = _blocks[blockId];
     return blockCache?.rowCache;
   }
 
@@ -119,7 +119,7 @@ class GridBloc extends Bloc<GridEvent, GridState> {
         return;
       }
 
-      final cache = GridBlockCacheService(
+      final cache = GridBlockCache(
         gridId: gridId,
         block: block,
         fieldCache: fieldCache,

+ 5 - 6
frontend/app_flowy/lib/workspace/application/grid/grid_service.dart

@@ -61,13 +61,12 @@ typedef FieldsCallback = void Function(List<Field>);
 
 class GridFieldCache {
   final String gridId;
-  late final GridFieldsListener _fieldListener;
+  final GridFieldsListener _fieldListener;
   FieldsNotifier? _fieldNotifier = FieldsNotifier();
   final Map<FieldsCallback, VoidCallback> _fieldsCallbackMap = {};
   final Map<FieldChangesetCallback, FieldChangesetCallback> _changesetCallbackMap = {};
 
-  GridFieldCache({required this.gridId}) {
-    _fieldListener = GridFieldsListener(gridId: gridId);
+  GridFieldCache({required this.gridId}) : _fieldListener = GridFieldsListener(gridId: gridId) {
     _fieldListener.start(onFieldsChanged: (result) {
       result.fold(
         (changeset) {
@@ -186,11 +185,11 @@ class GridFieldCache {
   }
 }
 
-class GridRowCacheDelegateImpl extends GridRowCacheDelegate {
+class GridRowCacheFieldNotifierImpl extends GridRowCacheFieldNotifier {
   final GridFieldCache _cache;
   FieldChangesetCallback? _onChangesetFn;
   FieldsCallback? _onFieldFn;
-  GridRowCacheDelegateImpl(GridFieldCache cache) : _cache = cache;
+  GridRowCacheFieldNotifierImpl(GridFieldCache cache) : _cache = cache;
 
   @override
   UnmodifiableListView<Field> get fields => _cache.unmodifiableFields;
@@ -202,7 +201,7 @@ class GridRowCacheDelegateImpl extends GridRowCacheDelegate {
   }
 
   @override
-  void onFieldUpdated(void Function(Field) callback) {
+  void onFieldChanged(void Function(Field) callback) {
     _onChangesetFn = (GridFieldChangeset changeset) {
       for (final updatedField in changeset.updatedFields) {
         callback(updatedField);

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

@@ -8,7 +8,7 @@ export 'grid_header_bloc.dart';
 export 'field/field_service.dart';
 export 'field/field_action_sheet_bloc.dart';
 export 'field/field_editor_bloc.dart';
-export 'field/field_editor_pannel_bloc.dart';
+export 'field/field_type_option_edit_bloc.dart';
 
 // Field Type Option
 export 'field/type_option/date_bloc.dart';

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

@@ -15,7 +15,7 @@ class RowActionSheetBloc extends Bloc<RowActionSheetEvent, RowActionSheetState>
       : _rowService = RowService(
           gridId: rowData.gridId,
           blockId: rowData.blockId,
-          rowId: rowData.rowId,
+          rowId: rowData.id,
         ),
         super(RowActionSheetState.initial(rowData)) {
     on<RowActionSheetEvent>(

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

@@ -11,19 +11,19 @@ part 'row_bloc.freezed.dart';
 
 class RowBloc extends Bloc<RowEvent, RowState> {
   final RowService _rowService;
-  final GridRowCacheService _rowCache;
+  final GridRowCache _rowCache;
   void Function()? _rowListenFn;
 
   RowBloc({
     required GridRow rowData,
-    required GridRowCacheService rowCache,
+    required GridRowCache rowCache,
   })  : _rowService = RowService(
           gridId: rowData.gridId,
           blockId: rowData.blockId,
-          rowId: rowData.rowId,
+          rowId: rowData.id,
         ),
         _rowCache = rowCache,
-        super(RowState.initial(rowData, rowCache.loadGridCells(rowData.rowId))) {
+        super(RowState.initial(rowData, rowCache.loadGridCells(rowData.id))) {
     on<RowEvent>(
       (event, emit) async {
         await event.map(
@@ -58,7 +58,7 @@ class RowBloc extends Bloc<RowEvent, RowState> {
 
   Future<void> _startListening() async {
     _rowListenFn = _rowCache.addListener(
-      rowId: state.rowData.rowId,
+      rowId: state.rowData.id,
       onCellUpdated: (cellDatas, reason) => add(RowEvent.didReceiveCellDatas(cellDatas, reason)),
       listenWhen: () => !isClosed,
     );

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

@@ -8,12 +8,12 @@ part 'row_detail_bloc.freezed.dart';
 
 class RowDetailBloc extends Bloc<RowDetailEvent, RowDetailState> {
   final GridRow rowData;
-  final GridRowCacheService _rowCache;
+  final GridRowCache _rowCache;
   void Function()? _rowListenFn;
 
   RowDetailBloc({
     required this.rowData,
-    required GridRowCacheService rowCache,
+    required GridRowCache rowCache,
   })  : _rowCache = rowCache,
         super(RowDetailState.initial()) {
     on<RowDetailEvent>(
@@ -41,14 +41,14 @@ class RowDetailBloc extends Bloc<RowDetailEvent, RowDetailState> {
 
   Future<void> _startListening() async {
     _rowListenFn = _rowCache.addListener(
-      rowId: rowData.rowId,
+      rowId: rowData.id,
       onCellUpdated: (cellDatas, reason) => add(RowDetailEvent.didReceiveCellDatas(cellDatas.values.toList())),
       listenWhen: () => !isClosed,
     );
   }
 
   Future<void> _loadCellData() async {
-    final cellDataMap = _rowCache.loadGridCells(rowData.rowId);
+    final cellDataMap = _rowCache.loadGridCells(rowData.id);
     if (!isClosed) {
       add(RowDetailEvent.didReceiveCellDatas(cellDataMap.values.toList()));
     }
@@ -58,13 +58,13 @@ class RowDetailBloc extends Bloc<RowDetailEvent, RowDetailState> {
 @freezed
 class RowDetailEvent with _$RowDetailEvent {
   const factory RowDetailEvent.initial() = _Initial;
-  const factory RowDetailEvent.didReceiveCellDatas(List<GridCell> gridCells) = _DidReceiveCellDatas;
+  const factory RowDetailEvent.didReceiveCellDatas(List<GridCellIdentifier> gridCells) = _DidReceiveCellDatas;
 }
 
 @freezed
 class RowDetailState with _$RowDetailState {
   const factory RowDetailState({
-    required List<GridCell> gridCells,
+    required List<GridCellIdentifier> gridCells,
   }) = _RowDetailState;
 
   factory RowDetailState.initial() => RowDetailState(

+ 60 - 49
frontend/app_flowy/lib/workspace/application/grid/row/row_service.dart

@@ -1,5 +1,4 @@
 import 'dart:collection';
-
 import 'package:app_flowy/workspace/application/grid/cell/cell_service/cell_service.dart';
 import 'package:dartz/dartz.dart';
 import 'package:flowy_sdk/dispatch/dispatch.dart';
@@ -15,40 +14,53 @@ part 'row_service.freezed.dart';
 
 typedef RowUpdateCallback = void Function();
 
-abstract class GridRowCacheDelegate with GridCellCacheDelegate {
+abstract class GridRowCacheFieldNotifier {
   UnmodifiableListView<Field> get fields;
-  void onFieldsChanged(void Function() callback);
+  void onFieldsChanged(VoidCallback callback);
+  void onFieldChanged(void Function(Field) callback);
   void dispose();
 }
 
-class GridRowCacheService {
+/// Cache the rows in memory
+/// Insert / delete / update row
+///
+/// Read https://appflowy.gitbook.io/docs/essential-documentation/contribute-to-appflowy/architecture/frontend/grid for more information.
+
+class GridRowCache {
   final String gridId;
   final GridBlock block;
-  final _Notifier _notifier;
+
+  /// _rows containers the current block's rows
+  /// Use List to reverse the order of the GridRow.
   List<GridRow> _rows = [];
+
+  /// Use Map for faster access the raw row data.
   final HashMap<String, Row> _rowByRowId;
-  final GridRowCacheDelegate _delegate;
-  final GridCellCacheService _cellCache;
 
-  List<GridRow> get rows => _rows;
-  GridCellCacheService get cellCache => _cellCache;
+  final GridCellCache _cellCache;
+  final GridRowCacheFieldNotifier _fieldNotifier;
+  final _GridRowChangesetNotifier _rowChangeReasonNotifier;
+
+  UnmodifiableListView<GridRow> get rows => UnmodifiableListView(_rows);
+  GridCellCache get cellCache => _cellCache;
 
-  GridRowCacheService({
+  GridRowCache({
     required this.gridId,
     required this.block,
-    required GridRowCacheDelegate delegate,
-  })  : _cellCache = GridCellCacheService(gridId: gridId, delegate: delegate),
+    required GridRowCacheFieldNotifier notifier,
+  })  : _cellCache = GridCellCache(gridId: gridId),
         _rowByRowId = HashMap(),
-        _notifier = _Notifier(),
-        _delegate = delegate {
+        _rowChangeReasonNotifier = _GridRowChangesetNotifier(),
+        _fieldNotifier = notifier {
     //
-    delegate.onFieldsChanged(() => _notifier.receive(const GridRowChangeReason.fieldDidChange()));
+    notifier.onFieldsChanged(() => _rowChangeReasonNotifier.receive(const GridRowChangeReason.fieldDidChange()));
+    notifier.onFieldChanged((field) => _cellCache.remove(field.id));
     _rows = block.rowInfos.map((rowInfo) => buildGridRow(rowInfo.rowId, rowInfo.height.toDouble())).toList();
   }
 
   Future<void> dispose() async {
-    _delegate.dispose();
-    _notifier.dispose();
+    _fieldNotifier.dispose();
+    _rowChangeReasonNotifier.dispose();
     await _cellCache.dispose();
   }
 
@@ -72,14 +84,15 @@ class GridRowCacheService {
     final Map<String, String> deletedRowByRowId = {for (var rowId in deletedRows) rowId: rowId};
 
     _rows.asMap().forEach((index, row) {
-      if (deletedRowByRowId[row.rowId] == null) {
+      if (deletedRowByRowId[row.id] == null) {
         newRows.add(row);
       } else {
+        _rowByRowId.remove(row.id);
         deletedIndex.add(DeletedIndex(index: index, row: row));
       }
     });
     _rows = newRows;
-    _notifier.receive(GridRowChangeReason.delete(deletedIndex));
+    _rowChangeReasonNotifier.receive(GridRowChangeReason.delete(deletedIndex));
   }
 
   void _insertRows(List<InsertedRow> insertRows) {
@@ -88,17 +101,16 @@ class GridRowCacheService {
     }
 
     InsertedIndexs insertIndexs = [];
-    final List<GridRow> newRows = _rows;
     for (final insertRow in insertRows) {
       final insertIndex = InsertedIndex(
         index: insertRow.index,
         rowId: insertRow.rowId,
       );
       insertIndexs.add(insertIndex);
-      newRows.insert(insertRow.index, (buildGridRow(insertRow.rowId, insertRow.height.toDouble())));
+      _rows.insert(insertRow.index, (buildGridRow(insertRow.rowId, insertRow.height.toDouble())));
     }
 
-    _notifier.receive(GridRowChangeReason.insert(insertIndexs));
+    _rowChangeReasonNotifier.receive(GridRowChangeReason.insert(insertIndexs));
   }
 
   void _updateRows(List<UpdatedRow> updatedRows) {
@@ -107,20 +119,19 @@ class GridRowCacheService {
     }
 
     final UpdatedIndexs updatedIndexs = UpdatedIndexs();
-    final List<GridRow> newRows = _rows;
     for (final updatedRow in updatedRows) {
       final rowId = updatedRow.rowId;
-      final index = newRows.indexWhere((row) => row.rowId == rowId);
+      final index = _rows.indexWhere((row) => row.id == rowId);
       if (index != -1) {
         _rowByRowId[rowId] = updatedRow.row;
 
-        newRows.removeAt(index);
-        newRows.insert(index, buildGridRow(rowId, updatedRow.row.height.toDouble()));
+        _rows.removeAt(index);
+        _rows.insert(index, buildGridRow(rowId, updatedRow.row.height.toDouble()));
         updatedIndexs[rowId] = UpdatedIndex(index: index, rowId: rowId);
       }
     }
 
-    _notifier.receive(GridRowChangeReason.update(updatedIndexs));
+    _rowChangeReasonNotifier.receive(GridRowChangeReason.update(updatedIndexs));
   }
 
   void _hideRows(List<String> hideRows) {}
@@ -130,8 +141,8 @@ class GridRowCacheService {
   void onRowsChanged(
     void Function(GridRowChangeReason) onRowChanged,
   ) {
-    _notifier.addListener(() {
-      onRowChanged(_notifier._reason);
+    _rowChangeReasonNotifier.addListener(() {
+      onRowChanged(_rowChangeReasonNotifier.reason);
     });
   }
 
@@ -150,12 +161,12 @@ class GridRowCacheService {
           final row = _rowByRowId[rowId];
           if (row != null) {
             final GridCellMap cellDataMap = _makeGridCells(rowId, row);
-            onCellUpdated(cellDataMap, _notifier._reason);
+            onCellUpdated(cellDataMap, _rowChangeReasonNotifier.reason);
           }
         }
       }
 
-      _notifier._reason.whenOrNull(
+      _rowChangeReasonNotifier.reason.whenOrNull(
         update: (indexs) {
           if (indexs[rowId] != null) notifyUpdate();
         },
@@ -163,12 +174,12 @@ class GridRowCacheService {
       );
     }
 
-    _notifier.addListener(listenrHandler);
+    _rowChangeReasonNotifier.addListener(listenrHandler);
     return listenrHandler;
   }
 
   void removeRowListener(VoidCallback callback) {
-    _notifier.removeListener(callback);
+    _rowChangeReasonNotifier.removeListener(callback);
   }
 
   GridCellMap loadGridCells(String rowId) {
@@ -194,9 +205,9 @@ class GridRowCacheService {
 
   GridCellMap _makeGridCells(String rowId, Row? row) {
     var cellDataMap = GridCellMap.new();
-    for (final field in _delegate.fields) {
+    for (final field in _fieldNotifier.fields) {
       if (field.visibility) {
-        cellDataMap[field.id] = GridCell(
+        cellDataMap[field.id] = GridCellIdentifier(
           rowId: rowId,
           gridId: gridId,
           field: field,
@@ -214,19 +225,19 @@ class GridRowCacheService {
     updatedRow.freeze();
 
     _rowByRowId[updatedRow.id] = updatedRow;
-    final index = _rows.indexWhere((gridRow) => gridRow.rowId == updatedRow.id);
+    final index = _rows.indexWhere((gridRow) => gridRow.id == updatedRow.id);
     if (index != -1) {
       // update the corresponding row in _rows if they are not the same
-      if (_rows[index].data != updatedRow) {
-        final row = _rows.removeAt(index).copyWith(data: updatedRow);
+      if (_rows[index].rawRow != updatedRow) {
+        final row = _rows.removeAt(index).copyWith(rawRow: updatedRow);
         _rows.insert(index, row);
 
         // Calculate the update index
         final UpdatedIndexs updatedIndexs = UpdatedIndexs();
-        updatedIndexs[row.rowId] = UpdatedIndex(index: index, rowId: row.rowId);
+        updatedIndexs[row.id] = UpdatedIndex(index: index, rowId: row.id);
 
         //
-        _notifier.receive(GridRowChangeReason.update(updatedIndexs));
+        _rowChangeReasonNotifier.receive(GridRowChangeReason.update(updatedIndexs));
       }
     }
   }
@@ -235,20 +246,20 @@ class GridRowCacheService {
     return GridRow(
       gridId: gridId,
       blockId: block.id,
-      fields: _delegate.fields,
-      rowId: rowId,
+      fields: _fieldNotifier.fields,
+      id: rowId,
       height: rowHeight,
     );
   }
 }
 
-class _Notifier extends ChangeNotifier {
-  GridRowChangeReason _reason = const InitialListState();
+class _GridRowChangesetNotifier extends ChangeNotifier {
+  GridRowChangeReason reason = const InitialListState();
 
-  _Notifier();
+  _GridRowChangesetNotifier();
 
-  void receive(GridRowChangeReason reason) {
-    _reason = reason;
+  void receive(GridRowChangeReason newReason) {
+    reason = newReason;
     reason.map(
       insert: (_) => notifyListeners(),
       delete: (_) => notifyListeners(),
@@ -318,10 +329,10 @@ class GridRow with _$GridRow {
   const factory GridRow({
     required String gridId,
     required String blockId,
-    required String rowId,
+    required String id,
     required UnmodifiableListView<Field> fields,
     required double height,
-    Row? data,
+    Row? rawRow,
   }) = _GridRow;
 }
 

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

@@ -227,14 +227,16 @@ class _GridRowsState extends State<_GridRows> {
     GridRow rowData,
     Animation<double> animation,
   ) {
-    final rowCache = context.read<GridBloc>().getRowCache(rowData.blockId, rowData.rowId);
+    final rowCache = context.read<GridBloc>().getRowCache(rowData.blockId, rowData.id);
+    final fieldCache = context.read<GridBloc>().fieldCache;
     if (rowCache != null) {
       return SizeTransition(
         sizeFactor: animation,
         child: GridRowWidget(
           rowData: rowData,
           rowCache: rowCache,
-          key: ValueKey(rowData.rowId),
+          fieldCache: fieldCache,
+          key: ValueKey(rowData.id),
         ),
       );
     } else {

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

@@ -1,4 +1,5 @@
 import 'package:app_flowy/workspace/application/grid/cell/cell_service/cell_service.dart';
+import 'package:app_flowy/workspace/application/grid/grid_service.dart';
 import 'package:flowy_sdk/protobuf/flowy-grid/field_entities.pb.dart';
 import 'package:flutter/services.dart';
 import 'package:flutter/widgets.dart';
@@ -12,28 +13,39 @@ import 'select_option_cell/select_option_cell.dart';
 import 'text_cell.dart';
 import 'url_cell/url_cell.dart';
 
-GridCellWidget buildGridCellWidget(GridCell gridCell, GridCellCacheService cellCache, {GridCellStyle? style}) {
-  final key = ValueKey(gridCell.cellId());
-
-  final cellContextBuilder = GridCellContextBuilder(gridCell: gridCell, cellCache: cellCache);
-
-  switch (gridCell.field.fieldType) {
-    case FieldType.Checkbox:
-      return CheckboxCell(cellContextBuilder: cellContextBuilder, key: key);
-    case FieldType.DateTime:
-      return DateCell(cellContextBuilder: cellContextBuilder, key: key, style: style);
-    case FieldType.SingleSelect:
-      return SingleSelectCell(cellContextBuilder: cellContextBuilder, style: style, key: key);
-    case FieldType.MultiSelect:
-      return MultiSelectCell(cellContextBuilder: cellContextBuilder, style: style, key: key);
-    case FieldType.Number:
-      return NumberCell(cellContextBuilder: cellContextBuilder, key: key);
-    case FieldType.RichText:
-      return GridTextCell(cellContextBuilder: cellContextBuilder, style: style, key: key);
-    case FieldType.URL:
-      return GridURLCell(cellContextBuilder: cellContextBuilder, style: style, key: key);
-  }
-  throw UnimplementedError;
+class GridCellBuilder {
+  final GridCellCache cellCache;
+  final GridFieldCache fieldCache;
+  GridCellBuilder({
+    required this.cellCache,
+    required this.fieldCache,
+  });
+
+  GridCellWidget build(GridCellIdentifier cell, {GridCellStyle? style}) {
+    final cellControllerBuilder = GridCellControllerBuilder(
+      cellId: cell,
+      cellCache: cellCache,
+      fieldCache: fieldCache,
+    );
+    final key = cell.key();
+    switch (cell.fieldType) {
+      case FieldType.Checkbox:
+        return CheckboxCell(cellControllerBuilder: cellControllerBuilder, key: key);
+      case FieldType.DateTime:
+        return DateCell(cellControllerBuilder: cellControllerBuilder, key: key, style: style);
+      case FieldType.SingleSelect:
+        return SingleSelectCell(cellContorllerBuilder: cellControllerBuilder, style: style, key: key);
+      case FieldType.MultiSelect:
+        return MultiSelectCell(cellContorllerBuilder: cellControllerBuilder, style: style, key: key);
+      case FieldType.Number:
+        return NumberCell(cellContorllerBuilder: cellControllerBuilder, key: key);
+      case FieldType.RichText:
+        return GridTextCell(cellContorllerBuilder: cellControllerBuilder, style: style, key: key);
+      case FieldType.URL:
+        return GridURLCell(cellContorllerBuilder: cellControllerBuilder, style: style, key: key);
+    }
+    throw UnimplementedError;
+  }
 }
 
 class BlankCell extends StatelessWidget {

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

@@ -7,9 +7,9 @@ import 'package:flutter_bloc/flutter_bloc.dart';
 import 'cell_builder.dart';
 
 class CheckboxCell extends GridCellWidget {
-  final GridCellContextBuilder cellContextBuilder;
+  final GridCellControllerBuilder cellControllerBuilder;
   CheckboxCell({
-    required this.cellContextBuilder,
+    required this.cellControllerBuilder,
     Key? key,
   }) : super(key: key);
 
@@ -22,7 +22,7 @@ class _CheckboxCellState extends GridCellState<CheckboxCell> {
 
   @override
   void initState() {
-    final cellContext = widget.cellContextBuilder.build();
+    final cellContext = widget.cellControllerBuilder.build();
     _cellBloc = getIt<CheckboxCellBloc>(param1: cellContext)..add(const CheckboxCellEvent.initial());
     super.initState();
   }

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

@@ -19,12 +19,12 @@ abstract class GridCellDelegate {
 }
 
 class DateCell extends GridCellWidget {
-  final GridCellContextBuilder cellContextBuilder;
+  final GridCellControllerBuilder cellControllerBuilder;
   late final DateCellStyle? cellStyle;
 
   DateCell({
     GridCellStyle? style,
-    required this.cellContextBuilder,
+    required this.cellControllerBuilder,
     Key? key,
   }) : super(key: key) {
     if (style != null) {
@@ -43,7 +43,7 @@ class _DateCellState extends GridCellState<DateCell> {
 
   @override
   void initState() {
-    final cellContext = widget.cellContextBuilder.build();
+    final cellContext = widget.cellControllerBuilder.build();
     _cellBloc = getIt<DateCellBloc>(param1: cellContext)..add(const DateCellEvent.initial());
     super.initState();
   }
@@ -80,7 +80,7 @@ class _DateCellState extends GridCellState<DateCell> {
     final calendar = DateCellEditor(onDismissed: () => widget.onCellEditing.value = false);
     calendar.show(
       context,
-      cellContext: bloc.cellContext.clone(),
+      cellController: bloc.cellContext.clone(),
     );
   }
 

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

@@ -31,16 +31,16 @@ class DateCellEditor with FlowyOverlayDelegate {
 
   Future<void> show(
     BuildContext context, {
-    required GridDateCellContext cellContext,
+    required GridDateCellController cellController,
   }) async {
     DateCellEditor.remove(context);
 
-    final result = await cellContext.getTypeOptionData();
+    final result = await cellController.getFieldTypeOption(DateTypeOptionDataParser());
     result.fold(
-      (data) {
+      (dateTypeOption) {
         final calendar = _CellCalendarWidget(
-          cellContext: cellContext,
-          dateTypeOption: DateTypeOption.fromBuffer(data.typeOptionData),
+          cellContext: cellController,
+          dateTypeOption: dateTypeOption,
         );
 
         FlowyOverlay.of(context).insertWithAnchor(
@@ -75,7 +75,7 @@ class DateCellEditor with FlowyOverlayDelegate {
 }
 
 class _CellCalendarWidget extends StatelessWidget {
-  final GridDateCellContext cellContext;
+  final GridDateCellController cellContext;
   final DateTypeOption dateTypeOption;
 
   const _CellCalendarWidget({

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

@@ -7,10 +7,10 @@ import 'package:flutter_bloc/flutter_bloc.dart';
 import 'cell_builder.dart';
 
 class NumberCell extends GridCellWidget {
-  final GridCellContextBuilder cellContextBuilder;
+  final GridCellControllerBuilder cellContorllerBuilder;
 
   NumberCell({
-    required this.cellContextBuilder,
+    required this.cellContorllerBuilder,
     Key? key,
   }) : super(key: key);
 
@@ -25,7 +25,7 @@ class _NumberCellState extends GridFocusNodeCellState<NumberCell> {
 
   @override
   void initState() {
-    final cellContext = widget.cellContextBuilder.build();
+    final cellContext = widget.cellContorllerBuilder.build();
     _cellBloc = getIt<NumberCellBloc>(param1: cellContext)..add(const NumberCellEvent.initial());
     _controller = TextEditingController(text: contentFromState(_cellBloc.state));
     super.initState();

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

@@ -21,11 +21,11 @@ class SelectOptionCellStyle extends GridCellStyle {
 }
 
 class SingleSelectCell extends GridCellWidget {
-  final GridCellContextBuilder cellContextBuilder;
+  final GridCellControllerBuilder cellContorllerBuilder;
   late final SelectOptionCellStyle? cellStyle;
 
   SingleSelectCell({
-    required this.cellContextBuilder,
+    required this.cellContorllerBuilder,
     GridCellStyle? style,
     Key? key,
   }) : super(key: key) {
@@ -45,7 +45,7 @@ class _SingleSelectCellState extends State<SingleSelectCell> {
 
   @override
   void initState() {
-    final cellContext = widget.cellContextBuilder.build() as GridSelectOptionCellContext;
+    final cellContext = widget.cellContorllerBuilder.build() as GridSelectOptionCellController;
     _cellBloc = getIt<SelectOptionCellBloc>(param1: cellContext)..add(const SelectOptionCellEvent.initial());
     super.initState();
   }
@@ -60,7 +60,7 @@ class _SingleSelectCellState extends State<SingleSelectCell> {
               selectOptions: state.selectedOptions,
               cellStyle: widget.cellStyle,
               onFocus: (value) => widget.onCellEditing.value = value,
-              cellContextBuilder: widget.cellContextBuilder);
+              cellContorllerBuilder: widget.cellContorllerBuilder);
         },
       ),
     );
@@ -75,11 +75,11 @@ class _SingleSelectCellState extends State<SingleSelectCell> {
 
 //----------------------------------------------------------------
 class MultiSelectCell extends GridCellWidget {
-  final GridCellContextBuilder cellContextBuilder;
+  final GridCellControllerBuilder cellContorllerBuilder;
   late final SelectOptionCellStyle? cellStyle;
 
   MultiSelectCell({
-    required this.cellContextBuilder,
+    required this.cellContorllerBuilder,
     GridCellStyle? style,
     Key? key,
   }) : super(key: key) {
@@ -99,7 +99,7 @@ class _MultiSelectCellState extends State<MultiSelectCell> {
 
   @override
   void initState() {
-    final cellContext = widget.cellContextBuilder.build() as GridSelectOptionCellContext;
+    final cellContext = widget.cellContorllerBuilder.build() as GridSelectOptionCellController;
     _cellBloc = getIt<SelectOptionCellBloc>(param1: cellContext)..add(const SelectOptionCellEvent.initial());
     super.initState();
   }
@@ -114,7 +114,7 @@ class _MultiSelectCellState extends State<MultiSelectCell> {
               selectOptions: state.selectedOptions,
               cellStyle: widget.cellStyle,
               onFocus: (value) => widget.onCellEditing.value = value,
-              cellContextBuilder: widget.cellContextBuilder);
+              cellContorllerBuilder: widget.cellContorllerBuilder);
         },
       ),
     );
@@ -131,12 +131,12 @@ class _SelectOptionCell extends StatelessWidget {
   final List<SelectOption> selectOptions;
   final void Function(bool) onFocus;
   final SelectOptionCellStyle? cellStyle;
-  final GridCellContextBuilder cellContextBuilder;
+  final GridCellControllerBuilder cellContorllerBuilder;
   const _SelectOptionCell({
     required this.selectOptions,
     required this.onFocus,
     required this.cellStyle,
-    required this.cellContextBuilder,
+    required this.cellContorllerBuilder,
     Key? key,
   }) : super(key: key);
 
@@ -172,7 +172,7 @@ class _SelectOptionCell extends StatelessWidget {
         InkWell(
           onTap: () {
             onFocus(true);
-            final cellContext = cellContextBuilder.build() as GridSelectOptionCellContext;
+            final cellContext = cellContorllerBuilder.build() as GridSelectOptionCellController;
             SelectOptionCellEditor.show(context, cellContext, () => onFocus(false));
           },
         ),

+ 5 - 5
frontend/app_flowy/lib/workspace/presentation/plugins/grid/src/widgets/cell/select_option_cell/select_option_editor.dart

@@ -24,11 +24,11 @@ import 'text_field.dart';
 const double _editorPannelWidth = 300;
 
 class SelectOptionCellEditor extends StatelessWidget with FlowyOverlayDelegate {
-  final GridSelectOptionCellContext cellContext;
+  final GridSelectOptionCellController cellController;
   final VoidCallback onDismissed;
 
   const SelectOptionCellEditor({
-    required this.cellContext,
+    required this.cellController,
     required this.onDismissed,
     Key? key,
   }) : super(key: key);
@@ -37,7 +37,7 @@ class SelectOptionCellEditor extends StatelessWidget with FlowyOverlayDelegate {
   Widget build(BuildContext context) {
     return BlocProvider(
       create: (context) => SelectOptionCellEditorBloc(
-        cellContext: cellContext,
+        cellController: cellController,
       )..add(const SelectOptionEditorEvent.initial()),
       child: BlocBuilder<SelectOptionCellEditorBloc, SelectOptionEditorState>(
         builder: (context, state) {
@@ -59,12 +59,12 @@ class SelectOptionCellEditor extends StatelessWidget with FlowyOverlayDelegate {
 
   static void show(
     BuildContext context,
-    GridSelectOptionCellContext cellContext,
+    GridSelectOptionCellController cellContext,
     VoidCallback onDismissed,
   ) {
     SelectOptionCellEditor.remove(context);
     final editor = SelectOptionCellEditor(
-      cellContext: cellContext,
+      cellController: cellContext,
       onDismissed: onDismissed,
     );
 

+ 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 GridCellContextBuilder cellContextBuilder;
+  final GridCellControllerBuilder cellContorllerBuilder;
   late final GridTextCellStyle? cellStyle;
   GridTextCell({
-    required this.cellContextBuilder,
+    required this.cellContorllerBuilder,
     GridCellStyle? style,
     Key? key,
   }) : super(key: key) {
@@ -39,7 +39,7 @@ class _GridTextCellState extends GridFocusNodeCellState<GridTextCell> {
 
   @override
   void initState() {
-    final cellContext = widget.cellContextBuilder.build();
+    final cellContext = widget.cellContorllerBuilder.build();
     _cellBloc = getIt<TextCellBloc>(param1: cellContext);
     _cellBloc.add(const TextCellEvent.initial());
     _controller = TextEditingController(text: _cellBloc.state.content);

+ 5 - 5
frontend/app_flowy/lib/workspace/presentation/plugins/grid/src/widgets/cell/url_cell/cell_editor.dart

@@ -7,21 +7,21 @@ import 'dart:async';
 import 'package:flutter_bloc/flutter_bloc.dart';
 
 class URLCellEditor extends StatefulWidget with FlowyOverlayDelegate {
-  final GridURLCellContext cellContext;
+  final GridURLCellController cellController;
   final VoidCallback completed;
-  const URLCellEditor({required this.cellContext, required this.completed, Key? key}) : super(key: key);
+  const URLCellEditor({required this.cellController, required this.completed, Key? key}) : super(key: key);
 
   @override
   State<URLCellEditor> createState() => _URLCellEditorState();
 
   static void show(
     BuildContext context,
-    GridURLCellContext cellContext,
+    GridURLCellController cellContext,
     VoidCallback completed,
   ) {
     FlowyOverlay.of(context).remove(identifier());
     final editor = URLCellEditor(
-      cellContext: cellContext,
+      cellController: cellContext,
       completed: completed,
     );
 
@@ -62,7 +62,7 @@ class _URLCellEditorState extends State<URLCellEditor> {
 
   @override
   void initState() {
-    _cellBloc = URLCellEditorBloc(cellContext: widget.cellContext);
+    _cellBloc = URLCellEditorBloc(cellContext: widget.cellController);
     _cellBloc.add(const URLCellEditorEvent.initial());
     _controller = TextEditingController(text: _cellBloc.state.content);
 

+ 9 - 9
frontend/app_flowy/lib/workspace/presentation/plugins/grid/src/widgets/cell/url_cell/url_cell.dart

@@ -31,10 +31,10 @@ enum GridURLCellAccessoryType {
 }
 
 class GridURLCell extends GridCellWidget {
-  final GridCellContextBuilder cellContextBuilder;
+  final GridCellControllerBuilder cellContorllerBuilder;
   late final GridURLCellStyle? cellStyle;
   GridURLCell({
-    required this.cellContextBuilder,
+    required this.cellContorllerBuilder,
     GridCellStyle? style,
     Key? key,
   }) : super(key: key) {
@@ -51,11 +51,11 @@ class GridURLCell extends GridCellWidget {
   GridCellAccessory accessoryFromType(GridURLCellAccessoryType ty, GridCellAccessoryBuildContext buildContext) {
     switch (ty) {
       case GridURLCellAccessoryType.edit:
-        final cellContext = cellContextBuilder.build() as GridURLCellContext;
+        final cellContext = cellContorllerBuilder.build() as GridURLCellController;
         return _EditURLAccessory(cellContext: cellContext, anchorContext: buildContext.anchorContext);
 
       case GridURLCellAccessoryType.copyURL:
-        final cellContext = cellContextBuilder.build() as GridURLCellContext;
+        final cellContext = cellContorllerBuilder.build() as GridURLCellController;
         return _CopyURLAccessory(cellContext: cellContext);
     }
   }
@@ -83,7 +83,7 @@ class _GridURLCellState extends GridCellState<GridURLCell> {
 
   @override
   void initState() {
-    final cellContext = widget.cellContextBuilder.build() as GridURLCellContext;
+    final cellContext = widget.cellContorllerBuilder.build() as GridURLCellController;
     _cellBloc = URLCellBloc(cellContext: cellContext);
     _cellBloc.add(const URLCellEvent.initial());
     super.initState();
@@ -132,7 +132,7 @@ class _GridURLCellState extends GridCellState<GridURLCell> {
     if (url.isNotEmpty && await canLaunchUrl(uri)) {
       await launchUrl(uri);
     } else {
-      final cellContext = widget.cellContextBuilder.build() as GridURLCellContext;
+      final cellContext = widget.cellContorllerBuilder.build() as GridURLCellController;
       widget.onCellEditing.value = true;
       URLCellEditor.show(context, cellContext, () {
         widget.onCellEditing.value = false;
@@ -155,7 +155,7 @@ class _GridURLCellState extends GridCellState<GridURLCell> {
 }
 
 class _EditURLAccessory extends StatelessWidget with GridCellAccessory {
-  final GridURLCellContext cellContext;
+  final GridURLCellController cellContext;
   final BuildContext anchorContext;
   const _EditURLAccessory({
     required this.cellContext,
@@ -176,7 +176,7 @@ class _EditURLAccessory extends StatelessWidget with GridCellAccessory {
 }
 
 class _CopyURLAccessory extends StatelessWidget with GridCellAccessory {
-  final GridURLCellContext cellContext;
+  final GridURLCellController cellContext;
   const _CopyURLAccessory({required this.cellContext, Key? key}) : super(key: key);
 
   @override
@@ -187,7 +187,7 @@ class _CopyURLAccessory extends StatelessWidget with GridCellAccessory {
 
   @override
   void onTap() {
-    final content = cellContext.getCellData(loadIfNoCache: false)?.content ?? "";
+    final content = cellContext.getCellData(loadIfNotExist: false)?.content ?? "";
     Clipboard.setData(ClipboardData(text: content));
     showMessageToast(LocaleKeys.grid_row_copyProperty.tr());
   }

+ 1 - 1
frontend/app_flowy/lib/workspace/presentation/plugins/grid/src/widgets/header/field_cell.dart

@@ -65,7 +65,7 @@ class GridFieldCell extends StatelessWidget {
     FieldEditor(
       gridId: state.gridId,
       fieldName: field.name,
-      contextLoader: FieldContextLoader(
+      typeOptionLoader: FieldTypeOptionLoader(
         gridId: state.gridId,
         field: field,
       ),

+ 16 - 13
frontend/app_flowy/lib/workspace/presentation/plugins/grid/src/widgets/header/field_editor.dart

@@ -8,17 +8,17 @@ import 'package:flutter/material.dart';
 import 'package:flutter_bloc/flutter_bloc.dart';
 import 'package:app_flowy/generated/locale_keys.g.dart';
 import 'field_name_input.dart';
-import 'field_editor_pannel.dart';
+import 'field_type_option_editor.dart';
 
 class FieldEditor extends StatelessWidget with FlowyOverlayDelegate {
   final String gridId;
   final String fieldName;
 
-  final IFieldContextLoader contextLoader;
+  final IFieldTypeOptionLoader typeOptionLoader;
   const FieldEditor({
     required this.gridId,
     required this.fieldName,
-    required this.contextLoader,
+    required this.typeOptionLoader,
     Key? key,
   }) : super(key: key);
 
@@ -28,7 +28,7 @@ class FieldEditor extends StatelessWidget with FlowyOverlayDelegate {
       create: (context) => FieldEditorBloc(
         gridId: gridId,
         fieldName: fieldName,
-        fieldContextLoader: contextLoader,
+        loader: typeOptionLoader,
       )..add(const FieldEditorEvent.initial()),
       child: BlocBuilder<FieldEditorBloc, FieldEditorState>(
         buildWhen: (p, c) => false,
@@ -38,9 +38,9 @@ class FieldEditor extends StatelessWidget with FlowyOverlayDelegate {
             children: [
               FlowyText.medium(LocaleKeys.grid_field_editProperty.tr(), fontSize: 12),
               const VSpace(10),
-              const _FieldNameTextField(),
+              const _FieldNameCell(),
               const VSpace(10),
-              const _FieldPannel(),
+              const _FieldTypeOptionCell(),
             ],
           );
         },
@@ -74,25 +74,28 @@ class FieldEditor extends StatelessWidget with FlowyOverlayDelegate {
   bool asBarrier() => true;
 }
 
-class _FieldPannel extends StatelessWidget {
-  const _FieldPannel({Key? key}) : super(key: key);
+class _FieldTypeOptionCell extends StatelessWidget {
+  const _FieldTypeOptionCell({Key? key}) : super(key: key);
 
   @override
   Widget build(BuildContext context) {
     return BlocBuilder<FieldEditorBloc, FieldEditorState>(
-      buildWhen: (p, c) => p.fieldContext != c.fieldContext,
+      buildWhen: (p, c) => p.field != c.field,
       builder: (context, state) {
-        return state.fieldContext.fold(
+        return state.field.fold(
           () => const SizedBox(),
-          (fieldContext) => FieldEditorPannel(fieldContext: fieldContext),
+          (fieldContext) {
+            final dataController = context.read<FieldEditorBloc>().dataController;
+            return FieldTypeOptionEditor(dataController: dataController);
+          },
         );
       },
     );
   }
 }
 
-class _FieldNameTextField extends StatelessWidget {
-  const _FieldNameTextField({Key? key}) : super(key: key);
+class _FieldNameCell extends StatelessWidget {
+  const _FieldNameCell({Key? key}) : super(key: key);
 
   @override
   Widget build(BuildContext context) {

+ 0 - 243
frontend/app_flowy/lib/workspace/presentation/plugins/grid/src/widgets/header/field_editor_pannel.dart

@@ -1,243 +0,0 @@
-import 'dart:typed_data';
-
-import 'package:app_flowy/workspace/application/grid/field/type_option/multi_select_type_option.dart';
-import 'package:app_flowy/workspace/application/grid/field/type_option/type_option_service.dart';
-import 'package:app_flowy/workspace/presentation/plugins/grid/src/widgets/header/type_option/checkbox.dart';
-import 'package:dartz/dartz.dart' show Either;
-import 'package:flowy_infra/image.dart';
-import 'package:flowy_infra/theme.dart';
-import 'package:flowy_infra_ui/flowy_infra_ui.dart';
-import 'package:flowy_infra_ui/style_widget/button.dart';
-import 'package:flowy_infra_ui/style_widget/text.dart';
-import 'package:flowy_sdk/protobuf/flowy-error/errors.pb.dart';
-import 'package:flowy_sdk/protobuf/flowy-grid/field_entities.pb.dart';
-import 'package:flutter/material.dart';
-import 'package:flutter_bloc/flutter_bloc.dart';
-import 'package:app_flowy/workspace/application/grid/prelude.dart';
-import 'package:app_flowy/workspace/presentation/plugins/grid/src/layout/sizes.dart';
-import 'package:app_flowy/workspace/presentation/plugins/grid/src/widgets/header/field_type_list.dart';
-import 'package:app_flowy/workspace/presentation/plugins/grid/src/widgets/header/type_option/date.dart';
-import 'field_type_extension.dart';
-import 'type_option/multi_select.dart';
-import 'type_option/number.dart';
-import 'type_option/rich_text.dart';
-import 'type_option/single_select.dart';
-import 'type_option/url.dart';
-
-typedef UpdateFieldCallback = void Function(Field, Uint8List);
-typedef SwitchToFieldCallback = Future<Either<FieldTypeOptionData, FlowyError>> Function(
-  String fieldId,
-  FieldType fieldType,
-);
-
-class FieldEditorPannel extends StatefulWidget {
-  final GridFieldContext fieldContext;
-
-  const FieldEditorPannel({
-    required this.fieldContext,
-    Key? key,
-  }) : super(key: key);
-
-  @override
-  State<FieldEditorPannel> createState() => _FieldEditorPannelState();
-}
-
-class _FieldEditorPannelState extends State<FieldEditorPannel> {
-  String? currentOverlayIdentifier;
-
-  @override
-  Widget build(BuildContext context) {
-    return BlocProvider(
-      create: (context) => FieldEditorPannelBloc(widget.fieldContext)..add(const FieldEditorPannelEvent.initial()),
-      child: BlocBuilder<FieldEditorPannelBloc, FieldEditorPannelState>(
-        builder: (context, state) {
-          List<Widget> children = [_switchFieldTypeButton(context, widget.fieldContext.field)];
-          final typeOptionWidget = _typeOptionWidget(context: context, state: state);
-
-          if (typeOptionWidget != null) {
-            children.add(typeOptionWidget);
-          }
-
-          return ListView(
-            shrinkWrap: true,
-            children: children,
-          );
-        },
-      ),
-    );
-  }
-
-  Widget _switchFieldTypeButton(BuildContext context, Field field) {
-    final theme = context.watch<AppTheme>();
-    return SizedBox(
-      height: GridSize.typeOptionItemHeight,
-      child: FlowyButton(
-        text: FlowyText.medium(field.fieldType.title(), fontSize: 12),
-        margin: const EdgeInsets.symmetric(horizontal: 6, vertical: 2),
-        hoverColor: theme.hover,
-        onTap: () {
-          final list = FieldTypeList(onSelectField: (newFieldType) {
-            widget.fieldContext.switchToField(newFieldType);
-          });
-          _showOverlay(context, list);
-        },
-        leftIcon: svgWidget(field.fieldType.iconName(), color: theme.iconColor),
-        rightIcon: svgWidget("grid/more", color: theme.iconColor),
-      ),
-    );
-  }
-
-  Widget? _typeOptionWidget({
-    required BuildContext context,
-    required FieldEditorPannelState state,
-  }) {
-    final overlayDelegate = TypeOptionOverlayDelegate(
-      showOverlay: _showOverlay,
-      hideOverlay: _hideOverlay,
-    );
-
-    final builder = _makeTypeOptionBuild(
-      typeOptionContext: _makeTypeOptionContext(widget.fieldContext),
-      overlayDelegate: overlayDelegate,
-    );
-
-    return builder.customWidget;
-  }
-
-  void _showOverlay(BuildContext context, Widget child, {VoidCallback? onRemoved}) {
-    final identifier = child.toString();
-    if (currentOverlayIdentifier != null) {
-      FlowyOverlay.of(context).remove(currentOverlayIdentifier!);
-    }
-
-    currentOverlayIdentifier = identifier;
-    FlowyOverlay.of(context).insertWithAnchor(
-      widget: OverlayContainer(
-        child: child,
-        constraints: BoxConstraints.loose(const Size(460, 440)),
-      ),
-      identifier: identifier,
-      anchorContext: context,
-      anchorDirection: AnchorDirection.leftWithCenterAligned,
-      style: FlowyOverlayStyle(blur: false),
-      anchorOffset: const Offset(-20, 0),
-    );
-  }
-
-  void _hideOverlay(BuildContext context) {
-    if (currentOverlayIdentifier != null) {
-      FlowyOverlay.of(context).remove(currentOverlayIdentifier!);
-    }
-  }
-}
-
-abstract class TypeOptionBuilder {
-  Widget? get customWidget;
-}
-
-TypeOptionBuilder _makeTypeOptionBuild({
-  required TypeOptionContext typeOptionContext,
-  required TypeOptionOverlayDelegate overlayDelegate,
-}) {
-  switch (typeOptionContext.field.fieldType) {
-    case FieldType.Checkbox:
-      return CheckboxTypeOptionBuilder(
-        typeOptionContext as CheckboxTypeOptionContext,
-      );
-    case FieldType.DateTime:
-      return DateTypeOptionBuilder(
-        typeOptionContext as DateTypeOptionContext,
-        overlayDelegate,
-      );
-    case FieldType.SingleSelect:
-      return SingleSelectTypeOptionBuilder(
-        typeOptionContext as SingleSelectTypeOptionContext,
-        overlayDelegate,
-      );
-    case FieldType.MultiSelect:
-      return MultiSelectTypeOptionBuilder(
-        typeOptionContext as MultiSelectTypeOptionContext,
-        overlayDelegate,
-      );
-    case FieldType.Number:
-      return NumberTypeOptionBuilder(
-        typeOptionContext as NumberTypeOptionContext,
-        overlayDelegate,
-      );
-    case FieldType.RichText:
-      return RichTextTypeOptionBuilder(
-        typeOptionContext as RichTextTypeOptionContext,
-      );
-
-    case FieldType.URL:
-      return URLTypeOptionBuilder(
-        typeOptionContext as URLTypeOptionContext,
-      );
-  }
-  throw UnimplementedError;
-}
-
-TypeOptionContext _makeTypeOptionContext(GridFieldContext fieldContext) {
-  switch (fieldContext.field.fieldType) {
-    case FieldType.Checkbox:
-      return CheckboxTypeOptionContext(
-        fieldContext: fieldContext,
-        dataBuilder: CheckboxTypeOptionDataBuilder(),
-      );
-    case FieldType.DateTime:
-      return DateTypeOptionContext(
-        fieldContext: fieldContext,
-        dataBuilder: DateTypeOptionDataBuilder(),
-      );
-    case FieldType.MultiSelect:
-      return MultiSelectTypeOptionContext(
-        fieldContext: fieldContext,
-        dataBuilder: MultiSelectTypeOptionDataBuilder(),
-      );
-    case FieldType.Number:
-      return NumberTypeOptionContext(
-        fieldContext: fieldContext,
-        dataBuilder: NumberTypeOptionDataBuilder(),
-      );
-    case FieldType.RichText:
-      return RichTextTypeOptionContext(
-        fieldContext: fieldContext,
-        dataBuilder: RichTextTypeOptionDataBuilder(),
-      );
-    case FieldType.SingleSelect:
-      return SingleSelectTypeOptionContext(
-        fieldContext: fieldContext,
-        dataBuilder: SingleSelectTypeOptionDataBuilder(),
-      );
-
-    case FieldType.URL:
-      return URLTypeOptionContext(
-        fieldContext: fieldContext,
-        dataBuilder: URLTypeOptionDataBuilder(),
-      );
-  }
-
-  throw UnimplementedError();
-}
-
-abstract class TypeOptionWidget extends StatelessWidget {
-  const TypeOptionWidget({Key? key}) : super(key: key);
-}
-
-typedef TypeOptionData = Uint8List;
-typedef TypeOptionDataCallback = void Function(TypeOptionData typeOptionData);
-typedef ShowOverlayCallback = void Function(
-  BuildContext anchorContext,
-  Widget child, {
-  VoidCallback? onRemoved,
-});
-typedef HideOverlayCallback = void Function(BuildContext anchorContext);
-
-class TypeOptionOverlayDelegate {
-  ShowOverlayCallback showOverlay;
-  HideOverlayCallback hideOverlay;
-  TypeOptionOverlayDelegate({
-    required this.showOverlay,
-    required this.hideOverlay,
-  });
-}

+ 127 - 0
frontend/app_flowy/lib/workspace/presentation/plugins/grid/src/widgets/header/field_type_option_editor.dart

@@ -0,0 +1,127 @@
+import 'dart:typed_data';
+import 'package:dartz/dartz.dart' show Either;
+import 'package:flowy_infra/image.dart';
+import 'package:flowy_infra/theme.dart';
+import 'package:flowy_infra_ui/flowy_infra_ui.dart';
+import 'package:flowy_infra_ui/style_widget/button.dart';
+import 'package:flowy_infra_ui/style_widget/text.dart';
+import 'package:flowy_sdk/protobuf/flowy-error/errors.pb.dart';
+import 'package:flowy_sdk/protobuf/flowy-grid/field_entities.pb.dart';
+import 'package:flutter/material.dart';
+import 'package:flutter_bloc/flutter_bloc.dart';
+import 'package:app_flowy/workspace/application/grid/prelude.dart';
+import 'package:app_flowy/workspace/presentation/plugins/grid/src/layout/sizes.dart';
+import 'package:app_flowy/workspace/presentation/plugins/grid/src/widgets/header/field_type_list.dart';
+import 'field_type_extension.dart';
+import 'type_option/builder.dart';
+
+typedef UpdateFieldCallback = void Function(Field, Uint8List);
+typedef SwitchToFieldCallback = Future<Either<FieldTypeOptionData, FlowyError>> Function(
+  String fieldId,
+  FieldType fieldType,
+);
+
+class FieldTypeOptionEditor extends StatefulWidget {
+  final TypeOptionDataController dataController;
+
+  const FieldTypeOptionEditor({
+    required this.dataController,
+    Key? key,
+  }) : super(key: key);
+
+  @override
+  State<FieldTypeOptionEditor> createState() => _FieldTypeOptionEditorState();
+}
+
+class _FieldTypeOptionEditorState extends State<FieldTypeOptionEditor> {
+  String? currentOverlayIdentifier;
+
+  @override
+  Widget build(BuildContext context) {
+    return BlocProvider(
+      create: (context) =>
+          FieldTypeOptionEditBloc(widget.dataController)..add(const FieldTypeOptionEditEvent.initial()),
+      child: BlocBuilder<FieldTypeOptionEditBloc, FieldTypeOptionEditState>(
+        builder: (context, state) {
+          List<Widget> children = [_switchFieldTypeButton(context, widget.dataController.field)];
+          final typeOptionWidget = _typeOptionWidget(context: context, state: state);
+
+          if (typeOptionWidget != null) {
+            children.add(typeOptionWidget);
+          }
+
+          return ListView(
+            shrinkWrap: true,
+            children: children,
+          );
+        },
+      ),
+    );
+  }
+
+  Widget _switchFieldTypeButton(BuildContext context, Field field) {
+    final theme = context.watch<AppTheme>();
+    return SizedBox(
+      height: GridSize.typeOptionItemHeight,
+      child: FlowyButton(
+        text: FlowyText.medium(field.fieldType.title(), fontSize: 12),
+        margin: const EdgeInsets.symmetric(horizontal: 6, vertical: 2),
+        hoverColor: theme.hover,
+        onTap: () {
+          final list = FieldTypeList(onSelectField: (newFieldType) {
+            widget.dataController.switchToField(newFieldType);
+          });
+          _showOverlay(context, list);
+        },
+        leftIcon: svgWidget(field.fieldType.iconName(), color: theme.iconColor),
+        rightIcon: svgWidget("grid/more", color: theme.iconColor),
+      ),
+    );
+  }
+
+  Widget? _typeOptionWidget({
+    required BuildContext context,
+    required FieldTypeOptionEditState state,
+  }) {
+    final overlayDelegate = TypeOptionOverlayDelegate(
+      showOverlay: _showOverlay,
+      hideOverlay: _hideOverlay,
+    );
+
+    return makeTypeOptionWidget(
+      context: context,
+      dataController: widget.dataController,
+      overlayDelegate: overlayDelegate,
+    );
+  }
+
+  void _showOverlay(BuildContext context, Widget child, {VoidCallback? onRemoved}) {
+    final identifier = child.toString();
+    if (currentOverlayIdentifier != null) {
+      FlowyOverlay.of(context).remove(currentOverlayIdentifier!);
+    }
+
+    currentOverlayIdentifier = identifier;
+    FlowyOverlay.of(context).insertWithAnchor(
+      widget: OverlayContainer(
+        child: child,
+        constraints: BoxConstraints.loose(const Size(460, 440)),
+      ),
+      identifier: identifier,
+      anchorContext: context,
+      anchorDirection: AnchorDirection.leftWithCenterAligned,
+      style: FlowyOverlayStyle(blur: false),
+      anchorOffset: const Offset(-20, 0),
+    );
+  }
+
+  void _hideOverlay(BuildContext context) {
+    if (currentOverlayIdentifier != null) {
+      FlowyOverlay.of(context).remove(currentOverlayIdentifier!);
+    }
+  }
+}
+
+abstract class TypeOptionWidget extends StatelessWidget {
+  const TypeOptionWidget({Key? key}) : super(key: key);
+}

+ 1 - 1
frontend/app_flowy/lib/workspace/presentation/plugins/grid/src/widgets/header/grid_header.dart

@@ -151,7 +151,7 @@ class CreateFieldButton extends StatelessWidget {
       onTap: () => FieldEditor(
         gridId: gridId,
         fieldName: "",
-        contextLoader: NewFieldContextLoader(gridId: gridId),
+        typeOptionLoader: NewFieldTypeOptionLoader(gridId: gridId),
       ).show(context),
       leftIcon: svgWidget("home/add"),
     );

+ 108 - 0
frontend/app_flowy/lib/workspace/presentation/plugins/grid/src/widgets/header/type_option/builder.dart

@@ -0,0 +1,108 @@
+import 'dart:typed_data';
+
+import 'package:app_flowy/workspace/application/grid/field/type_option/multi_select_type_option.dart';
+import 'package:app_flowy/workspace/presentation/plugins/grid/src/widgets/header/type_option/checkbox.dart';
+import 'package:app_flowy/workspace/application/grid/prelude.dart';
+import 'package:flowy_sdk/protobuf/flowy-grid/field_entities.pb.dart';
+import 'package:flutter/material.dart';
+import 'date.dart';
+import 'multi_select.dart';
+import 'number.dart';
+import 'rich_text.dart';
+import 'single_select.dart';
+import 'url.dart';
+
+typedef TypeOptionData = Uint8List;
+typedef TypeOptionDataCallback = void Function(TypeOptionData typeOptionData);
+typedef ShowOverlayCallback = void Function(
+  BuildContext anchorContext,
+  Widget child, {
+  VoidCallback? onRemoved,
+});
+typedef HideOverlayCallback = void Function(BuildContext anchorContext);
+
+class TypeOptionOverlayDelegate {
+  ShowOverlayCallback showOverlay;
+  HideOverlayCallback hideOverlay;
+  TypeOptionOverlayDelegate({
+    required this.showOverlay,
+    required this.hideOverlay,
+  });
+}
+
+abstract class TypeOptionWidgetBuilder {
+  Widget? build(BuildContext context);
+}
+
+Widget? makeTypeOptionWidget({
+  required BuildContext context,
+  required TypeOptionDataController dataController,
+  required TypeOptionOverlayDelegate overlayDelegate,
+}) {
+  final builder = makeTypeOptionWidgetBuilder(dataController, overlayDelegate);
+  return builder.build(context);
+}
+
+TypeOptionWidgetBuilder makeTypeOptionWidgetBuilder(
+  TypeOptionDataController dataController,
+  TypeOptionOverlayDelegate overlayDelegate,
+) {
+  switch (dataController.field.fieldType) {
+    case FieldType.Checkbox:
+      final context = CheckboxTypeOptionContext(
+        dataController: dataController,
+        dataParser: CheckboxTypeOptionWidgetDataParser(),
+      );
+      return CheckboxTypeOptionWidgetBuilder(context);
+    case FieldType.DateTime:
+      final context = DateTypeOptionContext(
+        dataController: dataController,
+        dataParser: DateTypeOptionDataParser(),
+      );
+      return DateTypeOptionWidgetBuilder(
+        context,
+        overlayDelegate,
+      );
+    case FieldType.SingleSelect:
+      final context = SingleSelectTypeOptionContext(
+        fieldContext: dataController,
+        dataBuilder: SingleSelectTypeOptionWidgetDataParser(),
+      );
+      return SingleSelectTypeOptionWidgetBuilder(
+        context,
+        overlayDelegate,
+      );
+    case FieldType.MultiSelect:
+      final context = MultiSelectTypeOptionContext(
+        dataController: dataController,
+        dataBuilder: MultiSelectTypeOptionWidgetDataParser(),
+      );
+      return MultiSelectTypeOptionWidgetBuilder(
+        context,
+        overlayDelegate,
+      );
+    case FieldType.Number:
+      final context = NumberTypeOptionContext(
+        dataController: dataController,
+        dataParser: NumberTypeOptionWidgetDataParser(),
+      );
+      return NumberTypeOptionWidgetBuilder(
+        context,
+        overlayDelegate,
+      );
+    case FieldType.RichText:
+      final context = RichTextTypeOptionContext(
+        dataController: dataController,
+        dataParser: RichTextTypeOptionWidgetDataParser(),
+      );
+      return RichTextTypeOptionWidgetBuilder(context);
+
+    case FieldType.URL:
+      final context = URLTypeOptionContext(
+        dataController: dataController,
+        dataParser: URLTypeOptionWidgetDataParser(),
+      );
+      return URLTypeOptionWidgetBuilder(context);
+  }
+  throw UnimplementedError;
+}

+ 6 - 6
frontend/app_flowy/lib/workspace/presentation/plugins/grid/src/widgets/header/type_option/checkbox.dart

@@ -1,20 +1,20 @@
 import 'package:app_flowy/workspace/application/grid/field/type_option/type_option_service.dart';
-import 'package:app_flowy/workspace/presentation/plugins/grid/src/widgets/header/field_editor_pannel.dart';
 import 'package:flowy_sdk/protobuf/flowy-grid/checkbox_type_option.pb.dart';
 import 'package:flutter/material.dart';
+import 'builder.dart';
 
-typedef CheckboxTypeOptionContext = TypeOptionContext<CheckboxTypeOption>;
+typedef CheckboxTypeOptionContext = TypeOptionWidgetContext<CheckboxTypeOption>;
 
-class CheckboxTypeOptionDataBuilder extends TypeOptionDataBuilder<CheckboxTypeOption> {
+class CheckboxTypeOptionWidgetDataParser extends TypeOptionDataParser<CheckboxTypeOption> {
   @override
   CheckboxTypeOption fromBuffer(List<int> buffer) {
     return CheckboxTypeOption.fromBuffer(buffer);
   }
 }
 
-class CheckboxTypeOptionBuilder extends TypeOptionBuilder {
-  CheckboxTypeOptionBuilder(CheckboxTypeOptionContext typeOptionContext);
+class CheckboxTypeOptionWidgetBuilder extends TypeOptionWidgetBuilder {
+  CheckboxTypeOptionWidgetBuilder(CheckboxTypeOptionContext typeOptionContext);
 
   @override
-  Widget? get customWidget => null;
+  Widget? build(BuildContext context) => null;
 }

+ 7 - 4
frontend/app_flowy/lib/workspace/presentation/plugins/grid/src/widgets/header/type_option/date.dart

@@ -1,6 +1,6 @@
 import 'package:app_flowy/workspace/application/grid/field/type_option/date_bloc.dart';
 import 'package:app_flowy/workspace/presentation/plugins/grid/src/layout/sizes.dart';
-import 'package:app_flowy/workspace/presentation/plugins/grid/src/widgets/header/field_editor_pannel.dart';
+import 'package:app_flowy/workspace/presentation/plugins/grid/src/widgets/header/field_type_option_editor.dart';
 import 'package:easy_localization/easy_localization.dart' hide DateFormat;
 import 'package:app_flowy/generated/locale_keys.g.dart';
 import 'package:flowy_infra/image.dart';
@@ -12,11 +12,12 @@ import 'package:flowy_infra_ui/widget/spacing.dart';
 import 'package:flowy_sdk/protobuf/flowy-grid/date_type_option_entities.pb.dart';
 import 'package:flutter/material.dart';
 import 'package:flutter_bloc/flutter_bloc.dart';
+import 'builder.dart';
 
-class DateTypeOptionBuilder extends TypeOptionBuilder {
+class DateTypeOptionWidgetBuilder extends TypeOptionWidgetBuilder {
   final DateTypeOptionWidget _widget;
 
-  DateTypeOptionBuilder(
+  DateTypeOptionWidgetBuilder(
     DateTypeOptionContext typeOptionContext,
     TypeOptionOverlayDelegate overlayDelegate,
   ) : _widget = DateTypeOptionWidget(
@@ -25,7 +26,9 @@ class DateTypeOptionBuilder extends TypeOptionBuilder {
         );
 
   @override
-  Widget? get customWidget => _widget;
+  Widget? build(BuildContext context) {
+    return _widget;
+  }
 }
 
 class DateTypeOptionWidget extends TypeOptionWidget {

+ 5 - 4
frontend/app_flowy/lib/workspace/presentation/plugins/grid/src/widgets/header/type_option/multi_select.dart

@@ -1,13 +1,14 @@
 import 'package:app_flowy/workspace/application/grid/field/type_option/multi_select_type_option.dart';
-import 'package:app_flowy/workspace/presentation/plugins/grid/src/widgets/header/field_editor_pannel.dart';
+import 'package:app_flowy/workspace/presentation/plugins/grid/src/widgets/header/field_type_option_editor.dart';
 import 'package:flutter/material.dart';
 
+import 'builder.dart';
 import 'select_option.dart';
 
-class MultiSelectTypeOptionBuilder extends TypeOptionBuilder {
+class MultiSelectTypeOptionWidgetBuilder extends TypeOptionWidgetBuilder {
   final MultiSelectTypeOptionWidget _widget;
 
-  MultiSelectTypeOptionBuilder(
+  MultiSelectTypeOptionWidgetBuilder(
     MultiSelectTypeOptionContext typeOptionContext,
     TypeOptionOverlayDelegate overlayDelegate,
   ) : _widget = MultiSelectTypeOptionWidget(
@@ -16,7 +17,7 @@ class MultiSelectTypeOptionBuilder extends TypeOptionBuilder {
         );
 
   @override
-  Widget? get customWidget => _widget;
+  Widget? build(BuildContext context) => _widget;
 }
 
 class MultiSelectTypeOptionWidget extends TypeOptionWidget {

+ 6 - 4
frontend/app_flowy/lib/workspace/presentation/plugins/grid/src/widgets/header/type_option/number.dart

@@ -2,7 +2,7 @@ import 'package:app_flowy/workspace/application/grid/field/type_option/number_bl
 import 'package:app_flowy/workspace/application/grid/field/type_option/number_format_bloc.dart';
 import 'package:app_flowy/workspace/presentation/plugins/grid/src/layout/sizes.dart';
 import 'package:app_flowy/workspace/presentation/plugins/grid/src/widgets/common/text_field.dart';
-import 'package:app_flowy/workspace/presentation/plugins/grid/src/widgets/header/field_editor_pannel.dart';
+import 'package:app_flowy/workspace/presentation/plugins/grid/src/widgets/header/field_type_option_editor.dart';
 import 'package:flowy_infra/image.dart';
 import 'package:flowy_infra/theme.dart';
 import 'package:flowy_infra_ui/flowy_infra_ui.dart';
@@ -15,10 +15,12 @@ import 'package:flutter_bloc/flutter_bloc.dart';
 import 'package:easy_localization/easy_localization.dart' hide NumberFormat;
 import 'package:app_flowy/generated/locale_keys.g.dart';
 
-class NumberTypeOptionBuilder extends TypeOptionBuilder {
+import 'builder.dart';
+
+class NumberTypeOptionWidgetBuilder extends TypeOptionWidgetBuilder {
   final NumberTypeOptionWidget _widget;
 
-  NumberTypeOptionBuilder(
+  NumberTypeOptionWidgetBuilder(
     NumberTypeOptionContext typeOptionContext,
     TypeOptionOverlayDelegate overlayDelegate,
   ) : _widget = NumberTypeOptionWidget(
@@ -27,7 +29,7 @@ class NumberTypeOptionBuilder extends TypeOptionBuilder {
         );
 
   @override
-  Widget? get customWidget => _widget;
+  Widget? build(BuildContext context) => _widget;
 }
 
 class NumberTypeOptionWidget extends TypeOptionWidget {

+ 6 - 7
frontend/app_flowy/lib/workspace/presentation/plugins/grid/src/widgets/header/type_option/rich_text.dart

@@ -1,21 +1,20 @@
 import 'package:app_flowy/workspace/application/grid/field/type_option/type_option_service.dart';
-import 'package:app_flowy/workspace/presentation/plugins/grid/src/widgets/header/field_editor_pannel.dart';
 import 'package:flowy_sdk/protobuf/flowy-grid/text_type_option.pb.dart';
-
 import 'package:flutter/material.dart';
+import 'builder.dart';
 
-typedef RichTextTypeOptionContext = TypeOptionContext<RichTextTypeOption>;
+typedef RichTextTypeOptionContext = TypeOptionWidgetContext<RichTextTypeOption>;
 
-class RichTextTypeOptionDataBuilder extends TypeOptionDataBuilder<RichTextTypeOption> {
+class RichTextTypeOptionWidgetDataParser extends TypeOptionDataParser<RichTextTypeOption> {
   @override
   RichTextTypeOption fromBuffer(List<int> buffer) {
     return RichTextTypeOption.fromBuffer(buffer);
   }
 }
 
-class RichTextTypeOptionBuilder extends TypeOptionBuilder {
-  RichTextTypeOptionBuilder(RichTextTypeOptionContext typeOptionContext);
+class RichTextTypeOptionWidgetBuilder extends TypeOptionWidgetBuilder {
+  RichTextTypeOptionWidgetBuilder(RichTextTypeOptionContext typeOptionContext);
 
   @override
-  Widget? get customWidget => null;
+  Widget? build(BuildContext context) => null;
 }

+ 1 - 1
frontend/app_flowy/lib/workspace/presentation/plugins/grid/src/widgets/header/type_option/select_option.dart

@@ -2,7 +2,6 @@ import 'package:app_flowy/workspace/application/grid/field/type_option/select_op
 import 'package:app_flowy/workspace/presentation/plugins/grid/src/layout/sizes.dart';
 import 'package:app_flowy/workspace/presentation/plugins/grid/src/widgets/cell/select_option_cell/extension.dart';
 import 'package:app_flowy/workspace/presentation/plugins/grid/src/widgets/common/text_field.dart';
-import 'package:app_flowy/workspace/presentation/plugins/grid/src/widgets/header/field_editor_pannel.dart';
 import 'package:flowy_infra/image.dart';
 import 'package:flowy_infra/theme.dart';
 import 'package:flowy_infra_ui/style_widget/button.dart';
@@ -14,6 +13,7 @@ import 'package:flutter_bloc/flutter_bloc.dart';
 import 'package:easy_localization/easy_localization.dart';
 import 'package:app_flowy/generated/locale_keys.g.dart';
 
+import 'builder.dart';
 import 'select_option_editor.dart';
 
 class SelectOptionTypeOptionWidget extends StatelessWidget {

+ 5 - 4
frontend/app_flowy/lib/workspace/presentation/plugins/grid/src/widgets/header/type_option/single_select.dart

@@ -1,12 +1,13 @@
 import 'package:app_flowy/workspace/application/grid/field/type_option/single_select_type_option.dart';
-import 'package:app_flowy/workspace/presentation/plugins/grid/src/widgets/header/field_editor_pannel.dart';
+import 'package:app_flowy/workspace/presentation/plugins/grid/src/widgets/header/field_type_option_editor.dart';
 import 'package:flutter/material.dart';
+import 'builder.dart';
 import 'select_option.dart';
 
-class SingleSelectTypeOptionBuilder extends TypeOptionBuilder {
+class SingleSelectTypeOptionWidgetBuilder extends TypeOptionWidgetBuilder {
   final SingleSelectTypeOptionWidget _widget;
 
-  SingleSelectTypeOptionBuilder(
+  SingleSelectTypeOptionWidgetBuilder(
     SingleSelectTypeOptionContext typeOptionContext,
     TypeOptionOverlayDelegate overlayDelegate,
   ) : _widget = SingleSelectTypeOptionWidget(
@@ -15,7 +16,7 @@ class SingleSelectTypeOptionBuilder extends TypeOptionBuilder {
         );
 
   @override
-  Widget? get customWidget => _widget;
+  Widget? build(BuildContext context) => _widget;
 }
 
 class SingleSelectTypeOptionWidget extends TypeOptionWidget {

+ 6 - 6
frontend/app_flowy/lib/workspace/presentation/plugins/grid/src/widgets/header/type_option/url.dart

@@ -1,20 +1,20 @@
 import 'package:app_flowy/workspace/application/grid/field/type_option/type_option_service.dart';
-import 'package:app_flowy/workspace/presentation/plugins/grid/src/widgets/header/field_editor_pannel.dart';
 import 'package:flowy_sdk/protobuf/flowy-grid/url_type_option.pb.dart';
 import 'package:flutter/material.dart';
+import 'builder.dart';
 
-typedef URLTypeOptionContext = TypeOptionContext<URLTypeOption>;
+typedef URLTypeOptionContext = TypeOptionWidgetContext<URLTypeOption>;
 
-class URLTypeOptionDataBuilder extends TypeOptionDataBuilder<URLTypeOption> {
+class URLTypeOptionWidgetDataParser extends TypeOptionDataParser<URLTypeOption> {
   @override
   URLTypeOption fromBuffer(List<int> buffer) {
     return URLTypeOption.fromBuffer(buffer);
   }
 }
 
-class URLTypeOptionBuilder extends TypeOptionBuilder {
-  URLTypeOptionBuilder(URLTypeOptionContext typeOptionContext);
+class URLTypeOptionWidgetBuilder extends TypeOptionWidgetBuilder {
+  URLTypeOptionWidgetBuilder(URLTypeOptionContext typeOptionContext);
 
   @override
-  Widget? get customWidget => null;
+  Widget? build(BuildContext context) => null;
 }

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

@@ -10,19 +10,25 @@ import 'package:flutter/foundation.dart';
 import 'package:flutter/material.dart';
 import 'package:flutter_bloc/flutter_bloc.dart';
 import 'package:provider/provider.dart';
-import 'row_action_sheet.dart';
 
+import 'row_action_sheet.dart';
 import 'row_detail.dart';
 
 class GridRowWidget extends StatefulWidget {
   final GridRow rowData;
-  final GridRowCacheService rowCache;
+  final GridRowCache rowCache;
+  final GridCellBuilder cellBuilder;
 
-  const GridRowWidget({
+  GridRowWidget({
     required this.rowData,
     required this.rowCache,
+    required GridFieldCache fieldCache,
     Key? key,
-  }) : super(key: key);
+  })  : cellBuilder = GridCellBuilder(
+          cellCache: rowCache.cellCache,
+          fieldCache: fieldCache,
+        ),
+        super(key: key);
 
   @override
   State<GridRowWidget> createState() => _GridRowWidgetState();
@@ -52,7 +58,11 @@ class _GridRowWidgetState extends State<GridRowWidget> {
             return Row(
               children: [
                 const _RowLeading(),
-                Expanded(child: _RowCells(cellCache: widget.rowCache.cellCache, onExpand: () => _expandRow(context))),
+                Expanded(
+                    child: _RowCells(
+                  builder: widget.cellBuilder,
+                  onExpand: () => _expandRow(context),
+                )),
                 const _RowTrailing(),
               ],
             );
@@ -72,6 +82,7 @@ class _GridRowWidgetState extends State<GridRowWidget> {
     final page = RowDetailPage(
       rowData: widget.rowData,
       rowCache: widget.rowCache,
+      cellBuilder: widget.cellBuilder,
     );
     page.show(context);
   }
@@ -146,9 +157,13 @@ class _DeleteRowButton extends StatelessWidget {
 }
 
 class _RowCells extends StatelessWidget {
-  final GridCellCacheService cellCache;
   final VoidCallback onExpand;
-  const _RowCells({required this.cellCache, required this.onExpand, Key? key}) : super(key: key);
+  final GridCellBuilder builder;
+  const _RowCells({
+    required this.builder,
+    required this.onExpand,
+    Key? key,
+  }) : super(key: key);
 
   @override
   Widget build(BuildContext context) {
@@ -168,13 +183,12 @@ class _RowCells extends StatelessWidget {
 
   List<Widget> _makeCells(BuildContext context, GridCellMap gridCellMap) {
     return gridCellMap.values.map(
-      (gridCell) {
-        final GridCellWidget child = buildGridCellWidget(gridCell, cellCache);
-
+      (cellId) {
+        final GridCellWidget child = builder.build(cellId);
         accessoryBuilder(GridCellAccessoryBuildContext buildContext) {
           final builder = child.accessoryBuilder;
           List<GridCellAccessory> accessories = [];
-          if (gridCell.field.isPrimary) {
+          if (cellId.field.isPrimary) {
             accessories.add(PrimaryCellAccessory(
               onTapCallback: onExpand,
               isCellEditing: buildContext.isCellEditing,
@@ -188,7 +202,7 @@ class _RowCells extends StatelessWidget {
         }
 
         return CellContainer(
-          width: gridCell.field.width.toDouble(),
+          width: cellId.field.width.toDouble(),
           child: child,
           rowStateNotifier: Provider.of<RegionStateNotifier>(context, listen: false),
           accessoryBuilder: accessoryBuilder,

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

@@ -22,11 +22,13 @@ import 'package:flutter_bloc/flutter_bloc.dart';
 
 class RowDetailPage extends StatefulWidget with FlowyOverlayDelegate {
   final GridRow rowData;
-  final GridRowCacheService rowCache;
+  final GridRowCache rowCache;
+  final GridCellBuilder cellBuilder;
 
   const RowDetailPage({
     required this.rowData,
     required this.rowCache,
+    required this.cellBuilder,
     Key? key,
   }) : super(key: key);
 
@@ -74,7 +76,7 @@ class _RowDetailPageState extends State<RowDetailPage> {
                 children: const [Spacer(), _CloseButton()],
               ),
             ),
-            Expanded(child: _PropertyList(cellCache: widget.rowCache.cellCache)),
+            Expanded(child: _PropertyList(cellBuilder: widget.cellBuilder)),
           ],
         ),
       ),
@@ -98,10 +100,10 @@ class _CloseButton extends StatelessWidget {
 }
 
 class _PropertyList extends StatelessWidget {
-  final GridCellCacheService cellCache;
+  final GridCellBuilder cellBuilder;
   final ScrollController _scrollController;
   _PropertyList({
-    required this.cellCache,
+    required this.cellBuilder,
     Key? key,
   })  : _scrollController = ScrollController(),
         super(key: key);
@@ -120,8 +122,8 @@ class _PropertyList extends StatelessWidget {
             itemCount: state.gridCells.length,
             itemBuilder: (BuildContext context, int index) {
               return _RowDetailCell(
-                gridCell: state.gridCells[index],
-                cellCache: cellCache,
+                cellId: state.gridCells[index],
+                cellBuilder: cellBuilder,
               );
             },
             separatorBuilder: (BuildContext context, int index) {
@@ -135,19 +137,19 @@ class _PropertyList extends StatelessWidget {
 }
 
 class _RowDetailCell extends StatelessWidget {
-  final GridCell gridCell;
-  final GridCellCacheService cellCache;
+  final GridCellIdentifier cellId;
+  final GridCellBuilder cellBuilder;
   const _RowDetailCell({
-    required this.gridCell,
-    required this.cellCache,
+    required this.cellId,
+    required this.cellBuilder,
     Key? key,
   }) : super(key: key);
 
   @override
   Widget build(BuildContext context) {
     final theme = context.watch<AppTheme>();
-    final style = _customCellStyle(theme, gridCell.field.fieldType);
-    final cell = buildGridCellWidget(gridCell, cellCache, style: style);
+    final style = _customCellStyle(theme, cellId.fieldType);
+    final cell = cellBuilder.build(cellId, style: style);
 
     final gesture = GestureDetector(
       behavior: HitTestBehavior.translucent,
@@ -167,7 +169,7 @@ class _RowDetailCell extends StatelessWidget {
           children: [
             SizedBox(
               width: 150,
-              child: FieldCellButton(field: gridCell.field, onTap: () => _showFieldEditor(context)),
+              child: FieldCellButton(field: cellId.field, onTap: () => _showFieldEditor(context)),
             ),
             const HSpace(10),
             Expanded(child: gesture),
@@ -179,11 +181,11 @@ class _RowDetailCell extends StatelessWidget {
 
   void _showFieldEditor(BuildContext context) {
     FieldEditor(
-      gridId: gridCell.gridId,
-      fieldName: gridCell.field.name,
-      contextLoader: FieldContextLoader(
-        gridId: gridCell.gridId,
-        field: gridCell.field,
+      gridId: cellId.gridId,
+      fieldName: cellId.field.name,
+      typeOptionLoader: FieldTypeOptionLoader(
+        gridId: cellId.gridId,
+        field: cellId.field,
       ),
     ).show(context);
   }

+ 1 - 1
frontend/app_flowy/lib/workspace/presentation/plugins/grid/src/widgets/toolbar/grid_property.dart

@@ -116,7 +116,7 @@ class _GridPropertyCell extends StatelessWidget {
         FieldEditor(
           gridId: gridId,
           fieldName: field.name,
-          contextLoader: FieldContextLoader(gridId: gridId, field: field),
+          typeOptionLoader: FieldTypeOptionLoader(gridId: gridId, field: field),
         ).show(context, anchorDirection: AnchorDirection.bottomRight);
       },
     );

+ 0 - 231
frontend/app_flowy/packages/flowy_infra/pubspec.lock

@@ -1,231 +0,0 @@
-# Generated by pub
-# See https://dart.dev/tools/pub/glossary#lockfile
-packages:
-  async:
-    dependency: transitive
-    description:
-      name: async
-      url: "https://pub.dartlang.org"
-    source: hosted
-    version: "2.8.2"
-  boolean_selector:
-    dependency: transitive
-    description:
-      name: boolean_selector
-      url: "https://pub.dartlang.org"
-    source: hosted
-    version: "2.1.0"
-  characters:
-    dependency: transitive
-    description:
-      name: characters
-      url: "https://pub.dartlang.org"
-    source: hosted
-    version: "1.2.0"
-  charcode:
-    dependency: transitive
-    description:
-      name: charcode
-      url: "https://pub.dartlang.org"
-    source: hosted
-    version: "1.3.1"
-  clock:
-    dependency: transitive
-    description:
-      name: clock
-      url: "https://pub.dartlang.org"
-    source: hosted
-    version: "1.1.0"
-  collection:
-    dependency: transitive
-    description:
-      name: collection
-      url: "https://pub.dartlang.org"
-    source: hosted
-    version: "1.15.0"
-  crypto:
-    dependency: transitive
-    description:
-      name: crypto
-      url: "https://pub.dartlang.org"
-    source: hosted
-    version: "3.0.1"
-  fake_async:
-    dependency: transitive
-    description:
-      name: fake_async
-      url: "https://pub.dartlang.org"
-    source: hosted
-    version: "1.2.0"
-  flutter:
-    dependency: "direct main"
-    description: flutter
-    source: sdk
-    version: "0.0.0"
-  flutter_lints:
-    dependency: "direct dev"
-    description:
-      name: flutter_lints
-      url: "https://pub.dartlang.org"
-    source: hosted
-    version: "1.0.4"
-  flutter_svg:
-    dependency: "direct main"
-    description:
-      name: flutter_svg
-      url: "https://pub.dartlang.org"
-    source: hosted
-    version: "0.22.0"
-  flutter_test:
-    dependency: "direct dev"
-    description: flutter
-    source: sdk
-    version: "0.0.0"
-  lints:
-    dependency: transitive
-    description:
-      name: lints
-      url: "https://pub.dartlang.org"
-    source: hosted
-    version: "1.0.1"
-  matcher:
-    dependency: transitive
-    description:
-      name: matcher
-      url: "https://pub.dartlang.org"
-    source: hosted
-    version: "0.12.11"
-  material_color_utilities:
-    dependency: transitive
-    description:
-      name: material_color_utilities
-      url: "https://pub.dartlang.org"
-    source: hosted
-    version: "0.1.3"
-  meta:
-    dependency: transitive
-    description:
-      name: meta
-      url: "https://pub.dartlang.org"
-    source: hosted
-    version: "1.7.0"
-  path:
-    dependency: transitive
-    description:
-      name: path
-      url: "https://pub.dartlang.org"
-    source: hosted
-    version: "1.8.0"
-  path_drawing:
-    dependency: transitive
-    description:
-      name: path_drawing
-      url: "https://pub.dartlang.org"
-    source: hosted
-    version: "0.5.1"
-  path_parsing:
-    dependency: transitive
-    description:
-      name: path_parsing
-      url: "https://pub.dartlang.org"
-    source: hosted
-    version: "0.2.1"
-  petitparser:
-    dependency: transitive
-    description:
-      name: petitparser
-      url: "https://pub.dartlang.org"
-    source: hosted
-    version: "4.2.0"
-  sky_engine:
-    dependency: transitive
-    description: flutter
-    source: sdk
-    version: "0.0.99"
-  source_span:
-    dependency: transitive
-    description:
-      name: source_span
-      url: "https://pub.dartlang.org"
-    source: hosted
-    version: "1.8.1"
-  stack_trace:
-    dependency: transitive
-    description:
-      name: stack_trace
-      url: "https://pub.dartlang.org"
-    source: hosted
-    version: "1.10.0"
-  stream_channel:
-    dependency: transitive
-    description:
-      name: stream_channel
-      url: "https://pub.dartlang.org"
-    source: hosted
-    version: "2.1.0"
-  string_scanner:
-    dependency: transitive
-    description:
-      name: string_scanner
-      url: "https://pub.dartlang.org"
-    source: hosted
-    version: "1.1.0"
-  term_glyph:
-    dependency: transitive
-    description:
-      name: term_glyph
-      url: "https://pub.dartlang.org"
-    source: hosted
-    version: "1.2.0"
-  test_api:
-    dependency: transitive
-    description:
-      name: test_api
-      url: "https://pub.dartlang.org"
-    source: hosted
-    version: "0.4.8"
-  textstyle_extensions:
-    dependency: "direct main"
-    description:
-      name: textstyle_extensions
-      url: "https://pub.dartlang.org"
-    source: hosted
-    version: "2.0.0-nullsafety"
-  time:
-    dependency: "direct main"
-    description:
-      name: time
-      url: "https://pub.dartlang.org"
-    source: hosted
-    version: "2.0.0"
-  typed_data:
-    dependency: transitive
-    description:
-      name: typed_data
-      url: "https://pub.dartlang.org"
-    source: hosted
-    version: "1.3.0"
-  uuid:
-    dependency: "direct main"
-    description:
-      name: uuid
-      url: "https://pub.dartlang.org"
-    source: hosted
-    version: "3.0.4"
-  vector_math:
-    dependency: transitive
-    description:
-      name: vector_math
-      url: "https://pub.dartlang.org"
-    source: hosted
-    version: "2.1.1"
-  xml:
-    dependency: transitive
-    description:
-      name: xml
-      url: "https://pub.dartlang.org"
-    source: hosted
-    version: "5.2.0"
-sdks:
-  dart: ">=2.14.0 <3.0.0"
-  flutter: ">=1.24.0-7.0"

+ 0 - 334
frontend/app_flowy/packages/flowy_infra_ui/example/pubspec.lock

@@ -1,334 +0,0 @@
-# Generated by pub
-# See https://dart.dev/tools/pub/glossary#lockfile
-packages:
-  animations:
-    dependency: transitive
-    description:
-      name: animations
-      url: "https://pub.dartlang.org"
-    source: hosted
-    version: "2.0.1"
-  async:
-    dependency: transitive
-    description:
-      name: async
-      url: "https://pub.dartlang.org"
-    source: hosted
-    version: "2.8.2"
-  boolean_selector:
-    dependency: transitive
-    description:
-      name: boolean_selector
-      url: "https://pub.dartlang.org"
-    source: hosted
-    version: "2.1.0"
-  characters:
-    dependency: transitive
-    description:
-      name: characters
-      url: "https://pub.dartlang.org"
-    source: hosted
-    version: "1.2.0"
-  charcode:
-    dependency: transitive
-    description:
-      name: charcode
-      url: "https://pub.dartlang.org"
-    source: hosted
-    version: "1.3.1"
-  clock:
-    dependency: transitive
-    description:
-      name: clock
-      url: "https://pub.dartlang.org"
-    source: hosted
-    version: "1.1.0"
-  collection:
-    dependency: transitive
-    description:
-      name: collection
-      url: "https://pub.dartlang.org"
-    source: hosted
-    version: "1.15.0"
-  crypto:
-    dependency: transitive
-    description:
-      name: crypto
-      url: "https://pub.dartlang.org"
-    source: hosted
-    version: "3.0.1"
-  cupertino_icons:
-    dependency: "direct main"
-    description:
-      name: cupertino_icons
-      url: "https://pub.dartlang.org"
-    source: hosted
-    version: "1.0.3"
-  dartz:
-    dependency: transitive
-    description:
-      name: dartz
-      url: "https://pub.dartlang.org"
-    source: hosted
-    version: "0.10.0-nullsafety.2"
-  equatable:
-    dependency: transitive
-    description:
-      name: equatable
-      url: "https://pub.dartlang.org"
-    source: hosted
-    version: "2.0.3"
-  fake_async:
-    dependency: transitive
-    description:
-      name: fake_async
-      url: "https://pub.dartlang.org"
-    source: hosted
-    version: "1.2.0"
-  flowy_infra:
-    dependency: transitive
-    description:
-      path: "../../flowy_infra"
-      relative: true
-    source: path
-    version: "0.0.1"
-  flowy_infra_ui:
-    dependency: "direct main"
-    description:
-      path: ".."
-      relative: true
-    source: path
-    version: "0.0.1"
-  flowy_infra_ui_platform_interface:
-    dependency: transitive
-    description:
-      path: "../flowy_infra_ui_platform_interface"
-      relative: true
-    source: path
-    version: "0.0.1"
-  flowy_infra_ui_web:
-    dependency: transitive
-    description:
-      path: "../flowy_infra_ui_web"
-      relative: true
-    source: path
-    version: "0.0.1"
-  flutter:
-    dependency: "direct main"
-    description: flutter
-    source: sdk
-    version: "0.0.0"
-  flutter_lints:
-    dependency: "direct dev"
-    description:
-      name: flutter_lints
-      url: "https://pub.dartlang.org"
-    source: hosted
-    version: "1.0.4"
-  flutter_svg:
-    dependency: transitive
-    description:
-      name: flutter_svg
-      url: "https://pub.dartlang.org"
-    source: hosted
-    version: "0.22.0"
-  flutter_test:
-    dependency: "direct dev"
-    description: flutter
-    source: sdk
-    version: "0.0.0"
-  flutter_web_plugins:
-    dependency: transitive
-    description: flutter
-    source: sdk
-    version: "0.0.0"
-  js:
-    dependency: transitive
-    description:
-      name: js
-      url: "https://pub.dartlang.org"
-    source: hosted
-    version: "0.6.3"
-  lint:
-    dependency: transitive
-    description:
-      name: lint
-      url: "https://pub.dartlang.org"
-    source: hosted
-    version: "1.5.3"
-  lints:
-    dependency: transitive
-    description:
-      name: lints
-      url: "https://pub.dartlang.org"
-    source: hosted
-    version: "1.0.1"
-  loading_indicator:
-    dependency: transitive
-    description:
-      name: loading_indicator
-      url: "https://pub.dartlang.org"
-    source: hosted
-    version: "3.0.1"
-  matcher:
-    dependency: transitive
-    description:
-      name: matcher
-      url: "https://pub.dartlang.org"
-    source: hosted
-    version: "0.12.11"
-  meta:
-    dependency: transitive
-    description:
-      name: meta
-      url: "https://pub.dartlang.org"
-    source: hosted
-    version: "1.7.0"
-  nested:
-    dependency: transitive
-    description:
-      name: nested
-      url: "https://pub.dartlang.org"
-    source: hosted
-    version: "1.0.0"
-  path:
-    dependency: transitive
-    description:
-      name: path
-      url: "https://pub.dartlang.org"
-    source: hosted
-    version: "1.8.0"
-  path_drawing:
-    dependency: transitive
-    description:
-      name: path_drawing
-      url: "https://pub.dartlang.org"
-    source: hosted
-    version: "0.5.1+1"
-  path_parsing:
-    dependency: transitive
-    description:
-      name: path_parsing
-      url: "https://pub.dartlang.org"
-    source: hosted
-    version: "0.2.1"
-  petitparser:
-    dependency: transitive
-    description:
-      name: petitparser
-      url: "https://pub.dartlang.org"
-    source: hosted
-    version: "4.4.0"
-  plugin_platform_interface:
-    dependency: transitive
-    description:
-      name: plugin_platform_interface
-      url: "https://pub.dartlang.org"
-    source: hosted
-    version: "2.0.1"
-  provider:
-    dependency: transitive
-    description:
-      name: provider
-      url: "https://pub.dartlang.org"
-    source: hosted
-    version: "6.0.1"
-  sky_engine:
-    dependency: transitive
-    description: flutter
-    source: sdk
-    version: "0.0.99"
-  source_span:
-    dependency: transitive
-    description:
-      name: source_span
-      url: "https://pub.dartlang.org"
-    source: hosted
-    version: "1.8.1"
-  stack_trace:
-    dependency: transitive
-    description:
-      name: stack_trace
-      url: "https://pub.dartlang.org"
-    source: hosted
-    version: "1.10.0"
-  stream_channel:
-    dependency: transitive
-    description:
-      name: stream_channel
-      url: "https://pub.dartlang.org"
-    source: hosted
-    version: "2.1.0"
-  string_scanner:
-    dependency: transitive
-    description:
-      name: string_scanner
-      url: "https://pub.dartlang.org"
-    source: hosted
-    version: "1.1.0"
-  styled_widget:
-    dependency: transitive
-    description:
-      name: styled_widget
-      url: "https://pub.dartlang.org"
-    source: hosted
-    version: "0.3.1+2"
-  term_glyph:
-    dependency: transitive
-    description:
-      name: term_glyph
-      url: "https://pub.dartlang.org"
-    source: hosted
-    version: "1.2.0"
-  test_api:
-    dependency: transitive
-    description:
-      name: test_api
-      url: "https://pub.dartlang.org"
-    source: hosted
-    version: "0.4.3"
-  textstyle_extensions:
-    dependency: transitive
-    description:
-      name: textstyle_extensions
-      url: "https://pub.dartlang.org"
-    source: hosted
-    version: "2.0.0-nullsafety"
-  time:
-    dependency: transitive
-    description:
-      name: time
-      url: "https://pub.dartlang.org"
-    source: hosted
-    version: "2.0.0"
-  typed_data:
-    dependency: transitive
-    description:
-      name: typed_data
-      url: "https://pub.dartlang.org"
-    source: hosted
-    version: "1.3.0"
-  uuid:
-    dependency: transitive
-    description:
-      name: uuid
-      url: "https://pub.dartlang.org"
-    source: hosted
-    version: "3.0.4"
-  vector_math:
-    dependency: transitive
-    description:
-      name: vector_math
-      url: "https://pub.dartlang.org"
-    source: hosted
-    version: "2.1.0"
-  xml:
-    dependency: transitive
-    description:
-      name: xml
-      url: "https://pub.dartlang.org"
-    source: hosted
-    version: "5.3.1"
-sdks:
-  dart: ">=2.14.0 <3.0.0"
-  flutter: ">=2.0.0"

+ 0 - 168
frontend/app_flowy/packages/flowy_infra_ui/flowy_infra_ui_platform_interface/pubspec.lock

@@ -1,168 +0,0 @@
-# Generated by pub
-# See https://dart.dev/tools/pub/glossary#lockfile
-packages:
-  async:
-    dependency: transitive
-    description:
-      name: async
-      url: "https://pub.dartlang.org"
-    source: hosted
-    version: "2.6.1"
-  boolean_selector:
-    dependency: transitive
-    description:
-      name: boolean_selector
-      url: "https://pub.dartlang.org"
-    source: hosted
-    version: "2.1.0"
-  characters:
-    dependency: transitive
-    description:
-      name: characters
-      url: "https://pub.dartlang.org"
-    source: hosted
-    version: "1.1.0"
-  charcode:
-    dependency: transitive
-    description:
-      name: charcode
-      url: "https://pub.dartlang.org"
-    source: hosted
-    version: "1.2.0"
-  clock:
-    dependency: transitive
-    description:
-      name: clock
-      url: "https://pub.dartlang.org"
-    source: hosted
-    version: "1.1.0"
-  collection:
-    dependency: transitive
-    description:
-      name: collection
-      url: "https://pub.dartlang.org"
-    source: hosted
-    version: "1.15.0"
-  fake_async:
-    dependency: transitive
-    description:
-      name: fake_async
-      url: "https://pub.dartlang.org"
-    source: hosted
-    version: "1.2.0"
-  flutter:
-    dependency: "direct main"
-    description: flutter
-    source: sdk
-    version: "0.0.0"
-  flutter_lints:
-    dependency: "direct dev"
-    description:
-      name: flutter_lints
-      url: "https://pub.dartlang.org"
-    source: hosted
-    version: "1.0.4"
-  flutter_test:
-    dependency: "direct dev"
-    description: flutter
-    source: sdk
-    version: "0.0.0"
-  lints:
-    dependency: transitive
-    description:
-      name: lints
-      url: "https://pub.dartlang.org"
-    source: hosted
-    version: "1.0.1"
-  matcher:
-    dependency: transitive
-    description:
-      name: matcher
-      url: "https://pub.dartlang.org"
-    source: hosted
-    version: "0.12.10"
-  meta:
-    dependency: transitive
-    description:
-      name: meta
-      url: "https://pub.dartlang.org"
-    source: hosted
-    version: "1.3.0"
-  path:
-    dependency: transitive
-    description:
-      name: path
-      url: "https://pub.dartlang.org"
-    source: hosted
-    version: "1.8.0"
-  plugin_platform_interface:
-    dependency: "direct main"
-    description:
-      name: plugin_platform_interface
-      url: "https://pub.dartlang.org"
-    source: hosted
-    version: "2.0.1"
-  sky_engine:
-    dependency: transitive
-    description: flutter
-    source: sdk
-    version: "0.0.99"
-  source_span:
-    dependency: transitive
-    description:
-      name: source_span
-      url: "https://pub.dartlang.org"
-    source: hosted
-    version: "1.8.1"
-  stack_trace:
-    dependency: transitive
-    description:
-      name: stack_trace
-      url: "https://pub.dartlang.org"
-    source: hosted
-    version: "1.10.0"
-  stream_channel:
-    dependency: transitive
-    description:
-      name: stream_channel
-      url: "https://pub.dartlang.org"
-    source: hosted
-    version: "2.1.0"
-  string_scanner:
-    dependency: transitive
-    description:
-      name: string_scanner
-      url: "https://pub.dartlang.org"
-    source: hosted
-    version: "1.1.0"
-  term_glyph:
-    dependency: transitive
-    description:
-      name: term_glyph
-      url: "https://pub.dartlang.org"
-    source: hosted
-    version: "1.2.0"
-  test_api:
-    dependency: transitive
-    description:
-      name: test_api
-      url: "https://pub.dartlang.org"
-    source: hosted
-    version: "0.3.0"
-  typed_data:
-    dependency: transitive
-    description:
-      name: typed_data
-      url: "https://pub.dartlang.org"
-    source: hosted
-    version: "1.3.0"
-  vector_math:
-    dependency: transitive
-    description:
-      name: vector_math
-      url: "https://pub.dartlang.org"
-    source: hosted
-    version: "2.1.0"
-sdks:
-  dart: ">=2.12.0 <3.0.0"
-  flutter: ">=1.17.0"

+ 0 - 187
frontend/app_flowy/packages/flowy_infra_ui/flowy_infra_ui_web/pubspec.lock

@@ -1,187 +0,0 @@
-# Generated by pub
-# See https://dart.dev/tools/pub/glossary#lockfile
-packages:
-  async:
-    dependency: transitive
-    description:
-      name: async
-      url: "https://pub.dartlang.org"
-    source: hosted
-    version: "2.6.1"
-  boolean_selector:
-    dependency: transitive
-    description:
-      name: boolean_selector
-      url: "https://pub.dartlang.org"
-    source: hosted
-    version: "2.1.0"
-  characters:
-    dependency: transitive
-    description:
-      name: characters
-      url: "https://pub.dartlang.org"
-    source: hosted
-    version: "1.1.0"
-  charcode:
-    dependency: transitive
-    description:
-      name: charcode
-      url: "https://pub.dartlang.org"
-    source: hosted
-    version: "1.2.0"
-  clock:
-    dependency: transitive
-    description:
-      name: clock
-      url: "https://pub.dartlang.org"
-    source: hosted
-    version: "1.1.0"
-  collection:
-    dependency: transitive
-    description:
-      name: collection
-      url: "https://pub.dartlang.org"
-    source: hosted
-    version: "1.15.0"
-  fake_async:
-    dependency: transitive
-    description:
-      name: fake_async
-      url: "https://pub.dartlang.org"
-    source: hosted
-    version: "1.2.0"
-  flowy_infra_ui_platform_interface:
-    dependency: "direct main"
-    description:
-      path: "../flowy_infra_ui_platform_interface"
-      relative: true
-    source: path
-    version: "0.0.1"
-  flutter:
-    dependency: transitive
-    description: flutter
-    source: sdk
-    version: "0.0.0"
-  flutter_lints:
-    dependency: "direct dev"
-    description:
-      name: flutter_lints
-      url: "https://pub.dartlang.org"
-    source: hosted
-    version: "1.0.4"
-  flutter_test:
-    dependency: "direct dev"
-    description: flutter
-    source: sdk
-    version: "0.0.0"
-  flutter_web_plugins:
-    dependency: "direct main"
-    description: flutter
-    source: sdk
-    version: "0.0.0"
-  js:
-    dependency: transitive
-    description:
-      name: js
-      url: "https://pub.dartlang.org"
-    source: hosted
-    version: "0.6.3"
-  lints:
-    dependency: transitive
-    description:
-      name: lints
-      url: "https://pub.dartlang.org"
-    source: hosted
-    version: "1.0.1"
-  matcher:
-    dependency: transitive
-    description:
-      name: matcher
-      url: "https://pub.dartlang.org"
-    source: hosted
-    version: "0.12.10"
-  meta:
-    dependency: transitive
-    description:
-      name: meta
-      url: "https://pub.dartlang.org"
-    source: hosted
-    version: "1.3.0"
-  path:
-    dependency: transitive
-    description:
-      name: path
-      url: "https://pub.dartlang.org"
-    source: hosted
-    version: "1.8.0"
-  plugin_platform_interface:
-    dependency: transitive
-    description:
-      name: plugin_platform_interface
-      url: "https://pub.dartlang.org"
-    source: hosted
-    version: "2.0.1"
-  sky_engine:
-    dependency: transitive
-    description: flutter
-    source: sdk
-    version: "0.0.99"
-  source_span:
-    dependency: transitive
-    description:
-      name: source_span
-      url: "https://pub.dartlang.org"
-    source: hosted
-    version: "1.8.1"
-  stack_trace:
-    dependency: transitive
-    description:
-      name: stack_trace
-      url: "https://pub.dartlang.org"
-    source: hosted
-    version: "1.10.0"
-  stream_channel:
-    dependency: transitive
-    description:
-      name: stream_channel
-      url: "https://pub.dartlang.org"
-    source: hosted
-    version: "2.1.0"
-  string_scanner:
-    dependency: transitive
-    description:
-      name: string_scanner
-      url: "https://pub.dartlang.org"
-    source: hosted
-    version: "1.1.0"
-  term_glyph:
-    dependency: transitive
-    description:
-      name: term_glyph
-      url: "https://pub.dartlang.org"
-    source: hosted
-    version: "1.2.0"
-  test_api:
-    dependency: transitive
-    description:
-      name: test_api
-      url: "https://pub.dartlang.org"
-    source: hosted
-    version: "0.3.0"
-  typed_data:
-    dependency: transitive
-    description:
-      name: typed_data
-      url: "https://pub.dartlang.org"
-    source: hosted
-    version: "1.3.0"
-  vector_math:
-    dependency: transitive
-    description:
-      name: vector_math
-      url: "https://pub.dartlang.org"
-    source: hosted
-    version: "2.1.0"
-sdks:
-  dart: ">=2.12.0 <3.0.0"
-  flutter: ">=1.17.0"

+ 0 - 320
frontend/app_flowy/packages/flowy_infra_ui/pubspec.lock

@@ -1,320 +0,0 @@
-# Generated by pub
-# See https://dart.dev/tools/pub/glossary#lockfile
-packages:
-  animations:
-    dependency: "direct main"
-    description:
-      name: animations
-      url: "https://pub.dartlang.org"
-    source: hosted
-    version: "2.0.1"
-  async:
-    dependency: transitive
-    description:
-      name: async
-      url: "https://pub.dartlang.org"
-    source: hosted
-    version: "2.8.2"
-  boolean_selector:
-    dependency: transitive
-    description:
-      name: boolean_selector
-      url: "https://pub.dartlang.org"
-    source: hosted
-    version: "2.1.0"
-  characters:
-    dependency: transitive
-    description:
-      name: characters
-      url: "https://pub.dartlang.org"
-    source: hosted
-    version: "1.2.0"
-  charcode:
-    dependency: transitive
-    description:
-      name: charcode
-      url: "https://pub.dartlang.org"
-    source: hosted
-    version: "1.3.1"
-  clock:
-    dependency: transitive
-    description:
-      name: clock
-      url: "https://pub.dartlang.org"
-    source: hosted
-    version: "1.1.0"
-  collection:
-    dependency: transitive
-    description:
-      name: collection
-      url: "https://pub.dartlang.org"
-    source: hosted
-    version: "1.15.0"
-  crypto:
-    dependency: transitive
-    description:
-      name: crypto
-      url: "https://pub.dartlang.org"
-    source: hosted
-    version: "3.0.1"
-  dartz:
-    dependency: "direct main"
-    description:
-      name: dartz
-      url: "https://pub.dartlang.org"
-    source: hosted
-    version: "0.10.0-nullsafety.2"
-  equatable:
-    dependency: "direct main"
-    description:
-      name: equatable
-      url: "https://pub.dartlang.org"
-    source: hosted
-    version: "2.0.3"
-  fake_async:
-    dependency: transitive
-    description:
-      name: fake_async
-      url: "https://pub.dartlang.org"
-    source: hosted
-    version: "1.2.0"
-  flowy_infra:
-    dependency: "direct main"
-    description:
-      path: "../flowy_infra"
-      relative: true
-    source: path
-    version: "0.0.1"
-  flowy_infra_ui_platform_interface:
-    dependency: "direct main"
-    description:
-      path: flowy_infra_ui_platform_interface
-      relative: true
-    source: path
-    version: "0.0.1"
-  flowy_infra_ui_web:
-    dependency: "direct main"
-    description:
-      path: flowy_infra_ui_web
-      relative: true
-    source: path
-    version: "0.0.1"
-  flutter:
-    dependency: "direct main"
-    description: flutter
-    source: sdk
-    version: "0.0.0"
-  flutter_lints:
-    dependency: "direct dev"
-    description:
-      name: flutter_lints
-      url: "https://pub.dartlang.org"
-    source: hosted
-    version: "1.0.4"
-  flutter_svg:
-    dependency: transitive
-    description:
-      name: flutter_svg
-      url: "https://pub.dartlang.org"
-    source: hosted
-    version: "0.22.0"
-  flutter_test:
-    dependency: "direct dev"
-    description: flutter
-    source: sdk
-    version: "0.0.0"
-  flutter_web_plugins:
-    dependency: transitive
-    description: flutter
-    source: sdk
-    version: "0.0.0"
-  js:
-    dependency: transitive
-    description:
-      name: js
-      url: "https://pub.dartlang.org"
-    source: hosted
-    version: "0.6.3"
-  lint:
-    dependency: transitive
-    description:
-      name: lint
-      url: "https://pub.dartlang.org"
-    source: hosted
-    version: "1.5.3"
-  lints:
-    dependency: transitive
-    description:
-      name: lints
-      url: "https://pub.dartlang.org"
-    source: hosted
-    version: "1.0.1"
-  loading_indicator:
-    dependency: "direct main"
-    description:
-      name: loading_indicator
-      url: "https://pub.dartlang.org"
-    source: hosted
-    version: "3.0.1"
-  matcher:
-    dependency: transitive
-    description:
-      name: matcher
-      url: "https://pub.dartlang.org"
-    source: hosted
-    version: "0.12.11"
-  meta:
-    dependency: transitive
-    description:
-      name: meta
-      url: "https://pub.dartlang.org"
-    source: hosted
-    version: "1.7.0"
-  nested:
-    dependency: transitive
-    description:
-      name: nested
-      url: "https://pub.dartlang.org"
-    source: hosted
-    version: "1.0.0"
-  path:
-    dependency: transitive
-    description:
-      name: path
-      url: "https://pub.dartlang.org"
-    source: hosted
-    version: "1.8.0"
-  path_drawing:
-    dependency: transitive
-    description:
-      name: path_drawing
-      url: "https://pub.dartlang.org"
-    source: hosted
-    version: "0.5.1+1"
-  path_parsing:
-    dependency: transitive
-    description:
-      name: path_parsing
-      url: "https://pub.dartlang.org"
-    source: hosted
-    version: "0.2.1"
-  petitparser:
-    dependency: transitive
-    description:
-      name: petitparser
-      url: "https://pub.dartlang.org"
-    source: hosted
-    version: "4.4.0"
-  plugin_platform_interface:
-    dependency: transitive
-    description:
-      name: plugin_platform_interface
-      url: "https://pub.dartlang.org"
-    source: hosted
-    version: "2.0.1"
-  provider:
-    dependency: "direct main"
-    description:
-      name: provider
-      url: "https://pub.dartlang.org"
-    source: hosted
-    version: "6.0.1"
-  sky_engine:
-    dependency: transitive
-    description: flutter
-    source: sdk
-    version: "0.0.99"
-  source_span:
-    dependency: transitive
-    description:
-      name: source_span
-      url: "https://pub.dartlang.org"
-    source: hosted
-    version: "1.8.1"
-  stack_trace:
-    dependency: transitive
-    description:
-      name: stack_trace
-      url: "https://pub.dartlang.org"
-    source: hosted
-    version: "1.10.0"
-  stream_channel:
-    dependency: transitive
-    description:
-      name: stream_channel
-      url: "https://pub.dartlang.org"
-    source: hosted
-    version: "2.1.0"
-  string_scanner:
-    dependency: transitive
-    description:
-      name: string_scanner
-      url: "https://pub.dartlang.org"
-    source: hosted
-    version: "1.1.0"
-  styled_widget:
-    dependency: "direct main"
-    description:
-      name: styled_widget
-      url: "https://pub.dartlang.org"
-    source: hosted
-    version: "0.3.1+2"
-  term_glyph:
-    dependency: transitive
-    description:
-      name: term_glyph
-      url: "https://pub.dartlang.org"
-    source: hosted
-    version: "1.2.0"
-  test_api:
-    dependency: transitive
-    description:
-      name: test_api
-      url: "https://pub.dartlang.org"
-    source: hosted
-    version: "0.4.3"
-  textstyle_extensions:
-    dependency: "direct main"
-    description:
-      name: textstyle_extensions
-      url: "https://pub.dartlang.org"
-    source: hosted
-    version: "2.0.0-nullsafety"
-  time:
-    dependency: transitive
-    description:
-      name: time
-      url: "https://pub.dartlang.org"
-    source: hosted
-    version: "2.0.0"
-  typed_data:
-    dependency: transitive
-    description:
-      name: typed_data
-      url: "https://pub.dartlang.org"
-    source: hosted
-    version: "1.3.0"
-  uuid:
-    dependency: transitive
-    description:
-      name: uuid
-      url: "https://pub.dartlang.org"
-    source: hosted
-    version: "3.0.4"
-  vector_math:
-    dependency: transitive
-    description:
-      name: vector_math
-      url: "https://pub.dartlang.org"
-    source: hosted
-    version: "2.1.0"
-  xml:
-    dependency: transitive
-    description:
-      name: xml
-      url: "https://pub.dartlang.org"
-    source: hosted
-    version: "5.3.1"
-sdks:
-  dart: ">=2.14.0 <3.0.0"
-  flutter: ">=2.0.0"

+ 0 - 309
frontend/app_flowy/packages/flowy_sdk/example/pubspec.lock

@@ -1,309 +0,0 @@
-# Generated by pub
-# See https://dart.dev/tools/pub/glossary#lockfile
-packages:
-  archive:
-    dependency: transitive
-    description:
-      name: archive
-      url: "https://pub.dartlang.org"
-    source: hosted
-    version: "3.1.6"
-  async:
-    dependency: transitive
-    description:
-      name: async
-      url: "https://pub.dartlang.org"
-    source: hosted
-    version: "2.8.2"
-  boolean_selector:
-    dependency: transitive
-    description:
-      name: boolean_selector
-      url: "https://pub.dartlang.org"
-    source: hosted
-    version: "2.1.0"
-  characters:
-    dependency: transitive
-    description:
-      name: characters
-      url: "https://pub.dartlang.org"
-    source: hosted
-    version: "1.2.0"
-  charcode:
-    dependency: transitive
-    description:
-      name: charcode
-      url: "https://pub.dartlang.org"
-    source: hosted
-    version: "1.3.1"
-  clock:
-    dependency: transitive
-    description:
-      name: clock
-      url: "https://pub.dartlang.org"
-    source: hosted
-    version: "1.1.0"
-  collection:
-    dependency: transitive
-    description:
-      name: collection
-      url: "https://pub.dartlang.org"
-    source: hosted
-    version: "1.15.0"
-  crypto:
-    dependency: transitive
-    description:
-      name: crypto
-      url: "https://pub.dartlang.org"
-    source: hosted
-    version: "3.0.1"
-  cupertino_icons:
-    dependency: "direct main"
-    description:
-      name: cupertino_icons
-      url: "https://pub.dartlang.org"
-    source: hosted
-    version: "1.0.2"
-  dartz:
-    dependency: transitive
-    description:
-      name: dartz
-      url: "https://pub.dartlang.org"
-    source: hosted
-    version: "0.10.0-nullsafety.2"
-  fake_async:
-    dependency: transitive
-    description:
-      name: fake_async
-      url: "https://pub.dartlang.org"
-    source: hosted
-    version: "1.2.0"
-  ffi:
-    dependency: transitive
-    description:
-      name: ffi
-      url: "https://pub.dartlang.org"
-    source: hosted
-    version: "1.0.0"
-  file:
-    dependency: transitive
-    description:
-      name: file
-      url: "https://pub.dartlang.org"
-    source: hosted
-    version: "6.1.2"
-  fixnum:
-    dependency: transitive
-    description:
-      name: fixnum
-      url: "https://pub.dartlang.org"
-    source: hosted
-    version: "1.0.0"
-  flowy_sdk:
-    dependency: "direct main"
-    description:
-      path: ".."
-      relative: true
-    source: path
-    version: "0.0.1"
-  flutter:
-    dependency: "direct main"
-    description: flutter
-    source: sdk
-    version: "0.0.0"
-  flutter_driver:
-    dependency: transitive
-    description: flutter
-    source: sdk
-    version: "0.0.0"
-  flutter_lints:
-    dependency: "direct dev"
-    description:
-      name: flutter_lints
-      url: "https://pub.dartlang.org"
-    source: hosted
-    version: "1.0.3"
-  flutter_test:
-    dependency: "direct dev"
-    description: flutter
-    source: sdk
-    version: "0.0.0"
-  freezed_annotation:
-    dependency: transitive
-    description:
-      name: freezed_annotation
-      url: "https://pub.dartlang.org"
-    source: hosted
-    version: "0.14.1"
-  fuchsia_remote_debug_protocol:
-    dependency: transitive
-    description: flutter
-    source: sdk
-    version: "0.0.0"
-  integration_test:
-    dependency: "direct dev"
-    description: flutter
-    source: sdk
-    version: "0.0.0"
-  isolates:
-    dependency: transitive
-    description:
-      name: isolates
-      url: "https://pub.dartlang.org"
-    source: hosted
-    version: "3.0.3+8"
-  json_annotation:
-    dependency: transitive
-    description:
-      name: json_annotation
-      url: "https://pub.dartlang.org"
-    source: hosted
-    version: "4.0.1"
-  lints:
-    dependency: transitive
-    description:
-      name: lints
-      url: "https://pub.dartlang.org"
-    source: hosted
-    version: "1.0.1"
-  logger:
-    dependency: transitive
-    description:
-      name: logger
-      url: "https://pub.dartlang.org"
-    source: hosted
-    version: "1.0.0"
-  matcher:
-    dependency: transitive
-    description:
-      name: matcher
-      url: "https://pub.dartlang.org"
-    source: hosted
-    version: "0.12.11"
-  material_color_utilities:
-    dependency: transitive
-    description:
-      name: material_color_utilities
-      url: "https://pub.dartlang.org"
-    source: hosted
-    version: "0.1.3"
-  meta:
-    dependency: transitive
-    description:
-      name: meta
-      url: "https://pub.dartlang.org"
-    source: hosted
-    version: "1.7.0"
-  path:
-    dependency: transitive
-    description:
-      name: path
-      url: "https://pub.dartlang.org"
-    source: hosted
-    version: "1.8.0"
-  platform:
-    dependency: transitive
-    description:
-      name: platform
-      url: "https://pub.dartlang.org"
-    source: hosted
-    version: "3.1.0"
-  process:
-    dependency: transitive
-    description:
-      name: process
-      url: "https://pub.dartlang.org"
-    source: hosted
-    version: "4.2.4"
-  protobuf:
-    dependency: transitive
-    description:
-      name: protobuf
-      url: "https://pub.dartlang.org"
-    source: hosted
-    version: "2.0.0"
-  sky_engine:
-    dependency: transitive
-    description: flutter
-    source: sdk
-    version: "0.0.99"
-  source_span:
-    dependency: transitive
-    description:
-      name: source_span
-      url: "https://pub.dartlang.org"
-    source: hosted
-    version: "1.8.1"
-  stack_trace:
-    dependency: transitive
-    description:
-      name: stack_trace
-      url: "https://pub.dartlang.org"
-    source: hosted
-    version: "1.10.0"
-  stream_channel:
-    dependency: transitive
-    description:
-      name: stream_channel
-      url: "https://pub.dartlang.org"
-    source: hosted
-    version: "2.1.0"
-  string_scanner:
-    dependency: transitive
-    description:
-      name: string_scanner
-      url: "https://pub.dartlang.org"
-    source: hosted
-    version: "1.1.0"
-  sync_http:
-    dependency: transitive
-    description:
-      name: sync_http
-      url: "https://pub.dartlang.org"
-    source: hosted
-    version: "0.3.0"
-  term_glyph:
-    dependency: transitive
-    description:
-      name: term_glyph
-      url: "https://pub.dartlang.org"
-    source: hosted
-    version: "1.2.0"
-  test_api:
-    dependency: transitive
-    description:
-      name: test_api
-      url: "https://pub.dartlang.org"
-    source: hosted
-    version: "0.4.8"
-  typed_data:
-    dependency: transitive
-    description:
-      name: typed_data
-      url: "https://pub.dartlang.org"
-    source: hosted
-    version: "1.3.0"
-  vector_math:
-    dependency: transitive
-    description:
-      name: vector_math
-      url: "https://pub.dartlang.org"
-    source: hosted
-    version: "2.1.1"
-  vm_service:
-    dependency: transitive
-    description:
-      name: vm_service
-      url: "https://pub.dartlang.org"
-    source: hosted
-    version: "7.5.0"
-  webdriver:
-    dependency: transitive
-    description:
-      name: webdriver
-      url: "https://pub.dartlang.org"
-    source: hosted
-    version: "3.0.0"
-sdks:
-  dart: ">=2.14.0 <3.0.0"
-  flutter: ">=1.17.0"

+ 0 - 504
frontend/app_flowy/packages/flowy_sdk/pubspec.lock

@@ -1,504 +0,0 @@
-# Generated by pub
-# See https://dart.dev/tools/pub/glossary#lockfile
-packages:
-  _fe_analyzer_shared:
-    dependency: transitive
-    description:
-      name: _fe_analyzer_shared
-      url: "https://pub.dartlang.org"
-    source: hosted
-    version: "20.0.0"
-  analyzer:
-    dependency: transitive
-    description:
-      name: analyzer
-      url: "https://pub.dartlang.org"
-    source: hosted
-    version: "1.4.0"
-  args:
-    dependency: transitive
-    description:
-      name: args
-      url: "https://pub.dartlang.org"
-    source: hosted
-    version: "1.6.0"
-  async:
-    dependency: transitive
-    description:
-      name: async
-      url: "https://pub.dartlang.org"
-    source: hosted
-    version: "2.8.2"
-  boolean_selector:
-    dependency: transitive
-    description:
-      name: boolean_selector
-      url: "https://pub.dartlang.org"
-    source: hosted
-    version: "2.1.0"
-  build:
-    dependency: transitive
-    description:
-      name: build
-      url: "https://pub.dartlang.org"
-    source: hosted
-    version: "2.0.0"
-  build_config:
-    dependency: transitive
-    description:
-      name: build_config
-      url: "https://pub.dartlang.org"
-    source: hosted
-    version: "0.4.7"
-  build_daemon:
-    dependency: transitive
-    description:
-      name: build_daemon
-      url: "https://pub.dartlang.org"
-    source: hosted
-    version: "2.1.10"
-  build_resolvers:
-    dependency: transitive
-    description:
-      name: build_resolvers
-      url: "https://pub.dartlang.org"
-    source: hosted
-    version: "2.0.0"
-  build_runner:
-    dependency: "direct dev"
-    description:
-      name: build_runner
-      url: "https://pub.dartlang.org"
-    source: hosted
-    version: "1.12.2"
-  build_runner_core:
-    dependency: transitive
-    description:
-      name: build_runner_core
-      url: "https://pub.dartlang.org"
-    source: hosted
-    version: "6.1.12"
-  built_collection:
-    dependency: transitive
-    description:
-      name: built_collection
-      url: "https://pub.dartlang.org"
-    source: hosted
-    version: "5.1.0"
-  built_value:
-    dependency: transitive
-    description:
-      name: built_value
-      url: "https://pub.dartlang.org"
-    source: hosted
-    version: "8.1.0"
-  characters:
-    dependency: transitive
-    description:
-      name: characters
-      url: "https://pub.dartlang.org"
-    source: hosted
-    version: "1.2.0"
-  charcode:
-    dependency: transitive
-    description:
-      name: charcode
-      url: "https://pub.dartlang.org"
-    source: hosted
-    version: "1.3.1"
-  checked_yaml:
-    dependency: transitive
-    description:
-      name: checked_yaml
-      url: "https://pub.dartlang.org"
-    source: hosted
-    version: "2.0.1"
-  cli_util:
-    dependency: transitive
-    description:
-      name: cli_util
-      url: "https://pub.dartlang.org"
-    source: hosted
-    version: "0.3.0"
-  clock:
-    dependency: transitive
-    description:
-      name: clock
-      url: "https://pub.dartlang.org"
-    source: hosted
-    version: "1.1.0"
-  code_builder:
-    dependency: transitive
-    description:
-      name: code_builder
-      url: "https://pub.dartlang.org"
-    source: hosted
-    version: "3.6.0"
-  collection:
-    dependency: transitive
-    description:
-      name: collection
-      url: "https://pub.dartlang.org"
-    source: hosted
-    version: "1.15.0"
-  convert:
-    dependency: transitive
-    description:
-      name: convert
-      url: "https://pub.dartlang.org"
-    source: hosted
-    version: "3.0.0"
-  crypto:
-    dependency: transitive
-    description:
-      name: crypto
-      url: "https://pub.dartlang.org"
-    source: hosted
-    version: "3.0.1"
-  dart_style:
-    dependency: transitive
-    description:
-      name: dart_style
-      url: "https://pub.dartlang.org"
-    source: hosted
-    version: "2.0.0"
-  dartz:
-    dependency: "direct main"
-    description:
-      name: dartz
-      url: "https://pub.dartlang.org"
-    source: hosted
-    version: "0.10.0-nullsafety.2"
-  fake_async:
-    dependency: transitive
-    description:
-      name: fake_async
-      url: "https://pub.dartlang.org"
-    source: hosted
-    version: "1.2.0"
-  ffi:
-    dependency: "direct main"
-    description:
-      name: ffi
-      url: "https://pub.dartlang.org"
-    source: hosted
-    version: "1.0.0"
-  file:
-    dependency: transitive
-    description:
-      name: file
-      url: "https://pub.dartlang.org"
-    source: hosted
-    version: "6.1.0"
-  fixnum:
-    dependency: transitive
-    description:
-      name: fixnum
-      url: "https://pub.dartlang.org"
-    source: hosted
-    version: "1.0.0"
-  flutter:
-    dependency: "direct main"
-    description: flutter
-    source: sdk
-    version: "0.0.0"
-  flutter_lints:
-    dependency: "direct dev"
-    description:
-      name: flutter_lints
-      url: "https://pub.dartlang.org"
-    source: hosted
-    version: "1.0.3"
-  flutter_test:
-    dependency: "direct dev"
-    description: flutter
-    source: sdk
-    version: "0.0.0"
-  freezed:
-    dependency: "direct dev"
-    description:
-      name: freezed
-      url: "https://pub.dartlang.org"
-    source: hosted
-    version: "0.14.1+2"
-  freezed_annotation:
-    dependency: "direct main"
-    description:
-      name: freezed_annotation
-      url: "https://pub.dartlang.org"
-    source: hosted
-    version: "0.14.1"
-  glob:
-    dependency: transitive
-    description:
-      name: glob
-      url: "https://pub.dartlang.org"
-    source: hosted
-    version: "2.0.1"
-  graphs:
-    dependency: transitive
-    description:
-      name: graphs
-      url: "https://pub.dartlang.org"
-    source: hosted
-    version: "1.0.0"
-  http_multi_server:
-    dependency: transitive
-    description:
-      name: http_multi_server
-      url: "https://pub.dartlang.org"
-    source: hosted
-    version: "2.2.0"
-  http_parser:
-    dependency: transitive
-    description:
-      name: http_parser
-      url: "https://pub.dartlang.org"
-    source: hosted
-    version: "3.1.4"
-  io:
-    dependency: transitive
-    description:
-      name: io
-      url: "https://pub.dartlang.org"
-    source: hosted
-    version: "0.3.4"
-  isolates:
-    dependency: "direct main"
-    description:
-      name: isolates
-      url: "https://pub.dartlang.org"
-    source: hosted
-    version: "3.0.3+8"
-  js:
-    dependency: transitive
-    description:
-      name: js
-      url: "https://pub.dartlang.org"
-    source: hosted
-    version: "0.6.3"
-  json_annotation:
-    dependency: transitive
-    description:
-      name: json_annotation
-      url: "https://pub.dartlang.org"
-    source: hosted
-    version: "4.0.1"
-  lints:
-    dependency: transitive
-    description:
-      name: lints
-      url: "https://pub.dartlang.org"
-    source: hosted
-    version: "1.0.1"
-  logger:
-    dependency: "direct main"
-    description:
-      name: logger
-      url: "https://pub.dartlang.org"
-    source: hosted
-    version: "1.0.0"
-  logging:
-    dependency: transitive
-    description:
-      name: logging
-      url: "https://pub.dartlang.org"
-    source: hosted
-    version: "1.0.1"
-  matcher:
-    dependency: transitive
-    description:
-      name: matcher
-      url: "https://pub.dartlang.org"
-    source: hosted
-    version: "0.12.11"
-  material_color_utilities:
-    dependency: transitive
-    description:
-      name: material_color_utilities
-      url: "https://pub.dartlang.org"
-    source: hosted
-    version: "0.1.3"
-  meta:
-    dependency: transitive
-    description:
-      name: meta
-      url: "https://pub.dartlang.org"
-    source: hosted
-    version: "1.7.0"
-  mime:
-    dependency: transitive
-    description:
-      name: mime
-      url: "https://pub.dartlang.org"
-    source: hosted
-    version: "0.9.7"
-  package_config:
-    dependency: transitive
-    description:
-      name: package_config
-      url: "https://pub.dartlang.org"
-    source: hosted
-    version: "2.0.0"
-  path:
-    dependency: transitive
-    description:
-      name: path
-      url: "https://pub.dartlang.org"
-    source: hosted
-    version: "1.8.0"
-  pedantic:
-    dependency: transitive
-    description:
-      name: pedantic
-      url: "https://pub.dartlang.org"
-    source: hosted
-    version: "1.11.0"
-  pool:
-    dependency: transitive
-    description:
-      name: pool
-      url: "https://pub.dartlang.org"
-    source: hosted
-    version: "1.5.0"
-  protobuf:
-    dependency: "direct main"
-    description:
-      name: protobuf
-      url: "https://pub.dartlang.org"
-    source: hosted
-    version: "2.0.0"
-  pub_semver:
-    dependency: transitive
-    description:
-      name: pub_semver
-      url: "https://pub.dartlang.org"
-    source: hosted
-    version: "2.0.0"
-  pubspec_parse:
-    dependency: transitive
-    description:
-      name: pubspec_parse
-      url: "https://pub.dartlang.org"
-    source: hosted
-    version: "1.0.0"
-  shelf:
-    dependency: transitive
-    description:
-      name: shelf
-      url: "https://pub.dartlang.org"
-    source: hosted
-    version: "0.7.9"
-  shelf_web_socket:
-    dependency: transitive
-    description:
-      name: shelf_web_socket
-      url: "https://pub.dartlang.org"
-    source: hosted
-    version: "0.2.3"
-  sky_engine:
-    dependency: transitive
-    description: flutter
-    source: sdk
-    version: "0.0.99"
-  source_gen:
-    dependency: transitive
-    description:
-      name: source_gen
-      url: "https://pub.dartlang.org"
-    source: hosted
-    version: "1.0.0"
-  source_span:
-    dependency: transitive
-    description:
-      name: source_span
-      url: "https://pub.dartlang.org"
-    source: hosted
-    version: "1.8.1"
-  stack_trace:
-    dependency: transitive
-    description:
-      name: stack_trace
-      url: "https://pub.dartlang.org"
-    source: hosted
-    version: "1.10.0"
-  stream_channel:
-    dependency: transitive
-    description:
-      name: stream_channel
-      url: "https://pub.dartlang.org"
-    source: hosted
-    version: "2.1.0"
-  stream_transform:
-    dependency: transitive
-    description:
-      name: stream_transform
-      url: "https://pub.dartlang.org"
-    source: hosted
-    version: "2.0.0"
-  string_scanner:
-    dependency: transitive
-    description:
-      name: string_scanner
-      url: "https://pub.dartlang.org"
-    source: hosted
-    version: "1.1.0"
-  term_glyph:
-    dependency: transitive
-    description:
-      name: term_glyph
-      url: "https://pub.dartlang.org"
-    source: hosted
-    version: "1.2.0"
-  test_api:
-    dependency: transitive
-    description:
-      name: test_api
-      url: "https://pub.dartlang.org"
-    source: hosted
-    version: "0.4.8"
-  timing:
-    dependency: transitive
-    description:
-      name: timing
-      url: "https://pub.dartlang.org"
-    source: hosted
-    version: "0.1.1+3"
-  typed_data:
-    dependency: transitive
-    description:
-      name: typed_data
-      url: "https://pub.dartlang.org"
-    source: hosted
-    version: "1.3.0"
-  vector_math:
-    dependency: transitive
-    description:
-      name: vector_math
-      url: "https://pub.dartlang.org"
-    source: hosted
-    version: "2.1.1"
-  watcher:
-    dependency: transitive
-    description:
-      name: watcher
-      url: "https://pub.dartlang.org"
-    source: hosted
-    version: "1.0.0"
-  web_socket_channel:
-    dependency: transitive
-    description:
-      name: web_socket_channel
-      url: "https://pub.dartlang.org"
-    source: hosted
-    version: "1.2.0"
-  yaml:
-    dependency: transitive
-    description:
-      name: yaml
-      url: "https://pub.dartlang.org"
-    source: hosted
-    version: "3.1.0"
-sdks:
-  dart: ">=2.14.0 <3.0.0"
-  flutter: ">=1.17.0"