Commit b3780ebc authored by Adam Barth's avatar Adam Barth Committed by GitHub

Fix asserts in Draggable dismounting (#4644)

In writing a test for #1927, I found a number of bugs in how Draggable shuts
down. Previously it would leak its recongizer. Now it disposes its recognizer
and the recognizer knows how to be disposed cleanly.

Fixes #1927
parent cff31a3f
......@@ -5,6 +5,8 @@
import 'dart:async';
import 'dart:ui' show Point, Offset;
import 'package:meta/meta.dart';
import 'arena.dart';
import 'binding.dart';
import 'constants.dart';
......@@ -147,7 +149,9 @@ abstract class MultiDragPointerState {
}
/// Releases any resources used by the object.
@mustCallSuper
void dispose() {
_arenaEntry?.resolve(GestureDisposition.rejected);
assert(() { _pendingDelta = null; return true; });
}
}
......@@ -258,14 +262,14 @@ abstract class MultiDragGestureRecognizer<T extends MultiDragPointerState> exten
assert(_pointers != null);
assert(_pointers.containsKey(pointer));
GestureBinding.instance.pointerRouter.removeRoute(pointer, _handleEvent);
_pointers[pointer].dispose();
_pointers.remove(pointer);
_pointers.remove(pointer).dispose();
}
@override
void dispose() {
for (int pointer in _pointers.keys)
for (int pointer in _pointers.keys.toList())
_removeState(pointer);
assert(_pointers.isEmpty);
_pointers = null;
super.dispose();
}
......
......@@ -262,6 +262,12 @@ class _DraggableState<T> extends State<DraggableBase<T>> {
_recognizer = config.createRecognizer(_startDrag);
}
@override
void dispose() {
_recognizer.dispose();
super.dispose();
}
GestureRecognizer _recognizer;
int _activeCount = 0;
......
......@@ -871,6 +871,75 @@ void main() {
await tester.pump();
}
});
testWidgets('Draggable disposes recognizer', (WidgetTester tester) async {
bool didTap = false;
await tester.pumpWidget(new Overlay(
initialEntries: <OverlayEntry>[
new OverlayEntry(
builder: (BuildContext context) => new GestureDetector(
onTap: () {
didTap = true;
},
child: new Draggable<dynamic>(
child: new Container(
decoration: new BoxDecoration(
backgroundColor: new Color(0xFFFFFF00)
)
),
feedback: new Container(
width: 100.0,
height: 100.0,
decoration: new BoxDecoration(
backgroundColor: new Color(0xFFFF0000)
)
)
)
)
)
]
));
await tester.startGesture(const Point(10.0, 10.0));
expect(didTap, isFalse);
// This tears down the draggable without terminating the gesture sequence,
// which used to trigger asserts in the multi-drag gesture recognizer.
await tester.pumpWidget(new Container(key: new UniqueKey()));
expect(didTap, isFalse);
});
testWidgets('Draggable plays nice with onTap', (WidgetTester tester) async {
await tester.pumpWidget(new Overlay(
initialEntries: <OverlayEntry>[
new OverlayEntry(
builder: (BuildContext context) => new GestureDetector(
onTap: () { /* registers a tap recognizer */ },
child: new Draggable<dynamic>(
child: new Container(
decoration: new BoxDecoration(
backgroundColor: new Color(0xFFFFFF00)
)
),
feedback: new Container(
width: 100.0,
height: 100.0,
decoration: new BoxDecoration(
backgroundColor: new Color(0xFFFF0000)
)
)
)
)
)
]
));
TestGesture firstGesture = await tester.startGesture(const Point(10.0, 10.0), pointer: 24);
TestGesture secondGesture = await tester.startGesture(const Point(10.0, 20.0), pointer: 25);
await firstGesture.moveBy(new Offset(100.0, 0.0));
await secondGesture.up();
});
}
class DragTargetData { }
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment