Fix docs and error messages for scroll directions + sample code (#123819)

Fix docs and error messages for scroll directions + sample code
diff --git a/examples/api/lib/painting/axis_direction/axis_direction.0.dart b/examples/api/lib/painting/axis_direction/axis_direction.0.dart
new file mode 100644
index 0000000..8a4e1f2
--- /dev/null
+++ b/examples/api/lib/painting/axis_direction/axis_direction.0.dart
@@ -0,0 +1,191 @@
+// Copyright 2014 The Flutter Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+// Flutter code sample for [AxisDirection]s.
+
+import 'package:flutter/material.dart';
+
+void main() => runApp(const ExampleApp());
+
+class ExampleApp extends StatelessWidget {
+  const ExampleApp({super.key});
+
+  static const String _title = 'Flutter Code Sample';
+
+  @override
+  Widget build(BuildContext context) {
+    return const MaterialApp(
+      title: _title,
+      home: MyWidget(),
+    );
+  }
+}
+
+class MyWidget extends StatefulWidget {
+  const MyWidget({ super.key });
+
+  @override
+  State<MyWidget> createState() => _MyWidgetState();
+}
+
+class _MyWidgetState extends State<MyWidget> {
+  final List<String> _alphabet = <String>[
+    'A', 'B', 'C', 'D', 'E', 'F', 'G', 'H', 'I', 'J', 'K', 'L', 'M', 'N', 'O',
+    'P', 'Q', 'R', 'S', 'T', 'U', 'V', 'W', 'X', 'Y', 'Z',
+  ];
+  final Widget _spacer = const SizedBox.square(dimension: 10);
+  AxisDirection _axisDirection = AxisDirection.down;
+
+  Widget _getArrows() {
+    final Widget arrow;
+    switch(_axisDirection) {
+      case AxisDirection.up:
+        arrow = const Icon(Icons.arrow_upward_rounded);
+        return Row(
+          mainAxisAlignment: MainAxisAlignment.center,
+          children: <Widget>[ arrow, arrow ],
+        );
+      case AxisDirection.down:
+        arrow = const Icon(Icons.arrow_downward_rounded);
+        return Row(
+          mainAxisAlignment: MainAxisAlignment.center,
+          children: <Widget>[ arrow, arrow ],
+        );
+      case AxisDirection.left:
+        arrow = const Icon(Icons.arrow_back_rounded);
+        return Column(
+          mainAxisAlignment: MainAxisAlignment.center,
+          children: <Widget>[ arrow, arrow ],
+        );
+      case AxisDirection.right:
+        arrow = const Icon(Icons.arrow_forward_rounded);
+        return Column(
+          mainAxisAlignment: MainAxisAlignment.center,
+          children: <Widget>[ arrow, arrow ],
+        );
+    }
+  }
+
+  void _onAxisDirectionChanged(AxisDirection? axisDirection) {
+    if (axisDirection != null && axisDirection != _axisDirection) {
+      setState(() {
+        // Respond to change in axis direction.
+        _axisDirection = axisDirection;
+      });
+    }
+  }
+
+  Widget _getLeading() {
+    return Container(
+      color: Colors.blue[100],
+      padding: const EdgeInsets.all(8.0),
+      child: Column(
+        mainAxisAlignment: MainAxisAlignment.spaceBetween,
+        children: <Widget>[
+          Text(axisDirectionToAxis(_axisDirection).toString()),
+          _spacer,
+          Text(_axisDirection.toString()),
+          _spacer,
+          const Text('GrowthDirection.forward'),
+          _spacer,
+          _getArrows(),
+        ],
+      ),
+    );
+  }
+
+  Widget _getRadioRow() {
+    return DefaultTextStyle(
+      style: const TextStyle(fontWeight: FontWeight.bold, color: Colors.white),
+      child: RadioTheme(
+        data: RadioThemeData(
+          fillColor: MaterialStateProperty.all<Color>(Colors.white),
+        ),
+        child: Padding(
+          padding: const EdgeInsets.all(8.0),
+          child: Row(
+            mainAxisAlignment: MainAxisAlignment.spaceAround,
+            children: <Widget>[
+              Radio<AxisDirection>(
+                value: AxisDirection.up,
+                groupValue: _axisDirection,
+                onChanged: _onAxisDirectionChanged,
+              ),
+              const Text('up'),
+              _spacer,
+              Radio<AxisDirection>(
+                value: AxisDirection.down,
+                groupValue: _axisDirection,
+                onChanged: _onAxisDirectionChanged,
+              ),
+              const Text('down'),
+              _spacer,
+              Radio<AxisDirection>(
+                value: AxisDirection.left,
+                groupValue: _axisDirection,
+                onChanged: _onAxisDirectionChanged,
+              ),
+              const Text('left'),
+              _spacer,
+              Radio<AxisDirection>(
+                value: AxisDirection.right,
+                groupValue: _axisDirection,
+                onChanged: _onAxisDirectionChanged,
+              ),
+              const Text('right'),
+              _spacer,
+            ],
+          ),
+        ),
+      ),
+    );
+  }
+
+  @override
+  Widget build(BuildContext context) {
+    return Scaffold(
+      appBar: AppBar(
+        title: const Text('AxisDirections'),
+        bottom: PreferredSize(
+          preferredSize: const Size.fromHeight(50),
+          child: Padding(
+            padding: const EdgeInsets.all(8.0),
+            child: _getRadioRow(),
+          ),
+        ),
+      ),
+      // Also works for ListView.builder, which creates a SliverList for itself.
+      // A CustomScrollView allows multiple slivers to be composed together.
+      body: CustomScrollView(
+        // This method is available to conveniently determine if an scroll
+        // view is reversed by its AxisDirection.
+        reverse: axisDirectionIsReversed(_axisDirection),
+        // This method is available to conveniently convert an AxisDirection
+        // into its Axis.
+        scrollDirection: axisDirectionToAxis(_axisDirection),
+        slivers: <Widget>[
+          SliverList.builder(
+            itemCount: 27,
+            itemBuilder: (BuildContext context, int index) {
+              final Widget child;
+              if (index == 0) {
+                child = _getLeading();
+              } else {
+                child = Container(
+                  color: index.isEven ? Colors.amber[100] : Colors.amberAccent,
+                  padding: const EdgeInsets.all(8.0),
+                  child: Center(child: Text(_alphabet[index - 1])),
+                );
+              }
+              return Padding(
+                padding: const EdgeInsets.all(8.0),
+                child: child,
+              );
+            }
+          ),
+        ],
+      ),
+    );
+  }
+}
diff --git a/examples/api/lib/rendering/growth_direction/growth_direction.0.dart b/examples/api/lib/rendering/growth_direction/growth_direction.0.dart
new file mode 100644
index 0000000..b58c9fc
--- /dev/null
+++ b/examples/api/lib/rendering/growth_direction/growth_direction.0.dart
@@ -0,0 +1,225 @@
+// Copyright 2014 The Flutter Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+// Flutter code sample for [GrowthDirection]s.
+
+import 'package:flutter/material.dart';
+import 'package:flutter/rendering.dart';
+
+void main() => runApp(const ExampleApp());
+
+class ExampleApp extends StatelessWidget {
+  const ExampleApp({super.key});
+
+  static const String _title = 'Flutter Code Sample';
+
+  @override
+  Widget build(BuildContext context) {
+    return const MaterialApp(
+      title: _title,
+      home: MyWidget(),
+    );
+  }
+}
+
+class MyWidget extends StatefulWidget {
+  const MyWidget({ super.key });
+
+  @override
+  State<MyWidget> createState() => _MyWidgetState();
+}
+
+class _MyWidgetState extends State<MyWidget> {
+  final List<String> _alphabet = <String>[
+    'A', 'B', 'C', 'D', 'E', 'F', 'G', 'H', 'I', 'J', 'K', 'L', 'M', 'N', 'O',
+    'P', 'Q', 'R', 'S', 'T', 'U', 'V', 'W', 'X', 'Y', 'Z',
+  ];
+  final Widget _spacer = const SizedBox.square(dimension: 10);
+  final UniqueKey _center = UniqueKey();
+  AxisDirection _axisDirection = AxisDirection.down;
+
+  Widget _getArrows(AxisDirection axisDirection) {
+    final Widget arrow;
+    switch(axisDirection) {
+      case AxisDirection.up:
+        arrow = const Icon(Icons.arrow_upward_rounded);
+        return Row(
+          mainAxisAlignment: MainAxisAlignment.center,
+          children: <Widget>[ arrow, arrow ],
+        );
+      case AxisDirection.down:
+        arrow = const Icon(Icons.arrow_downward_rounded);
+        return Row(
+          mainAxisAlignment: MainAxisAlignment.center,
+          children: <Widget>[ arrow, arrow ],
+        );
+      case AxisDirection.left:
+        arrow = const Icon(Icons.arrow_back_rounded);
+        return Column(
+          mainAxisAlignment: MainAxisAlignment.center,
+          children: <Widget>[ arrow, arrow ],
+        );
+      case AxisDirection.right:
+        arrow = const Icon(Icons.arrow_forward_rounded);
+        return Column(
+          mainAxisAlignment: MainAxisAlignment.center,
+          children: <Widget>[ arrow, arrow ],
+        );
+    }
+  }
+
+  void _onAxisDirectionChanged(AxisDirection? axisDirection) {
+    if (axisDirection != null && axisDirection != _axisDirection) {
+      setState(() {
+        // Respond to change in axis direction.
+        _axisDirection = axisDirection;
+      });
+    }
+  }
+
+  Widget _getLeading(SliverConstraints constraints, bool isForward) {
+    return Container(
+      color: isForward ? Colors.orange[300] : Colors.green[400],
+      padding: const EdgeInsets.all(8.0),
+      child: Column(
+        mainAxisAlignment: MainAxisAlignment.spaceBetween,
+        children: <Widget>[
+          Text(constraints.axis.toString()),
+          _spacer,
+          Text(constraints.axisDirection.toString()),
+          _spacer,
+          Text(constraints.growthDirection.toString()),
+          _spacer,
+          _getArrows(isForward
+            ? _axisDirection
+            // This method is available to conveniently flip an AxisDirection
+            // into its opposite direction.
+            : flipAxisDirection(_axisDirection),
+          ),
+        ],
+      ),
+    );
+  }
+
+  Widget _getRadioRow() {
+    return DefaultTextStyle(
+      style: const TextStyle(fontWeight: FontWeight.bold, color: Colors.white),
+      child: RadioTheme(
+        data: RadioThemeData(
+          fillColor: MaterialStateProperty.all<Color>(Colors.white),
+        ),
+        child: Padding(
+          padding: const EdgeInsets.all(8.0),
+          child: Row(
+            mainAxisAlignment: MainAxisAlignment.spaceAround,
+            children: <Widget>[
+              Radio<AxisDirection>(
+                value: AxisDirection.up,
+                groupValue: _axisDirection,
+                onChanged: _onAxisDirectionChanged,
+              ),
+              const Text('up'),
+              _spacer,
+              Radio<AxisDirection>(
+                value: AxisDirection.down,
+                groupValue: _axisDirection,
+                onChanged: _onAxisDirectionChanged,
+              ),
+              const Text('down'),
+              _spacer,
+              Radio<AxisDirection>(
+                value: AxisDirection.left,
+                groupValue: _axisDirection,
+                onChanged: _onAxisDirectionChanged,
+              ),
+              const Text('left'),
+              _spacer,
+              Radio<AxisDirection>(
+                value: AxisDirection.right,
+                groupValue: _axisDirection,
+                onChanged: _onAxisDirectionChanged,
+              ),
+              const Text('right'),
+              _spacer,
+            ],
+          ),
+        ),
+      ),
+    );
+  }
+
+  Widget _getList({ required bool isForward }) {
+    // The SliverLayoutBuilder is not necessary, and is here to allow us to see
+    // the SliverConstraints & directional information that is provided to the
+    // SliverList when laying out.
+    return SliverLayoutBuilder(
+      builder: (BuildContext context, SliverConstraints constraints) {
+        return SliverList.builder(
+          itemCount: 27,
+          itemBuilder: (BuildContext context, int index) {
+            final Widget child;
+            if (index == 0) {
+              child = _getLeading(constraints, isForward);
+            } else {
+              child = Container(
+                color: isForward
+                  ? (index.isEven ? Colors.amber[100] : Colors.amberAccent)
+                  : (index.isEven ? Colors.green[100] : Colors.lightGreen),
+                padding: const EdgeInsets.all(8.0),
+                child: Center(child: Text(_alphabet[index - 1])),
+              );
+            }
+            return Padding(
+              padding: const EdgeInsets.all(8.0),
+              child: child,
+            );
+          },
+        );
+      },
+    );
+  }
+
+  @override
+  Widget build(BuildContext context) {
+    return Scaffold(
+      appBar: AppBar(
+        title: const Text('GrowthDirections'),
+        bottom: PreferredSize(
+          preferredSize: const Size.fromHeight(50),
+          child: Padding(
+            padding: const EdgeInsets.all(8.0),
+            child: _getRadioRow(),
+          ),
+        ),
+      ),
+      body: CustomScrollView(
+        // This method is available to conveniently determine if an scroll
+        // view is reversed by its AxisDirection.
+        reverse: axisDirectionIsReversed(_axisDirection),
+        // This method is available to conveniently convert an AxisDirection
+        // into its Axis.
+        scrollDirection: axisDirectionToAxis(_axisDirection),
+        // Places the leading edge of the center sliver in the middle of the
+        // viewport. Changing this value between 0.0 (the default) and 1.0
+        // changes the position of the inflection point between GrowthDirections
+        // in the viewport when the slivers are laid out.
+        anchor: 0.5,
+        center: _center,
+        slivers: <Widget>[
+          _getList(isForward: false),
+          SliverToBoxAdapter(
+            // This sliver will be located at the anchor. The scroll position
+            // will progress in either direction from this point.
+            key: _center,
+            child: const Padding(
+              padding: EdgeInsets.all(8.0),
+              child: Center(child: Text('0', style: TextStyle(fontWeight: FontWeight.bold))),
+            ),
+          ),
+          _getList(isForward: true),
+        ],
+      ),
+    );
+  }
+}
diff --git a/examples/api/lib/rendering/scroll_direction/scroll_direction.0.dart b/examples/api/lib/rendering/scroll_direction/scroll_direction.0.dart
new file mode 100644
index 0000000..211157d
--- /dev/null
+++ b/examples/api/lib/rendering/scroll_direction/scroll_direction.0.dart
@@ -0,0 +1,207 @@
+// Copyright 2014 The Flutter Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+// Flutter code sample for [ScrollDirection].
+
+import 'package:flutter/material.dart';
+import 'package:flutter/rendering.dart';
+
+void main() => runApp(const ExampleApp());
+
+class ExampleApp extends StatelessWidget {
+  const ExampleApp({super.key});
+
+  static const String _title = 'Flutter Code Sample';
+
+  @override
+  Widget build(BuildContext context) {
+    return const MaterialApp(
+      title: _title,
+      home: MyWidget(),
+    );
+  }
+}
+
+class MyWidget extends StatefulWidget {
+  const MyWidget({ super.key });
+
+  @override
+  State<MyWidget> createState() => _MyWidgetState();
+}
+
+class _MyWidgetState extends State<MyWidget> {
+  final List<String> alphabet = <String>[
+    'A', 'B', 'C', 'D', 'E', 'F', 'G', 'H', 'I', 'J', 'K', 'L', 'M', 'N', 'O',
+    'P', 'Q', 'R', 'S', 'T', 'U', 'V', 'W', 'X', 'Y', 'Z',
+  ];
+  final Widget spacer = const SizedBox.square(dimension: 10);
+  ScrollDirection scrollDirection = ScrollDirection.idle;
+  AxisDirection _axisDirection = AxisDirection.down;
+
+  Widget _getArrows() {
+    final Widget arrow;
+    switch(_axisDirection) {
+      case AxisDirection.up:
+        arrow = const Icon(Icons.arrow_upward_rounded);
+        return Row(
+          mainAxisAlignment: MainAxisAlignment.center,
+          children: <Widget>[ arrow, arrow ],
+        );
+      case AxisDirection.down:
+        arrow = const Icon(Icons.arrow_downward_rounded);
+        return Row(
+          mainAxisAlignment: MainAxisAlignment.center,
+          children: <Widget>[ arrow, arrow ],
+        );
+      case AxisDirection.left:
+        arrow = const Icon(Icons.arrow_back_rounded);
+        return Column(
+          mainAxisAlignment: MainAxisAlignment.center,
+          children: <Widget>[ arrow, arrow ],
+        );
+      case AxisDirection.right:
+        arrow = const Icon(Icons.arrow_forward_rounded);
+        return Column(
+          mainAxisAlignment: MainAxisAlignment.center,
+          children: <Widget>[ arrow, arrow ],
+        );
+    }
+  }
+
+  void _onAxisDirectionChanged(AxisDirection? axisDirection) {
+    if (axisDirection != null && axisDirection != _axisDirection) {
+      setState(() {
+        // Respond to change in axis direction.
+        _axisDirection = axisDirection;
+      });
+    }
+  }
+
+  Widget _getLeading() {
+    return Container(
+      color: Colors.blue[100],
+      padding: const EdgeInsets.all(8.0),
+      child: Column(
+        mainAxisAlignment: MainAxisAlignment.spaceBetween,
+        children: <Widget>[
+          Text(axisDirectionToAxis(_axisDirection).toString()),
+          spacer,
+          Text(_axisDirection.toString()),
+          spacer,
+          const Text('GrowthDirection.forward'),
+          spacer,
+          Text(scrollDirection.toString()),
+          spacer,
+          _getArrows(),
+        ],
+      ),
+    );
+  }
+
+  Widget _getRadioRow() {
+    return DefaultTextStyle(
+      style: const TextStyle(fontWeight: FontWeight.bold, color: Colors.white),
+      child: RadioTheme(
+        data: RadioThemeData(
+          fillColor: MaterialStateProperty.all<Color>(Colors.white),
+        ),
+        child: Padding(
+          padding: const EdgeInsets.all(8.0),
+          child: Row(
+            mainAxisAlignment: MainAxisAlignment.spaceAround,
+            children: <Widget>[
+              Radio<AxisDirection>(
+                value: AxisDirection.up,
+                groupValue: _axisDirection,
+                onChanged: _onAxisDirectionChanged,
+              ),
+              const Text('up'),
+              spacer,
+              Radio<AxisDirection>(
+                value: AxisDirection.down,
+                groupValue: _axisDirection,
+                onChanged: _onAxisDirectionChanged,
+              ),
+              const Text('down'),
+              spacer,
+              Radio<AxisDirection>(
+                value: AxisDirection.left,
+                groupValue: _axisDirection,
+                onChanged: _onAxisDirectionChanged,
+              ),
+              const Text('left'),
+              spacer,
+              Radio<AxisDirection>(
+                value: AxisDirection.right,
+                groupValue: _axisDirection,
+                onChanged: _onAxisDirectionChanged,
+              ),
+              const Text('right'),
+              spacer,
+            ],
+          ),
+        ),
+      ),
+    );
+  }
+
+  bool _handleNotification(UserScrollNotification notification) {
+    if (notification.direction != scrollDirection) {
+      setState((){
+        scrollDirection = notification.direction;
+      });
+    }
+    return false;
+  }
+
+  @override
+  Widget build(BuildContext context) {
+    return Scaffold(
+      appBar: AppBar(
+        title: const Text('ScrollDirections'),
+        bottom: PreferredSize(
+          preferredSize: const Size.fromHeight(50),
+          child: Padding(
+            padding: const EdgeInsets.all(8.0),
+            child: _getRadioRow(),
+          ),
+        ),
+      ),
+      body: NotificationListener<UserScrollNotification>(
+        onNotification: _handleNotification,
+        // Also works for ListView.builder, which creates a SliverList for itself.
+        // A CustomScrollView allows multiple slivers to be composed together.
+        child: CustomScrollView(
+          // This method is available to conveniently determine if an scroll
+          // view is reversed by its AxisDirection.
+          reverse: axisDirectionIsReversed(_axisDirection),
+          // This method is available to conveniently convert an AxisDirection
+          // into its Axis.
+          scrollDirection: axisDirectionToAxis(_axisDirection),
+          slivers: <Widget>[
+            SliverList.builder(
+              itemCount: 27,
+              itemBuilder: (BuildContext context, int index) {
+                final Widget child;
+                if (index == 0) {
+                  child = _getLeading();
+                } else {
+                  child = Container(
+                    color: index.isEven ? Colors.amber[100] : Colors.amberAccent,
+                    padding: const EdgeInsets.all(8.0),
+                    child: Center(child: Text(alphabet[index - 1])),
+                  );
+                }
+                return Padding(
+                  padding: const EdgeInsets.all(8.0),
+                  child: child,
+                );
+              }
+            ),
+          ],
+        ),
+      ),
+    );
+  }
+}
diff --git a/examples/api/test/painting/axis_direction.0_test.dart b/examples/api/test/painting/axis_direction.0_test.dart
new file mode 100644
index 0000000..0a346c1
--- /dev/null
+++ b/examples/api/test/painting/axis_direction.0_test.dart
@@ -0,0 +1,37 @@
+// Copyright 2014 The Flutter Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+import 'package:flutter/material.dart';
+import 'package:flutter/rendering.dart';
+import 'package:flutter_api_samples/painting/axis_direction/axis_direction.0.dart' as example;
+import 'package:flutter_test/flutter_test.dart';
+
+void main() {
+  testWidgets('Example app has radio buttons to toggle AxisDirection', (WidgetTester tester) async {
+    await tester.pumpWidget(
+      const example.ExampleApp(),
+    );
+
+    expect(find.byType(Radio<AxisDirection>), findsNWidgets(4));
+    final RenderViewport viewport = tester.renderObject(find.byType(Viewport));
+    expect(find.text('AxisDirection.down'), findsOneWidget);
+    expect(find.text('Axis.vertical'), findsOneWidget);
+    expect(find.text('GrowthDirection.forward'), findsOneWidget);
+    expect(find.byIcon(Icons.arrow_downward_rounded), findsNWidgets(2));
+    expect(viewport.axisDirection, AxisDirection.down);
+
+    await tester.tap(
+      find.byWidgetPredicate((Widget widget) {
+        return widget is Radio<AxisDirection> && widget.value == AxisDirection.up;
+      })
+    );
+    await tester.pumpAndSettle();
+
+    expect(find.text('AxisDirection.up'), findsOneWidget);
+    expect(find.text('Axis.vertical'), findsOneWidget);
+    expect(find.text('GrowthDirection.forward'), findsOneWidget);
+    expect(find.byIcon(Icons.arrow_upward_rounded), findsNWidgets(2));
+    expect(viewport.axisDirection, AxisDirection.up);
+  });
+}
diff --git a/examples/api/test/rendering/growth_direction.0_test.dart b/examples/api/test/rendering/growth_direction.0_test.dart
new file mode 100644
index 0000000..3fd61fe
--- /dev/null
+++ b/examples/api/test/rendering/growth_direction.0_test.dart
@@ -0,0 +1,42 @@
+// Copyright 2014 The Flutter Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+import 'package:flutter/material.dart';
+import 'package:flutter/rendering.dart';
+import 'package:flutter_api_samples/rendering/growth_direction/growth_direction.0.dart' as example;
+import 'package:flutter_test/flutter_test.dart';
+
+void main() {
+  testWidgets('Example app has GrowthDirections represented', (WidgetTester tester) async {
+    await tester.pumpWidget(
+      const example.ExampleApp(),
+    );
+
+    final RenderViewport viewport = tester.renderObject(find.byType(Viewport));
+    expect(find.text('AxisDirection.down'), findsNWidgets(2));
+    expect(find.text('Axis.vertical'), findsNWidgets(2));
+    expect(find.text('GrowthDirection.forward'), findsOneWidget);
+    expect(find.text('GrowthDirection.reverse'), findsOneWidget);
+    expect(find.byIcon(Icons.arrow_upward_rounded), findsNWidgets(2));
+    expect(find.byIcon(Icons.arrow_downward_rounded), findsNWidgets(2));
+    expect(viewport.axisDirection, AxisDirection.down);
+    expect(viewport.anchor, 0.5);
+    expect(viewport.center, isNotNull);
+
+    await tester.tap(
+        find.byWidgetPredicate((Widget widget) {
+          return widget is Radio<AxisDirection> && widget.value == AxisDirection.up;
+        })
+    );
+    await tester.pumpAndSettle();
+
+    expect(find.text('AxisDirection.up'), findsNWidgets(2));
+    expect(find.text('Axis.vertical'), findsNWidgets(2));
+    expect(find.text('GrowthDirection.forward'), findsOneWidget);
+    expect(find.text('GrowthDirection.reverse'), findsOneWidget);
+    expect(find.byIcon(Icons.arrow_upward_rounded), findsNWidgets(2));
+    expect(find.byIcon(Icons.arrow_downward_rounded), findsNWidgets(2));
+    expect(viewport.axisDirection, AxisDirection.up);
+  });
+}
diff --git a/examples/api/test/rendering/scroll_direction.0_test.dart b/examples/api/test/rendering/scroll_direction.0_test.dart
new file mode 100644
index 0000000..5e4075c
--- /dev/null
+++ b/examples/api/test/rendering/scroll_direction.0_test.dart
@@ -0,0 +1,39 @@
+// Copyright 2014 The Flutter Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+import 'package:flutter/material.dart';
+import 'package:flutter/rendering.dart';
+import 'package:flutter_api_samples/rendering/scroll_direction/scroll_direction.0.dart' as example;
+import 'package:flutter_test/flutter_test.dart';
+
+void main() {
+  testWidgets('Example app has ScrollDirection represented', (WidgetTester tester) async {
+    await tester.pumpWidget(
+      const example.ExampleApp(),
+    );
+
+    expect(find.byType(Radio<AxisDirection>), findsNWidgets(4));
+    final RenderViewport viewport = tester.renderObject(find.byType(Viewport));
+    expect(find.text('AxisDirection.down'), findsOneWidget);
+    expect(find.text('Axis.vertical'), findsOneWidget);
+    expect(find.text('GrowthDirection.forward'), findsOneWidget);
+    expect(find.text('ScrollDirection.idle'), findsOneWidget);
+    expect(find.byIcon(Icons.arrow_downward_rounded), findsNWidgets(2));
+    expect(viewport.axisDirection, AxisDirection.down);
+
+    await tester.tap(
+        find.byWidgetPredicate((Widget widget) {
+          return widget is Radio<AxisDirection> && widget.value == AxisDirection.up;
+        })
+    );
+    await tester.pumpAndSettle();
+
+    expect(find.text('AxisDirection.up'), findsOneWidget);
+    expect(find.text('Axis.vertical'), findsOneWidget);
+    expect(find.text('GrowthDirection.forward'), findsOneWidget);
+    expect(find.text('ScrollDirection.idle'), findsOneWidget);
+    expect(find.byIcon(Icons.arrow_upward_rounded), findsNWidgets(2));
+    expect(viewport.axisDirection, AxisDirection.up);
+  });
+}
diff --git a/packages/flutter/lib/src/material/input_chip.dart b/packages/flutter/lib/src/material/input_chip.dart
index da909a6..8dcee82 100644
--- a/packages/flutter/lib/src/material/input_chip.dart
+++ b/packages/flutter/lib/src/material/input_chip.dart
@@ -30,8 +30,8 @@
 /// Input chips work together with other UI elements. They can appear:
 ///
 ///  * In a [Wrap] widget.
-///  * In a horizontally scrollable list, like a [ListView] whose
-///    scrollDirection is [Axis.horizontal].
+///  * In a horizontally scrollable list, for example configured such as a
+///    [ListView] with [ListView.scrollDirection] set to [Axis.horizontal].
 ///
 /// {@tool dartpad}
 /// This example shows how to create [InputChip]s with [onSelected] and
diff --git a/packages/flutter/lib/src/painting/basic_types.dart b/packages/flutter/lib/src/painting/basic_types.dart
index 26c67b2..6107e10 100644
--- a/packages/flutter/lib/src/painting/basic_types.dart
+++ b/packages/flutter/lib/src/painting/basic_types.dart
@@ -111,7 +111,7 @@
 /// See also:
 ///
 ///  * [AxisDirection], which is a directional version of this enum (with values
-///    light left and right, rather than just horizontal).
+///    like left and right, rather than just horizontal).
 ///  * [TextDirection], which disambiguates between left-to-right horizontal
 ///    content and right-to-left horizontal content.
 enum Axis {
@@ -167,33 +167,102 @@
   down,
 }
 
-/// A direction along either the horizontal or vertical [Axis].
+/// A direction along either the horizontal or vertical [Axis] in which the
+/// origin, or zero position, is determined.
+///
+/// This value relates to the direction in which the scroll offset increases
+/// from the origin. This value does not represent the direction of user input
+/// that may be modifying the scroll offset, such as from a drag. For the
+/// active scrolling direction, see [ScrollDirection].
+///
+/// {@tool dartpad}
+/// This sample shows a [CustomScrollView], with [Radio] buttons in the
+/// [AppBar.bottom] that change the [AxisDirection] to illustrate different
+/// configurations.
+///
+/// ** See code in examples/api/lib/painting/axis_direction/axis_direction.0.dart **
+/// {@end-tool}
+///
+/// See also:
+///
+///   * [ScrollDirection], the direction of active scrolling, relative to the positive
+///     scroll offset axis given by an [AxisDirection] and a [GrowthDirection].
+///   * [GrowthDirection], the direction in which slivers and their content are
+///     ordered, relative to the scroll offset axis as specified by
+///     [AxisDirection].
+///   * [CustomScrollView.anchor], the relative position of the zero scroll
+///     offset in a viewport and inflection point for [AxisDirection]s of the
+///     same cardinal [Axis].
+///   * [axisDirectionIsReversed], which returns whether traveling along the
+///     given axis direction visits coordinates along that axis in numerically
+///     decreasing order.
 enum AxisDirection {
-  /// Zero is at the bottom and positive values are above it: `⇈`
+  /// A direction in the [Axis.vertical] where zero is at the bottom and
+  /// positive values are above it: `⇈`
   ///
   /// Alphabetical content with a [GrowthDirection.forward] would have the A at
-  /// the bottom and the Z at the top. This is an unusual configuration.
+  /// the bottom and the Z at the top.
+  ///
+  /// For example, the behavior of a [ListView] with [ListView.reverse] set to
+  /// true would have this axis direction.
+  ///
+  /// See also:
+  ///
+  ///   * [axisDirectionIsReversed], which returns whether traveling along the
+  ///     given axis direction visits coordinates along that axis in numerically
+  ///     decreasing order.
   up,
 
-  /// Zero is on the left and positive values are to the right of it: `⇉`
+  /// A direction in the [Axis.horizontal] where zero is on the left and
+  /// positive values are to the right of it: `⇉`
   ///
   /// Alphabetical content with a [GrowthDirection.forward] would have the A on
   /// the left and the Z on the right. This is the ordinary reading order for a
   /// horizontal set of tabs in an English application, for example.
+  ///
+  /// For example, the behavior of a [ListView] with [ListView.scrollDirection]
+  /// set to [Axis.horizontal] would have this axis direction.
+  ///
+  /// See also:
+  ///
+  ///   * [axisDirectionIsReversed], which returns whether traveling along the
+  ///     given axis direction visits coordinates along that axis in numerically
+  ///     decreasing order.
   right,
 
-  /// Zero is at the top and positive values are below it: `⇊`
+  /// A direction in the [Axis.vertical] where zero is at the top and positive
+  /// values are below it: `⇊`
   ///
   /// Alphabetical content with a [GrowthDirection.forward] would have the A at
   /// the top and the Z at the bottom. This is the ordinary reading order for a
   /// vertical list.
+  ///
+  /// For example, the default behavior of a [ListView] would have this axis
+  /// direction.
+  ///
+  /// See also:
+  ///
+  ///   * [axisDirectionIsReversed], which returns whether traveling along the
+  ///     given axis direction visits coordinates along that axis in numerically
+  ///     decreasing order.
   down,
 
-  /// Zero is to the right and positive values are to the left of it: `⇇`
+  /// A direction in the [Axis.horizontal] where zero is to the right and
+  /// positive values are to the left of it: `⇇`
   ///
   /// Alphabetical content with a [GrowthDirection.forward] would have the A at
   /// the right and the Z at the left. This is the ordinary reading order for a
   /// horizontal set of tabs in a Hebrew application, for example.
+  ///
+  /// For example, the behavior of a [ListView] with [ListView.scrollDirection]
+  /// set to [Axis.horizontal] and [ListView.reverse] set to true would have
+  /// this axis direction.
+  ///
+  /// See also:
+  ///
+  ///   * [axisDirectionIsReversed], which returns whether traveling along the
+  ///     given axis direction visits coordinates along that axis in numerically
+  ///     decreasing order.
   left,
 }
 
diff --git a/packages/flutter/lib/src/rendering/sliver.dart b/packages/flutter/lib/src/rendering/sliver.dart
index 590334f..d245116 100644
--- a/packages/flutter/lib/src/rendering/sliver.dart
+++ b/packages/flutter/lib/src/rendering/sliver.dart
@@ -27,15 +27,48 @@
 /// [GrowthDirection.reverse] would have the Z at the top (at scroll offset
 /// zero) and the A below it.
 ///
-/// The direction in which the scroll offset increases is given by
-/// [applyGrowthDirectionToAxisDirection].
+/// {@template flutter.rendering.GrowthDirection.sample}
+/// Most scroll views by default are ordered [GrowthDirection.forward].
+/// Changing the default values of [ScrollView.anchor],
+/// [ScrollView.center], or both, can configure a scroll view for
+/// [GrowthDirection.reverse].
+///
+/// {@tool dartpad}
+/// This sample shows a [CustomScrollView], with [Radio] buttons in the
+/// [AppBar.bottom] that change the [AxisDirection] to illustrate different
+/// configurations. The [CustomScrollView.anchor] and [CustomScrollView.center]
+/// properties are also set to have the 0 scroll offset positioned in the middle
+/// of the viewport, with [GrowthDirection.forward] and [GrowthDirection.reverse]
+/// illustrated on either side. The sliver that shares the
+/// [CustomScrollView.center] key is positioned at the [CustomScrollView.anchor].
+///
+/// ** See code in examples/api/lib/rendering/growth_direction/growth_direction.0.dart **
+/// {@end-tool}
+/// {@endtemplate}
+///
+/// See also:
+///
+///   * [applyGrowthDirectionToAxisDirection], which returns the direction in
+///     which the scroll offset increases.
 enum GrowthDirection {
   /// This sliver's contents are ordered in the same direction as the
-  /// [AxisDirection].
+  /// [AxisDirection]. For example, a vertical alphabetical list that is going
+  /// [AxisDirection.down] with a [GrowthDirection.forward] would have the A at
+  /// the top and the Z at the bottom, with the A adjacent to the origin.
+  ///
+  /// See also:
+  ///
+  ///   * [applyGrowthDirectionToAxisDirection], which returns the direction in
+  ///     which the scroll offset increases.
   forward,
 
   /// This sliver's contents are ordered in the opposite direction of the
   /// [AxisDirection].
+  ///
+  /// See also:
+  ///
+  ///   * [applyGrowthDirectionToAxisDirection], which returns the direction in
+  ///     which the scroll offset increases.
   reverse,
 }
 
@@ -57,11 +90,12 @@
   }
 }
 
