diff --git a/packages/flutter/lib/src/widgets/drag_target.dart b/packages/flutter/lib/src/widgets/drag_target.dart index 73003de174..defc875918 100644 --- a/packages/flutter/lib/src/widgets/drag_target.dart +++ b/packages/flutter/lib/src/widgets/drag_target.dart @@ -391,7 +391,7 @@ class _DragAvatar extends Drag { final _OnDragEnd onDragEnd; _DragTargetState _activeTarget; - List<_DragTargetState> _lastTargets = <_DragTargetState>[]; + List<_DragTargetState> _enteredTargets = <_DragTargetState>[]; Point _position; Offset _lastOffset; OverlayEntry _entry; @@ -422,12 +422,12 @@ class _DragAvatar extends Drag { List<_DragTargetState> targets = _getDragTargets(result.path).toList(); bool listsMatch = false; - if (targets.length >= _lastTargets.length && _lastTargets.isNotEmpty) { + if (targets.length >= _enteredTargets.length && _enteredTargets.isNotEmpty) { listsMatch = true; Iterator<_DragTargetState> iterator = targets.iterator; - for (int i = 0; i < _lastTargets.length; i += 1) { + for (int i = 0; i < _enteredTargets.length; i += 1) { iterator.moveNext(); - if (iterator.current != _lastTargets[i]) { + if (iterator.current != _enteredTargets[i]) { listsMatch = false; break; } @@ -439,13 +439,11 @@ class _DragAvatar extends Drag { return; // Leave old targets. - for (int i = 0; i < _lastTargets.length; i += 1) - _lastTargets[i].didLeave(data); - _lastTargets.clear(); + _leaveAllEntered(); // Enter new targets. _DragTargetState newTarget = targets.firstWhere((_DragTargetState target) { - _lastTargets.add(target); + _enteredTargets.add(target); return target.didEnter(data); }, orElse: () => null @@ -466,16 +464,20 @@ class _DragAvatar extends Drag { } } + void _leaveAllEntered() { + for (int i = 0; i < _enteredTargets.length; i += 1) + _enteredTargets[i].didLeave(data); + _enteredTargets.clear(); + } + void finish(_DragEndKind endKind, [Velocity velocity]) { bool wasAccepted = false; - if (_activeTarget != null) { - if (endKind == _DragEndKind.dropped && _activeTarget != null) { - _activeTarget.didDrop(data); - wasAccepted = true; - } else { - _activeTarget.didLeave(data); - } + if (endKind == _DragEndKind.dropped && _activeTarget != null) { + _activeTarget.didDrop(data); + wasAccepted = true; + _enteredTargets.remove(_activeTarget); } + _leaveAllEntered(); _activeTarget = null; _entry.remove(); _entry = null; diff --git a/packages/flutter/test/widget/draggable_test.dart b/packages/flutter/test/widget/draggable_test.dart index 2d44518ee2..8b133bb6d0 100644 --- a/packages/flutter/test/widget/draggable_test.dart +++ b/packages/flutter/test/widget/draggable_test.dart @@ -11,30 +11,22 @@ void main() { testWidgets((WidgetTester tester) { List accepted = []; - tester.pumpWidget(new MaterialApp( - routes: { - '/': (BuildContext context) { return new Column( - children: [ - new Draggable( + tester.pumpWidget(new MaterialApp(routes: { + '/': (BuildContext context) { + return new Column(children: [ + new Draggable( data: 1, child: new Text('Source'), - feedback: new Text('Dragging') - ), - new DragTarget( - builder: (BuildContext context, List data, List rejects) { - return new Container( - height: 100.0, - child: new Text('Target') - ); - }, - onAccept: (int data) { - accepted.add(data); - } - ), - ]); - }, - } - )); + feedback: new Text('Dragging')), + new DragTarget(builder: + (BuildContext context, List data, List 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); @@ -74,44 +66,31 @@ void main() { List events = []; Point firstLocation, secondLocation; - tester.pumpWidget(new MaterialApp( - routes: { - '/': (BuildContext context) { return new Column( - children: [ - new Draggable( + tester.pumpWidget(new MaterialApp(routes: { + '/': (BuildContext context) { + return new Column(children: [ + new Draggable( data: 1, child: new Text('Source'), - feedback: new Text('Dragging') - ), - new Stack( - children: [ - new GestureDetector( - behavior: HitTestBehavior.opaque, - onTap: () { - events.add('tap'); - }, - child: new Container( - child: new Text('Button') - ) - ), - new DragTarget( - builder: (BuildContext context, List data, List rejects) { - return new IgnorePointer( - child: new Container( - child: new Text('Target') - ) - ); - }, - onAccept: (int data) { - events.add('drop'); - } - ), - ] - ), - ]); - }, - } - )); + feedback: new Text('Dragging')), + new Stack(children: [ + new GestureDetector( + behavior: HitTestBehavior.opaque, + onTap: () { + events.add('tap'); + }, + child: new Container(child: new Text('Button'))), + new DragTarget(builder: (BuildContext context, + List data, List rejects) { + return new IgnorePointer( + child: new Container(child: new Text('Target'))); + }, onAccept: (int data) { + events.add('drop'); + }), + ]), + ]); + }, + })); expect(events, isEmpty); expect(tester.findText('Source'), isNotNull); @@ -172,35 +151,27 @@ void main() { List events = []; Point firstLocation, secondLocation; - tester.pumpWidget(new MaterialApp( - routes: { - '/': (BuildContext context) { return new Column( - children: [ - new Draggable( + tester.pumpWidget(new MaterialApp(routes: { + '/': (BuildContext context) { + return new Column(children: [ + new Draggable( data: 1, child: new GestureDetector( - behavior: HitTestBehavior.opaque, - onTap: () { - events.add('tap'); - }, - child: new Container( - child: new Text('Button') - ) - ), - feedback: new Text('Dragging') - ), - new DragTarget( - builder: (BuildContext context, List data, List rejects) { - return new Text('Target'); - }, - onAccept: (int data) { - events.add('drop'); - } - ), - ]); - }, - } - )); + behavior: HitTestBehavior.opaque, + onTap: () { + events.add('tap'); + }, + child: new Container(child: new Text('Button'))), + feedback: new Text('Dragging')), + new DragTarget(builder: + (BuildContext context, List data, List rejects) { + return new Text('Target'); + }, onAccept: (int data) { + events.add('drop'); + }), + ]); + }, + })); expect(events, isEmpty); expect(tester.findText('Button'), isNotNull); @@ -224,7 +195,6 @@ void main() { tester.pump(); expect(events, equals(['drop'])); events.clear(); - }); }); @@ -233,27 +203,22 @@ void main() { List events = []; Point firstLocation, secondLocation; - tester.pumpWidget(new MaterialApp( - routes: { - '/': (BuildContext context) { return new Column( - children: [ - new LongPressDraggable( + tester.pumpWidget(new MaterialApp(routes: { + '/': (BuildContext context) { + return new Column(children: [ + new LongPressDraggable( data: 1, child: new Text('Source'), - feedback: new Text('Dragging') - ), - new DragTarget( - builder: (BuildContext context, List data, List rejects) { - return new Text('Target'); - }, - onAccept: (int data) { - events.add('drop'); - } - ), - ]); - }, - } - )); + feedback: new Text('Dragging')), + new DragTarget(builder: + (BuildContext context, List data, List rejects) { + return new Text('Target'); + }, onAccept: (int data) { + events.add('drop'); + }), + ]); + }, + })); expect(events, isEmpty); expect(tester.findText('Source'), isNotNull); @@ -275,7 +240,6 @@ void main() { gesture.up(); tester.pump(); expect(events, isEmpty); - }); }); @@ -284,27 +248,22 @@ void main() { List events = []; Point firstLocation, secondLocation; - tester.pumpWidget(new MaterialApp( - routes: { - '/': (BuildContext context) { return new Column( - children: [ - new Draggable( + tester.pumpWidget(new MaterialApp(routes: { + '/': (BuildContext context) { + return new Column(children: [ + new Draggable( data: 1, child: new Text('Source'), - feedback: new Text('Dragging') - ), - new DragTarget( - builder: (BuildContext context, List data, List rejects) { - return new Text('Target'); - }, - onAccept: (int data) { - events.add('drop'); - } - ), - ]); - }, - } - )); + feedback: new Text('Dragging')), + new DragTarget(builder: + (BuildContext context, List data, List rejects) { + return new Text('Target'); + }, onAccept: (int data) { + events.add('drop'); + }), + ]); + }, + })); expect(events, isEmpty); expect(tester.findText('Source'), isNotNull); @@ -331,44 +290,33 @@ void main() { }); }); - test('Drag and drop - horizontal and vertical draggables in vertical block', () { + test('Drag and drop - horizontal and vertical draggables in vertical block', + () { testWidgets((WidgetTester tester) { List events = []; Point firstLocation, secondLocation, thirdLocation; - tester.pumpWidget(new MaterialApp( - routes: { - '/': (BuildContext context) { - return new Block( - children: [ - new DragTarget( - builder: (BuildContext context, List data, List rejects) { - return new Text('Target'); - }, - onAccept: (int data) { - events.add('drop $data'); - } - ), - new Container(height: 400.0), - new HorizontalDraggable( - data: 1, - child: new Text('H'), - feedback: new Text('Dragging') - ), - new VerticalDraggable( - data: 2, - child: new Text('V'), - feedback: new Text('Dragging') - ), - new Container(height: 500.0), - new Container(height: 500.0), - new Container(height: 500.0), - new Container(height: 500.0), - ] - ); - }, - } - )); + tester.pumpWidget(new MaterialApp(routes: { + '/': (BuildContext context) { + return new Block(children: [ + new DragTarget(builder: + (BuildContext context, List data, List rejects) { + return new Text('Target'); + }, onAccept: (int data) { + events.add('drop $data'); + }), + new Container(height: 400.0), + new HorizontalDraggable( + data: 1, child: new Text('H'), feedback: new Text('Dragging')), + new VerticalDraggable( + data: 2, child: new Text('V'), feedback: new Text('Dragging')), + new Container(height: 500.0), + new Container(height: 500.0), + new Container(height: 500.0), + new Container(height: 500.0), + ]); + }, + })); expect(events, isEmpty); expect(tester.findText('Target'), isNotNull); @@ -438,49 +386,36 @@ void main() { expect(events, equals([])); expect(tester.getCenter(tester.findText('Target')).y, lessThan(0.0)); events.clear(); - }); }); - test('Drag and drop - horizontal and vertical draggables in horizontal block', () { + test('Drag and drop - horizontal and vertical draggables in horizontal block', + () { testWidgets((WidgetTester tester) { List events = []; Point firstLocation, secondLocation, thirdLocation; - tester.pumpWidget(new MaterialApp( - routes: { - '/': (BuildContext context) { - return new Block( - scrollDirection: Axis.horizontal, - children: [ - new DragTarget( - builder: (BuildContext context, List data, List rejects) { - return new Text('Target'); - }, - onAccept: (int data) { - events.add('drop $data'); - } - ), - new Container(width: 400.0), - new HorizontalDraggable( - data: 1, - child: new Text('H'), - feedback: new Text('Dragging') - ), - new VerticalDraggable( - data: 2, - child: new Text('V'), - feedback: new Text('Dragging') - ), - new Container(width: 500.0), - new Container(width: 500.0), - new Container(width: 500.0), - new Container(width: 500.0), - ] - ); - }, - } - )); + tester.pumpWidget(new MaterialApp(routes: { + '/': (BuildContext context) { + return new Block(scrollDirection: Axis.horizontal, children: [ + new DragTarget(builder: + (BuildContext context, List data, List rejects) { + return new Text('Target'); + }, onAccept: (int data) { + events.add('drop $data'); + }), + new Container(width: 400.0), + new HorizontalDraggable( + data: 1, child: new Text('H'), feedback: new Text('Dragging')), + new VerticalDraggable( + data: 2, child: new Text('V'), feedback: new Text('Dragging')), + new Container(width: 500.0), + new Container(width: 500.0), + new Container(width: 500.0), + new Container(width: 500.0), + ]); + }, + })); expect(events, isEmpty); expect(tester.findText('Target'), isNotNull); @@ -550,42 +485,35 @@ void main() { expect(events, equals([])); expect(tester.getCenter(tester.findText('Target')).x, lessThan(0.0)); events.clear(); - }); }); - test('Drag and drop - onDraggableDropped not called if dropped on accepting target', () { + test( + 'Drag and drop - onDraggableDropped not called if dropped on accepting target', + () { testWidgets((WidgetTester tester) { List accepted = []; bool onDraggableCanceledCalled = false; - tester.pumpWidget(new MaterialApp( - routes: { - '/': (BuildContext context) { return new Column( - children: [ - new Draggable( + tester.pumpWidget(new MaterialApp(routes: { + '/': (BuildContext context) { + return new Column(children: [ + new Draggable( data: 1, child: new Text('Source'), feedback: new Text('Dragging'), onDraggableCanceled: (Velocity velocity, Offset offset) { onDraggableCanceledCalled = true; - } - ), - new DragTarget( - builder: (BuildContext context, List data, List rejects) { - return new Container( - height: 100.0, - child: new Text('Target') - ); - }, - onAccept: (int data) { - accepted.add(data); - } - ), - ]); - }, - } - )); + }), + new DragTarget(builder: + (BuildContext context, List data, List 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); @@ -624,18 +552,19 @@ void main() { }); }); - test('Drag and drop - onDraggableDropped called if dropped on non-accepting target', () { + test( + 'Drag and drop - onDraggableDropped called if dropped on non-accepting target', + () { testWidgets((WidgetTester tester) { List accepted = []; bool onDraggableCanceledCalled = false; Velocity onDraggableCanceledVelocity; Offset onDraggableCanceledOffset; - tester.pumpWidget(new MaterialApp( - routes: { - '/': (BuildContext context) { return new Column( - children: [ - new Draggable( + tester.pumpWidget(new MaterialApp(routes: { + '/': (BuildContext context) { + return new Column(children: [ + new Draggable( data: 1, child: new Text('Source'), feedback: new Text('Dragging'), @@ -643,21 +572,17 @@ void main() { onDraggableCanceledCalled = true; onDraggableCanceledVelocity = velocity; onDraggableCanceledOffset = offset; - } - ), - new DragTarget( - builder: (BuildContext context, List data, List rejects) { + }), + new DragTarget( + builder: (BuildContext context, List data, + List rejects) { return new Container( - height: 100.0, - child: new Text('Target') - ); + height: 100.0, child: new Text('Target')); }, - onWillAccept: (int data) => false - ), - ]); - }, - } - )); + onWillAccept: (int data) => false), + ]); + }, + })); expect(accepted, isEmpty); expect(tester.findText('Source'), isNotNull); @@ -694,22 +619,24 @@ void main() { expect(tester.findText('Target'), isNotNull); expect(onDraggableCanceledCalled, isTrue); expect(onDraggableCanceledVelocity, equals(Velocity.zero)); - expect(onDraggableCanceledOffset, equals(new Offset(secondLocation.x, secondLocation.y))); + expect(onDraggableCanceledOffset, + equals(new Offset(secondLocation.x, secondLocation.y))); }); }); - test('Drag and drop - onDraggableDropped called if dropped on non-accepting target with correct velocity', () { + test( + 'Drag and drop - onDraggableDropped called if dropped on non-accepting target with correct velocity', + () { testWidgets((WidgetTester tester) { List accepted = []; bool onDraggableCanceledCalled = false; Velocity onDraggableCanceledVelocity; Offset onDraggableCanceledOffset; - tester.pumpWidget(new MaterialApp( - routes: { - '/': (BuildContext context) { return new Column( - children: [ - new Draggable( + tester.pumpWidget(new MaterialApp(routes: { + '/': (BuildContext context) { + return new Column(children: [ + new Draggable( data: 1, child: new Text('Source'), feedback: new Text('Source'), @@ -717,21 +644,17 @@ void main() { onDraggableCanceledCalled = true; onDraggableCanceledVelocity = velocity; onDraggableCanceledOffset = offset; - } - ), - new DragTarget( - builder: (BuildContext context, List data, List rejects) { + }), + new DragTarget( + builder: (BuildContext context, List data, + List rejects) { return new Container( - height: 100.0, - child: new Text('Target') - ); + height: 100.0, child: new Text('Target')); }, - onWillAccept: (int data) => false - ), - ]); - }, - } - )); + onWillAccept: (int data) => false), + ]); + }, + })); expect(accepted, isEmpty); expect(tester.findText('Source'), isNotNull); @@ -740,7 +663,7 @@ void main() { expect(onDraggableCanceledCalled, isFalse); Point flingStart = tester.getTopLeft(tester.findText('Source')); - tester.flingFrom(flingStart, new Offset(0.0,100.0), 1000.0); + tester.flingFrom(flingStart, new Offset(0.0, 100.0), 1000.0); tester.pump(); expect(accepted, isEmpty); @@ -748,9 +671,14 @@ void main() { 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))); + 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))); }); }); @@ -759,52 +687,36 @@ void main() { List acceptedInts = []; List acceptedDoubles = []; - tester.pumpWidget(new MaterialApp( - routes: { - '/': (BuildContext context) { return new Column( - children: [ - new Draggable( + tester.pumpWidget(new MaterialApp(routes: { + '/': (BuildContext context) { + return new Column(children: [ + new Draggable( data: 1, child: new Text('IntSource'), - feedback: new Text('IntDragging') - ), - new Draggable( + feedback: new Text('IntDragging')), + new Draggable( data: 1.0, child: new Text('DoubleSource'), - feedback: new Text('DoubleDragging') - ), - new Stack(children:[ - new DragTarget( - builder: (BuildContext context, List data, List rejects) { - return new IgnorePointer( - child: new Container( - height: 100.0, - child: new Text('Target1') - ) - ); - }, - onAccept: (int data) { - acceptedInts.add(data); - } - ), - new DragTarget( - builder: (BuildContext context, List data, List rejects) { - return new IgnorePointer( - child: new Container( - height: 100.0, - child: new Text('Target2') - ) - ); - }, - onAccept: (double data) { - acceptedDoubles.add(data); - } - ), - ]) - ]); - }, - } - )); + feedback: new Text('DoubleDragging')), + new Stack(children: [ + new DragTarget(builder: (BuildContext context, + List data, List rejects) { + return new IgnorePointer(child: new Container( + height: 100.0, child: new Text('Target1'))); + }, onAccept: (int data) { + acceptedInts.add(data); + }), + new DragTarget(builder: (BuildContext context, + List data, List rejects) { + return new IgnorePointer(child: new Container( + height: 100.0, child: new Text('Target2'))); + }, onAccept: (double data) { + acceptedDoubles.add(data); + }), + ]) + ]); + }, + })); expect(acceptedInts, isEmpty); expect(acceptedDoubles, isEmpty); @@ -820,7 +732,8 @@ void main() { Point targetLocation = tester.getCenter(tester.findText('Target1')); // Drag the double draggable. - TestGesture doubleGesture = tester.startGesture(doubleLocation, pointer: 7); + TestGesture doubleGesture = + tester.startGesture(doubleLocation, pointer: 7); tester.pump(); expect(acceptedInts, isEmpty); @@ -872,4 +785,62 @@ void main() { expect(tester.findText('DoubleDragging'), isNull); }); }); + + test('Drag and drop - allow pass thru of unaccepted data twice test', () { + testWidgets((WidgetTester tester) { + List acceptedDragTargetDatas = []; + List acceptedExtendedDragTargetDatas = []; + DragTargetData dragTargetData = new DragTargetData(); + tester.pumpWidget(new MaterialApp(routes: { + '/': (BuildContext context) { + return new Column(children: [ + new Draggable( + data: dragTargetData, + child: new Text('Source'), + feedback: new Text('Dragging')), + new Stack(children: [ + new DragTarget(builder: (BuildContext context, + List data, List rejects) { + return new IgnorePointer(child: new Container( + height: 100.0, child: new Text('Target1'))); + }, onAccept: (DragTargetData data) { + acceptedDragTargetDatas.add(data); + }), + new DragTarget(builder: (BuildContext context, + List data, List rejects) { + return new IgnorePointer(child: new Container( + height: 100.0, child: new Text('Target2'))); + }, onAccept: (ExtendedDragTargetData data) { + acceptedExtendedDragTargetDatas.add(data); + }), + ]) + ]); + }, + })); + + Point dragTargetLocation = tester.getCenter(tester.findText('Source')); + Point targetLocation = tester.getCenter(tester.findText('Target1')); + + for (int i = 0; i < 2; i += 1) { + TestGesture gesture = tester.startGesture(dragTargetLocation); + tester.pump(); + gesture.moveTo(targetLocation); + tester.pump(); + gesture.up(); + tester.pump(); + + expect(acceptedDragTargetDatas, equals([dragTargetData])); + expect(acceptedExtendedDragTargetDatas, isEmpty); + + acceptedDragTargetDatas.clear(); + tester.pump(); + } + }); + }); +} + +class DragTargetData { +} + +class ExtendedDragTargetData extends DragTargetData { }