Forráskód Böngészése

chore: replace FieldPB with GridField

appflowy 2 éve
szülő
commit
54d6f3709e
39 módosított fájl, 338 hozzáadás és 278 törlés
  1. 2 1
      frontend/app_flowy/lib/plugins/board/application/board_bloc.dart
  2. 15 30
      frontend/app_flowy/lib/plugins/board/application/board_data_controller.dart
  3. 3 7
      frontend/app_flowy/lib/plugins/board/application/card/board_date_cell_bloc.dart
  4. 5 5
      frontend/app_flowy/lib/plugins/board/application/card/card_bloc.dart
  5. 4 4
      frontend/app_flowy/lib/plugins/board/application/card/card_data_controller.dart
  6. 8 8
      frontend/app_flowy/lib/plugins/board/presentation/board_page.dart
  7. 5 5
      frontend/app_flowy/lib/plugins/board/presentation/toolbar/board_setting.dart
  8. 2 2
      frontend/app_flowy/lib/plugins/board/presentation/toolbar/board_toolbar.dart
  9. 2 2
      frontend/app_flowy/lib/plugins/grid/application/block/block_cache.dart
  10. 13 12
      frontend/app_flowy/lib/plugins/grid/application/cell/cell_service/cell_controller.dart
  11. 4 4
      frontend/app_flowy/lib/plugins/grid/application/cell/cell_service/cell_service.dart
  12. 1 1
      frontend/app_flowy/lib/plugins/grid/application/cell/date_cal_bloc.dart
  13. 3 7
      frontend/app_flowy/lib/plugins/grid/application/cell/date_cell_bloc.dart
  14. 1 1
      frontend/app_flowy/lib/plugins/grid/application/cell/select_option_service.dart
  15. 118 47
      frontend/app_flowy/lib/plugins/grid/application/field/field_cache.dart
  16. 2 0
      frontend/app_flowy/lib/plugins/grid/application/field/field_service.dart
  17. 1 0
      frontend/app_flowy/lib/plugins/grid/application/field/type_option/type_option_context.dart
  18. 4 3
      frontend/app_flowy/lib/plugins/grid/application/field/type_option/type_option_data_controller.dart
  19. 6 4
      frontend/app_flowy/lib/plugins/grid/application/grid_bloc.dart
  20. 7 22
      frontend/app_flowy/lib/plugins/grid/application/grid_data_controller.dart
  21. 9 10
      frontend/app_flowy/lib/plugins/grid/application/grid_header_bloc.dart
  22. 11 9
      frontend/app_flowy/lib/plugins/grid/application/row/row_bloc.dart
  23. 4 3
      frontend/app_flowy/lib/plugins/grid/application/row/row_cache.dart
  24. 4 4
      frontend/app_flowy/lib/plugins/grid/application/row/row_data_controller.dart
  25. 15 14
      frontend/app_flowy/lib/plugins/grid/application/setting/group_bloc.dart
  26. 18 14
      frontend/app_flowy/lib/plugins/grid/application/setting/property_bloc.dart
  27. 1 0
      frontend/app_flowy/lib/plugins/grid/application/setting/setting_controller.dart
  28. 12 9
      frontend/app_flowy/lib/plugins/grid/presentation/grid_page.dart
  29. 2 2
      frontend/app_flowy/lib/plugins/grid/presentation/layout/layout.dart
  30. 1 0
      frontend/app_flowy/lib/plugins/grid/presentation/widgets/header/field_cell.dart
  31. 4 4
      frontend/app_flowy/lib/plugins/grid/presentation/widgets/header/grid_header.dart
  32. 6 4
      frontend/app_flowy/lib/plugins/grid/presentation/widgets/header/type_option/builder.dart
  33. 2 2
      frontend/app_flowy/lib/plugins/grid/presentation/widgets/row/grid_row.dart
  34. 3 3
      frontend/app_flowy/lib/plugins/grid/presentation/widgets/row/row_detail.dart
  35. 11 10
      frontend/app_flowy/lib/plugins/grid/presentation/widgets/toolbar/grid_group.dart
  36. 18 14
      frontend/app_flowy/lib/plugins/grid/presentation/widgets/toolbar/grid_property.dart
  37. 3 3
      frontend/app_flowy/lib/plugins/grid/presentation/widgets/toolbar/grid_setting.dart
  38. 3 3
      frontend/app_flowy/lib/plugins/grid/presentation/widgets/toolbar/grid_toolbar.dart
  39. 5 5
      frontend/app_flowy/lib/startup/deps_resolver.dart

+ 2 - 1
frontend/app_flowy/lib/plugins/board/application/board_bloc.dart