-/// Flips the [ScrollDirection] if the [GrowthDirection] is [GrowthDirection.reverse].
+/// Flips the [ScrollDirection] if the [GrowthDirection] is
+/// [GrowthDirection.reverse].
 ///
 /// Specifically, returns `scrollDirection` if `scrollDirection` is
-/// [GrowthDirection.forward], otherwise returns [flipScrollDirection] applied to
-/// `scrollDirection`.
+/// [GrowthDirection.forward], otherwise returns [flipScrollDirection] applied
+/// to `scrollDirection`.
 ///
 /// This function is useful in [RenderSliver] subclasses that are given both an
 /// [ScrollDirection] and a [GrowthDirection] and wish to compute the
@@ -135,6 +169,14 @@
 
   /// The direction in which the [scrollOffset] and [remainingPaintExtent]
   /// increase.
+  ///
+  /// {@tool dartpad}
+  /// This sample shows a [CustomScrollView], with [Radio] buttons in the
+  /// [AppBar.bottom] that change the [AxisDirection] to illustrate different
+  /// configurations.
+  ///
+  /// ** See code in examples/api/lib/painting/axis_direction/axis_direction.0.dart **
+  /// {@end-tool}
   final AxisDirection axisDirection;
 
   /// The direction in which the contents of slivers are ordered, relative to
