From 6dbb70899129fc7578bf9996d917a1d8c890cf8e Mon Sep 17 00:00:00 2001 From: appflowy Date: Fri, 15 Apr 2022 15:41:55 +0800 Subject: [PATCH] chore: repalce sliver header with single scroll view --- .../application/grid/grid_header_bloc.dart | 8 ++- .../grid/src/controller/grid_scroll.dart | 27 +++++---- .../plugins/grid/src/grid_page.dart | 43 +++++++------ .../grid/src/widgets/header/grid_header.dart | 60 +++++++++++-------- frontend/app_flowy/pubspec.lock | 7 +++ frontend/app_flowy/pubspec.yaml | 1 + 6 files changed, 93 insertions(+), 53 deletions(-) diff --git a/frontend/app_flowy/lib/workspace/application/grid/grid_header_bloc.dart b/frontend/app_flowy/lib/workspace/application/grid/grid_header_bloc.dart index 13f65ff4d6..8ea390d7ab 100644 --- a/frontend/app_flowy/lib/workspace/application/grid/grid_header_bloc.dart +++ b/frontend/app_flowy/lib/workspace/application/grid/grid_header_bloc.dart @@ -1,3 +1,4 @@ +import 'package:app_flowy/workspace/application/grid/field/field_service.dart'; import 'package:flowy_sdk/protobuf/flowy-grid-data-model/grid.pb.dart'; import 'package:flutter_bloc/flutter_bloc.dart'; import 'package:freezed_annotation/freezed_annotation.dart'; @@ -7,13 +8,14 @@ import 'grid_service.dart'; part 'grid_header_bloc.freezed.dart'; class GridHeaderBloc extends Bloc { - // final FieldService _fieldService; + final FieldService _fieldService; final GridFieldCache fieldCache; GridHeaderBloc({ required String gridId, required this.fieldCache, - }) : super(GridHeaderState.initial(fieldCache.clonedFields)) { + }) : _fieldService = FieldService(gridId: gridId), + super(GridHeaderState.initial(fieldCache.clonedFields)) { on( (event, emit) async { await event.map( @@ -23,6 +25,7 @@ class GridHeaderBloc extends Bloc { didReceiveFieldUpdate: (_DidReceiveFieldUpdate value) { emit(state.copyWith(fields: value.fields)); }, + moveField: (_MoveField value) {}, ); }, ); @@ -46,6 +49,7 @@ class GridHeaderBloc extends Bloc { class GridHeaderEvent with _$GridHeaderEvent { const factory GridHeaderEvent.initial() = _InitialHeader; const factory GridHeaderEvent.didReceiveFieldUpdate(List fields) = _DidReceiveFieldUpdate; + const factory GridHeaderEvent.moveField(int fromIndex, int toIndex) = _MoveField; } @freezed diff --git a/frontend/app_flowy/lib/workspace/presentation/plugins/grid/src/controller/grid_scroll.dart b/frontend/app_flowy/lib/workspace/presentation/plugins/grid/src/controller/grid_scroll.dart index 213b16cfae..dddd93d175 100755 --- a/frontend/app_flowy/lib/workspace/presentation/plugins/grid/src/controller/grid_scroll.dart +++ b/frontend/app_flowy/lib/workspace/presentation/plugins/grid/src/controller/grid_scroll.dart @@ -1,21 +1,28 @@ import 'package:flutter/material.dart'; +import 'package:linked_scroll_controller/linked_scroll_controller.dart'; class GridScrollController { - final ScrollController _verticalController = ScrollController(); - final ScrollController _horizontalController = ScrollController(); + final LinkedScrollControllerGroup _scrollGroupContorller; + final ScrollController verticalController; + final ScrollController horizontalController; - ScrollController get verticalController => _verticalController; - ScrollController get horizontalController => _horizontalController; + final List _linkHorizontalControllers = []; - GridScrollController(); + GridScrollController({required LinkedScrollControllerGroup scrollGroupContorller}) + : _scrollGroupContorller = scrollGroupContorller, + verticalController = ScrollController(), + horizontalController = scrollGroupContorller.addAndGet(); - // final SelectionChangeCallback? onSelectionChanged; - - // final ShouldApplySelection? shouldApplySelection; - - // final ScrollCallback? onScroll; + ScrollController linkHorizontalController() { + final controller = _scrollGroupContorller.addAndGet(); + _linkHorizontalControllers.add(controller); + return controller; + } void dispose() { + for (final controller in _linkHorizontalControllers) { + controller.dispose(); + } verticalController.dispose(); horizontalController.dispose(); } diff --git a/frontend/app_flowy/lib/workspace/presentation/plugins/grid/src/grid_page.dart b/frontend/app_flowy/lib/workspace/presentation/plugins/grid/src/grid_page.dart index 790dc0f83c..340f7c7026 100755 --- a/frontend/app_flowy/lib/workspace/presentation/plugins/grid/src/grid_page.dart +++ b/frontend/app_flowy/lib/workspace/presentation/plugins/grid/src/grid_page.dart @@ -9,6 +9,7 @@ import 'package:flowy_sdk/protobuf/flowy-folder-data-model/view.pb.dart'; import 'package:flowy_sdk/protobuf/flowy-grid-data-model/grid.pb.dart' show Field; import 'package:flutter_bloc/flutter_bloc.dart'; import 'package:flutter/material.dart'; +import 'package:linked_scroll_controller/linked_scroll_controller.dart'; import 'controller/grid_scroll.dart'; import 'layout/layout.dart'; import 'layout/sizes.dart'; @@ -73,17 +74,23 @@ class FlowyGrid extends StatefulWidget { } class _FlowyGridState extends State { - final _scrollController = GridScrollController(); + final _scrollController = GridScrollController(scrollGroupContorller: LinkedScrollControllerGroup()); + + @override + void dispose() { + _scrollController.dispose(); + super.dispose(); + } @override Widget build(BuildContext context) { return BlocBuilder( buildWhen: (previous, current) => previous.fields.length != current.fields.length, builder: (context, state) { + final contentWidth = GridLayout.headerWidth(state.fields); final child = _wrapScrollView( - state.fields, + contentWidth, [ - _GridHeader(gridId: state.gridId, fields: state.fields), const _GridRows(), const _GridFooter(), ], @@ -91,6 +98,7 @@ class _FlowyGridState extends State { return Column(children: [ const _GridToolbarAdaptor(), + _gridHeader(context, state.gridId, contentWidth), Flexible(child: child), ]); }, @@ -98,7 +106,7 @@ class _FlowyGridState extends State { } Widget _wrapScrollView( - List fields, + double contentWidth, List slivers, ) { final verticalScrollView = ScrollConfiguration( @@ -111,7 +119,7 @@ class _FlowyGridState extends State { ); final sizedVerticalScrollView = SizedBox( - width: GridLayout.headerWidth(fields), + width: contentWidth, child: verticalScrollView, ); @@ -128,6 +136,19 @@ class _FlowyGridState extends State { child: horizontalScrollView, ); } + + Widget _gridHeader(BuildContext context, String gridId, double contentWidth) { + final fieldCache = context.read().fieldCache; + + return SizedBox( + width: contentWidth, + child: GridHeaderSliverAdaptor( + gridId: gridId, + fieldCache: fieldCache, + anchorScrollController: _scrollController.linkHorizontalController(), + ), + ); + } } class _GridToolbarAdaptor extends StatelessWidget { @@ -149,18 +170,6 @@ class _GridToolbarAdaptor extends StatelessWidget { } } -class _GridHeader extends StatelessWidget { - final String gridId; - final List fields; - const _GridHeader({Key? key, required this.gridId, required this.fields}) : super(key: key); - - @override - Widget build(BuildContext context) { - final fieldCache = context.read().fieldCache; - return GridHeaderSliverAdaptor(gridId: gridId, fieldCache: fieldCache); - } -} - class _GridRows extends StatefulWidget { const _GridRows({Key? key}) : super(key: key); diff --git a/frontend/app_flowy/lib/workspace/presentation/plugins/grid/src/widgets/header/grid_header.dart b/frontend/app_flowy/lib/workspace/presentation/plugins/grid/src/widgets/header/grid_header.dart index a00b9e6295..2ac5e5f673 100644 --- a/frontend/app_flowy/lib/workspace/presentation/plugins/grid/src/widgets/header/grid_header.dart +++ b/frontend/app_flowy/lib/workspace/presentation/plugins/grid/src/widgets/header/grid_header.dart @@ -13,25 +13,41 @@ import 'package:reorderables/reorderables.dart'; import 'field_editor.dart'; import 'field_cell.dart'; -class GridHeaderSliverAdaptor extends StatelessWidget { +class GridHeaderSliverAdaptor extends StatefulWidget { final String gridId; final GridFieldCache fieldCache; + final ScrollController anchorScrollController; + const GridHeaderSliverAdaptor({ + required this.gridId, + required this.fieldCache, + required this.anchorScrollController, + Key? key, + }) : super(key: key); - const GridHeaderSliverAdaptor({required this.gridId, required this.fieldCache, Key? key}) : super(key: key); + @override + State createState() => _GridHeaderSliverAdaptorState(); +} +class _GridHeaderSliverAdaptorState extends State { @override Widget build(BuildContext context) { return BlocProvider( create: (context) => - getIt(param1: gridId, param2: fieldCache)..add(const GridHeaderEvent.initial()), + getIt(param1: widget.gridId, param2: widget.fieldCache)..add(const GridHeaderEvent.initial()), child: BlocBuilder( buildWhen: (previous, current) => previous.fields.length != current.fields.length, builder: (context, state) { - return SliverPersistentHeader( - delegate: SliverHeaderDelegateImplementation(gridId: gridId, fields: state.fields), - floating: true, - pinned: true, + return SingleChildScrollView( + scrollDirection: Axis.horizontal, + controller: widget.anchorScrollController, + child: SizedBox(height: GridSize.headerHeight, child: _GridHeader(gridId: widget.gridId)), ); + + // return SliverPersistentHeader( + // delegate: SliverHeaderDelegateImplementation(gridId: gridId, fields: state.fields), + // floating: true, + // pinned: true, + // ); }, ), ); @@ -46,7 +62,7 @@ class SliverHeaderDelegateImplementation extends SliverPersistentHeaderDelegate @override Widget build(BuildContext context, double shrinkOffset, bool overlapsContent) { - return _GridHeader(gridId: gridId, fields: fields); + return _GridHeader(gridId: gridId); } @override @@ -66,13 +82,7 @@ class SliverHeaderDelegateImplementation extends SliverPersistentHeaderDelegate class _GridHeader extends StatefulWidget { final String gridId; - final List fields; - - const _GridHeader({ - Key? key, - required this.gridId, - required this.fields, - }) : super(key: key); + const _GridHeader({Key? key, required this.gridId}) : super(key: key); @override State<_GridHeader> createState() => _GridHeaderState(); @@ -93,15 +103,17 @@ class _GridHeaderState extends State<_GridHeader> { return Container( color: theme.surface, - child: ReorderableRow( - crossAxisAlignment: CrossAxisAlignment.stretch, - scrollController: ScrollController(), - header: const _CellLeading(), - footer: _CellTrailing(gridId: widget.gridId), - onReorder: (int oldIndex, int newIndex) { - Log.info("from $oldIndex to $newIndex"); - }, - children: cells, + child: RepaintBoundary( + child: ReorderableRow( + crossAxisAlignment: CrossAxisAlignment.stretch, + scrollController: ScrollController(), + header: const _CellLeading(), + footer: _CellTrailing(gridId: widget.gridId), + onReorder: (int oldIndex, int newIndex) { + Log.info("from $oldIndex to $newIndex"); + }, + children: cells, + ), ), ); }, diff --git a/frontend/app_flowy/pubspec.lock b/frontend/app_flowy/pubspec.lock index 1a3a3172f4..8f7e6d6db2 100644 --- a/frontend/app_flowy/pubspec.lock +++ b/frontend/app_flowy/pubspec.lock @@ -653,6 +653,13 @@ packages: url: "https://pub.dartlang.org" source: hosted version: "4.4.0" + linked_scroll_controller: + dependency: "direct main" + description: + name: linked_scroll_controller + url: "https://pub.dartlang.org" + source: hosted + version: "0.2.0" lint: dependency: transitive description: diff --git a/frontend/app_flowy/pubspec.yaml b/frontend/app_flowy/pubspec.yaml index bcc3303602..fd2b75b9e0 100644 --- a/frontend/app_flowy/pubspec.yaml +++ b/frontend/app_flowy/pubspec.yaml @@ -75,6 +75,7 @@ dependencies: fluttertoast: ^8.0.8 table_calendar: ^3.0.5 reorderables: + linked_scroll_controller: ^0.2.0 dev_dependencies: flutter_lints: ^1.0.0