| // 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<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 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), |
| ), |
| ], |
| ), |
| 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.surface, |
| 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, |
| ), |
| ), |
| ], |
| ), |
| ); |
| } |
| } |