@@ -158,14 +200,16 @@
   ///
   /// Normally, the absolute zero offset is determined by the viewport's
   /// [RenderViewport.center] and [RenderViewport.anchor] properties.
+  ///
+  /// {@macro flutter.rendering.GrowthDirection.sample}
   final GrowthDirection growthDirection;
 
   /// The direction in which the user is attempting to scroll, relative to the
   /// [axisDirection] and [growthDirection].
   ///
-  /// For example, if [growthDirection] is [GrowthDirection.reverse] and
+  /// For example, if [growthDirection] is [GrowthDirection.forward] and
   /// [axisDirection] is [AxisDirection.down], then a
-  /// [ScrollDirection.forward] means that the user is scrolling up, in the
+  /// [ScrollDirection.reverse] means that the user is scrolling down, in the
   /// positive [scrollOffset] direction.
   ///
   /// If the _user_ is not scrolling, this will return [ScrollDirection.idle]
@@ -176,6 +220,8 @@
   /// scroll offset. For example, [RenderSliverFloatingPersistentHeader] will
   /// only expand a floating app bar when the [userScrollDirection] is in the
   /// positive scroll offset direction.
+  ///
+  /// {@macro flutter.rendering.ScrollDirection.sample}
   final ScrollDirection userScrollDirection;
 
   /// The scroll offset, in this sliver's coordinate system, that corresponds to
