chore: update board layout, support footer and header

This commit is contained in:
appflowy 2022-08-04 14:18:34 +08:00
parent a4b4b20cfc
commit dbc5de2968
11 changed files with 296 additions and 246 deletions

View File

@ -46,9 +46,22 @@ class _MultiBoardListExampleState extends State<MultiBoardListExample> {
return Board( return Board(
dataController: boardData, dataController: boardData,
background: Container(color: Colors.red), background: Container(color: Colors.red),
builder: (context, item) { footBuilder: (context, columnData) {
return Container(
color: Colors.purple,
height: 30,
);
},
headerBuilder: (context, columnData) {
return Container(
color: Colors.yellow,
height: 30,
);
},
cardBuilder: (context, item) {
return _RowWidget(item: item as TextItem, key: ObjectKey(item)); return _RowWidget(item: item as TextItem, key: ObjectKey(item));
}, },
columnConstraints: const BoxConstraints.tightFor(width: 240),
); );
} }
} }

View File

@ -28,7 +28,7 @@ class _SingleBoardListExampleState extends State<SingleBoardListExample> {
Widget build(BuildContext context) { Widget build(BuildContext context) {
return Board( return Board(
dataController: boardData, dataController: boardData,
builder: (context, item) { cardBuilder: (context, item) {
return _RowWidget(item: item as TextItem, key: ObjectKey(item)); return _RowWidget(item: item as TextItem, key: ObjectKey(item));
}, },
); );

View File

@ -13,8 +13,7 @@ class BoardOverlayEntry {
/// Whether this entry occludes the entire overlay. /// Whether this entry occludes the entire overlay.
/// ///
/// If an entry claims to be opaque, then, for efficiency, the overlay will /// If an entry claims to be opaque, then, for efficiency, the overlay will
/// skip building entries below that entry unless they have [maintainState] /// skip building entries below that entry.
/// set.
bool get opaque => _opaque; bool get opaque => _opaque;
bool _opaque; bool _opaque;

View File

@ -20,25 +20,37 @@ class Board extends StatelessWidget {
/// Defaults to 0.0. /// Defaults to 0.0.
final double runSpacing; final double runSpacing;
///
final Widget? background; final Widget? background;
final BoardColumnItemWidgetBuilder builder; ///
final BoardColumnCardBuilder cardBuilder;
///
final BoardColumnHeaderBuilder? headerBuilder;
///
final BoardColumnFooterBuilder? footBuilder;
/// ///
final BoardDataController dataController; final BoardDataController dataController;
final BoxConstraints columnConstraints;
/// ///
final BoardPhantomController passthroughPhantomContorller; final BoardPhantomController phantomController;
Board({ Board({
required this.dataController, required this.dataController,
required this.builder, required this.cardBuilder,
this.spacing = 10.0, this.spacing = 10.0,
this.runSpacing = 0.0, this.runSpacing = 0.0,
this.background, this.background,
this.footBuilder,
this.headerBuilder,
this.columnConstraints = const BoxConstraints(maxWidth: 200),
Key? key, Key? key,
}) : passthroughPhantomContorller = }) : phantomController = BoardPhantomController(delegate: dataController),
BoardPhantomController(delegate: dataController),
super(key: key); super(key: key);
@override @override
@ -51,41 +63,25 @@ class Board extends StatelessWidget {
List<String> acceptColumns = List<String> acceptColumns =
dataController.columnControllers.keys.toList(); dataController.columnControllers.keys.toList();
dataController.columnControllers.forEach((columnId, dataController) { dataController.columnControllers.forEach((columnId, controller) {
Widget child = Widget child = _buildColumn(columnId, acceptColumns, controller);
buildBoardColumn(columnId, acceptColumns, dataController);
if (children.isEmpty) {
// children.add(SizedBox(width: spacing));
}
// if (background != null) {
// child = Stack(children: [
// background!,
// child,
// ]);
// }
// children.add(Expanded(key: ValueKey(columnId), child: child));
children.add(child); children.add(child);
// children.add(SizedBox(width: spacing));
}); });
return BoardColumnContainer( return BoardColumnContainer(
onReorder: (fromIndex, toIndex) {}, onReorder: (fromIndex, toIndex) {},
boardDataController: dataController, boardDataController: dataController,
background: background,
spacing: spacing,
children: children, children: children,
); );
// return Row(
// crossAxisAlignment: CrossAxisAlignment.start,
// mainAxisAlignment: MainAxisAlignment.spaceEvenly,
// children: children,
// );
}, },
), ),
); );
} }
/// ///
Widget buildBoardColumn( Widget _buildColumn(
String columnId, String columnId,
List<String> acceptColumns, List<String> acceptColumns,
BoardColumnDataController dataController, BoardColumnDataController dataController,
@ -95,18 +91,19 @@ class Board extends StatelessWidget {
value: dataController, value: dataController,
child: Consumer<BoardColumnDataController>( child: Consumer<BoardColumnDataController>(
builder: (context, value, child) { builder: (context, value, child) {
return SizedBox( return ConstrainedBox(
width: 200, constraints: columnConstraints,
child: BoardColumnWidget( child: BoardColumnWidget(
header: Container(color: Colors.yellow, height: 30), headerBuilder: headerBuilder,
builder: builder, footBuilder: footBuilder,
cardBuilder: cardBuilder,
acceptColumns: acceptColumns, acceptColumns: acceptColumns,
dataController: dataController, dataController: dataController,
scrollController: ScrollController(), scrollController: ScrollController(),
onReorder: (_, int fromIndex, int toIndex) { onReorder: (_, int fromIndex, int toIndex) {
dataController.move(fromIndex, toIndex); dataController.move(fromIndex, toIndex);
}, },
phantomController: passthroughPhantomContorller, phantomController: phantomController,
), ),
); );
}, },

View File

@ -4,34 +4,33 @@ import '../../rendering/board_overlay.dart';
import '../../utils/log.dart'; import '../../utils/log.dart';
import '../phantom/phantom_controller.dart'; import '../phantom/phantom_controller.dart';
import '../flex/reorder_flex.dart'; import '../flex/reorder_flex.dart';
import '../flex/drag_state.dart'; import '../flex/drag_target_inteceptor.dart';
import '../flex/reorder_flex_ext.dart';
import 'data_controller.dart'; import 'data_controller.dart';
typedef OnDragStarted = void Function(int index); typedef OnColumnDragStarted = void Function(int index);
typedef OnDragEnded = void Function(String listId); typedef OnColumnDragEnded = void Function(String listId);
typedef OnReorder = void Function(String listId, int fromIndex, int toIndex); typedef OnColumnReorder = void Function(
typedef OnDeleted = void Function(String listId, int deletedIndex); String listId, int fromIndex, int toIndex);
typedef OnInserted = void Function(String listId, int insertedIndex); typedef OnColumnDeleted = void Function(String listId, int deletedIndex);
typedef OnPassedInPhantom = void Function( typedef OnColumnInserted = void Function(String listId, int insertedIndex);
String listId,
FlexDragTargetData dragTargetData,
int phantomIndex,
);
typedef BoardColumnItemWidgetBuilder = Widget Function( typedef BoardColumnCardBuilder = Widget Function(
BuildContext context, ColumnItem item); BuildContext context, ColumnItem item);
typedef BoardColumnHeaderBuilder = Widget Function(
BuildContext context, BoardColumnData columnData);
typedef BoardColumnFooterBuilder = Widget Function(
BuildContext context, BoardColumnData columnData);
class BoardColumnWidget extends StatefulWidget { class BoardColumnWidget extends StatefulWidget {
final Widget? header;
final Widget? footer;
final BoardColumnDataController dataController; final BoardColumnDataController dataController;
final ScrollController? scrollController; final ScrollController? scrollController;
final ReorderFlexConfig config; final ReorderFlexConfig config;
final OnDragStarted? onDragStarted; final OnColumnDragStarted? onDragStarted;
final OnReorder onReorder; final OnColumnReorder onReorder;
final OnDragEnded? onDragEnded; final OnColumnDragEnded? onDragEnded;
final BoardPhantomController phantomController; final BoardPhantomController phantomController;
@ -39,18 +38,25 @@ class BoardColumnWidget extends StatefulWidget {
final List<String> acceptColumns; final List<String> acceptColumns;
final BoardColumnItemWidgetBuilder builder; final BoardColumnCardBuilder cardBuilder;
final BoardColumnHeaderBuilder? headerBuilder;
final BoardColumnFooterBuilder? footBuilder;
final double? spacing;
const BoardColumnWidget({ const BoardColumnWidget({
Key? key, Key? key,
this.header, this.headerBuilder,
this.footer, this.footBuilder,
required this.builder, required this.cardBuilder,
required this.onReorder, required this.onReorder,
required this.dataController, required this.dataController,
required this.phantomController, required this.phantomController,
required this.acceptColumns, required this.acceptColumns,
this.config = const ReorderFlexConfig(), this.config = const ReorderFlexConfig(),
this.spacing,
this.onDragStarted, this.onDragStarted,
this.scrollController, this.scrollController,
this.onDragEnded, this.onDragEnded,
@ -69,45 +75,59 @@ class _BoardColumnWidgetState extends State<BoardColumnWidget> {
@override @override
void initState() { void initState() {
_overlayEntry = BoardOverlayEntry( _overlayEntry = BoardOverlayEntry(
builder: (BuildContext context) { builder: (BuildContext context) {
final children = widget.dataController.items final children = widget.dataController.items
.map((item) => _buildWidget(context, item)) .map((item) => _buildWidget(context, item))
.toList(); .toList();
final dragTargetExtension = ReorderFlextDragTargetExtension( final header = widget.headerBuilder
reorderFlexId: widget.columnId, ?.call(context, widget.dataController.columnData);
delegate: widget.phantomController,
acceptReorderFlexIds: widget.acceptColumns,
draggableTargetBuilder: PhantomReorderDraggableBuilder(),
);
return ReorderFlex( final footer =
key: widget.key, widget.footBuilder?.call(context, widget.dataController.columnData);
header: widget.header,
footer: widget.footer, final interceptor = CrossReorderFlexDragTargetInterceptor(
scrollController: widget.scrollController, reorderFlexId: widget.columnId,
config: widget.config, delegate: widget.phantomController,
onDragStarted: (index) { acceptReorderFlexIds: widget.acceptColumns,
widget.phantomController.columnStartDragging(widget.columnId); draggableTargetBuilder: PhantomDraggableBuilder(),
widget.onDragStarted?.call(index); );
},
onReorder: ((fromIndex, toIndex) { final reorderFlex = ReorderFlex(
if (widget.phantomController.isFromColumn(widget.columnId)) { key: widget.key,
widget.onReorder(widget.columnId, fromIndex, toIndex); scrollController: widget.scrollController,
widget.phantomController.transformIndex(fromIndex, toIndex); config: widget.config,
} onDragStarted: (index) {
}), widget.phantomController.columnStartDragging(widget.columnId);
onDragEnded: () { widget.onDragStarted?.call(index);
widget.phantomController.columnEndDragging(widget.columnId); },
widget.onDragEnded?.call(widget.columnId); onReorder: ((fromIndex, toIndex) {
_printItems(widget.dataController); if (widget.phantomController.isFromColumn(widget.columnId)) {
}, widget.onReorder(widget.columnId, fromIndex, toIndex);
dataSource: widget.dataController, widget.phantomController.transformIndex(fromIndex, toIndex);
dragTargetExtension: dragTargetExtension, }
children: children, }),
); onDragEnded: () {
}, widget.phantomController.columnEndDragging(widget.columnId);
opaque: false); widget.onDragEnded?.call(widget.columnId);
_printItems(widget.dataController);
},
dataSource: widget.dataController,
interceptor: interceptor,
spacing: widget.spacing,
children: children,
);
return Column(
children: [
if (header != null) header,
Expanded(child: reorderFlex),
if (footer != null) footer,
],
);
},
opaque: false,
);
super.initState(); super.initState();
} }
@ -127,7 +147,7 @@ class _BoardColumnWidgetState extends State<BoardColumnWidget> {
passthroughPhantomContext: item.phantomContext, passthroughPhantomContext: item.phantomContext,
); );
} else { } else {
return widget.builder(context, item); return widget.cardBuilder(context, item);
} }
} }
} }

View File

@ -1,12 +1,10 @@
import 'package:flutter/material.dart'; import 'package:flutter/material.dart';
import '../../flowy_board.dart';
import '../rendering/board_overlay.dart'; import '../rendering/board_overlay.dart';
import 'flex/reorder_flex.dart'; import 'flex/reorder_flex.dart';
import 'board.dart';
class BoardColumnContainer extends StatefulWidget { class BoardColumnContainer extends StatefulWidget {
final Widget? header;
final Widget? footer;
final ScrollController? scrollController; final ScrollController? scrollController;
final OnDragStarted? onDragStarted; final OnDragStarted? onDragStarted;
final OnReorder onReorder; final OnReorder onReorder;
@ -15,6 +13,7 @@ class BoardColumnContainer extends StatefulWidget {
final List<Widget> children; final List<Widget> children;
final EdgeInsets? padding; final EdgeInsets? padding;
final Widget? background; final Widget? background;
final double spacing;
final ReorderFlexConfig config; final ReorderFlexConfig config;
const BoardColumnContainer({ const BoardColumnContainer({
@ -23,11 +22,10 @@ class BoardColumnContainer extends StatefulWidget {
required this.children, required this.children,
this.onDragStarted, this.onDragStarted,
this.onDragEnded, this.onDragEnded,
this.header,
this.footer,
this.scrollController, this.scrollController,
this.padding, this.padding,
this.background, this.background,
this.spacing = 0.0,
this.config = const ReorderFlexConfig(), this.config = const ReorderFlexConfig(),
Key? key, Key? key,
}) : super(key: key); }) : super(key: key);
@ -47,8 +45,6 @@ class _BoardColumnContainerState extends State<BoardColumnContainer> {
builder: (BuildContext context) { builder: (BuildContext context) {
Widget reorderFlex = ReorderFlex( Widget reorderFlex = ReorderFlex(
key: widget.key, key: widget.key,
header: widget.header,
footer: widget.footer,
scrollController: widget.scrollController, scrollController: widget.scrollController,
config: widget.config, config: widget.config,
onDragStarted: (index) {}, onDragStarted: (index) {},
@ -56,6 +52,7 @@ class _BoardColumnContainerState extends State<BoardColumnContainer> {
onDragEnded: () {}, onDragEnded: () {},
dataSource: widget.boardDataController, dataSource: widget.boardDataController,
direction: Axis.horizontal, direction: Axis.horizontal,
spacing: widget.spacing,
children: widget.children, children: widget.children,
); );
@ -65,17 +62,7 @@ class _BoardColumnContainerState extends State<BoardColumnContainer> {
child: reorderFlex, child: reorderFlex,
); );
} }
return _wrapStack(reorderFlex);
return Expanded(
child: Stack(
alignment: AlignmentDirectional.center,
children: [
Container(
color: Colors.red,
),
reorderFlex
],
));
}, },
opaque: false); opaque: false);
super.initState(); super.initState();
@ -88,4 +75,14 @@ class _BoardColumnContainerState extends State<BoardColumnContainer> {
initialEntries: [_overlayEntry], initialEntries: [_overlayEntry],
); );
} }
Widget _wrapStack(Widget child) {
return Stack(
alignment: AlignmentDirectional.topStart,
children: [
if (widget.background != null) widget.background!,
child,
],
);
}
} }

View File

@ -4,7 +4,7 @@ abstract class DragTargetData {
int get draggingIndex; int get draggingIndex;
} }
abstract class ReorderDraggableTargetBuilder { abstract class ReorderFlexDraggableTargetBuilder {
Widget? build<T extends DragTargetData>( Widget? build<T extends DragTargetData>(
BuildContext context, BuildContext context,
Widget child, Widget child,
@ -51,7 +51,7 @@ class ReorderDragTarget<T extends DragTargetData> extends StatefulWidget {
/// the target. /// the target.
final void Function(T dragTargetData)? onLeave; final void Function(T dragTargetData)? onLeave;
final ReorderDraggableTargetBuilder? draggableTargetBuilder; final ReorderFlexDraggableTargetBuilder? draggableTargetBuilder;
ReorderDragTarget({ ReorderDragTarget({
Key? key, Key? key,
@ -158,9 +158,10 @@ class _ReorderDragTargetState<T extends DragTargetData>
transform: Matrix4.rotationZ(0), transform: Matrix4.rotationZ(0),
alignment: FractionalOffset.topLeft, alignment: FractionalOffset.topLeft,
child: Material( child: Material(
elevation: 3.0, elevation: 6.0,
color: Colors.transparent, color: Colors.transparent,
borderRadius: BorderRadius.zero, borderRadius: BorderRadius.zero,
clipBehavior: Clip.hardEdge,
child: ConstrainedBox(constraints: constraints, child: child), child: ConstrainedBox(constraints: constraints, child: child),
), ),
); );

View File

@ -0,0 +1,114 @@
import 'package:flutter/material.dart';
import '../../utils/log.dart';
import 'drag_state.dart';
import 'drag_target.dart';
import 'reorder_flex.dart';
abstract class ReorderFlexDragTargetInterceptor {
bool canHandler(FlexDragTargetData dragTargetData);
bool onWillAccept(
BuildContext context,
ReorderFlexState reorderFlexState,
FlexDragTargetData dragTargetData,
int itemIndex,
);
void onAccept(FlexDragTargetData dragTargetData);
void onLeave(FlexDragTargetData dragTargetData);
ReorderFlexDraggableTargetBuilder? get draggableTargetBuilder;
}
abstract class CrossReorderFlexDragTargetDelegate {
bool acceptNewDragTargetData(
String columnId,
FlexDragTargetData dragTargetData,
int index,
);
void updateDragTargetData(
String columnId,
FlexDragTargetData dragTargetData,
int index,
);
}
class CrossReorderFlexDragTargetInterceptor
extends ReorderFlexDragTargetInterceptor {
final String reorderFlexId;
final List<String> acceptReorderFlexIds;
final CrossReorderFlexDragTargetDelegate delegate;
@override
final ReorderFlexDraggableTargetBuilder? draggableTargetBuilder;
CrossReorderFlexDragTargetInterceptor({
required this.reorderFlexId,
required this.delegate,
required this.acceptReorderFlexIds,
this.draggableTargetBuilder,
});
@override
bool canHandler(FlexDragTargetData dragTargetData) {
if (acceptReorderFlexIds.isEmpty) {
return true;
}
if (acceptReorderFlexIds.contains(dragTargetData.reorderFlexId)) {
/// If the columnId equal to the dragTargetData's columnId,
/// it means the dragTarget is dragging on the top of its own list.
/// Otherwise, it means the dargTarget was moved to another list.
return reorderFlexId != dragTargetData.reorderFlexId;
} else {
return false;
}
}
@override
bool onWillAccept(
BuildContext context,
ReorderFlexState reorderFlexState,
FlexDragTargetData dragTargetData,
int itemIndex,
) {
final isNewDragTarget = delegate.acceptNewDragTargetData(
reorderFlexId,
dragTargetData,
itemIndex,
);
if (isNewDragTarget == false) {
delegate.updateDragTargetData(
reorderFlexId,
dragTargetData,
itemIndex,
);
reorderFlexState.onWillAccept(
context,
dragTargetData.draggingIndex,
itemIndex,
);
} else {
Log.debug(
'[$CrossReorderFlexDragTargetInterceptor] move Column${dragTargetData.reorderFlexId}:${dragTargetData.draggingIndex} '
'to Column$reorderFlexId:$itemIndex');
}
return true;
}
@override
void onAccept(FlexDragTargetData dragTargetData) {
Log.trace(
'[$CrossReorderFlexDragTargetInterceptor] Column$reorderFlexId on onAccept');
}
@override
void onLeave(FlexDragTargetData dragTargetData) {
Log.trace(
'[$CrossReorderFlexDragTargetInterceptor] Column$reorderFlexId on leave');
}
}

View File

@ -2,12 +2,11 @@ import 'dart:math';
import 'package:flutter/material.dart'; import 'package:flutter/material.dart';
import 'package:flutter/rendering.dart'; import 'package:flutter/rendering.dart';
import '../../utils/log.dart'; import '../../utils/log.dart';
import 'reorder_mixin.dart'; import 'reorder_mixin.dart';
import 'drag_target.dart'; import 'drag_target.dart';
import 'drag_state.dart'; import 'drag_state.dart';
import 'reorder_flex_ext.dart'; import 'drag_target_inteceptor.dart';
typedef OnDragStarted = void Function(int index); typedef OnDragStarted = void Function(int index);
typedef OnDragEnded = void Function(); typedef OnDragEnded = void Function();
@ -33,14 +32,13 @@ class ReorderFlexConfig {
} }
class ReorderFlex extends StatefulWidget with DraggingReorderFlex { class ReorderFlex extends StatefulWidget with DraggingReorderFlex {
final Widget? header;
final Widget? footer;
final ReorderFlexConfig config; final ReorderFlexConfig config;
final List<Widget> children; final List<Widget> children;
final EdgeInsets? padding; final EdgeInsets? padding;
final double? spacing;
final Axis direction; final Axis direction;
final MainAxisAlignment mainAxisAlignment = MainAxisAlignment.spaceEvenly; final MainAxisAlignment mainAxisAlignment = MainAxisAlignment.start;
final ScrollController? scrollController; final ScrollController? scrollController;
final OnDragStarted? onDragStarted; final OnDragStarted? onDragStarted;
@ -49,12 +47,10 @@ class ReorderFlex extends StatefulWidget with DraggingReorderFlex {
final ReoderFlextDataSource dataSource; final ReoderFlextDataSource dataSource;
final ReorderFlextDragTargetExtension? dragTargetExtension; final ReorderFlexDragTargetInterceptor? interceptor;
const ReorderFlex({ const ReorderFlex({
Key? key, Key? key,
this.header,
this.footer,
this.scrollController, this.scrollController,
required this.dataSource, required this.dataSource,
required this.children, required this.children,
@ -62,9 +58,9 @@ class ReorderFlex extends StatefulWidget with DraggingReorderFlex {
required this.onReorder, required this.onReorder,
this.onDragStarted, this.onDragStarted,
this.onDragEnded, this.onDragEnded,
this.dragTargetExtension, this.interceptor,
// ignore: unused_element
this.padding, this.padding,
this.spacing,
this.direction = Axis.vertical, this.direction = Axis.vertical,
}) : super(key: key); }) : super(key: key);
@ -136,23 +132,19 @@ class ReorderFlexState extends State<ReorderFlex>
@override @override
Widget build(BuildContext context) { Widget build(BuildContext context) {
final List<Widget> children = []; final List<Widget> children = [];
if (widget.header != null) {
children.add(widget.header!);
}
for (int i = 0; i < widget.children.length; i += 1) { for (int i = 0; i < widget.children.length; i += 1) {
Widget child = widget.children[i]; Widget child = widget.children[i];
if (widget.spacing != null) {
children.add(SizedBox(width: widget.spacing!));
}
final wrapChild = _wrap(child, i); final wrapChild = _wrap(child, i);
children.add(wrapChild); children.add(wrapChild);
} }
if (widget.footer != null) { final child = _wrapContainer(children);
children.add(widget.footer!); return _wrapScrollView(child: child);
}
return _wrapScrollView(
child: _wrapContainer(children),
);
} }
@override @override
@ -316,54 +308,49 @@ class ReorderFlexState extends State<ReorderFlex>
}); });
}, },
onWillAccept: (FlexDragTargetData dragTargetData) { onWillAccept: (FlexDragTargetData dragTargetData) {
Log.debug(
'[$ReorderDragTarget] ${widget.dataSource.identifier} on will accept, count: ${widget.dataSource.items.length}');
assert(widget.dataSource.items.length > childIndex); assert(widget.dataSource.items.length > childIndex);
if (_requestDragExtensionToHanlder( if (_interceptDragTarget(
dragTargetData, dragTargetData,
(extension) { (interceptor) => interceptor.onWillAccept(
extension.onWillAccept( builderContext,
this, this,
builderContext, dragTargetData,
dragTargetData, childIndex,
dragState.isDragging(), ),
dragTargetData.draggingIndex,
childIndex,
);
},
)) { )) {
return true; return true;
} else { } else {
Log.debug(
'[$ReorderDragTarget] ${widget.dataSource.identifier} on will accept, count: ${widget.dataSource.items.length}');
final dragIndex = dragTargetData.draggingIndex; final dragIndex = dragTargetData.draggingIndex;
return onWillAccept(builderContext, dragIndex, childIndex); return onWillAccept(builderContext, dragIndex, childIndex);
} }
}, },
onAccept: (dragTargetData) { onAccept: (dragTargetData) {
_requestDragExtensionToHanlder( _interceptDragTarget(
dragTargetData, dragTargetData,
(extension) => extension.onAccept(dragTargetData), (interceptor) => interceptor.onAccept(dragTargetData),
); );
}, },
onLeave: (dragTargetData) { onLeave: (dragTargetData) {
_requestDragExtensionToHanlder( _interceptDragTarget(
dragTargetData, dragTargetData,
(extension) => extension.onLeave(dragTargetData), (interceptor) => interceptor.onLeave(dragTargetData),
); );
}, },
draggableTargetBuilder: draggableTargetBuilder: widget.interceptor?.draggableTargetBuilder,
widget.dragTargetExtension?.draggableTargetBuilder,
child: child, child: child,
); );
} }
bool _requestDragExtensionToHanlder( bool _interceptDragTarget(
FlexDragTargetData dragTargetData, FlexDragTargetData dragTargetData,
void Function(ReorderFlextDragTargetExtension) callback, void Function(ReorderFlexDragTargetInterceptor) callback,
) { ) {
final extension = widget.dragTargetExtension; final interceptor = widget.interceptor;
if (extension != null && extension.canHandler(dragTargetData)) { if (interceptor != null && interceptor.canHandler(dragTargetData)) {
callback(extension); callback(interceptor);
return true; return true;
} else { } else {
return false; return false;
@ -466,14 +453,16 @@ class ReorderFlexState extends State<ReorderFlex>
case Axis.horizontal: case Axis.horizontal:
return Row( return Row(
mainAxisSize: MainAxisSize.min, mainAxisSize: MainAxisSize.min,
mainAxisAlignment: MainAxisAlignment.start, crossAxisAlignment: CrossAxisAlignment.start,
mainAxisAlignment: widget.mainAxisAlignment,
children: children, children: children,
); );
case Axis.vertical: case Axis.vertical:
default: default:
return Column( return Column(
mainAxisSize: MainAxisSize.min, mainAxisSize: MainAxisSize.min,
mainAxisAlignment: MainAxisAlignment.start, crossAxisAlignment: CrossAxisAlignment.start,
mainAxisAlignment: widget.mainAxisAlignment,
children: children, children: children,
); );
} }
@ -514,9 +503,7 @@ class ReorderFlexState extends State<ReorderFlex>
curve: Curves.easeInOut, curve: Curves.easeInOut,
) )
.then((void value) { .then((void value) {
setState(() { setState(() => _scrolling = false);
_scrolling = false;
});
}); });
} }
} }

