Unverified Commit 6d07b1b4 authored by Ming Lyu (CareF)'s avatar Ming Lyu (CareF) Committed by GitHub

Add WidgetController.scrollUntilVisible (#62097)

parent aa771d50
......@@ -699,6 +699,51 @@ abstract class WidgetController {
///
/// Shorthand for `Scrollable.ensureVisible(element(finder))`
Future<void> ensureVisible(Finder finder) => Scrollable.ensureVisible(element(finder));
/// Repeatedly scrolls the `scrollable` by `delta` in the
/// [Scrpllable.axisDirection] until `finder` is visible.
///
/// Between each scroll, wait for `duration` time for settling.
///
/// Throws a [StateError] if `finder` is not found for maximum `maxScrolls`
/// times.
///
/// This is different from [ensureVisible] in that this allows looking for
/// `finder` that is not built yet, but the caller must specify the scrollable
/// that will build child specified by `finder`.
Future<void> scrollUntilVisible(
Finder finder,
Finder scrollable,
double delta, {
int maxScrolls = 50,
Duration duration = const Duration(milliseconds: 50),
}
) {
assert(maxScrolls > 0);
return TestAsyncUtils.guard<void>(() async {
Offset moveStep;
switch(widget<Scrollable>(scrollable).axisDirection) {
case AxisDirection.up:
moveStep = Offset(0, delta);
break;
case AxisDirection.down:
moveStep = Offset(0, -delta);
break;
case AxisDirection.left:
moveStep = Offset(delta, 0);
break;
case AxisDirection.right:
moveStep = Offset(-delta, 0);
break;
}
while(maxScrolls > 0 && finder.evaluate().isEmpty) {
await drag(scrollable, moveStep);
await pump(duration);
maxScrolls -= 1;
}
await Scrollable.ensureVisible(element(finder));
});
}
}
/// Variant of [WidgetController] that can be used in tests running
......
......@@ -439,4 +439,95 @@ void main() {
expect(find.text('Item 15', skipOffstage: true), findsOneWidget);
},
);
group('scrollUntilVisible: scrolls to make unbuilt widget visible', () {
testWidgets(
'Vertical',
(WidgetTester tester) async {
await tester.pumpWidget(
MaterialApp(
home: Scaffold(
body: ListView.builder(
itemCount: 50,
shrinkWrap: true,
itemBuilder: (BuildContext context, int i) => ListTile(title: Text('Item $i')),
),
),
),
);
// Make sure widget isn't built yet.
expect(find.text('Item 45', skipOffstage: false), findsNothing);
await tester.scrollUntilVisible(
find.text('Item 45', skipOffstage: false),
find.byType(Scrollable),
100,
);
await tester.pumpAndSettle();
// Now the widget is on screen.
expect(find.text('Item 45', skipOffstage: true), findsOneWidget);
},
);
testWidgets(
'Horizontal',
(WidgetTester tester) async {
await tester.pumpWidget(
MaterialApp(
home: Scaffold(
body: ListView.builder(
itemCount: 50,
shrinkWrap: true,
scrollDirection: Axis.horizontal,
// ListTile does not support horizontal list
itemBuilder: (BuildContext context, int i) => Container(child: Text('Item $i')),
),
),
),
);
// Make sure widget isn't built yet.
expect(find.text('Item 45', skipOffstage: false), findsNothing);
await tester.scrollUntilVisible(
find.text('Item 45', skipOffstage: false),
find.byType(Scrollable),
100,
);
await tester.pumpAndSettle();
// Now the widget is on screen.
expect(find.text('Item 45', skipOffstage: true), findsOneWidget);
},
);
testWidgets(
'Fail',
(WidgetTester tester) async {
await tester.pumpWidget(
MaterialApp(
home: Scaffold(
body: ListView.builder(
itemCount: 50,
shrinkWrap: true,
itemBuilder: (BuildContext context, int i) => ListTile(title: Text('Item $i')),
),
),
),
);
try {
await tester.scrollUntilVisible(
find.text('Item 55', skipOffstage: false),
find.byType(Scrollable),
100,
);
} on StateError catch (e) {
expect(e.message, 'No element');
}
},
);
});
}
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