diff --git a/packages/flutter/lib/src/rendering/viewport.dart b/packages/flutter/lib/src/rendering/viewport.dart
index 1d31f19..bf07a88 100644
--- a/packages/flutter/lib/src/rendering/viewport.dart
+++ b/packages/flutter/lib/src/rendering/viewport.dart
@@ -1222,9 +1222,10 @@
 /// given [offset]. As the offset varies, different children are visible through
 /// the viewport.
 ///
-/// [RenderViewport] hosts a bidirectional list of slivers, anchored on a
-/// [center] sliver, which is placed at the zero scroll offset. The center
-/// widget is displayed in the viewport according to the [anchor] property.
+/// [RenderViewport] hosts a bidirectional list of slivers in a single shared
+/// [Axis], anchored on a [center] sliver, which is placed at the zero scroll
+/// offset. The center widget is displayed in the viewport according to the
+/// [anchor] property.
 ///
 /// Slivers that are earlier in the child list than [center] are displayed in
 /// reverse order in the reverse [axisDirection] starting from the [center]. For
@@ -1234,6 +1235,8 @@
 /// example, in the preceding scenario, the first sliver after [center] is
 /// placed below the [center].
 ///
+/// {@macro flutter.rendering.GrowthDirection.sample}
+///
 /// [RenderViewport] cannot contain [RenderBox] children directly. Instead, use
 /// a [RenderSliverList], [RenderSliverFixedExtentList], [RenderSliverGrid], or
 /// a [RenderSliverToBoxAdapter], for example.
