chore: update documentation

This commit is contained in:
appflowy 2022-09-05 16:29:16 +08:00
parent a3d2cef40a
commit c7fdfb4090
16 changed files with 545 additions and 515 deletions

View File

@ -22,7 +22,7 @@ part 'board_bloc.freezed.dart';
class BoardBloc extends Bloc<BoardEvent, BoardState> { class BoardBloc extends Bloc<BoardEvent, BoardState> {
final BoardDataController _gridDataController; final BoardDataController _gridDataController;
late final AFBoardDataController boardController; late final AppFlowyBoardDataController boardController;
final MoveRowFFIService _rowService; final MoveRowFFIService _rowService;
LinkedHashMap<String, GroupController> groupControllers = LinkedHashMap(); LinkedHashMap<String, GroupController> groupControllers = LinkedHashMap();
@ -34,8 +34,8 @@ class BoardBloc extends Bloc<BoardEvent, BoardState> {
: _rowService = MoveRowFFIService(gridId: view.id), : _rowService = MoveRowFFIService(gridId: view.id),
_gridDataController = BoardDataController(view: view), _gridDataController = BoardDataController(view: view),
super(BoardState.initial(view.id)) { super(BoardState.initial(view.id)) {
boardController = AFBoardDataController( boardController = AppFlowyBoardDataController(
onMoveColumn: ( onMoveGroup: (
fromColumnId, fromColumnId,
fromIndex, fromIndex,
toColumnId, toColumnId,
@ -43,7 +43,7 @@ class BoardBloc extends Bloc<BoardEvent, BoardState> {
) { ) {
_moveGroup(fromColumnId, toColumnId); _moveGroup(fromColumnId, toColumnId);
}, },
onMoveColumnItem: ( onMoveGroupItem: (
columnId, columnId,
fromIndex, fromIndex,
toIndex, toIndex,
@ -52,15 +52,15 @@ class BoardBloc extends Bloc<BoardEvent, BoardState> {
final toRow = groupControllers[columnId]?.rowAtIndex(toIndex); final toRow = groupControllers[columnId]?.rowAtIndex(toIndex);
_moveRow(fromRow, columnId, toRow); _moveRow(fromRow, columnId, toRow);
}, },
onMoveColumnItemToColumn: ( onMoveGroupItemToGroup: (
fromColumnId, fromGroupId,
fromIndex, fromIndex,
toColumnId, toGroupId,
toIndex, toIndex,
) { ) {
final fromRow = groupControllers[fromColumnId]?.rowAtIndex(fromIndex); final fromRow = groupControllers[fromGroupId]?.rowAtIndex(fromIndex);
final toRow = groupControllers[toColumnId]?.rowAtIndex(toIndex); final toRow = groupControllers[toGroupId]?.rowAtIndex(toIndex);
_moveRow(fromRow, toColumnId, toRow); _moveRow(fromRow, toGroupId, toRow);
}, },
); );
@ -165,10 +165,10 @@ class BoardBloc extends Bloc<BoardEvent, BoardState> {
boardController.clear(); boardController.clear();
// //
List<AFBoardColumnData> columns = groups List<AppFlowyBoardGroupData> columns = groups
.where((group) => fieldController.getField(group.fieldId) != null) .where((group) => fieldController.getField(group.fieldId) != null)
.map((group) { .map((group) {
return AFBoardColumnData( return AppFlowyBoardGroupData(
id: group.groupId, id: group.groupId,
name: group.desc, name: group.desc,
items: _buildRows(group), items: _buildRows(group),
@ -178,7 +178,7 @@ class BoardBloc extends Bloc<BoardEvent, BoardState> {
), ),
); );
}).toList(); }).toList();
boardController.addColumns(columns); boardController.addGroups(columns);
for (final group in groups) { for (final group in groups) {
final delegate = GroupControllerDelegateImpl( final delegate = GroupControllerDelegateImpl(
@ -227,8 +227,8 @@ class BoardBloc extends Bloc<BoardEvent, BoardState> {
if (isClosed) return; if (isClosed) return;
for (final group in updatedGroups) { for (final group in updatedGroups) {
final columnController = final columnController =
boardController.getColumnController(group.groupId); boardController.getGroupController(group.groupId);
columnController?.updateColumnName(group.desc); columnController?.updateGroupName(group.desc);
} }
}, },
onError: (err) { onError: (err) {
@ -243,13 +243,13 @@ class BoardBloc extends Bloc<BoardEvent, BoardState> {
); );
} }
List<AFColumnItem> _buildRows(GroupPB group) { List<AppFlowyGroupItem> _buildRows(GroupPB group) {
final items = group.rows.map((row) { final items = group.rows.map((row) {
final fieldContext = fieldController.getField(group.fieldId); final fieldContext = fieldController.getField(group.fieldId);
return BoardColumnItem(row: row, fieldContext: fieldContext!); return BoardColumnItem(row: row, fieldContext: fieldContext!);
}).toList(); }).toList();
return <AFColumnItem>[...items]; return <AppFlowyGroupItem>[...items];
} }
Future<void> _loadGrid(Emitter<BoardState> emit) async { Future<void> _loadGrid(Emitter<BoardState> emit) async {
@ -335,7 +335,7 @@ class GridFieldEquatable extends Equatable {
UnmodifiableListView<FieldPB> get value => UnmodifiableListView(_fields); UnmodifiableListView<FieldPB> get value => UnmodifiableListView(_fields);
} }
class BoardColumnItem extends AFColumnItem { class BoardColumnItem extends AppFlowyGroupItem {
final RowPB row; final RowPB row;
final GridFieldContext fieldContext; final GridFieldContext fieldContext;
@ -350,7 +350,7 @@ class BoardColumnItem extends AFColumnItem {
class GroupControllerDelegateImpl extends GroupControllerDelegate { class GroupControllerDelegateImpl extends GroupControllerDelegate {
final GridFieldController fieldController; final GridFieldController fieldController;
final AFBoardDataController controller; final AppFlowyBoardDataController controller;
final void Function(String, RowPB, int?) onNewColumnItem; final void Function(String, RowPB, int?) onNewColumnItem;
GroupControllerDelegateImpl({ GroupControllerDelegateImpl({
@ -369,16 +369,16 @@ class GroupControllerDelegateImpl extends GroupControllerDelegate {
if (index != null) { if (index != null) {
final item = BoardColumnItem(row: row, fieldContext: fieldContext); final item = BoardColumnItem(row: row, fieldContext: fieldContext);
controller.insertColumnItem(group.groupId, index, item); controller.insertGroupItem(group.groupId, index, item);
} else { } else {
final item = BoardColumnItem(row: row, fieldContext: fieldContext); final item = BoardColumnItem(row: row, fieldContext: fieldContext);
controller.addColumnItem(group.groupId, item); controller.addGroupItem(group.groupId, item);
} }
} }
@override @override
void removeRow(GroupPB group, String rowId) { void removeRow(GroupPB group, String rowId) {
controller.removeColumnItem(group.groupId, rowId); controller.removeGroupItem(group.groupId, rowId);
} }
@override @override
@ -388,7 +388,7 @@ class GroupControllerDelegateImpl extends GroupControllerDelegate {
Log.warn("FieldContext should not be null"); Log.warn("FieldContext should not be null");
return; return;
} }
controller.updateColumnItem( controller.updateGroupItem(
group.groupId, group.groupId,
BoardColumnItem(row: row, fieldContext: fieldContext), BoardColumnItem(row: row, fieldContext: fieldContext),
); );
@ -404,9 +404,9 @@ class GroupControllerDelegateImpl extends GroupControllerDelegate {
final item = BoardColumnItem(row: row, fieldContext: fieldContext); final item = BoardColumnItem(row: row, fieldContext: fieldContext);
if (index != null) { if (index != null) {
controller.insertColumnItem(group.groupId, index, item); controller.insertGroupItem(group.groupId, index, item);
} else { } else {
controller.addColumnItem(group.groupId, item); controller.addGroupItem(group.groupId, item);
} }
onNewColumnItem(group.groupId, row, index); onNewColumnItem(group.groupId, row, index);
} }

View File

@ -65,8 +65,8 @@ class _BoardContentState extends State<BoardContent> {
late ScrollController scrollController; late ScrollController scrollController;
late AFBoardScrollManager scrollManager; late AFBoardScrollManager scrollManager;
final config = AFBoardConfig( final config = AppFlowyBoardConfig(
columnBackgroundColor: HexColor.fromHex('#F7F8FC'), groupBackgroundColor: HexColor.fromHex('#F7F8FC'),
); );
@override @override
@ -101,7 +101,7 @@ class _BoardContentState extends State<BoardContent> {
Expanded _buildBoard(BuildContext context) { Expanded _buildBoard(BuildContext context) {
return Expanded( return Expanded(
child: AFBoard( child: AppFlowyBoard(
scrollManager: scrollManager, scrollManager: scrollManager,
scrollController: scrollController, scrollController: scrollController,
dataController: context.read<BoardBloc>().boardController, dataController: context.read<BoardBloc>().boardController,
@ -112,9 +112,9 @@ class _BoardContentState extends State<BoardContent> {
column, column,
columnItem, columnItem,
), ),
columnConstraints: const BoxConstraints.tightFor(width: 300), groupConstraints: const BoxConstraints.tightFor(width: 300),
config: AFBoardConfig( config: AppFlowyBoardConfig(
columnBackgroundColor: HexColor.fromHex('#F7F8FC'), groupBackgroundColor: HexColor.fromHex('#F7F8FC'),
), ),
), ),
); );
@ -149,14 +149,14 @@ class _BoardContentState extends State<BoardContent> {
Widget _buildHeader( Widget _buildHeader(
BuildContext context, BuildContext context,
AFBoardColumnData columnData, AppFlowyBoardGroupData columnData,
) { ) {
final boardCustomData = columnData.customData as BoardCustomData; final boardCustomData = columnData.customData as BoardCustomData;
return AppFlowyColumnHeader( return AppFlowyGroupHeader(
title: Flexible( title: Flexible(
fit: FlexFit.tight, fit: FlexFit.tight,
child: FlowyText.medium( child: FlowyText.medium(
columnData.headerData.columnName, columnData.headerData.groupName,
fontSize: 14, fontSize: 14,
overflow: TextOverflow.clip, overflow: TextOverflow.clip,
color: context.read<AppTheme>().textColor, color: context.read<AppTheme>().textColor,
@ -181,14 +181,14 @@ class _BoardContentState extends State<BoardContent> {
); );
} }
Widget _buildFooter(BuildContext context, AFBoardColumnData columnData) { Widget _buildFooter(BuildContext context, AppFlowyBoardGroupData columnData) {
final boardCustomData = columnData.customData as BoardCustomData; final boardCustomData = columnData.customData as BoardCustomData;
final group = boardCustomData.group; final group = boardCustomData.group;
if (group.isDefault) { if (group.isDefault) {
return const SizedBox(); return const SizedBox();
} else { } else {
return AppFlowyColumnFooter( return AppFlowyGroupFooter(
icon: SizedBox( icon: SizedBox(
height: 20, height: 20,
width: 20, width: 20,
@ -215,8 +215,8 @@ class _BoardContentState extends State<BoardContent> {
Widget _buildCard( Widget _buildCard(
BuildContext context, BuildContext context,
AFBoardColumnData column, AppFlowyBoardGroupData column,
AFColumnItem columnItem, AppFlowyGroupItem columnItem,
) { ) {
final boardColumnItem = columnItem as BoardColumnItem; final boardColumnItem = columnItem as BoardColumnItem;
final rowPB = boardColumnItem.row; final rowPB = boardColumnItem.row;
@ -242,7 +242,7 @@ class _BoardContentState extends State<BoardContent> {
}, },
); );
return AppFlowyColumnItemCard( return AppFlowyGroupItemCard(
key: ValueKey(columnItem.id), key: ValueKey(columnItem.id),
margin: config.cardPadding, margin: config.cardPadding,
decoration: _makeBoxDecoration(context), decoration: _makeBoxDecoration(context),

View File

@ -9,6 +9,7 @@
<a href="https://twitter.com/appflowy"><b>Twitter</b></a> <a href="https://twitter.com/appflowy"><b>Twitter</b></a>
</p> </p>
<p align="center"> <p align="center">
<img src="https://github.com/AppFlowy-IO/AppFlowy/blob/main/frontend/app_flowy/packages/appflowy_board/example/gifs/appflowy_board_video_1.gif?raw=true" width="680" title="AppFlowyBoard"> <img src="https://github.com/AppFlowy-IO/AppFlowy/blob/main/frontend/app_flowy/packages/appflowy_board/example/gifs/appflowy_board_video_1.gif?raw=true" width="680" title="AppFlowyBoard">
</p> </p>
@ -30,6 +31,7 @@ Add the AppFlowy Board [Flutter package](https://docs.flutter.dev/development/pa
With Flutter: With Flutter:
```dart ```dart
flutter pub add appflowy_board flutter pub add appflowy_board
flutter pub get
``` ```
This will add a line like this to your package's pubspec.yaml (and run an implicit flutter pub get): This will add a line like this to your package's pubspec.yaml (and run an implicit flutter pub get):
@ -38,6 +40,8 @@ dependencies:
appflowy_board: ^0.0.6 appflowy_board: ^0.0.6
``` ```
## Create board
Import the package in your Dart file: Import the package in your Dart file:
```dart ```dart
import 'package:appflowy_board/appflowy_board.dart'; import 'package:appflowy_board/appflowy_board.dart';
@ -47,14 +51,18 @@ import 'package:appflowy_board/appflowy_board.dart';
To quickly grasp how it can be used, look at the /example/lib folder. To quickly grasp how it can be used, look at the /example/lib folder.
First, run main.dart to play with the demo. First, run main.dart to play with the demo.
Second, let's delve into multi_board_list_example.dart to understand a few key components: Second, let's delve into multi_board_list_example.dart to understand a few key components:
* A Board widget is created via instantiating an AFBoard() object. * A Board widget is created via instantiating an AFBoard() object.
* In the AFBoard() object, you can find: * In the AFBoard() object, you can find:
* AFBoardDataController, which is defined in board_data.dart, is feeded with prepopulated mock data. It also contains callback functions to materialize future user data. * AFBoardDataController, which is defined in board_data.dart, is feeded with prepopulated mock data. It also contains callback functions to materialize future user data.
* Three builders: AppFlowyColumnHeader, AppFlowyColumnFooter, AppFlowyColumnItemCard. See below image for what they are used for. * Three builders: AppFlowyColumnHeader, AppFlowyColumnFooter, AppFlowyColumnItemCard. See below image for what they are used for.
<p>
<!-- <p>
<img src="https://github.com/AppFlowy-IO/AppFlowy/blob/main/frontend/app_flowy/packages/appflowy_board/example/gifs/appflowy_board_builders.jpg?raw=true" width="100%" title="AppFlowyBoard"> <img src="https://github.com/AppFlowy-IO/AppFlowy/blob/main/frontend/app_flowy/packages/appflowy_board/example/gifs/appflowy_board_builders.jpg?raw=true" width="100%" title="AppFlowyBoard">
</p> </p> -->
## Glossary ## Glossary
Please refer to the API documentation. Please refer to the API documentation.
@ -68,4 +76,4 @@ Please look at [CONTRIBUTING.md](https://appflowy.gitbook.io/docs/essential-docu
Distributed under the AGPLv3 License. See [LICENSE](https://github.com/AppFlowy-IO/AppFlowy-Docs/blob/main/LICENSE) for more information. Distributed under the AGPLv3 License. See [LICENSE](https://github.com/AppFlowy-IO/AppFlowy-Docs/blob/main/LICENSE) for more information.
d

View File

@ -9,21 +9,22 @@ class MultiBoardListExample extends StatefulWidget {
} }
class _MultiBoardListExampleState extends State<MultiBoardListExample> { class _MultiBoardListExampleState extends State<MultiBoardListExample> {
final AFBoardDataController boardDataController = AFBoardDataController( final AppFlowyBoardDataController boardDataController =
onMoveColumn: (fromColumnId, fromIndex, toColumnId, toIndex) { AppFlowyBoardDataController(
onMoveGroup: (fromColumnId, fromIndex, toColumnId, toIndex) {
// debugPrint('Move column from $fromIndex to $toIndex'); // debugPrint('Move column from $fromIndex to $toIndex');
}, },
onMoveColumnItem: (columnId, fromIndex, toIndex) { onMoveGroupItem: (columnId, fromIndex, toIndex) {
// debugPrint('Move $columnId:$fromIndex to $columnId:$toIndex'); // debugPrint('Move $columnId:$fromIndex to $columnId:$toIndex');
}, },
onMoveColumnItemToColumn: (fromColumnId, fromIndex, toColumnId, toIndex) { onMoveGroupItemToGroup: (fromColumnId, fromIndex, toColumnId, toIndex) {
// debugPrint('Move $fromColumnId:$fromIndex to $toColumnId:$toIndex'); // debugPrint('Move $fromColumnId:$fromIndex to $toColumnId:$toIndex');
}, },
); );
@override @override
void initState() { void initState() {
List<AFColumnItem> a = [ List<AppFlowyGroupItem> a = [
TextItem("Card 1"), TextItem("Card 1"),
TextItem("Card 2"), TextItem("Card 2"),
RichTextItem(title: "Card 3", subtitle: 'Aug 1, 2020 4:05 PM'), RichTextItem(title: "Card 3", subtitle: 'Aug 1, 2020 4:05 PM'),
@ -35,82 +36,83 @@ class _MultiBoardListExampleState extends State<MultiBoardListExample> {
TextItem("Card 9"), TextItem("Card 9"),
]; ];
final column1 = AFBoardColumnData(id: "To Do", name: "To Do", items: a); final column1 =
final column2 = AFBoardColumnData( AppFlowyBoardGroupData(id: "To Do", name: "To Do", items: a);
final column2 = AppFlowyBoardGroupData(
id: "In Progress", id: "In Progress",
name: "In Progress", name: "In Progress",
items: <AFColumnItem>[ items: <AppFlowyGroupItem>[
RichTextItem(title: "Card 10", subtitle: 'Aug 1, 2020 4:05 PM'), RichTextItem(title: "Card 10", subtitle: 'Aug 1, 2020 4:05 PM'),
TextItem("Card 11"), TextItem("Card 11"),
], ],
); );
final column3 = final column3 = AppFlowyBoardGroupData(
AFBoardColumnData(id: "Done", name: "Done", items: <AFColumnItem>[]); id: "Done", name: "Done", items: <AppFlowyGroupItem>[]);
boardDataController.addColumn(column1); boardDataController.addGroup(column1);
boardDataController.addColumn(column2); boardDataController.addGroup(column2);
boardDataController.addColumn(column3); boardDataController.addGroup(column3);
super.initState(); super.initState();
} }
@override @override
Widget build(BuildContext context) { Widget build(BuildContext context) {
final config = AFBoardConfig( final config = AppFlowyBoardConfig(
columnBackgroundColor: HexColor.fromHex('#F7F8FC'), groupBackgroundColor: HexColor.fromHex('#F7F8FC'),
); );
return Container( return Container(
color: Colors.white, color: Colors.white,
child: Padding( child: Padding(
padding: const EdgeInsets.symmetric(vertical: 30, horizontal: 20), padding: const EdgeInsets.symmetric(vertical: 30, horizontal: 20),
child: AFBoard( child: AppFlowyBoard(
dataController: boardDataController, dataController: boardDataController,
footerBuilder: (context, columnData) { footerBuilder: (context, columnData) {
return AppFlowyColumnFooter( return AppFlowyGroupFooter(
icon: const Icon(Icons.add, size: 20), icon: const Icon(Icons.add, size: 20),
title: const Text('New'), title: const Text('New'),
height: 50, height: 50,
margin: config.columnItemPadding, margin: config.groupItemPadding,
); );
}, },
headerBuilder: (context, columnData) { headerBuilder: (context, columnData) {
return AppFlowyColumnHeader( return AppFlowyGroupHeader(
icon: const Icon(Icons.lightbulb_circle), icon: const Icon(Icons.lightbulb_circle),
title: SizedBox( title: SizedBox(
width: 60, width: 60,
child: TextField( child: TextField(
controller: TextEditingController() controller: TextEditingController()
..text = columnData.headerData.columnName, ..text = columnData.headerData.groupName,
onSubmitted: (val) { onSubmitted: (val) {
boardDataController boardDataController
.getColumnController(columnData.headerData.columnId)! .getGroupController(columnData.headerData.groupId)!
.updateColumnName(val); .updateGroupName(val);
}, },
), ),
), ),
addIcon: const Icon(Icons.add, size: 20), addIcon: const Icon(Icons.add, size: 20),
moreIcon: const Icon(Icons.more_horiz, size: 20), moreIcon: const Icon(Icons.more_horiz, size: 20),
height: 50, height: 50,
margin: config.columnItemPadding, margin: config.groupItemPadding,
); );
}, },
cardBuilder: (context, column, columnItem) { cardBuilder: (context, column, columnItem) {
return AppFlowyColumnItemCard( return AppFlowyGroupItemCard(
key: ValueKey(columnItem.id), key: ValueKey(columnItem.id),
child: _buildCard(columnItem), child: _buildCard(columnItem),
); );
}, },
columnConstraints: const BoxConstraints.tightFor(width: 240), groupConstraints: const BoxConstraints.tightFor(width: 240),
config: AFBoardConfig( config: AppFlowyBoardConfig(
columnBackgroundColor: HexColor.fromHex('#F7F8FC'), groupBackgroundColor: HexColor.fromHex('#F7F8FC'),
), ),
), ),
), ),
); );
} }
Widget _buildCard(AFColumnItem item) { Widget _buildCard(AppFlowyGroupItem item) {
if (item is TextItem) { if (item is TextItem) {
return Align( return Align(
alignment: Alignment.centerLeft, alignment: Alignment.centerLeft,
@ -172,7 +174,7 @@ class _RichTextCardState extends State<RichTextCard> {
} }
} }
class TextItem extends AFColumnItem { class TextItem extends AppFlowyGroupItem {
final String s; final String s;
TextItem(this.s); TextItem(this.s);
@ -181,7 +183,7 @@ class TextItem extends AFColumnItem {
String get id => s; String get id => s;
} }
class RichTextItem extends AFColumnItem { class RichTextItem extends AppFlowyGroupItem {
final String title; final String title;
final String subtitle; final String subtitle;

View File

@ -9,11 +9,11 @@ class SingleBoardListExample extends StatefulWidget {
} }
class _SingleBoardListExampleState extends State<SingleBoardListExample> { class _SingleBoardListExampleState extends State<SingleBoardListExample> {
final AFBoardDataController boardData = AFBoardDataController(); final AppFlowyBoardDataController boardData = AppFlowyBoardDataController();
@override @override
void initState() { void initState() {
final column = AFBoardColumnData( final column = AppFlowyBoardGroupData(
id: "1", id: "1",
name: "1", name: "1",
items: [ items: [
@ -24,13 +24,13 @@ class _SingleBoardListExampleState extends State<SingleBoardListExample> {
], ],
); );
boardData.addColumn(column); boardData.addGroup(column);
super.initState(); super.initState();
} }
@override @override
Widget build(BuildContext context) { Widget build(BuildContext context) {
return AFBoard( return AppFlowyBoard(
dataController: boardData, dataController: boardData,
cardBuilder: (context, column, columnItem) { cardBuilder: (context, column, columnItem) {
return _RowWidget( return _RowWidget(
@ -55,7 +55,7 @@ class _RowWidget extends StatelessWidget {
} }
} }
class TextItem extends AFColumnItem { class TextItem extends AppFlowyGroupItem {
final String s; final String s;
TextItem(this.s); TextItem(this.s);

View File

@ -11,70 +11,78 @@ import 'reorder_phantom/phantom_controller.dart';
import '../rendering/board_overlay.dart'; import '../rendering/board_overlay.dart';
class AFBoardScrollManager { class AFBoardScrollManager {
BoardColumnsState? _columnState; BoardGroupsState? _groupState;
// AFBoardScrollManager(); void scrollToBottom(String groupId, VoidCallback? completed) {
_groupState
void scrollToBottom(String columnId, VoidCallback? completed) { ?.getReorderFlexState(groupId: groupId)
_columnState
?.getReorderFlexState(columnId: columnId)
?.scrollToBottom(completed); ?.scrollToBottom(completed);
} }
} }
class AFBoardConfig { class AppFlowyBoardConfig {
final double cornerRadius; final double cornerRadius;
final EdgeInsets columnPadding; final EdgeInsets groupPadding;
final EdgeInsets columnItemPadding; final EdgeInsets groupItemPadding;
final EdgeInsets footerPadding; final EdgeInsets footerPadding;
final EdgeInsets headerPadding; final EdgeInsets headerPadding;
final EdgeInsets cardPadding; final EdgeInsets cardPadding;
final Color columnBackgroundColor; final Color groupBackgroundColor;
const AFBoardConfig({ const AppFlowyBoardConfig({
this.cornerRadius = 6.0, this.cornerRadius = 6.0,
this.columnPadding = const EdgeInsets.symmetric(horizontal: 8), this.groupPadding = const EdgeInsets.symmetric(horizontal: 8),
this.columnItemPadding = const EdgeInsets.symmetric(horizontal: 12), this.groupItemPadding = const EdgeInsets.symmetric(horizontal: 12),
this.footerPadding = const EdgeInsets.symmetric(horizontal: 12), this.footerPadding = const EdgeInsets.symmetric(horizontal: 12),
this.headerPadding = const EdgeInsets.symmetric(horizontal: 16), this.headerPadding = const EdgeInsets.symmetric(horizontal: 16),
this.cardPadding = const EdgeInsets.symmetric(horizontal: 3, vertical: 4), this.cardPadding = const EdgeInsets.symmetric(horizontal: 3, vertical: 4),
this.columnBackgroundColor = Colors.transparent, this.groupBackgroundColor = Colors.transparent,
}); });
} }
class AFBoard extends StatelessWidget { class AppFlowyBoard extends StatelessWidget {
/// The direction to use as the main axis. /// The direction to use as the main axis.
final Axis direction = Axis.vertical; final Axis direction = Axis.vertical;
/// /// The widget that will be rendered as the background of the board.
final Widget? background; final Widget? background;
/// The [cardBuilder] function which will be invoked on each card build.
/// The [cardBuilder] takes the [BuildContext],[AppFlowyBoardGroupData] and
/// the corresponding [AppFlowyGroupItem].
/// ///
final AFBoardColumnCardBuilder cardBuilder; /// must return a widget.
final AppFlowyBoardCardBuilder cardBuilder;
/// The [headerBuilder] function which will be invoked on each group build.
/// The [headerBuilder] takes the [BuildContext] and [AppFlowyBoardGroupData].
///
/// must return a widget.
final AppFlowyBoardHeaderBuilder? headerBuilder;
/// The [footerBuilder] function which will be invoked on each group build.
/// The [footerBuilder] takes the [BuildContext] and [AppFlowyBoardGroupData].
///
/// must return a widget.
final AppFlowyBoardFooterBuilder? footerBuilder;
/// ///
final AFBoardColumnHeaderBuilder? headerBuilder; final AppFlowyBoardDataController dataController;
/// final BoxConstraints groupConstraints;
final AFBoardColumnFooterBuilder? footerBuilder;
///
final AFBoardDataController dataController;
final BoxConstraints columnConstraints;
/// ///
late final BoardPhantomController phantomController; late final BoardPhantomController phantomController;
final ScrollController? scrollController; final ScrollController? scrollController;
final AFBoardConfig config; final AppFlowyBoardConfig config;
final AFBoardScrollManager? scrollManager; final AFBoardScrollManager? scrollManager;
final BoardColumnsState _columnState = BoardColumnsState(); final BoardGroupsState _groupState = BoardGroupsState();
AFBoard({ AppFlowyBoard({
required this.dataController, required this.dataController,
required this.cardBuilder, required this.cardBuilder,
this.background, this.background,
@ -82,13 +90,13 @@ class AFBoard extends StatelessWidget {
this.headerBuilder, this.headerBuilder,
this.scrollController, this.scrollController,
this.scrollManager, this.scrollManager,
this.columnConstraints = const BoxConstraints(maxWidth: 200), this.groupConstraints = const BoxConstraints(maxWidth: 200),
this.config = const AFBoardConfig(), this.config = const AppFlowyBoardConfig(),
Key? key, Key? key,
}) : super(key: key) { }) : super(key: key) {
phantomController = BoardPhantomController( phantomController = BoardPhantomController(
delegate: dataController, delegate: dataController,
columnsState: _columnState, groupsState: _groupState,
); );
} }
@ -96,10 +104,10 @@ class AFBoard extends StatelessWidget {
Widget build(BuildContext context) { Widget build(BuildContext context) {
return ChangeNotifierProvider.value( return ChangeNotifierProvider.value(
value: dataController, value: dataController,
child: Consumer<AFBoardDataController>( child: Consumer<AppFlowyBoardDataController>(
builder: (context, notifier, child) { builder: (context, notifier, child) {
if (scrollManager != null) { if (scrollManager != null) {
scrollManager!._columnState = _columnState; scrollManager!._groupState = _groupState;
} }
return AFBoardContent( return AFBoardContent(
@ -107,15 +115,15 @@ class AFBoard extends StatelessWidget {
dataController: dataController, dataController: dataController,
scrollController: scrollController, scrollController: scrollController,
scrollManager: scrollManager, scrollManager: scrollManager,
columnsState: _columnState, columnsState: _groupState,
background: background, background: background,
delegate: phantomController, delegate: phantomController,
columnConstraints: columnConstraints, columnConstraints: groupConstraints,
cardBuilder: cardBuilder, cardBuilder: cardBuilder,
footBuilder: footerBuilder, footerBuilder: footerBuilder,
headerBuilder: headerBuilder, headerBuilder: headerBuilder,
phantomController: phantomController, phantomController: phantomController,
onReorder: dataController.moveColumn, onReorder: dataController.moveGroup,
); );
}, },
), ),
@ -128,22 +136,22 @@ class AFBoardContent extends StatefulWidget {
final OnDragStarted? onDragStarted; final OnDragStarted? onDragStarted;
final OnReorder onReorder; final OnReorder onReorder;
final OnDragEnded? onDragEnded; final OnDragEnded? onDragEnded;
final AFBoardDataController dataController; final AppFlowyBoardDataController dataController;
final Widget? background; final Widget? background;
final AFBoardConfig config; final AppFlowyBoardConfig config;
final ReorderFlexConfig reorderFlexConfig; final ReorderFlexConfig reorderFlexConfig;
final BoxConstraints columnConstraints; final BoxConstraints columnConstraints;
final AFBoardScrollManager? scrollManager; final AFBoardScrollManager? scrollManager;
final BoardColumnsState columnsState; final BoardGroupsState columnsState;
/// ///
final AFBoardColumnCardBuilder cardBuilder; final AppFlowyBoardCardBuilder cardBuilder;
/// ///
final AFBoardColumnHeaderBuilder? headerBuilder; final AppFlowyBoardHeaderBuilder? headerBuilder;
/// ///
final AFBoardColumnFooterBuilder? footBuilder; final AppFlowyBoardFooterBuilder? footerBuilder;
final OverlapDragTargetDelegate delegate; final OverlapDragTargetDelegate delegate;
@ -162,7 +170,7 @@ class AFBoardContent extends StatefulWidget {
this.background, this.background,
required this.columnConstraints, required this.columnConstraints,
required this.cardBuilder, required this.cardBuilder,
this.footBuilder, this.footerBuilder,
this.headerBuilder, this.headerBuilder,
required this.phantomController, required this.phantomController,
Key? key, Key? key,
@ -178,13 +186,15 @@ class _AFBoardContentState extends State<AFBoardContent> {
GlobalKey(debugLabel: '$AFBoardContent overlay key'); GlobalKey(debugLabel: '$AFBoardContent overlay key');
late BoardOverlayEntry _overlayEntry; late BoardOverlayEntry _overlayEntry;
final Map<String, GlobalObjectKey> _reorderFlexKeys = {};
@override @override
void initState() { void initState() {
_overlayEntry = BoardOverlayEntry( _overlayEntry = BoardOverlayEntry(
builder: (BuildContext context) { builder: (BuildContext context) {
final interceptor = OverlappingDragTargetInterceptor( final interceptor = OverlappingDragTargetInterceptor(
reorderFlexId: widget.dataController.identifier, reorderFlexId: widget.dataController.identifier,
acceptedReorderFlexId: widget.dataController.columnIds, acceptedReorderFlexId: widget.dataController.groupIds,
delegate: widget.delegate, delegate: widget.delegate,
columnsState: widget.columnsState, columnsState: widget.columnsState,
); );
@ -233,40 +243,45 @@ class _AFBoardContentState extends State<AFBoardContent> {
List<Widget> _buildColumns() { List<Widget> _buildColumns() {
final List<Widget> children = final List<Widget> children =
widget.dataController.columnDatas.asMap().entries.map( widget.dataController.groupDatas.asMap().entries.map(
(item) { (item) {
final columnData = item.value; final columnData = item.value;
final columnIndex = item.key; final columnIndex = item.key;
final dataSource = _BoardColumnDataSourceImpl( final dataSource = _BoardGroupDataSourceImpl(
columnId: columnData.id, groupId: columnData.id,
dataController: widget.dataController, dataController: widget.dataController,
); );
if (_reorderFlexKeys[columnData.id] == null) {
_reorderFlexKeys[columnData.id] = GlobalObjectKey(columnData.id);
}
GlobalObjectKey reorderFlexKey = _reorderFlexKeys[columnData.id]!;
return ChangeNotifierProvider.value( return ChangeNotifierProvider.value(
key: ValueKey(columnData.id), key: ValueKey(columnData.id),
value: widget.dataController.getColumnController(columnData.id), value: widget.dataController.getGroupController(columnData.id),
child: Consumer<AFBoardColumnDataController>( child: Consumer<AFBoardGroupDataController>(
builder: (context, value, child) { builder: (context, value, child) {
final boardColumn = AFBoardColumnWidget( final boardColumn = AppFlowyBoardGroupWidget(
reorderFlexKey: reorderFlexKey,
// key: PageStorageKey<String>(columnData.id), // key: PageStorageKey<String>(columnData.id),
margin: _marginFromIndex(columnIndex), margin: _marginFromIndex(columnIndex),
itemMargin: widget.config.columnItemPadding, itemMargin: widget.config.groupItemPadding,
headerBuilder: _buildHeader, headerBuilder: _buildHeader,
footBuilder: widget.footBuilder, footerBuilder: widget.footerBuilder,
cardBuilder: widget.cardBuilder, cardBuilder: widget.cardBuilder,
dataSource: dataSource, dataSource: dataSource,
scrollController: ScrollController(), scrollController: ScrollController(),
phantomController: widget.phantomController, phantomController: widget.phantomController,
onReorder: widget.dataController.moveColumnItem, onReorder: widget.dataController.moveGroupItem,
cornerRadius: widget.config.cornerRadius, cornerRadius: widget.config.cornerRadius,
backgroundColor: widget.config.columnBackgroundColor, backgroundColor: widget.config.groupBackgroundColor,
dragStateStorage: widget.columnsState, dragStateStorage: widget.columnsState,
dragTargetIndexKeyStorage: widget.columnsState, dragTargetIndexKeyStorage: widget.columnsState,
); );
widget.columnsState.addColumn(columnData.id, boardColumn); widget.columnsState.addGroup(columnData.id, boardColumn);
return ConstrainedBox( return ConstrainedBox(
constraints: widget.columnConstraints, constraints: widget.columnConstraints,
child: boardColumn, child: boardColumn,
@ -282,79 +297,79 @@ class _AFBoardContentState extends State<AFBoardContent> {
Widget? _buildHeader( Widget? _buildHeader(
BuildContext context, BuildContext context,
AFBoardColumnData columnData, AppFlowyBoardGroupData groupData,
) { ) {
if (widget.headerBuilder == null) { if (widget.headerBuilder == null) {
return null; return null;
} }
return Selector<AFBoardColumnDataController, AFBoardColumnHeaderData>( return Selector<AFBoardGroupDataController, AppFlowyBoardGroupHeaderData>(
selector: (context, controller) => controller.columnData.headerData, selector: (context, controller) => controller.groupData.headerData,
builder: (context, headerData, _) { builder: (context, headerData, _) {
return widget.headerBuilder!(context, columnData)!; return widget.headerBuilder!(context, groupData)!;
}, },
); );
} }
EdgeInsets _marginFromIndex(int index) { EdgeInsets _marginFromIndex(int index) {
if (widget.dataController.columnDatas.isEmpty) { if (widget.dataController.groupDatas.isEmpty) {
return widget.config.columnPadding; return widget.config.groupPadding;
} }
if (index == 0) { if (index == 0) {
return EdgeInsets.only(right: widget.config.columnPadding.right); return EdgeInsets.only(right: widget.config.groupPadding.right);
} }
if (index == widget.dataController.columnDatas.length - 1) { if (index == widget.dataController.groupDatas.length - 1) {
return EdgeInsets.only(left: widget.config.columnPadding.left); return EdgeInsets.only(left: widget.config.groupPadding.left);
} }
return widget.config.columnPadding; return widget.config.groupPadding;
} }
} }
class _BoardColumnDataSourceImpl extends AFBoardColumnDataDataSource { class _BoardGroupDataSourceImpl extends AppFlowyBoardGroupDataDataSource {
String columnId; String groupId;
final AFBoardDataController dataController; final AppFlowyBoardDataController dataController;
_BoardColumnDataSourceImpl({ _BoardGroupDataSourceImpl({
required this.columnId, required this.groupId,
required this.dataController, required this.dataController,
}); });
@override @override
AFBoardColumnData get columnData => AppFlowyBoardGroupData get groupData =>
dataController.getColumnController(columnId)!.columnData; dataController.getGroupController(groupId)!.groupData;
@override @override
List<String> get acceptedColumnIds => dataController.columnIds; List<String> get acceptedGroupIds => dataController.groupIds;
} }
class BoardColumnContext { class BoardGroupContext {
GlobalKey? columnKey; GlobalKey? groupKey;
DraggingState? draggingState; DraggingState? draggingState;
} }
class BoardColumnsState extends DraggingStateStorage class BoardGroupsState extends DraggingStateStorage
with ReorderDragTargetIndexKeyStorage { with ReorderDragTargetIndexKeyStorage {
/// Quick access to the [AFBoardColumnWidget] /// Quick access to the [AppFlowyBoardGroupWidget]
final Map<String, GlobalKey> columnKeys = {}; final Map<String, GlobalKey> groupKeys = {};
final Map<String, DraggingState> columnDragStates = {}; final Map<String, DraggingState> groupDragStates = {};
final Map<String, Map<String, GlobalObjectKey>> columnDragDragTargets = {}; final Map<String, Map<String, GlobalObjectKey>> groupDragDragTargets = {};
void addColumn(String columnId, AFBoardColumnWidget columnWidget) { void addGroup(String groupId, AppFlowyBoardGroupWidget groupWidget) {
columnKeys[columnId] = columnWidget.globalKey; groupKeys[groupId] = groupWidget.reorderFlexKey;
} }
ReorderFlexState? getReorderFlexState({required String columnId}) { ReorderFlexState? getReorderFlexState({required String groupId}) {
final flexGlobalKey = columnKeys[columnId]; final flexGlobalKey = groupKeys[groupId];
if (flexGlobalKey == null) return null; if (flexGlobalKey == null) return null;
if (flexGlobalKey.currentState is! ReorderFlexState) return null; if (flexGlobalKey.currentState is! ReorderFlexState) return null;
final state = flexGlobalKey.currentState as ReorderFlexState; final state = flexGlobalKey.currentState as ReorderFlexState;
return state; return state;
} }
ReorderFlex? getReorderFlex({required String columnId}) { ReorderFlex? getReorderFlex({required String groupId}) {
final flexGlobalKey = columnKeys[columnId]; final flexGlobalKey = groupKeys[groupId];
if (flexGlobalKey == null) return null; if (flexGlobalKey == null) return null;
if (flexGlobalKey.currentWidget is! ReorderFlex) return null; if (flexGlobalKey.currentWidget is! ReorderFlex) return null;
final widget = flexGlobalKey.currentWidget as ReorderFlex; final widget = flexGlobalKey.currentWidget as ReorderFlex;
@ -363,18 +378,18 @@ class BoardColumnsState extends DraggingStateStorage
@override @override
DraggingState? read(String reorderFlexId) { DraggingState? read(String reorderFlexId) {
return columnDragStates[reorderFlexId]; return groupDragStates[reorderFlexId];
} }
@override @override
void write(String reorderFlexId, DraggingState state) { void write(String reorderFlexId, DraggingState state) {
Log.trace('$reorderFlexId Write dragging state: $state'); Log.trace('$reorderFlexId Write dragging state: $state');
columnDragStates[reorderFlexId] = state; groupDragStates[reorderFlexId] = state;
} }
@override @override
void remove(String reorderFlexId) { void remove(String reorderFlexId) {
columnDragStates.remove(reorderFlexId); groupDragStates.remove(reorderFlexId);
} }
@override @override
@ -383,20 +398,20 @@ class BoardColumnsState extends DraggingStateStorage
String key, String key,
GlobalObjectKey<State<StatefulWidget>> value, GlobalObjectKey<State<StatefulWidget>> value,
) { ) {
Map<String, GlobalObjectKey>? column = columnDragDragTargets[reorderFlexId]; Map<String, GlobalObjectKey>? group = groupDragDragTargets[reorderFlexId];
if (column == null) { if (group == null) {
column = {}; group = {};
columnDragDragTargets[reorderFlexId] = column; groupDragDragTargets[reorderFlexId] = group;
} }
column[key] = value; group[key] = value;
} }
@override @override
GlobalObjectKey<State<StatefulWidget>>? readKey( GlobalObjectKey<State<StatefulWidget>>? readKey(
String reorderFlexId, String key) { String reorderFlexId, String key) {
Map<String, GlobalObjectKey>? column = columnDragDragTargets[reorderFlexId]; Map<String, GlobalObjectKey>? group = groupDragDragTargets[reorderFlexId];
if (column != null) { if (group != null) {
return column[key]; return group[key];
} else { } else {
return null; return null;
} }

View File

@ -9,49 +9,49 @@ import '../reorder_flex/reorder_flex.dart';
import '../reorder_flex/drag_target_interceptor.dart'; import '../reorder_flex/drag_target_interceptor.dart';
import 'board_column_data.dart'; import 'board_column_data.dart';
typedef OnColumnDragStarted = void Function(int index); typedef OnGroupDragStarted = void Function(int index);
typedef OnColumnDragEnded = void Function(String listId); typedef OnGroupDragEnded = void Function(String listId);
typedef OnColumnReorder = void Function( typedef OnGroupReorder = void Function(
String listId, String listId,
int fromIndex, int fromIndex,
int toIndex, int toIndex,
); );
typedef OnColumnDeleted = void Function(String listId, int deletedIndex); typedef OnGroupDeleted = void Function(String listId, int deletedIndex);
typedef OnColumnInserted = void Function(String listId, int insertedIndex); typedef OnGroupInserted = void Function(String listId, int insertedIndex);
typedef AFBoardColumnCardBuilder = Widget Function( typedef AppFlowyBoardCardBuilder = Widget Function(
BuildContext context, BuildContext context,
AFBoardColumnData columnData, AppFlowyBoardGroupData groupData,
AFColumnItem item, AppFlowyGroupItem item,
); );
typedef AFBoardColumnHeaderBuilder = Widget? Function( typedef AppFlowyBoardHeaderBuilder = Widget? Function(
BuildContext context, BuildContext context,
AFBoardColumnData columnData, AppFlowyBoardGroupData groupData,
); );
typedef AFBoardColumnFooterBuilder = Widget Function( typedef AppFlowyBoardFooterBuilder = Widget Function(
BuildContext context, BuildContext context,
AFBoardColumnData columnData, AppFlowyBoardGroupData groupData,
); );
abstract class AFBoardColumnDataDataSource extends ReoderFlexDataSource { abstract class AppFlowyBoardGroupDataDataSource extends ReoderFlexDataSource {
AFBoardColumnData get columnData; AppFlowyBoardGroupData get groupData;
List<String> get acceptedColumnIds; List<String> get acceptedGroupIds;
@override @override
String get identifier => columnData.id; String get identifier => groupData.id;
@override @override
UnmodifiableListView<AFColumnItem> get items => columnData.items; UnmodifiableListView<AppFlowyGroupItem> get items => groupData.items;
void debugPrint() { void debugPrint() {
String msg = '[$AFBoardColumnDataDataSource] $columnData data: '; String msg = '[$AppFlowyBoardGroupDataDataSource] $groupData data: ';
for (var element in items) { for (var element in items) {
msg = '$msg$element,'; msg = '$msg$element,';
} }
@ -60,25 +60,25 @@ abstract class AFBoardColumnDataDataSource extends ReoderFlexDataSource {
} }
} }
/// [AFBoardColumnWidget] represents the column of the Board. /// [AppFlowyBoardGroupWidget] represents the column of the Board.
/// ///
class AFBoardColumnWidget extends StatefulWidget { class AppFlowyBoardGroupWidget extends StatefulWidget {
final AFBoardColumnDataDataSource dataSource; final AppFlowyBoardGroupDataDataSource dataSource;
final ScrollController? scrollController; final ScrollController? scrollController;
final ReorderFlexConfig config; final ReorderFlexConfig config;
final OnColumnDragStarted? onDragStarted; final OnGroupDragStarted? onDragStarted;
final OnColumnReorder onReorder; final OnGroupReorder onReorder;
final OnColumnDragEnded? onDragEnded; final OnGroupDragEnded? onDragEnded;
final BoardPhantomController phantomController; final BoardPhantomController phantomController;
String get columnId => dataSource.columnData.id; String get groupId => dataSource.groupData.id;
final AFBoardColumnCardBuilder cardBuilder; final AppFlowyBoardCardBuilder cardBuilder;
final AFBoardColumnHeaderBuilder? headerBuilder; final AppFlowyBoardHeaderBuilder? headerBuilder;
final AFBoardColumnFooterBuilder? footBuilder; final AppFlowyBoardFooterBuilder? footerBuilder;
final EdgeInsets margin; final EdgeInsets margin;
@ -92,12 +92,13 @@ class AFBoardColumnWidget extends StatefulWidget {
final ReorderDragTargetIndexKeyStorage? dragTargetIndexKeyStorage; final ReorderDragTargetIndexKeyStorage? dragTargetIndexKeyStorage;
final GlobalObjectKey globalKey; final GlobalObjectKey reorderFlexKey;
AFBoardColumnWidget({ const AppFlowyBoardGroupWidget({
Key? key, Key? key,
required this.reorderFlexKey,
this.headerBuilder, this.headerBuilder,
this.footBuilder, this.footerBuilder,
required this.cardBuilder, required this.cardBuilder,
required this.onReorder, required this.onReorder,
required this.dataSource, required this.dataSource,
@ -111,59 +112,59 @@ class AFBoardColumnWidget extends StatefulWidget {
this.itemMargin = EdgeInsets.zero, this.itemMargin = EdgeInsets.zero,
this.cornerRadius = 0.0, this.cornerRadius = 0.0,
this.backgroundColor = Colors.transparent, this.backgroundColor = Colors.transparent,
}) : globalKey = GlobalObjectKey(dataSource.columnData.id), }) : config = const ReorderFlexConfig(setStateWhenEndDrag: false),
config = const ReorderFlexConfig(setStateWhenEndDrag: false),
super(key: key); super(key: key);
@override @override
State<AFBoardColumnWidget> createState() => _AFBoardColumnWidgetState(); State<AppFlowyBoardGroupWidget> createState() =>
_AppFlowyBoardGroupWidgetState();
} }
class _AFBoardColumnWidgetState extends State<AFBoardColumnWidget> { class _AppFlowyBoardGroupWidgetState extends State<AppFlowyBoardGroupWidget> {
final GlobalKey _columnOverlayKey = final GlobalKey _columnOverlayKey =
GlobalKey(debugLabel: '$AFBoardColumnWidget overlay key'); GlobalKey(debugLabel: '$AppFlowyBoardGroupWidget overlay key');
late BoardOverlayEntry _overlayEntry; late BoardOverlayEntry _overlayEntry;
@override @override
void initState() { void initState() {
_overlayEntry = BoardOverlayEntry( _overlayEntry = BoardOverlayEntry(
builder: (BuildContext context) { builder: (BuildContext context) {
final children = widget.dataSource.columnData.items final children = widget.dataSource.groupData.items
.map((item) => _buildWidget(context, item)) .map((item) => _buildWidget(context, item))
.toList(); .toList();
final header = final header =
widget.headerBuilder?.call(context, widget.dataSource.columnData); widget.headerBuilder?.call(context, widget.dataSource.groupData);
final footer = final footer =
widget.footBuilder?.call(context, widget.dataSource.columnData); widget.footerBuilder?.call(context, widget.dataSource.groupData);
final interceptor = CrossReorderFlexDragTargetInterceptor( final interceptor = CrossReorderFlexDragTargetInterceptor(
reorderFlexId: widget.columnId, reorderFlexId: widget.groupId,
delegate: widget.phantomController, delegate: widget.phantomController,
acceptedReorderFlexIds: widget.dataSource.acceptedColumnIds, acceptedReorderFlexIds: widget.dataSource.acceptedGroupIds,
draggableTargetBuilder: PhantomDraggableBuilder(), draggableTargetBuilder: PhantomDraggableBuilder(),
); );
Widget reorderFlex = ReorderFlex( Widget reorderFlex = ReorderFlex(
key: widget.globalKey, key: widget.reorderFlexKey,
dragStateStorage: widget.dragStateStorage, dragStateStorage: widget.dragStateStorage,
dragTargetIndexKeyStorage: widget.dragTargetIndexKeyStorage, dragTargetIndexKeyStorage: widget.dragTargetIndexKeyStorage,
scrollController: widget.scrollController, scrollController: widget.scrollController,
config: widget.config, config: widget.config,
onDragStarted: (index) { onDragStarted: (index) {
widget.phantomController.columnStartDragging(widget.columnId); widget.phantomController.groupStartDragging(widget.groupId);
widget.onDragStarted?.call(index); widget.onDragStarted?.call(index);
}, },
onReorder: ((fromIndex, toIndex) { onReorder: ((fromIndex, toIndex) {
if (widget.phantomController.isFromColumn(widget.columnId)) { if (widget.phantomController.isFromGroup(widget.groupId)) {
widget.onReorder(widget.columnId, fromIndex, toIndex); widget.onReorder(widget.groupId, fromIndex, toIndex);
widget.phantomController.transformIndex(fromIndex, toIndex); widget.phantomController.transformIndex(fromIndex, toIndex);
} }
}), }),
onDragEnded: () { onDragEnded: () {
widget.phantomController.columnEndDragging(widget.columnId); widget.phantomController.groupEndDragging(widget.groupId);
widget.onDragEnded?.call(widget.columnId); widget.onDragEnded?.call(widget.groupId);
widget.dataSource.debugPrint(); widget.dataSource.debugPrint();
}, },
dataSource: widget.dataSource, dataSource: widget.dataSource,
@ -171,6 +172,10 @@ class _AFBoardColumnWidgetState extends State<AFBoardColumnWidget> {
children: children, children: children,
); );
reorderFlex = Expanded(
child: Padding(padding: widget.itemMargin, child: reorderFlex),
);
return Container( return Container(
margin: widget.margin, margin: widget.margin,
clipBehavior: Clip.hardEdge, clipBehavior: Clip.hardEdge,
@ -181,9 +186,7 @@ class _AFBoardColumnWidgetState extends State<AFBoardColumnWidget> {
child: Column( child: Column(
children: [ children: [
if (header != null) header, if (header != null) header,
Expanded( reorderFlex,
child: Padding(padding: widget.itemMargin, child: reorderFlex),
),
if (footer != null) footer, if (footer != null) footer,
], ],
), ),
@ -202,15 +205,15 @@ class _AFBoardColumnWidgetState extends State<AFBoardColumnWidget> {
); );
} }
Widget _buildWidget(BuildContext context, AFColumnItem item) { Widget _buildWidget(BuildContext context, AppFlowyGroupItem item) {
if (item is PhantomColumnItem) { if (item is PhantomGroupItem) {
return PassthroughPhantomWidget( return PassthroughPhantomWidget(
key: UniqueKey(), key: UniqueKey(),
opacity: widget.config.draggingWidgetOpacity, opacity: widget.config.draggingWidgetOpacity,
passthroughPhantomContext: item.phantomContext, passthroughPhantomContext: item.phantomContext,
); );
} else { } else {
return widget.cardBuilder(context, widget.dataSource.columnData, item); return widget.cardBuilder(context, widget.dataSource.groupData, item);
} }
} }
} }

View File

@ -5,14 +5,14 @@ import 'package:flutter/material.dart';
import '../../utils/log.dart'; import '../../utils/log.dart';
import '../reorder_flex/reorder_flex.dart'; import '../reorder_flex/reorder_flex.dart';
abstract class AFColumnItem extends ReoderFlexItem { abstract class AppFlowyGroupItem extends ReoderFlexItem {
bool get isPhantom => false; bool get isPhantom => false;
@override @override
String toString() => id; String toString() => id;
} }
/// [AFBoardColumnDataController] is used to handle the [AFBoardColumnData]. /// [AFBoardGroupDataController] is used to handle the [AppFlowyBoardGroupData].
/// * Remove an item by calling [removeAt] method. /// * Remove an item by calling [removeAt] method.
/// * Move item to another position by calling [move] method. /// * Move item to another position by calling [move] method.
/// * Insert item to index by calling [insert] method /// * Insert item to index by calling [insert] method
@ -20,23 +20,23 @@ abstract class AFColumnItem extends ReoderFlexItem {
/// ///
/// All there operations will notify listeners by default. /// All there operations will notify listeners by default.
/// ///
class AFBoardColumnDataController extends ChangeNotifier with EquatableMixin { class AFBoardGroupDataController extends ChangeNotifier with EquatableMixin {
final AFBoardColumnData columnData; final AppFlowyBoardGroupData groupData;
AFBoardColumnDataController({ AFBoardGroupDataController({
required this.columnData, required this.groupData,
}); });
@override @override
List<Object?> get props => columnData.props; List<Object?> get props => groupData.props;
/// Returns the readonly List<ColumnItem> /// Returns the readonly List<AppFlowyGroupItem>
UnmodifiableListView<AFColumnItem> get items => UnmodifiableListView<AppFlowyGroupItem> get items =>
UnmodifiableListView(columnData.items); UnmodifiableListView(groupData.items);
void updateColumnName(String newName) { void updateGroupName(String newName) {
if (columnData.headerData.columnName != newName) { if (groupData.headerData.groupName != newName) {
columnData.headerData.columnName = newName; groupData.headerData.groupName = newName;
notifyListeners(); notifyListeners();
} }
} }
@ -46,19 +46,18 @@ class AFBoardColumnDataController extends ChangeNotifier with EquatableMixin {
/// * [notify] the default value of [notify] is true, it will notify the /// * [notify] the default value of [notify] is true, it will notify the
/// listener. Set to [false] if you do not want to notify the listeners. /// listener. Set to [false] if you do not want to notify the listeners.
/// ///
AFColumnItem removeAt(int index, {bool notify = true}) { AppFlowyGroupItem removeAt(int index, {bool notify = true}) {
assert(index >= 0); assert(index >= 0);
Log.debug( Log.debug('[$AFBoardGroupDataController] $groupData remove item at $index');
'[$AFBoardColumnDataController] $columnData remove item at $index'); final item = groupData._items.removeAt(index);
final item = columnData._items.removeAt(index);
if (notify) { if (notify) {
notifyListeners(); notifyListeners();
} }
return item; return item;
} }
void removeWhere(bool Function(AFColumnItem) condition) { void removeWhere(bool Function(AppFlowyGroupItem) condition) {
final index = items.indexWhere(condition); final index = items.indexWhere(condition);
if (index != -1) { if (index != -1) {
removeAt(index); removeAt(index);
@ -75,9 +74,9 @@ class AFBoardColumnDataController extends ChangeNotifier with EquatableMixin {
return false; return false;
} }
Log.debug( Log.debug(
'[$AFBoardColumnDataController] $columnData move item from $fromIndex to $toIndex'); '[$AFBoardGroupDataController] $groupData move item from $fromIndex to $toIndex');
final item = columnData._items.removeAt(fromIndex); final item = groupData._items.removeAt(fromIndex);
columnData._items.insert(toIndex, item); groupData._items.insert(toIndex, item);
notifyListeners(); notifyListeners();
return true; return true;
} }
@ -86,18 +85,18 @@ class AFBoardColumnDataController extends ChangeNotifier with EquatableMixin {
/// is true. /// is true.
/// ///
/// The default value of [notify] is true. /// The default value of [notify] is true.
bool insert(int index, AFColumnItem item, {bool notify = true}) { bool insert(int index, AppFlowyGroupItem item, {bool notify = true}) {
assert(index >= 0); assert(index >= 0);
Log.debug( Log.debug(
'[$AFBoardColumnDataController] $columnData insert $item at $index'); '[$AFBoardGroupDataController] $groupData insert $item at $index');
if (_containsItem(item)) { if (_containsItem(item)) {
return false; return false;
} else { } else {
if (columnData._items.length > index) { if (groupData._items.length > index) {
columnData._items.insert(index, item); groupData._items.insert(index, item);
} else { } else {
columnData._items.add(item); groupData._items.add(item);
} }
if (notify) notifyListeners(); if (notify) notifyListeners();
@ -105,74 +104,75 @@ class AFBoardColumnDataController extends ChangeNotifier with EquatableMixin {
} }
} }
bool add(AFColumnItem item, {bool notify = true}) { bool add(AppFlowyGroupItem item, {bool notify = true}) {
if (_containsItem(item)) { if (_containsItem(item)) {
return false; return false;
} else { } else {
columnData._items.add(item); groupData._items.add(item);
if (notify) notifyListeners(); if (notify) notifyListeners();
return true; return true;
} }
} }
/// Replace the item at index with the [newItem]. /// Replace the item at index with the [newItem].
void replace(int index, AFColumnItem newItem) { void replace(int index, AppFlowyGroupItem newItem) {
if (columnData._items.isEmpty) { if (groupData._items.isEmpty) {
columnData._items.add(newItem); groupData._items.add(newItem);
Log.debug('[$AFBoardColumnDataController] $columnData add $newItem'); Log.debug('[$AFBoardGroupDataController] $groupData add $newItem');
} else { } else {
if (index >= columnData._items.length) { if (index >= groupData._items.length) {
return; return;
} }
final removedItem = columnData._items.removeAt(index); final removedItem = groupData._items.removeAt(index);
columnData._items.insert(index, newItem); groupData._items.insert(index, newItem);
Log.debug( Log.debug(
'[$AFBoardColumnDataController] $columnData replace $removedItem with $newItem at $index'); '[$AFBoardGroupDataController] $groupData replace $removedItem with $newItem at $index');
} }
notifyListeners(); notifyListeners();
} }
void replaceOrInsertItem(AFColumnItem newItem) { void replaceOrInsertItem(AppFlowyGroupItem newItem) {
final index = columnData._items.indexWhere((item) => item.id == newItem.id); final index = groupData._items.indexWhere((item) => item.id == newItem.id);
if (index != -1) { if (index != -1) {
columnData._items.removeAt(index); groupData._items.removeAt(index);
columnData._items.insert(index, newItem); groupData._items.insert(index, newItem);
notifyListeners(); notifyListeners();
} else { } else {
columnData._items.add(newItem); groupData._items.add(newItem);
notifyListeners(); notifyListeners();
} }
} }
bool _containsItem(AFColumnItem item) { bool _containsItem(AppFlowyGroupItem item) {
return columnData._items.indexWhere((element) => element.id == item.id) != return groupData._items.indexWhere((element) => element.id == item.id) !=
-1; -1;
} }
} }
/// [AFBoardColumnData] represents the data of each Column of the Board. /// [AppFlowyBoardGroupData] represents the data of each group of the Board.
class AFBoardColumnData<CustomData> extends ReoderFlexItem with EquatableMixin { class AppFlowyBoardGroupData<CustomData> extends ReoderFlexItem
with EquatableMixin {
@override @override
final String id; final String id;
AFBoardColumnHeaderData headerData; AppFlowyBoardGroupHeaderData headerData;
final List<AFColumnItem> _items; final List<AppFlowyGroupItem> _items;
final CustomData? customData; final CustomData? customData;
AFBoardColumnData({ AppFlowyBoardGroupData({
this.customData, this.customData,
required this.id, required this.id,
required String name, required String name,
List<AFColumnItem> items = const [], List<AppFlowyGroupItem> items = const [],
}) : _items = items, }) : _items = items,
headerData = AFBoardColumnHeaderData( headerData = AppFlowyBoardGroupHeaderData(
columnId: id, groupId: id,
columnName: name, groupName: name,
); );
/// Returns the readonly List<ColumnItem> /// Returns the readonly List<AppFlowyGroupItem>
UnmodifiableListView<AFColumnItem> get items => UnmodifiableListView<AppFlowyGroupItem> get items =>
UnmodifiableListView([..._items]); UnmodifiableListView([..._items]);
@override @override
@ -180,13 +180,14 @@ class AFBoardColumnData<CustomData> extends ReoderFlexItem with EquatableMixin {
@override @override
String toString() { String toString() {
return 'Column:[$id]'; return 'Group:[$id]';
} }
} }
class AFBoardColumnHeaderData { class AppFlowyBoardGroupHeaderData {
String columnId; String groupId;
String columnName; String groupName;
AFBoardColumnHeaderData({required this.columnId, required this.columnName}); AppFlowyBoardGroupHeaderData(
{required this.groupId, required this.groupName});
} }

View File

@ -8,213 +8,213 @@ import 'reorder_flex/reorder_flex.dart';
import 'package:flutter/material.dart'; import 'package:flutter/material.dart';
import 'reorder_phantom/phantom_controller.dart'; import 'reorder_phantom/phantom_controller.dart';
typedef OnMoveColumn = void Function( typedef OnMoveGroup = void Function(
String fromColumnId, String fromGroupId,
int fromIndex, int fromIndex,
String toColumnId, String toGroupId,
int toIndex, int toIndex,
); );
typedef OnMoveColumnItem = void Function( typedef OnMoveGroupItem = void Function(
String columnId, String groupId,
int fromIndex, int fromIndex,
int toIndex, int toIndex,
); );
typedef OnMoveColumnItemToColumn = void Function( typedef OnMoveGroupItemToGroup = void Function(
String fromColumnId, String fromGroupId,
int fromIndex, int fromIndex,
String toColumnId, String toGroupId,
int toIndex, int toIndex,
); );
class AFBoardDataController extends ChangeNotifier class AppFlowyBoardDataController extends ChangeNotifier
with EquatableMixin, BoardPhantomControllerDelegate, ReoderFlexDataSource { with EquatableMixin, BoardPhantomControllerDelegate, ReoderFlexDataSource {
final List<AFBoardColumnData> _columnDatas = []; final List<AppFlowyBoardGroupData> _groupDatas = [];
final OnMoveColumn? onMoveColumn; final OnMoveGroup? onMoveGroup;
final OnMoveColumnItem? onMoveColumnItem; final OnMoveGroupItem? onMoveGroupItem;
final OnMoveColumnItemToColumn? onMoveColumnItemToColumn; final OnMoveGroupItemToGroup? onMoveGroupItemToGroup;
List<AFBoardColumnData> get columnDatas => _columnDatas; UnmodifiableListView<AppFlowyBoardGroupData> get groupDatas =>
UnmodifiableListView(_groupDatas);
List<String> get columnIds => List<String> get groupIds =>
_columnDatas.map((columnData) => columnData.id).toList(); _groupDatas.map((groupData) => groupData.id).toList();
final LinkedHashMap<String, AFBoardColumnDataController> _columnControllers = final LinkedHashMap<String, AFBoardGroupDataController> _groupControllers =
LinkedHashMap(); LinkedHashMap();
AFBoardDataController({ AppFlowyBoardDataController({
this.onMoveColumn, this.onMoveGroup,
this.onMoveColumnItem, this.onMoveGroupItem,
this.onMoveColumnItemToColumn, this.onMoveGroupItemToGroup,
}); });
void addColumn(AFBoardColumnData columnData, {bool notify = true}) { void addGroup(AppFlowyBoardGroupData groupData, {bool notify = true}) {
if (_columnControllers[columnData.id] != null) return; if (_groupControllers[groupData.id] != null) return;
final controller = AFBoardColumnDataController(columnData: columnData); final controller = AFBoardGroupDataController(groupData: groupData);
_columnDatas.add(columnData); _groupDatas.add(groupData);
_columnControllers[columnData.id] = controller; _groupControllers[groupData.id] = controller;
if (notify) notifyListeners(); if (notify) notifyListeners();
} }
void addColumns(List<AFBoardColumnData> columns, {bool notify = true}) { void addGroups(List<AppFlowyBoardGroupData> groups, {bool notify = true}) {
for (final column in columns) { for (final column in groups) {
addColumn(column, notify: false); addGroup(column, notify: false);
} }
if (columns.isNotEmpty && notify) notifyListeners(); if (groups.isNotEmpty && notify) notifyListeners();
} }
void removeColumn(String columnId, {bool notify = true}) { void removeGroup(String groupId, {bool notify = true}) {
final index = _columnDatas.indexWhere((column) => column.id == columnId); final index = _groupDatas.indexWhere((group) => group.id == groupId);
if (index == -1) { if (index == -1) {
Log.warn( Log.warn(
'Try to remove Column:[$columnId] failed. Column:[$columnId] not exist'); 'Try to remove Group:[$groupId] failed. Group:[$groupId] not exist');
} }
if (index != -1) { if (index != -1) {
_columnDatas.removeAt(index); _groupDatas.removeAt(index);
_columnControllers.remove(columnId); _groupControllers.remove(groupId);
if (notify) notifyListeners(); if (notify) notifyListeners();
} }
} }
void removeColumns(List<String> columnIds, {bool notify = true}) { void removeGroups(List<String> groupIds, {bool notify = true}) {
for (final columnId in columnIds) { for (final groupId in groupIds) {
removeColumn(columnId, notify: false); removeGroup(groupId, notify: false);
} }
if (columnIds.isNotEmpty && notify) notifyListeners(); if (groupIds.isNotEmpty && notify) notifyListeners();
} }
void clear() { void clear() {
_columnDatas.clear(); _groupDatas.clear();
_columnControllers.clear(); _groupControllers.clear();
notifyListeners(); notifyListeners();
} }
AFBoardColumnDataController? getColumnController(String columnId) { AFBoardGroupDataController? getGroupController(String groupId) {
final columnController = _columnControllers[columnId]; final groupController = _groupControllers[groupId];
if (columnController == null) { if (groupController == null) {
Log.warn('Column:[$columnId] \'s controller is not exist'); Log.warn('Group:[$groupId] \'s controller is not exist');
} }
return columnController; return groupController;
} }
void moveColumn(int fromIndex, int toIndex, {bool notify = true}) { void moveGroup(int fromIndex, int toIndex, {bool notify = true}) {
final toColumnData = _columnDatas[toIndex]; final toGroupData = _groupDatas[toIndex];
final fromColumnData = _columnDatas.removeAt(fromIndex); final fromGroupData = _groupDatas.removeAt(fromIndex);
_columnDatas.insert(toIndex, fromColumnData); _groupDatas.insert(toIndex, fromGroupData);
onMoveColumn?.call(fromColumnData.id, fromIndex, toColumnData.id, toIndex); onMoveGroup?.call(fromGroupData.id, fromIndex, toGroupData.id, toIndex);
if (notify) notifyListeners(); if (notify) notifyListeners();
} }
void moveColumnItem(String columnId, int fromIndex, int toIndex) { void moveGroupItem(String groupId, int fromIndex, int toIndex) {
if (getColumnController(columnId)?.move(fromIndex, toIndex) ?? false) { if (getGroupController(groupId)?.move(fromIndex, toIndex) ?? false) {
onMoveColumnItem?.call(columnId, fromIndex, toIndex); onMoveGroupItem?.call(groupId, fromIndex, toIndex);
} }
} }
void addColumnItem(String columnId, AFColumnItem item) { void addGroupItem(String groupId, AppFlowyGroupItem item) {
getColumnController(columnId)?.add(item); getGroupController(groupId)?.add(item);
} }
void insertColumnItem(String columnId, int index, AFColumnItem item) { void insertGroupItem(String groupId, int index, AppFlowyGroupItem item) {
getColumnController(columnId)?.insert(index, item); getGroupController(groupId)?.insert(index, item);
} }
void removeColumnItem(String columnId, String itemId) { void removeGroupItem(String groupId, String itemId) {
getColumnController(columnId)?.removeWhere((item) => item.id == itemId); getGroupController(groupId)?.removeWhere((item) => item.id == itemId);
} }
void updateColumnItem(String columnId, AFColumnItem item) { void updateGroupItem(String groupId, AppFlowyGroupItem item) {
getColumnController(columnId)?.replaceOrInsertItem(item); getGroupController(groupId)?.replaceOrInsertItem(item);
} }
@override @override
@protected @protected
void swapColumnItem( void swapGroupItem(
String fromColumnId, String fromGroupId,
int fromColumnIndex, int fromGroupIndex,
String toColumnId, String toGroupId,
int toColumnIndex, int toGroupIndex,
) { ) {
final fromColumnController = getColumnController(fromColumnId)!; final fromGroupController = getGroupController(fromGroupId)!;
final toColumnController = getColumnController(toColumnId)!; final toGroupController = getGroupController(toGroupId)!;
final item = fromColumnController.removeAt(fromColumnIndex); final item = fromGroupController.removeAt(fromGroupIndex);
if (toColumnController.items.length > toColumnIndex) { if (toGroupController.items.length > toGroupIndex) {
assert(toColumnController.items[toColumnIndex] is PhantomColumnItem); assert(toGroupController.items[toGroupIndex] is PhantomGroupItem);
} }
toColumnController.replace(toColumnIndex, item); toGroupController.replace(toGroupIndex, item);
onMoveColumnItemToColumn?.call( onMoveGroupItemToGroup?.call(
fromColumnId, fromGroupId,
fromColumnIndex, fromGroupIndex,
toColumnId, toGroupId,
toColumnIndex, toGroupIndex,
); );
} }
@override @override
List<Object?> get props { List<Object?> get props {
return [_columnDatas]; return [_groupDatas];
} }
@override @override
AFBoardColumnDataController? controller(String columnId) { AFBoardGroupDataController? controller(String groupId) {
return _columnControllers[columnId]; return _groupControllers[groupId];
} }
@override @override
String get identifier => '$AFBoardDataController'; String get identifier => '$AppFlowyBoardDataController';
@override @override
UnmodifiableListView<ReoderFlexItem> get items => UnmodifiableListView<ReoderFlexItem> get items =>
UnmodifiableListView(_columnDatas); UnmodifiableListView(_groupDatas);
@override @override
@protected @protected
bool removePhantom(String columnId) { bool removePhantom(String groupId) {
final columnController = getColumnController(columnId); final groupController = getGroupController(groupId);
if (columnController == null) { if (groupController == null) {
Log.warn('Can not find the column controller with columnId: $columnId'); Log.warn('Can not find the group controller with groupId: $groupId');
return false; return false;
} }
final index = columnController.items.indexWhere((item) => item.isPhantom); final index = groupController.items.indexWhere((item) => item.isPhantom);
final isExist = index != -1; final isExist = index != -1;
if (isExist) { if (isExist) {
columnController.removeAt(index); groupController.removeAt(index);
Log.debug( Log.debug(
'[$AFBoardDataController] Column:[$columnId] remove phantom, current count: ${columnController.items.length}'); '[$AppFlowyBoardDataController] Group:[$groupId] remove phantom, current count: ${groupController.items.length}');
} }
return isExist; return isExist;
} }
@override @override
@protected @protected
void updatePhantom(String columnId, int newIndex) { void updatePhantom(String groupId, int newIndex) {
final columnDataController = getColumnController(columnId)!; final groupController = getGroupController(groupId)!;
final index = final index = groupController.items.indexWhere((item) => item.isPhantom);
columnDataController.items.indexWhere((item) => item.isPhantom);
if (index != -1) { if (index != -1) {
if (index != newIndex) { if (index != newIndex) {
Log.trace( Log.trace(
'[$BoardPhantomController] update $columnId:$index to $columnId:$newIndex'); '[$BoardPhantomController] update $groupId:$index to $groupId:$newIndex');
final item = columnDataController.removeAt(index, notify: false); final item = groupController.removeAt(index, notify: false);
columnDataController.insert(newIndex, item, notify: false); groupController.insert(newIndex, item, notify: false);
} }
} }
} }
@override @override
@protected @protected
void insertPhantom(String columnId, int index, PhantomColumnItem item) { void insertPhantom(String groupId, int index, PhantomGroupItem item) {
getColumnController(columnId)!.insert(index, item); getGroupController(groupId)!.insert(index, item);
} }
} }

View File

@ -55,7 +55,7 @@ class OverlappingDragTargetInterceptor extends DragTargetInterceptor {
final String reorderFlexId; final String reorderFlexId;
final List<String> acceptedReorderFlexId; final List<String> acceptedReorderFlexId;
final OverlapDragTargetDelegate delegate; final OverlapDragTargetDelegate delegate;
final BoardColumnsState columnsState; final BoardGroupsState columnsState;
Timer? _delayOperation; Timer? _delayOperation;
OverlappingDragTargetInterceptor({ OverlappingDragTargetInterceptor({
@ -81,7 +81,7 @@ class OverlappingDragTargetInterceptor extends DragTargetInterceptor {
delegate.cancel(); delegate.cancel();
} else { } else {
// Ignore the event if the dragTarget overlaps with the other column's dragTargets. // Ignore the event if the dragTarget overlaps with the other column's dragTargets.
final columnKeys = columnsState.columnDragDragTargets[dragTargetId]; final columnKeys = columnsState.groupDragDragTargets[dragTargetId];
if (columnKeys != null) { if (columnKeys != null) {
final keys = columnKeys.values.toList(); final keys = columnKeys.values.toList();
if (dragTargetData.isOverlapWithWidgets(keys)) { if (dragTargetData.isOverlapWithWidgets(keys)) {
@ -102,7 +102,7 @@ class OverlappingDragTargetInterceptor extends DragTargetInterceptor {
delegate.moveTo(dragTargetId, dragTargetData, index); delegate.moveTo(dragTargetId, dragTargetData, index);
columnsState columnsState
.getReorderFlexState(columnId: dragTargetId) .getReorderFlexState(groupId: dragTargetId)
?.resetDragTargetIndex(index); ?.resetDragTargetIndex(index);
} }
}); });
@ -165,13 +165,13 @@ class CrossReorderFlexDragTargetInterceptor extends DragTargetInterceptor {
@override @override
void onAccept(FlexDragTargetData dragTargetData) { void onAccept(FlexDragTargetData dragTargetData) {
Log.trace( Log.trace(
'[$CrossReorderFlexDragTargetInterceptor] Column:[$reorderFlexId] on onAccept'); '[$CrossReorderFlexDragTargetInterceptor] Group:[$reorderFlexId] on onAccept');
} }
@override @override
void onLeave(FlexDragTargetData dragTargetData) { void onLeave(FlexDragTargetData dragTargetData) {
Log.trace( Log.trace(
'[$CrossReorderFlexDragTargetInterceptor] Column:[$reorderFlexId] on leave'); '[$CrossReorderFlexDragTargetInterceptor] Group:[$reorderFlexId] on leave');
} }
@override @override

View File

@ -49,8 +49,8 @@ class ReorderFlexConfig {
/// Determines if setSatte method needs to be called when the drag is complete. /// Determines if setSatte method needs to be called when the drag is complete.
/// Default value is [true]. /// Default value is [true].
/// ///
/// If the [ReorderFlex] will be rebuild after the [ReorderFlex]'s children /// If the [ReorderFlex] needs to be rebuild after the [ReorderFlex] end dragging,
/// were changed, then the [setStateWhenEndDrag] should set to [false]. /// the [setStateWhenEndDrag] should set to [true].
final bool setStateWhenEndDrag; final bool setStateWhenEndDrag;
final bool useMoveAnimation; final bool useMoveAnimation;
@ -252,7 +252,7 @@ class ReorderFlexState extends State<ReorderFlex>
} }
Log.trace( Log.trace(
'Rebuild: Column:[${dragState.reorderFlexId}] ${dragState.toString()}, childIndex: $childIndex shiftedIndex: $shiftedIndex'); 'Rebuild: Group:[${dragState.reorderFlexId}] ${dragState.toString()}, childIndex: $childIndex shiftedIndex: $shiftedIndex');
final currentIndex = dragState.currentIndex; final currentIndex = dragState.currentIndex;
final dragPhantomIndex = dragState.phantomIndex; final dragPhantomIndex = dragState.phantomIndex;
@ -368,7 +368,7 @@ class ReorderFlexState extends State<ReorderFlex>
), ),
onDragStarted: (draggingWidget, draggingIndex, size) { onDragStarted: (draggingWidget, draggingIndex, size) {
Log.debug( Log.debug(
"[DragTarget] Column:[${widget.dataSource.identifier}] start dragging item at $draggingIndex"); "[DragTarget] Group:[${widget.dataSource.identifier}] start dragging item at $draggingIndex");
_startDragging(draggingWidget, draggingIndex, size); _startDragging(draggingWidget, draggingIndex, size);
widget.onDragStarted?.call(draggingIndex); widget.onDragStarted?.call(draggingIndex);
widget.dragStateStorage?.remove(widget.reorderFlexId); widget.dragStateStorage?.remove(widget.reorderFlexId);
@ -379,7 +379,7 @@ class ReorderFlexState extends State<ReorderFlex>
onDragEnded: (dragTargetData) { onDragEnded: (dragTargetData) {
if (!mounted) return; if (!mounted) return;
Log.debug( Log.debug(
"[DragTarget]: Column:[${widget.dataSource.identifier}] end dragging"); "[DragTarget]: Group:[${widget.dataSource.identifier}] end dragging");
_notifier.updateDragTargetIndex(-1); _notifier.updateDragTargetIndex(-1);
onDragEnded() { onDragEnded() {

View File

@ -8,30 +8,30 @@ import '../reorder_flex/drag_target_interceptor.dart';
import 'phantom_state.dart'; import 'phantom_state.dart';
abstract class BoardPhantomControllerDelegate { abstract class BoardPhantomControllerDelegate {
AFBoardColumnDataController? controller(String columnId); AFBoardGroupDataController? controller(String groupId);
bool removePhantom(String columnId); bool removePhantom(String groupId);
/// Insert the phantom into column /// Insert the phantom into the group
/// ///
/// * [toColumnId] id of the column /// * [groupId] id of the group
/// * [phantomIndex] the phantom occupies index /// * [phantomIndex] the phantom occupies index
void insertPhantom( void insertPhantom(
String columnId, String groupId,
int index, int index,
PhantomColumnItem item, PhantomGroupItem item,
); );
/// Update the column's phantom index if it exists. /// Update the group's phantom index if it exists.
/// [toColumnId] the id of the column /// [toGroupId] the id of the group
/// [dragTargetIndex] the index of the dragTarget /// [dragTargetIndex] the index of the dragTarget
void updatePhantom(String columnId, int newIndex); void updatePhantom(String groupId, int newIndex);
void swapColumnItem( void swapGroupItem(
String fromColumnId, String fromGroupId,
int fromColumnIndex, int fromGroupIndex,
String toColumnId, String toGroupId,
int toColumnIndex, int toGroupIndex,
); );
} }
@ -39,16 +39,16 @@ class BoardPhantomController extends OverlapDragTargetDelegate
with CrossReorderFlexDragTargetDelegate { with CrossReorderFlexDragTargetDelegate {
PhantomRecord? phantomRecord; PhantomRecord? phantomRecord;
final BoardPhantomControllerDelegate delegate; final BoardPhantomControllerDelegate delegate;
final BoardColumnsState columnsState; final BoardGroupsState groupsState;
final phantomState = ColumnPhantomState(); final phantomState = GroupPhantomState();
BoardPhantomController({ BoardPhantomController({
required this.delegate, required this.delegate,
required this.columnsState, required this.groupsState,
}); });
bool isFromColumn(String columnId) { bool isFromGroup(String groupId) {
if (phantomRecord != null) { if (phantomRecord != null) {
return phantomRecord!.fromColumnId == columnId; return phantomRecord!.fromGroupId == groupId;
} else { } else {
return true; return true;
} }
@ -58,32 +58,32 @@ class BoardPhantomController extends OverlapDragTargetDelegate
if (phantomRecord == null) { if (phantomRecord == null) {
return; return;
} }
assert(phantomRecord!.fromColumnIndex == fromIndex); assert(phantomRecord!.fromGroupIndex == fromIndex);
phantomRecord?.updateFromColumnIndex(toIndex); phantomRecord?.updateFromGroupIndex(toIndex);
} }
void columnStartDragging(String columnId) { void groupStartDragging(String groupId) {
phantomState.setColumnIsDragging(columnId, true); phantomState.setGroupIsDragging(groupId, true);
} }
/// Remove the phantom in the column when the column is end dragging. /// Remove the phantom in the group when the group is end dragging.
void columnEndDragging(String columnId) { void groupEndDragging(String groupId) {
phantomState.setColumnIsDragging(columnId, false); phantomState.setGroupIsDragging(groupId, false);
if (phantomRecord == null) return; if (phantomRecord == null) return;
final fromColumnId = phantomRecord!.fromColumnId; final fromGroupId = phantomRecord!.fromGroupId;
final toColumnId = phantomRecord!.toColumnId; final toGroupId = phantomRecord!.toGroupId;
if (fromColumnId == columnId) { if (fromGroupId == groupId) {
phantomState.notifyDidRemovePhantom(toColumnId); phantomState.notifyDidRemovePhantom(toGroupId);
} }
if (phantomRecord!.toColumnId == columnId) { if (phantomRecord!.toGroupId == groupId) {
delegate.swapColumnItem( delegate.swapGroupItem(
fromColumnId, fromGroupId,
phantomRecord!.fromColumnIndex, phantomRecord!.fromGroupIndex,
toColumnId, toGroupId,
phantomRecord!.toColumnIndex, phantomRecord!.toGroupIndex,
); );
// Log.debug( // Log.debug(
@ -92,16 +92,16 @@ class BoardPhantomController extends OverlapDragTargetDelegate
} }
} }
/// Remove the phantom in the column if it contains phantom /// Remove the phantom in the group if it contains phantom
void _removePhantom(String columnId) { void _removePhantom(String groupId) {
if (delegate.removePhantom(columnId)) { if (delegate.removePhantom(groupId)) {
phantomState.notifyDidRemovePhantom(columnId); phantomState.notifyDidRemovePhantom(groupId);
phantomState.removeColumnListener(columnId); phantomState.removeGroupListener(groupId);
} }
} }
void _insertPhantom( void _insertPhantom(
String toColumnId, String toGroupId,
FlexDragTargetData dragTargetData, FlexDragTargetData dragTargetData,
int phantomIndex, int phantomIndex,
) { ) {
@ -109,38 +109,38 @@ class BoardPhantomController extends OverlapDragTargetDelegate
index: phantomIndex, index: phantomIndex,
dragTargetData: dragTargetData, dragTargetData: dragTargetData,
); );
phantomState.addColumnListener(toColumnId, phantomContext); phantomState.addGroupListener(toGroupId, phantomContext);
delegate.insertPhantom( delegate.insertPhantom(
toColumnId, toGroupId,
phantomIndex, phantomIndex,
PhantomColumnItem(phantomContext), PhantomGroupItem(phantomContext),
); );
phantomState.notifyDidInsertPhantom(toColumnId, phantomIndex); phantomState.notifyDidInsertPhantom(toGroupId, phantomIndex);
} }
/// Reset or initial the [PhantomRecord] /// Reset or initial the [PhantomRecord]
/// ///
/// ///
/// * [columnId] the id of the column /// * [groupId] the id of the group
/// * [dragTargetData] /// * [dragTargetData]
/// * [dragTargetIndex] /// * [dragTargetIndex]
/// ///
void _resetPhantomRecord( void _resetPhantomRecord(
String columnId, String groupId,
FlexDragTargetData dragTargetData, FlexDragTargetData dragTargetData,
int dragTargetIndex, int dragTargetIndex,
) { ) {
// Log.debug( // Log.debug(
// '[$BoardPhantomController] move Column:[${dragTargetData.reorderFlexId}]:${dragTargetData.draggingIndex} ' // '[$BoardPhantomController] move Group:[${dragTargetData.reorderFlexId}]:${dragTargetData.draggingIndex} '
// 'to Column:[$columnId]:$dragTargetIndex'); // 'to Group:[$groupId]:$dragTargetIndex');
phantomRecord = PhantomRecord( phantomRecord = PhantomRecord(
toColumnId: columnId, toGroupId: groupId,
toColumnIndex: dragTargetIndex, toGroupIndex: dragTargetIndex,
fromColumnId: dragTargetData.reorderFlexId, fromGroupId: dragTargetData.reorderFlexId,
fromColumnIndex: dragTargetData.draggingIndex, fromGroupIndex: dragTargetData.draggingIndex,
); );
Log.debug('[$BoardPhantomController] will move: $phantomRecord'); Log.debug('[$BoardPhantomController] will move: $phantomRecord');
} }
@ -158,10 +158,10 @@ class BoardPhantomController extends OverlapDragTargetDelegate
return true; return true;
} }
final isNewDragTarget = phantomRecord!.toColumnId != reorderFlexId; final isNewDragTarget = phantomRecord!.toGroupId != reorderFlexId;
if (isNewDragTarget) { if (isNewDragTarget) {
/// Remove the phantom when the dragTarget is moved from one column to another column. /// Remove the phantom when the dragTarget is moved from one group to another group.
_removePhantom(phantomRecord!.toColumnId); _removePhantom(phantomRecord!.toGroupId);
_resetPhantomRecord(reorderFlexId, dragTargetData, dragTargetIndex); _resetPhantomRecord(reorderFlexId, dragTargetData, dragTargetIndex);
_insertPhantom(reorderFlexId, dragTargetData, dragTargetIndex); _insertPhantom(reorderFlexId, dragTargetData, dragTargetIndex);
} }
@ -177,9 +177,9 @@ class BoardPhantomController extends OverlapDragTargetDelegate
phantomRecord?.updateInsertedIndex(dragTargetIndex); phantomRecord?.updateInsertedIndex(dragTargetIndex);
assert(phantomRecord != null); assert(phantomRecord != null);
if (phantomRecord!.toColumnId == reorderFlexId) { if (phantomRecord!.toGroupId == reorderFlexId) {
/// Update the existing phantom index /// Update the existing phantom index
delegate.updatePhantom(phantomRecord!.toColumnId, dragTargetIndex); delegate.updatePhantom(phantomRecord!.toGroupId, dragTargetIndex);
} }
} }
@ -189,8 +189,8 @@ class BoardPhantomController extends OverlapDragTargetDelegate
return; return;
} }
/// Remove the phantom when the dragTarge is go back to the original column. /// Remove the phantom when the dragTarge is go back to the original group.
_removePhantom(phantomRecord!.toColumnId); _removePhantom(phantomRecord!.toGroupId);
phantomRecord = null; phantomRecord = null;
} }
@ -215,63 +215,63 @@ class BoardPhantomController extends OverlapDragTargetDelegate
final controller = delegate.controller(dragTargetId); final controller = delegate.controller(dragTargetId);
if (controller != null) { if (controller != null) {
return controller.columnData.items.length; return controller.groupData.items.length;
} else { } else {
return 0; return 0;
} }
} }
} }
/// Use [PhantomRecord] to record where to remove the column item and where to /// Use [PhantomRecord] to record where to remove the group item and where to
/// insert the column item. /// insert the group item.
/// ///
/// [fromColumnId] the column that phantom comes from /// [fromGroupId] the group that phantom comes from
/// [fromColumnIndex] the index of the phantom from the original column /// [fromGroupIndex] the index of the phantom from the original group
/// [toColumnId] the column that the phantom moves into /// [toGroupId] the group that the phantom moves into
/// [toColumnIndex] the index of the phantom moves into the column /// [toGroupIndex] the index of the phantom moves into the group
/// ///
class PhantomRecord { class PhantomRecord {
final String fromColumnId; final String fromGroupId;
int fromColumnIndex; int fromGroupIndex;
final String toColumnId; final String toGroupId;
int toColumnIndex; int toGroupIndex;
PhantomRecord({ PhantomRecord({
required this.toColumnId, required this.toGroupId,
required this.toColumnIndex, required this.toGroupIndex,
required this.fromColumnId, required this.fromGroupId,
required this.fromColumnIndex, required this.fromGroupIndex,
}); });
void updateFromColumnIndex(int index) { void updateFromGroupIndex(int index) {
if (fromColumnIndex == index) { if (fromGroupIndex == index) {
return; return;
} }
fromColumnIndex = index; fromGroupIndex = index;
} }
void updateInsertedIndex(int index) { void updateInsertedIndex(int index) {
if (toColumnIndex == index) { if (toGroupIndex == index) {
return; return;
} }
Log.debug( Log.debug(
'[$PhantomRecord] Column:[$toColumnId] update position $toColumnIndex -> $index'); '[$PhantomRecord] Group:[$toGroupId] update position $toGroupIndex -> $index');
toColumnIndex = index; toGroupIndex = index;
} }
@override @override
String toString() { String toString() {
return 'Column:[$fromColumnId]:$fromColumnIndex to Column:[$toColumnId]:$toColumnIndex'; return 'Group:[$fromGroupId]:$fromGroupIndex to Group:[$toGroupId]:$toGroupIndex';
} }
} }
class PhantomColumnItem extends AFColumnItem { class PhantomGroupItem extends AppFlowyGroupItem {
final PassthroughPhantomContext phantomContext; final PassthroughPhantomContext phantomContext;
PhantomColumnItem(PassthroughPhantomContext insertedPhantom) PhantomGroupItem(PassthroughPhantomContext insertedPhantom)
: phantomContext = insertedPhantom; : phantomContext = insertedPhantom;
@override @override
@ -305,7 +305,8 @@ class PassthroughPhantomContext extends FakeDragTargetEventTrigger
Widget? get draggingWidget => dragTargetData.draggingWidget; Widget? get draggingWidget => dragTargetData.draggingWidget;
AFColumnItem get itemData => dragTargetData.reorderFlexItem as AFColumnItem; AppFlowyGroupItem get itemData =>
dragTargetData.reorderFlexItem as AppFlowyGroupItem;
@override @override
void Function(int?)? onInserted; void Function(int?)? onInserted;

View File

@ -1,48 +1,48 @@
import 'phantom_controller.dart'; import 'phantom_controller.dart';
import 'package:flutter/material.dart'; import 'package:flutter/material.dart';
class ColumnPhantomState { class GroupPhantomState {
final _states = <String, ColumnState>{}; final _states = <String, GroupState>{};
void setColumnIsDragging(String columnId, bool isDragging) { void setGroupIsDragging(String groupId, bool isDragging) {
_stateWithId(columnId).isDragging = isDragging; _stateWithId(groupId).isDragging = isDragging;
} }
bool isDragging(String columnId) { bool isDragging(String groupId) {
return _stateWithId(columnId).isDragging; return _stateWithId(groupId).isDragging;
} }
void addColumnListener(String columnId, PassthroughPhantomListener listener) { void addGroupListener(String groupId, PassthroughPhantomListener listener) {
_stateWithId(columnId).notifier.addListener( _stateWithId(groupId).notifier.addListener(
onInserted: (index) => listener.onInserted?.call(index), onInserted: (index) => listener.onInserted?.call(index),
onDeleted: () => listener.onDragEnded?.call(), onDeleted: () => listener.onDragEnded?.call(),
); );
} }
void removeColumnListener(String columnId) { void removeGroupListener(String groupId) {
_stateWithId(columnId).notifier.dispose(); _stateWithId(groupId).notifier.dispose();
_states.remove(columnId); _states.remove(groupId);
} }
void notifyDidInsertPhantom(String columnId, int index) { void notifyDidInsertPhantom(String groupId, int index) {
_stateWithId(columnId).notifier.insert(index); _stateWithId(groupId).notifier.insert(index);
} }
void notifyDidRemovePhantom(String columnId) { void notifyDidRemovePhantom(String groupId) {
_stateWithId(columnId).notifier.remove(); _stateWithId(groupId).notifier.remove();
} }
ColumnState _stateWithId(String columnId) { GroupState _stateWithId(String groupId) {
var state = _states[columnId]; var state = _states[groupId];
if (state == null) { if (state == null) {
state = ColumnState(); state = GroupState();
_states[columnId] = state; _states[groupId] = state;
} }
return state; return state;
} }
} }
class ColumnState { class GroupState {
bool isDragging = false; bool isDragging = false;
final notifier = PassthroughPhantomNotifier(); final notifier = PassthroughPhantomNotifier();
} }

View File

@ -1,12 +1,12 @@
import 'package:flutter/material.dart'; import 'package:flutter/material.dart';
class AppFlowyColumnItemCard extends StatefulWidget { class AppFlowyGroupItemCard extends StatefulWidget {
final Widget? child; final Widget? child;
final EdgeInsets margin; final EdgeInsets margin;
final BoxConstraints boxConstraints; final BoxConstraints boxConstraints;
final BoxDecoration decoration; final BoxDecoration decoration;
const AppFlowyColumnItemCard({ const AppFlowyGroupItemCard({
this.child, this.child,
this.margin = const EdgeInsets.all(4), this.margin = const EdgeInsets.all(4),
this.decoration = const BoxDecoration( this.decoration = const BoxDecoration(
@ -18,10 +18,10 @@ class AppFlowyColumnItemCard extends StatefulWidget {
}) : super(key: key); }) : super(key: key);
@override @override
State<AppFlowyColumnItemCard> createState() => _AppFlowyColumnItemCardState(); State<AppFlowyGroupItemCard> createState() => _AppFlowyGroupItemCardState();
} }
class _AppFlowyColumnItemCardState extends State<AppFlowyColumnItemCard> { class _AppFlowyGroupItemCardState extends State<AppFlowyGroupItemCard> {
@override @override
Widget build(BuildContext context) { Widget build(BuildContext context) {
return Padding( return Padding(

View File

@ -2,14 +2,14 @@ import 'package:flutter/material.dart';
typedef OnFooterAddButtonClick = void Function(); typedef OnFooterAddButtonClick = void Function();
class AppFlowyColumnFooter extends StatefulWidget { class AppFlowyGroupFooter extends StatefulWidget {
final double height; final double height;
final Widget? icon; final Widget? icon;
final Widget? title; final Widget? title;
final EdgeInsets margin; final EdgeInsets margin;
final OnFooterAddButtonClick? onAddButtonClick; final OnFooterAddButtonClick? onAddButtonClick;
const AppFlowyColumnFooter({ const AppFlowyGroupFooter({
this.icon, this.icon,
this.title, this.title,
this.margin = const EdgeInsets.symmetric(horizontal: 12), this.margin = const EdgeInsets.symmetric(horizontal: 12),
@ -19,10 +19,10 @@ class AppFlowyColumnFooter extends StatefulWidget {
}) : super(key: key); }) : super(key: key);
@override @override
State<AppFlowyColumnFooter> createState() => _AppFlowyColumnFooterState(); State<AppFlowyGroupFooter> createState() => _AppFlowyGroupFooterState();
} }
class _AppFlowyColumnFooterState extends State<AppFlowyColumnFooter> { class _AppFlowyGroupFooterState extends State<AppFlowyGroupFooter> {
@override @override
Widget build(BuildContext context) { Widget build(BuildContext context) {
return GestureDetector( return GestureDetector(

View File

@ -3,7 +3,7 @@ import 'package:flutter/material.dart';
typedef OnHeaderAddButtonClick = void Function(); typedef OnHeaderAddButtonClick = void Function();
typedef OnHeaderMoreButtonClick = void Function(); typedef OnHeaderMoreButtonClick = void Function();
class AppFlowyColumnHeader extends StatefulWidget { class AppFlowyGroupHeader extends StatefulWidget {
final double height; final double height;
final Widget? icon; final Widget? icon;
final Widget? title; final Widget? title;
@ -13,7 +13,7 @@ class AppFlowyColumnHeader extends StatefulWidget {
final OnHeaderAddButtonClick? onAddButtonClick; final OnHeaderAddButtonClick? onAddButtonClick;
final OnHeaderMoreButtonClick? onMoreButtonClick; final OnHeaderMoreButtonClick? onMoreButtonClick;
const AppFlowyColumnHeader({ const AppFlowyGroupHeader({
required this.height, required this.height,
this.icon, this.icon,
this.title, this.title,
@ -26,10 +26,10 @@ class AppFlowyColumnHeader extends StatefulWidget {
}) : super(key: key); }) : super(key: key);
@override @override
State<AppFlowyColumnHeader> createState() => _AppFlowyColumnHeaderState(); State<AppFlowyGroupHeader> createState() => _AppFlowyGroupHeaderState();
} }
class _AppFlowyColumnHeaderState extends State<AppFlowyColumnHeader> { class _AppFlowyGroupHeaderState extends State<AppFlowyGroupHeader> {
@override @override
Widget build(BuildContext context) { Widget build(BuildContext context) {
List<Widget> children = []; List<Widget> children = [];