Commit a6eb0a3e authored by Hans Muller's avatar Hans Muller Committed by GitHub

Gallery animation demo back button update (#9853)

parent f752cd38
......@@ -15,6 +15,8 @@ import 'sections.dart';
import 'widgets.dart';
const Color _kAppBackgroundColor = const Color(0xFF353662);
const Duration _kScrollDuration = const Duration(milliseconds: 400);
const Curve _kScrollCurve = Curves.fastOutSlowIn;
// This app's contents start out at _kHeadingMaxHeight and they function like
// an appbar. Initially the appbar occupies most of the screen and its section
......@@ -449,6 +451,13 @@ class _AnimationDemoHomeState extends State<AnimationDemoHome> {
);
}
void _handleBackButton(double midScrollOffset) {
if (_scrollController.offset >= midScrollOffset)
_scrollController.animateTo(0.0, curve: _kScrollCurve, duration: _kScrollDuration);
else
Navigator.of(context).maybePop();
}
// Only enable paging for the heading when the user has scrolled to midScrollOffset.
// Paging is enabled/disabled by setting the heading's PageView scroll physics.
bool _handleScrollNotification(ScrollNotification notification, double midScrollOffset) {
......@@ -466,18 +475,16 @@ class _AnimationDemoHomeState extends State<AnimationDemoHome> {
}
void _maybeScroll(double midScrollOffset, int pageIndex, double xOffset) {
const Duration duration = const Duration(milliseconds: 400);
const Curve curve = Curves.fastOutSlowIn;
if (_scrollController.offset < midScrollOffset) {
// Scroll the overall list to the point where only one section card shows.
// At the same time scroll the PageViews to the page at pageIndex.
_headingPageController.animateToPage(pageIndex, curve: curve, duration: duration);
_scrollController.animateTo(midScrollOffset, curve: curve, duration: duration);
_headingPageController.animateToPage(pageIndex, curve: _kScrollCurve, duration: _kScrollDuration);
_scrollController.animateTo(midScrollOffset, curve: _kScrollCurve, duration: _kScrollDuration);
} else {
// One one section card is showing: scroll one page forward or back.
final double centerX = _headingPageController.position.viewportDimension / 2.0;
final int newPageIndex = xOffset > centerX ? pageIndex + 1 : pageIndex - 1;
_headingPageController.animateToPage(newPageIndex, curve: curve, duration: duration);
_headingPageController.animateToPage(newPageIndex, curve: _kScrollCurve, duration: _kScrollDuration);
}
}
......@@ -605,9 +612,15 @@ class _AnimationDemoHomeState extends State<AnimationDemoHome> {
new Positioned(
top: statusBarHeight,
left: 0.0,
child: const IconTheme(
child: new IconTheme(
data: const IconThemeData(color: Colors.white),
child: const BackButton(),
child: new IconButton(
icon: const BackButtonIcon(),
tooltip: 'Back',
onPressed: () {
_handleBackButton(appBarMidScrollOffset);
}
),
),
),
],
......
......@@ -9,6 +9,35 @@ import 'icon_button.dart';
import 'icons.dart';
import 'theme.dart';
/// A "back" icon that's appropriate for the current [TargetPlatform].
///
/// See also:
///
/// * [BackButton], an [IconButton] with a [BackButtonIcon] that calls
/// [Navigator.maybePop] to return to the previous route.
/// * [IconButton], which is a more general widget for creating buttons
/// with icons.
/// * [Icon], a material design icon.
class BackButtonIcon extends StatelessWidget {
const BackButtonIcon({ Key key }) : super(key: key);
/// Returns tha appropriate "back" icon for the given `platform`.
static IconData _getIconData(TargetPlatform platform) {
switch (platform) {
case TargetPlatform.android:
case TargetPlatform.fuchsia:
return Icons.arrow_back;
case TargetPlatform.iOS:
return Icons.arrow_back_ios;
}
assert(false);
return null;
}
@override
Widget build(BuildContext context) => new Icon(_getIconData(Theme.of(context).platform));
}
/// A material design back button.
///
/// A [BackButton] is an [IconButton] with a "back" icon appropriate for the
......@@ -27,6 +56,8 @@ import 'theme.dart';
///
/// * [AppBar], which automatically uses a [BackButton] in its
/// [AppBar.leading] slot when appropriate.
/// * [BackButtonIcon], which is useful if you need to create a back button
/// that responds differently to being pressed.
/// * [IconButton], which is a more general widget for creating buttons with
/// icons.
/// * [CloseButton], an alternative which may be more appropriate for leaf
......@@ -36,27 +67,14 @@ class BackButton extends StatelessWidget {
/// target platform.
const BackButton({ Key key }) : super(key: key);
/// Returns tha appropriate "back" icon for the given `platform`.
static IconData getIconData(TargetPlatform platform) {
switch (platform) {
case TargetPlatform.android:
case TargetPlatform.fuchsia:
return Icons.arrow_back;
case TargetPlatform.iOS:
return Icons.arrow_back_ios;
}
assert(false);
return null;
}
@override
Widget build(BuildContext context) {
return new IconButton(
icon: new Icon(getIconData(Theme.of(context).platform)),
icon: const BackButtonIcon(),
tooltip: 'Back', // TODO(ianh): Figure out how to localize this string
onPressed: () {
Navigator.of(context).maybePop();
},
}
);
}
}
......
......@@ -32,4 +32,31 @@ void main() {
expect(find.text('Home'), findsOneWidget);
});
testWidgets('BackButton icon', (WidgetTester tester) async {
final Key iOSKey = new UniqueKey();
final Key androidKey = new UniqueKey();
await tester.pumpWidget(
new MaterialApp(
home: new Column(
children: <Widget>[
new Theme(
data: new ThemeData(platform: TargetPlatform.iOS),
child: new BackButtonIcon(key: iOSKey),
),
new Theme(
data: new ThemeData(platform: TargetPlatform.android),
child: new BackButtonIcon(key: androidKey),
),
],
),
),
);
final Icon iOSIcon = tester.widget(find.descendant(of: find.byKey(iOSKey), matching: find.byType(Icon)));
final Icon androidIcon = tester.widget(find.descendant(of: find.byKey(androidKey), matching: find.byType(Icon)));
expect(iOSIcon == androidIcon, false);
});
}
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