@@ -1323,6 +1326,8 @@
   /// vertically centered within the viewport. If the [anchor] is 1.0, and the
   /// [axisDirection] is [AxisDirection.right], then the zero scroll offset is
   /// on the left edge of the viewport.
+  ///
+  /// {@macro flutter.rendering.GrowthDirection.sample}
   double get anchor => _anchor;
   double _anchor;
   set anchor(double value) {
@@ -1340,10 +1345,15 @@
   /// [ViewportOffset.pixels] of [offset] is `0`.
   ///
   /// Children after [center] will be placed in the [axisDirection] relative to
-  /// the [center]. Children before [center] will be placed in the opposite of
-  /// the [axisDirection] relative to the [center].
+  /// the [center].
   ///
-  /// The [center] must be a child of the viewport.
+  /// Children before [center] will be placed in the opposite of
+  /// the [axisDirection] relative to the [center]. These children above
+  /// [center] will have a growth direction of [GrowthDirection.reverse].
+  ///
+  /// The [center] must be a direct child of the viewport.
+  ///
+  /// {@macro flutter.rendering.GrowthDirection.sample}
   RenderSliver? get center => _center;
   RenderSliver? _center;
   set center(RenderSliver? value) {
diff --git a/packages/flutter/lib/src/rendering/viewport_offset.dart b/packages/flutter/lib/src/rendering/viewport_offset.dart
index 8f5f979..44d5cad 100644
--- a/packages/flutter/lib/src/rendering/viewport_offset.dart
+++ b/packages/flutter/lib/src/rendering/viewport_offset.dart
@@ -8,28 +8,58 @@
 /// The direction of a scroll, relative to the positive scroll offset axis given
 /// by an [AxisDirection] and a [GrowthDirection].
 ///
-/// This contrasts to [GrowthDirection] in that it has a third value, [idle],
-/// for the case where no scroll is occurring.
+/// This is similar to [GrowthDirection], but contrasts in that it has a third
+/// value, [idle], for the case where no scroll is occurring.
 ///
 /// This is used by [RenderSliverFloatingPersistentHeader] to only expand when
 /// the user is scrolling in the same direction as the detected scroll offset
 /// change.
+///
+/// {@template flutter.rendering.ScrollDirection.sample}
+/// {@tool dartpad}
+/// This sample shows a [CustomScrollView], with [Radio] buttons in the
+/// [AppBar.bottom] that change the [AxisDirection] to illustrate different
+/// configurations. With a [NotificationListener] to listen to
+/// [UserScrollNotification]s, which occur when the [ScrollDirection] changes
+/// or stops.
+///
+/// ** See code in examples/api/lib/rendering/scroll_direction/scroll_direction.0.dart **
+/// {@end-tool}
+/// {@endtemplate}
+///
+/// See also:
+///
+///  * [AxisDirection], which is a directional version of this enum (with values
+///    like left and right, rather than just horizontal).
+///  * [GrowthDirection], the direction in which slivers and their content are
+///    ordered, relative to the scroll offset axis as specified by
+///    [AxisDirection].
+///  * [UserScrollNotification], which will notify listeners when the
+///    [ScrollDirection] changes.
 enum ScrollDirection {
   /// No scrolling is underway.
   idle,
 
-  /// Scrolling is happening in the positive scroll offset direction.
-  ///
-  /// For example, for the [GrowthDirection.forward] part of a vertical
-  /// [AxisDirection.down] list, this means the content is moving up, exposing
-  /// lower content.
-  forward,
-
   /// Scrolling is happening in the negative scroll offset direction.
   ///
   /// For example, for the [GrowthDirection.forward] part of a vertical
-  /// [AxisDirection.down] list, this means the content is moving down, exposing
-  /// earlier content.
+  /// [AxisDirection.down] list, which is the default directional configuration
+  /// of all scroll views, this means the content is going down, exposing
+  /// earlier content as it approaches the zero position.
+  ///
+  /// An anecdote for this most common case is 'forward is toward' the zero
+  /// position.
+  forward,
+
+  /// Scrolling is happening in the positive scroll offset direction.
+  ///
+  /// For example, for the [GrowthDirection.forward] part of a vertical
+  /// [AxisDirection.down] list, which is the default directional configuration
+  /// of all scroll views, this means the content is moving up, exposing
+  /// lower content.
+  ///
+  /// An anecdote for this most common case is reversing, or backing away, from
+  /// the zero position.
   reverse,
 }
 
@@ -216,6 +246,8 @@
   /// For example, [RenderSliverFloatingPersistentHeader] will only expand a
   /// floating app bar when the [userScrollDirection] is in the positive scroll
   /// offset direction.
+  ///
+  /// {@macro flutter.rendering.ScrollDirection.sample}
   ScrollDirection get userScrollDirection;
 
   /// Whether a viewport is allowed to change [pixels] implicitly to respond to
diff --git a/packages/flutter/lib/src/widgets/animated_scroll_view.dart b/packages/flutter/lib/src/widgets/animated_scroll_view.dart
index 53196a8..7dda551 100644
--- a/packages/flutter/lib/src/widgets/animated_scroll_view.dart
+++ b/packages/flutter/lib/src/widgets/animated_scroll_view.dart
@@ -401,9 +401,7 @@
   /// {@endtemplate}
   final int initialItemCount;
 
-  /// The axis along which the scroll view scrolls.
-  ///
-  /// Defaults to [Axis.vertical].
+  /// {@macro flutter.widgets.scroll_view.scrollDirection}
   final Axis scrollDirection;
 
   /// Whether the scroll view scrolls in the reading direction.
diff --git a/packages/flutter/lib/src/widgets/nested_scroll_view.dart b/packages/flutter/lib/src/widgets/nested_scroll_view.dart
index 4d8f40e..333e97b 100644
--- a/packages/flutter/lib/src/widgets/nested_scroll_view.dart
+++ b/packages/flutter/lib/src/widgets/nested_scroll_view.dart
@@ -196,9 +196,7 @@
   /// scroll view is scrolled.
   final ScrollController? controller;
 
-  /// The axis along which the scroll view scrolls.
-  ///
-  /// Defaults to [Axis.vertical].
+  /// {@macro flutter.widgets.scroll_view.scrollDirection}
   final Axis scrollDirection;
 
   /// Whether the scroll view scrolls in the reading direction.
diff --git a/packages/flutter/lib/src/widgets/page_view.dart b/packages/flutter/lib/src/widgets/page_view.dart
index 8e2723e..9660dde 100644
--- a/packages/flutter/lib/src/widgets/page_view.dart
+++ b/packages/flutter/lib/src/widgets/page_view.dart
@@ -823,7 +823,10 @@
   /// {@macro flutter.widgets.scrollable.restorationId}
   final String? restorationId;
 
-  /// The axis along which the page view scrolls.
+  /// The [Axis] along which the scroll view's offset increases with each page.
+  ///
+  /// For the direction in which active scrolling may be occurring, see
+  /// [ScrollDirection].
   ///
   /// Defaults to [Axis.horizontal].
   final Axis scrollDirection;
diff --git a/packages/flutter/lib/src/widgets/primary_scroll_controller.dart b/packages/flutter/lib/src/widgets/primary_scroll_controller.dart
index 73fa42f..dea9982 100644
--- a/packages/flutter/lib/src/widgets/primary_scroll_controller.dart
+++ b/packages/flutter/lib/src/widgets/primary_scroll_controller.dart
@@ -84,6 +84,9 @@
   /// PrimaryScrollController.none into the tree to prevent further descendant
   /// ScrollViews from inheriting the current PrimaryScrollController.
   ///
+  /// For the direction in which active scrolling may be occurring, see
+  /// [ScrollDirection].
+  ///
   /// Defaults to [Axis.vertical].
   final Axis? scrollDirection;
 
diff --git a/packages/flutter/lib/src/widgets/scroll_notification.dart b/packages/flutter/lib/src/widgets/scroll_notification.dart
index 597bd80..6f7dd67 100644
--- a/packages/flutter/lib/src/widgets/scroll_notification.dart
+++ b/packages/flutter/lib/src/widgets/scroll_notification.dart
@@ -268,8 +268,13 @@
   }
 }
 
-/// A notification that the user has changed the direction in which they are
-/// scrolling.
+/// A notification that the user has changed the [ScrollDirection] in which they
+/// are scrolling, or have stopped scrolling.
+///
+/// For the direction that the [ScrollView] is oriented to, and the direction
+/// contents are being laid out in, see [AxisDirection] & [GrowthDirection].
+///
+/// {@macro flutter.rendering.ScrollDirection.sample}
 ///
 /// See also:
 ///
@@ -284,6 +289,13 @@
   });
 
   /// The direction in which the user is scrolling.
+  ///
+  /// This does not represent the current [AxisDirection] or [GrowthDirection]
+  /// of the [Viewport], which respectively represent the direction that the
+  /// scroll offset is increasing in, and the direction that contents are being
+  /// laid out in.
+  ///
+  /// {@macro flutter.rendering.ScrollDirection.sample}
   final ScrollDirection direction;
 
   @override
diff --git a/packages/flutter/lib/src/widgets/scroll_view.dart b/packages/flutter/lib/src/widgets/scroll_view.dart
index 861497d..0e518cf 100644
--- a/packages/flutter/lib/src/widgets/scroll_view.dart
+++ b/packages/flutter/lib/src/widgets/scroll_view.dart
@@ -114,7 +114,10 @@
        physics = physics ?? ((primary ?? false) || (primary == null && controller == null && identical(scrollDirection, Axis.vertical)) ? const AlwaysScrollableScrollPhysics() : null);
 
   /// {@template flutter.widgets.scroll_view.scrollDirection}
