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
This commit is contained in:
parent
9a8ec4536d
commit
9d6d03fc08
@ -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,
|
||||
|
@ -0,0 +1,252 @@
|
||||
// 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();
|
||||
}
|
Loading…
x
Reference in New Issue
Block a user