| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257 | import 'package:app_flowy/plugins/board/application/card/card_bloc.dart';import 'package:app_flowy/plugins/board/application/card/card_data_controller.dart';import 'package:app_flowy/plugins/grid/presentation/widgets/row/row_action_sheet.dart';import 'package:appflowy_popover/appflowy_popover.dart';import 'package:flowy_infra/image.dart';import 'package:flowy_infra_ui/flowy_infra_ui.dart';import 'package:flutter/foundation.dart';import 'package:flutter/material.dart';import 'package:flutter_bloc/flutter_bloc.dart';import 'board_cell.dart';import 'card_cell_builder.dart';import 'container/accessory.dart';import 'container/card_container.dart';class BoardCard extends StatefulWidget {  final String gridId;  final String groupId;  final String fieldId;  final bool isEditing;  final CardDataController dataController;  final BoardCellBuilder cellBuilder;  final void Function(BuildContext) openCard;  final VoidCallback onStartEditing;  final VoidCallback onEndEditing;  const BoardCard({    required this.gridId,    required this.groupId,    required this.fieldId,    required this.isEditing,    required this.dataController,    required this.cellBuilder,    required this.openCard,    required this.onStartEditing,    required this.onEndEditing,    Key? key,  }) : super(key: key);  @override  State<BoardCard> createState() => _BoardCardState();}class _BoardCardState extends State<BoardCard> {  late BoardCardBloc _cardBloc;  late EditableRowNotifier rowNotifier;  late PopoverController popoverController;  AccessoryType? accessoryType;  @override  void initState() {    rowNotifier = EditableRowNotifier(isEditing: widget.isEditing);    _cardBloc = BoardCardBloc(      gridId: widget.gridId,      groupFieldId: widget.fieldId,      dataController: widget.dataController,      isEditing: widget.isEditing,    )..add(const BoardCardEvent.initial());    rowNotifier.isEditing.addListener(() {      if (!mounted) return;      _cardBloc.add(BoardCardEvent.setIsEditing(rowNotifier.isEditing.value));      if (rowNotifier.isEditing.value) {        widget.onStartEditing();      } else {        widget.onEndEditing();      }    });    popoverController = PopoverController();    super.initState();  }  @override  Widget build(BuildContext context) {    return BlocProvider.value(      value: _cardBloc,      child: BlocBuilder<BoardCardBloc, BoardCardState>(        buildWhen: (previous, current) {          // Rebuild when:          // 1.If the length of the cells is not the same          // 2.isEditing changed          if (previous.cells.length != current.cells.length ||              previous.isEditing != current.isEditing) {            return true;          }          // 3.Compare the content of the cells. The cells consists of          // list of [BoardCellEquatable] that extends the [Equatable].          return !listEquals(previous.cells, current.cells);        },        builder: (context, state) {          return AppFlowyPopover(            controller: popoverController,            triggerActions: PopoverTriggerFlags.none,            constraints: BoxConstraints.loose(const Size(140, 200)),            margin: const EdgeInsets.all(6),            direction: PopoverDirection.rightWithCenterAligned,            popupBuilder: (popoverContext) => _handlePopoverBuilder(              context,              popoverContext,            ),            child: BoardCardContainer(              buildAccessoryWhen: () => state.isEditing == false,              accessoryBuilder: (context) {                return [                  _CardEditOption(rowNotifier: rowNotifier),                  _CardMoreOption(),                ];              },              openAccessory: _handleOpenAccessory,              openCard: (context) => widget.openCard(context),              child: _CellColumn(                groupId: widget.groupId,                rowNotifier: rowNotifier,                cellBuilder: widget.cellBuilder,                cells: state.cells,              ),            ),          );        },      ),    );  }  void _handleOpenAccessory(AccessoryType newAccessoryType) {    accessoryType = newAccessoryType;    switch (newAccessoryType) {      case AccessoryType.edit:        break;      case AccessoryType.more:        popoverController.show();        break;    }  }  Widget _handlePopoverBuilder(    BuildContext context,    BuildContext popoverContext,  ) {    switch (accessoryType!) {      case AccessoryType.edit:        throw UnimplementedError();      case AccessoryType.more:        return GridRowActionSheet(          rowData: context.read<BoardCardBloc>().rowInfo(),        );    }  }  @override  Future<void> dispose() async {    rowNotifier.dispose();    _cardBloc.close();    super.dispose();  }}class _CellColumn extends StatelessWidget {  final String groupId;  final BoardCellBuilder cellBuilder;  final EditableRowNotifier rowNotifier;  final List<BoardCellEquatable> cells;  const _CellColumn({    required this.groupId,    required this.rowNotifier,    required this.cellBuilder,    required this.cells,    Key? key,  }) : super(key: key);  @override  Widget build(BuildContext context) {    return Column(      mainAxisSize: MainAxisSize.min,      children: _makeCells(context, cells),    );  }  List<Widget> _makeCells(    BuildContext context,    List<BoardCellEquatable> cells,  ) {    final List<Widget> children = [];    // Remove all the cell listeners.    rowNotifier.unbind();    cells.asMap().forEach(      (int index, BoardCellEquatable cell) {        final isEditing = index == 0 ? rowNotifier.isEditing.value : false;        final cellNotifier = EditableCellNotifier(isEditing: isEditing);        if (index == 0) {          // Only use the first cell to receive user's input when click the edit          // button          rowNotifier.bindCell(cell.identifier, cellNotifier);        }        final child = Padding(          key: cell.identifier.key(),          padding: const EdgeInsets.only(left: 4, right: 4),          child: cellBuilder.buildCell(            groupId,            cell.identifier,            cellNotifier,          ),        );        children.add(child);      },    );    return children;  }}class _CardMoreOption extends StatelessWidget with CardAccessory {  _CardMoreOption({Key? key}) : super(key: key);  @override  Widget build(BuildContext context) {    return Padding(      padding: const EdgeInsets.all(3.0),      child: svgWidget(        'grid/details',        color: Theme.of(context).colorScheme.onSurface,      ),    );  }  @override  AccessoryType get type => AccessoryType.more;}class _CardEditOption extends StatelessWidget with CardAccessory {  final EditableRowNotifier rowNotifier;  const _CardEditOption({    required this.rowNotifier,    Key? key,  }) : super(key: key);  @override  Widget build(BuildContext context) {    return Padding(      padding: const EdgeInsets.all(3.0),      child: svgWidget(        'editor/edit',        color: Theme.of(context).colorScheme.onSurface,      ),    );  }  @override  void onTap(BuildContext context) => rowNotifier.becomeFirstResponder();  @override  AccessoryType get type => AccessoryType.edit;}
 |