-  /// The axis along which the scroll view scrolls.
+  /// The [Axis] along which the scroll view's offset increases.
+  ///
+  /// For the direction in which active scrolling may be occurring, see
+  /// [ScrollDirection].
   ///
   /// Defaults to [Axis.vertical].
   /// {@endtemplate}
@@ -276,6 +279,23 @@
   /// supports [center]; for that class, the given key must be the key of one of
   /// the slivers in the [CustomScrollView.slivers] list.
   ///
+  /// Most scroll views by default are ordered [GrowthDirection.forward].
+  /// Changing the default values of [ScrollView.anchor],
+  /// [ScrollView.center], or both, can configure a scroll view for
+  /// [GrowthDirection.reverse].
+  ///
+  /// {@tool dartpad}
+  /// This sample shows a [CustomScrollView], with [Radio] buttons in the
+  /// [AppBar.bottom] that change the [AxisDirection] to illustrate different
+  /// configurations. The [CustomScrollView.anchor] and [CustomScrollView.center]
+  /// properties are also set to have the 0 scroll offset positioned in the middle
+  /// of the viewport, with [GrowthDirection.forward] and [GrowthDirection.reverse]
+  /// illustrated on either side. The sliver that shares the
+  /// [CustomScrollView.center] key is positioned at the [CustomScrollView.anchor].
+  ///
+  /// ** See code in examples/api/lib/rendering/growth_direction/growth_direction.0.dart **
+  /// {@end-tool}
+  ///
   /// See also:
   ///
   ///  * [anchor], which controls where the [center] as aligned in the viewport.
