Untitled diff

Created Diff never expires
59 removals
Lines
Total
Removed
Words
Total
Removed
To continue using this feature, upgrade to
Diffchecker logo
Diffchecker Pro
711 lines
95 additions
Lines
Total
Added
Words
Total
Added
To continue using this feature, upgrade to
Diffchecker logo
Diffchecker Pro
721 lines
// Copyright 2015 The Chromium Authors. All rights reserved.
// Copyright 2015 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
// found in the LICENSE file.


import 'dart:math' as math;
import 'dart:math' as math;


import 'package:flutter/foundation.dart';
import 'package:flutter/foundation.dart';
import 'package:flutter/rendering.dart';
import 'package:flutter/rendering.dart';
import 'package:flutter/services.dart';
import 'package:flutter/services.dart';
import 'package:flutter/widgets.dart';
import 'package:flutter/widgets.dart';


import 'back_button.dart';
import 'back_button.dart';
import 'constants.dart';
import 'constants.dart';
import 'flexible_space_bar.dart';
import 'flexible_space_bar.dart';
import 'icon_button.dart';
import 'icon_button.dart';
import 'icons.dart';
import 'icons.dart';
import 'material.dart';
import 'material.dart';
import 'material_localizations.dart';
import 'material_localizations.dart';
import 'scaffold.dart';
import 'scaffold.dart';
import 'tabs.dart';
import 'tabs.dart';
import 'theme.dart';
import 'theme.dart';
import 'typography.dart';
import 'typography.dart';


// Examples can assume:
// Examples can assume:
// void _airDress() { }
// void _airDress() { }
// void _restitchDress() { }
// void _restitchDress() { }
// void _repairDress() { }
// void _repairDress() { }


const double _kLeadingWidth = kToolbarHeight; // So the leading button is square.
const double _kLeadingWidth =
kToolbarHeight; // So the leading button is square.


// Bottom justify the kToolbarHeight child which may overflow the top.
// Bottom justify the kToolbarHeight child which may overflow the top.
class _ToolbarContainerLayout extends SingleChildLayoutDelegate {
class _ToolbarContainerLayout extends SingleChildLayoutDelegate {
const _ToolbarContainerLayout();
const _ToolbarContainerLayout();


@override
@override
BoxConstraints getConstraintsForChild(BoxConstraints constraints) {
BoxConstraints getConstraintsForChild(BoxConstraints constraints) {
return constraints.tighten(height: kToolbarHeight);
return constraints.tighten(height: kToolbarHeight);
}
}


@override
@override
Size getSize(BoxConstraints constraints) {
Size getSize(BoxConstraints constraints) {
return new Size(constraints.maxWidth, kToolbarHeight);
return new Size(constraints.maxWidth, kToolbarHeight);
}
}


@override
@override
Offset getPositionForChild(Size size, Size childSize) {
Offset getPositionForChild(Size size, Size childSize) {
return new Offset(0.0, size.height - childSize.height);
return new Offset(0.0, size.height - childSize.height);
}
}


@override
@override
bool shouldRelayout(_ToolbarContainerLayout oldDelegate) => false;
bool shouldRelayout(_ToolbarContainerLayout oldDelegate) => false;
}
}


// TODO(eseidel) Toolbar needs to change size based on orientation:
// TODO(eseidel) Toolbar needs to change size based on orientation:
// http://material.google.com/layout/structure.html#structure-app-bar
// http://material.google.com/layout/structure.html#structure-app-bar
// Mobile Landscape: 48dp
// Mobile Landscape: 48dp
// Mobile Portrait: 56dp
// Mobile Portrait: 56dp
// Tablet/Desktop: 64dp
// Tablet/Desktop: 64dp


