mirror of
https://github.com/AppFlowy-IO/AppFlowy.git
synced 2024-08-30 18:12:39 +00:00
chore: reload row when fields were changed
This commit is contained in:
parent
6b5126b12e
commit
518d11162b
@ -365,4 +365,11 @@ class GridCell with _$GridCell {
|
||||
required Field field,
|
||||
Cell? cell,
|
||||
}) = _GridCell;
|
||||
|
||||
// ignore: unused_element
|
||||
const GridCell._();
|
||||
|
||||
String cellId() {
|
||||
return rowId + field.id + "${field.fieldType}";
|
||||
}
|
||||
}
|
||||
|
@ -1,5 +1,7 @@
|
||||
import 'dart:collection';
|
||||
import 'package:app_flowy/workspace/application/grid/cell/cell_service.dart';
|
||||
import 'package:equatable/equatable.dart';
|
||||
import 'package:flowy_sdk/protobuf/flowy-grid-data-model/grid.pb.dart' show Field;
|
||||
import 'package:flutter_bloc/flutter_bloc.dart';
|
||||
import 'package:freezed_annotation/freezed_annotation.dart';
|
||||
import 'dart:async';
|
||||
@ -28,7 +30,13 @@ class RowBloc extends Bloc<RowEvent, RowState> {
|
||||
_rowService.createRow();
|
||||
},
|
||||
didReceiveCellDatas: (_DidReceiveCellDatas value) async {
|
||||
emit(state.copyWith(cellDataMap: value.cellData));
|
||||
final fields = value.gridCellMap.values.map((e) => CellSnapshot(e.field)).toList();
|
||||
final snapshots = UnmodifiableListView(fields);
|
||||
emit(state.copyWith(
|
||||
gridCellMap: value.gridCellMap,
|
||||
snapshots: snapshots,
|
||||
changeReason: value.reason,
|
||||
));
|
||||
},
|
||||
);
|
||||
},
|
||||
@ -47,7 +55,7 @@ class RowBloc extends Bloc<RowEvent, RowState> {
|
||||
Future<void> _startListening() async {
|
||||
_rowListenFn = _rowCache.addRowListener(
|
||||
rowId: state.rowData.rowId,
|
||||
onUpdated: (cellDatas) => add(RowEvent.didReceiveCellDatas(cellDatas)),
|
||||
onUpdated: (cellDatas, reason) => add(RowEvent.didReceiveCellDatas(cellDatas, reason)),
|
||||
listenWhen: () => !isClosed,
|
||||
);
|
||||
}
|
||||
@ -57,18 +65,35 @@ class RowBloc extends Bloc<RowEvent, RowState> {
|
||||
class RowEvent with _$RowEvent {
|
||||
const factory RowEvent.initial() = _InitialRow;
|
||||
const factory RowEvent.createRow() = _CreateRow;
|
||||
const factory RowEvent.didReceiveCellDatas(GridCellMap cellData) = _DidReceiveCellDatas;
|
||||
const factory RowEvent.didReceiveCellDatas(GridCellMap gridCellMap, GridRowChangeReason reason) =
|
||||
_DidReceiveCellDatas;
|
||||
}
|
||||
|
||||
@freezed
|
||||
class RowState with _$RowState {
|
||||
const factory RowState({
|
||||
required GridRow rowData,
|
||||
required GridCellMap cellDataMap,
|
||||
required GridCellMap gridCellMap,
|
||||
required UnmodifiableListView<CellSnapshot> snapshots,
|
||||
GridRowChangeReason? changeReason,
|
||||
}) = _RowState;
|
||||
|
||||
factory RowState.initial(GridRow rowData, GridCellMap cellDataMap) => RowState(
|
||||
rowData: rowData,
|
||||
cellDataMap: cellDataMap,
|
||||
gridCellMap: cellDataMap,
|
||||
snapshots: UnmodifiableListView(cellDataMap.values.map((e) => CellSnapshot(e.field)).toList()),
|
||||
);
|
||||
}
|
||||
|
||||
class CellSnapshot extends Equatable {
|
||||
final Field _field;
|
||||
|
||||
const CellSnapshot(Field field) : _field = field;
|
||||
|
||||
@override
|
||||
List<Object?> get props => [
|
||||
_field.id,
|
||||
_field.fieldType,
|
||||
_field.visibility,
|
||||
];
|
||||
}
|
||||
|
@ -42,7 +42,7 @@ class RowDetailBloc extends Bloc<RowDetailEvent, RowDetailState> {
|
||||
Future<void> _startListening() async {
|
||||
_rowListenFn = _rowCache.addRowListener(
|
||||
rowId: rowData.rowId,
|
||||
onUpdated: (cellDatas) => add(RowDetailEvent.didReceiveCellDatas(cellDatas.values.toList())),
|
||||
onUpdated: (cellDatas, reason) => add(RowDetailEvent.didReceiveCellDatas(cellDatas.values.toList())),
|
||||
listenWhen: () => !isClosed,
|
||||
);
|
||||
}
|
||||
|
@ -83,7 +83,7 @@ class GridRowCache {
|
||||
|
||||
RowUpdateCallback addRowListener({
|
||||
required String rowId,
|
||||
void Function(GridCellMap)? onUpdated,
|
||||
void Function(GridCellMap, GridRowChangeReason)? onUpdated,
|
||||
bool Function()? listenWhen,
|
||||
}) {
|
||||
listenrHandler() async {
|
||||
@ -99,7 +99,7 @@ class GridRowCache {
|
||||
final row = _rowsNotifier.rowDataWithId(rowId);
|
||||
if (row != null) {
|
||||
final GridCellMap cellDataMap = _makeGridCells(rowId, row);
|
||||
onUpdated(cellDataMap);
|
||||
onUpdated(cellDataMap, _rowsNotifier._changeReason);
|
||||
}
|
||||
}
|
||||
|
||||
@ -339,7 +339,7 @@ class GridRow with _$GridRow {
|
||||
const factory GridRow({
|
||||
required String gridId,
|
||||
required String rowId,
|
||||
required List<Field> fields,
|
||||
required UnmodifiableListView<Field> fields,
|
||||
required double height,
|
||||
Row? data,
|
||||
}) = _GridRow;
|
||||
|
@ -15,7 +15,7 @@ import 'selection_cell/selection_cell.dart';
|
||||
import 'text_cell.dart';
|
||||
|
||||
GridCellWidget buildGridCellWidget(GridCell gridCell, GridCellCache cellCache, {GridCellStyle? style}) {
|
||||
final key = ValueKey(gridCell.rowId + gridCell.field.id);
|
||||
final key = ValueKey(gridCell.cellId());
|
||||
|
||||
final cellContextBuilder = GridCellContextBuilder(gridCell: gridCell, cellCache: cellCache);
|
||||
|
||||
@ -82,6 +82,25 @@ class GridCellRequestFocusNotifier extends ChangeNotifier {
|
||||
|
||||
abstract class GridCellStyle {}
|
||||
|
||||
class CellSingleFocusNode extends FocusNode {
|
||||
VoidCallback? _listener;
|
||||
|
||||
void setSingleListener(VoidCallback listener) {
|
||||
if (_listener != null) {
|
||||
removeListener(_listener!);
|
||||
}
|
||||
|
||||
_listener = listener;
|
||||
super.addListener(listener);
|
||||
}
|
||||
|
||||
void removeSingleListener() {
|
||||
if (_listener != null) {
|
||||
removeListener(_listener!);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
class CellStateNotifier extends ChangeNotifier {
|
||||
bool _isFocus = false;
|
||||
bool _onEnter = false;
|
||||
|
@ -22,7 +22,7 @@ class NumberCell extends GridCellWidget {
|
||||
class _NumberCellState extends State<NumberCell> {
|
||||
late NumberCellBloc _cellBloc;
|
||||
late TextEditingController _controller;
|
||||
late FocusNode _focusNode;
|
||||
late CellSingleFocusNode _focusNode;
|
||||
Timer? _delayOperation;
|
||||
|
||||
@override
|
||||
@ -30,11 +30,8 @@ class _NumberCellState extends State<NumberCell> {
|
||||
final cellContext = widget.cellContextBuilder.build();
|
||||
_cellBloc = getIt<NumberCellBloc>(param1: cellContext)..add(const NumberCellEvent.initial());
|
||||
_controller = TextEditingController(text: _cellBloc.state.content);
|
||||
_focusNode = FocusNode();
|
||||
_focusNode.addListener(() {
|
||||
widget.onFocus.value = _focusNode.hasFocus;
|
||||
focusChanged();
|
||||
});
|
||||
_focusNode = CellSingleFocusNode();
|
||||
_listenFocusNode();
|
||||
super.initState();
|
||||
}
|
||||
|
||||
@ -72,10 +69,19 @@ class _NumberCellState extends State<NumberCell> {
|
||||
widget.requestFocus.removeAllListener();
|
||||
_delayOperation?.cancel();
|
||||
_cellBloc.close();
|
||||
_focusNode.removeSingleListener();
|
||||
_focusNode.dispose();
|
||||
super.dispose();
|
||||
}
|
||||
|
||||
@override
|
||||
void didUpdateWidget(covariant NumberCell oldWidget) {
|
||||
if (oldWidget != widget) {
|
||||
_listenFocusNode();
|
||||
}
|
||||
super.didUpdateWidget(oldWidget);
|
||||
}
|
||||
|
||||
Future<void> focusChanged() async {
|
||||
if (mounted) {
|
||||
_delayOperation?.cancel();
|
||||
@ -92,6 +98,14 @@ class _NumberCellState extends State<NumberCell> {
|
||||
}
|
||||
}
|
||||
|
||||
void _listenFocusNode() {
|
||||
widget.onFocus.value = _focusNode.hasFocus;
|
||||
_focusNode.setSingleListener(() {
|
||||
widget.onFocus.value = _focusNode.hasFocus;
|
||||
focusChanged();
|
||||
});
|
||||
}
|
||||
|
||||
void _listenCellRequestFocus(BuildContext context) {
|
||||
widget.requestFocus.addListener(() {
|
||||
if (_focusNode.hasFocus == false && _focusNode.canRequestFocus) {
|
||||
|
@ -35,9 +35,7 @@ class GridTextCell extends GridCellWidget {
|
||||
class _GridTextCellState extends State<GridTextCell> {
|
||||
late TextCellBloc _cellBloc;
|
||||
late TextEditingController _controller;
|
||||
late FocusNode _focusNode;
|
||||
|
||||
VoidCallback? _focusNodeListener;
|
||||
late CellSingleFocusNode _focusNode;
|
||||
Timer? _delayOperation;
|
||||
|
||||
@override
|
||||
@ -46,44 +44,37 @@ class _GridTextCellState extends State<GridTextCell> {
|
||||
_cellBloc = getIt<TextCellBloc>(param1: cellContext);
|
||||
_cellBloc.add(const TextCellEvent.initial());
|
||||
_controller = TextEditingController(text: _cellBloc.state.content);
|
||||
_focusNode = FocusNode();
|
||||
_focusNode.addListener(() {
|
||||
widget.onFocus.value = _focusNode.hasFocus;
|
||||
focusChanged();
|
||||
});
|
||||
_focusNode = CellSingleFocusNode();
|
||||
|
||||
_listenFocusNode();
|
||||
_listenRequestFocus(context);
|
||||
super.initState();
|
||||
}
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
_listenCellRequestFocus(context);
|
||||
|
||||
return BlocProvider.value(
|
||||
value: _cellBloc,
|
||||
child: BlocConsumer<TextCellBloc, TextCellState>(
|
||||
child: BlocListener<TextCellBloc, TextCellState>(
|
||||
listener: (context, state) {
|
||||
if (_controller.text != state.content) {
|
||||
_controller.text = state.content;
|
||||
}
|
||||
},
|
||||
buildWhen: (previous, current) => previous.content != current.content,
|
||||
builder: (context, state) {
|
||||
return TextField(
|
||||
controller: _controller,
|
||||
focusNode: _focusNode,
|
||||
onChanged: (value) => focusChanged(),
|
||||
onEditingComplete: () => _focusNode.unfocus(),
|
||||
maxLines: null,
|
||||
style: const TextStyle(fontSize: 14, fontWeight: FontWeight.w500),
|
||||
decoration: InputDecoration(
|
||||
contentPadding: EdgeInsets.zero,
|
||||
border: InputBorder.none,
|
||||
hintText: widget.cellStyle?.placeholder,
|
||||
isDense: true,
|
||||
),
|
||||
);
|
||||
},
|
||||
child: TextField(
|
||||
controller: _controller,
|
||||
focusNode: _focusNode,
|
||||
onChanged: (value) => focusChanged(),
|
||||
onEditingComplete: () => _focusNode.unfocus(),
|
||||
maxLines: null,
|
||||
style: const TextStyle(fontSize: 14, fontWeight: FontWeight.w500),
|
||||
decoration: InputDecoration(
|
||||
contentPadding: EdgeInsets.zero,
|
||||
border: InputBorder.none,
|
||||
hintText: widget.cellStyle?.placeholder,
|
||||
isDense: true,
|
||||
),
|
||||
),
|
||||
),
|
||||
);
|
||||
}
|
||||
@ -93,17 +84,29 @@ class _GridTextCellState extends State<GridTextCell> {
|
||||
widget.requestFocus.removeAllListener();
|
||||
_delayOperation?.cancel();
|
||||
_cellBloc.close();
|
||||
_focusNode.removeSingleListener();
|
||||
_focusNode.dispose();
|
||||
|
||||
super.dispose();
|
||||
}
|
||||
|
||||
@override
|
||||
void didUpdateWidget(covariant GridTextCell oldWidget) {
|
||||
// TODO: implement didUpdateWidget
|
||||
if (oldWidget != widget) {
|
||||
_listenFocusNode();
|
||||
}
|
||||
super.didUpdateWidget(oldWidget);
|
||||
}
|
||||
|
||||
void _listenCellRequestFocus(BuildContext context) {
|
||||
void _listenFocusNode() {
|
||||
widget.onFocus.value = _focusNode.hasFocus;
|
||||
_focusNode.setSingleListener(() {
|
||||
widget.onFocus.value = _focusNode.hasFocus;
|
||||
focusChanged();
|
||||
});
|
||||
}
|
||||
|
||||
void _listenRequestFocus(BuildContext context) {
|
||||
widget.requestFocus.addListener(() {
|
||||
if (_focusNode.hasFocus == false && _focusNode.canRequestFocus) {
|
||||
FocusScope.of(context).requestFocus(_focusNode);
|
||||
|
@ -4,6 +4,7 @@ import 'package:app_flowy/workspace/presentation/plugins/grid/src/widgets/cell/p
|
||||
import 'package:flowy_infra/image.dart';
|
||||
import 'package:flowy_infra/theme.dart';
|
||||
import 'package:flowy_infra_ui/style_widget/icon_button.dart';
|
||||
import 'package:flutter/foundation.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:flutter_bloc/flutter_bloc.dart';
|
||||
import 'package:provider/provider.dart';
|
||||
@ -153,14 +154,14 @@ class _RowCells extends StatelessWidget {
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return BlocBuilder<RowBloc, RowState>(
|
||||
buildWhen: (previous, current) => previous.cellDataMap != current.cellDataMap,
|
||||
buildWhen: (previous, current) => !listEquals(previous.snapshots, current.snapshots),
|
||||
builder: (context, state) {
|
||||
return IntrinsicHeight(
|
||||
child: Row(
|
||||
mainAxisSize: MainAxisSize.max,
|
||||
mainAxisAlignment: MainAxisAlignment.start,
|
||||
crossAxisAlignment: CrossAxisAlignment.stretch,
|
||||
children: _makeCells(context, state.cellDataMap),
|
||||
children: _makeCells(context, state.gridCellMap),
|
||||
));
|
||||
},
|
||||
);
|
||||
|
Loading…
Reference in New Issue
Block a user