Commit 9d6d03fc authored by ebeem's avatar ebeem Committed by Ian Hickson

Add ability to set collapse mode in flexible space bar (#18526)

* Add ability to set collapse mode in flexible space bar

* Add tests to collapse mode in flexible space bar

* Fix minor style nit

* Fix getting collapse mode does not handle unknown values

* Change const key to final in space bar collapse mode test

* Return null collapse padding if mode is unknown
parent 9a8ec453
......@@ -10,6 +10,18 @@ import 'package:flutter/widgets.dart';
import 'constants.dart';
import 'theme.dart';
/// The collapsing effect while the space bar expands or collapses.
enum CollapseMode {
/// The background widget will scroll in a parallax fashion.
parallax,
/// The background widget pin in place until it reaches the min extent.
pin,
/// The background widget will act as normal with no collapsing effect.
none,
}
/// The part of a material design [AppBar] that expands and collapses.
///
/// Most commonly used in in the [SliverAppBar.flexibleSpace] field, a flexible
......@@ -34,8 +46,10 @@ class FlexibleSpaceBar extends StatefulWidget {
Key key,
this.title,
this.background,
this.centerTitle
}) : super(key: key);
this.centerTitle,
this.collapseMode = CollapseMode.parallax
}) : assert(collapseMode != null),
super(key: key);
/// The primary contents of the flexible space bar when expanded.
///
......@@ -52,6 +66,11 @@ class FlexibleSpaceBar extends StatefulWidget {
/// Defaults to being adapted to the current [TargetPlatform].
final bool centerTitle;
/// Collapse effect while scrolling.
///
/// Defaults to [CollapseMode.parallax].
final CollapseMode collapseMode;
/// Wraps a widget that contains an [AppBar] to convey sizing information down
/// to the [FlexibleSpaceBar].
///
......@@ -106,6 +125,19 @@ class _FlexibleSpaceBarState extends State<FlexibleSpaceBar> {
return null;
}
double _getCollapsePadding(double t, _FlexibleSpaceBarSettings settings) {
switch (widget.collapseMode) {
case CollapseMode.pin:
return -(settings.maxExtent - settings.currentExtent);
case CollapseMode.none:
return 0.0;
case CollapseMode.parallax:
final double deltaExtent = settings.maxExtent - settings.minExtent;
return -new Tween<double>(begin: 0.0, end: deltaExtent / 4.0).lerp(t);
}
return null;
}
@override
Widget build(BuildContext context) {
final _FlexibleSpaceBarSettings settings = context.inheritFromWidgetOfExactType(_FlexibleSpaceBarSettings);
......@@ -125,10 +157,9 @@ class _FlexibleSpaceBarState extends State<FlexibleSpaceBar> {
const double fadeEnd = 1.0;
assert(fadeStart <= fadeEnd);
final double opacity = 1.0 - new Interval(fadeStart, fadeEnd).transform(t);
final double parallax = new Tween<double>(begin: 0.0, end: deltaExtent / 4.0).lerp(t);
if (opacity > 0.0) {
children.add(new Positioned(
top: -parallax,
top: _getCollapsePadding(t, settings),
left: 0.0,
right: 0.0,
height: settings.maxExtent,
......
// 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 'package:flutter/material.dart';
import 'package:flutter_test/flutter_test.dart';
final Key blockKey = new UniqueKey();
const double expandedAppbarHeight = 250.0;
final Key appbarContainerKey = new UniqueKey();
void main() {
testWidgets('FlexibleSpaceBar collapse mode none on Android', (WidgetTester tester) async {
await tester.pumpWidget(
new MaterialApp(
theme: new ThemeData(platform: TargetPlatform.android),
home: new Scaffold(
body: new CustomScrollView(
key: blockKey,
slivers: <Widget>[
new SliverAppBar(
expandedHeight: expandedAppbarHeight,
pinned: true,
flexibleSpace: new FlexibleSpaceBar(
background: new Container(
key: appbarContainerKey,
),
collapseMode: CollapseMode.none,
),
),
new SliverToBoxAdapter(
child: new Container(
height: 10000.0,
),
),
],
),
),
),
);
final Finder appbarContainer = find.byKey(appbarContainerKey);
final Offset topBeforeScroll = tester.getTopLeft(appbarContainer);
await slowDrag(tester, blockKey, const Offset(0.0, -100.0));
final Offset topAfterScroll = tester.getTopLeft(appbarContainer);
expect(topBeforeScroll.dy, equals(0.0));
expect(topAfterScroll.dy, equals(0.0));
});
testWidgets('FlexibleSpaceBar collapse mode none on IOS', (WidgetTester tester) async {
await tester.pumpWidget(
new MaterialApp(
theme: new ThemeData(platform: TargetPlatform.iOS),
home: new Scaffold(
body: new CustomScrollView(
key: blockKey,
slivers: <Widget>[
new SliverAppBar(
expandedHeight: expandedAppbarHeight,
pinned: true,
flexibleSpace: new FlexibleSpaceBar(
background: new Container(
key: appbarContainerKey,
),
collapseMode: CollapseMode.none,
),
),
new SliverToBoxAdapter(
child: new Container(
height: 10000.0,
),
),
],
),
),
),
);
final Finder appbarContainer = find.byKey(appbarContainerKey);
final Offset topBeforeScroll = tester.getTopLeft(appbarContainer);
await slowDrag(tester, blockKey, const Offset(0.0, -100.0));
final Offset topAfterScroll = tester.getTopLeft(appbarContainer);
expect(topBeforeScroll.dy, equals(0.0));
expect(topAfterScroll.dy, equals(0.0));
});
testWidgets('FlexibleSpaceBar collapse mode pin on Android', (WidgetTester tester) async {
await tester.pumpWidget(
new MaterialApp(
theme: new ThemeData(platform: TargetPlatform.android),
home: new Scaffold(
body: new CustomScrollView(
key: blockKey,
slivers: <Widget>[
new SliverAppBar(
expandedHeight: expandedAppbarHeight,
pinned: true,
flexibleSpace: new FlexibleSpaceBar(
background: new Container(
key: appbarContainerKey,
),
collapseMode: CollapseMode.pin,
),
),
new SliverToBoxAdapter(
child: new Container(
height: 10000.0,
),
),
],
),
),
),
);
final Finder appbarContainer = find.byKey(appbarContainerKey);
final Offset topBeforeScroll = tester.getTopLeft(appbarContainer);
await slowDrag(tester, blockKey, const Offset(0.0, -100.0));
final Offset topAfterScroll = tester.getTopLeft(appbarContainer);
expect(topBeforeScroll.dy, equals(0.0));
expect(topAfterScroll.dy, equals(-100.0));
});
testWidgets('FlexibleSpaceBar collapse mode pin on IOS', (WidgetTester tester) async {
await tester.pumpWidget(
new MaterialApp(
theme: new ThemeData(platform: TargetPlatform.iOS),
home: new Scaffold(
body: new CustomScrollView(
key: blockKey,
slivers: <Widget>[
new SliverAppBar(
expandedHeight: expandedAppbarHeight,
pinned: true,
flexibleSpace: new FlexibleSpaceBar(
background: new Container(
key: appbarContainerKey,
),
collapseMode: CollapseMode.pin,
),
),
new SliverToBoxAdapter(
child: new Container(
height: 10000.0,
),
),
],
),
),
),
);
final Finder appbarContainer = find.byKey(appbarContainerKey);
final Offset topBeforeScroll = tester.getTopLeft(appbarContainer);
await slowDrag(tester, blockKey, const Offset(0.0, -100.0));
final Offset topAfterScroll = tester.getTopLeft(appbarContainer);
expect(topBeforeScroll.dy, equals(0.0));
expect(topAfterScroll.dy, equals(-100.0));
});
testWidgets('FlexibleSpaceBar collapse mode parallax on Android', (WidgetTester tester) async {
await tester.pumpWidget(
new MaterialApp(
theme: new ThemeData(platform: TargetPlatform.android),
home: new Scaffold(
body: new CustomScrollView(
key: blockKey,
slivers: <Widget>[
new SliverAppBar(
expandedHeight: expandedAppbarHeight,
pinned: true,
flexibleSpace: new FlexibleSpaceBar(
background: new Container(
key: appbarContainerKey,
),
collapseMode: CollapseMode.parallax,
),
),
new SliverToBoxAdapter(
child: new Container(
height: 10000.0,
),
),
],
),
),
),
);
final Finder appbarContainer = find.byKey(appbarContainerKey);
final Offset topBeforeScroll = tester.getTopLeft(appbarContainer);
await slowDrag(tester, blockKey, const Offset(0.0, -100.0));
final Offset topAfterScroll = tester.getTopLeft(appbarContainer);
expect(topBeforeScroll.dy, equals(0.0));
expect(topAfterScroll.dy, lessThan(10.0));
expect(topAfterScroll.dy, greaterThan(-50.0));
});
testWidgets('FlexibleSpaceBar collapse mode parallax on IOS', (WidgetTester tester) async {
await tester.pumpWidget(
new MaterialApp(
theme: new ThemeData(platform: TargetPlatform.iOS),
home: new Scaffold(
body: new CustomScrollView(
key: blockKey,
slivers: <Widget>[
new SliverAppBar(
expandedHeight: expandedAppbarHeight,
pinned: true,
flexibleSpace: new FlexibleSpaceBar(
background: new Container(
key: appbarContainerKey,
),
collapseMode: CollapseMode.parallax,
),
),
new SliverToBoxAdapter(
child: new Container(
height: 10000.0,
),
),
],
),
),
),
);
final Finder appbarContainer = find.byKey(appbarContainerKey);
final Offset topBeforeScroll = tester.getTopLeft(appbarContainer);
await slowDrag(tester, blockKey, const Offset(0.0, -100.0));
final Offset topAfterScroll = tester.getTopLeft(appbarContainer);
expect(topBeforeScroll.dy, equals(0.0));
expect(topAfterScroll.dy, lessThan(10.0));
expect(topAfterScroll.dy, greaterThan(-50.0));
});
}
Future<Null> slowDrag(WidgetTester tester, Key widget, Offset offset) async {
final Offset target = tester.getCenter(find.byKey(widget));
final TestGesture gesture = await tester.startGesture(target);
await gesture.moveBy(offset);
await tester.pump(const Duration(milliseconds: 10));
await gesture.up();
}
\ No newline at end of file
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment