mirror of
				https://github.com/AppFlowy-IO/AppFlowy.git
				synced 2025-11-03 19:43:52 +00:00 
			
		
		
		
	chore: optimize cell focus
This commit is contained in:
		
							parent
							
								
									d0b4abb0c8
								
							
						
					
					
						commit
						f972cdd3fb
					
				@ -44,7 +44,16 @@ class BlankCell extends StatelessWidget {
 | 
				
			|||||||
abstract class GridCellWidget extends HoverWidget {
 | 
					abstract class GridCellWidget extends HoverWidget {
 | 
				
			||||||
  @override
 | 
					  @override
 | 
				
			||||||
  final ValueNotifier<bool> onFocus = ValueNotifier<bool>(false);
 | 
					  final ValueNotifier<bool> onFocus = ValueNotifier<bool>(false);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  final GridCellRequestFocusNotifier requestFocus = GridCellRequestFocusNotifier();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  GridCellWidget({Key? key}) : super(key: key);
 | 
					  GridCellWidget({Key? key}) : super(key: key);
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					class GridCellRequestFocusNotifier extends ChangeNotifier {
 | 
				
			||||||
 | 
					  void notify() {
 | 
				
			||||||
 | 
					    notifyListeners();
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
abstract class GridCellStyle {}
 | 
					abstract class GridCellStyle {}
 | 
				
			||||||
 | 
				
			|||||||
@ -1,3 +1,4 @@
 | 
				
			|||||||
 | 
					import 'package:app_flowy/workspace/presentation/plugins/grid/src/widgets/row/grid_row.dart';
 | 
				
			||||||
import 'package:flowy_infra/theme.dart';
 | 
					import 'package:flowy_infra/theme.dart';
 | 
				
			||||||
import 'package:flutter/material.dart';
 | 
					import 'package:flutter/material.dart';
 | 
				
			||||||
import 'package:provider/provider.dart';
 | 
					import 'package:provider/provider.dart';
 | 
				
			||||||
@ -31,17 +32,20 @@ class CellContainer extends StatelessWidget {
 | 
				
			|||||||
  final GridCellWidget child;
 | 
					  final GridCellWidget child;
 | 
				
			||||||
  final Widget? expander;
 | 
					  final Widget? expander;
 | 
				
			||||||
  final double width;
 | 
					  final double width;
 | 
				
			||||||
 | 
					  final RegionStateNotifier rowStateNotifier;
 | 
				
			||||||
  const CellContainer({
 | 
					  const CellContainer({
 | 
				
			||||||
    Key? key,
 | 
					    Key? key,
 | 
				
			||||||
    required this.child,
 | 
					    required this.child,
 | 
				
			||||||
    required this.width,
 | 
					    required this.width,
 | 
				
			||||||
 | 
					    required this.rowStateNotifier,
 | 
				
			||||||
    this.expander,
 | 
					    this.expander,
 | 
				
			||||||
  }) : super(key: key);
 | 
					  }) : super(key: key);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  @override
 | 
					  @override
 | 
				
			||||||
  Widget build(BuildContext context) {
 | 
					  Widget build(BuildContext context) {
 | 
				
			||||||
    return ChangeNotifierProvider(
 | 
					    return ChangeNotifierProxyProvider<RegionStateNotifier, CellStateNotifier>(
 | 
				
			||||||
      create: (_) => CellStateNotifier(),
 | 
					      create: (_) => CellStateNotifier(),
 | 
				
			||||||
 | 
					      update: (_, row, cell) => cell!..onEnter = row.onEnter,
 | 
				
			||||||
      child: Selector<CellStateNotifier, bool>(
 | 
					      child: Selector<CellStateNotifier, bool>(
 | 
				
			||||||
        selector: (context, notifier) => notifier.isFocus,
 | 
					        selector: (context, notifier) => notifier.isFocus,
 | 
				
			||||||
        builder: (context, isFocus, _) {
 | 
					        builder: (context, isFocus, _) {
 | 
				
			||||||
@ -54,11 +58,15 @@ class CellContainer extends StatelessWidget {
 | 
				
			|||||||
            container = _CellEnterRegion(child: container, expander: expander!);
 | 
					            container = _CellEnterRegion(child: container, expander: expander!);
 | 
				
			||||||
          }
 | 
					          }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
          return Container(
 | 
					          return GestureDetector(
 | 
				
			||||||
            constraints: BoxConstraints(maxWidth: width),
 | 
					            behavior: HitTestBehavior.translucent,
 | 
				
			||||||
            decoration: _makeBoxDecoration(context, isFocus),
 | 
					            onTap: () => child.requestFocus.notify(),
 | 
				
			||||||
            padding: GridSize.cellContentInsets,
 | 
					            child: Container(
 | 
				
			||||||
            child: container,
 | 
					              constraints: BoxConstraints(maxWidth: width),
 | 
				
			||||||
 | 
					              decoration: _makeBoxDecoration(context, isFocus),
 | 
				
			||||||
 | 
					              padding: GridSize.cellContentInsets,
 | 
				
			||||||
 | 
					              child: container,
 | 
				
			||||||
 | 
					            ),
 | 
				
			||||||
          );
 | 
					          );
 | 
				
			||||||
        },
 | 
					        },
 | 
				
			||||||
      ),
 | 
					      ),
 | 
				
			||||||
 | 
				
			|||||||
@ -24,6 +24,7 @@ class _CheckboxCellState extends State<CheckboxCell> {
 | 
				
			|||||||
  void initState() {
 | 
					  void initState() {
 | 
				
			||||||
    final cellContext = widget.cellContextBuilder.build();
 | 
					    final cellContext = widget.cellContextBuilder.build();
 | 
				
			||||||
    _cellBloc = getIt<CheckboxCellBloc>(param1: cellContext)..add(const CheckboxCellEvent.initial());
 | 
					    _cellBloc = getIt<CheckboxCellBloc>(param1: cellContext)..add(const CheckboxCellEvent.initial());
 | 
				
			||||||
 | 
					    _listenCellRequestFocus();
 | 
				
			||||||
    super.initState();
 | 
					    super.initState();
 | 
				
			||||||
  }
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@ -48,9 +49,21 @@ class _CheckboxCellState extends State<CheckboxCell> {
 | 
				
			|||||||
    );
 | 
					    );
 | 
				
			||||||
  }
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  @override
 | 
				
			||||||
 | 
					  void didUpdateWidget(covariant CheckboxCell oldWidget) {
 | 
				
			||||||
 | 
					    _listenCellRequestFocus();
 | 
				
			||||||
 | 
					    super.didUpdateWidget(oldWidget);
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  @override
 | 
					  @override
 | 
				
			||||||
  Future<void> dispose() async {
 | 
					  Future<void> dispose() async {
 | 
				
			||||||
    _cellBloc.close();
 | 
					    _cellBloc.close();
 | 
				
			||||||
    super.dispose();
 | 
					    super.dispose();
 | 
				
			||||||
  }
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  void _listenCellRequestFocus() {
 | 
				
			||||||
 | 
					    widget.requestFocus.addListener(() {
 | 
				
			||||||
 | 
					      _cellBloc.add(const CheckboxCellEvent.select());
 | 
				
			||||||
 | 
					    });
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
				
			|||||||
@ -23,6 +23,7 @@ class _NumberCellState extends State<NumberCell> {
 | 
				
			|||||||
  late NumberCellBloc _cellBloc;
 | 
					  late NumberCellBloc _cellBloc;
 | 
				
			||||||
  late TextEditingController _controller;
 | 
					  late TextEditingController _controller;
 | 
				
			||||||
  late FocusNode _focusNode;
 | 
					  late FocusNode _focusNode;
 | 
				
			||||||
 | 
					  VoidCallback? _focusListener;
 | 
				
			||||||
  Timer? _delayOperation;
 | 
					  Timer? _delayOperation;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  @override
 | 
					  @override
 | 
				
			||||||
@ -40,6 +41,7 @@ class _NumberCellState extends State<NumberCell> {
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
  @override
 | 
					  @override
 | 
				
			||||||
  Widget build(BuildContext context) {
 | 
					  Widget build(BuildContext context) {
 | 
				
			||||||
 | 
					    _listenCellRequestFocus(context);
 | 
				
			||||||
    return BlocProvider.value(
 | 
					    return BlocProvider.value(
 | 
				
			||||||
      value: _cellBloc,
 | 
					      value: _cellBloc,
 | 
				
			||||||
      child: BlocConsumer<NumberCellBloc, NumberCellState>(
 | 
					      child: BlocConsumer<NumberCellBloc, NumberCellState>(
 | 
				
			||||||
@ -68,6 +70,9 @@ class _NumberCellState extends State<NumberCell> {
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
  @override
 | 
					  @override
 | 
				
			||||||
  Future<void> dispose() async {
 | 
					  Future<void> dispose() async {
 | 
				
			||||||
 | 
					    if (_focusListener != null) {
 | 
				
			||||||
 | 
					      widget.requestFocus.removeListener(_focusListener!);
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
    _delayOperation?.cancel();
 | 
					    _delayOperation?.cancel();
 | 
				
			||||||
    _cellBloc.close();
 | 
					    _cellBloc.close();
 | 
				
			||||||
    _focusNode.dispose();
 | 
					    _focusNode.dispose();
 | 
				
			||||||
@ -89,4 +94,19 @@ class _NumberCellState extends State<NumberCell> {
 | 
				
			|||||||
      });
 | 
					      });
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
  }
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  void _listenCellRequestFocus(BuildContext context) {
 | 
				
			||||||
 | 
					    if (_focusListener != null) {
 | 
				
			||||||
 | 
					      widget.requestFocus.removeListener(_focusListener!);
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    focusListener() {
 | 
				
			||||||
 | 
					      if (_focusNode.hasFocus == false && _focusNode.canRequestFocus) {
 | 
				
			||||||
 | 
					        FocusScope.of(context).requestFocus(_focusNode);
 | 
				
			||||||
 | 
					      }
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    _focusListener = focusListener;
 | 
				
			||||||
 | 
					    widget.requestFocus.addListener(focusListener);
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
				
			|||||||
@ -36,7 +36,7 @@ class _GridTextCellState extends State<GridTextCell> {
 | 
				
			|||||||
  late TextCellBloc _cellBloc;
 | 
					  late TextCellBloc _cellBloc;
 | 
				
			||||||
  late TextEditingController _controller;
 | 
					  late TextEditingController _controller;
 | 
				
			||||||
  late FocusNode _focusNode;
 | 
					  late FocusNode _focusNode;
 | 
				
			||||||
 | 
					  VoidCallback? _focusListener;
 | 
				
			||||||
  Timer? _delayOperation;
 | 
					  Timer? _delayOperation;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  @override
 | 
					  @override
 | 
				
			||||||
@ -50,11 +50,14 @@ class _GridTextCellState extends State<GridTextCell> {
 | 
				
			|||||||
      widget.onFocus.value = _focusNode.hasFocus;
 | 
					      widget.onFocus.value = _focusNode.hasFocus;
 | 
				
			||||||
      focusChanged();
 | 
					      focusChanged();
 | 
				
			||||||
    });
 | 
					    });
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    super.initState();
 | 
					    super.initState();
 | 
				
			||||||
  }
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  @override
 | 
					  @override
 | 
				
			||||||
  Widget build(BuildContext context) {
 | 
					  Widget build(BuildContext context) {
 | 
				
			||||||
 | 
					    _listenCellRequestFocus(context);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    return BlocProvider.value(
 | 
					    return BlocProvider.value(
 | 
				
			||||||
      value: _cellBloc,
 | 
					      value: _cellBloc,
 | 
				
			||||||
      child: BlocConsumer<TextCellBloc, TextCellState>(
 | 
					      child: BlocConsumer<TextCellBloc, TextCellState>(
 | 
				
			||||||
@ -84,8 +87,26 @@ class _GridTextCellState extends State<GridTextCell> {
 | 
				
			|||||||
    );
 | 
					    );
 | 
				
			||||||
  }
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  void _listenCellRequestFocus(BuildContext context) {
 | 
				
			||||||
 | 
					    if (_focusListener != null) {
 | 
				
			||||||
 | 
					      widget.requestFocus.removeListener(_focusListener!);
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    focusListener() {
 | 
				
			||||||
 | 
					      if (_focusNode.hasFocus == false && _focusNode.canRequestFocus) {
 | 
				
			||||||
 | 
					        FocusScope.of(context).requestFocus(_focusNode);
 | 
				
			||||||
 | 
					      }
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    _focusListener = focusListener;
 | 
				
			||||||
 | 
					    widget.requestFocus.addListener(focusListener);
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  @override
 | 
					  @override
 | 
				
			||||||
  Future<void> dispose() async {
 | 
					  Future<void> dispose() async {
 | 
				
			||||||
 | 
					    if (_focusListener != null) {
 | 
				
			||||||
 | 
					      widget.requestFocus.removeListener(_focusListener!);
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
    _delayOperation?.cancel();
 | 
					    _delayOperation?.cancel();
 | 
				
			||||||
    _cellBloc.close();
 | 
					    _cellBloc.close();
 | 
				
			||||||
    _focusNode.dispose();
 | 
					    _focusNode.dispose();
 | 
				
			||||||
 | 
				
			|||||||
@ -88,7 +88,7 @@ class _RowLeading extends StatelessWidget {
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
  @override
 | 
					  @override
 | 
				
			||||||
  Widget build(BuildContext context) {
 | 
					  Widget build(BuildContext context) {
 | 
				
			||||||
    return Consumer<_RegionStateNotifier>(
 | 
					    return Consumer<RegionStateNotifier>(
 | 
				
			||||||
      builder: (context, state, _) {
 | 
					      builder: (context, state, _) {
 | 
				
			||||||
        return SizedBox(width: GridSize.leadingHeaderPadding, child: state.onEnter ? _activeWidget() : null);
 | 
					        return SizedBox(width: GridSize.leadingHeaderPadding, child: state.onEnter ? _activeWidget() : null);
 | 
				
			||||||
      },
 | 
					      },
 | 
				
			||||||
@ -164,13 +164,13 @@ class _RowCells extends StatelessWidget {
 | 
				
			|||||||
        return Row(
 | 
					        return Row(
 | 
				
			||||||
          mainAxisSize: MainAxisSize.min,
 | 
					          mainAxisSize: MainAxisSize.min,
 | 
				
			||||||
          mainAxisAlignment: MainAxisAlignment.center,
 | 
					          mainAxisAlignment: MainAxisAlignment.center,
 | 
				
			||||||
          children: _makeCells(state.cellDataMap),
 | 
					          children: _makeCells(context, state.cellDataMap),
 | 
				
			||||||
        );
 | 
					        );
 | 
				
			||||||
      },
 | 
					      },
 | 
				
			||||||
    );
 | 
					    );
 | 
				
			||||||
  }
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  List<Widget> _makeCells(GridCellMap gridCellMap) {
 | 
					  List<Widget> _makeCells(BuildContext context, GridCellMap gridCellMap) {
 | 
				
			||||||
    return gridCellMap.values.map(
 | 
					    return gridCellMap.values.map(
 | 
				
			||||||
      (gridCell) {
 | 
					      (gridCell) {
 | 
				
			||||||
        Widget? expander;
 | 
					        Widget? expander;
 | 
				
			||||||
@ -181,6 +181,7 @@ class _RowCells extends StatelessWidget {
 | 
				
			|||||||
        return CellContainer(
 | 
					        return CellContainer(
 | 
				
			||||||
          width: gridCell.field.width.toDouble(),
 | 
					          width: gridCell.field.width.toDouble(),
 | 
				
			||||||
          child: buildGridCellWidget(gridCell, cellCache),
 | 
					          child: buildGridCellWidget(gridCell, cellCache),
 | 
				
			||||||
 | 
					          rowStateNotifier: Provider.of<RegionStateNotifier>(context, listen: false),
 | 
				
			||||||
          expander: expander,
 | 
					          expander: expander,
 | 
				
			||||||
        );
 | 
					        );
 | 
				
			||||||
      },
 | 
					      },
 | 
				
			||||||
@ -188,7 +189,7 @@ class _RowCells extends StatelessWidget {
 | 
				
			|||||||
  }
 | 
					  }
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
class _RegionStateNotifier extends ChangeNotifier {
 | 
					class RegionStateNotifier extends ChangeNotifier {
 | 
				
			||||||
  bool _onEnter = false;
 | 
					  bool _onEnter = false;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  set onEnter(bool value) {
 | 
					  set onEnter(bool value) {
 | 
				
			||||||
@ -226,11 +227,11 @@ class _RowEnterRegion extends StatefulWidget {
 | 
				
			|||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
class _RowEnterRegionState extends State<_RowEnterRegion> {
 | 
					class _RowEnterRegionState extends State<_RowEnterRegion> {
 | 
				
			||||||
  late _RegionStateNotifier _rowStateNotifier;
 | 
					  late RegionStateNotifier _rowStateNotifier;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  @override
 | 
					  @override
 | 
				
			||||||
  void initState() {
 | 
					  void initState() {
 | 
				
			||||||
    _rowStateNotifier = _RegionStateNotifier();
 | 
					    _rowStateNotifier = RegionStateNotifier();
 | 
				
			||||||
    super.initState();
 | 
					    super.initState();
 | 
				
			||||||
  }
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
				
			|||||||
		Loading…
	
	
			
			x
			
			
		
	
		Reference in New Issue
	
	Block a user