// 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/foundation.dart'; import 'package:flutter/material.dart'; /// Flutter code sample for [PageView]. void main() => runApp(const PageViewExampleApp()); class PageViewExampleApp extends StatelessWidget { const PageViewExampleApp({super.key}); @override Widget build(BuildContext context) { return MaterialApp( home: Scaffold( appBar: AppBar(title: const Text('PageView Sample')), body: const PageViewExample(), ), ); } } class PageViewExample extends StatefulWidget { const PageViewExample({super.key}); @override State createState() => _PageViewExampleState(); } class _PageViewExampleState extends State 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 TextTheme textTheme = Theme.of(context).textTheme; return Stack( alignment: Alignment.bottomCenter, children: [ PageView( /// [PageView.scrollDirection] defaults to [Axis.horizontal]. /// Use [Axis.vertical] to scroll vertically. controller: _pageViewController, onPageChanged: _handlePageViewChanged, children: [ Center( child: Text('First Page', style: textTheme.titleLarge), ), Center( child: Text('Second Page', style: textTheme.titleLarge), ), Center( child: Text('Third Page', style: textTheme.titleLarge), ), ], ), 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: [ 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, ), ), ], ), ); } }