| // 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/widgets.dart'; |
| |
| import 'banner_theme.dart'; |
| import 'button_bar.dart'; |
| import 'button_theme.dart'; |
| import 'divider.dart'; |
| import 'theme.dart'; |
| |
| /// A Material Design banner. |
| /// |
| /// A banner displays an important, succinct message, and provides actions for |
| /// users to address (or dismiss the banner). A user action is required for it |
| /// to be dismissed. |
| /// |
| /// Banners should be displayed at the top of the screen, below a top app bar. |
| /// They are persistent and non-modal, allowing the user to either ignore them or |
| /// interact with them at any time. |
| /// |
| /// The [actions] will be placed beside the [content] if there is only one. |
| /// Otherwise, the [actions] will be placed below the [content]. Use |
| /// [forceActionsBelow] to override this behavior. |
| /// |
| /// The [actions] and [content] must be provided. An optional leading widget |
| /// (typically an [Image]) can also be provided. The [contentTextStyle] and |
| /// [backgroundColor] can be provided to customize the banner. |
| /// |
| /// This widget is unrelated to the widgets library [Banner] widget. |
| class MaterialBanner extends StatelessWidget { |
| /// Creates a [MaterialBanner]. |
| /// |
| /// The [actions], [content], and [forceActionsBelow] must be non-null. |
| /// The [actions.length] must be greater than 0. |
| const MaterialBanner({ |
| Key key, |
| @required this.content, |
| this.contentTextStyle, |
| @required this.actions, |
| this.leading, |
| this.backgroundColor, |
| this.padding, |
| this.leadingPadding, |
| this.forceActionsBelow = false, |
| }) : assert(content != null), |
| assert(actions != null), |
| assert(forceActionsBelow != null), |
| super(key: key); |
| |
| /// The content of the [MaterialBanner]. |
| /// |
| /// Typically a [Text] widget. |
| final Widget content; |
| |
| /// Style for the text in the [content] of the [MaterialBanner]. |
| /// |
| /// If `null`, [MaterialBannerThemeData.contentTextStyle] is used. If that is |
| /// also `null`, [ThemeData.textTheme.bodyText2] is used. |
| final TextStyle contentTextStyle; |
| |
| /// The set of actions that are displayed at the bottom or trailing side of |
| /// the [MaterialBanner]. |
| /// |
| /// Typically this is a list of [FlatButton] widgets. |
| /// |
| /// These widgets will be wrapped in a [ButtonBar], which introduces 8 pixels |
| /// of padding on each side. |
| final List<Widget> actions; |
| |
| /// The (optional) leading widget of the [MaterialBanner]. |
| /// |
| /// Typically an [Icon] widget. |
| final Widget leading; |
| |
| /// The color of the surface of this [MaterialBanner]. |
| /// |
| /// If `null`, [MaterialBannerThemeData.backgroundColor] is used. If that is |
| /// also `null`, [ThemeData.colorScheme.surface] is used. |
| final Color backgroundColor; |
| |
| /// The amount of space by which to inset the [content]. |
| /// |
| /// If the [actions] are below the [content], this defaults to |
| /// `EdgeInsetsDirectional.only(start: 16.0, top: 24.0, end: 16.0, bottom: 4.0)`. |
| /// |
| /// If the [actions] are trailing the [content], this defaults to |
| /// `EdgeInsetsDirectional.only(start: 16.0, top: 2.0)`. |
| final EdgeInsetsGeometry padding; |
| |
| /// The amount of space by which to inset the [leading] widget. |
| /// |
| /// This defaults to `EdgeInsetsDirectional.only(end: 16.0)`. |
| final EdgeInsetsGeometry leadingPadding; |
| |
| /// An override to force the [actions] to be below the [content] regardless of |
| /// how many there are. |
| /// |
| /// If this is `true`, the [actions] will be placed below the [content]. If |
| /// this is `false`, the [actions] will be placed on the trailing side of the |
| /// [content] if [actions.length] is `1` and below the [content] if greater |
| /// than `1`. |
| final bool forceActionsBelow; |
| |
| @override |
| Widget build(BuildContext context) { |
| assert(actions.isNotEmpty); |
| |
| final ThemeData theme = Theme.of(context); |
| final MaterialBannerThemeData bannerTheme = MaterialBannerTheme.of(context); |
| |
| final bool isSingleRow = actions.length == 1 && !forceActionsBelow; |
| final EdgeInsetsGeometry padding = this.padding ?? bannerTheme.padding ?? (isSingleRow |
| ? const EdgeInsetsDirectional.only(start: 16.0, top: 2.0) |
| : const EdgeInsetsDirectional.only(start: 16.0, top: 24.0, end: 16.0, bottom: 4.0)); |
| final EdgeInsetsGeometry leadingPadding = this.leadingPadding |
| ?? bannerTheme.padding |
| ?? const EdgeInsetsDirectional.only(end: 16.0); |
| |
| final Widget buttonBar = ButtonBar( |
| layoutBehavior: ButtonBarLayoutBehavior.constrained, |
| children: actions, |
| ); |
| |
| final Color backgroundColor = this.backgroundColor |
| ?? bannerTheme.backgroundColor |
| ?? theme.colorScheme.surface; |
| final TextStyle textStyle = contentTextStyle |
| ?? bannerTheme.contentTextStyle |
| ?? theme.textTheme.bodyText2; |
| |
| return Container( |
| color: backgroundColor, |
| child: Column( |
| children: <Widget>[ |
| Padding( |
| padding: padding, |
| child: Row( |
| children: <Widget>[ |
| if (leading != null) |
| Padding( |
| padding: leadingPadding, |
| child: leading, |
| ), |
| Expanded( |
| child: DefaultTextStyle( |
| style: textStyle, |
| child: content, |
| ), |
| ), |
| if (isSingleRow) |
| buttonBar, |
| ], |
| ), |
| ), |
| if (!isSingleRow) |
| buttonBar, |
| const Divider(height: 0), |
| ], |
| ), |
| ); |
| } |
| } |