blob: 54d3fb67ffad1ac614322c32b9a62a356fce7a9b [file]
// Copyright 2016 The Chromium 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 'dart:math' as math;
import 'package:flutter/widgets.dart';
import 'app_bar.dart';
import 'constants.dart';
import 'scaffold.dart';
import 'theme.dart';
/// The part of a material design [AppBar] that expands and collapses.
///
/// Most commonly used in in the [AppBar.flexibleSpace] field, a flexible space
/// bar expands and contracts as the app scrolls so that the [AppBar] reaches
/// from the top of the app to the top of the scrolling contents of the app.
///
/// Requires one of its ancestors to be a [Scaffold] widget because the
/// [Scaffold] coordinates the scrolling effect between the flexible space and
/// its body.
///
/// See also:
///
/// * [AppBar]
/// * [Scaffold]
/// * <https://www.google.com/design/spec/patterns/scrolling-techniques.html>
class FlexibleSpaceBar extends StatefulWidget {
/// Creates a flexible space bar.
///
/// Most commonly used in the [AppBar.flexibleSpace] field. Requires one of
/// its ancestors to be a [Scaffold] widget.
FlexibleSpaceBar({
Key key,
this.title,
this.background,
this.centerTitle
}) : super(key: key);
/// The primary contents of the flexible space bar when expanded.
///
/// Typically a [Text] widget.
final Widget title;
/// Shown behind the [title] when expanded.
///
/// Typically an [AssetImage] widget with [AssetImage.fit] set to [ImageFit.cover].
final Widget background;
/// Whether the title should be centered.
///
/// Defaults to being adapted to the current [TargetPlatform].
final bool centerTitle;
@override
_FlexibleSpaceBarState createState() => new _FlexibleSpaceBarState();
}
class _FlexibleSpaceBarState extends State<FlexibleSpaceBar> {
bool _getEffectiveCenterTitle(ThemeData theme) {
if (config.centerTitle != null)
return config.centerTitle;
assert(theme.platform != null);
switch (theme.platform) {
case TargetPlatform.android:
case TargetPlatform.fuchsia:
return false;
case TargetPlatform.iOS:
return true;
}
return null;
}
Widget _buildContent(BuildContext context, BoxConstraints constraints) {
final Size size = constraints.biggest;
final double statusBarHeight = MediaQuery.of(context).padding.top;
final double currentHeight = size.height;
final double maxHeight = statusBarHeight + AppBar.getExpandedHeightFor(context);
final double minHeight = statusBarHeight + kToolBarHeight;
final double deltaHeight = maxHeight - minHeight;
// 0.0 -> Expanded
// 1.0 -> Collapsed to toolbar
final double t = (1.0 - (currentHeight - minHeight) / deltaHeight).clamp(0.0, 1.0);
final List<Widget> children = <Widget>[];
// background image
if (config.background != null) {
final double fadeStart = math.max(0.0, 1.0 - kToolBarHeight / deltaHeight);
final double fadeEnd = 1.0;
final double opacity = 1.0 - new Interval(fadeStart, fadeEnd).transform(t);
final double parallax = new Tween<double>(begin: 0.0, end: deltaHeight / 4.0).lerp(t);
if (opacity > 0.0) {
children.add(new Positioned(
top: -parallax,
left: 0.0,
right: 0.0,
height: maxHeight,
child: new Opacity(
opacity: opacity,
child: config.background
)
));
}
}
if (config.title != null) {
final ThemeData theme = Theme.of(context);
final double opacity = (1.0 - (minHeight - currentHeight) / (kToolBarHeight - statusBarHeight)).clamp(0.0, 1.0);
if (opacity > 0.0) {
TextStyle titleStyle = theme.primaryTextTheme.title;
titleStyle = titleStyle.copyWith(
color: titleStyle.color.withOpacity(opacity)
);
final bool effectiveCenterTitle = _getEffectiveCenterTitle(theme);
final double scaleValue = new Tween<double>(begin: 1.5, end: 1.0).lerp(t);
final Matrix4 scaleTransform = new Matrix4.identity()
..scale(scaleValue, scaleValue, 1.0);
final FractionalOffset titleAlignment = effectiveCenterTitle ? FractionalOffset.bottomCenter : FractionalOffset.bottomLeft;
children.add(new Container(
padding: new EdgeInsets.only(
left: effectiveCenterTitle ? 0.0 : 72.0,
bottom: 16.0
),
child: new Transform(
alignment: titleAlignment,
transform: scaleTransform,
child: new Align(
alignment: titleAlignment,
child: new DefaultTextStyle(style: titleStyle, child: config.title)
)
)
));
}
}
return new ClipRect(child: new Stack(children: children));
}
@override
Widget build(BuildContext context) {
return new LayoutBuilder(builder: _buildContent);
}
}