|  | @@ -133,7 +133,7 @@ class IGridCellController<T, D> extends Equatable {
 | 
											
												
													
														|  |    final IGridCellDataPersistence<D> _cellDataPersistence;
 |  |    final IGridCellDataPersistence<D> _cellDataPersistence;
 | 
											
												
													
														|  |  
 |  |  
 | 
											
												
													
														|  |    CellListener? _cellListener;
 |  |    CellListener? _cellListener;
 | 
											
												
													
														|  | -  ValueNotifier<T?>? _cellDataNotifier;
 |  | 
 | 
											
												
													
														|  | 
 |  | +  CellDataNotifier<T?>? _cellDataNotifier;
 | 
											
												
													
														|  |  
 |  |  
 | 
											
												
													
														|  |    bool isListening = false;
 |  |    bool isListening = false;
 | 
											
												
													
														|  |    VoidCallback? _onFieldChangedFn;
 |  |    VoidCallback? _onFieldChangedFn;
 | 
											
										
											
												
													
														|  | @@ -170,8 +170,20 @@ class IGridCellController<T, D> extends Equatable {
 | 
											
												
													
														|  |  
 |  |  
 | 
											
												
													
														|  |    FieldType get fieldType => cellId.fieldInfo.fieldType;
 |  |    FieldType get fieldType => cellId.fieldInfo.fieldType;
 | 
											
												
													
														|  |  
 |  |  
 | 
											
												
													
														|  | 
 |  | +  /// Listen on the cell content or field changes
 | 
											
												
													
														|  | 
 |  | +  ///
 | 
											
												
													
														|  | 
 |  | +  /// An optional [listenWhenOnCellChanged] can be implemented for more
 | 
											
												
													
														|  | 
 |  | +  ///  granular control over when [listener] is called.
 | 
											
												
													
														|  | 
 |  | +  /// [listenWhenOnCellChanged] will be invoked on each [onCellChanged]
 | 
											
												
													
														|  | 
 |  | +  /// get called.
 | 
											
												
													
														|  | 
 |  | +  /// [listenWhenOnCellChanged] takes the previous `value` and current
 | 
											
												
													
														|  | 
 |  | +  /// `value` and must return a [bool] which determines whether or not
 | 
											
												
													
														|  | 
 |  | +  ///  the [onCellChanged] function will be invoked.
 | 
											
												
													
														|  | 
 |  | +  /// [onCellChanged] is optional and if omitted, it will default to `true`.
 | 
											
												
													
														|  | 
 |  | +  ///
 | 
											
												
													
														|  |    VoidCallback? startListening({
 |  |    VoidCallback? startListening({
 | 
											
												
													
														|  |      required void Function(T?) onCellChanged,
 |  |      required void Function(T?) onCellChanged,
 | 
											
												
													
														|  | 
 |  | +    bool Function(T? oldValue, T? newValue)? listenWhenOnCellChanged,
 | 
											
												
													
														|  |      VoidCallback? onCellFieldChanged,
 |  |      VoidCallback? onCellFieldChanged,
 | 
											
												
													
														|  |    }) {
 |  |    }) {
 | 
											
												
													
														|  |      if (isListening) {
 |  |      if (isListening) {
 | 
											
										
											
												
													
														|  | @@ -180,7 +192,10 @@ class IGridCellController<T, D> extends Equatable {
 | 
											
												
													
														|  |      }
 |  |      }
 | 
											
												
													
														|  |      isListening = true;
 |  |      isListening = true;
 | 
											
												
													
														|  |  
 |  |  
 | 
											
												
													
														|  | -    _cellDataNotifier = ValueNotifier(_cellsCache.get(_cacheKey));
 |  | 
 | 
											
												
													
														|  | 
 |  | +    _cellDataNotifier = CellDataNotifier(
 | 
											
												
													
														|  | 
 |  | +      value: _cellsCache.get(_cacheKey),
 | 
											
												
													
														|  | 
 |  | +      listenWhen: listenWhenOnCellChanged,
 | 
											
												
													
														|  | 
 |  | +    );
 | 
											
												
													
														|  |      _cellListener =
 |  |      _cellListener =
 | 
											
												
													
														|  |          CellListener(rowId: cellId.rowId, fieldId: cellId.fieldInfo.id);
 |  |          CellListener(rowId: cellId.rowId, fieldId: cellId.fieldInfo.id);
 | 
											
												
													
														|  |  
 |  |  
 | 
											
										
											
												
													
														|  | @@ -255,24 +270,21 @@ class IGridCellController<T, D> extends Equatable {
 | 
											
												
													
														|  |    /// You can set [deduplicate] to true (default is false) to reduce the save operation.
 |  |    /// You can set [deduplicate] to true (default is false) to reduce the save operation.
 | 
											
												
													
														|  |    /// It's useful when you call this method when user editing the [TextField].
 |  |    /// It's useful when you call this method when user editing the [TextField].
 | 
											
												
													
														|  |    /// The default debounce interval is 300 milliseconds.
 |  |    /// The default debounce interval is 300 milliseconds.
 | 
											
												
													
														|  | -  void saveCellData(D data,
 |  | 
 | 
											
												
													
														|  | -      {bool deduplicate = false,
 |  | 
 | 
											
												
													
														|  | -      void Function(Option<FlowyError>)? resultCallback}) async {
 |  | 
 | 
											
												
													
														|  | 
 |  | +  void saveCellData(
 | 
											
												
													
														|  | 
 |  | +    D data, {
 | 
											
												
													
														|  | 
 |  | +    bool deduplicate = false,
 | 
											
												
													
														|  | 
 |  | +    void Function(Option<FlowyError>)? onFinish,
 | 
											
												
													
														|  | 
 |  | +  }) async {
 | 
											
												
													
														|  | 
 |  | +    _loadDataOperation?.cancel();
 | 
											
												
													
														|  |      if (deduplicate) {
 |  |      if (deduplicate) {
 | 
											
												
													
														|  | -      _loadDataOperation?.cancel();
 |  | 
 | 
											
												
													
														|  | -
 |  | 
 | 
											
												
													
														|  |        _saveDataOperation?.cancel();
 |  |        _saveDataOperation?.cancel();
 | 
											
												
													
														|  |        _saveDataOperation = Timer(const Duration(milliseconds: 300), () async {
 |  |        _saveDataOperation = Timer(const Duration(milliseconds: 300), () async {
 | 
											
												
													
														|  |          final result = await _cellDataPersistence.save(data);
 |  |          final result = await _cellDataPersistence.save(data);
 | 
											
												
													
														|  | -        if (resultCallback != null) {
 |  | 
 | 
											
												
													
														|  | -          resultCallback(result);
 |  | 
 | 
											
												
													
														|  | -        }
 |  | 
 | 
											
												
													
														|  | 
 |  | +        onFinish?.call(result);
 | 
											
												
													
														|  |        });
 |  |        });
 | 
											
												
													
														|  |      } else {
 |  |      } else {
 | 
											
												
													
														|  |        final result = await _cellDataPersistence.save(data);
 |  |        final result = await _cellDataPersistence.save(data);
 | 
											
												
													
														|  | -      if (resultCallback != null) {
 |  | 
 | 
											
												
													
														|  | -        resultCallback(result);
 |  | 
 | 
											
												
													
														|  | -      }
 |  | 
 | 
											
												
													
														|  | 
 |  | +      onFinish?.call(result);
 | 
											
												
													
														|  |      }
 |  |      }
 | 
											
												
													
														|  |    }
 |  |    }
 | 
											
												
													
														|  |  
 |  |  
 | 
											
										
											
												
													
														|  | @@ -302,6 +314,7 @@ class IGridCellController<T, D> extends Equatable {
 | 
											
												
													
														|  |      await _cellListener?.stop();
 |  |      await _cellListener?.stop();
 | 
											
												
													
														|  |      _loadDataOperation?.cancel();
 |  |      _loadDataOperation?.cancel();
 | 
											
												
													
														|  |      _saveDataOperation?.cancel();
 |  |      _saveDataOperation?.cancel();
 | 
											
												
													
														|  | 
 |  | +    _cellDataNotifier?.dispose();
 | 
											
												
													
														|  |      _cellDataNotifier = null;
 |  |      _cellDataNotifier = null;
 | 
											
												
													
														|  |  
 |  |  
 | 
											
												
													
														|  |      if (_onFieldChangedFn != null) {
 |  |      if (_onFieldChangedFn != null) {
 | 
											
										
											
												
													
														|  | @@ -343,3 +356,23 @@ class GridCellFieldNotifierImpl extends IGridCellFieldNotifier {
 | 
											
												
													
														|  |      );
 |  |      );
 | 
											
												
													
														|  |    }
 |  |    }
 | 
											
												
													
														|  |  }
 |  |  }
 | 
											
												
													
														|  | 
 |  | +
 | 
											
												
													
														|  | 
 |  | +class CellDataNotifier<T> extends ChangeNotifier {
 | 
											
												
													
														|  | 
 |  | +  T _value;
 | 
											
												
													
														|  | 
 |  | +  bool Function(T? oldValue, T? newValue)? listenWhen;
 | 
											
												
													
														|  | 
 |  | +  CellDataNotifier({required T value, this.listenWhen}) : _value = value;
 | 
											
												
													
														|  | 
 |  | +
 | 
											
												
													
														|  | 
 |  | +  set value(T newValue) {
 | 
											
												
													
														|  | 
 |  | +    if (listenWhen?.call(_value, newValue) ?? false) {
 | 
											
												
													
														|  | 
 |  | +      _value = newValue;
 | 
											
												
													
														|  | 
 |  | +      notifyListeners();
 | 
											
												
													
														|  | 
 |  | +    } else {
 | 
											
												
													
														|  | 
 |  | +      if (_value != newValue) {
 | 
											
												
													
														|  | 
 |  | +        _value = newValue;
 | 
											
												
													
														|  | 
 |  | +        notifyListeners();
 | 
											
												
													
														|  | 
 |  | +      }
 | 
											
												
													
														|  | 
 |  | +    }
 | 
											
												
													
														|  | 
 |  | +  }
 | 
											
												
													
														|  | 
 |  | +
 | 
											
												
													
														|  | 
 |  | +  T get value => _value;
 | 
											
												
													
														|  | 
 |  | +}
 |