/// A material design app bar.
/// A material design app bar.
///
///
/// An app bar consists of a toolbar and potentially other widgets, such as a
/// 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
/// [TabBar] and a [FlexibleSpaceBar]. App bars typically expose one or more
/// common [actions] with [IconButton]s which are optionally followed by a
/// common [actions] with [IconButton]s which are optionally followed by a
/// [PopupMenuButton] for less common operations (sometimes called the "overflow
/// [PopupMenuButton] for less common operations (sometimes called the "overflow
/// menu").
/// menu").
///
///
/// App bars are typically used in the [Scaffold.appBar] property, which places
/// 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
/// 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
/// scrollable app bar, see [SliverAppBar], which embeds an [AppBar] in a sliver
/// for use in a [CustomScrollView].
/// for use in a [CustomScrollView].
///
///
/// The AppBar displays the toolbar widgets, [leading], [title], and [actions],
/// The AppBar displays the toolbar widgets, [leading], [title], and [actions],
/// above the [bottom] (if any). The [bottom] is usually used for a [TabBar]. If
/// 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
/// 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
/// 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.
/// appears in the toolbar when the writing language is left-to-right (e.g.
/// English):
/// English):
///
///
/// ![The leading widget is in the top left, the actions are in the top right,
/// ![The leading widget is in the top left, the actions are in the top right,
/// the title is between them. The bottom is, naturally, at the bottom, and the
/// the title is between them. The bottom is, naturally, at the bottom, and the
/// flexibleSpace is behind all of them.](https://flutter.github.io/assets-for-api-docs/assets/material/app_bar.png)
/// flexibleSpace is behind all of them.](https://flutter.github.io/assets-for-api-docs/assets/material/app_bar.png)
///
///
/// If the [leading] widget is omitted, but the [AppBar] is in a [Scaffold] with
/// 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
/// 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
/// the nearest [Navigator] has any previous routes, a [BackButton] is inserted
/// instead. This behavior can be turned off by setting the [automaticallyImplyLeading]
/// 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
/// to false. In that case a null leading widget will result in the middle/title widget
/// stretching to start.
/// stretching to start.
///
///
/// ## Sample code
/// ## Sample code
///
///
/// ```dart
/// ```dart
/// new AppBar(
/// new AppBar(
/// title: new Text('My Fancy Dress'),
/// title: new Text('My Fancy Dress'),
/// actions: <Widget>[
/// actions: <Widget>[
/// new IconButton(
/// new IconButton(
/// icon: new Icon(Icons.playlist_play),
/// icon: new Icon(Icons.playlist_play),
/// tooltip: 'Air it',
/// tooltip: 'Air it',
/// onPressed: _airDress,
/// onPressed: _airDress,
/// ),
/// ),
/// new IconButton(
/// new IconButton(
/// icon: new Icon(Icons.playlist_add),
/// icon: new Icon(Icons.playlist_add),
/// tooltip: 'Restitch it',
/// tooltip: 'Restitch it',
/// onPressed: _restitchDress,
/// onPressed: _restitchDress,
/// ),
/// ),
/// new IconButton(
/// new IconButton(
/// icon: new Icon(Icons.playlist_add_check),
/// icon: new Icon(Icons.playlist_add_check),
/// tooltip: 'Repair it',
/// tooltip: 'Repair it',
/// onPressed: _repairDress,
/// onPressed: _repairDress,
/// ),
/// ),
/// ],
/// ],
/// )
/// )
/// ```
/// ```
///
///
/// See also:
/// See also:
///
///
/// * [Scaffold], which displays the [AppBar] in its [Scaffold.appBar] slot.
/// * [Scaffold], which displays the [AppBar] in its [Scaffold.appBar] slot.
/// * [SliverAppBar], which uses [AppBar] to provide a flexible app bar that
/// * [SliverAppBar], which uses [AppBar] to provide a flexible app bar that
/// can be used in a [CustomScrollView].
/// can be used in a [CustomScrollView].
/// * [TabBar], which is typically placed in the [bottom] slot of the [AppBar]
/// * [TabBar], which is typically placed in the [bottom] slot of the [AppBar]
/// if the screen has multiple pages arranged in tabs.
/// if the screen has multiple pages arranged in tabs.
/// * [IconButton], which is used with [actions] to show buttons on the app bar.
/// * [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].
/// * [PopupMenuButton], to show a popup menu on the app bar, via [actions].
/// * [FlexibleSpaceBar], which is used with [flexibleSpace] when the app bar
/// * [FlexibleSpaceBar], which is used with [flexibleSpace] when the app bar
/// can expand and collapse.
/// can expand and collapse.
/// * <https://material.google.com/layout/structure.html#structure-toolbars>
/// * <https://material.google.com/layout/structure.html#structure-toolbars>
class AppBar extends StatefulWidget implements PreferredSizeWidget {
class AppBar extends StatefulWidget implements PreferredSizeWidget {
/// Creates a material design app bar.
/// Creates a material design app bar.
///
///
/// The arguments [elevation], [primary], [toolbarOpacity], [bottomOpacity]
/// The arguments [elevation], [primary], [toolbarOpacity], [bottomOpacity]
/// and [automaticallyImplyLeading] must not be null.
/// and [automaticallyImplyLeading] must not be null.
///
///
/// Typically used in the [Scaffold.appBar] property.
/// Typically used in the [Scaffold.appBar] property.
AppBar({
AppBar({
Key key,
Key key,
this.leading,
this.leading,
this.automaticallyImplyLeading = true,
this.automaticallyImplyLeading = true,
this.title,
this.title,
this.actions,
this.actions,
this.flexibleSpace,
this.flexibleSpace,
this.bottom,
this.bottom,
this.elevation = 4.0,
this.elevation = 4.0,
this.backgroundColor,
this.backgroundColor,
this.brightness,
this.brightness,
this.iconTheme,
this.iconTheme,
this.textTheme,
this.textTheme,
this.primary = true,
this.primary = true,
this.centerTitle,
this.centerTitle,
this.titleSpacing = NavigationToolbar.kMiddleSpacing,
this.titleSpacing = NavigationToolbar.kMiddleSpacing,
this.toolbarOpacity = 1.0,
this.toolbarOpacity = 1.0,
this.bottomOpacity = 1.0,
this.bottomOpacity = 1.0,
}) : assert(automaticallyImplyLeading != null),
}) : assert(automaticallyImplyLeading != null),
assert(elevation != null),
assert(elevation != null),
assert(primary != null),
assert(primary != null),
assert(titleSpacing != null),
assert(titleSpacing != null),
assert(toolbarOpacity != null),
assert(toolbarOpacity != null),
assert(bottomOpacity != null),
assert(bottomOpacity != null),
preferredSize = new Size.fromHeight(kToolbarHeight + (bottom?.preferredSize?.height ?? 0.0)),
preferredSize = new Size.fromHeight(
super(key: key);
kToolbarHeight + (bottom?.preferredSize?.height ?? 0.0)),
super(key: key);


/// A widget to display before the [title].
/// A widget to display before the [title].
///
///
/// If this is null and [automaticallyImplyLeading] is set to true, the
/// If this is null and [automaticallyImplyLeading] is set to true, the
/// [AppBar] will imply an appropriate widget. For example, if the [AppBar] is
/// [AppBar] will imply an appropriate widget. For example, if the [AppBar] is
/// in a [Scaffold] that also has a [Drawer], the [Scaffold] will fill this
/// in a [Scaffold] that also has a [Drawer], the [Scaffold] will fill this
/// widget with an [IconButton] that opens the drawer (using [Icons.menu]). If
/// 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]
/// there's no [Drawer] and the parent [Navigator] can go back, the [AppBar]
/// will use a [BackButton] that calls [Navigator.maybePop].
/// will use a [BackButton] that calls [Navigator.maybePop].
final Widget leading;
final Widget leading;


/// Controls whether we should try to imply the leading widget if null.
/// Controls whether we should try to imply the leading widget if null.
///
///
/// If true and [leading] is null, automatically try to deduce what the leading
/// If true and [leading] is null, automatically try to deduce what the leading
/// widget should be. If false and [leading] is null, leading space is given to [title].
/// widget should be. If false and [leading] is null, leading space is given to [title].
/// If leading widget is not null, this parameter has no effect.
/// If leading widget is not null, this parameter has no effect.
final bool automaticallyImplyLeading;
final bool automaticallyImplyLeading;


/// The primary widget displayed in the appbar.
/// The primary widget displayed in the appbar.
///
///
/// Typically a [Text] widget containing a description of the current contents
/// Typically a [Text] widget containing a description of the current contents
/// of the app.
/// of the app.
final Widget title;
final Widget title;


/// Widgets to display after the [title] widget.
/// Widgets to display after the [title] widget.
///
///
/// Typically these widgets are [IconButton]s representing common operations.
/// Typically these widgets are [IconButton]s representing common operations.
/// For less common operations, consider using a [PopupMenuButton] as the
/// For less common operations, consider using a [PopupMenuButton] as the
/// last action.
/// last action.
///
///
/// ## Sample code
/// ## Sample code
///
///
/// ```dart
/// ```dart
/// new Scaffold(
/// new Scaffold(
/// appBar: new AppBar(
/// appBar: new AppBar(
/// title: new Text('Hello World'),
/// title: new Text('Hello World'),
/// actions: <Widget>[
/// actions: <Widget>[
/// new IconButton(
/// new IconButton(
/// icon: new Icon(Icons.shopping_cart),
/// icon: new Icon(Icons.shopping_cart),
/// tooltip: 'Open shopping cart',
/// tooltip: 'Open shopping cart',
/// onPressed: () {
/// onPressed: () {
/// // ...
/// // ...
/// },
/// },
/// ),
/// ),
/// ],
/// ],
/// ),
/// ),
/// )
/// )
/// ```
/// ```
final List<Widget> actions;
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 tabbar. It's height will
/// be the same as the app bar's overall height.
/// be the same as the app bar's overall height.
///
///
/// A flexible space isn't actually flexible unless the [AppBar]'s container
/// A flexible space isn't actually flexible unless the [AppBar]'s container
/// changes the [AppBar]'s size. A [SliverAppBar] in a [CustomScrollView]
/// changes the [AppBar]'s size. A [SliverAppBar] in a [CustomScrollView]
/// changes the [AppBar]'s height when scrolled.
/// changes the [AppBar]'s height when scrolled.
///
///
/// Typically a [FlexibleSpaceBar]. See [FlexibleSpaceBar] for details.
/// Typically a [FlexibleSpaceBar]. See [FlexibleSpaceBar] for details.
final Widget flexibleSpace;
final Widget flexibleSpace;


/// This widget appears across the bottom of the app bar.
/// This widget appears across the bottom of the app bar.
///
///
/// Typically a [TabBar]. Only widgets that implement [PreferredSizeWidget] can
/// Typically a [TabBar]. Only widgets that implement [PreferredSizeWidget] can
/// be used at the bottom of an app bar.
/// be used at the bottom of an app bar.
///
///
/// See also:
/// See also:
///
///
/// * [PreferredSize], which can be used to give an arbitrary widget a preferred size.
/// * [PreferredSize], which can be used to give an arbitrary widget a preferred size.
final PreferredSizeWidget bottom;
final PreferredSizeWidget bottom;


/// The z-coordinate at which to place this app bar. This controls the size of
/// The z-coordinate at which to place this app bar. This controls the size of
/// the shadow below the app bar.
/// the shadow below the app bar.
///
///
/// Defaults to 4, the appropriate elevation for app bars.
/// Defaults to 4, the appropriate elevation for app bars.
final double elevation;
final double elevation;


/// The color to use for the app bar's material. Typically this should be set
/// The color to use for the app bar's material. Typically this should be set
/// along with [brightness], [iconTheme], [textTheme].
/// along with [brightness], [iconTheme], [textTheme].
///
///
/// Defaults to [ThemeData.primaryColor].
/// Defaults to [ThemeData.primaryColor].
final Color backgroundColor;
final Color backgroundColor;


/// The brightness of the app bar's material. Typically this is set along
/// The brightness of the app bar's material. Typically this is set along
/// with [backgroundColor], [iconTheme], [textTheme].
/// with [backgroundColor], [iconTheme], [textTheme].
///
///
/// Defaults to [ThemeData.primaryColorBrightness].
/// Defaults to [ThemeData.primaryColorBrightness].
final Brightness brightness;
final Brightness brightness;


/// The color, opacity, and size to use for app bar icons. Typically this
/// The color, opacity, and size to use for app bar icons. Typically this
/// is set along with [backgroundColor], [brightness], [textTheme].
/// is set along with [backgroundColor], [brightness], [textTheme].
///
///
/// Defaults to [ThemeData.primaryIconTheme].
/// Defaults to [ThemeData.primaryIconTheme].
final IconThemeData iconTheme;
final IconThemeData iconTheme;


/// The typographic styles to use for text in the app bar. Typically this is
/// The typographic styles to use for text in the app bar. Typically this is
/// set along with [brightness] [backgroundColor], [iconTheme].
/// set along with [brightness] [backgroundColor], [iconTheme].
///
///
/// Defaults to [ThemeData.primaryTextTheme].
/// Defaults to [ThemeData.primaryTextTheme].
final TextTheme textTheme;
final TextTheme textTheme;


/// Whether this app bar is being displayed at the top of the screen.
/// 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 appbar's toolbar elements and [bottom] widget will be
/// padded on top by the height of the system status bar. The layout
/// padded on top by the height of the system status bar. The layout
/// of the [flexibleSpace] is not affected by the [primary] property.
/// of the [flexibleSpace] is not affected by the [primary] property.
final bool primary;
final bool primary;


/// Whether the title should be centered.
/// Whether the title should be centered.
///
///
/// Defaults to being adapted to the current [TargetPlatform].
/// Defaults to being adapted to the current [TargetPlatform].
final bool centerTitle;
final bool centerTitle;


/// The spacing around [title] content on the horizontal axis. This spacing is
/// The spacing around [title] content on the horizontal axis. This spacing is
/// applied even if there is no [leading] content or [actions]. If you want
/// applied even if there is no [leading] content or [actions]. If you want
/// [title] to take all the space available, set this value to 0.0.
/// [title] to take all the space available, set this value to 0.0.
///
///
/// Defaults to [NavigationToolbar.kMiddleSpacing].
/// Defaults to [NavigationToolbar.kMiddleSpacing].
final double titleSpacing;
final double titleSpacing;


/// How opaque the toolbar part of the app bar is.
/// How opaque the toolbar part of the app bar is.
///
///
/// A value of 1.0 is fully opaque, and a value of 0.0 is fully transparent.
/// A value of 1.0 is fully opaque, and a value of 0.0 is fully transparent.
///
///
/// Typically, this value is not changed from its default value (1.0). It is
/// Typically, this value is not changed from its default value (1.0). It is
/// used by [SliverAppBar] to animate the opacity of the toolbar when the app
/// used by [SliverAppBar] to animate the opacity of the toolbar when the app
/// bar is scrolled.
/// bar is scrolled.
final double toolbarOpacity;
final double toolbarOpacity;


/// How opaque the bottom part of the app bar is.
/// How opaque the bottom part of the app bar is.
///
///
/// A value of 1.0 is fully opaque, and a value of 0.0 is fully transparent.
/// A value of 1.0 is fully opaque, and a value of 0.0 is fully transparent.
///
///
/// Typically, this value is not changed from its default value (1.0). It is
/// Typically, this value is not changed from its default value (1.0). It is
/// used by [SliverAppBar] to animate the opacity of the toolbar when the app
/// used by [SliverAppBar] to animate the opacity of the toolbar when the app
/// bar is scrolled.
/// bar is scrolled.
final double bottomOpacity;
final double bottomOpacity;


/// A size whose height is the sum of [kToolbarHeight] and the [bottom] widget's
/// A size whose height is the sum of [kToolbarHeight] and the [bottom] widget's
/// preferred height.
/// preferred height.
///
///
/// [Scaffold] uses this this size to set its app bar's height.
/// [Scaffold] uses this this size to set its app bar's height.
@override
@override
final Size preferredSize;
final Size preferredSize;


bool _getEffectiveCenterTitle(ThemeData themeData) {
bool _getEffectiveCenterTitle(ThemeData themeData) {
if (centerTitle != null)
if (centerTitle != null) return centerTitle;
return centerTitle;
assert(themeData.platform != null);
assert(themeData.platform != null);
switch (themeData.platform) {
switch (themeData.platform) {
case TargetPlatform.android:
case TargetPlatform.android:
case TargetPlatform.fuchsia:
case TargetPlatform.fuchsia:
return false;
return false;
case TargetPlatform.iOS:
case TargetPlatform.iOS:
return actions == null || actions.length < 2;
return actions == null || actions.length < 2;
}
}
return null;
return null;
}
}


@override
@override
_AppBarState createState() => new _AppBarState();
_AppBarState createState() => new _AppBarState();
}
}


