257 lines
7.2 KiB
Dart
Raw Normal View History

2022-08-12 20:10:56 +08:00
import 'package:app_flowy/plugins/board/application/card/card_bloc.dart';
import 'package:app_flowy/plugins/board/application/card/card_data_controller.dart';
2022-08-16 17:36:39 +08:00
import 'package:app_flowy/plugins/grid/presentation/widgets/row/row_action_sheet.dart';
2022-09-19 18:12:41 +08:00
import 'package:appflowy_popover/appflowy_popover.dart';
2022-08-13 11:51:26 +08:00
import 'package:flowy_infra/image.dart';
import 'package:flowy_infra/theme.dart';
import 'package:flowy_infra_ui/flowy_infra_ui.dart';
import 'package:flutter/foundation.dart';
2022-08-10 17:59:28 +08:00
import 'package:flutter/material.dart';
2022-08-12 20:10:56 +08:00
import 'package:flutter_bloc/flutter_bloc.dart';
2022-08-30 20:54:11 +08:00
import 'board_cell.dart';
2022-08-12 20:10:56 +08:00
import 'card_cell_builder.dart';
import 'container/accessory.dart';
import 'container/card_container.dart';
2022-08-10 17:59:28 +08:00
2022-08-12 20:10:56 +08:00
class BoardCard extends StatefulWidget {
final String gridId;
2022-08-25 16:17:19 +08:00
final String groupId;
final String fieldId;
final bool isEditing;
2022-08-12 20:10:56 +08:00
final CardDataController dataController;
final BoardCellBuilder cellBuilder;
2022-08-16 18:02:39 +08:00
final void Function(BuildContext) openCard;
2022-10-06 22:26:18 +08:00
final VoidCallback onStartEditing;
final VoidCallback onEndEditing;
2022-08-12 20:10:56 +08:00
const BoardCard({
required this.gridId,
2022-08-25 16:17:19 +08:00
required this.groupId,
required this.fieldId,
required this.isEditing,
2022-08-12 20:10:56 +08:00
required this.dataController,
required this.cellBuilder,
2022-08-16 18:02:39 +08:00
required this.openCard,
2022-10-06 22:26:18 +08:00
required this.onStartEditing,
required this.onEndEditing,
2022-08-12 20:10:56 +08:00
Key? key,
}) : super(key: key);
@override
State<BoardCard> createState() => _BoardCardState();
}
class _BoardCardState extends State<BoardCard> {
late BoardCardBloc _cardBloc;
2022-08-30 20:54:11 +08:00
late EditableRowNotifier rowNotifier;
late PopoverController popoverController;
AccessoryType? accessoryType;
2022-08-12 20:10:56 +08:00
@override
void initState() {
rowNotifier = EditableRowNotifier(isEditing: widget.isEditing);
2022-08-12 20:10:56 +08:00
_cardBloc = BoardCardBloc(
gridId: widget.gridId,
groupFieldId: widget.fieldId,
2022-08-12 20:10:56 +08:00
dataController: widget.dataController,
isEditing: widget.isEditing,
2022-08-24 21:06:10 +08:00
)..add(const BoardCardEvent.initial());
rowNotifier.isEditing.addListener(() {
if (!mounted) return;
_cardBloc.add(BoardCardEvent.setIsEditing(rowNotifier.isEditing.value));
2022-10-06 22:26:18 +08:00
if (rowNotifier.isEditing.value) {
widget.onStartEditing();
} else {
widget.onEndEditing();
}
});
popoverController = PopoverController();
2022-08-12 20:10:56 +08:00
super.initState();
}
2022-08-10 17:59:28 +08:00
@override
Widget build(BuildContext context) {
2022-08-12 20:10:56 +08:00
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);
},
2022-08-12 20:10:56 +08:00
builder: (context, state) {
return AppFlowyPopover(
controller: popoverController,
2022-09-22 21:02:10 +08:00
triggerActions: PopoverTriggerFlags.none,
constraints: BoxConstraints.loose(const Size(140, 200)),
2022-09-23 17:50:32 +08:00
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,
),
),
2022-08-12 20:10:56 +08:00
);
},
),
);
}
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(
2022-09-23 17:27:30 +08:00
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,
) {
2022-08-29 15:42:08 +08:00
final List<Widget> children = [];
// Remove all the cell listeners.
rowNotifier.unbind();
2022-08-29 15:42:08 +08:00
cells.asMap().forEach(
(int index, BoardCellEquatable cell) {
final isEditing = index == 0 ? rowNotifier.isEditing.value : false;
final cellNotifier = EditableCellNotifier(isEditing: isEditing);
2022-09-06 21:10:41 +08:00
if (index == 0) {
// Only use the first cell to receive user's input when click the edit
// button
rowNotifier.bindCell(cell.identifier, cellNotifier);
2022-09-06 21:10:41 +08:00
}
final child = Padding(
key: cell.identifier.key(),
padding: const EdgeInsets.only(left: 4, right: 4),
child: cellBuilder.buildCell(
groupId,
cell.identifier,
cellNotifier,
),
);
2022-08-31 20:57:34 +08:00
children.add(child);
2022-08-12 20:10:56 +08:00
},
2022-08-29 15:42:08 +08:00
);
return children;
2022-08-10 17:59:28 +08:00
}
}
2022-08-13 11:51:26 +08:00
class _CardMoreOption extends StatelessWidget with CardAccessory {
_CardMoreOption({Key? key}) : super(key: key);
2022-08-13 11:51:26 +08:00
@override
Widget build(BuildContext context) {
2022-08-30 20:54:11 +08:00
return Padding(
padding: const EdgeInsets.all(3.0),
child:
svgWidget('grid/details', color: context.read<AppTheme>().iconColor),
);
2022-08-13 11:51:26 +08:00
}
@override
AccessoryType get type => AccessoryType.more;
2022-08-13 11:51:26 +08:00
}
2022-08-30 20:54:11 +08:00
class _CardEditOption extends StatelessWidget with CardAccessory {
final EditableRowNotifier rowNotifier;
2022-08-30 20:54:11 +08:00
const _CardEditOption({
required this.rowNotifier,
2022-08-30 20:54:11 +08:00
Key? key,
}) : super(key: key);
@override
Widget build(BuildContext context) {
return Padding(
padding: const EdgeInsets.all(3.0),
child: svgWidget(
'editor/edit',
color: context.read<AppTheme>().iconColor,
),
);
}
@override
void onTap(BuildContext context) => rowNotifier.becomeFirstResponder();
@override
AccessoryType get type => AccessoryType.edit;
2022-08-30 20:54:11 +08:00
}