View File

@ -1,78 +0,0 @@
import 'package:flutter/material.dart';
import '../../utils/log.dart';
import 'drag_state.dart';
import 'drag_target.dart';
import 'reorder_flex.dart';
abstract class DragTargetExtensionDelegate {
bool acceptNewDragTargetData(
String columnId,
FlexDragTargetData dragTargetData,
int index,
);
void updateDragTargetData(
String columnId,
FlexDragTargetData dragTargetData,
int index,
);
}
class ReorderFlextDragTargetExtension {
final String reorderFlexId;
final List<String> acceptReorderFlexIds;
final DragTargetExtensionDelegate delegate;
final ReorderDraggableTargetBuilder? draggableTargetBuilder;
ReorderFlextDragTargetExtension({
required this.reorderFlexId,
required this.delegate,
required this.acceptReorderFlexIds,
this.draggableTargetBuilder,
});
bool canHandler(FlexDragTargetData dragTargetData) {
/// If the columnId equal to the dragTargetData's columnId,
/// it means the dragTarget is dragging on the top of its own list.
/// Otherwise, it means the dargTarget was moved to another list.
///
if (!acceptReorderFlexIds.contains(dragTargetData.reorderFlexId)) {
return false;
}
return reorderFlexId != dragTargetData.reorderFlexId;
}
bool onWillAccept(
ReorderFlexState reorderFlexState,
BuildContext context,
FlexDragTargetData dragTargetData,
bool isDragging,
int dragIndex,
int itemIndex,
) {
final isNewDragTarget = delegate.acceptNewDragTargetData(
reorderFlexId, dragTargetData, itemIndex);
if (isNewDragTarget == false) {
delegate.updateDragTargetData(reorderFlexId, dragTargetData, itemIndex);
reorderFlexState.onWillAccept(context, dragIndex, itemIndex);
} else {
Log.debug(
'[$ReorderFlextDragTargetExtension] move Column${dragTargetData.reorderFlexId}:${dragTargetData.draggingIndex} '
'to Column$reorderFlexId:$itemIndex');
}
return true;
}
void onAccept(FlexDragTargetData dragTargetData) {
Log.trace(
'[$ReorderFlextDragTargetExtension] Column$reorderFlexId on onAccept');
}
void onLeave(FlexDragTargetData dragTargetData) {
Log.trace(
'[$ReorderFlextDragTargetExtension] Column$reorderFlexId on leave');
}
}

View File

@ -3,7 +3,7 @@ import '../../../flowy_board.dart';
import '../../utils/log.dart'; import '../../utils/log.dart';
import '../flex/drag_state.dart'; import '../flex/drag_state.dart';
import '../flex/drag_target.dart'; import '../flex/drag_target.dart';
import '../flex/reorder_flex_ext.dart'; import '../flex/drag_target_inteceptor.dart';
import 'phantom_state.dart'; import 'phantom_state.dart';
abstract class BoardPhantomControllerDelegate { abstract class BoardPhantomControllerDelegate {
@ -14,7 +14,7 @@ mixin ColumnDataPhantomMixim {
BoardColumnDataController? get; BoardColumnDataController? get;
} }
class BoardPhantomController extends DragTargetExtensionDelegate { class BoardPhantomController extends CrossReorderFlexDragTargetDelegate {
final BoardPhantomControllerDelegate delegate; final BoardPhantomControllerDelegate delegate;
PhantomRecord? phantomRecord; PhantomRecord? phantomRecord;
@ -313,7 +313,7 @@ class PassthroughPhantomWidget extends PhantomWidget {
); );
} }
class PhantomReorderDraggableBuilder extends ReorderDraggableTargetBuilder { class PhantomDraggableBuilder extends ReorderFlexDraggableTargetBuilder {
@override @override
Widget? build<T extends DragTargetData>( Widget? build<T extends DragTargetData>(
BuildContext context, BuildContext context,