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
149a1957
Unverified
Commit
149a1957
authored
Dec 21, 2020
by
xubaolin
Committed by
GitHub
Dec 21, 2020
Browse files
Options
Browse Files
Download
Email Patches
Plain Diff
fix a DragTarget type cast bug (#72512)
parent
b7932853
Changes
2
Hide whitespace changes
Inline
Side-by-side
Showing
2 changed files
with
140 additions
and
14 deletions
+140
-14
drag_target.dart
packages/flutter/lib/src/widgets/drag_target.dart
+25
-14
draggable_test.dart
packages/flutter/test/widgets/draggable_test.dart
+115
-0
No files found.
packages/flutter/lib/src/widgets/drag_target.dart
View file @
149a1957
...
@@ -2,6 +2,7 @@
...
@@ -2,6 +2,7 @@
// Use of this source code is governed by a BSD-style license that can be
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
// found in the LICENSE file.
import
'package:flutter/foundation.dart'
show
kIsWeb
;
import
'package:flutter/gestures.dart'
;
import
'package:flutter/gestures.dart'
;
import
'package:flutter/rendering.dart'
;
import
'package:flutter/rendering.dart'
;
import
'package:flutter/services.dart'
;
import
'package:flutter/services.dart'
;
...
@@ -644,18 +645,28 @@ class DragTarget<T extends Object> extends StatefulWidget {
...
@@ -644,18 +645,28 @@ class DragTarget<T extends Object> extends StatefulWidget {
_DragTargetState
<
T
>
createState
()
=>
_DragTargetState
<
T
>();
_DragTargetState
<
T
>
createState
()
=>
_DragTargetState
<
T
>();
}
}
List
<
T
?>
_mapAvatarsToData
<
T
extends
Object
>(
List
<
_DragAvatar
<
T
>>
avatars
)
{
List
<
T
?>
_mapAvatarsToData
<
T
extends
Object
>(
List
<
_DragAvatar
<
Object
>>
avatars
)
{
return
avatars
.
map
<
T
?>((
_DragAvatar
<
T
>
avatar
)
=>
avatar
.
data
).
toList
();
return
avatars
.
map
<
T
?>((
_DragAvatar
<
Object
>
avatar
)
=>
avatar
.
data
as
T
?
).
toList
();
}
}
class
_DragTargetState
<
T
extends
Object
>
extends
State
<
DragTarget
<
T
>>
{
class
_DragTargetState
<
T
extends
Object
>
extends
State
<
DragTarget
<
T
>>
{
final
List
<
_DragAvatar
<
T
>>
_candidateAvatars
=
<
_DragAvatar
<
T
>>[];
final
List
<
_DragAvatar
<
Object
>>
_candidateAvatars
=
<
_DragAvatar
<
Object
>>[];
final
List
<
_DragAvatar
<
Object
>>
_rejectedAvatars
=
<
_DragAvatar
<
Object
>>[];
final
List
<
_DragAvatar
<
Object
>>
_rejectedAvatars
=
<
_DragAvatar
<
Object
>>[];
// On non-web platforms, checks if data Object is equal to type[T] or subtype of [T].
// On web, it does the same, but requires a check for ints and doubles
// because dart doubles and ints are backed by the same kind of object on web.
// JavaScript does not support integers.
bool
isExpectedDataType
(
Object
?
data
,
Type
type
)
{
if
(
kIsWeb
&&
((
type
==
int
&&
T
==
double
)
||
(
type
==
double
&&
T
==
int
)))
return
false
;
return
data
is
T
?;
}
bool
didEnter
(
_DragAvatar
<
Object
>
avatar
)
{
bool
didEnter
(
_DragAvatar
<
Object
>
avatar
)
{
assert
(!
_candidateAvatars
.
contains
(
avatar
));
assert
(!
_candidateAvatars
.
contains
(
avatar
));
assert
(!
_rejectedAvatars
.
contains
(
avatar
));
assert
(!
_rejectedAvatars
.
contains
(
avatar
));
if
(
avatar
is
_DragAvatar
<
T
>
&&
(
widget
.
onWillAccept
==
null
||
widget
.
onWillAccept
!(
avatar
.
data
)
))
{
if
(
widget
.
onWillAccept
==
null
||
widget
.
onWillAccept
!(
avatar
.
data
as
T
?
))
{
setState
(()
{
setState
(()
{
_candidateAvatars
.
add
(
avatar
);
_candidateAvatars
.
add
(
avatar
);
});
});
...
@@ -750,8 +761,8 @@ class _DragAvatar<T extends Object> extends Drag {
...
@@ -750,8 +761,8 @@ class _DragAvatar<T extends Object> extends Drag {
final
OverlayState
overlayState
;
final
OverlayState
overlayState
;
final
bool
ignoringFeedbackSemantics
;
final
bool
ignoringFeedbackSemantics
;
_DragTargetState
<
T
>?
_activeTarget
;
_DragTargetState
<
Object
>?
_activeTarget
;
final
List
<
_DragTargetState
<
T
>>
_enteredTargets
=
<
_DragTargetState
<
T
>>[];
final
List
<
_DragTargetState
<
Object
>>
_enteredTargets
=
<
_DragTargetState
<
Object
>>[];
Offset
_position
;
Offset
_position
;
Offset
?
_lastOffset
;
Offset
?
_lastOffset
;
OverlayEntry
?
_entry
;
OverlayEntry
?
_entry
;
...
@@ -783,12 +794,12 @@ class _DragAvatar<T extends Object> extends Drag {
...
@@ -783,12 +794,12 @@ class _DragAvatar<T extends Object> extends Drag {
final
HitTestResult
result
=
HitTestResult
();
final
HitTestResult
result
=
HitTestResult
();
WidgetsBinding
.
instance
!.
hitTest
(
result
,
globalPosition
+
feedbackOffset
);
WidgetsBinding
.
instance
!.
hitTest
(
result
,
globalPosition
+
feedbackOffset
);
final
List
<
_DragTargetState
<
T
>>
targets
=
_getDragTargets
(
result
.
path
).
toList
();
final
List
<
_DragTargetState
<
Object
>>
targets
=
_getDragTargets
(
result
.
path
).
toList
();
bool
listsMatch
=
false
;
bool
listsMatch
=
false
;
if
(
targets
.
length
>=
_enteredTargets
.
length
&&
_enteredTargets
.
isNotEmpty
)
{
if
(
targets
.
length
>=
_enteredTargets
.
length
&&
_enteredTargets
.
isNotEmpty
)
{
listsMatch
=
true
;
listsMatch
=
true
;
final
Iterator
<
_DragTargetState
<
T
>>
iterator
=
targets
.
iterator
;
final
Iterator
<
_DragTargetState
<
Object
>>
iterator
=
targets
.
iterator
;
for
(
int
i
=
0
;
i
<
_enteredTargets
.
length
;
i
+=
1
)
{
for
(
int
i
=
0
;
i
<
_enteredTargets
.
length
;
i
+=
1
)
{
iterator
.
moveNext
();
iterator
.
moveNext
();
if
(
iterator
.
current
!=
_enteredTargets
[
i
])
{
if
(
iterator
.
current
!=
_enteredTargets
[
i
])
{
...
@@ -800,7 +811,7 @@ class _DragAvatar<T extends Object> extends Drag {
...
@@ -800,7 +811,7 @@ class _DragAvatar<T extends Object> extends Drag {
// If everything's the same, report moves, and bail early.
// If everything's the same, report moves, and bail early.
if
(
listsMatch
)
{
if
(
listsMatch
)
{
for
(
final
_DragTargetState
<
T
>
target
in
_enteredTargets
)
{
for
(
final
_DragTargetState
<
Object
>
target
in
_enteredTargets
)
{
target
.
didMove
(
this
);
target
.
didMove
(
this
);
}
}
return
;
return
;
...
@@ -810,8 +821,8 @@ class _DragAvatar<T extends Object> extends Drag {
...
@@ -810,8 +821,8 @@ class _DragAvatar<T extends Object> extends Drag {
_leaveAllEntered
();
_leaveAllEntered
();
// Enter new targets.
// Enter new targets.
final
_DragTargetState
<
T
>?
newTarget
=
targets
.
cast
<
_DragTargetState
<
T
>?>().
firstWhere
(
final
_DragTargetState
<
Object
>?
newTarget
=
targets
.
cast
<
_DragTargetState
<
Object
>?>().
firstWhere
(
(
_DragTargetState
<
T
>?
target
)
{
(
_DragTargetState
<
Object
>?
target
)
{
if
(
target
==
null
)
if
(
target
==
null
)
return
false
;
return
false
;
_enteredTargets
.
add
(
target
);
_enteredTargets
.
add
(
target
);
...
@@ -821,21 +832,21 @@ class _DragAvatar<T extends Object> extends Drag {
...
@@ -821,21 +832,21 @@ class _DragAvatar<T extends Object> extends Drag {
);
);
// Report moves to the targets.
// Report moves to the targets.
for
(
final
_DragTargetState
<
T
>
target
in
_enteredTargets
)
{
for
(
final
_DragTargetState
<
Object
>
target
in
_enteredTargets
)
{
target
.
didMove
(
this
);
target
.
didMove
(
this
);
}
}
_activeTarget
=
newTarget
;
_activeTarget
=
newTarget
;
}
}
Iterable
<
_DragTargetState
<
T
>>
_getDragTargets
(
Iterable
<
HitTestEntry
>
path
)
sync
*
{
Iterable
<
_DragTargetState
<
Object
>>
_getDragTargets
(
Iterable
<
HitTestEntry
>
path
)
sync
*
{
// Look for the RenderBoxes that corresponds to the hit target (the hit target
// Look for the RenderBoxes that corresponds to the hit target (the hit target
// widgets build RenderMetaData boxes for us for this purpose).
// widgets build RenderMetaData boxes for us for this purpose).
for
(
final
HitTestEntry
entry
in
path
)
{
for
(
final
HitTestEntry
entry
in
path
)
{
final
HitTestTarget
target
=
entry
.
target
;
final
HitTestTarget
target
=
entry
.
target
;
if
(
target
is
RenderMetaData
)
{
if
(
target
is
RenderMetaData
)
{
final
dynamic
metaData
=
target
.
metaData
;
final
dynamic
metaData
=
target
.
metaData
;
if
(
metaData
is
_DragTargetState
<
T
>
)
if
(
metaData
is
_DragTargetState
&&
metaData
.
isExpectedDataType
(
data
,
T
)
)
yield
metaData
;
yield
metaData
;
}
}
}
}
...
...
packages/flutter/test/widgets/draggable_test.dart
View file @
149a1957
...
@@ -2533,6 +2533,121 @@ void main() {
...
@@ -2533,6 +2533,121 @@ void main() {
findsNothing
);
findsNothing
);
});
});
// Regression test for https://github.com/flutter/flutter/issues/72483
testWidgets
(
'Drag and drop - DragTarget<Object> can accept Draggable<int> data'
,
(
WidgetTester
tester
)
async
{
final
List
<
Object
>
accepted
=
<
Object
>[];
await
tester
.
pumpWidget
(
MaterialApp
(
home:
Column
(
children:
<
Widget
>[
const
Draggable
<
int
>(
data:
1
,
child:
Text
(
'Source'
),
feedback:
Text
(
'Dragging'
),
),
DragTarget
<
Object
>(
builder:
(
BuildContext
context
,
List
<
Object
?>
data
,
List
<
dynamic
>
rejects
)
{
return
Container
(
height:
100.0
,
child:
const
Text
(
'Target'
));
},
onAccept:
accepted
.
add
,
),
],
),
));
expect
(
accepted
,
isEmpty
);
final
Offset
firstLocation
=
tester
.
getCenter
(
find
.
text
(
'Source'
));
final
TestGesture
gesture
=
await
tester
.
startGesture
(
firstLocation
,
pointer:
7
);
await
tester
.
pump
();
final
Offset
secondLocation
=
tester
.
getCenter
(
find
.
text
(
'Target'
));
await
gesture
.
moveTo
(
secondLocation
);
await
tester
.
pump
();
await
gesture
.
up
();
await
tester
.
pump
();
expect
(
accepted
,
equals
(<
int
>[
1
]));
});
testWidgets
(
'Drag and drop - DragTarget<int> can accept Draggable<Object> data when runtime type is int'
,
(
WidgetTester
tester
)
async
{
final
List
<
int
>
accepted
=
<
int
>[];
await
tester
.
pumpWidget
(
MaterialApp
(
home:
Column
(
children:
<
Widget
>[
const
Draggable
<
Object
>(
data:
1
,
child:
Text
(
'Source'
),
feedback:
Text
(
'Dragging'
),
),
DragTarget
<
int
>(
builder:
(
BuildContext
context
,
List
<
int
?>
data
,
List
<
dynamic
>
rejects
)
{
return
Container
(
height:
100.0
,
child:
const
Text
(
'Target'
));
},
onAccept:
accepted
.
add
,
),
],
),
));
expect
(
accepted
,
isEmpty
);
final
Offset
firstLocation
=
tester
.
getCenter
(
find
.
text
(
'Source'
));
final
TestGesture
gesture
=
await
tester
.
startGesture
(
firstLocation
,
pointer:
7
);
await
tester
.
pump
();
final
Offset
secondLocation
=
tester
.
getCenter
(
find
.
text
(
'Target'
));
await
gesture
.
moveTo
(
secondLocation
);
await
tester
.
pump
();
await
gesture
.
up
();
await
tester
.
pump
();
expect
(
accepted
,
equals
(<
int
>[
1
]));
});
testWidgets
(
'Drag and drop - DragTarget<int> should not accept Draggable<Object> data when runtime type null'
,
(
WidgetTester
tester
)
async
{
final
List
<
int
>
accepted
=
<
int
>[];
bool
isReceiveNullDataForCheck
=
false
;
await
tester
.
pumpWidget
(
MaterialApp
(
home:
Column
(
children:
<
Widget
>[
const
Draggable
<
Object
>(
child:
Text
(
'Source'
),
feedback:
Text
(
'Dragging'
),
),
DragTarget
<
int
>(
builder:
(
BuildContext
context
,
List
<
int
?>
data
,
List
<
dynamic
>
rejects
)
{
return
Container
(
height:
100.0
,
child:
const
Text
(
'Target'
));
},
onAccept:
accepted
.
add
,
onWillAccept:
(
int
?
data
)
{
if
(
data
==
null
)
isReceiveNullDataForCheck
=
true
;
return
data
!=
null
;
},
),
],
),
));
expect
(
accepted
,
isEmpty
);
final
Offset
firstLocation
=
tester
.
getCenter
(
find
.
text
(
'Source'
));
final
TestGesture
gesture
=
await
tester
.
startGesture
(
firstLocation
,
pointer:
7
);
await
tester
.
pump
();
final
Offset
secondLocation
=
tester
.
getCenter
(
find
.
text
(
'Target'
));
await
gesture
.
moveTo
(
secondLocation
);
await
tester
.
pump
();
await
gesture
.
up
();
await
tester
.
pump
();
expect
(
accepted
,
isEmpty
);
expect
(
isReceiveNullDataForCheck
,
true
);
});
testWidgets
(
'Drag and drop can contribute semantics'
,
(
WidgetTester
tester
)
async
{
testWidgets
(
'Drag and drop can contribute semantics'
,
(
WidgetTester
tester
)
async
{
final
SemanticsTester
semantics
=
SemanticsTester
(
tester
);
final
SemanticsTester
semantics
=
SemanticsTester
(
tester
);
await
tester
.
pumpWidget
(
MaterialApp
(
await
tester
.
pumpWidget
(
MaterialApp
(
...
...
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