| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346 | import 'package:app_flowy/plugins/grid/application/field/field_cache.dart';import 'package:app_flowy/plugins/grid/application/row/row_data_controller.dart';import 'package:app_flowy/startup/startup.dart';import 'package:app_flowy/plugins/grid/application/grid_bloc.dart';import 'package:flowy_infra/theme.dart';import 'package:flowy_infra_ui/style_widget/scrolling/styled_list.dart';import 'package:flowy_infra_ui/style_widget/scrolling/styled_scroll_bar.dart';import 'package:flowy_infra_ui/style_widget/scrolling/styled_scrollview.dart';import 'package:flowy_infra_ui/style_widget/text.dart';import 'package:flowy_infra_ui/widget/error_page.dart';import 'package:flowy_sdk/protobuf/flowy-folder/view.pb.dart';import 'package:flutter_bloc/flutter_bloc.dart';import 'package:flutter/material.dart';import 'package:linked_scroll_controller/linked_scroll_controller.dart';import '../application/row/row_cache.dart';import 'controller/grid_scroll.dart';import 'layout/layout.dart';import 'layout/sizes.dart';import 'widgets/cell/cell_builder.dart';import 'widgets/row/grid_row.dart';import 'widgets/footer/grid_footer.dart';import 'widgets/header/grid_header.dart';import 'widgets/row/row_detail.dart';import 'widgets/shortcuts.dart';import 'widgets/toolbar/grid_toolbar.dart';class GridPage extends StatefulWidget {  final ViewPB view;  GridPage({Key? key, required this.view}) : super(key: ValueKey(view.id));  @override  State<GridPage> createState() => _GridPageState();}class _GridPageState extends State<GridPage> {  @override  Widget build(BuildContext context) {    return MultiBlocProvider(      providers: [        BlocProvider<GridBloc>(          create: (context) => getIt<GridBloc>(param1: widget.view)            ..add(const GridEvent.initial()),        ),      ],      child: BlocBuilder<GridBloc, GridState>(        builder: (context, state) {          return state.loadingState.map(            loading: (_) =>                const Center(child: CircularProgressIndicator.adaptive()),            finish: (result) => result.successOrFail.fold(              (_) => const GridShortcuts(child: FlowyGrid()),              (err) => FlowyErrorPage(err.toString()),            ),          );        },      ),    );  }  @override  void dispose() {    super.dispose();  }  @override  void deactivate() {    super.deactivate();  }  @override  void didUpdateWidget(covariant GridPage oldWidget) {    super.didUpdateWidget(oldWidget);  }}class FlowyGrid extends StatefulWidget {  const FlowyGrid({Key? key}) : super(key: key);  @override  State<FlowyGrid> createState() => _FlowyGridState();}class _FlowyGridState extends State<FlowyGrid> {  final _scrollController = GridScrollController(      scrollGroupController: LinkedScrollControllerGroup());  late ScrollController headerScrollController;  @override  void initState() {    headerScrollController = _scrollController.linkHorizontalController();    super.initState();  }  @override  void dispose() {    _scrollController.dispose();    super.dispose();  }  @override  Widget build(BuildContext context) {    return BlocBuilder<GridBloc, GridState>(      buildWhen: (previous, current) => previous.fields != current.fields,      builder: (context, state) {        final contentWidth = GridLayout.headerWidth(state.fields.value);        final child = _wrapScrollView(          contentWidth,          [            const _GridRows(),            const _GridFooter(),          ],        );        return Column(          crossAxisAlignment: CrossAxisAlignment.start,          children: [            const _GridToolbarAdaptor(),            _gridHeader(context, state.gridId),            Flexible(child: child),          ],        );      },    );  }  Widget _wrapScrollView(    double contentWidth,    List<Widget> slivers,  ) {    final verticalScrollView = ScrollConfiguration(      behavior: const ScrollBehavior().copyWith(scrollbars: false),      child: CustomScrollView(        physics: StyledScrollPhysics(),        controller: _scrollController.verticalController,        slivers: slivers,      ),    );    final sizedVerticalScrollView = SizedBox(      width: contentWidth,      child: verticalScrollView,    );    final horizontalScrollView = StyledSingleChildScrollView(      controller: _scrollController.horizontalController,      axis: Axis.horizontal,      child: sizedVerticalScrollView,    );    return ScrollbarListStack(      axis: Axis.vertical,      controller: _scrollController.verticalController,      barSize: GridSize.scrollBarSize,      child: horizontalScrollView,    );  }  Widget _gridHeader(BuildContext context, String gridId) {    final fieldCache = context.read<GridBloc>().dataController.fieldCache;    return GridHeaderSliverAdaptor(      gridId: gridId,      fieldCache: fieldCache,      anchorScrollController: headerScrollController,    );  }}class _GridToolbarAdaptor extends StatelessWidget {  const _GridToolbarAdaptor({Key? key}) : super(key: key);  @override  Widget build(BuildContext context) {    return BlocSelector<GridBloc, GridState, GridToolbarContext>(      selector: (state) {        final fieldCache = context.read<GridBloc>().dataController.fieldCache;        return GridToolbarContext(          gridId: state.gridId,          fieldCache: fieldCache,        );      },      builder: (context, toolbarContext) {        return GridToolbar(toolbarContext: toolbarContext);      },    );  }}class _GridRows extends StatefulWidget {  const _GridRows({Key? key}) : super(key: key);  @override  State<_GridRows> createState() => _GridRowsState();}class _GridRowsState extends State<_GridRows> {  final _key = GlobalKey<SliverAnimatedListState>();  @override  Widget build(BuildContext context) {    return BlocConsumer<GridBloc, GridState>(      listenWhen: (previous, current) => previous.reason != current.reason,      listener: (context, state) {        state.reason.mapOrNull(          insert: (value) {            for (final item in value.items) {              _key.currentState?.insertItem(item.index);            }          },          delete: (value) {            for (final item in value.items) {              _key.currentState?.removeItem(                item.index,                (context, animation) =>                    _renderRow(context, item.row, animation),              );            }          },        );      },      buildWhen: (previous, current) => false,      builder: (context, state) {        return SliverAnimatedList(          key: _key,          initialItemCount: context.read<GridBloc>().state.rowInfos.length,          itemBuilder:              (BuildContext context, int index, Animation<double> animation) {            final RowInfo rowInfo =                context.read<GridBloc>().state.rowInfos[index];            return _renderRow(context, rowInfo, animation);          },        );      },    );  }  Widget _renderRow(    BuildContext context,    RowInfo rowInfo,    Animation<double> animation,  ) {    final rowCache = context.read<GridBloc>().getRowCache(          rowInfo.rowPB.blockId,          rowInfo.rowPB.id,        );    /// Return placeholder widget if the rowCache is null.    if (rowCache == null) return const SizedBox();    final fieldCache = context.read<GridBloc>().dataController.fieldCache;    final dataController = GridRowDataController(      rowInfo: rowInfo,      fieldCache: fieldCache,      rowCache: rowCache,    );    return SizeTransition(      sizeFactor: animation,      child: GridRowWidget(        rowInfo: rowInfo,        dataController: dataController,        cellBuilder: GridCellBuilder(delegate: dataController),        openDetailPage: (context, cellBuilder) {          _openRowDetailPage(            context,            rowInfo,            fieldCache,            rowCache,            cellBuilder,          );        },        key: ValueKey(rowInfo.rowPB.id),      ),    );  }  void _openRowDetailPage(    BuildContext context,    RowInfo rowInfo,    GridFieldCache fieldCache,    GridRowCache rowCache,    GridCellBuilder cellBuilder,  ) {    final dataController = GridRowDataController(      rowInfo: rowInfo,      fieldCache: fieldCache,      rowCache: rowCache,    );    RowDetailPage(      cellBuilder: cellBuilder,      dataController: dataController,    ).show(context);  }}class _GridFooter extends StatelessWidget {  const _GridFooter({Key? key}) : super(key: key);  @override  Widget build(BuildContext context) {    final rowCount = context.watch<GridBloc>().state.rowInfos.length;    final theme = context.watch<AppTheme>();    return SliverPadding(      padding: const EdgeInsets.only(bottom: 200),      sliver: SliverToBoxAdapter(        child: SizedBox(          height: GridSize.footerHeight,          child: Padding(            padding: GridSize.headerContentInsets,            child: Row(              children: [                SizedBox(width: GridSize.leadingHeaderPadding),                Column(                  crossAxisAlignment: CrossAxisAlignment.start,                  children: [                    const SizedBox(width: 120, child: GridAddRowButton()),                    const SizedBox(height: 30),                    _rowCountTextWidget(theme: theme, count: rowCount)                  ],                ),              ],            ),          ),        ),      ),    );  }  Widget _rowCountTextWidget({required AppTheme theme, required int count}) {    return Row(      mainAxisAlignment: MainAxisAlignment.start,      children: [        FlowyText.regular(          'Count : ',          fontSize: 13,          color: theme.shader3,        ),        FlowyText.regular(          count.toString(),          fontSize: 13,        ),      ],    );  }}
 |