Unverified Commit 826b7046 authored by Daniel Iglesia's avatar Daniel Iglesia Committed by GitHub

Add rootOverlay flag to [Draggable], to put feedback on root [Overlay] (#67375)

parent 727cee6d
...@@ -124,6 +124,7 @@ class Draggable<T extends Object> extends StatefulWidget { ...@@ -124,6 +124,7 @@ class Draggable<T extends Object> extends StatefulWidget {
this.onDragEnd, this.onDragEnd,
this.onDragCompleted, this.onDragCompleted,
this.ignoringFeedbackSemantics = true, this.ignoringFeedbackSemantics = true,
this.rootOverlay = false,
}) : assert(child != null), }) : assert(child != null),
assert(feedback != null), assert(feedback != null),
assert(ignoringFeedbackSemantics != null), assert(ignoringFeedbackSemantics != null),
...@@ -261,6 +262,15 @@ class Draggable<T extends Object> extends StatefulWidget { ...@@ -261,6 +262,15 @@ class Draggable<T extends Object> extends StatefulWidget {
/// the tree (i.e. [State.mounted] is true). /// the tree (i.e. [State.mounted] is true).
final DragEndCallback? onDragEnd; final DragEndCallback? onDragEnd;
/// Whether the feedback widget will be put on the root [Overlay].
///
/// When false, the feedback widget will be put on the closest [Overlay]. When
/// true, the [feedback] widget will be put on the farthest (aka root)
/// [Overlay].
///
/// Defaults to false.
final bool rootOverlay;
/// Creates a gesture recognizer that recognizes the start of the drag. /// Creates a gesture recognizer that recognizes the start of the drag.
/// ///
/// Subclasses can override this function to customize when they start /// Subclasses can override this function to customize when they start
...@@ -390,7 +400,7 @@ class _DraggableState<T extends Object> extends State<Draggable<T>> { ...@@ -390,7 +400,7 @@ class _DraggableState<T extends Object> extends State<Draggable<T>> {
_activeCount += 1; _activeCount += 1;
}); });
final _DragAvatar<T> avatar = _DragAvatar<T>( final _DragAvatar<T> avatar = _DragAvatar<T>(
overlayState: Overlay.of(context, debugRequiredFor: widget)!, overlayState: Overlay.of(context, debugRequiredFor: widget, rootOverlay: widget.rootOverlay)!,
data: widget.data, data: widget.data,
axis: widget.axis, axis: widget.axis,
initialPosition: position, initialPosition: position,
...@@ -427,7 +437,7 @@ class _DraggableState<T extends Object> extends State<Draggable<T>> { ...@@ -427,7 +437,7 @@ class _DraggableState<T extends Object> extends State<Draggable<T>> {
@override @override
Widget build(BuildContext context) { Widget build(BuildContext context) {
assert(Overlay.of(context, debugRequiredFor: widget) != null); assert(Overlay.of(context, debugRequiredFor: widget, rootOverlay: widget.rootOverlay) != null);
final bool canDrag = widget.maxSimultaneousDrags == null || final bool canDrag = widget.maxSimultaneousDrags == null ||
_activeCount < widget.maxSimultaneousDrags!; _activeCount < widget.maxSimultaneousDrags!;
final bool showChild = _activeCount == 0 || widget.childWhenDragging == null; final bool showChild = _activeCount == 0 || widget.childWhenDragging == null;
......
...@@ -2318,6 +2318,70 @@ void main() { ...@@ -2318,6 +2318,70 @@ void main() {
await _testChildAnchorFeedbackPosition(tester: tester, left: 100.0, top: 100.0); await _testChildAnchorFeedbackPosition(tester: tester, left: 100.0, top: 100.0);
}); });
testWidgets('Drag feedback is put on root overlay with [rootOverlay] flag', (WidgetTester tester) async {
final GlobalKey<NavigatorState> rootNavigatorKey = GlobalKey<NavigatorState>();
final GlobalKey<NavigatorState> childNavigatorKey = GlobalKey<NavigatorState>();
// Create a [MaterialApp], with a nested [Navigator], which has the
// [Draggable].
await tester.pumpWidget(MaterialApp(
navigatorKey: rootNavigatorKey,
home: Column(
children: <Widget>[
Container(
height: 200.0,
child: Navigator(
key: childNavigatorKey,
onGenerateRoute: (RouteSettings settings) {
if (settings.name == '/') {
return MaterialPageRoute<void>(
settings: settings,
builder: (BuildContext context) => const Draggable<int>(
data: 1,
child: Text('Source'),
feedback: Text('Dragging'),
rootOverlay: true,
),
);
}
throw UnsupportedError('Unsupported route: $settings');
},
),
),
DragTarget<int>(
builder: (BuildContext context, List<int> data, List<dynamic> rejects) {
return Container(
height: 300.0, child: const Center(child: Text('Target 1')),
);
},
),
],
),
));
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 1'));
await gesture.moveTo(secondLocation);
await tester.pump();
// Expect that the feedback widget is a descendant of the root overlay,
// but not a descendant of the child overlay.
expect(
find.descendant(
of: find.byType(Overlay).first,
matching: find.text('Dragging'),
),
findsOneWidget);
expect(
find.descendant(
of: find.byType(Overlay).last,
matching: find.text('Dragging'),
),
findsNothing);
});
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);
......
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