mirror of
https://github.com/AppFlowy-IO/AppFlowy.git
synced 2024-08-30 18:12:39 +00:00
chore: fix insert animation
This commit is contained in:
parent
5e6b9496fc
commit
aec5c3e807
@ -6,11 +6,11 @@
|
||||
* Update example
|
||||
* Add AppFlowy style widget
|
||||
|
||||
## 0.0.2
|
||||
# 0.0.2
|
||||
|
||||
* Update documentation
|
||||
|
||||
## 0.0.1
|
||||
# 0.0.1
|
||||
|
||||
* Support drag and drop column
|
||||
* Support drag and drop column items from one to another
|
||||
|
@ -26,13 +26,18 @@ class _MultiBoardListExampleState extends State<MultiBoardListExample> {
|
||||
List<AFColumnItem> a = [
|
||||
TextItem("Card 1"),
|
||||
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'),
|
||||
TextItem("Card 4"),
|
||||
TextItem("Card 5"),
|
||||
TextItem("Card 6"),
|
||||
RichTextItem(title: "Card 7", subtitle: 'Aug 1, 2020 4:05 PM'),
|
||||
RichTextItem(title: "Card 8", subtitle: 'Aug 1, 2020 4:05 PM'),
|
||||
TextItem("Card 9"),
|
||||
];
|
||||
final column1 = AFBoardColumnData(id: "To Do", items: a);
|
||||
final column2 = AFBoardColumnData(id: "In Progress", items: <AFColumnItem>[
|
||||
// RichTextItem(title: "Card 5", subtitle: 'Aug 1, 2020 4:05 PM'),
|
||||
// TextItem("Card 6"),
|
||||
RichTextItem(title: "Card 10", subtitle: 'Aug 1, 2020 4:05 PM'),
|
||||
TextItem("Card 11"),
|
||||
]);
|
||||
|
||||
final column3 = AFBoardColumnData(id: "Done", items: <AFColumnItem>[]);
|
||||
@ -93,7 +98,7 @@ class _MultiBoardListExampleState extends State<MultiBoardListExample> {
|
||||
return Align(
|
||||
alignment: Alignment.centerLeft,
|
||||
child: Padding(
|
||||
padding: const EdgeInsets.symmetric(horizontal: 20),
|
||||
padding: const EdgeInsets.symmetric(horizontal: 20, vertical: 40),
|
||||
child: Text(item.s),
|
||||
),
|
||||
);
|
||||
|
@ -6,7 +6,7 @@ const DART_LOG = "Dart_LOG";
|
||||
class Log {
|
||||
// static const enableLog = bool.hasEnvironment(DART_LOG);
|
||||
// static final shared = Log();
|
||||
static const enableLog = false;
|
||||
static const enableLog = true;
|
||||
|
||||
static void info(String? message) {
|
||||
if (enableLog) {
|
||||
@ -16,19 +16,19 @@ class Log {
|
||||
|
||||
static void debug(String? message) {
|
||||
if (enableLog) {
|
||||
debugPrint('🐛[Debug]=> $message');
|
||||
debugPrint('🐛[Debug] - ${DateTime.now().second}=> $message');
|
||||
}
|
||||
}
|
||||
|
||||
static void warn(String? message) {
|
||||
if (enableLog) {
|
||||
debugPrint('🐛[Warn]=> $message');
|
||||
debugPrint('🐛[Warn] - ${DateTime.now().second} => $message');
|
||||
}
|
||||
}
|
||||
|
||||
static void trace(String? message) {
|
||||
if (enableLog) {
|
||||
// debugPrint('❗️[Trace]=> $message');
|
||||
debugPrint('❗️[Trace] - ${DateTime.now().second}=> $message');
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -197,7 +197,8 @@ class AFBoardDataController extends ChangeNotifier
|
||||
assert(index != -1);
|
||||
if (index != -1) {
|
||||
if (index != newIndex) {
|
||||
// Log.debug('[$BoardPhantomController] update $toColumnId:$index to $toColumnId:$phantomIndex');
|
||||
Log.debug(
|
||||
'[$BoardPhantomController] update $columnId:$index to $columnId:$newIndex');
|
||||
final item = columnDataController.removeAt(index, notify: false);
|
||||
columnDataController.insert(newIndex, item, notify: false);
|
||||
}
|
||||
|
@ -43,7 +43,7 @@ class FlexDragTargetData extends DragTargetData {
|
||||
}
|
||||
|
||||
class DraggingState {
|
||||
final String id;
|
||||
final String reorderFlexId;
|
||||
|
||||
/// The member of widget.children currently being dragged.
|
||||
Widget? _draggingWidget;
|
||||
@ -72,7 +72,7 @@ class DraggingState {
|
||||
/// The additional margin to place around a computed drop area.
|
||||
static const double _dropAreaMargin = 0.0;
|
||||
|
||||
DraggingState(this.id);
|
||||
DraggingState(this.reorderFlexId);
|
||||
|
||||
Size get dropAreaSize {
|
||||
if (feedbackSize == null) {
|
||||
@ -132,7 +132,7 @@ class DraggingState {
|
||||
}
|
||||
|
||||
void updateNextIndex(int index) {
|
||||
Log.trace('updateNextIndex: $index');
|
||||
Log.trace('$reorderFlexId updateNextIndex: $index');
|
||||
nextIndex = index;
|
||||
}
|
||||
|
||||
|
@ -1,3 +1,4 @@
|
||||
import 'package:appflowy_board/src/utils/log.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:flutter/scheduler.dart';
|
||||
import 'package:provider/provider.dart';
|
||||
@ -222,10 +223,10 @@ class DragTargetAnimation {
|
||||
value: 0, vsync: vsync, duration: reorderAnimationDuration);
|
||||
|
||||
insertController = AnimationController(
|
||||
value: 0.0, vsync: vsync, duration: const Duration(milliseconds: 100));
|
||||
value: 0.0, vsync: vsync, duration: const Duration(milliseconds: 200));
|
||||
|
||||
deleteController = AnimationController(
|
||||
value: 0.0, vsync: vsync, duration: const Duration(milliseconds: 10));
|
||||
value: 0.0, vsync: vsync, duration: const Duration(milliseconds: 1));
|
||||
}
|
||||
|
||||
void startDragging() {
|
||||
@ -371,6 +372,7 @@ class _DragTargeMovePlaceholderState extends State<DragTargeMovePlaceholder> {
|
||||
}
|
||||
|
||||
abstract class FakeDragTargetEventTrigger {
|
||||
void fakeOnDragStart(void Function(int?) callback);
|
||||
void fakeOnDragEnded(VoidCallback callback);
|
||||
}
|
||||
|
||||
@ -421,6 +423,10 @@ class _FakeDragTargetState<T extends DragTargetData>
|
||||
/// Start insert animation
|
||||
widget.insertAnimationController.forward(from: 0.0);
|
||||
|
||||
widget.eventTrigger.fakeOnDragStart((insertIndex) {
|
||||
Log.debug("[$FakeDragTarget] on drag $insertIndex");
|
||||
});
|
||||
|
||||
widget.eventTrigger.fakeOnDragEnded(() {
|
||||
WidgetsBinding.instance.addPostFrameCallback((_) {
|
||||
widget.onDragEnded(widget.eventData.dragTargetData as T);
|
||||
|
@ -1,3 +1,5 @@
|
||||
import 'dart:async';
|
||||
|
||||
import 'package:flutter/material.dart';
|
||||
|
||||
import '../../utils/log.dart';
|
||||
@ -8,6 +10,8 @@ import 'reorder_flex.dart';
|
||||
/// [DragTargetInterceptor] is used to intercept the [DragTarget]'s
|
||||
/// [onWillAccept], [OnAccept], and [onLeave] event.
|
||||
abstract class DragTargetInterceptor {
|
||||
String get reorderFlexId;
|
||||
|
||||
/// Returns [yes] to receive the [DragTarget]'s event.
|
||||
bool canHandler(FlexDragTargetData dragTargetData);
|
||||
|
||||
@ -37,7 +41,7 @@ abstract class OverlapDragTargetDelegate {
|
||||
int dragTargetIndex,
|
||||
);
|
||||
|
||||
bool canMoveTo(String dragTargetId);
|
||||
int canMoveTo(String dragTargetId);
|
||||
}
|
||||
|
||||
/// [OverlappingDragTargetInterceptor] is used to receive the overlapping
|
||||
@ -47,6 +51,7 @@ abstract class OverlapDragTargetDelegate {
|
||||
/// Receive the [DragTarget] event if the [acceptedReorderFlexId] contains
|
||||
/// the passed in dragTarget' reorderFlexId.
|
||||
class OverlappingDragTargetInterceptor extends DragTargetInterceptor {
|
||||
@override
|
||||
final String reorderFlexId;
|
||||
final List<String> acceptedReorderFlexId;
|
||||
final OverlapDragTargetDelegate delegate;
|
||||
@ -72,8 +77,11 @@ class OverlappingDragTargetInterceptor extends DragTargetInterceptor {
|
||||
if (dragTargetId == dragTargetData.reorderFlexId) {
|
||||
delegate.cancel();
|
||||
} else {
|
||||
if (delegate.canMoveTo(dragTargetId)) {
|
||||
delegate.moveTo(dragTargetId, dragTargetData, 0);
|
||||
final index = delegate.canMoveTo(dragTargetId);
|
||||
Log.trace(
|
||||
'[$OverlappingDragTargetInterceptor] move to $dragTargetId at $index');
|
||||
if (index != -1) {
|
||||
delegate.moveTo(dragTargetId, dragTargetData, index);
|
||||
}
|
||||
}
|
||||
|
||||
@ -96,6 +104,7 @@ abstract class CrossReorderFlexDragTargetDelegate {
|
||||
}
|
||||
|
||||
class CrossReorderFlexDragTargetInterceptor extends DragTargetInterceptor {
|
||||
@override
|
||||
final String reorderFlexId;
|
||||
final List<String> acceptedReorderFlexIds;
|
||||
final CrossReorderFlexDragTargetDelegate delegate;
|
||||
@ -119,8 +128,12 @@ class CrossReorderFlexDragTargetInterceptor extends DragTargetInterceptor {
|
||||
/// 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.
|
||||
Log.trace(
|
||||
"[$CrossReorderFlexDragTargetInterceptor] $reorderFlexId accept ${dragTargetData.reorderFlexId} ${reorderFlexId != dragTargetData.reorderFlexId}");
|
||||
return reorderFlexId != dragTargetData.reorderFlexId;
|
||||
} else {
|
||||
Log.trace(
|
||||
"[$CrossReorderFlexDragTargetInterceptor] not accept ${dragTargetData.reorderFlexId}");
|
||||
return false;
|
||||
}
|
||||
}
|
||||
@ -151,6 +164,9 @@ class CrossReorderFlexDragTargetInterceptor extends DragTargetInterceptor {
|
||||
dragTargetIndex,
|
||||
);
|
||||
|
||||
Log.debug(
|
||||
'[$CrossReorderFlexDragTargetInterceptor] dargTargetIndex: $dragTargetIndex, reorderFlexId: $reorderFlexId');
|
||||
|
||||
if (isNewDragTarget == false) {
|
||||
delegate.updateDragTargetData(reorderFlexId, dragTargetIndex);
|
||||
reorderFlexState.handleOnWillAccept(context, dragTargetIndex);
|
||||
|
@ -36,10 +36,10 @@ class ReorderFlexConfig {
|
||||
final double draggingWidgetOpacity = 0.3;
|
||||
|
||||
// How long an animation to reorder an element
|
||||
final Duration reorderAnimationDuration = const Duration(milliseconds: 250);
|
||||
final Duration reorderAnimationDuration = const Duration(milliseconds: 300);
|
||||
|
||||
// How long an animation to scroll to an off-screen element
|
||||
final Duration scrollAnimationDuration = const Duration(milliseconds: 250);
|
||||
final Duration scrollAnimationDuration = const Duration(milliseconds: 300);
|
||||
|
||||
final bool useMoveAnimation;
|
||||
|
||||
@ -213,8 +213,8 @@ class ReorderFlexState extends State<ReorderFlex>
|
||||
shiftedIndex = dragState.calculateShiftedIndex(childIndex);
|
||||
}
|
||||
|
||||
Log.trace(
|
||||
'Rebuild: Column:[${dragState.id}] ${dragState.toString()}, childIndex: $childIndex shiftedIndex: $shiftedIndex');
|
||||
// Log.trace(
|
||||
// 'Rebuild: Column:[${dragState.id}] ${dragState.toString()}, childIndex: $childIndex shiftedIndex: $shiftedIndex');
|
||||
final currentIndex = dragState.currentIndex;
|
||||
final dragPhantomIndex = dragState.phantomIndex;
|
||||
|
||||
@ -330,6 +330,8 @@ class ReorderFlexState extends State<ReorderFlex>
|
||||
widget.onDragStarted?.call(draggingIndex);
|
||||
},
|
||||
onDragEnded: (dragTargetData) {
|
||||
if (!mounted) return;
|
||||
|
||||
Log.debug(
|
||||
"[DragTarget]: Column:[${widget.dataSource.identifier}] end dragging");
|
||||
_notifier.updateDragTargetIndex(-1);
|
||||
@ -346,21 +348,21 @@ class ReorderFlexState extends State<ReorderFlex>
|
||||
});
|
||||
},
|
||||
onWillAccept: (FlexDragTargetData dragTargetData) {
|
||||
// Do not receive any events if the Insert item is animating.
|
||||
if (_animation.deleteController.isAnimating) {
|
||||
return false;
|
||||
}
|
||||
|
||||
assert(widget.dataSource.items.length > dragTargetIndex);
|
||||
if (_interceptDragTarget(
|
||||
dragTargetData,
|
||||
(interceptor) => interceptor.onWillAccept(
|
||||
if (_interceptDragTarget(dragTargetData, (interceptor) {
|
||||
interceptor.onWillAccept(
|
||||
context: builderContext,
|
||||
reorderFlexState: this,
|
||||
dragTargetData: dragTargetData,
|
||||
dragTargetId: reorderFlexItem.id,
|
||||
dragTargetIndex: dragTargetIndex,
|
||||
),
|
||||
)) {
|
||||
);
|
||||
})) {
|
||||
return true;
|
||||
} else {
|
||||
return handleOnWillAccept(builderContext, dragTargetIndex);
|
||||
@ -524,7 +526,7 @@ class ReorderFlexState extends State<ReorderFlex>
|
||||
// screen, then it is already on-screen.
|
||||
final double margin = widget.direction == Axis.horizontal
|
||||
? dragState.dropAreaSize.width
|
||||
: dragState.dropAreaSize.height;
|
||||
: dragState.dropAreaSize.height / 2.0;
|
||||
if (_scrollController.hasClients) {
|
||||
final double scrollOffset = _scrollController.offset;
|
||||
final double topOffset = max(
|
||||
|
@ -59,12 +59,13 @@ class BoardPhantomController extends OverlapDragTargetDelegate
|
||||
}
|
||||
|
||||
void columnStartDragging(String columnId) {
|
||||
columnsState.setColumnIsDragging(columnId, false);
|
||||
columnsState.setColumnIsDragging(columnId, true);
|
||||
}
|
||||
|
||||
/// Remove the phantom in the column when the column is end dragging.
|
||||
void columnEndDragging(String columnId) {
|
||||
columnsState.setColumnIsDragging(columnId, true);
|
||||
columnsState.setColumnIsDragging(columnId, false);
|
||||
|
||||
if (phantomRecord == null) return;
|
||||
|
||||
final fromColumnId = phantomRecord!.fromColumnId;
|
||||
@ -73,19 +74,18 @@ class BoardPhantomController extends OverlapDragTargetDelegate
|
||||
columnsState.notifyDidRemovePhantom(toColumnId);
|
||||
}
|
||||
|
||||
if (columnsState.isDragging(fromColumnId) == false) {
|
||||
return;
|
||||
if (phantomRecord!.toColumnId == columnId) {
|
||||
delegate.swapColumnItem(
|
||||
fromColumnId,
|
||||
phantomRecord!.fromColumnIndex,
|
||||
toColumnId,
|
||||
phantomRecord!.toColumnIndex,
|
||||
);
|
||||
|
||||
Log.debug(
|
||||
"[$BoardPhantomController] did move ${phantomRecord.toString()}");
|
||||
phantomRecord = null;
|
||||
}
|
||||
|
||||
delegate.swapColumnItem(
|
||||
fromColumnId,
|
||||
phantomRecord!.fromColumnIndex,
|
||||
toColumnId,
|
||||
phantomRecord!.toColumnIndex,
|
||||
);
|
||||
|
||||
Log.debug("[$BoardPhantomController] did move ${phantomRecord.toString()}");
|
||||
phantomRecord = null;
|
||||
}
|
||||
|
||||
/// Remove the phantom in the column if it contains phantom
|
||||
@ -113,7 +113,7 @@ class BoardPhantomController extends OverlapDragTargetDelegate
|
||||
PhantomColumnItem(phantomContext),
|
||||
);
|
||||
|
||||
columnsState.notifyDidInsertPhantom(toColumnId);
|
||||
columnsState.notifyDidInsertPhantom(toColumnId, phantomIndex);
|
||||
}
|
||||
|
||||
/// Reset or initial the [PhantomRecord]
|
||||
@ -128,8 +128,9 @@ class BoardPhantomController extends OverlapDragTargetDelegate
|
||||
FlexDragTargetData dragTargetData,
|
||||
int dragTargetIndex,
|
||||
) {
|
||||
// Log.debug('[$BoardPhantomController] move Column:[${dragTargetData.reorderFlexId}]:${dragTargetData.draggingIndex} '
|
||||
// 'to Column:[$columnId]:$index');
|
||||
// Log.debug(
|
||||
// '[$BoardPhantomController] move Column:[${dragTargetData.reorderFlexId}]:${dragTargetData.draggingIndex} '
|
||||
// 'to Column:[$columnId]:$dragTargetIndex');
|
||||
|
||||
phantomRecord = PhantomRecord(
|
||||
toColumnId: columnId,
|
||||
@ -202,8 +203,23 @@ class BoardPhantomController extends OverlapDragTargetDelegate
|
||||
}
|
||||
|
||||
@override
|
||||
bool canMoveTo(String dragTargetId) {
|
||||
return delegate.controller(dragTargetId)?.columnData.items.isEmpty ?? false;
|
||||
int canMoveTo(String dragTargetId) {
|
||||
// if (columnsState.isDragging(dragTargetId)) {
|
||||
// return -1;
|
||||
// }
|
||||
|
||||
// final controller = delegate.controller(dragTargetId);
|
||||
// if (controller != null) {
|
||||
// return controller.columnData.items.length;
|
||||
// } else {
|
||||
// return 0;
|
||||
// }
|
||||
|
||||
if (delegate.controller(dragTargetId)?.columnData.items.isEmpty ?? false) {
|
||||
return 0;
|
||||
} else {
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -294,7 +310,7 @@ class PassthroughPhantomContext extends FakeDragTargetEventTrigger
|
||||
AFColumnItem get itemData => dragTargetData.reorderFlexItem as AFColumnItem;
|
||||
|
||||
@override
|
||||
VoidCallback? onInserted;
|
||||
void Function(int?)? onInserted;
|
||||
|
||||
@override
|
||||
VoidCallback? onDragEnded;
|
||||
@ -308,6 +324,11 @@ class PassthroughPhantomContext extends FakeDragTargetEventTrigger
|
||||
void fakeOnDragEnded(VoidCallback callback) {
|
||||
onDragEnded = callback;
|
||||
}
|
||||
|
||||
@override
|
||||
void fakeOnDragStart(void Function(int? index) callback) {
|
||||
onInserted = callback;
|
||||
}
|
||||
}
|
||||
|
||||
class PassthroughPhantomWidget extends PhantomWidget {
|
||||
|
@ -14,7 +14,7 @@ class ColumnPhantomStateController {
|
||||
|
||||
void addColumnListener(String columnId, PassthroughPhantomListener listener) {
|
||||
_stateWithId(columnId).notifier.addListener(
|
||||
onInserted: (c) => listener.onInserted?.call(),
|
||||
onInserted: (index) => listener.onInserted?.call(index),
|
||||
onDeleted: () => listener.onDragEnded?.call(),
|
||||
);
|
||||
}
|
||||
@ -24,8 +24,8 @@ class ColumnPhantomStateController {
|
||||
_states.remove(columnId);
|
||||
}
|
||||
|
||||
void notifyDidInsertPhantom(String columnId) {
|
||||
_stateWithId(columnId).notifier.insert();
|
||||
void notifyDidInsertPhantom(String columnId, int index) {
|
||||
_stateWithId(columnId).notifier.insert(index);
|
||||
}
|
||||
|
||||
void notifyDidRemovePhantom(String columnId) {
|
||||
@ -48,7 +48,7 @@ class ColumnState {
|
||||
}
|
||||
|
||||
abstract class PassthroughPhantomListener {
|
||||
VoidCallback? get onInserted;
|
||||
void Function(int?)? get onInserted;
|
||||
VoidCallback? get onDragEnded;
|
||||
}
|
||||
|
||||
@ -57,8 +57,8 @@ class PassthroughPhantomNotifier {
|
||||
|
||||
final removeNotifier = PhantomDeleteNotifier();
|
||||
|
||||
void insert() {
|
||||
insertNotifier.insert();
|
||||
void insert(int index) {
|
||||
insertNotifier.insert(index);
|
||||
}
|
||||
|
||||
void remove() {
|
||||
@ -66,12 +66,12 @@ class PassthroughPhantomNotifier {
|
||||
}
|
||||
|
||||
void addListener({
|
||||
void Function(PassthroughPhantomContext? insertedPhantom)? onInserted,
|
||||
void Function(int? insertedIndex)? onInserted,
|
||||
void Function()? onDeleted,
|
||||
}) {
|
||||
if (onInserted != null) {
|
||||
insertNotifier.addListener(() {
|
||||
onInserted(insertNotifier.insertedPhantom);
|
||||
onInserted(insertNotifier.insertedIndex);
|
||||
});
|
||||
}
|
||||
|
||||
@ -89,9 +89,11 @@ class PassthroughPhantomNotifier {
|
||||
}
|
||||
|
||||
class PhantomInsertNotifier extends ChangeNotifier {
|
||||
int insertedIndex = -1;
|
||||
PassthroughPhantomContext? insertedPhantom;
|
||||
|
||||
void insert() {
|
||||
void insert(int index) {
|
||||
insertedIndex = index;
|
||||
notifyListeners();
|
||||
}
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user