Unverified Commit e8a0cf3a authored by huycozy's avatar huycozy Committed by GitHub

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
parent 406c4dbf
...@@ -392,7 +392,6 @@ final Set<String> _knownMissingTests = <String>{ ...@@ -392,7 +392,6 @@ final Set<String> _knownMissingTests = <String>{
'examples/api/test/widgets/nested_scroll_view/nested_scroll_view.2_test.dart', '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.1_test.dart',
'examples/api/test/widgets/nested_scroll_view/nested_scroll_view.0_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/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/media_query/media_query_data.system_gesture_insets.0_test.dart',
'examples/api/test/widgets/async/stream_builder.0_test.dart', 'examples/api/test/widgets/async/stream_builder.0_test.dart',
......
...@@ -2,6 +2,7 @@ ...@@ -2,6 +2,7 @@
// Use of this source code is governed by a BSD-style license that can be // Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file. // found in the LICENSE file.
import 'package:flutter/foundation.dart';
import 'package:flutter/material.dart'; import 'package:flutter/material.dart';
/// Flutter code sample for [PageView]. /// Flutter code sample for [PageView].
...@@ -22,27 +23,171 @@ class PageViewExampleApp extends StatelessWidget { ...@@ -22,27 +23,171 @@ class PageViewExampleApp extends StatelessWidget {
} }
} }
class PageViewExample extends StatelessWidget { class PageViewExample extends StatefulWidget {
const PageViewExample({super.key}); 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 @override
Widget build(BuildContext context) { Widget build(BuildContext context) {
final PageController controller = PageController(); final TextTheme textTheme = Theme.of(context).textTheme;
return PageView(
/// [PageView.scrollDirection] defaults to [Axis.horizontal]. return Stack(
/// Use [Axis.vertical] to scroll vertically. alignment: Alignment.bottomCenter,
controller: controller, children: <Widget>[
children: const <Widget>[ PageView(
Center( /// [PageView.scrollDirection] defaults to [Axis.horizontal].
child: Text('First Page'), /// Use [Axis.vertical] to scroll vertically.
), controller: _pageViewController,
Center( onPageChanged: _handlePageViewChanged,
child: Text('Second Page'), 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( PageIndicator(
child: Text('Third Page'), 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,
),
),
],
),
);
}
} }
// 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());
}
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