Unverified Commit 9f4b9bfd authored by Kate Lovett's avatar Kate Lovett Committed by GitHub

Apply PrimaryScrollController updates to SingleChildScrollView (#106430)

parent 3022db2b
...@@ -46,6 +46,7 @@ class ScrollMetricsDemoState extends State<ScrollMetricsDemo> { ...@@ -46,6 +46,7 @@ class ScrollMetricsDemoState extends State<ScrollMetricsDemo> {
height: windowSize, height: windowSize,
width: double.infinity, width: double.infinity,
child: const SingleChildScrollView( child: const SingleChildScrollView(
primary: true,
child: FlutterLogo( child: FlutterLogo(
size: 300.0, size: 300.0,
), ),
......
...@@ -142,7 +142,7 @@ class SingleChildScrollView extends StatelessWidget { ...@@ -142,7 +142,7 @@ class SingleChildScrollView extends StatelessWidget {
this.scrollDirection = Axis.vertical, this.scrollDirection = Axis.vertical,
this.reverse = false, this.reverse = false,
this.padding, this.padding,
bool? primary, this.primary,
this.physics, this.physics,
this.controller, this.controller,
this.child, this.child,
...@@ -153,11 +153,12 @@ class SingleChildScrollView extends StatelessWidget { ...@@ -153,11 +153,12 @@ class SingleChildScrollView extends StatelessWidget {
}) : assert(scrollDirection != null), }) : assert(scrollDirection != null),
assert(dragStartBehavior != null), assert(dragStartBehavior != null),
assert(clipBehavior != null), assert(clipBehavior != null),
assert(!(controller != null && (primary ?? false)), assert(
'Primary ScrollViews obtain their ScrollController via inheritance from a PrimaryScrollController widget. ' !(controller != null && (primary ?? false)),
'You cannot both set primary to true and pass an explicit controller.', 'Primary ScrollViews obtain their ScrollController via inheritance '
), 'from a PrimaryScrollController widget. You cannot both set primary to '
primary = primary ?? controller == null && identical(scrollDirection, Axis.vertical); 'true and pass an explicit controller.',
);
/// The axis along which the scroll view scrolls. /// The axis along which the scroll view scrolls.
/// ///
...@@ -195,20 +196,8 @@ class SingleChildScrollView extends StatelessWidget { ...@@ -195,20 +196,8 @@ class SingleChildScrollView extends StatelessWidget {
/// [ScrollController.animateTo]). /// [ScrollController.animateTo]).
final ScrollController? controller; final ScrollController? controller;
/// Whether this is the primary scroll view associated with the parent /// {@macro flutter.widgets.scroll_view.primary}
/// [PrimaryScrollController]. final bool? primary;
///
/// When true, the scroll view is used for default [ScrollAction]s. If a
/// ScrollAction is not handled by an otherwise focused part of the application,
/// the ScrollAction will be evaluated using this scroll view, for example,
/// when executing [Shortcuts] key events like page up and down.
///
/// On iOS, this identifies the scroll view that will scroll to top in
/// response to a tap in the status bar.
///
/// Defaults to true when [scrollDirection] is vertical and [controller] is
/// not specified.
final bool primary;
/// How the scroll view should respond to user input. /// How the scroll view should respond to user input.
/// ///
...@@ -248,9 +237,13 @@ class SingleChildScrollView extends StatelessWidget { ...@@ -248,9 +237,13 @@ class SingleChildScrollView extends StatelessWidget {
if (padding != null) { if (padding != null) {
contents = Padding(padding: padding!, child: contents); contents = Padding(padding: padding!, child: contents);
} }
final ScrollController? scrollController = primary final bool effectivePrimary = primary
?? controller == null && PrimaryScrollController.shouldInherit(context, scrollDirection);
final ScrollController? scrollController = effectivePrimary
? PrimaryScrollController.of(context) ? PrimaryScrollController.of(context)
: controller; : controller;
Widget scrollable = Scrollable( Widget scrollable = Scrollable(
dragStartBehavior: dragStartBehavior, dragStartBehavior: dragStartBehavior,
axisDirection: axisDirection, axisDirection: axisDirection,
...@@ -280,7 +273,9 @@ class SingleChildScrollView extends StatelessWidget { ...@@ -280,7 +273,9 @@ class SingleChildScrollView extends StatelessWidget {
); );
} }
return primary && scrollController != null return effectivePrimary && scrollController != null
// Further descendant ScrollViews will not inherit the same
// PrimaryScrollController
? PrimaryScrollController.none(child: scrollable) ? PrimaryScrollController.none(child: scrollable)
: scrollable; : scrollable;
} }
......
...@@ -1079,6 +1079,7 @@ void main() { ...@@ -1079,6 +1079,7 @@ void main() {
isAlwaysShown: true, isAlwaysShown: true,
controller: scrollController, controller: scrollController,
child: const SingleChildScrollView( child: const SingleChildScrollView(
primary: true,
child: SizedBox(width: 4000.0, height: 4000.0), child: SizedBox(width: 4000.0, height: 4000.0),
), ),
), ),
......
...@@ -34,6 +34,19 @@ class TestScrollController extends ScrollController { ...@@ -34,6 +34,19 @@ class TestScrollController extends ScrollController {
} }
} }
Widget primaryScrollControllerBoilerplate({ required Widget child, required ScrollController controller }) {
return Directionality(
textDirection: TextDirection.ltr,
child: MediaQuery(
data: const MediaQueryData(),
child: PrimaryScrollController(
controller: controller,
child: child,
),
),
);
}
void main() { void main() {
testWidgets('SingleChildScrollView overflow and clipRect test', (WidgetTester tester) async { testWidgets('SingleChildScrollView overflow and clipRect test', (WidgetTester tester) async {
// the test widowSize is Size(800.0, 600.0) // the test widowSize is Size(800.0, 600.0)
...@@ -210,23 +223,41 @@ void main() { ...@@ -210,23 +223,41 @@ void main() {
)); ));
}); });
testWidgets('Vertical SingleChildScrollViews are primary by default', (WidgetTester tester) async { testWidgets('Vertical SingleChildScrollViews are not primary by default', (WidgetTester tester) async {
const SingleChildScrollView view = SingleChildScrollView(); const SingleChildScrollView view = SingleChildScrollView();
expect(view.primary, isTrue); expect(view.primary, isNull);
}); });
testWidgets('Horizontal SingleChildScrollViews are non-primary by default', (WidgetTester tester) async { testWidgets('Horizontal SingleChildScrollViews are not primary by default', (WidgetTester tester) async {
const SingleChildScrollView view = SingleChildScrollView(scrollDirection: Axis.horizontal); const SingleChildScrollView view = SingleChildScrollView(scrollDirection: Axis.horizontal);
expect(view.primary, isFalse); expect(view.primary, isNull);
}); });
testWidgets('SingleChildScrollViews with controllers are non-primary by default', (WidgetTester tester) async { testWidgets('SingleChildScrollViews with controllers are not primary by default', (WidgetTester tester) async {
final SingleChildScrollView view = SingleChildScrollView( final SingleChildScrollView view = SingleChildScrollView(
controller: ScrollController(), controller: ScrollController(),
); );
expect(view.primary, isFalse); expect(view.primary, isNull);
}); });
testWidgets('Vertical SingleChildScrollViews use PrimaryScrollController by default on mobile', (WidgetTester tester) async {
final ScrollController controller = ScrollController();
await tester.pumpWidget(primaryScrollControllerBoilerplate(
child: const SingleChildScrollView(),
controller: controller,
));
expect(controller.hasClients, isTrue);
}, variant: TargetPlatformVariant.mobile());
testWidgets("Vertical SingleChildScrollViews don't use PrimaryScrollController by default on desktop", (WidgetTester tester) async {
final ScrollController controller = ScrollController();
await tester.pumpWidget(primaryScrollControllerBoilerplate(
child: const SingleChildScrollView(),
controller: controller,
));
expect(controller.hasClients, isFalse);
}, variant: TargetPlatformVariant.desktop());
testWidgets('Nested scrollables have a null PrimaryScrollController', (WidgetTester tester) async { testWidgets('Nested scrollables have a null PrimaryScrollController', (WidgetTester tester) async {
const Key innerKey = Key('inner'); const Key innerKey = Key('inner');
final ScrollController primaryScrollController = ScrollController(); final ScrollController primaryScrollController = ScrollController();
......
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