Skip to content
Projects
Groups
Snippets
Help
Loading...
Help
Submit feedback
Sign in
Toggle navigation
F
Front-End
Project
Project
Details
Activity
Releases
Cycle Analytics
Repository
Repository
Files
Commits
Branches
Tags
Contributors
Graph
Compare
Charts
Issues
0
Issues
0
List
Board
Labels
Milestones
Merge Requests
0
Merge Requests
0
CI / CD
CI / CD
Pipelines
Jobs
Schedules
Charts
Wiki
Wiki
Snippets
Snippets
Members
Members
Collapse sidebar
Close sidebar
Activity
Graph
Charts
Create a new issue
Jobs
Commits
Issue Boards
Open sidebar
abdullh.alsoleman
Front-End
Commits
f90ccf48
Commit
f90ccf48
authored
Mar 25, 2016
by
Andrew Wilson
Browse files
Options
Browse Files
Download
Email Patches
Plain Diff
Add a callabck when a Draggable is dropped without being accepted.
parent
03830d56
Changes
3
Hide whitespace changes
Inline
Side-by-side
Showing
3 changed files
with
241 additions
and
19 deletions
+241
-19
drag_target.dart
packages/flutter/lib/src/widgets/drag_target.dart
+38
-16
draggable_test.dart
packages/flutter/test/widget/draggable_test.dart
+202
-2
instrumentation.dart
packages/flutter_test/lib/src/instrumentation.dart
+1
-1
No files found.
packages/flutter/lib/src/widgets/drag_target.dart
View file @
f90ccf48
...
...
@@ -15,6 +15,9 @@ typedef bool DragTargetWillAccept<T>(T data);
typedef
void
DragTargetAccept
<
T
>(
T
data
);
typedef
Widget
DragTargetBuilder
<
T
>(
BuildContext
context
,
List
<
T
>
candidateData
,
List
<
dynamic
>
rejectedData
);
/// Called when a [Draggable] is dropped without being accepted by a [DragTarget].
typedef
void
OnDraggableCanceled
(
Velocity
velocity
,
Offset
offset
);
/// Where the [Draggable] should be anchored during a drag.
enum
DragAnchor
{
/// Display the feedback anchored at the position of the original child. If
...
...
@@ -45,7 +48,8 @@ abstract class DraggableBase<T> extends StatefulWidget {
this
.
feedback
,
this
.
feedbackOffset
:
Offset
.
zero
,
this
.
dragAnchor
:
DragAnchor
.
child
,
this
.
maxSimultaneousDrags
this
.
maxSimultaneousDrags
,
this
.
onDraggableCanceled
})
:
super
(
key:
key
)
{
assert
(
child
!=
null
);
assert
(
feedback
!=
null
);
...
...
@@ -80,6 +84,9 @@ abstract class DraggableBase<T> extends StatefulWidget {
/// dragged at a time.
final
int
maxSimultaneousDrags
;
/// Called when the draggable is dropped without being accepted by a [DragTarget].
final
OnDraggableCanceled
onDraggableCanceled
;
/// Should return a new MultiDragGestureRecognizer instance
/// constructed with the given arguments.
MultiDragGestureRecognizer
<
MultiDragPointerState
>
createRecognizer
(
GestureMultiDragStartCallback
onStart
);
...
...
@@ -98,7 +105,8 @@ class Draggable<T> extends DraggableBase<T> {
Widget
feedback
,
Offset
feedbackOffset:
Offset
.
zero
,
DragAnchor
dragAnchor:
DragAnchor
.
child
,
int
maxSimultaneousDrags
int
maxSimultaneousDrags
,
OnDraggableCanceled
onDraggableCanceled
})
:
super
(
key:
key
,
data:
data
,
...
...
@@ -107,7 +115,8 @@ class Draggable<T> extends DraggableBase<T> {
feedback:
feedback
,
feedbackOffset:
feedbackOffset
,
dragAnchor:
dragAnchor
,
maxSimultaneousDrags:
maxSimultaneousDrags
maxSimultaneousDrags:
maxSimultaneousDrags
,
onDraggableCanceled:
onDraggableCanceled
);
@override
...
...
@@ -127,7 +136,8 @@ class HorizontalDraggable<T> extends DraggableBase<T> {
Widget
feedback
,
Offset
feedbackOffset:
Offset
.
zero
,
DragAnchor
dragAnchor:
DragAnchor
.
child
,
int
maxSimultaneousDrags
int
maxSimultaneousDrags
,
OnDraggableCanceled
onDraggableCanceled
})
:
super
(
key:
key
,
data:
data
,
...
...
@@ -136,7 +146,8 @@ class HorizontalDraggable<T> extends DraggableBase<T> {
feedback:
feedback
,
feedbackOffset:
feedbackOffset
,
dragAnchor:
dragAnchor
,
maxSimultaneousDrags:
maxSimultaneousDrags
maxSimultaneousDrags:
maxSimultaneousDrags
,
onDraggableCanceled:
onDraggableCanceled
);
@override
...
...
@@ -156,7 +167,8 @@ class VerticalDraggable<T> extends DraggableBase<T> {
Widget
feedback
,
Offset
feedbackOffset:
Offset
.
zero
,
DragAnchor
dragAnchor:
DragAnchor
.
child
,
int
maxSimultaneousDrags
int
maxSimultaneousDrags
,
OnDraggableCanceled
onDraggableCanceled
})
:
super
(
key:
key
,
data:
data
,
...
...
@@ -165,7 +177,8 @@ class VerticalDraggable<T> extends DraggableBase<T> {
feedback:
feedback
,
feedbackOffset:
feedbackOffset
,
dragAnchor:
dragAnchor
,
maxSimultaneousDrags:
maxSimultaneousDrags
maxSimultaneousDrags:
maxSimultaneousDrags
,
onDraggableCanceled:
onDraggableCanceled
);
@override
...
...
@@ -184,7 +197,8 @@ class LongPressDraggable<T> extends DraggableBase<T> {
Widget
feedback
,
Offset
feedbackOffset:
Offset
.
zero
,
DragAnchor
dragAnchor:
DragAnchor
.
child
,
int
maxSimultaneousDrags
int
maxSimultaneousDrags
,
OnDraggableCanceled
onDraggableCanceled
})
:
super
(
key:
key
,
data:
data
,
...
...
@@ -193,7 +207,8 @@ class LongPressDraggable<T> extends DraggableBase<T> {
feedback:
feedback
,
feedbackOffset:
feedbackOffset
,
dragAnchor:
dragAnchor
,
maxSimultaneousDrags:
maxSimultaneousDrags
maxSimultaneousDrags:
maxSimultaneousDrags
,
onDraggableCanceled:
onDraggableCanceled
);
@override
...
...
@@ -248,9 +263,11 @@ class _DraggableState<T> extends State<DraggableBase<T>> {
dragStartPoint:
dragStartPoint
,
feedback:
config
.
feedback
,
feedbackOffset:
config
.
feedbackOffset
,
onDragEnd:
()
{
onDragEnd:
(
Velocity
velocity
,
Offset
offset
,
bool
wasAccepted
)
{
setState
(()
{
_activeCount
-=
1
;
if
(!
wasAccepted
&&
config
.
onDraggableCanceled
!=
null
)
config
.
onDraggableCanceled
(
velocity
,
offset
);
});
}
);
...
...
@@ -341,6 +358,7 @@ class _DragTargetState<T> extends State<DragTarget<T>> {
enum
_DragEndKind
{
dropped
,
canceled
}
typedef
void
_OnDragEnd
(
Velocity
velocity
,
Offset
offset
,
bool
wasAccepted
);
// The lifetime of this object is a little dubious right now. Specifically, it
// lives as long as the pointer is down. Arguably it should self-immolate if the
...
...
@@ -370,7 +388,7 @@ class _DragAvatar<T> extends Drag {
final
Point
dragStartPoint
;
final
Widget
feedback
;
final
Offset
feedbackOffset
;
final
VoidCallback
onDragEnd
;
final
_OnDragEnd
onDragEnd
;
_DragTargetState
<
T
>
_activeTarget
;
bool
_activeTargetWillAcceptDrop
=
false
;
...
...
@@ -387,7 +405,7 @@ class _DragAvatar<T> extends Drag {
@override
void
end
(
Velocity
velocity
)
{
finish
(
_DragEndKind
.
dropped
);
finish
(
_DragEndKind
.
dropped
,
velocity
);
}
@override
...
...
@@ -422,19 +440,23 @@ class _DragAvatar<T> extends Drag {
return
null
;
}
void
finish
(
_DragEndKind
endKind
)
{
void
finish
(
_DragEndKind
endKind
,
[
Velocity
velocity
])
{
bool
wasAccepted
=
false
;
if
(
_activeTarget
!=
null
)
{
if
(
endKind
==
_DragEndKind
.
dropped
&&
_activeTargetWillAcceptDrop
)
if
(
endKind
==
_DragEndKind
.
dropped
&&
_activeTargetWillAcceptDrop
)
{
_activeTarget
.
didDrop
(
data
);
else
wasAccepted
=
true
;
}
else
{
_activeTarget
.
didLeave
(
data
);
}
}
_activeTarget
=
null
;
_activeTargetWillAcceptDrop
=
false
;
_entry
.
remove
();
_entry
=
null
;
// TODO(ianh): consider passing _entry as well so the client can perform an animation.
if
(
onDragEnd
!=
null
)
onDragEnd
();
onDragEnd
(
velocity
??
Velocity
.
zero
,
_lastOffset
,
wasAccepted
);
}
Widget
_build
(
BuildContext
context
)
{
...
...
packages/flutter/test/widget/draggable_test.dart
View file @
f90ccf48
...
...
@@ -9,7 +9,7 @@ import 'package:test/test.dart';
void
main
(
)
{
test
(
'Drag and drop - control test'
,
()
{
testWidgets
((
WidgetTester
tester
)
{
List
<
dynamic
>
accepted
=
<
dynamic
>[];
List
<
int
>
accepted
=
<
int
>[];
tester
.
pumpWidget
(
new
MaterialApp
(
routes:
<
String
,
WidgetBuilder
>{
...
...
@@ -62,7 +62,7 @@ void main() {
gesture
.
up
();
tester
.
pump
();
expect
(
accepted
,
equals
([
1
]));
expect
(
accepted
,
equals
(
<
int
>
[
1
]));
expect
(
tester
.
findText
(
'Source'
),
isNotNull
);
expect
(
tester
.
findText
(
'Dragging'
),
isNull
);
expect
(
tester
.
findText
(
'Target'
),
isNotNull
);
...
...
@@ -553,4 +553,204 @@ void main() {
});
});
test
(
'Drag and drop - onDraggableDropped not called if dropped on accepting target'
,
()
{
testWidgets
((
WidgetTester
tester
)
{
List
<
int
>
accepted
=
<
int
>[];
bool
onDraggableCanceledCalled
=
false
;
tester
.
pumpWidget
(
new
MaterialApp
(
routes:
<
String
,
WidgetBuilder
>{
'/'
:
(
BuildContext
context
)
{
return
new
Column
(
children:
<
Widget
>[
new
Draggable
<
int
>(
data:
1
,
child:
new
Text
(
'Source'
),
feedback:
new
Text
(
'Dragging'
),
onDraggableCanceled:
(
Velocity
velocity
,
Offset
offset
)
{
onDraggableCanceledCalled
=
true
;
}
),
new
DragTarget
<
int
>(
builder:
(
BuildContext
context
,
List
<
int
>
data
,
List
<
dynamic
>
rejects
)
{
return
new
Container
(
height:
100.0
,
child:
new
Text
(
'Target'
)
);
},
onAccept:
(
int
data
)
{
accepted
.
add
(
data
);
}
),
]);
},
}
));
expect
(
accepted
,
isEmpty
);
expect
(
tester
.
findText
(
'Source'
),
isNotNull
);
expect
(
tester
.
findText
(
'Dragging'
),
isNull
);
expect
(
tester
.
findText
(
'Target'
),
isNotNull
);
expect
(
onDraggableCanceledCalled
,
isFalse
);
Point
firstLocation
=
tester
.
getCenter
(
tester
.
findText
(
'Source'
));
TestGesture
gesture
=
tester
.
startGesture
(
firstLocation
,
pointer:
7
);
tester
.
pump
();
expect
(
accepted
,
isEmpty
);
expect
(
tester
.
findText
(
'Source'
),
isNotNull
);
expect
(
tester
.
findText
(
'Dragging'
),
isNotNull
);
expect
(
tester
.
findText
(
'Target'
),
isNotNull
);
expect
(
onDraggableCanceledCalled
,
isFalse
);
Point
secondLocation
=
tester
.
getCenter
(
tester
.
findText
(
'Target'
));
gesture
.
moveTo
(
secondLocation
);
tester
.
pump
();
expect
(
accepted
,
isEmpty
);
expect
(
tester
.
findText
(
'Source'
),
isNotNull
);
expect
(
tester
.
findText
(
'Dragging'
),
isNotNull
);
expect
(
tester
.
findText
(
'Target'
),
isNotNull
);
expect
(
onDraggableCanceledCalled
,
isFalse
);
gesture
.
up
();
tester
.
pump
();
expect
(
accepted
,
equals
(<
int
>[
1
]));
expect
(
tester
.
findText
(
'Source'
),
isNotNull
);
expect
(
tester
.
findText
(
'Dragging'
),
isNull
);
expect
(
tester
.
findText
(
'Target'
),
isNotNull
);
expect
(
onDraggableCanceledCalled
,
isFalse
);
});
});
test
(
'Drag and drop - onDraggableDropped called if dropped on non-accepting target'
,
()
{
testWidgets
((
WidgetTester
tester
)
{
List
<
int
>
accepted
=
<
int
>[];
bool
onDraggableCanceledCalled
=
false
;
Velocity
onDraggableCanceledVelocity
;
Offset
onDraggableCanceledOffset
;
tester
.
pumpWidget
(
new
MaterialApp
(
routes:
<
String
,
WidgetBuilder
>{
'/'
:
(
BuildContext
context
)
{
return
new
Column
(
children:
<
Widget
>[
new
Draggable
<
int
>(
data:
1
,
child:
new
Text
(
'Source'
),
feedback:
new
Text
(
'Dragging'
),
onDraggableCanceled:
(
Velocity
velocity
,
Offset
offset
)
{
onDraggableCanceledCalled
=
true
;
onDraggableCanceledVelocity
=
velocity
;
onDraggableCanceledOffset
=
offset
;
}
),
new
DragTarget
<
int
>(
builder:
(
BuildContext
context
,
List
<
int
>
data
,
List
<
dynamic
>
rejects
)
{
return
new
Container
(
height:
100.0
,
child:
new
Text
(
'Target'
)
);
},
onWillAccept:
(
int
data
)
=>
false
),
]);
},
}
));
expect
(
accepted
,
isEmpty
);
expect
(
tester
.
findText
(
'Source'
),
isNotNull
);
expect
(
tester
.
findText
(
'Dragging'
),
isNull
);
expect
(
tester
.
findText
(
'Target'
),
isNotNull
);
expect
(
onDraggableCanceledCalled
,
isFalse
);
Point
firstLocation
=
tester
.
getTopLeft
(
tester
.
findText
(
'Source'
));
TestGesture
gesture
=
tester
.
startGesture
(
firstLocation
,
pointer:
7
);
tester
.
pump
();
expect
(
accepted
,
isEmpty
);
expect
(
tester
.
findText
(
'Source'
),
isNotNull
);
expect
(
tester
.
findText
(
'Dragging'
),
isNotNull
);
expect
(
tester
.
findText
(
'Target'
),
isNotNull
);
expect
(
onDraggableCanceledCalled
,
isFalse
);
Point
secondLocation
=
tester
.
getCenter
(
tester
.
findText
(
'Target'
));
gesture
.
moveTo
(
secondLocation
);
tester
.
pump
();
expect
(
accepted
,
isEmpty
);
expect
(
tester
.
findText
(
'Source'
),
isNotNull
);
expect
(
tester
.
findText
(
'Dragging'
),
isNotNull
);
expect
(
tester
.
findText
(
'Target'
),
isNotNull
);
expect
(
onDraggableCanceledCalled
,
isFalse
);
gesture
.
up
();
tester
.
pump
();
expect
(
accepted
,
isEmpty
);
expect
(
tester
.
findText
(
'Source'
),
isNotNull
);
expect
(
tester
.
findText
(
'Dragging'
),
isNull
);
expect
(
tester
.
findText
(
'Target'
),
isNotNull
);
expect
(
onDraggableCanceledCalled
,
isTrue
);
expect
(
onDraggableCanceledVelocity
,
equals
(
Velocity
.
zero
));
expect
(
onDraggableCanceledOffset
,
equals
(
new
Offset
(
secondLocation
.
x
,
secondLocation
.
y
)));
});
});
test
(
'Drag and drop - onDraggableDropped called if dropped on non-accepting target with correct velocity'
,
()
{
testWidgets
((
WidgetTester
tester
)
{
List
<
int
>
accepted
=
<
int
>[];
bool
onDraggableCanceledCalled
=
false
;
Velocity
onDraggableCanceledVelocity
;
Offset
onDraggableCanceledOffset
;
tester
.
pumpWidget
(
new
MaterialApp
(
routes:
<
String
,
WidgetBuilder
>{
'/'
:
(
BuildContext
context
)
{
return
new
Column
(
children:
<
Widget
>[
new
Draggable
<
int
>(
data:
1
,
child:
new
Text
(
'Source'
),
feedback:
new
Text
(
'Source'
),
onDraggableCanceled:
(
Velocity
velocity
,
Offset
offset
)
{
onDraggableCanceledCalled
=
true
;
onDraggableCanceledVelocity
=
velocity
;
onDraggableCanceledOffset
=
offset
;
}
),
new
DragTarget
<
int
>(
builder:
(
BuildContext
context
,
List
<
int
>
data
,
List
<
dynamic
>
rejects
)
{
return
new
Container
(
height:
100.0
,
child:
new
Text
(
'Target'
)
);
},
onWillAccept:
(
int
data
)
=>
false
),
]);
},
}
));
expect
(
accepted
,
isEmpty
);
expect
(
tester
.
findText
(
'Source'
),
isNotNull
);
expect
(
tester
.
findText
(
'Dragging'
),
isNull
);
expect
(
tester
.
findText
(
'Target'
),
isNotNull
);
expect
(
onDraggableCanceledCalled
,
isFalse
);
Point
flingStart
=
tester
.
getTopLeft
(
tester
.
findText
(
'Source'
));
tester
.
flingFrom
(
flingStart
,
new
Offset
(
0.0
,
100.0
),
1000.0
);
tester
.
pump
();
expect
(
accepted
,
isEmpty
);
expect
(
tester
.
findText
(
'Source'
),
isNotNull
);
expect
(
tester
.
findText
(
'Dragging'
),
isNull
);
expect
(
tester
.
findText
(
'Target'
),
isNotNull
);
expect
(
onDraggableCanceledCalled
,
isTrue
);
expect
(
onDraggableCanceledVelocity
.
pixelsPerSecond
.
dx
.
abs
(),
lessThan
(
0.0000001
));
expect
((
onDraggableCanceledVelocity
.
pixelsPerSecond
.
dy
-
1000.0
).
abs
(),
lessThan
(
0.0000001
));
expect
(
onDraggableCanceledOffset
,
equals
(
new
Offset
(
flingStart
.
x
,
flingStart
.
y
)
+
new
Offset
(
0.0
,
100.0
)));
});
});
}
packages/flutter_test/lib/src/instrumentation.dart
View file @
f90ccf48
...
...
@@ -190,7 +190,7 @@ class Instrumentation {
final
double
timeStampDelta
=
1000.0
*
offset
.
distance
/
(
kMoveCount
*
velocity
);
double
timeStamp
=
0.0
;
dispatchEvent
(
p
.
down
(
startLocation
,
timeStamp:
new
Duration
(
milliseconds:
timeStamp
.
round
())),
result
);
for
(
int
i
=
0
;
i
<
kMoveCount
;
i
++)
{
for
(
int
i
=
0
;
i
<
=
kMoveCount
;
i
++)
{
final
Point
location
=
startLocation
+
Offset
.
lerp
(
Offset
.
zero
,
offset
,
i
/
kMoveCount
);
dispatchEvent
(
p
.
move
(
location
,
timeStamp:
new
Duration
(
milliseconds:
timeStamp
.
round
())),
result
);
timeStamp
+=
timeStampDelta
;
...
...
Write
Preview
Markdown
is supported
0%
Try again
or
attach a new file
Attach a file
Cancel
You are about to add
0
people
to the discussion. Proceed with caution.
Finish editing this message first!
Cancel
Please
register
or
sign in
to comment