Fix PageView API doc sample fails on Desktop and Web (#135910)

### Description

This PR aims to improve/fix the PageView API doc sample for Web and Desktop platforms. On these platforms, mouse dragging gestures do not provide natural behavior similar to other desktop applications. This PR will add navigation buttons (indicators) so users can interact with the demo.

### Related issue

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

### Demo video

https://github.com/flutter/flutter/assets/104349824/0f9c60bd-8b18-404e-b5b6-1d594604de31
diff --git a/dev/bots/check_code_samples.dart b/dev/bots/check_code_samples.dart
index 1082702..b9ef328 100644
--- a/dev/bots/check_code_samples.dart
+++ b/dev/bots/check_code_samples.dart
@@ -392,7 +392,6 @@
   'examples/api/test/widgets/nested_scroll_view/nested_scroll_view.2_test.dart',
   'examples/api/test/widgets/nested_scroll_view/nested_scroll_view.1_test.dart',
   'examples/api/test/widgets/nested_scroll_view/nested_scroll_view.0_test.dart',
-  'examples/api/test/widgets/page_view/page_view.0_test.dart',
   'examples/api/test/widgets/scroll_position/scroll_metrics_notification.0_test.dart',
   'examples/api/test/widgets/media_query/media_query_data.system_gesture_insets.0_test.dart',
   'examples/api/test/widgets/async/stream_builder.0_test.dart',
diff --git a/examples/api/lib/widgets/page_view/page_view.0.dart b/examples/api/lib/widgets/page_view/page_view.0.dart
index 862aafc..4bf3581 100644
--- a/examples/api/lib/widgets/page_view/page_view.0.dart
+++ b/examples/api/lib/widgets/page_view/page_view.0.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/foundation.dart';
 import 'package:flutter/material.dart';
 
 /// Flutter code sample for [PageView].
@@ -22,27 +23,171 @@
   }
 }
 
-class PageViewExample extends StatelessWidget {
+class PageViewExample extends StatefulWidget {
   const PageViewExample({super.key});
 
   @override
+  State<PageViewExample> createState() => _PageViewExampleState();
+}
+
+class _PageViewExampleState extends State<PageViewExample> with TickerProviderStateMixin {
+  late PageController _pageViewController;
+  late TabController _tabController;
+  int _currentPageIndex = 0;
+
+  @override
+  void initState() {
+    super.initState();
+    _pageViewController = PageController();
+    _tabController = TabController(length: 3, vsync: this);
+  }
+
+  @override
+  void dispose() {
+    super.dispose();
+    _pageViewController.dispose();
+    _tabController.dispose();
+  }
+
+  @override
   Widget build(BuildContext context) {
-    final PageController controller = PageController();
-    return PageView(
-      /// [PageView.scrollDirection] defaults to [Axis.horizontal].
-      /// Use [Axis.vertical] to scroll vertically.
-      controller: controller,
-      children: const <Widget>[
-        Center(
-          child: Text('First Page'),
+    final TextTheme textTheme = Theme.of(context).textTheme;
+
+    return Stack(
+      alignment: Alignment.bottomCenter,
+      children: <Widget>[
+        PageView(
+          /// [PageView.scrollDirection] defaults to [Axis.horizontal].
+          /// Use [Axis.vertical] to scroll vertically.
+          controller: _pageViewController,
+          onPageChanged: _handlePageViewChanged,
+          children: <Widget>[
+            Center(
+              child: Text('First Page', style: textTheme.titleLarge),
+            ),
+            Center(
+              child: Text('Second Page', style: textTheme.titleLarge),
+            ),
+            Center(
+              child: Text('Third Page', style: textTheme.titleLarge),
+            ),
+          ],
         ),
-        Center(
-          child: Text('Second Page'),
-        ),
-        Center(
-          child: Text('Third Page'),
+        PageIndicator(
+          tabController: _tabController,
+          currentPageIndex: _currentPageIndex,
+          onUpdateCurrentPageIndex: _updateCurrentPageIndex,
+          isOnDesktopAndWeb: _isOnDesktopAndWeb,
         ),
       ],
     );
   }
+
+  void _handlePageViewChanged(int currentPageIndex) {
+    if (!_isOnDesktopAndWeb) {
+      return;
+    }
+    _tabController.index = currentPageIndex;
+    setState(() {
+      _currentPageIndex = currentPageIndex;
+    });
+  }
+
+  void _updateCurrentPageIndex(int index) {
+    _tabController.index = index;
+    _pageViewController.animateToPage(
+      index,
+      duration: const Duration(milliseconds: 400),
+      curve: Curves.easeInOut,
+    );
+  }
+
+  bool get _isOnDesktopAndWeb {
+    if (kIsWeb) {
+      return true;
+    }
+    switch (defaultTargetPlatform) {
+      case TargetPlatform.macOS:
+      case TargetPlatform.linux:
+      case TargetPlatform.windows:
+        return true;
+      case TargetPlatform.android:
+      case TargetPlatform.iOS:
+      case TargetPlatform.fuchsia:
+        return false;
+    }
+  }
+}
+
+/// Page indicator for desktop and web platforms.
+///
+/// On Desktop and Web, drag gesture for horizontal scrolling in a PageView is disabled by default.
+/// You can defined a custom scroll behavior to activate drag gestures,
+/// see https://docs.flutter.dev/release/breaking-changes/default-scroll-behavior-drag.
+///
+/// In this sample, we use a TabPageSelector to navigate between pages,
+/// in order to build natural behavior similar to other desktop applications.
+class PageIndicator extends StatelessWidget {
+  const PageIndicator({
+    super.key,
+    required this.tabController,
+    required this.currentPageIndex,
+    required this.onUpdateCurrentPageIndex,
+    required this.isOnDesktopAndWeb,
+  });
+
+  final int currentPageIndex;
+  final TabController tabController;
+  final void Function(int) onUpdateCurrentPageIndex;
+  final bool isOnDesktopAndWeb;
+
+  @override
+  Widget build(BuildContext context) {
+    if (!isOnDesktopAndWeb) {
+      return const SizedBox.shrink();
+    }
+    final ColorScheme colorScheme = Theme.of(context).colorScheme;
+
+    return Padding(
+      padding: const EdgeInsets.all(8.0),
+      child: Row(
+        mainAxisAlignment: MainAxisAlignment.center,
+        children: <Widget>[
+          IconButton(
+            splashRadius: 16.0,
+            padding: EdgeInsets.zero,
+            onPressed: () {
+              if (currentPageIndex == 0) {
+                return;
+              }
+              onUpdateCurrentPageIndex(currentPageIndex - 1);
+            },
+            icon: const Icon(
+              Icons.arrow_left_rounded,
+              size: 32.0,
+            ),
+          ),
+          TabPageSelector(
+            controller: tabController,
+            color: colorScheme.background,
+            selectedColor: colorScheme.primary,
+          ),
+          IconButton(
+            splashRadius: 16.0,
+            padding: EdgeInsets.zero,
+            onPressed: () {
+              if (currentPageIndex == 2) {
+                return;
+              }
+              onUpdateCurrentPageIndex(currentPageIndex + 1);
+            },
+            icon: const Icon(
+              Icons.arrow_right_rounded,
+              size: 32.0,
+            ),
+          ),
+        ],
+      ),
+    );
+  }
 }
diff --git a/examples/api/test/widgets/page_view/page_view.0_test.dart b/examples/api/test/widgets/page_view/page_view.0_test.dart
new file mode 100644
index 0000000..a994f22
--- /dev/null
+++ b/examples/api/test/widgets/page_view/page_view.0_test.dart
@@ -0,0 +1,66 @@
+// 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_api_samples/widgets/page_view/page_view.0.dart' as example;
+import 'package:flutter_test/flutter_test.dart';
+
+void main() {
+  testWidgets('PageView swipe gestures on mobile platforms', (WidgetTester tester) async {
+    await tester.pumpWidget(
+      const example.PageViewExampleApp(),
+    );
+
+    // Verify that first page is shown initially.
+    expect(find.text('First Page'), findsOneWidget);
+
+    // Swipe to the left.
+    await tester.fling(find.text('First Page'), const Offset(-300.0, 0.0), 3000);
+    await tester.pumpAndSettle();
+    // Verify that the second page is shown.
+    expect(find.text('Second Page'), findsOneWidget);
+
+    // Swipe back to the right.
+    await tester.fling(find.text('Second Page'), const Offset(300.0, 0.0), 3000);
+    await tester.pumpAndSettle();
+    // Verify that first page is shown.
+    expect(find.text('First Page'), findsOneWidget);
+  }, variant: TargetPlatformVariant.mobile());
+
+  testWidgets('PageView navigation using forward/backward buttons on desktop platforms', (WidgetTester tester) async {
+    await tester.pumpWidget(
+      const example.PageViewExampleApp(),
+    );
+
+    // Verify that first page is shown along with forward/backward buttons in page indicator.
+    expect(find.text('First Page'), findsOneWidget);
+    expect(find.byType(TabPageSelector), findsOneWidget);
+
+    // Tap forward button on page indicator area.
+    await tester.tap(find.byIcon(Icons.arrow_right_rounded));
+    await tester.pumpAndSettle();
+    // Verify that second page is shown.
+    expect(find.text('Second Page'), findsOneWidget);
+
+    // Verify that page indicator index is updated.
+    final TabPageSelector pageIndicator = tester.widget<TabPageSelector>(find.byType(TabPageSelector));
+    expect(pageIndicator.controller?.index, 1);
+
+    // Verify that page view index is also updated with same index to page indicator.
+    final PageView pageView = tester.widget<PageView>(find.byType(PageView));
+    expect(pageView.controller.page, 1);
+
+    // Tap backward button on page indicator area.
+    await tester.tap(find.byIcon(Icons.arrow_left_rounded));
+    await tester.pumpAndSettle();
+    // Verify that first page is shown.
+    expect(find.text('First Page'), findsOneWidget);
+
+    // Tap backward button one more time.
+    await tester.tap(find.byIcon(Icons.arrow_left_rounded));
+    await tester.pumpAndSettle();
+    // Verify that first page is still shown.
+    expect(find.text('First Page'), findsOneWidget);
+  }, variant: TargetPlatformVariant.desktop());
+}