| // 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/widgets.dart'; |
| |
| import 'action_icons_theme.dart'; |
| import 'button_style.dart'; |
| import 'debug.dart'; |
| import 'icon_button.dart'; |
| import 'icons.dart'; |
| import 'material_localizations.dart'; |
| import 'scaffold.dart'; |
| import 'theme.dart'; |
| |
| abstract class _ActionButton extends StatelessWidget { |
| /// Creates a Material Design icon button. |
| const _ActionButton({ |
| super.key, |
| this.color, |
| required this.icon, |
| required this.onPressed, |
| this.style, |
| }); |
| |
| /// The icon to display inside the button. |
| final Widget icon; |
| |
| /// The callback that is called when the button is tapped |
| /// or otherwise activated. |
| /// |
| /// If this is set to null, the button will do a default action |
| /// when it is tapped or activated. |
| final VoidCallback? onPressed; |
| |
| /// The color to use for the icon. |
| /// |
| /// Defaults to the [IconThemeData.color] specified in the ambient [IconTheme], |
| /// which usually matches the ambient [Theme]'s [ThemeData.iconTheme]. |
| final Color? color; |
| |
| /// Customizes this icon button's appearance. |
| /// |
| /// The [style] is only used for Material 3 [IconButton]s. If [ThemeData.useMaterial3] |
| /// is set to true, [style] is preferred for icon button customization, and any |
| /// parameters defined in [style] will override the same parameters in [IconButton]. |
| /// |
| /// Null by default. |
| final ButtonStyle? style; |
| |
| /// This returns the appropriate tooltip text for this action button. |
| String _getTooltip(BuildContext context); |
| |
| /// This is the default function that is called when [onPressed] is set |
| /// to null. |
| void _onPressedCallback(BuildContext context); |
| |
| @override |
| Widget build(BuildContext context) { |
| assert(debugCheckHasMaterialLocalizations(context)); |
| return IconButton( |
| icon: icon, |
| style: style, |
| color: color, |
| tooltip: _getTooltip(context), |
| onPressed: () { |
| if (onPressed != null) { |
| onPressed!(); |
| } else { |
| _onPressedCallback(context); |
| } |
| }, |
| ); |
| } |
| } |
| |
| typedef _ActionIconBuilderCallback = WidgetBuilder? Function(ActionIconThemeData? actionIconTheme); |
| typedef _ActionIconDataCallback = IconData Function(BuildContext context); |
| typedef _AndroidSemanticsLabelCallback = String Function(MaterialLocalizations materialLocalization); |
| |
| class _ActionIcon extends StatelessWidget { |
| const _ActionIcon({ |
| required this.iconBuilderCallback, |
| required this.getIcon, |
| required this.getAndroidSemanticsLabel, |
| }); |
| |
| final _ActionIconBuilderCallback iconBuilderCallback; |
| final _ActionIconDataCallback getIcon; |
| final _AndroidSemanticsLabelCallback getAndroidSemanticsLabel; |
| |
| @override |
| Widget build(BuildContext context) { |
| final ActionIconThemeData? actionIconTheme = ActionIconTheme.of(context); |
| final WidgetBuilder? iconBuilder = iconBuilderCallback(actionIconTheme); |
| if (iconBuilder != null) { |
| return iconBuilder(context); |
| } |
| |
| final IconData data = getIcon(context); |
| final String? semanticsLabel; |
| // This can't use the platform from Theme because it is the Android OS that |
| // expects the duplicated tooltip and label. |
| switch (defaultTargetPlatform) { |
| case TargetPlatform.android: |
| semanticsLabel = getAndroidSemanticsLabel(MaterialLocalizations.of(context)); |
| case TargetPlatform.fuchsia: |
| case TargetPlatform.linux: |
| case TargetPlatform.windows: |
| case TargetPlatform.iOS: |
| case TargetPlatform.macOS: |
| semanticsLabel = null; |
| } |
| |
| return Icon(data, semanticLabel: semanticsLabel); |
| } |
| } |
| |
| /// A "back" icon that's appropriate for the current [TargetPlatform]. |
| /// |
| /// The current platform is determined by querying for the ambient [Theme]. |
| /// |
| /// See also: |
| /// |
| /// * [BackButton], an [IconButton] with a [BackButtonIcon] that calls |
| /// [Navigator.maybePop] to return to the previous route. |
| /// * [IconButton], which is a more general widget for creating buttons |
| /// with icons. |
| /// * [Icon], a Material Design icon. |
| /// * [ThemeData.platform], which specifies the current platform. |
| class BackButtonIcon extends StatelessWidget { |
| /// Creates an icon that shows the appropriate "back" image for |
| /// the current platform (as obtained from the [Theme]). |
| const BackButtonIcon({ super.key }); |
| |
| @override |
| Widget build(BuildContext context) { |
| return _ActionIcon( |
| iconBuilderCallback: (ActionIconThemeData? actionIconTheme) { |
| return actionIconTheme?.backButtonIconBuilder; |
| }, |
| getIcon: (BuildContext context) { |
| if (kIsWeb) { |
| // Always use 'Icons.arrow_back' as a back_button icon in web. |
| return Icons.arrow_back; |
| } |
| switch (Theme.of(context).platform) { |
| case TargetPlatform.android: |
| case TargetPlatform.fuchsia: |
| case TargetPlatform.linux: |
| case TargetPlatform.windows: |
| return Icons.arrow_back; |
| case TargetPlatform.iOS: |
| case TargetPlatform.macOS: |
| return Icons.arrow_back_ios; |
| } |
| }, |
| getAndroidSemanticsLabel: (MaterialLocalizations materialLocalization) { |
| return materialLocalization.backButtonTooltip; |
| }, |
| ); |
| } |
| } |
| |
| /// A Material Design back icon button. |
| /// |
| /// A [BackButton] is an [IconButton] with a "back" icon appropriate for the |
| /// current [TargetPlatform]. When pressed, the back button calls |
| /// [Navigator.maybePop] to return to the previous route unless a custom |
| /// [onPressed] callback is provided. |
| /// |
| /// The [onPressed] callback can, for instance, be used to pop the platform's navigation stack |
| /// via [SystemNavigator] instead of Flutter's [Navigator] in add-to-app |
| /// situations. |
| /// |
| /// In Material Design 3, both [style]'s [ButtonStyle.iconColor] and [color] are |
| /// used to override the default icon color of [BackButton]. If both exist, the [ButtonStyle.iconColor] |
| /// will override [color] for states where [ButtonStyle.foregroundColor] resolves to non-null. |
| /// |
| /// When deciding to display a [BackButton], consider using |
| /// `ModalRoute.of(context)?.canPop` to check whether the current route can be |
| /// popped. If that value is false (e.g., because the current route is the |
| /// initial route), the [BackButton] will not have any effect when pressed, |
| /// which could frustrate the user. |
| /// |
| /// Requires one of its ancestors to be a [Material] widget. |
| /// |
| /// See also: |
| /// |
| /// * [AppBar], which automatically uses a [BackButton] in its |
| /// [AppBar.leading] slot when the [Scaffold] has no [Drawer] and the |
| /// current [Route] is not the [Navigator]'s first route. |
| /// * [BackButtonIcon], which is useful if you need to create a back button |
| /// that responds differently to being pressed. |
| /// * [IconButton], which is a more general widget for creating buttons with |
| /// icons. |
| /// * [CloseButton], an alternative which may be more appropriate for leaf |
| /// node pages in the navigation tree. |
| class BackButton extends _ActionButton { |
| /// Creates an [IconButton] with the appropriate "back" icon for the current |
| /// target platform. |
| const BackButton({ |
| super.key, |
| super.color, |
| super.style, |
| super.onPressed, |
| }) : super(icon: const BackButtonIcon()); |
| |
| @override |
| void _onPressedCallback(BuildContext context) => Navigator.maybePop(context); |
| |
| @override |
| String _getTooltip(BuildContext context) { |
| return MaterialLocalizations.of(context).backButtonTooltip; |
| } |
| } |
| |
| /// A "close" icon that's appropriate for the current [TargetPlatform]. |
| /// |
| /// The current platform is determined by querying for the ambient [Theme]. |
| /// |
| /// See also: |
| /// |
| /// * [CloseButton], an [IconButton] with a [CloseButtonIcon] that calls |
| /// [Navigator.maybePop] to return to the previous route. |
| /// * [IconButton], which is a more general widget for creating buttons |
| /// with icons. |
| /// * [Icon], a Material Design icon. |
| /// * [ThemeData.platform], which specifies the current platform. |
| class CloseButtonIcon extends StatelessWidget { |
| /// Creates an icon that shows the appropriate "close" image for |
| /// the current platform (as obtained from the [Theme]). |
| const CloseButtonIcon({ super.key }); |
| |
| @override |
| Widget build(BuildContext context) { |
| return _ActionIcon( |
| iconBuilderCallback: (ActionIconThemeData? actionIconTheme) { |
| return actionIconTheme?.closeButtonIconBuilder; |
| }, |
| getIcon: (BuildContext context) => Icons.close, |
| getAndroidSemanticsLabel: (MaterialLocalizations materialLocalization) { |
| return materialLocalization.closeButtonTooltip; |
| }, |
| ); |
| } |
| } |
| |
| /// A Material Design close icon button. |
| /// |
| /// A [CloseButton] is an [IconButton] with a "close" icon. When pressed, the |
| /// close button calls [Navigator.maybePop] to return to the previous route. |
| /// |
| /// The [onPressed] callback can, for instance, be used to pop the platform's navigation stack |
| /// via [SystemNavigator] instead of Flutter's [Navigator] in add-to-app |
| /// situations. |
| /// |
| /// In Material Design 3, both [style]'s [ButtonStyle.iconColor] and [color] are |
| /// used to override the default icon color of [CloseButton]. If both exist, the [ButtonStyle.iconColor] |
| /// will override [color] for states where [ButtonStyle.foregroundColor] resolves to non-null. |
| /// |
| /// Use a [CloseButton] instead of a [BackButton] on fullscreen dialogs or |
| /// pages that may solicit additional actions to close. |
| /// |
| /// See also: |
| /// |
| /// * [AppBar], which automatically uses a [CloseButton] in its |
| /// [AppBar.leading] slot when appropriate. |
| /// * [BackButton], which is more appropriate for middle nodes in the |
| /// navigation tree or where pages can be popped instantaneously with |
| /// no user data consequence. |
| /// * [IconButton], to create other Material Design icon buttons. |
| class CloseButton extends _ActionButton { |
| /// Creates a Material Design close icon button. |
| const CloseButton({ super.key, super.color, super.onPressed, super.style }) |
| : super(icon: const CloseButtonIcon()); |
| |
| @override |
| void _onPressedCallback(BuildContext context) => Navigator.maybePop(context); |
| |
| @override |
| String _getTooltip(BuildContext context) { |
| return MaterialLocalizations.of(context).closeButtonTooltip; |
| } |
| } |
| |
| /// A "drawer" icon that's appropriate for the current [TargetPlatform]. |
| /// |
| /// The current platform is determined by querying for the ambient [Theme]. |
| /// |
| /// See also: |
| /// |
| /// * [DrawerButton], an [IconButton] with a [DrawerButtonIcon] that calls |
| /// [ScaffoldState.openDrawer] to open the [Scaffold.drawer]. |
| /// * [EndDrawerButton], an [IconButton] with an [EndDrawerButtonIcon] that |
| /// calls [ScaffoldState.openEndDrawer] to open the [Scaffold.endDrawer]. |
| /// * [IconButton], which is a more general widget for creating buttons |
| /// with icons. |
| /// * [Icon], a Material Design icon. |
| /// * [ThemeData.platform], which specifies the current platform. |
| class DrawerButtonIcon extends StatelessWidget { |
| /// Creates an icon that shows the appropriate "close" image for |
| /// the current platform (as obtained from the [Theme]). |
| const DrawerButtonIcon({ super.key }); |
| |
| @override |
| Widget build(BuildContext context) { |
| return _ActionIcon( |
| iconBuilderCallback: (ActionIconThemeData? actionIconTheme) { |
| return actionIconTheme?.drawerButtonIconBuilder; |
| }, |
| getIcon: (BuildContext context) => Icons.menu, |
| getAndroidSemanticsLabel: (MaterialLocalizations materialLocalization) { |
| return materialLocalization.openAppDrawerTooltip; |
| }, |
| ); |
| } |
| } |
| |
| /// A Material Design drawer icon button. |
| /// |
| /// A [DrawerButton] is an [IconButton] with a "drawer" icon. When pressed, the |
| /// close button calls [ScaffoldState.openDrawer] to the [Scaffold.drawer]. |
| /// |
| /// The default behaviour on press can be overridden with [onPressed]. |
| /// |
| /// See also: |
| /// |
| /// * [EndDrawerButton], an [IconButton] with an [EndDrawerButtonIcon] that |
| /// calls [ScaffoldState.openEndDrawer] to open the [Scaffold.endDrawer]. |
| /// * [IconButton], which is a more general widget for creating buttons |
| /// with icons. |
| /// * [Icon], a Material Design icon. |
| /// * [ThemeData.platform], which specifies the current platform. |
| class DrawerButton extends _ActionButton { |
| /// Creates a Material Design drawer icon button. |
| const DrawerButton({ |
| super.key, |
| super.style, |
| super.onPressed, |
| }) : super(icon: const DrawerButtonIcon()); |
| |
| @override |
| void _onPressedCallback(BuildContext context) => Scaffold.of(context).openDrawer(); |
| |
| @override |
| String _getTooltip(BuildContext context) { |
| return MaterialLocalizations.of(context).openAppDrawerTooltip; |
| } |
| } |
| |
| /// A "end drawer" icon that's appropriate for the current [TargetPlatform]. |
| /// |
| /// The current platform is determined by querying for the ambient [Theme]. |
| /// |
| /// See also: |
| /// |
| /// * [DrawerButton], an [IconButton] with a [DrawerButtonIcon] that calls |
| /// [ScaffoldState.openDrawer] to open the [Scaffold.drawer]. |
| /// * [EndDrawerButton], an [IconButton] with an [EndDrawerButtonIcon] that |
| /// calls [ScaffoldState.openEndDrawer] to open the [Scaffold.endDrawer] |
| /// * [IconButton], which is a more general widget for creating buttons |
| /// with icons. |
| /// * [Icon], a Material Design icon. |
| /// * [ThemeData.platform], which specifies the current platform. |
| class EndDrawerButtonIcon extends StatelessWidget { |
| /// Creates an icon that shows the appropriate "end drawer" image for |
| /// the current platform (as obtained from the [Theme]). |
| const EndDrawerButtonIcon({ super.key }); |
| |
| @override |
| Widget build(BuildContext context) { |
| return _ActionIcon( |
| iconBuilderCallback: (ActionIconThemeData? actionIconTheme) { |
| return actionIconTheme?.endDrawerButtonIconBuilder; |
| }, |
| getIcon: (BuildContext context) => Icons.menu, |
| getAndroidSemanticsLabel: (MaterialLocalizations materialLocalization) { |
| return materialLocalization.openAppDrawerTooltip; |
| }, |
| ); |
| } |
| } |
| |
| /// A Material Design end drawer icon button. |
| /// |
| /// A [EndDrawerButton] is an [IconButton] with a "drawer" icon. When pressed, the |
| /// end drawer button calls [ScaffoldState.openEndDrawer] to open the [Scaffold.endDrawer]. |
| /// |
| /// The default behaviour on press can be overridden with [onPressed]. |
| /// |
| /// See also: |
| /// |
| /// * [DrawerButton], an [IconButton] with a [DrawerButtonIcon] that calls |
| /// [ScaffoldState.openDrawer] to open a drawer. |
| /// * [IconButton], which is a more general widget for creating buttons |
| /// with icons. |
| /// * [Icon], a Material Design icon. |
| /// * [ThemeData.platform], which specifies the current platform. |
| class EndDrawerButton extends _ActionButton { |
| /// Creates a Material Design end drawer icon button. |
| const EndDrawerButton({ |
| super.key, |
| super.style, |
| super.onPressed, |
| }) : super(icon: const EndDrawerButtonIcon()); |
| |
| @override |
| void _onPressedCallback(BuildContext context) => Scaffold.of(context).openEndDrawer(); |
| |
| @override |
| String _getTooltip(BuildContext context) { |
| return MaterialLocalizations.of(context).openAppDrawerTooltip; |
| } |
| } |