class _AppBarState extends State<AppBar> {
class _AppBarState extends State<AppBar> {
void _handleDrawerButton() {
void _handleDrawerButton() {
Scaffold.of(context).openDrawer();
Scaffold.of(context).openDrawer();
}
}


void _handleDrawerButtonEnd() {
void _handleDrawerButtonEnd() {
Scaffold.of(context).openEndDrawer();
Scaffold.of(context).openEndDrawer();
}
}


@override
@override
Widget build(BuildContext context) {
Widget build(BuildContext context) {
assert(!widget.primary || debugCheckHasMediaQuery(context));
assert(!widget.primary || debugCheckHasMediaQuery(context));
final ThemeData themeData = Theme.of(context);
final ThemeData themeData = Theme.of(context);
final ScaffoldState scaffold = Scaffold.of(context, nullOk: true);
final ScaffoldState scaffold = Scaffold.of(context, nullOk: true);
final ModalRoute<dynamic> parentRoute = ModalRoute.of(context);
final ModalRoute<dynamic> parentRoute = ModalRoute.of(context);


final bool hasDrawer = scaffold?.hasDrawer ?? false;
final bool hasDrawer = scaffold?.hasDrawer ?? false;
final bool hasEndDrawer = scaffold?.hasEndDrawer ?? false;
final bool hasEndDrawer = scaffold?.hasEndDrawer ?? false;
final bool canPop = parentRoute?.canPop ?? false;
final bool canPop = parentRoute?.canPop ?? false;
final bool useCloseButton = parentRoute is PageRoute<dynamic> && parentRoute.fullscreenDialog;
final bool useCloseButton =
parentRoute is PageRoute<dynamic> && parentRoute.fullscreenDialog;


IconThemeData appBarIconTheme = widget.iconTheme ?? themeData.primaryIconTheme;
IconThemeData appBarIconTheme =
TextStyle centerStyle = widget.textTheme?.title ?? themeData.primaryTextTheme.title;
widget.iconTheme ?? themeData.primaryIconTheme;
TextStyle sideStyle = widget.textTheme?.body1 ?? themeData.primaryTextTheme.body1;
TextStyle centerStyle =
widget.textTheme?.title ?? themeData.primaryTextTheme.title;
TextStyle sideStyle =
widget.textTheme?.body1 ?? themeData.primaryTextTheme.body1;


if (widget.toolbarOpacity != 1.0) {
if (widget.toolbarOpacity != 1.0) {
final double opacity = const Interval(0.25, 1.0, curve: Curves.fastOutSlowIn).transform(widget.toolbarOpacity);
final double opacity =
const Interval(0.25, 1.0, curve: Curves.fastOutSlowIn)
.transform(widget.toolbarOpacity);
if (centerStyle?.color != null)
if (centerStyle?.color != null)
centerStyle = centerStyle.copyWith(color: centerStyle.color.withOpacity(opacity));
centerStyle =
centerStyle.copyWith(color: centerStyle.color.withOpacity(opacity));
if (sideStyle?.color != null)
if (sideStyle?.color != null)
sideStyle = sideStyle.copyWith(color: sideStyle.color.withOpacity(opacity));
sideStyle =
sideStyle.copyWith(color: sideStyle.color.withOpacity(opacity));
appBarIconTheme = appBarIconTheme.copyWith(
appBarIconTheme = appBarIconTheme.copyWith(
opacity: opacity * (appBarIconTheme.opacity ?? 1.0)
opacity: opacity * (appBarIconTheme.opacity ?? 1.0));
);
}
}


Widget leading = widget.leading;
Widget leading = widget.leading;
if (leading == null && widget.automaticallyImplyLeading) {
if (leading == null && widget.automaticallyImplyLeading) {
if (hasDrawer) {
if (hasDrawer) {
leading = new IconButton(
leading = new IconButton(
icon: const Icon(Icons.menu),
icon: const Icon(Icons.menu),
onPressed: _handleDrawerButton,
onPressed: _handleDrawerButton,
tooltip: MaterialLocalizations.of(context).openAppDrawerTooltip,
tooltip: MaterialLocalizations.of(context).openAppDrawerTooltip,
);
);
} else {
} else {
if (canPop)
if (canPop)
leading = useCloseButton ? const CloseButton() : const BackButton();
leading = useCloseButton ? const CloseButton() : const BackButton();
}
}
}
}
if (leading != null) {
if (leading != null) {
leading = new ConstrainedBox(
leading = new ConstrainedBox(
constraints: const BoxConstraints.tightFor(width: _kLeadingWidth),
constraints: const BoxConstraints.tightFor(width: _kLeadingWidth),
child: leading,
child: leading,
);
);
}
}


Widget title = widget.title;
Widget title = widget.title;
if (title != null) {
if (title != null) {
bool namesRoute;
bool namesRoute;
switch (defaultTargetPlatform) {
switch (defaultTargetPlatform) {
case TargetPlatform.android:
case TargetPlatform.android:
case TargetPlatform.fuchsia:
case TargetPlatform.fuchsia:
namesRoute = true;
namesRoute = true;
break;
break;
case TargetPlatform.iOS:
case TargetPlatform.iOS:
break;
break;
}
}
title = new DefaultTextStyle(
title = new DefaultTextStyle(
style: centerStyle,
style: centerStyle,
softWrap: false,
softWrap: false,
overflow: TextOverflow.ellipsis,
overflow: TextOverflow.ellipsis,
child: new Semantics(
child: new Semantics(
namesRoute: namesRoute,
namesRoute: namesRoute,
child: title,
child: title,
header: true,
header: true,
),
),
);
);
}
}


Widget actions;
Widget actions;
if (widget.actions != null && widget.actions.isNotEmpty) {
if (widget.actions != null && widget.actions.isNotEmpty) {
actions = new Row(
actions = new Row(
mainAxisSize: MainAxisSize.min,
mainAxisSize: MainAxisSize.min,
crossAxisAlignment: CrossAxisAlignment.stretch,
crossAxisAlignment: CrossAxisAlignment.stretch,
children: widget.actions,
children: widget.actions,
);
);
} else if (hasEndDrawer) {
} else if (hasEndDrawer) {
actions = new IconButton(
actions = new IconButton(
icon: const Icon(Icons.menu),
icon: const Icon(Icons.menu),
onPressed: _handleDrawerButtonEnd,
onPressed: _handleDrawerButtonEnd,
tooltip: MaterialLocalizations.of(context).openAppDrawerTooltip,
tooltip: MaterialLocalizations.of(context).openAppDrawerTooltip,
);
);
}
}


final Widget toolbar = new NavigationToolbar(
final Widget toolbar = new NavigationToolbar(
leading: leading,
leading: leading,
middle: title,
middle: title,
trailing: actions,
trailing: actions,
centerMiddle: widget._getEffectiveCenterTitle(themeData),
centerMiddle: widget._getEffectiveCenterTitle(themeData),
middleSpacing: widget.titleSpacing,
middleSpacing: widget.titleSpacing,
);
);


// If the toolbar is allocated less than kToolbarHeight make it
// If the toolbar is allocated less than kToolbarHeight make it
// appear to scroll upwards within its shrinking container.
// appear to scroll upwards within its shrinking container.
Widget appBar = new ClipRect(
Widget appBar = new ClipRect(
child: new CustomSingleChildLayout(
child: new CustomSingleChildLayout(
delegate: const _ToolbarContainerLayout(),
delegate: const _ToolbarContainerLayout(),
child: IconTheme.merge(
child: IconTheme.merge(
data: appBarIconTheme,
data: appBarIconTheme,
child: new DefaultTextStyle(
child: new DefaultTextStyle(
style: sideStyle,
style: sideStyle,
child: toolbar,
child: toolbar,
),
),
),
),
),
),
);
);
if (widget.bottom != null) {
if (widget.bottom != null) {
appBar = new Column(
appBar = new Column(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: <Widget>[
children: <Widget>[
new Flexible(
new Flexible(
child: new ConstrainedBox(
child: new ConstrainedBox(
constraints: const BoxConstraints(maxHeight: kToolbarHeight),
constraints: const BoxConstraints(maxHeight: kToolbarHeight),
child: appBar,
child: appBar,
),
),
),
),
widget.bottomOpacity == 1.0 ? widget.bottom : new Opacity(
widget.bottomOpacity == 1.0
opacity: const Interval(0.25, 1.0, curve: Curves.fastOutSlowIn).transform(widget.bottomOpacity),
? widget.bottom
child: widget.bottom,
: new Opacity(
),
opacity:
const Interval(0.25, 1.0, curve: Curves.fastOutSlowIn)
.transform(widget.bottomOpacity),
child: widget.bottom,
),
],
],
);
);
}
}


// The padding applies to the toolbar and tabbar, not the flexible space.
// The padding applies to the toolbar and tabbar, not the flexible space.
if (widget.primary) {
if (widget.primary) {
appBar = new SafeArea(
appBar = new SafeArea(
top: true,
top: true,
child: appBar,
child: appBar,
);
);
}
}


appBar = new Align(
appBar = new Align(
alignment: Alignment.topCenter,
alignment: Alignment.topCenter,
child: appBar,
child: appBar,
);
);


if (widget.flexibleSpace != null) {
if (widget.flexibleSpace != null) {
appBar = new Stack(
appBar = new Stack(
fit: StackFit.passthrough,
fit: StackFit.passthrough,
children: <Widget>[
children: <Widget>[
widget.flexibleSpace,
widget.flexibleSpace,
appBar,
appBar,
],
],
);
);
}
}
final Brightness brightness = widget.brightness ?? themeData.primaryColorBrightness;
final Brightness brightness =
widget.brightness ?? themeData.primaryColorBrightness;
final SystemUiOverlayStyle overlayStyle = brightness == Brightness.dark
final SystemUiOverlayStyle overlayStyle = brightness == Brightness.dark
? SystemUiOverlayStyle.light
? SystemUiOverlayStyle.light
: SystemUiOverlayStyle.dark;
: SystemUiOverlayStyle.dark;


return new Semantics(
return new Semantics(
container: true,
container: true,
explicitChildNodes: true,
explicitChildNodes: true,
child: new AnnotatedRegion<SystemUiOverlayStyle>(
child: new AnnotatedRegion<SystemUiOverlayStyle>(
value: overlayStyle,
value: overlayStyle,
child: new Material(
child: new Material(
color: widget.backgroundColor ?? themeData.primaryColor,
color: widget.backgroundColor ?? themeData.primaryColor,
elevation: widget.elevation,
elevation: widget.elevation,
child: appBar,
child: appBar,
),
),
),
),
);
);
}
}
}
}


class _FloatingAppBar extends StatefulWidget {
class _FloatingAppBar extends StatefulWidget {
const _FloatingAppBar({ Key key, this.child }) : super(key: key);
const _FloatingAppBar({Key key, this.child}) : super(key: key);


final Widget child;
final Widget child;


@override
@override
_FloatingAppBarState createState() => new _FloatingAppBarState();
_FloatingAppBarState createState() => new _FloatingAppBarState();
}
}


// A wrapper for the widget created by _SliverAppBarDelegate that starts and
// A wrapper for the widget created by _SliverAppBarDelegate that starts and
/// stops the floating appbar's snap-into-view or snap-out-of-view animation.
/// stops the floating appbar's snap-into-view or snap-out-of-view animation.
class _FloatingAppBarState extends State<_FloatingAppBar> {
class _FloatingAppBarState extends State<_FloatingAppBar> {
ScrollPosition _position;
ScrollPosition _position;


@override
@override
void didChangeDependencies() {
void didChangeDependencies() {
super.didChangeDependencies();
super.didChangeDependencies();
if (_position != null)
if (_position != null)
_position.isScrollingNotifier.removeListener(_isScrollingListener);
_position.isScrollingNotifier.removeListener(_isScrollingListener);
_position = Scrollable.of(context)?.position;
_position = Scrollable.of(context)?.position;
if (_position != null)
if (_position != null)
_position.isScrollingNotifier.addListener(_isScrollingListener);
_position.isScrollingNotifier.addListener(_isScrollingListener);
}
}


@override
@override
void dispose() {
void dispose() {
if (_position != null)
if (_position != null)
_position.isScrollingNotifier.removeListener(_isScrollingListener);
_position.isScrollingNotifier.removeListener(_isScrollingListener);
super.dispose();
super.dispose();
}
}


RenderSliverFloatingPersistentHeader _headerRenderer() {
RenderSliverFloatingPersistentHeader _headerRenderer() {
return context.ancestorRenderObjectOfType(const TypeMatcher<RenderSliverFloatingPersistentHeader>());
return context.ancestorRenderObjectOfType(
const TypeMatcher<RenderSliverFloatingPersistentHeader>());
}
}


void _isScrollingListener() {
void _isScrollingListener() {
if (_position == null)
if (_position == null) return;
return;


// When a scroll stops, then maybe snap the appbar into view.
// When a scroll stops, then maybe snap the appbar into view.
// Similarly, when a scroll starts, then maybe stop the snap animation.
// Similarly, when a scroll starts, then maybe stop the snap animation.
final RenderSliverFloatingPersistentHeader header = _headerRenderer();
final RenderSliverFloatingPersistentHeader header = _headerRenderer();
if (_position.isScrollingNotifier.value)
if (_position.isScrollingNotifier.value)
header?.maybeStopSnapAnimation(_position.userScrollDirection);
header?.maybeStopSnapAnimation(_position.userScrollDirection);
else
else
header?.maybeStartSnapAnimation(_position.userScrollDirection);
header?.maybeStartSnapAnimation(_position.userScrollDirection);
}
}


@override
@override
Widget build(BuildContext context) => widget.child;
Widget build(BuildContext context) => widget.child;
}
}


class _SliverAppBarDelegate extends SliverPersistentHeaderDelegate {
class _SliverAppBarDelegate extends SliverPersistentHeaderDelegate {
_SliverAppBarDelegate({
_SliverAppBarDelegate({
@required this.leading,
@required this.leading,
@required this.automaticallyImplyLeading,
@required this.automaticallyImplyLeading,
@required this.title,
@required this.title,
@required this.actions,
@required this.actions,
@required this.flexibleSpace,
@required this.flexibleSpace,
@required this.bottom,
@required this.bottom,
@required this.elevation,
@required this.elevation,
@required this.forceElevated,
@required this.forceElevated,
@required this.backgroundColor,
@required this.backgroundColor,
@required this.brightness,
@required this.brightness,
@required this.iconTheme,
@required this.iconTheme,
@required this.textTheme,
@required this.textTheme,
@required this.primary,
@required this.primary,
@required this.centerTitle,
@required this.centerTitle,
@required this.titleSpacing,
@required this.titleSpacing,
@required this.expandedHeight,
@required this.expandedHeight,
@required this.collapsedHeight,
@required this.collapsedHeight,
@required this.topPadding,
@required this.topPadding,
@required this.floating,
@required this.floating,
@required this.pinned,
@required this.pinned,
@required this.snapConfiguration,
@required this.snapConfiguration,
}) : assert(primary || topPadding == 0.0),
}) : assert(primary || topPadding == 0.0),
_bottomHeight = bottom?.preferredSize?.height ?? 0.0;
_bottomHeight = bottom?.preferredSize?.height ?? 0.0;


final Widget leading;
final Widget leading;
final bool automaticallyImplyLeading;
final bool automaticallyImplyLeading;
final Widget title;
final Widget title;
final List<Widget> actions;
final List<Widget> actions;
final Widget flexibleSpace;
final Widget flexibleSpace;
final PreferredSizeWidget bottom;
final PreferredSizeWidget bottom;
final double elevation;
final double elevation;
final bool forceElevated;
final bool forceElevated;
final Color backgroundColor;
final Color backgroundColor;
final Brightness brightness;
final Brightness brightness;
final IconThemeData iconTheme;
final IconThemeData iconTheme;
final TextTheme textTheme;
final TextTheme textTheme;
final bool primary;
final bool primary;
final bool centerTitle;
final bool centerTitle;
final double titleSpacing;
final double titleSpacing;
final double expandedHeight;
final double expandedHeight;
final double collapsedHeight;
final double collapsedHeight;
final double topPadding;
final double topPadding;
final bool floating;
final bool floating;
final bool pinned;
final bool pinned;


final double _bottomHeight;
final double _bottomHeight;


@override
@override
double get minExtent => collapsedHeight ?? (topPadding + kToolbarHeight + _bottomHeight);
double get minExtent =>
collapsedHeight ?? (topPadding + kToolbarHeight + _bottomHeight);


@override
@override
double get maxExtent => math.max(topPadding + (expandedHeight ?? kToolbarHeight + _bottomHeight), minExtent);
double get maxExtent => math.max(
topPadding + (expandedHeight ?? kToolbarHeight + _bottomHeight),
minExtent);


@override
@override
final FloatingHeaderSnapConfiguration snapConfiguration;
final FloatingHeaderSnapConfiguration snapConfiguration;


@override
@override
Widget build(BuildContext context, double shrinkOffset, bool overlapsContent) {
Widget build(
BuildContext context, double shrinkOffset, bool overlapsContent) {
final double visibleMainHeight = maxExtent - shrinkOffset - topPadding;
final double visibleMainHeight = maxExtent - shrinkOffset - topPadding;
final double toolbarOpacity = pinned && !floating ? 1.0
final double toolbarOpacity = pinned && !floating
: ((visibleMainHeight - _bottomHeight) / kToolbarHeight).clamp(0.0, 1.0);
? 1.0
: ((visibleMainHeight - _bottomHeight) / kToolbarHeight)
.clamp(0.0, 1.0);
final Widget appBar = FlexibleSpaceBar.createSettings(
final Widget appBar = FlexibleSpaceBar.createSettings(
minExtent: minExtent,
minExtent: minExtent,
maxExtent: maxExtent,
maxExtent: maxExtent,
currentExtent: math.max(minExtent, maxExtent - shrinkOffset),
currentExtent: math.max(minExtent, maxExtent - shrinkOffset),
toolbarOpacity: toolbarOpacity,
toolbarOpacity: toolbarOpacity,
child: new AppBar(
child: new AppBar(
leading: leading,
leading: leading,
automaticallyImplyLeading: automaticallyImplyLeading,
automaticallyImplyLeading: automaticallyImplyLeading,
title: title,
title: title,
actions: actions,
actions: actions,
flexibleSpace: (title == null && flexibleSpace != null)
flexibleSpace: (title == null && flexibleSpace != null)
? new Semantics(child: flexibleSpace, header: true)
? new Semantics(child: flexibleSpace, header: true)
: flexibleSpace,
: flexibleSpace,
bottom: bottom,
bottom: bottom,
elevation: forceElevated || overlapsContent || (pinned && shrinkOffset > maxExtent - minExtent) ? elevation ?? 4.0 : 0.0,
elevation: forceElevated ||
overlapsContent ||
(pinned && shrinkOffset > maxExtent - minExtent)
? elevation ?? 4.0
: 0.0,
backgroundColor: backgroundColor,
backgroundColor: backgroundColor,
brightness: brightness,
brightness: brightness,
iconTheme: iconTheme,
iconTheme: iconTheme,
textTheme: textTheme,
textTheme: textTheme,
primary: primary,
primary: primary,
centerTitle: centerTitle,
centerTitle: centerTitle,
titleSpacing: titleSpacing,
titleSpacing: titleSpacing,
toolbarOpacity: toolbarOpacity,
toolbarOpacity: toolbarOpacity,
bottomOpacity: pinned ? 1.0 : (visibleMainHeight / _bottomHeight).clamp(0.0, 1.0),
bottomOpacity:
pinned ? 1.0 : (visibleMainHeight / _bottomHeight).clamp(0.0, 1.0),
),
),
);
);
return floating ? new _FloatingAppBar(child: appBar) : appBar;
return floating ? new _FloatingAppBar(child: appBar) : appBar;
}
}


@override
@override
bool shouldRebuild(covariant _SliverAppBarDelegate oldDelegate) {
bool shouldRebuild(covariant _SliverAppBarDelegate oldDelegate) {
return leading != oldDelegate.leading
return leading != oldDelegate.leading ||
|| automaticallyImplyLeading != oldDelegate.automaticallyImplyLeading
automaticallyImplyLeading != oldDelegate.automaticallyImplyLeading ||
|| title != oldDelegate.title
title != oldDelegate.title ||
|| actions != oldDelegate.actions
actions != oldDelegate.actions ||
|| flexibleSpace != oldDelegate.flexibleSpace
flexibleSpace != oldDelegate.flexibleSpace ||
|| bottom != oldDelegate.bottom
bottom != oldDelegate.bottom ||
|| _bottomHeight != oldDelegate._bottomHeight
_bottomHeight != oldDelegate._bottomHeight ||
|| elevation != oldDelegate.elevation
elevation != oldDelegate.elevation ||
|| backgroundColor != oldDelegate.backgroundColor
backgroundColor != oldDelegate.backgroundColor ||
|| brightness != oldDelegate.brightness
brightness != oldDelegate.brightness ||
|| iconTheme != oldDelegate.iconTheme
iconTheme != oldDelegate.iconTheme ||
|| textTheme != oldDelegate.textTheme
textTheme != oldDelegate.textTheme ||
|| primary != oldDelegate.primary
primary != oldDelegate.primary ||
|| centerTitle != oldDelegate.centerTitle
centerTitle != oldDelegate.centerTitle ||
|| titleSpacing != oldDelegate.titleSpacing
titleSpacing != oldDelegate.titleSpacing ||
|| expandedHeight != oldDelegate.expandedHeight
expandedHeight != oldDelegate.expandedHeight ||
|| topPadding != oldDelegate.topPadding
topPadding != oldDelegate.topPadding ||
|| pinned != oldDelegate.pinned
pinned != oldDelegate.pinned ||
|| floating != oldDelegate.floating
floating != oldDelegate.floating ||
|| snapConfiguration != oldDelegate.snapConfiguration;
snapConfiguration != oldDelegate.snapConfiguration;
}
}


@override
@override
String toString() {
String toString() {
return '${describeIdentity(this)}(topPadding: ${topPadding.toStringAsFixed(1)}, bottomHeight: ${_bottomHeight.toStringAsFixed(1)}, ...)';
return '${describeIdentity(this)}(topPadding: ${topPadding.toStringAsFixed(1)}, bottomHeight: ${_bottomHeight.toStringAsFixed(1)}, ...)';
}
}
}
}