@@ -25,7 +25,8 @@ class BoardBloc extends Bloc<BoardEvent, BoardState> {
   final MoveRowFFIService _rowService;
   LinkedHashMap<String, GroupController> groupControllers = LinkedHashMap();
 
-  GridFieldCache get fieldCache => _gridDataController.fieldCache;
+  GridFieldController get fieldController =>
+      _gridDataController.fieldController;
   String get gridId => _gridDataController.gridId;
 
   BoardBloc({required ViewPB view})

+ 15 - 30
frontend/app_flowy/lib/plugins/board/application/board_data_controller.dart

@@ -12,7 +12,7 @@ import 'package:flowy_sdk/protobuf/flowy-grid/protobuf.dart';
 
 import 'board_listener.dart';
 
-typedef OnFieldsChanged = void Function(UnmodifiableListView<FieldPB>);
+typedef OnFieldsChanged = void Function(UnmodifiableListView<GridFieldContext>);
 typedef OnGridChanged = void Function(GridPB);
 typedef DidLoadGroups = void Function(List<GroupPB>);
 typedef OnUpdatedGroup = void Function(List<GroupPB>);
@@ -29,7 +29,7 @@ typedef OnError = void Function(FlowyError);
 class BoardDataController {
   final String gridId;
   final GridFFIService _gridFFIService;
-  final GridFieldCache fieldCache;
+  final GridFieldController fieldController;
   final BoardListener _listener;
 
   // key: the block id
@@ -56,7 +56,7 @@ class BoardDataController {
         // ignore: prefer_collection_literals
         _blocks = LinkedHashMap(),
         _gridFFIService = GridFFIService(gridId: view.id),
-        fieldCache = GridFieldCache(gridId: view.id);
+        fieldController = GridFieldController(gridId: view.id);
 
   void addListener({
     required OnGridChanged onGridChanged,
@@ -75,7 +75,7 @@ class BoardDataController {
     _onRowsChanged = onRowsChanged;
     _onError = onError;
 
-    fieldCache.addListener(onFields: (fields) {
+    fieldController.addListener(onFields: (fields) {
       _onFieldsChanged?.call(UnmodifiableListView(fields));
     });
 
@@ -113,16 +113,15 @@ class BoardDataController {
       () => result.fold(
         (grid) async {
           _onGridChanged?.call(grid);
-
-          return await _loadFields(grid).then((result) {
-            return result.fold(
-              (l) {
-                _loadGroups(grid.blocks);
-                return left(l);
-              },
-              (err) => right(err),
-            );
-          });
+          return await fieldController.loadFields(fieldIds: grid.fields).then(
+                (result) => result.fold(
+                  (l) {
+                    _loadGroups(grid.blocks);
+                    return left(l);
+                  },
+                  (err) => right(err),
+                ),
+              );
         },
         (err) => right(err),
       ),
@@ -136,33 +135,19 @@ class BoardDataController {
 
   Future<void> dispose() async {
     await _gridFFIService.closeGrid();
-    await fieldCache.dispose();
+    await fieldController.dispose();
 
     for (final blockCache in _blocks.values) {
       blockCache.dispose();
     }
   }
 
-  Future<Either<Unit, FlowyError>> _loadFields(GridPB grid) async {
-    final result = await _gridFFIService.getFields(fieldIds: grid.fields);
-    return Future(
-      () => result.fold(
-        (fields) {
-          fieldCache.fields = fields.items;
-          _onFieldsChanged?.call(UnmodifiableListView(fieldCache.fields));
-          return left(unit);
-        },
-        (err) => right(err),
-      ),
-    );
-  }
-
   Future<void> _loadGroups(List<BlockPB> blocks) async {
     for (final block in blocks) {
       final cache = GridBlockCache(
         gridId: gridId,
         block: block,
-        fieldCache: fieldCache,
+        fieldController: fieldController,
       );
 
       cache.addListener(onRowsChanged: (reason) {

+ 3 - 7
frontend/app_flowy/lib/plugins/board/application/card/board_date_cell_bloc.dart

@@ -1,6 +1,6 @@
 import 'package:app_flowy/plugins/grid/application/cell/cell_service/cell_service.dart';
+import 'package:app_flowy/plugins/grid/application/field/field_cache.dart';
 import 'package:flowy_sdk/protobuf/flowy-grid/date_type_option_entities.pb.dart';
-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';
@@ -20,8 +20,6 @@ class BoardDateCellBloc extends Bloc<BoardDateCellEvent, BoardDateCellState> {
             emit(state.copyWith(
                 data: cellData, dateStr: _dateStrFromCellData(cellData)));
           },
-          didReceiveFieldUpdate: (FieldPB value) =>
-              emit(state.copyWith(field: value)),
         );
       },
     );
@@ -53,8 +51,6 @@ class BoardDateCellEvent with _$BoardDateCellEvent {
   const factory BoardDateCellEvent.initial() = _InitialCell;
   const factory BoardDateCellEvent.didReceiveCellUpdate(DateCellDataPB? data) =
       _DidReceiveCellUpdate;
-  const factory BoardDateCellEvent.didReceiveFieldUpdate(FieldPB field) =
-      _DidReceiveFieldUpdate;
 }
 
 @freezed
@@ -62,14 +58,14 @@ class BoardDateCellState with _$BoardDateCellState {
   const factory BoardDateCellState({
     required DateCellDataPB? data,
     required String dateStr,
-    required FieldPB field,
+    required GridFieldContext fieldContext,
   }) = _BoardDateCellState;
 
   factory BoardDateCellState.initial(GridDateCellController context) {
     final cellData = context.getCellData();
 
     return BoardDateCellState(
-      field: context.field,
+      fieldContext: context.fieldContext,
       data: cellData,
       dateStr: _dateStrFromCellData(cellData),
     );

+ 5 - 5
frontend/app_flowy/lib/plugins/board/application/card/card_bloc.dart

@@ -59,7 +59,7 @@ class BoardCardBloc extends Bloc<BoardCardEvent, BoardCardState> {
     return RowInfo(
       gridId: _rowService.gridId,
       fields: UnmodifiableListView(
-        state.cells.map((cell) => cell.identifier.field).toList(),
+        state.cells.map((cell) => cell.identifier.fieldContext).toList(),
       ),
       rowPB: state.rowPB,
     );
@@ -120,9 +120,9 @@ class BoardCellEquatable extends Equatable {
 
   @override
   List<Object?> get props => [
-        identifier.field.id,
-        identifier.field.fieldType,
-        identifier.field.visibility,
-        identifier.field.width,
+        identifier.fieldContext.id,
+        identifier.fieldContext.fieldType,
+        identifier.fieldContext.visibility,
+        identifier.fieldContext.width,
       ];
 }

+ 4 - 4
frontend/app_flowy/lib/plugins/board/application/card/card_data_controller.dart

@@ -10,15 +10,15 @@ typedef OnCardChanged = void Function(GridCellMap, RowsChangedReason);
 
 class CardDataController extends BoardCellBuilderDelegate {
   final RowPB rowPB;
-  final GridFieldCache _fieldCache;
+  final GridFieldController _fieldController;
   final GridRowCache _rowCache;
   final List<VoidCallback> _onCardChangedListeners = [];
 
   CardDataController({
     required this.rowPB,
-    required GridFieldCache fieldCache,
+    required GridFieldController fieldController,
     required GridRowCache rowCache,
-  })  : _fieldCache = fieldCache,
+  })  : _fieldController = fieldController,
         _rowCache = rowCache;
 
   GridCellMap loadData() {
@@ -41,7 +41,7 @@ class CardDataController extends BoardCellBuilderDelegate {
   @override
   GridCellFieldNotifier buildFieldNotifier() {
     return GridCellFieldNotifier(
-        notifier: GridCellFieldNotifierImpl(_fieldCache));
+        notifier: GridCellFieldNotifierImpl(_fieldController));
   }
 
   @override

+ 8 - 8
frontend/app_flowy/lib/plugins/board/presentation/board_page.dart

@@ -222,10 +222,10 @@ class _BoardContentState extends State<BoardContent> {
     /// Return placeholder widget if the rowCache is null.
     if (rowCache == null) return SizedBox(key: ObjectKey(columnItem));
 
-    final fieldCache = context.read<BoardBloc>().fieldCache;
+    final fieldController = context.read<BoardBloc>().fieldController;
     final gridId = context.read<BoardBloc>().gridId;
     final cardController = CardDataController(
-      fieldCache: fieldCache,
+      fieldController: fieldController,
       rowCache: rowCache,
       rowPB: rowPB,
     );
@@ -252,7 +252,7 @@ class _BoardContentState extends State<BoardContent> {
         dataController: cardController,
         openCard: (context) => _openCard(
           gridId,
-          fieldCache,
+          fieldController,
           rowPB,
           rowCache,
           context,
@@ -271,17 +271,17 @@ class _BoardContentState extends State<BoardContent> {
     );
   }
 
-  void _openCard(String gridId, GridFieldCache fieldCache, RowPB rowPB,
-      GridRowCache rowCache, BuildContext context) {
+  void _openCard(String gridId, GridFieldController fieldController,
+      RowPB rowPB, GridRowCache rowCache, BuildContext context) {
     final rowInfo = RowInfo(
       gridId: gridId,
-      fields: UnmodifiableListView(fieldCache.fields),
+      fields: UnmodifiableListView(fieldController.fieldContexts),
       rowPB: rowPB,
     );
 
     final dataController = GridRowDataController(
       rowInfo: rowInfo,
-      fieldCache: fieldCache,
+      fieldController: fieldController,
       rowCache: rowCache,
     );
 
@@ -302,7 +302,7 @@ class _ToolbarBlocAdaptor extends StatelessWidget {
         final bloc = context.read<BoardBloc>();
         final toolbarContext = BoardToolbarContext(
           viewId: bloc.gridId,
-          fieldCache: bloc.fieldCache,
+          fieldController: bloc.fieldController,
         );
 
         return BoardToolbar(toolbarContext: toolbarContext);

+ 5 - 5
frontend/app_flowy/lib/plugins/board/presentation/toolbar/board_setting.dart

@@ -19,16 +19,16 @@ import 'board_toolbar.dart';
 
 class BoardSettingContext {
   final String viewId;
-  final GridFieldCache fieldCache;
+  final GridFieldController fieldController;
   BoardSettingContext({
     required this.viewId,
-    required this.fieldCache,
+    required this.fieldController,
   });
 
   factory BoardSettingContext.from(BoardToolbarContext toolbarContext) =>
       BoardSettingContext(
         viewId: toolbarContext.viewId,
-        fieldCache: toolbarContext.fieldCache,
+        fieldController: toolbarContext.fieldController,
       );
 }
 
@@ -93,13 +93,13 @@ class BoardSettingList extends StatelessWidget {
           case BoardSettingAction.properties:
             GridPropertyList(
                     gridId: settingContext.viewId,
-                    fieldCache: settingContext.fieldCache)
+                    fieldController: settingContext.fieldController)
                 .show(context);
             break;
           case BoardSettingAction.groups:
             GridGroupList(
                     viewId: settingContext.viewId,
-                    fieldCache: settingContext.fieldCache)
+                    fieldController: settingContext.fieldController)
                 .show(context);
             break;
         }

+ 2 - 2
frontend/app_flowy/lib/plugins/board/presentation/toolbar/board_toolbar.dart

@@ -9,11 +9,11 @@ import 'board_setting.dart';
 
 class BoardToolbarContext {
   final String viewId;
-  final GridFieldCache fieldCache;
+  final GridFieldController fieldController;
 
   BoardToolbarContext({
     required this.viewId,
-    required this.fieldCache,
+    required this.fieldController,
   });
 }
 

+ 2 - 2
frontend/app_flowy/lib/plugins/grid/application/block/block_cache.dart

@@ -19,12 +19,12 @@ class GridBlockCache {
   GridBlockCache({
     required this.gridId,
     required this.block,
-    required GridFieldCache fieldCache,
+    required GridFieldController fieldController,
   }) {
     _rowCache = GridRowCache(
       gridId: gridId,
       block: block,
-      notifier: GridRowFieldNotifierImpl(fieldCache),
+      notifier: GridRowFieldNotifierImpl(fieldController),
     );
 
     _listener = GridBlockListener(blockId: block.id);

+ 13 - 12
frontend/app_flowy/lib/plugins/grid/application/cell/cell_service/cell_controller.dart

@@ -148,10 +148,10 @@ class IGridCellController<T, D> extends Equatable {
         _cellDataLoader = cellDataLoader,
         _cellDataPersistence = cellDataPersistence,
         _fieldNotifier = fieldNotifier,
-        _fieldService =
-            FieldService(gridId: cellId.gridId, fieldId: cellId.field.id),
-        _cacheKey =
-            GridCellCacheKey(rowId: cellId.rowId, fieldId: cellId.field.id);
+        _fieldService = FieldService(
+            gridId: cellId.gridId, fieldId: cellId.fieldContext.id),
+        _cacheKey = GridCellCacheKey(
+            rowId: cellId.rowId, fieldId: cellId.fieldContext.id);
 
   IGridCellController<T, D> clone() {
     return IGridCellController(
@@ -166,11 +166,11 @@ class IGridCellController<T, D> extends Equatable {
 
   String get rowId => cellId.rowId;
 
-  String get fieldId => cellId.field.id;
+  String get fieldId => cellId.fieldContext.id;
 
-  FieldPB get field => cellId.field;
+  GridFieldContext get fieldContext => cellId.fieldContext;
 
-  FieldType get fieldType => cellId.field.fieldType;
+  FieldType get fieldType => cellId.fieldContext.fieldType;
 
   VoidCallback? startListening(
       {required void Function(T?) onCellChanged,
@@ -182,7 +182,8 @@ class IGridCellController<T, D> extends Equatable {
     isListening = true;
 
     _cellDataNotifier = ValueNotifier(_cellsCache.get(_cacheKey));
-    _cellListener = CellListener(rowId: cellId.rowId, fieldId: cellId.field.id);
+    _cellListener =
+        CellListener(rowId: cellId.rowId, fieldId: cellId.fieldContext.id);
 
     /// 1.Listen on user edit event and load the new cell data if needed.
     /// For example:
@@ -308,14 +309,14 @@ class IGridCellController<T, D> extends Equatable {
 
   @override
   List<Object> get props =>
-      [_cellsCache.get(_cacheKey) ?? "", cellId.rowId + cellId.field.id];
+      [_cellsCache.get(_cacheKey) ?? "", cellId.rowId + cellId.fieldContext.id];
 }
 
 class GridCellFieldNotifierImpl extends IGridCellFieldNotifier {
-  final GridFieldCache _cache;
-  FieldChangesetCallback? _onChangesetFn;
+  final GridFieldController _cache;
+  OnChangeset? _onChangesetFn;
 
-  GridCellFieldNotifierImpl(GridFieldCache cache) : _cache = cache;
+  GridCellFieldNotifierImpl(GridFieldController cache) : _cache = cache;
 
   @override
   void onCellDispose() {

+ 4 - 4
frontend/app_flowy/lib/plugins/grid/application/cell/cell_service/cell_service.dart

@@ -60,17 +60,17 @@ class GridCellIdentifier with _$GridCellIdentifier {
   const factory GridCellIdentifier({
     required String gridId,
     required String rowId,
-    required FieldPB field,
+    required GridFieldContext fieldContext,
   }) = _GridCellIdentifier;
 
   // ignore: unused_element
   const GridCellIdentifier._();
 
-  String get fieldId => field.id;
+  String get fieldId => fieldContext.id;
 
-  FieldType get fieldType => field.fieldType;
+  FieldType get fieldType => fieldContext.fieldType;
 
   ValueKey key() {
-    return ValueKey("$rowId$fieldId${field.fieldType}");
+    return ValueKey("$rowId$fieldId${fieldContext.fieldType}");
   }
 }

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

@@ -176,7 +176,7 @@ class DateCalBloc extends Bloc<DateCalEvent, DateCalState> {
 
     final result = await FieldService.updateFieldTypeOption(
       gridId: cellController.gridId,
-      fieldId: cellController.field.id,
+      fieldId: cellController.fieldContext.id,
       typeOptionData: newDateTypeOption.writeToBuffer(),
     );
 

+ 3 - 7
frontend/app_flowy/lib/plugins/grid/application/cell/date_cell_bloc.dart

@@ -1,5 +1,5 @@
+import 'package:app_flowy/plugins/grid/application/field/field_cache.dart';
 import 'package:flowy_sdk/protobuf/flowy-grid/date_type_option_entities.pb.dart';
-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';
@@ -20,8 +20,6 @@ class DateCellBloc extends Bloc<DateCellEvent, DateCellState> {
             emit(state.copyWith(
                 data: cellData, dateStr: _dateStrFromCellData(cellData)));
           },
-          didReceiveFieldUpdate: (FieldPB value) =>
-              emit(state.copyWith(field: value)),
         );
       },
     );
@@ -53,8 +51,6 @@ class DateCellEvent with _$DateCellEvent {
   const factory DateCellEvent.initial() = _InitialCell;
   const factory DateCellEvent.didReceiveCellUpdate(DateCellDataPB? data) =
       _DidReceiveCellUpdate;
-  const factory DateCellEvent.didReceiveFieldUpdate(FieldPB field) =
-      _DidReceiveFieldUpdate;
 }
 
 @freezed
@@ -62,14 +58,14 @@ class DateCellState with _$DateCellState {
   const factory DateCellState({
     required DateCellDataPB? data,
     required String dateStr,
-    required FieldPB field,
+    required GridFieldContext fieldContext,
   }) = _DateCellState;
 
   factory DateCellState.initial(GridDateCellController context) {
     final cellData = context.getCellData();
 
     return DateCellState(
-      field: context.field,
+      fieldContext: context.fieldContext,
       data: cellData,
       dateStr: _dateStrFromCellData(cellData),
     );

+ 1 - 1
frontend/app_flowy/lib/plugins/grid/application/cell/select_option_service.dart

@@ -11,7 +11,7 @@ class SelectOptionService {
   SelectOptionService({required this.cellId});
 
   String get gridId => cellId.gridId;
-  String get fieldId => cellId.field.id;
+  String get fieldId => cellId.fieldContext.id;
   String get rowId => cellId.rowId;
 
   Future<Either<Unit, FlowyError>> create({required String name}) {

+ 118 - 47
frontend/app_flowy/lib/plugins/grid/application/field/field_cache.dart

@@ -1,36 +1,47 @@
 import 'dart:collection';
-
 import 'package:app_flowy/plugins/grid/application/field/grid_listener.dart';
+import 'package:app_flowy/plugins/grid/application/grid_service.dart';
+import 'package:app_flowy/plugins/grid/application/setting/setting_listener.dart';
+import 'package:dartz/dartz.dart';
 import 'package:flowy_sdk/log.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/foundation.dart';
-
 import '../row/row_cache.dart';
 
-class FieldsNotifier extends ChangeNotifier {
-  List<FieldPB> _fields = [];
+class _GridFieldNotifier extends ChangeNotifier {
+  List<GridFieldContext> _fieldContexts = [];
+
+  set fieldContexts(List<GridFieldContext> fieldContexts) {
+    _fieldContexts = fieldContexts;
+    notifyListeners();
+  }
 
-  set fields(List<FieldPB> fields) {
-    _fields = fields;
+  void notify() {
     notifyListeners();
   }
 
-  List<FieldPB> get fields => _fields;
+  List<GridFieldContext> get fieldContexts => _fieldContexts;
 }
 
-typedef FieldChangesetCallback = void Function(FieldChangesetPB);
-typedef FieldsCallback = void Function(List<FieldPB>);
+typedef OnChangeset = void Function(FieldChangesetPB);
+typedef OnReceiveFields = void Function(List<GridFieldContext>);
 
-class GridFieldCache {
+class GridFieldController {
   final String gridId;
   final GridFieldsListener _fieldListener;
-  FieldsNotifier? _fieldNotifier = FieldsNotifier();
-  final Map<FieldsCallback, VoidCallback> _fieldsCallbackMap = {};
-  final Map<FieldChangesetCallback, FieldChangesetCallback>
-      _changesetCallbackMap = {};
+  final SettingListener _settingListener;
+  final Map<OnReceiveFields, VoidCallback> _fieldCallbackMap = {};
+  final Map<OnChangeset, OnChangeset> _changesetCallbackMap = {};
+
+  _GridFieldNotifier? _fieldNotifier = _GridFieldNotifier();
+  final GridFFIService _gridFFIService;
 
-  GridFieldCache({required this.gridId})
-      : _fieldListener = GridFieldsListener(gridId: gridId) {
+  GridFieldController({required this.gridId})
+      : _fieldListener = GridFieldsListener(gridId: gridId),
+        _gridFFIService = GridFFIService(gridId: gridId),
+        _settingListener = SettingListener(gridId: gridId) {
+    //Listen on field's changes
     _fieldListener.start(onFieldsChanged: (result) {
       result.fold(
         (changeset) {
@@ -44,6 +55,28 @@ class GridFieldCache {
         (err) => Log.error(err),
       );
     });
+
+    //Listen on setting changes
+    _settingListener.start(onSettingUpdated: (result) {
+      result.fold(
+        (setting) {
+          final List<String> groupFieldIds = setting.groupConfigurations.items
+              .map((item) => item.groupFieldId)
+              .toList();
+          bool isChanged = false;
+          if (_fieldNotifier != null) {
+            for (var field in _fieldNotifier!.fieldContexts) {
+              if (groupFieldIds.contains(field.id)) {
+                field._isGroupField = true;
+                isChanged = true;
+              }
+            }
+            if (isChanged) _fieldNotifier?.notify();
+          }
+        },
+        (r) => Log.error(r),
+      );
+    });
   }
 
   Future<void> dispose() async {
@@ -52,49 +85,62 @@ class GridFieldCache {
     _fieldNotifier = null;
   }
 
-  List<FieldPB> get fields => [..._fieldNotifier?.fields ?? []];
+  List<GridFieldContext> get fieldContexts =>
+      [..._fieldNotifier?.fieldContexts ?? []];
 
-  set fields(List<FieldPB> fields) {
-    _fieldNotifier?.fields = [...fields];
+  Future<Either<Unit, FlowyError>> loadFields(
+      {required List<FieldIdPB> fieldIds}) async {
+    final result = await _gridFFIService.getFields(fieldIds: fieldIds);
+    return Future(
+      () => result.fold(
+        (newFields) {
+          _fieldNotifier?.fieldContexts = newFields.items
+              .map((field) => GridFieldContext(field: field))
+              .toList();
+          return left(unit);
+        },
+        (err) => right(err),
+      ),
+    );
   }
 
   void addListener({
-    FieldsCallback? onFields,
-    FieldChangesetCallback? onChangeset,
+    OnReceiveFields? onFields,
+    OnChangeset? onChangeset,
     bool Function()? listenWhen,
   }) {
     if (onChangeset != null) {
-      fn(c) {
+      callback(c) {
         if (listenWhen != null && listenWhen() == false) {
           return;
         }
         onChangeset(c);
       }
 
-      _changesetCallbackMap[onChangeset] = fn;
+      _changesetCallbackMap[onChangeset] = callback;
     }
 
     if (onFields != null) {
-      fn() {
+      callback() {
         if (listenWhen != null && listenWhen() == false) {
           return;
         }
-        onFields(fields);
+        onFields(fieldContexts);
       }
 
-      _fieldsCallbackMap[onFields] = fn;
-      _fieldNotifier?.addListener(fn);
+      _fieldCallbackMap[onFields] = callback;
+      _fieldNotifier?.addListener(callback);
     }
   }
 
   void removeListener({
-    FieldsCallback? onFieldsListener,
-    FieldChangesetCallback? onChangesetListener,
+    OnReceiveFields? onFieldsListener,
+    OnChangeset? onChangesetListener,
   }) {
     if (onFieldsListener != null) {
-      final fn = _fieldsCallbackMap.remove(onFieldsListener);
-      if (fn != null) {
-        _fieldNotifier?.removeListener(fn);
+      final callback = _fieldCallbackMap.remove(onFieldsListener);
+      if (callback != null) {
+        _fieldNotifier?.removeListener(callback);
       }
     }
 
@@ -107,56 +153,58 @@ class GridFieldCache {
     if (deletedFields.isEmpty) {
       return;
     }
-    final List<FieldPB> newFields = fields;
+    final List<GridFieldContext> newFields = fieldContexts;
     final Map<String, FieldIdPB> deletedFieldMap = {
       for (var fieldOrder in deletedFields) fieldOrder.fieldId: fieldOrder
     };
 
     newFields.retainWhere((field) => (deletedFieldMap[field.id] == null));
-    _fieldNotifier?.fields = newFields;
+    _fieldNotifier?.fieldContexts = newFields;
   }
 
   void _insertFields(List<IndexFieldPB> insertedFields) {
     if (insertedFields.isEmpty) {
       return;
     }
-    final List<FieldPB> newFields = fields;
+    final List<GridFieldContext> newFields = fieldContexts;
     for (final indexField in insertedFields) {
+      final gridField = GridFieldContext(field: indexField.field_1);
       if (newFields.length > indexField.index) {
-        newFields.insert(indexField.index, indexField.field_1);
+        newFields.insert(indexField.index, gridField);
       } else {
-        newFields.add(indexField.field_1);
+        newFields.add(gridField);
       }
     }
-    _fieldNotifier?.fields = newFields;
+    _fieldNotifier?.fieldContexts = newFields;
   }
 
   void _updateFields(List<FieldPB> updatedFields) {
     if (updatedFields.isEmpty) {
       return;
     }
-    final List<FieldPB> newFields = fields;
+    final List<GridFieldContext> newFields = fieldContexts;
     for (final updatedField in updatedFields) {
       final index =
           newFields.indexWhere((field) => field.id == updatedField.id);
       if (index != -1) {
         newFields.removeAt(index);
-        newFields.insert(index, updatedField);
+        final gridField = GridFieldContext(field: updatedField);
+        newFields.insert(index, gridField);
       }
     }
-    _fieldNotifier?.fields = newFields;
+    _fieldNotifier?.fieldContexts = newFields;
   }
 }
 
 class GridRowFieldNotifierImpl extends IGridRowFieldNotifier {
-  final GridFieldCache _cache;
-  FieldChangesetCallback? _onChangesetFn;
-  FieldsCallback? _onFieldFn;
-  GridRowFieldNotifierImpl(GridFieldCache cache) : _cache = cache;
+  final GridFieldController _cache;
+  OnChangeset? _onChangesetFn;
+  OnReceiveFields? _onFieldFn;
+  GridRowFieldNotifierImpl(GridFieldController cache) : _cache = cache;
 
   @override
-  UnmodifiableListView<FieldPB> get fields =>
-      UnmodifiableListView(_cache.fields);
+  UnmodifiableListView<GridFieldContext> get fields =>
+      UnmodifiableListView(_cache.fieldContexts);
 
   @override
   void onRowFieldsChanged(VoidCallback callback) {
@@ -188,3 +236,26 @@ class GridRowFieldNotifierImpl extends IGridRowFieldNotifier {
     }
   }
 }
+
+class GridFieldContext {
+  final FieldPB _field;
+  bool _isGroupField = false;
+
+  String get id => _field.id;
+
+  FieldType get fieldType => _field.fieldType;
+
+  bool get visibility => _field.visibility;
+
+  double get width => _field.width.toDouble();
+
+  bool get isPrimary => _field.isPrimary;
+
+  String get name => _field.name;
+
+  FieldPB get field => _field;
+
+  bool get isGroupField => _isGroupField;
+
+  GridFieldContext({required FieldPB field}) : _field = field;
+}

+ 2 - 0
frontend/app_flowy/lib/plugins/grid/application/field/field_service.dart

@@ -5,6 +5,8 @@ import 'package:flowy_sdk/protobuf/flowy-grid/field_entities.pb.dart';
 import 'package:flowy_sdk/protobuf/flowy-grid/grid_entities.pb.dart';
 import 'package:flutter/foundation.dart';
 import 'package:freezed_annotation/freezed_annotation.dart';
+
+import 'field_cache.dart';
 part 'field_service.freezed.dart';
 
 /// FieldService consists of lots of event functions. We define the events in the backend(Rust),

+ 1 - 0
frontend/app_flowy/lib/plugins/grid/application/field/type_option/type_option_context.dart

@@ -1,3 +1,4 @@
+import 'package:app_flowy/plugins/grid/application/field/field_cache.dart';
 import 'package:flowy_sdk/dispatch/dispatch.dart';
 import 'package:flowy_sdk/protobuf/flowy-error/errors.pb.dart';
 import 'package:flowy_sdk/protobuf/flowy-grid/checkbox_type_option.pb.dart';

+ 4 - 3
frontend/app_flowy/lib/plugins/grid/application/field/type_option/type_option_data_controller.dart

@@ -1,3 +1,4 @@
+import 'package:app_flowy/plugins/grid/application/field/field_cache.dart';
 import 'package:flowy_infra/notifier.dart';
 import 'package:flowy_sdk/protobuf/flowy-error/errors.pb.dart';
 import 'package:flowy_sdk/protobuf/flowy-grid/field_entities.pb.dart';
@@ -17,12 +18,12 @@ class TypeOptionDataController {
   TypeOptionDataController({
     required this.gridId,
     required this.loader,
-    FieldPB? field,
+    GridFieldContext? fieldContext,
   }) {
-    if (field != null) {
+    if (fieldContext != null) {
       _data = FieldTypeOptionDataPB.create()
         ..gridId = gridId
-        ..field_2 = field;
+        ..field_2 = fieldContext.field;
     }
   }
 

+ 6 - 4
frontend/app_flowy/lib/plugins/grid/application/grid_bloc.dart

@@ -7,6 +7,7 @@ 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_cache.dart';
+import 'field/field_cache.dart';
 import 'grid_data_controller.dart';
 import 'row/row_cache.dart';
 import 'dart:collection';
@@ -101,7 +102,7 @@ class GridEvent with _$GridEvent {
     RowsChangedReason listState,
   ) = _DidReceiveRowUpdate;
   const factory GridEvent.didReceiveFieldUpdate(
-    UnmodifiableListView<FieldPB> fields,
+    UnmodifiableListView<GridFieldContext> fields,
   ) = _DidReceiveFieldUpdate;
 
   const factory GridEvent.didReceiveGridUpdate(
@@ -138,9 +139,9 @@ class GridLoadingState with _$GridLoadingState {
 }
 
 class GridFieldEquatable extends Equatable {
-  final UnmodifiableListView<FieldPB> _fields;
+  final UnmodifiableListView<GridFieldContext> _fields;
   const GridFieldEquatable(
-    UnmodifiableListView<FieldPB> fields,
+    UnmodifiableListView<GridFieldContext> fields,
   ) : _fields = fields;
 
   @override
@@ -157,5 +158,6 @@ class GridFieldEquatable extends Equatable {
     ];
   }
 
-  UnmodifiableListView<FieldPB> get value => UnmodifiableListView(_fields);
+  UnmodifiableListView<GridFieldContext> get value =>
+      UnmodifiableListView(_fields);
 }

+ 7 - 22
frontend/app_flowy/lib/plugins/grid/application/grid_data_controller.dart

@@ -4,7 +4,6 @@ import 'package:flowy_sdk/log.dart';
 import 'package:flowy_sdk/protobuf/flowy-error/errors.pb.dart';
 import 'package:flowy_sdk/protobuf/flowy-folder/view.pb.dart';
 import 'package:flowy_sdk/protobuf/flowy-grid/block_entities.pb.dart';
-import 'package:flowy_sdk/protobuf/flowy-grid/field_entities.pb.dart';
 import 'package:flowy_sdk/protobuf/flowy-grid/grid_entities.pb.dart';
 import 'dart:async';
 import 'package:dartz/dartz.dart';
@@ -13,7 +12,7 @@ import 'field/field_cache.dart';
 import 'prelude.dart';
 import 'row/row_cache.dart';
 
-typedef OnFieldsChanged = void Function(UnmodifiableListView<FieldPB>);
+typedef OnFieldsChanged = void Function(UnmodifiableListView<GridFieldContext>);
 typedef OnGridChanged = void Function(GridPB);
 
 typedef OnRowsChanged = void Function(
@@ -25,7 +24,7 @@ typedef ListenOnRowChangedCondition = bool Function();
 class GridDataController {
   final String gridId;
   final GridFFIService _gridFFIService;
-  final GridFieldCache fieldCache;
+  final GridFieldController fieldController;
 
   // key: the block id
   final LinkedHashMap<String, GridBlockCache> _blocks;
@@ -49,7 +48,7 @@ class GridDataController {
         // ignore: prefer_collection_literals
         _blocks = LinkedHashMap(),
         _gridFFIService = GridFFIService(gridId: view.id),
-        fieldCache = GridFieldCache(gridId: view.id);
+        fieldController = GridFieldController(gridId: view.id);
 
   void addListener({
     required OnGridChanged onGridChanged,
@@ -60,7 +59,7 @@ class GridDataController {
     _onRowChanged = onRowsChanged;
     _onFieldsChanged = onFieldsChanged;
 
-    fieldCache.addListener(onFields: (fields) {
+    fieldController.addListener(onFields: (fields) {
       _onFieldsChanged?.call(UnmodifiableListView(fields));
     });
   }
@@ -72,7 +71,7 @@ class GridDataController {
         (grid) async {
           _initialBlocks(grid.blocks);
           _onGridChanged?.call(grid);
-          return await _loadFields(grid);
+          return await fieldController.loadFields(fieldIds: grid.fields);
         },
         (err) => right(err),
       ),
@@ -85,7 +84,7 @@ class GridDataController {
 
   Future<void> dispose() async {
     await _gridFFIService.closeGrid();
-    await fieldCache.dispose();
+    await fieldController.dispose();
 
     for (final blockCache in _blocks.values) {
       blockCache.dispose();
@@ -102,7 +101,7 @@ class GridDataController {
       final cache = GridBlockCache(
         gridId: gridId,
         block: block,
-        fieldCache: fieldCache,
+        fieldController: fieldController,
       );
 
       cache.addListener(
@@ -114,18 +113,4 @@ class GridDataController {
       _blocks[block.id] = cache;
     }
   }
-
-  Future<Either<Unit, FlowyError>> _loadFields(GridPB grid) async {
-    final result = await _gridFFIService.getFields(fieldIds: grid.fields);
-    return Future(
-      () => result.fold(
-        (fields) {
-          fieldCache.fields = fields.items;
-          _onFieldsChanged?.call(UnmodifiableListView(fieldCache.fields));
-          return left(unit);
-        },
-        (err) => right(err),
-      ),
-    );
-  }
 }

+ 9 - 10
frontend/app_flowy/lib/plugins/grid/application/grid_header_bloc.dart

@@ -4,19 +4,18 @@ 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/field_cache.dart';
 
 part 'grid_header_bloc.freezed.dart';
 
 class GridHeaderBloc extends Bloc<GridHeaderEvent, GridHeaderState> {
-  final GridFieldCache fieldCache;
+  final GridFieldController fieldController;
   final String gridId;
 
   GridHeaderBloc({
     required this.gridId,
-    required this.fieldCache,
-  }) : super(GridHeaderState.initial(fieldCache.fields)) {
+    required this.fieldController,
+  }) : super(GridHeaderState.initial(fieldController.fieldContexts)) {
     on<GridHeaderEvent>(
       (event, emit) async {
         await event.map(
@@ -36,7 +35,7 @@ class GridHeaderBloc extends Bloc<GridHeaderEvent, GridHeaderState> {
 
   Future<void> _moveField(
       _MoveField value, Emitter<GridHeaderState> emit) async {
-    final fields = List<FieldPB>.from(state.fields);
+    final fields = List<GridFieldContext>.from(state.fields);
     fields.insert(value.toIndex, fields.removeAt(value.fromIndex));
     emit(state.copyWith(fields: fields));
 
@@ -49,7 +48,7 @@ class GridHeaderBloc extends Bloc<GridHeaderEvent, GridHeaderState> {
   }
 
   Future<void> _startListening() async {
-    fieldCache.addListener(
+    fieldController.addListener(
       onFields: (fields) => add(GridHeaderEvent.didReceiveFieldUpdate(fields)),
       listenWhen: () => !isClosed,
     );
@@ -64,18 +63,18 @@ class GridHeaderBloc extends Bloc<GridHeaderEvent, GridHeaderState> {
 @freezed
 class GridHeaderEvent with _$GridHeaderEvent {
   const factory GridHeaderEvent.initial() = _InitialHeader;
-  const factory GridHeaderEvent.didReceiveFieldUpdate(List<FieldPB> fields) =
-      _DidReceiveFieldUpdate;
+  const factory GridHeaderEvent.didReceiveFieldUpdate(
+      List<GridFieldContext> fields) = _DidReceiveFieldUpdate;
   const factory GridHeaderEvent.moveField(
       FieldPB field, int fromIndex, int toIndex) = _MoveField;
 }
 
 @freezed
 class GridHeaderState with _$GridHeaderState {
-  const factory GridHeaderState({required List<FieldPB> fields}) =
+  const factory GridHeaderState({required List<GridFieldContext> fields}) =
       _GridHeaderState;
 
-  factory GridHeaderState.initial(List<FieldPB> fields) {
+  factory GridHeaderState.initial(List<GridFieldContext> fields) {
     // final List<FieldPB> newFields = List.from(fields);
     // newFields.retainWhere((field) => field.visibility);
     return GridHeaderState(fields: fields);

+ 11 - 9
frontend/app_flowy/lib/plugins/grid/application/row/row_bloc.dart

@@ -1,7 +1,7 @@
 import 'dart:collection';
 import 'package:app_flowy/plugins/grid/application/cell/cell_service/cell_service.dart';
+import 'package:app_flowy/plugins/grid/application/field/field_cache.dart';
 import 'package:equatable/equatable.dart';
-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';
@@ -35,7 +35,7 @@ class RowBloc extends Bloc<RowEvent, RowState> {
           },
           didReceiveCells: (_DidReceiveCells value) async {
             final cells = value.gridCellMap.values
-                .map((e) => GridCellEquatable(e.field))
+                .map((e) => GridCellEquatable(e.fieldContext))
                 .toList();
             emit(state.copyWith(
               gridCellMap: value.gridCellMap,
@@ -87,21 +87,23 @@ class RowState with _$RowState {
         rowInfo: rowInfo,
         gridCellMap: cellDataMap,
         cells: UnmodifiableListView(
-          cellDataMap.values.map((e) => GridCellEquatable(e.field)).toList(),
+          cellDataMap.values
+              .map((e) => GridCellEquatable(e.fieldContext))
+              .toList(),
         ),
       );
 }
 
 class GridCellEquatable extends Equatable {
-  final FieldPB _field;
+  final GridFieldContext _fieldContext;
 
-  const GridCellEquatable(FieldPB field) : _field = field;
+  const GridCellEquatable(GridFieldContext field) : _fieldContext = field;
 
   @override
   List<Object?> get props => [
-        _field.id,
-        _field.fieldType,
-        _field.visibility,
-        _field.width,
+        _fieldContext.id,
+        _fieldContext.fieldType,
+        _fieldContext.visibility,
+        _fieldContext.width,
       ];
 }

+ 4 - 3
frontend/app_flowy/lib/plugins/grid/application/row/row_cache.dart

@@ -1,5 +1,6 @@
 import 'dart:collection';
 import 'package:app_flowy/plugins/grid/application/cell/cell_service/cell_service.dart';
+import 'package:app_flowy/plugins/grid/application/field/field_cache.dart';
 import 'package:flowy_sdk/dispatch/dispatch.dart';
 import 'package:flowy_sdk/log.dart';
 import 'package:flowy_sdk/protobuf/flowy-grid/block_entities.pb.dart';
@@ -12,7 +13,7 @@ part 'row_cache.freezed.dart';
 typedef RowUpdateCallback = void Function();
 
 abstract class IGridRowFieldNotifier {
-  UnmodifiableListView<FieldPB> get fields;
+  UnmodifiableListView<GridFieldContext> get fields;
   void onRowFieldsChanged(VoidCallback callback);
   void onRowFieldChanged(void Function(FieldPB) callback);
   void onRowDispose();
@@ -217,7 +218,7 @@ class GridRowCache {
         cellDataMap[field.id] = GridCellIdentifier(
           rowId: rowId,
           gridId: gridId,
-          field: field,
+          fieldContext: field,
         );
       }
     }
@@ -284,7 +285,7 @@ class _RowChangesetNotifier extends ChangeNotifier {
 class RowInfo with _$RowInfo {
   const factory RowInfo({
     required String gridId,
-    required UnmodifiableListView<FieldPB> fields,
+    required UnmodifiableListView<GridFieldContext> fields,
     required RowPB rowPB,
   }) = _RowInfo;
 }

+ 4 - 4
frontend/app_flowy/lib/plugins/grid/application/row/row_data_controller.dart

@@ -10,14 +10,14 @@ typedef OnRowChanged = void Function(GridCellMap, RowsChangedReason);
 class GridRowDataController extends GridCellBuilderDelegate {
   final RowInfo rowInfo;
   final List<VoidCallback> _onRowChangedListeners = [];
-  final GridFieldCache _fieldCache;
+  final GridFieldController _fieldController;
   final GridRowCache _rowCache;
 
   GridRowDataController({
     required this.rowInfo,
-    required GridFieldCache fieldCache,
+    required GridFieldController fieldController,
     required GridRowCache rowCache,
-  })  : _fieldCache = fieldCache,
+  })  : _fieldController = fieldController,
         _rowCache = rowCache;
 
   GridCellMap loadData() {
@@ -41,7 +41,7 @@ class GridRowDataController extends GridCellBuilderDelegate {
   @override
   GridCellFieldNotifier buildFieldNotifier() {
     return GridCellFieldNotifier(
-        notifier: GridCellFieldNotifierImpl(_fieldCache));
+        notifier: GridCellFieldNotifierImpl(_fieldController));
   }
 
   @override

+ 15 - 14
frontend/app_flowy/lib/plugins/grid/application/setting/group_bloc.dart

@@ -1,6 +1,5 @@
 import 'package:app_flowy/plugins/grid/application/field/field_service.dart';
 import 'package:flowy_sdk/log.dart';
-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';
@@ -11,14 +10,15 @@ import 'setting_controller.dart';
 part 'group_bloc.freezed.dart';
 
 class GridGroupBloc extends Bloc<GridGroupEvent, GridGroupState> {
-  final GridFieldCache _fieldCache;
+  final GridFieldController _fieldController;
   final SettingController _settingController;
-  Function(List<FieldPB>)? _onFieldsFn;
+  Function(List<GridFieldContext>)? _onFieldsFn;
 
-  GridGroupBloc({required String viewId, required GridFieldCache fieldCache})
-      : _fieldCache = fieldCache,
+  GridGroupBloc(
+      {required String viewId, required GridFieldController fieldController})
+      : _fieldController = fieldController,
         _settingController = SettingController(viewId: viewId),
-        super(GridGroupState.initial(viewId, fieldCache.fields)) {
+        super(GridGroupState.initial(viewId, fieldController.fieldContexts)) {
     on<GridGroupEvent>(
       (event, emit) async {
         await event.map(
@@ -36,7 +36,7 @@ class GridGroupBloc extends Bloc<GridGroupEvent, GridGroupState> {
             );
           },
           didReceiveFieldUpdate: (_DidReceiveFieldUpdate value) {
-            emit(state.copyWith(fields: value.fields));
+            emit(state.copyWith(fieldContexts: value.fields));
           },
           moveField: (_MoveField value) {
             //
@@ -49,7 +49,7 @@ class GridGroupBloc extends Bloc<GridGroupEvent, GridGroupState> {
   @override
   Future<void> close() async {
     if (_onFieldsFn != null) {
-      _fieldCache.removeListener(onFieldsListener: _onFieldsFn!);
+      _fieldController.removeListener(onFieldsListener: _onFieldsFn!);
       _onFieldsFn = null;
     }
     return super.close();
@@ -57,7 +57,7 @@ class GridGroupBloc extends Bloc<GridGroupEvent, GridGroupState> {
 
   void _startListening() {
     _onFieldsFn = (fields) => add(GridGroupEvent.didReceiveFieldUpdate(fields));
-    _fieldCache.addListener(
+    _fieldController.addListener(
       onFields: _onFieldsFn,
       listenWhen: () => !isClosed,
     );
@@ -74,8 +74,8 @@ class GridGroupEvent with _$GridGroupEvent {
   const factory GridGroupEvent.initial() = _Initial;
   const factory GridGroupEvent.setFieldVisibility(
       String fieldId, bool visibility) = _SetFieldVisibility;
-  const factory GridGroupEvent.didReceiveFieldUpdate(List<FieldPB> fields) =
-      _DidReceiveFieldUpdate;
+  const factory GridGroupEvent.didReceiveFieldUpdate(
+      List<GridFieldContext> fields) = _DidReceiveFieldUpdate;
   const factory GridGroupEvent.moveField(int fromIndex, int toIndex) =
       _MoveField;
 }
@@ -84,12 +84,13 @@ class GridGroupEvent with _$GridGroupEvent {
 class GridGroupState with _$GridGroupState {
   const factory GridGroupState({
     required String gridId,
-    required List<FieldPB> fields,
+    required List<GridFieldContext> fieldContexts,
   }) = _GridGroupState;
 
-  factory GridGroupState.initial(String gridId, List<FieldPB> fields) =>
+  factory GridGroupState.initial(
+          String gridId, List<GridFieldContext> fieldContexts) =>
       GridGroupState(
         gridId: gridId,
-        fields: fields,
+        fieldContexts: fieldContexts,
       );
 }

+ 18 - 14
frontend/app_flowy/lib/plugins/grid/application/setting/property_bloc.dart

@@ -1,6 +1,5 @@
 import 'package:app_flowy/plugins/grid/application/field/field_service.dart';
 import 'package:flowy_sdk/log.dart';
-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';
@@ -10,12 +9,14 @@ import '../field/field_cache.dart';
 part 'property_bloc.freezed.dart';
 
 class GridPropertyBloc extends Bloc<GridPropertyEvent, GridPropertyState> {
-  final GridFieldCache _fieldCache;
-  Function(List<FieldPB>)? _onFieldsFn;
+  final GridFieldController _fieldController;
+  Function(List<GridFieldContext>)? _onFieldsFn;
 
-  GridPropertyBloc({required String gridId, required GridFieldCache fieldCache})
-      : _fieldCache = fieldCache,
-        super(GridPropertyState.initial(gridId, fieldCache.fields)) {
+  GridPropertyBloc(
+      {required String gridId, required GridFieldController fieldController})
+      : _fieldController = fieldController,
+        super(
+            GridPropertyState.initial(gridId, fieldController.fieldContexts)) {
     on<GridPropertyEvent>(
       (event, emit) async {
         await event.map(
@@ -33,7 +34,7 @@ class GridPropertyBloc extends Bloc<GridPropertyEvent, GridPropertyState> {
             );
           },
           didReceiveFieldUpdate: (_DidReceiveFieldUpdate value) {
-            emit(state.copyWith(fields: value.fields));
+            emit(state.copyWith(fieldContexts: value.fields));
           },
           moveField: (_MoveField value) {
             //
@@ -46,7 +47,7 @@ class GridPropertyBloc extends Bloc<GridPropertyEvent, GridPropertyState> {
   @override
   Future<void> close() async {
     if (_onFieldsFn != null) {
-      _fieldCache.removeListener(onFieldsListener: _onFieldsFn!);
+      _fieldController.removeListener(onFieldsListener: _onFieldsFn!);
       _onFieldsFn = null;
     }
     return super.close();
@@ -55,7 +56,7 @@ class GridPropertyBloc extends Bloc<GridPropertyEvent, GridPropertyState> {
   void _startListening() {
     _onFieldsFn =
         (fields) => add(GridPropertyEvent.didReceiveFieldUpdate(fields));
-    _fieldCache.addListener(
+    _fieldController.addListener(
       onFields: _onFieldsFn,
       listenWhen: () => !isClosed,
     );
@@ -67,8 +68,8 @@ class GridPropertyEvent with _$GridPropertyEvent {
   const factory GridPropertyEvent.initial() = _Initial;
   const factory GridPropertyEvent.setFieldVisibility(
       String fieldId, bool visibility) = _SetFieldVisibility;
-  const factory GridPropertyEvent.didReceiveFieldUpdate(List<FieldPB> fields) =
-      _DidReceiveFieldUpdate;
+  const factory GridPropertyEvent.didReceiveFieldUpdate(
+      List<GridFieldContext> fields) = _DidReceiveFieldUpdate;
   const factory GridPropertyEvent.moveField(int fromIndex, int toIndex) =
       _MoveField;
 }
@@ -77,12 +78,15 @@ class GridPropertyEvent with _$GridPropertyEvent {
 class GridPropertyState with _$GridPropertyState {
   const factory GridPropertyState({
     required String gridId,
-    required List<FieldPB> fields,
+    required List<GridFieldContext> fieldContexts,
   }) = _GridPropertyState;
 
-  factory GridPropertyState.initial(String gridId, List<FieldPB> fields) =>
+  factory GridPropertyState.initial(
+    String gridId,
+    List<GridFieldContext> fieldContexts,
+  ) =>
       GridPropertyState(
         gridId: gridId,
-        fields: fields,
+        fieldContexts: fieldContexts,
       );
 }

+ 1 - 0
frontend/app_flowy/lib/plugins/grid/application/setting/setting_controller.dart

@@ -41,6 +41,7 @@ class SettingController {
     required OnError onError,
   }) {
     assert(_onSettingUpdated == null, 'Should call once');
+    assert(_onError == null, 'Should call once');
     _onSettingUpdated = onSettingUpdated;
     _onError = onError;
   }

+ 12 - 9
frontend/app_flowy/lib/plugins/grid/presentation/grid_page.dart

@@ -157,10 +157,11 @@ class _FlowyGridState extends State<FlowyGrid> {
   }
 
   Widget _gridHeader(BuildContext context, String gridId) {
-    final fieldCache = context.read<GridBloc>().dataController.fieldCache;
+    final fieldController =
+        context.read<GridBloc>().dataController.fieldController;
     return GridHeaderSliverAdaptor(
       gridId: gridId,
-      fieldCache: fieldCache,
+      fieldController: fieldController,
       anchorScrollController: headerScrollController,
     );
   }
@@ -173,10 +174,11 @@ class _GridToolbarAdaptor extends StatelessWidget {
   Widget build(BuildContext context) {
     return BlocSelector<GridBloc, GridState, GridToolbarContext>(
       selector: (state) {
-        final fieldCache = context.read<GridBloc>().dataController.fieldCache;
+        final fieldController =
+            context.read<GridBloc>().dataController.fieldController;
         return GridToolbarContext(
           gridId: state.gridId,
-          fieldCache: fieldCache,
+          fieldController: fieldController,
         );
       },
       builder: (context, toolbarContext) {
@@ -247,10 +249,11 @@ class _GridRowsState extends State<_GridRows> {
     /// Return placeholder widget if the rowCache is null.
     if (rowCache == null) return const SizedBox();
 
-    final fieldCache = context.read<GridBloc>().dataController.fieldCache;
+    final fieldController =
+        context.read<GridBloc>().dataController.fieldController;
     final dataController = GridRowDataController(
       rowInfo: rowInfo,
-      fieldCache: fieldCache,
+      fieldController: fieldController,
       rowCache: rowCache,
     );
 
@@ -264,7 +267,7 @@ class _GridRowsState extends State<_GridRows> {
           _openRowDetailPage(
             context,
             rowInfo,
-            fieldCache,
+            fieldController,
             rowCache,
             cellBuilder,
           );
@@ -277,13 +280,13 @@ class _GridRowsState extends State<_GridRows> {
   void _openRowDetailPage(
     BuildContext context,
     RowInfo rowInfo,
-    GridFieldCache fieldCache,
+    GridFieldController fieldController,
     GridRowCache rowCache,
     GridCellBuilder cellBuilder,
   ) {
     final dataController = GridRowDataController(
       rowInfo: rowInfo,
-      fieldCache: fieldCache,
+      fieldController: fieldController,
       rowCache: rowCache,
     );
 

+ 2 - 2
frontend/app_flowy/lib/plugins/grid/presentation/layout/layout.dart

@@ -1,8 +1,8 @@
-import 'package:flowy_sdk/protobuf/flowy-grid/field_entities.pb.dart';
+import 'package:app_flowy/plugins/grid/application/field/field_cache.dart';
 import 'sizes.dart';
 
 class GridLayout {
-  static double headerWidth(List<FieldPB> fields) {
+  static double headerWidth(List<GridFieldContext> fields) {
     if (fields.isEmpty) return 0;
 
     final fieldsWidth = fields

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

@@ -1,3 +1,4 @@
+import 'package:app_flowy/plugins/grid/application/field/field_cache.dart';
 import 'package:app_flowy/plugins/grid/application/field/field_cell_bloc.dart';
 import 'package:app_flowy/plugins/grid/application/field/field_service.dart';
 import 'package:app_flowy/plugins/grid/application/field/type_option/type_option_context.dart';

+ 4 - 4
frontend/app_flowy/lib/plugins/grid/presentation/widgets/header/grid_header.dart

@@ -18,11 +18,11 @@ import 'field_cell.dart';
 
 class GridHeaderSliverAdaptor extends StatefulWidget {
   final String gridId;
-  final GridFieldCache fieldCache;
+  final GridFieldController fieldController;
   final ScrollController anchorScrollController;
   const GridHeaderSliverAdaptor({
     required this.gridId,
-    required this.fieldCache,
+    required this.fieldController,
     required this.anchorScrollController,
     Key? key,
   }) : super(key: key);
@@ -38,7 +38,7 @@ class _GridHeaderSliverAdaptorState extends State<GridHeaderSliverAdaptor> {
     return BlocProvider(
       create: (context) {
         final bloc = getIt<GridHeaderBloc>(
-            param1: widget.gridId, param2: widget.fieldCache);
+            param1: widget.gridId, param2: widget.fieldController);
         bloc.add(const GridHeaderEvent.initial());
         return bloc;
       },
@@ -84,7 +84,7 @@ class _GridHeaderState extends State<_GridHeader> {
         final cells = state.fields
             .where((field) => field.visibility)
             .map((field) =>
-                GridFieldCellContext(gridId: widget.gridId, field: field))
+                GridFieldCellContext(gridId: widget.gridId, field: field.field))
             .map((ctx) => GridFieldCell(ctx, key: ValueKey(ctx.field.id)))
             .toList();
 

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

@@ -1,5 +1,6 @@
 import 'dart:typed_data';
 
+import 'package:app_flowy/plugins/grid/application/field/field_cache.dart';
 import 'package:app_flowy/plugins/grid/application/field/type_option/type_option_context.dart';
 import 'package:app_flowy/plugins/grid/application/field/type_option/type_option_data_controller.dart';
 import 'package:flowy_sdk/protobuf/flowy-grid/checkbox_type_option.pb.dart';
@@ -129,17 +130,18 @@ TypeOptionWidgetBuilder makeTypeOptionWidgetBuilder({
 
 TypeOptionContext<T> makeTypeOptionContext<T extends GeneratedMessage>({
   required String gridId,
-  required FieldPB field,
+  required GridFieldContext fieldContext,
 }) {
-  final loader = FieldTypeOptionLoader(gridId: gridId, field: field);
+  final loader =
+      FieldTypeOptionLoader(gridId: gridId, field: fieldContext.field);
   final dataController = TypeOptionDataController(
     gridId: gridId,
     loader: loader,
-    field: field,
+    fieldContext: fieldContext,
   );
   return makeTypeOptionContextWithDataController(
     gridId: gridId,
-    fieldType: field.fieldType,
+    fieldType: fieldContext.fieldType,
     dataController: dataController,
   );
 }

+ 2 - 2
frontend/app_flowy/lib/plugins/grid/presentation/widgets/row/grid_row.dart

@@ -189,13 +189,13 @@ class RowContent extends StatelessWidget {
         final GridCellWidget child = builder.build(cellId);
 
         return CellContainer(
-          width: cellId.field.width.toDouble(),
+          width: cellId.fieldContext.width.toDouble(),
           rowStateNotifier:
               Provider.of<RegionStateNotifier>(context, listen: false),
           accessoryBuilder: (buildContext) {
             final builder = child.accessoryBuilder;
             List<GridCellAccessory> accessories = [];
-            if (cellId.field.isPrimary) {
+            if (cellId.fieldContext.isPrimary) {
               accessories.add(PrimaryCellAccessory(
                 onTapCallback: onExpand,
                 isCellEditing: buildContext.isCellEditing,

+ 3 - 3
frontend/app_flowy/lib/plugins/grid/presentation/widgets/row/row_detail.dart

@@ -215,7 +215,7 @@ class _RowDetailCell extends StatelessWidget {
             SizedBox(
               width: 150,
               child: FieldCellButton(
-                field: cellId.field,
+                field: cellId.fieldContext.fieldContext,
                 onTap: () => _showFieldEditor(context),
               ),
             ),
@@ -230,10 +230,10 @@ class _RowDetailCell extends StatelessWidget {
   void _showFieldEditor(BuildContext context) {
     FieldEditor(
       gridId: cellId.gridId,
-      fieldName: cellId.field.name,
+      fieldName: cellId.fieldContext.name,
       typeOptionLoader: FieldTypeOptionLoader(
         gridId: cellId.gridId,
-        field: cellId.field,
+        field: cellId.fieldContext.fieldContext,
       ),
     ).show(context);
   }

+ 11 - 10
frontend/app_flowy/lib/plugins/grid/presentation/widgets/toolbar/grid_group.dart

@@ -6,7 +6,6 @@ import 'package:flowy_infra/theme.dart';
 import 'package:flowy_infra_ui/style_widget/button.dart';
 import 'package:flowy_infra_ui/style_widget/text.dart';
 import 'package:flowy_infra_ui/widget/spacing.dart';
-import 'package:flowy_sdk/protobuf/flowy-grid/field_entities.pb.dart';
 import 'package:flutter/material.dart';
 import 'package:app_flowy/plugins/grid/application/setting/group_bloc.dart';
 
@@ -14,10 +13,10 @@ import 'package:flutter_bloc/flutter_bloc.dart';
 
 class GridGroupList extends StatelessWidget {
   final String viewId;
-  final GridFieldCache fieldCache;
+  final GridFieldController fieldController;
   const GridGroupList({
     required this.viewId,
-    required this.fieldCache,
+    required this.fieldController,
     Key? key,
   }) : super(key: key);
 
@@ -26,13 +25,13 @@ class GridGroupList extends StatelessWidget {
     return BlocProvider(
       create: (context) => GridGroupBloc(
         viewId: viewId,
-        fieldCache: fieldCache,
+        fieldController: fieldController,
       )..add(const GridGroupEvent.initial()),
       child: BlocBuilder<GridGroupBloc, GridGroupState>(
         builder: (context, state) {
-          final cells = state.fields.map((field) {
+          final cells = state.fieldContexts.map((field) {
             return _GridGroupCell(
-              field: field,
+              fieldContext: field,
               key: ValueKey(field.id),
             );
           }).toList();
@@ -56,8 +55,9 @@ class GridGroupList extends StatelessWidget {
 }
 
 class _GridGroupCell extends StatelessWidget {
-  final FieldPB field;
-  const _GridGroupCell({required this.field, Key? key}) : super(key: key);
+  final GridFieldContext fieldContext;
+  const _GridGroupCell({required this.fieldContext, Key? key})
+      : super(key: key);
 
   @override
   Widget build(BuildContext context) {
@@ -73,9 +73,10 @@ class _GridGroupCell extends StatelessWidget {
     //                 ),
 
     return FlowyButton(
-      text: FlowyText.medium(field.name, fontSize: 12),
+      text: FlowyText.medium(fieldContext.name, fontSize: 12),
       hoverColor: theme.hover,
-      leftIcon: svgWidget(field.fieldType.iconName(), color: theme.iconColor),
+      leftIcon:
+          svgWidget(fieldContext.fieldType.iconName(), color: theme.iconColor),
       onTap: () {},
     );
   }

+ 18 - 14
frontend/app_flowy/lib/plugins/grid/presentation/widgets/toolbar/grid_property.dart

@@ -9,7 +9,6 @@ import 'package:flowy_infra_ui/style_widget/button.dart';
 import 'package:flowy_infra_ui/style_widget/icon_button.dart';
 import 'package:flowy_infra_ui/style_widget/text.dart';
 import 'package:flowy_infra_ui/widget/spacing.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:styled_widget/styled_widget.dart';
@@ -20,10 +19,10 @@ import '../header/field_editor.dart';
 
 class GridPropertyList extends StatelessWidget with FlowyOverlayDelegate {
   final String gridId;
-  final GridFieldCache fieldCache;
+  final GridFieldController fieldController;
   const GridPropertyList({
     required this.gridId,
-    required this.fieldCache,
+    required this.fieldController,
     Key? key,
   }) : super(key: key);
 
@@ -45,13 +44,13 @@ class GridPropertyList extends StatelessWidget with FlowyOverlayDelegate {
   Widget build(BuildContext context) {
     return BlocProvider(
       create: (context) =>
-          getIt<GridPropertyBloc>(param1: gridId, param2: fieldCache)
+          getIt<GridPropertyBloc>(param1: gridId, param2: fieldController)
             ..add(const GridPropertyEvent.initial()),
       child: BlocBuilder<GridPropertyBloc, GridPropertyState>(
         builder: (context, state) {
-          final cells = state.fields.map((field) {
+          final cells = state.fieldContexts.map((field) {
             return _GridPropertyCell(
-                gridId: gridId, field: field, key: ValueKey(field.id));
+                gridId: gridId, fieldContext: field, key: ValueKey(field.id));
           }).toList();
 
           return ListView.separated(
@@ -78,16 +77,17 @@ class GridPropertyList extends StatelessWidget with FlowyOverlayDelegate {
 }
 
 class _GridPropertyCell extends StatelessWidget {
-  final FieldPB field;
+  final GridFieldContext fieldContext;
   final String gridId;
-  const _GridPropertyCell({required this.gridId, required this.field, Key? key})
+  const _GridPropertyCell(
+      {required this.gridId, required this.fieldContext, Key? key})
       : super(key: key);
 
   @override
   Widget build(BuildContext context) {
     final theme = context.watch<AppTheme>();
 
-    final checkmark = field.visibility
+    final checkmark = fieldContext.visibility
         ? svgWidget('home/show', color: theme.iconColor)
         : svgWidget('home/hide', color: theme.iconColor);
 
@@ -105,7 +105,7 @@ class _GridPropertyCell extends StatelessWidget {
           onPressed: () {
             context.read<GridPropertyBloc>().add(
                 GridPropertyEvent.setFieldVisibility(
-                    field.id, !field.visibility));
+                    fieldContext.id, !fieldContext.visibility));
           },
           icon: checkmark.padding(all: 6),
         )
@@ -115,14 +115,18 @@ class _GridPropertyCell extends StatelessWidget {
 
   FlowyButton _editFieldButton(AppTheme theme, BuildContext context) {
     return FlowyButton(
-      text: FlowyText.medium(field.name, fontSize: 12),
+      text: FlowyText.medium(fieldContext.name, fontSize: 12),
       hoverColor: theme.hover,
-      leftIcon: svgWidget(field.fieldType.iconName(), color: theme.iconColor),
+      leftIcon:
+          svgWidget(fieldContext.fieldType.iconName(), color: theme.iconColor),
       onTap: () {
         FieldEditor(
           gridId: gridId,
-          fieldName: field.name,
-          typeOptionLoader: FieldTypeOptionLoader(gridId: gridId, field: field),
+          fieldName: fieldContext.name,
+          typeOptionLoader: FieldTypeOptionLoader(
+            gridId: gridId,
+            field: fieldContext.field,
+          ),
         ).show(context, anchorDirection: AnchorDirection.bottomRight);
       },
     );

+ 3 - 3
frontend/app_flowy/lib/plugins/grid/presentation/widgets/toolbar/grid_setting.dart

@@ -17,11 +17,11 @@ import 'grid_property.dart';
 
 class GridSettingContext {
   final String gridId;
-  final GridFieldCache fieldCache;
+  final GridFieldController fieldController;
 
   GridSettingContext({
     required this.gridId,
-    required this.fieldCache,
+    required this.fieldController,
   });
 }
 
@@ -44,7 +44,7 @@ class GridSettingList extends StatelessWidget {
           case GridSettingAction.properties:
             GridPropertyList(
                     gridId: settingContext.gridId,
-                    fieldCache: settingContext.fieldCache)
+                    fieldController: settingContext.fieldController)
                 .show(context);
             break;
         }

+ 3 - 3
frontend/app_flowy/lib/plugins/grid/presentation/widgets/toolbar/grid_toolbar.dart

@@ -11,10 +11,10 @@ import 'grid_setting.dart';
 
 class GridToolbarContext {
   final String gridId;
-  final GridFieldCache fieldCache;
+  final GridFieldController fieldController;
   GridToolbarContext({
     required this.gridId,
-    required this.fieldCache,
+    required this.fieldController,
   });
 }
 
@@ -26,7 +26,7 @@ class GridToolbar extends StatelessWidget {
   Widget build(BuildContext context) {
     final settingContext = GridSettingContext(
       gridId: toolbarContext.gridId,
-      fieldCache: toolbarContext.fieldCache,
+      fieldController: toolbarContext.fieldController,
     );
     return SizedBox(
       height: 40,

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

@@ -154,10 +154,10 @@ void _resolveGridDeps(GetIt getIt) {
     (view, _) => GridBloc(view: view),
   );
 
-  getIt.registerFactoryParam<GridHeaderBloc, String, GridFieldCache>(
-    (gridId, fieldCache) => GridHeaderBloc(
+  getIt.registerFactoryParam<GridHeaderBloc, String, GridFieldController>(
+    (gridId, fieldController) => GridHeaderBloc(
       gridId: gridId,
-      fieldCache: fieldCache,
+      fieldController: fieldController,
     ),
   );
 
@@ -200,7 +200,7 @@ void _resolveGridDeps(GetIt getIt) {
     ),
   );
 
-  getIt.registerFactoryParam<GridPropertyBloc, String, GridFieldCache>(
-    (gridId, cache) => GridPropertyBloc(gridId: gridId, fieldCache: cache),
+  getIt.registerFactoryParam<GridPropertyBloc, String, GridFieldController>(
+    (gridId, cache) => GridPropertyBloc(gridId: gridId, fieldController: cache),
   );
 }