|
|
@@ -1,5 +1,5 @@
|
|
|
-import 'package:flutter/foundation.dart';
|
|
|
import 'package:flutter/material.dart';
|
|
|
+import 'package:flutter/rendering.dart';
|
|
|
import 'package:flutter/services.dart';
|
|
|
|
|
|
const double _kLeadingWidth =
|
|
|
@@ -28,11 +28,136 @@ class _ToolbarContainerLayout extends SingleChildLayoutDelegate {
|
|
|
bool shouldRelayout(_ToolbarContainerLayout oldDelegate) => false;
|
|
|
}
|
|
|
|
|
|
+// TODO(eseidel): Toolbar needs to change size based on orientation:
|
|
|
+// https://material.io/design/components/app-bars-top.html#specs
|
|
|
+// Mobile Landscape: 48dp
|
|
|
+// Mobile Portrait: 56dp
|
|
|
+// Tablet/Desktop: 64dp
|
|
|
+
|
|
|
+/// A material design app bar.
|
|
|
+///
|
|
|
+/// An app bar consists of a toolbar and potentially other widgets, such as a
|
|
|
+/// [TabBar] and a [FlexibleSpaceBar]. App bars typically expose one or more
|
|
|
+/// common [actions] with [IconButton]s which are optionally followed by a
|
|
|
+/// [PopupMenuButton] for less common operations (sometimes called the "overflow
|
|
|
+/// menu").
|
|
|
+///
|
|
|
+/// App bars are typically used in the [Scaffold.appBar] property, which places
|
|
|
+/// the app bar as a fixed-height widget at the top of the screen. For a scrollable
|
|
|
+/// app bar, see [SliverAppBar], which embeds an [AppBar] in a sliver for use in
|
|
|
+/// a [CustomScrollView].
|
|
|
+///
|
|
|
+/// When not used as [Scaffold.appBar], or when wrapped in a [Hero], place the app
|
|
|
+/// bar in a [MediaQuery] to take care of the padding around the content of the
|
|
|
+/// app bar if needed, as the padding will not be handled by [Scaffold].
|
|
|
+///
|
|
|
+/// The AppBar displays the toolbar widgets, [leading], [title], and [actions],
|
|
|
+/// above the [bottom] (if any). The [bottom] is usually used for a [TabBar]. If
|
|
|
+/// a [flexibleSpace] widget is specified then it is stacked behind the toolbar
|
|
|
+/// and the bottom widget. The following diagram shows where each of these slots
|
|
|
+/// appears in the toolbar when the writing language is left-to-right (e.g.
|
|
|
+/// English):
|
|
|
+///
|
|
|
+/// 
|
|
|
+///
|
|
|
+/// If the [leading] widget is omitted, but the [AppBar] is in a [Scaffold] with
|
|
|
+/// a [Drawer], then a button will be inserted to open the drawer. Otherwise, if
|
|
|
+/// the nearest [Navigator] has any previous routes, a [BackButton] is inserted
|
|
|
+/// instead. This behavior can be turned off by setting the [automaticallyImplyLeading]
|
|
|
+/// to false. In that case a null leading widget will result in the middle/title widget
|
|
|
+/// stretching to start.
|
|
|
+///
|
|
|
+/// {@tool dartpad --template=stateless_widget_material}
|
|
|
+///
|
|
|
+/// This sample shows an [AppBar] with two simple actions. The first action
|
|
|
+/// opens a [SnackBar], while the second action navigates to a new page.
|
|
|
+///
|
|
|
+/// ```dart preamble
|
|
|
+/// final GlobalKey<ScaffoldState> scaffoldKey = GlobalKey<ScaffoldState>();
|
|
|
+/// final SnackBar snackBar = const SnackBar(content: Text('Showing Snackbar'));
|
|
|
+///
|
|
|
+/// void openPage(BuildContext context) {
|
|
|
+/// Navigator.push(context, MaterialPageRoute(
|
|
|
+/// builder: (BuildContext context) {
|
|
|
+/// return Scaffold(
|
|
|
+/// appBar: AppBar(
|
|
|
+/// title: const Text('Next page'),
|
|
|
+/// ),
|
|
|
+/// body: const Center(
|
|
|
+/// child: Text(
|
|
|
+/// 'This is the next page',
|
|
|
+/// style: TextStyle(fontSize: 24),
|
|
|
+/// ),
|
|
|
+/// ),
|
|
|
+/// );
|
|
|
+/// },
|
|
|
+/// ));
|
|
|
+/// }
|
|
|
+/// ```
|
|
|
+///
|
|
|
+/// ```dart
|
|
|
+/// Widget build(BuildContext context) {
|
|
|
+/// return Scaffold(
|
|
|
+/// key: scaffoldKey,
|
|
|
+/// appBar: AppBar(
|
|
|
+/// title: const Text('AppBar Demo'),
|
|
|
+/// actions: <Widget>[
|
|
|
+/// IconButton(
|
|
|
+/// icon: const Icon(Icons.add_alert),
|
|
|
+/// tooltip: 'Show Snackbar',
|
|
|
+/// onPressed: () {
|
|
|
+/// scaffoldKey.currentState.showSnackBar(snackBar);
|
|
|
+/// },
|
|
|
+/// ),
|
|
|
+/// IconButton(
|
|
|
+/// icon: const Icon(Icons.navigate_next),
|
|
|
+/// tooltip: 'Next page',
|
|
|
+/// onPressed: () {
|
|
|
+/// openPage(context);
|
|
|
+/// },
|
|
|
+/// ),
|
|
|
+/// ],
|
|
|
+/// ),
|
|
|
+/// body: const Center(
|
|
|
+/// child: Text(
|
|
|
+/// 'This is the home page',
|
|
|
+/// style: TextStyle(fontSize: 24),
|
|
|
+/// ),
|
|
|
+/// ),
|
|
|
+/// );
|
|
|
+/// }
|
|
|
+/// ```
|
|
|
+/// {@end-tool}
|
|
|
+///
|
|
|
+/// See also:
|
|
|
+///
|
|
|
+/// * [Scaffold], which displays the [AppBar] in its [Scaffold.appBar] slot.
|
|
|
+/// * [SliverAppBar], which uses [AppBar] to provide a flexible app bar that
|
|
|
+/// can be used in a [CustomScrollView].
|
|
|
+/// * [TabBar], which is typically placed in the [bottom] slot of the [AppBar]
|
|
|
+/// if the screen has multiple pages arranged in tabs.
|
|
|
+/// * [IconButton], which is used with [actions] to show buttons on the app bar.
|
|
|
+/// * [PopupMenuButton], to show a popup menu on the app bar, via [actions].
|
|
|
+/// * [FlexibleSpaceBar], which is used with [flexibleSpace] when the app bar
|
|
|
+/// can expand and collapse.
|
|
|
+/// * <https://material.io/design/components/app-bars-top.html>
|
|
|
+///
|
|
|
+/// 未来版本可能会被删除!
|
|
|
+/// Future versions may be removed.
|
|
|
+@deprecated
|
|
|
class MyAppBar extends StatefulWidget implements PreferredSizeWidget {
|
|
|
/// Creates a material design app bar.
|
|
|
///
|
|
|
- /// The arguments [elevation], [primary], [toolbarOpacity], [bottomOpacity]
|
|
|
- /// and [automaticallyImplyLeading] must not be null.
|
|
|
+ /// The arguments [primary], [toolbarOpacity], [bottomOpacity]
|
|
|
+ /// and [automaticallyImplyLeading] must not be null. Additionally, if
|
|
|
+ /// [elevation] is specified, it must be non-negative.
|
|
|
+ ///
|
|
|
+ /// If [backgroundColor], [elevation], [brightness], [iconTheme],
|
|
|
+ /// [actionsIconTheme], or [textTheme] are null, then their [AppBarTheme]
|
|
|
+ /// values will be used. If the corresponding [AppBarTheme] property is null,
|
|
|
+ /// then the default specified in the property's documentation will be used.
|
|
|
///
|
|
|
/// Typically used in the [Scaffold.appBar] property.
|
|
|
MyAppBar({
|
|
|
@@ -43,10 +168,12 @@ class MyAppBar extends StatefulWidget implements PreferredSizeWidget {
|
|
|
this.actions,
|
|
|
this.flexibleSpace,
|
|
|
this.bottom,
|
|
|
- this.elevation = 4.0,
|
|
|
+ this.elevation,
|
|
|
+ this.shape,
|
|
|
this.backgroundColor,
|
|
|
this.brightness,
|
|
|
this.iconTheme,
|
|
|
+ this.actionsIconTheme,
|
|
|
this.textTheme,
|
|
|
this.primary = true,
|
|
|
this.centerTitle,
|
|
|
@@ -54,7 +181,7 @@ class MyAppBar extends StatefulWidget implements PreferredSizeWidget {
|
|
|
this.toolbarOpacity = 1.0,
|
|
|
this.bottomOpacity = 1.0,
|
|
|
}) : assert(automaticallyImplyLeading != null),
|
|
|
- assert(elevation != null),
|
|
|
+ assert(elevation == null || elevation >= 0.0),
|
|
|
assert(primary != null),
|
|
|
assert(titleSpacing != null),
|
|
|
assert(toolbarOpacity != null),
|
|
|
@@ -71,6 +198,37 @@ class MyAppBar extends StatefulWidget implements PreferredSizeWidget {
|
|
|
/// widget with an [IconButton] that opens the drawer (using [Icons.menu]). If
|
|
|
/// there's no [Drawer] and the parent [Navigator] can go back, the [AppBar]
|
|
|
/// will use a [BackButton] that calls [Navigator.maybePop].
|
|
|
+ ///
|
|
|
+ /// {@tool sample}
|
|
|
+ ///
|
|
|
+ /// The following code shows how the drawer button could be manually specified
|
|
|
+ /// instead of relying on [automaticallyImplyLeading]:
|
|
|
+ ///
|
|
|
+ /// ```dart
|
|
|
+ /// AppBar(
|
|
|
+ /// leading: Builder(
|
|
|
+ /// builder: (BuildContext context) {
|
|
|
+ /// return IconButton(
|
|
|
+ /// icon: const Icon(Icons.menu),
|
|
|
+ /// onPressed: () { Scaffold.of(context).openDrawer(); },
|
|
|
+ /// tooltip: MaterialLocalizations.of(context).openAppDrawerTooltip,
|
|
|
+ /// );
|
|
|
+ /// },
|
|
|
+ /// ),
|
|
|
+ /// )
|
|
|
+ /// ```
|
|
|
+ /// {@end-tool}
|
|
|
+ ///
|
|
|
+ /// The [Builder] is used in this example to ensure that the `context` refers
|
|
|
+ /// to that part of the subtree. That way this code snippet can be used even
|
|
|
+ /// inside the very code that is creating the [Scaffold] (in which case,
|
|
|
+ /// without the [Builder], the `context` wouldn't be able to see the
|
|
|
+ /// [Scaffold], since it would refer to an ancestor of that widget).
|
|
|
+ ///
|
|
|
+ /// See also:
|
|
|
+ ///
|
|
|
+ /// * [Scaffold.appBar], in which an [AppBar] is usually placed.
|
|
|
+ /// * [Scaffold.drawer], in which the [Drawer] is usually placed.
|
|
|
final Widget leading;
|
|
|
|
|
|
/// Controls whether we should try to imply the leading widget if null.
|
|
|
@@ -80,7 +238,7 @@ class MyAppBar extends StatefulWidget implements PreferredSizeWidget {
|
|
|
/// If leading widget is not null, this parameter has no effect.
|
|
|
final bool automaticallyImplyLeading;
|
|
|
|
|
|
- /// The primary widget displayed in the appbar.
|
|
|
+ /// The primary widget displayed in the app bar.
|
|
|
///
|
|
|
/// Typically a [Text] widget containing a description of the current contents
|
|
|
/// of the app.
|
|
|
@@ -91,28 +249,9 @@ class MyAppBar extends StatefulWidget implements PreferredSizeWidget {
|
|
|
/// Typically these widgets are [IconButton]s representing common operations.
|
|
|
/// For less common operations, consider using a [PopupMenuButton] as the
|
|
|
/// last action.
|
|
|
- ///
|
|
|
- /// ## Sample code
|
|
|
- ///
|
|
|
- /// ```dart
|
|
|
- /// Scaffold(
|
|
|
- /// appBar: AppBar(
|
|
|
- /// title: Text('Hello World'),
|
|
|
- /// actions: <Widget>[
|
|
|
- /// IconButton(
|
|
|
- /// icon: Icon(Icons.shopping_cart),
|
|
|
- /// tooltip: 'Open shopping cart',
|
|
|
- /// onPressed: () {
|
|
|
- /// // ...
|
|
|
- /// },
|
|
|
- /// ),
|
|
|
- /// ],
|
|
|
- /// ),
|
|
|
- /// )
|
|
|
- /// ```
|
|
|
final List<Widget> actions;
|
|
|
|
|
|
- /// This widget is stacked behind the toolbar and the tabbar. It's height will
|
|
|
+ /// This widget is stacked behind the toolbar and the tab bar. It's height will
|
|
|
/// be the same as the app bar's overall height.
|
|
|
///
|
|
|
/// A flexible space isn't actually flexible unless the [AppBar]'s container
|
|
|
@@ -132,39 +271,63 @@ class MyAppBar extends StatefulWidget implements PreferredSizeWidget {
|
|
|
/// * [PreferredSize], which can be used to give an arbitrary widget a preferred size.
|
|
|
final PreferredSizeWidget bottom;
|
|
|
|
|
|
- /// The z-coordinate at which to place this app bar. This controls the size of
|
|
|
- /// the shadow below the app bar.
|
|
|
+ /// The z-coordinate at which to place this app bar relative to its parent.
|
|
|
+ ///
|
|
|
+ /// This controls the size of the shadow below the app bar.
|
|
|
+ ///
|
|
|
+ /// The value is non-negative.
|
|
|
///
|
|
|
- /// Defaults to 4, the appropriate elevation for app bars.
|
|
|
+ /// If this property is null, then [ThemeData.appBarTheme.elevation] is used,
|
|
|
+ /// if that is also null, the default value is 4, the appropriate elevation
|
|
|
+ /// for app bars.
|
|
|
final double elevation;
|
|
|
|
|
|
+ /// The material's shape as well its shadow.
|
|
|
+ ///
|
|
|
+ /// A shadow is only displayed if the [elevation] is greater than
|
|
|
+ /// zero.
|
|
|
+ final ShapeBorder shape;
|
|
|
+
|
|
|
/// The color to use for the app bar's material. Typically this should be set
|
|
|
/// along with [brightness], [iconTheme], [textTheme].
|
|
|
///
|
|
|
- /// Defaults to [ThemeData.primaryColor].
|
|
|
+ /// If this property is null, then [ThemeData.appBarTheme.color] is used,
|
|
|
+ /// if that is also null, then [ThemeData.primaryColor] is used.
|
|
|
final Color backgroundColor;
|
|
|
|
|
|
/// The brightness of the app bar's material. Typically this is set along
|
|
|
/// with [backgroundColor], [iconTheme], [textTheme].
|
|
|
///
|
|
|
- /// Defaults to [ThemeData.primaryColorBrightness].
|
|
|
+ /// If this property is null, then [ThemeData.appBarTheme.brightness] is used,
|
|
|
+ /// if that is also null, then [ThemeData.primaryColorBrightness] is used.
|
|
|
final Brightness brightness;
|
|
|
|
|
|
/// The color, opacity, and size to use for app bar icons. Typically this
|
|
|
/// is set along with [backgroundColor], [brightness], [textTheme].
|
|
|
///
|
|
|
- /// Defaults to [ThemeData.primaryIconTheme].
|
|
|
+ /// If this property is null, then [ThemeData.appBarTheme.iconTheme] is used,
|
|
|
+ /// if that is also null, then [ThemeData.primaryIconTheme] is used.
|
|
|
final IconThemeData iconTheme;
|
|
|
|
|
|
+ /// The color, opacity, and size to use for the icons that appear in the app
|
|
|
+ /// bar's [actions]. This should only be used when the [actions] should be
|
|
|
+ /// themed differently than the icon that appears in the app bar's [leading]
|
|
|
+ /// widget.
|
|
|
+ ///
|
|
|
+ /// If this property is null, then [ThemeData.appBarTheme.actionsIconTheme] is
|
|
|
+ /// used, if that is also null, then this falls back to [iconTheme].
|
|
|
+ final IconThemeData actionsIconTheme;
|
|
|
+
|
|
|
/// The typographic styles to use for text in the app bar. Typically this is
|
|
|
/// set along with [brightness] [backgroundColor], [iconTheme].
|
|
|
///
|
|
|
- /// Defaults to [ThemeData.primaryTextTheme].
|
|
|
+ /// If this property is null, then [ThemeData.appBarTheme.textTheme] is used,
|
|
|
+ /// if that is also null, then [ThemeData.primaryTextTheme] is used.
|
|
|
final TextTheme textTheme;
|
|
|
|
|
|
/// Whether this app bar is being displayed at the top of the screen.
|
|
|
///
|
|
|
- /// If true, the appbar's toolbar elements and [bottom] widget will be
|
|
|
+ /// If true, the app bar's toolbar elements and [bottom] widget will be
|
|
|
/// padded on top by the height of the system status bar. The layout
|
|
|
/// of the [flexibleSpace] is not affected by the [primary] property.
|
|
|
final bool primary;
|
|
|
@@ -202,14 +365,14 @@ class MyAppBar extends StatefulWidget implements PreferredSizeWidget {
|
|
|
/// A size whose height is the sum of [kToolbarHeight] and the [bottom] widget's
|
|
|
/// preferred height.
|
|
|
///
|
|
|
- /// [Scaffold] uses this this size to set its app bar's height.
|
|
|
+ /// [Scaffold] uses this size to set its app bar's height.
|
|
|
@override
|
|
|
final Size preferredSize;
|
|
|
|
|
|
- bool _getEffectiveCenterTitle(ThemeData themeData) {
|
|
|
+ bool _getEffectiveCenterTitle(ThemeData theme) {
|
|
|
if (centerTitle != null) return centerTitle;
|
|
|
- assert(themeData.platform != null);
|
|
|
- switch (themeData.platform) {
|
|
|
+ assert(theme.platform != null);
|
|
|
+ switch (theme.platform) {
|
|
|
case TargetPlatform.android:
|
|
|
case TargetPlatform.fuchsia:
|
|
|
return false;
|
|
|
@@ -224,6 +387,8 @@ class MyAppBar extends StatefulWidget implements PreferredSizeWidget {
|
|
|
}
|
|
|
|
|
|
class _MyAppBarState extends State<MyAppBar> {
|
|
|
+ static const double _defaultElevation = 4.0;
|
|
|
+
|
|
|
void _handleDrawerButton() {
|
|
|
Scaffold.of(context).openDrawer();
|
|
|
}
|
|
|
@@ -236,7 +401,8 @@ class _MyAppBarState extends State<MyAppBar> {
|
|
|
Widget build(BuildContext context) {
|
|
|
assert(!widget.primary || debugCheckHasMediaQuery(context));
|
|
|
assert(debugCheckHasMaterialLocalizations(context));
|
|
|
- final ThemeData themeData = Theme.of(context);
|
|
|
+ final ThemeData theme = Theme.of(context);
|
|
|
+ final AppBarTheme appBarTheme = AppBarTheme.of(context);
|
|
|
final ScaffoldState scaffold = Scaffold.of(context, nullOk: true);
|
|
|
final ModalRoute<dynamic> parentRoute = ModalRoute.of(context);
|
|
|
|
|
|
@@ -246,12 +412,17 @@ class _MyAppBarState extends State<MyAppBar> {
|
|
|
final bool useCloseButton =
|
|
|
parentRoute is PageRoute<dynamic> && parentRoute.fullscreenDialog;
|
|
|
|
|
|
- IconThemeData appBarIconTheme =
|
|
|
- widget.iconTheme ?? themeData.primaryIconTheme;
|
|
|
- TextStyle centerStyle =
|
|
|
- widget.textTheme?.title ?? themeData.primaryTextTheme.title;
|
|
|
- TextStyle sideStyle =
|
|
|
- widget.textTheme?.body1 ?? themeData.primaryTextTheme.body1;
|
|
|
+ IconThemeData overallIconTheme =
|
|
|
+ widget.iconTheme ?? appBarTheme.iconTheme ?? theme.primaryIconTheme;
|
|
|
+ IconThemeData actionsIconTheme = widget.actionsIconTheme ??
|
|
|
+ appBarTheme.actionsIconTheme ??
|
|
|
+ overallIconTheme;
|
|
|
+ TextStyle centerStyle = widget.textTheme?.title ??
|
|
|
+ appBarTheme.textTheme?.title ??
|
|
|
+ theme.primaryTextTheme.title;
|
|
|
+ TextStyle sideStyle = widget.textTheme?.body1 ??
|
|
|
+ appBarTheme.textTheme?.body1 ??
|
|
|
+ theme.primaryTextTheme.body1;
|
|
|
|
|
|
if (widget.toolbarOpacity != 1.0) {
|
|
|
final double opacity =
|
|
|
@@ -263,16 +434,18 @@ class _MyAppBarState extends State<MyAppBar> {
|
|
|
if (sideStyle?.color != null)
|
|
|
sideStyle =
|
|
|
sideStyle.copyWith(color: sideStyle.color.withOpacity(opacity));
|
|
|
- appBarIconTheme = appBarIconTheme.copyWith(
|
|
|
- opacity: opacity * (appBarIconTheme.opacity ?? 1.0));
|
|
|
+ overallIconTheme = overallIconTheme.copyWith(
|
|
|
+ opacity: opacity * (overallIconTheme.opacity ?? 1.0));
|
|
|
+ actionsIconTheme = actionsIconTheme.copyWith(
|
|
|
+ opacity: opacity * (actionsIconTheme.opacity ?? 1.0));
|
|
|
}
|
|
|
|
|
|
Widget leading = widget.leading;
|
|
|
- //if (leading == null && widget.automaticallyImplyLeading) {
|
|
|
+// if (leading == null && widget.automaticallyImplyLeading) {
|
|
|
if (widget.automaticallyImplyLeading) {
|
|
|
if (hasDrawer) {
|
|
|
leading = IconButton(
|
|
|
- //icon: const Icon(Icons.menu),
|
|
|
+// icon: const Icon(Icons.menu),
|
|
|
icon: leading ?? const Icon(Icons.menu),
|
|
|
onPressed: _handleDrawerButton,
|
|
|
tooltip: MaterialLocalizations.of(context).openAppDrawerTooltip,
|
|
|
@@ -292,7 +465,7 @@ class _MyAppBarState extends State<MyAppBar> {
|
|
|
Widget title = widget.title;
|
|
|
if (title != null) {
|
|
|
bool namesRoute;
|
|
|
- switch (defaultTargetPlatform) {
|
|
|
+ switch (theme.platform) {
|
|
|
case TargetPlatform.android:
|
|
|
case TargetPlatform.fuchsia:
|
|
|
namesRoute = true;
|
|
|
@@ -306,7 +479,7 @@ class _MyAppBarState extends State<MyAppBar> {
|
|
|
overflow: TextOverflow.ellipsis,
|
|
|
child: Semantics(
|
|
|
namesRoute: namesRoute,
|
|
|
- child: title,
|
|
|
+ child: _AppBarTitleBox(child: title),
|
|
|
header: true,
|
|
|
),
|
|
|
);
|
|
|
@@ -327,11 +500,19 @@ class _MyAppBarState extends State<MyAppBar> {
|
|
|
);
|
|
|
}
|
|
|
|
|
|
+ // Allow the trailing actions to have their own theme if necessary.
|
|
|
+ if (actions != null) {
|
|
|
+ actions = IconTheme.merge(
|
|
|
+ data: actionsIconTheme,
|
|
|
+ child: actions,
|
|
|
+ );
|
|
|
+ }
|
|
|
+
|
|
|
final Widget toolbar = NavigationToolbar(
|
|
|
leading: leading,
|
|
|
middle: title,
|
|
|
trailing: actions,
|
|
|
- centerMiddle: widget._getEffectiveCenterTitle(themeData),
|
|
|
+ centerMiddle: widget._getEffectiveCenterTitle(theme),
|
|
|
middleSpacing: widget.titleSpacing,
|
|
|
);
|
|
|
|
|
|
@@ -341,7 +522,7 @@ class _MyAppBarState extends State<MyAppBar> {
|
|
|
child: CustomSingleChildLayout(
|
|
|
delegate: const _ToolbarContainerLayout(),
|
|
|
child: IconTheme.merge(
|
|
|
- data: appBarIconTheme,
|
|
|
+ data: overallIconTheme,
|
|
|
child: DefaultTextStyle(
|
|
|
style: sideStyle,
|
|
|
child: toolbar,
|
|
|
@@ -359,14 +540,14 @@ class _MyAppBarState extends State<MyAppBar> {
|
|
|
child: appBar,
|
|
|
),
|
|
|
),
|
|
|
- widget.bottomOpacity == 1.0
|
|
|
- ? widget.bottom
|
|
|
- : Opacity(
|
|
|
- opacity:
|
|
|
- const Interval(0.25, 1.0, curve: Curves.fastOutSlowIn)
|
|
|
- .transform(widget.bottomOpacity),
|
|
|
- child: widget.bottom,
|
|
|
- ),
|
|
|
+ if (widget.bottomOpacity == 1.0)
|
|
|
+ widget.bottom
|
|
|
+ else
|
|
|
+ Opacity(
|
|
|
+ opacity: const Interval(0.25, 1.0, curve: Curves.fastOutSlowIn)
|
|
|
+ .transform(widget.bottomOpacity),
|
|
|
+ child: widget.bottom,
|
|
|
+ ),
|
|
|
],
|
|
|
);
|
|
|
}
|
|
|
@@ -393,23 +574,69 @@ class _MyAppBarState extends State<MyAppBar> {
|
|
|
],
|
|
|
);
|
|
|
}
|
|
|
- final Brightness brightness =
|
|
|
- widget.brightness ?? themeData.primaryColorBrightness;
|
|
|
+ final Brightness brightness = widget.brightness ??
|
|
|
+ appBarTheme.brightness ??
|
|
|
+ theme.primaryColorBrightness;
|
|
|
final SystemUiOverlayStyle overlayStyle = brightness == Brightness.dark
|
|
|
? SystemUiOverlayStyle.light
|
|
|
: SystemUiOverlayStyle.dark;
|
|
|
|
|
|
return Semantics(
|
|
|
container: true,
|
|
|
- explicitChildNodes: true,
|
|
|
child: AnnotatedRegion<SystemUiOverlayStyle>(
|
|
|
value: overlayStyle,
|
|
|
child: Material(
|
|
|
- color: widget.backgroundColor ?? themeData.primaryColor,
|
|
|
- elevation: widget.elevation,
|
|
|
- child: appBar,
|
|
|
+ color:
|
|
|
+ widget.backgroundColor ?? appBarTheme.color ?? theme.primaryColor,
|
|
|
+ elevation:
|
|
|
+ widget.elevation ?? appBarTheme.elevation ?? _defaultElevation,
|
|
|
+ shape: widget.shape,
|
|
|
+ child: Semantics(
|
|
|
+ explicitChildNodes: true,
|
|
|
+ child: appBar,
|
|
|
+ ),
|
|
|
),
|
|
|
),
|
|
|
);
|
|
|
}
|
|
|
+} // Layout the AppBar's title with unconstrained height, vertically
|
|
|
+
|
|
|
+// center it within its (NavigationToolbar) parent, and allow the
|
|
|
+// parent to constrain the title's actual height.
|
|
|
+class _AppBarTitleBox extends SingleChildRenderObjectWidget {
|
|
|
+ const _AppBarTitleBox({Key key, @required Widget child})
|
|
|
+ : assert(child != null),
|
|
|
+ super(key: key, child: child);
|
|
|
+
|
|
|
+ @override
|
|
|
+ _RenderAppBarTitleBox createRenderObject(BuildContext context) {
|
|
|
+ return _RenderAppBarTitleBox(
|
|
|
+ textDirection: Directionality.of(context),
|
|
|
+ );
|
|
|
+ }
|
|
|
+
|
|
|
+ @override
|
|
|
+ void updateRenderObject(
|
|
|
+ BuildContext context, _RenderAppBarTitleBox renderObject) {
|
|
|
+ renderObject.textDirection = Directionality.of(context);
|
|
|
+ }
|
|
|
+}
|
|
|
+
|
|
|
+class _RenderAppBarTitleBox extends RenderAligningShiftedBox {
|
|
|
+ _RenderAppBarTitleBox({
|
|
|
+ RenderBox child,
|
|
|
+ TextDirection textDirection,
|
|
|
+ }) : super(
|
|
|
+ child: child,
|
|
|
+ alignment: Alignment.center,
|
|
|
+ textDirection: textDirection);
|
|
|
+
|
|
|
+ @override
|
|
|
+ void performLayout() {
|
|
|
+ final BoxConstraints innerConstraints =
|
|
|
+ constraints.copyWith(maxHeight: double.infinity);
|
|
|
+ child.layout(innerConstraints, parentUsesSize: true);
|
|
|
+ size = constraints.constrain(child.size);
|
|
|
+ alignChild();
|
|
|
+ }
|
|
|
}
|