/// A material design app bar that integrates with a [CustomScrollView].
/// A material design app bar that integrates with a [CustomScrollView].
///
///
/// An app bar consists of a toolbar and potentially other widgets, such as a
/// 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
/// [TabBar] and a [FlexibleSpaceBar]. App bars typically expose one or more
/// common actions with [IconButton]s which are optionally followed by a
/// common actions with [IconButton]s which are optionally followed by a
/// [PopupMenuButton] for less common operations.
/// [PopupMenuButton] for less common operations.
///
///
/// Sliver app bars are typically used as the first child of a
/// Sliver app bars are typically used as the first child of a
/// [CustomScrollView], which lets the app bar integrate with the scroll view so
/// [CustomScrollView], which lets the app bar integrate with the scroll view so
/// that it can vary in height according to the scroll offset or float above the
/// that it can vary in height according to the scroll offset or float above the
/// other content in the scroll view. For a fixed-height app bar at the top of
/// other content in the scroll view. For a fixed-height app bar at the top of
/// the screen see [AppBar], which is used in the [Scaffold.appBar] slot.
/// the screen see [AppBar], which is used in the [Scaffold.appBar] slot.
///
///
/// The AppBar displays the toolbar widgets, [leading], [title], and
/// The AppBar displays the toolbar widgets, [leading], [title], and
/// [actions], above the [bottom] (if any). If a [flexibleSpace] widget is
/// [actions], above the [bottom] (if any). If a [flexibleSpace] widget is
/// specified then it is stacked behind the toolbar and the bottom widget.
/// specified then it is stacked behind the toolbar and the bottom widget.
///
///
/// ## Sample code
/// ## Sample code
///
///
/// This is an example that could be included in a [CustomScrollView]'s
/// This is an example that could be included in a [CustomScrollView]'s
/// [CustomScrollView.slivers] list:
/// [CustomScrollView.slivers] list:
///
///
/// ```dart
/// ```dart
/// new SliverAppBar(
/// new SliverAppBar(
/// expandedHeight: 150.0,
/// expandedHeight:
/// flexibleSpace: const FlexibleSpaceBar(
/// title: const Text('Available seats'),
/// ),
/// actions: <Widget>[
/// new IconButton(
/// icon: const Icon(Icons.add_circle),
/// tooltip: 'Add new entry',
/// onPressed: () { /* ... */ },
/// ),
/// ]
/// )
/// ```
///
/// Se