@@ -290,6 +310,23 @@
   /// within the viewport. If the [anchor] is 1.0, and the axis direction is
   /// [AxisDirection.right], then the zero scroll offset is on the left edge of
   /// the viewport.
+  ///
+  /// Most scroll views by default are ordered [GrowthDirection.forward].
+  /// Changing the default values of [ScrollView.anchor],
+  /// [ScrollView.center], or both, can configure a scroll view for
+  /// [GrowthDirection.reverse].
+  ///
+  /// {@tool dartpad}
+  /// This sample shows a [CustomScrollView], with [Radio] buttons in the
+  /// [AppBar.bottom] that change the [AxisDirection] to illustrate different
+  /// configurations. The [CustomScrollView.anchor] and [CustomScrollView.center]
+  /// properties are also set to have the 0 scroll offset positioned in the middle
+  /// of the viewport, with [GrowthDirection.forward] and [GrowthDirection.reverse]
+  /// illustrated on either side. The sliver that shares the
+  /// [CustomScrollView.center] key is positioned at the [CustomScrollView.anchor].
+  ///
+  /// ** See code in examples/api/lib/rendering/growth_direction/growth_direction.0.dart **
+  /// {@end-tool}
   /// {@endtemplate}
   final double anchor;
 
diff --git a/packages/flutter/lib/src/widgets/scrollbar.dart b/packages/flutter/lib/src/widgets/scrollbar.dart
index ababcca..e98ad61 100644
--- a/packages/flutter/lib/src/widgets/scrollbar.dart
+++ b/packages/flutter/lib/src/widgets/scrollbar.dart
@@ -1087,9 +1087,9 @@
   ///   * When providing a controller, the same ScrollController must also be
   ///     provided to the associated Scrollable widget.
   ///   * The [PrimaryScrollController] is used by default for a [ScrollView]
-  ///     that has not been provided a [ScrollController] and that has an
-  ///     [Axis.vertical] [ScrollDirection]. This automatic behavior does not
-  ///     apply to those with a ScrollDirection of Axis.horizontal. To explicitly
+  ///     that has not been provided a [ScrollController] and that has a
+  ///     [ScrollView.scrollDirection] of [Axis.vertical]. This automatic
+  ///     behavior does not apply to those with [Axis.horizontal]. To explicitly
   ///     use the PrimaryScrollController, set [ScrollView.primary] to true.
   ///
   /// Defaults to false when null.
@@ -1172,9 +1172,9 @@
   ///   * When providing a controller, the same ScrollController must also be
   ///     provided to the associated Scrollable widget.
   ///   * The [PrimaryScrollController] is used by default for a [ScrollView]
-  ///     that has not been provided a [ScrollController] and that has an
-  ///     [Axis.vertical] [ScrollDirection]. This automatic behavior does not
-  ///     apply to those with a ScrollDirection of Axis.horizontal. To explicitly
+  ///     that has not been provided a [ScrollController] and that has a
+  ///     [ScrollView.scrollDirection] of [Axis.vertical]. This automatic
+  ///     behavior does not apply to those with Axis.horizontal. To explicitly
   ///     use the PrimaryScrollController, set [ScrollView.primary] to true.
   ///
   /// Defaults to false when null.
@@ -1576,7 +1576,7 @@
             'ScrollController should be associated with the ScrollView that '
             'the Scrollbar is being applied to.'
             '${tryPrimary
-              ? 'A ScrollView with an Axis.vertical ScrollDirection on mobile '
+              ? 'When ScrollView.scrollDirection is Axis.vertical on mobile '
                 'platforms will automatically use the '
                 'PrimaryScrollController if the user has not provided a '
                 'ScrollController. To use the PrimaryScrollController '
diff --git a/packages/flutter/lib/src/widgets/single_child_scroll_view.dart b/packages/flutter/lib/src/widgets/single_child_scroll_view.dart
index 3957f02..818a8a1 100644
--- a/packages/flutter/lib/src/widgets/single_child_scroll_view.dart
+++ b/packages/flutter/lib/src/widgets/single_child_scroll_view.dart
@@ -158,9 +158,7 @@
          'true and pass an explicit controller.',
        );
 
-  /// The axis along which the scroll view scrolls.
-  ///
-  /// Defaults to [Axis.vertical].
+  /// {@macro flutter.widgets.scroll_view.scrollDirection}
   final Axis scrollDirection;
 
   /// Whether the scroll view scrolls in the reading direction.
diff --git a/packages/flutter/lib/src/widgets/viewport.dart b/packages/flutter/lib/src/widgets/viewport.dart
index a4fcb70..92aa832 100644
--- a/packages/flutter/lib/src/widgets/viewport.dart
+++ b/packages/flutter/lib/src/widgets/viewport.dart
@@ -96,6 +96,8 @@
   /// vertically centered within the viewport. If the [anchor] is 1.0, and the
   /// [axisDirection] is [AxisDirection.right], then the zero scroll offset is
   /// on the left edge of the viewport.
+  ///
+  /// {@macro flutter.rendering.GrowthDirection.sample}
   final double anchor;
 
   /// Which part of the content inside the viewport should be visible.
@@ -115,6 +117,8 @@
   /// the [axisDirection] relative to the [center].
   ///
   /// The [center] must be the key of a child of the viewport.
+  ///
+  /// {@macro flutter.rendering.GrowthDirection.sample}
   final Key? center;
 
   /// {@macro flutter.rendering.RenderViewportBase.cacheExtent}