Fix NestedScrollView example crash when switching tabs on desktop (#186993)

Fixes https://github.com/flutter/flutter/issues/183199

The `NestedScrollView` API example that uses a `TabBar`/`TabBarView`
throws on desktop when you scroll an inner list and then switch tabs.
The inner `CustomScrollView`s are coordinated by the `NestedScrollView`
through a shared controller, which can be attached to more than one
`ScrollPosition` at a time (for example mid-transition between tabs).
The automatic desktop scrollbar attaches to that shared controller, and
a single scrollbar cannot represent multiple positions, so
`RawScrollbar` throws the multiple-position assertion.

This leaves the framework unchanged and fixes the example itself: the
`TabBarView` body is wrapped in a `ScrollConfiguration` that disables
the default scrollbars, since a single scrollbar cannot correctly
represent the coordinated inner positions. A regression test reproduces
the exact assertion from the issue (scroll, then a mouse-wheel pointer
signal during a tab transition) and confirms it no longer throws.

## Tests

- `flutter test
examples/api/test/widgets/nested_scroll_view/nested_scroll_view.0_test.dart`
- `flutter analyze
examples/api/lib/widgets/nested_scroll_view/nested_scroll_view.0.dart
examples/api/test/widgets/nested_scroll_view/nested_scroll_view.0_test.dart`

## Pre-launch Checklist

- [x] I read the [Contributor Guide] and followed the process outlined
there for submitting PRs.
- [x] I read the [AI contribution guidelines] and understand my
responsibilities, or I am not using AI tools.
- [x] I read the [Tree Hygiene] wiki page, which explains my
responsibilities.
- [x] I read and followed the [Flutter Style Guide], including [Features
we expect every widget to implement].
- [x] I signed the [CLA].
- [x] I listed at least one issue that this PR fixes in the description
above.
- [x] I updated/added relevant documentation (doc comments with `///`).
- [x] I added new tests to check the change I am making, or this PR is
[test-exempt].
- [x] I followed the [breaking change policy] and added [Data Driven
Fixes] where supported.
- [x] All existing and new tests are passing.

<!-- Links -->
[Contributor Guide]:
https://github.com/flutter/flutter/blob/main/docs/contributing/Tree-hygiene.md#overview
[AI contribution guidelines]:
https://github.com/flutter/flutter/blob/main/docs/contributing/Tree-hygiene.md#ai-contribution-guidelines
[Tree Hygiene]:
https://github.com/flutter/flutter/blob/main/docs/contributing/Tree-hygiene.md
[test-exempt]:
https://github.com/flutter/flutter/blob/main/docs/contributing/Tree-hygiene.md#tests
[Flutter Style Guide]:
https://github.com/flutter/flutter/blob/main/docs/contributing/Style-guide-for-Flutter-repo.md
[Features we expect every widget to implement]:
https://github.com/flutter/flutter/blob/main/docs/contributing/Style-guide-for-Flutter-repo.md#features-we-expect-every-widget-to-implement
[CLA]: https://cla.developers.google.com/
[breaking change policy]:
https://github.com/flutter/flutter/blob/main/docs/contributing/Tree-hygiene.md#handling-breaking-changes
[Data Driven Fixes]:
https://github.com/flutter/flutter/blob/main/docs/contributing/Data-driven-Fixes.md
diff --git a/examples/api/lib/widgets/nested_scroll_view/nested_scroll_view.0.dart b/examples/api/lib/widgets/nested_scroll_view/nested_scroll_view.0.dart
index c88b9e9..400eb2e 100644
--- a/examples/api/lib/widgets/nested_scroll_view/nested_scroll_view.0.dart
+++ b/examples/api/lib/widgets/nested_scroll_view/nested_scroll_view.0.dart
@@ -64,66 +64,75 @@
               ),
             ];
           },
-          body: TabBarView(
-            // These are the contents of the tab views, below the tabs.
-            children: tabs.map((String name) {
-              return SafeArea(
-                top: false,
-                bottom: false,
-                child: Builder(
-                  // This Builder is needed to provide a BuildContext that is
-                  // "inside" the NestedScrollView, so that
-                  // sliverOverlapAbsorberHandleFor() can find the
-                  // NestedScrollView.
-                  builder: (BuildContext context) {
-                    return CustomScrollView(
-                      // The "controller" and "primary" members should be left
-                      // unset, so that the NestedScrollView can control this
-                      // inner scroll view.
-                      // If the "controller" property is set, then this scroll
-                      // view will not be associated with the NestedScrollView.
-                      // The PageStorageKey should be unique to this ScrollView;
-                      // it allows the list to remember its scroll position when
-                      // the tab view is not on the screen.
-                      key: PageStorageKey<String>(name),
-                      slivers: <Widget>[
-                        SliverOverlapInjector(
-                          // This is the flip side of the SliverOverlapAbsorber
-                          // above.
-                          handle:
-                              NestedScrollView.sliverOverlapAbsorberHandleFor(
-                                context,
-                              ),
-                        ),
-                        SliverPadding(
-                          padding: const .all(8.0),
-                          // In this example, the inner scroll view has
-                          // fixed-height list items, hence the use of
-                          // SliverFixedExtentList. However, one could use any
-                          // sliver widget here, e.g. SliverList or SliverGrid.
-                          sliver: SliverFixedExtentList.builder(
-                            // The items in this example are fixed to 48 pixels
-                            // high. This matches the Material Design spec for
-                            // ListTile widgets.
-                            itemExtent: 48.0,
-                            // The itemCount of the SliverFixedExtentList.builder
-                            // specifies how many children this inner list
-                            // has. In this example, each tab has a list of
-                            // exactly 30 items, but this is arbitrary.
-                            itemCount: 30,
-                            itemBuilder: (BuildContext context, int index) {
-                              // This builder is called for each child.
-                              // In this example, we just number each list item.
-                              return ListTile(title: Text('Item $index'));
-                            },
+          body: ScrollConfiguration(
+            // Scrollbars have different default behaviors based on platform
+            // expectations. For the purpose of this sample, which can be run on
+            // any platform, default scrollbars are disabled for the inner
+            // scrollables.
+            behavior: ScrollConfiguration.of(
+              context,
+            ).copyWith(scrollbars: false),
+            child: TabBarView(
+              // These are the contents of the tab views, below the tabs.
+              children: tabs.map((String name) {
+                return SafeArea(
+                  top: false,
+                  bottom: false,
+                  child: Builder(
+                    // This Builder is needed to provide a BuildContext that is
+                    // "inside" the NestedScrollView, so that
+                    // sliverOverlapAbsorberHandleFor() can find the
+                    // NestedScrollView.
+                    builder: (BuildContext context) {
+                      return CustomScrollView(
+                        // The "controller" and "primary" members should be left
+                        // unset, so that the NestedScrollView can control this
+                        // inner scroll view.
+                        // If the "controller" property is set, then this scroll
+                        // view will not be associated with the NestedScrollView.
+                        // The PageStorageKey should be unique to this ScrollView;
+                        // it allows the list to remember its scroll position when
+                        // the tab view is not on the screen.
+                        key: PageStorageKey<String>(name),
+                        slivers: <Widget>[
+                          SliverOverlapInjector(
+                            // This is the flip side of the SliverOverlapAbsorber
+                            // above.
+                            handle:
+                                NestedScrollView.sliverOverlapAbsorberHandleFor(
+                                  context,
+                                ),
                           ),
-                        ),
-                      ],
-                    );
-                  },
-                ),
-              );
-            }).toList(),
+                          SliverPadding(
+                            padding: const .all(8.0),
+                            // In this example, the inner scroll view has
+                            // fixed-height list items, hence the use of
+                            // SliverFixedExtentList. However, one could use any
+                            // sliver widget here, e.g. SliverList or SliverGrid.
+                            sliver: SliverFixedExtentList.builder(
+                              // The items in this example are fixed to 48 pixels
+                              // high. This matches the Material Design spec for
+                              // ListTile widgets.
+                              itemExtent: 48.0,
+                              // The itemCount of the SliverFixedExtentList.builder
+                              // specifies how many children this inner list
+                              // has. In this example, each tab has a list of
+                              // exactly 30 items, but this is arbitrary.
+                              itemCount: 30,
+                              itemBuilder: (BuildContext context, int index) {
+                                // This builder is called for each child.
+                                // In this example, we just number each list item.
+                                return ListTile(title: Text('Item $index'));
+                              },
+                            ),
+                          ),
+                        ],
+                      );
+                    },
+                  ),
+                );
+              }).toList(),
+            ),
           ),
         ),
       ),
diff --git a/examples/api/test/widgets/nested_scroll_view/nested_scroll_view.0_test.dart b/examples/api/test/widgets/nested_scroll_view/nested_scroll_view.0_test.dart
index 7768df8..b183365 100644
--- a/examples/api/test/widgets/nested_scroll_view/nested_scroll_view.0_test.dart
+++ b/examples/api/test/widgets/nested_scroll_view/nested_scroll_view.0_test.dart
@@ -2,6 +2,7 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
+import 'package:flutter/gestures.dart';
 import 'package:flutter/material.dart';
 import 'package:flutter_api_samples/widgets/nested_scroll_view/nested_scroll_view.0.dart'
     as example;
@@ -50,4 +51,52 @@
       lessThan(initialAppBarHeight),
     );
   });
+
+  testWidgets(
+    'Does not crash when scrolling an inner list then switching tabs on desktop',
+    (WidgetTester tester) async {
+      // Regression test for https://github.com/flutter/flutter/issues/183199
+      //
+      // Without the ScrollConfiguration in this example, the default desktop
+      // scrollbar attaches to the NestedScrollView's coordinated controller and
+      // throws once more than one ScrollPosition is attached to it, which happens
+      // mid tab transition.
+      await tester.pumpWidget(const example.NestedScrollViewExampleApp());
+      await tester.pumpAndSettle();
+
+      // The example opts out of the default scrollbars for its body.
+      expect(find.byType(Scrollbar), findsNothing);
+
+      // Scroll the first tab's inner list.
+      await tester.drag(
+        find.text('Item 0'),
+        const Offset(0.0, -100.0),
+        touchSlopY: 0.0,
+      );
+      await tester.pump();
+
+      // Begin, but do not finish, a tab transition so both tabs' inner scroll
+      // views are attached to the coordinated controller at the same time.
+      await tester.fling(
+        find.byType(TabBarView),
+        const Offset(-300.0, 0.0),
+        800.0,
+      );
+      await tester.pump();
+      await tester.pump(const Duration(milliseconds: 30));
+
+      // A mouse-wheel pointer signal mid-transition previously drove the
+      // scrollbar validation while more than one position was attached.
+      final TestPointer pointer = TestPointer(1, PointerDeviceKind.mouse);
+      final Offset center = tester.getCenter(find.byType(TabBarView));
+      await tester.sendEventToBinding(pointer.hover(center));
+      await tester.sendEventToBinding(pointer.scroll(const Offset(0.0, 60.0)));
+      await tester.pump();
+      await tester.pump(const Duration(milliseconds: 700));
+      await tester.pumpAndSettle();
+
+      expect(tester.takeException(), isNull);
+    },
+    variant: TargetPlatformVariant.desktop(),
+  );
 }