mirror of
https://github.com/flutter/flutter.git
synced 2025-06-03 00:51:18 +00:00
Gallery animation demo back button update (#9853)
This commit is contained in:
parent
f752cd3844
commit
a6eb0a3e38
@ -15,6 +15,8 @@ import 'sections.dart';
|
|||||||
import 'widgets.dart';
|
import 'widgets.dart';
|
||||||
|
|
||||||
const Color _kAppBackgroundColor = const Color(0xFF353662);
|
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
|
// 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
|
// 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.
|
// 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.
|
// Paging is enabled/disabled by setting the heading's PageView scroll physics.
|
||||||
bool _handleScrollNotification(ScrollNotification notification, double midScrollOffset) {
|
bool _handleScrollNotification(ScrollNotification notification, double midScrollOffset) {
|
||||||
@ -466,18 +475,16 @@ class _AnimationDemoHomeState extends State<AnimationDemoHome> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
void _maybeScroll(double midScrollOffset, int pageIndex, double xOffset) {
|
void _maybeScroll(double midScrollOffset, int pageIndex, double xOffset) {
|
||||||
const Duration duration = const Duration(milliseconds: 400);
|
|
||||||
const Curve curve = Curves.fastOutSlowIn;
|
|
||||||
if (_scrollController.offset < midScrollOffset) {
|
if (_scrollController.offset < midScrollOffset) {
|
||||||
// Scroll the overall list to the point where only one section card shows.
|
// 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.
|
// At the same time scroll the PageViews to the page at pageIndex.
|
||||||
_headingPageController.animateToPage(pageIndex, curve: curve, duration: duration);
|
_headingPageController.animateToPage(pageIndex, curve: _kScrollCurve, duration: _kScrollDuration);
|
||||||
_scrollController.animateTo(midScrollOffset, curve: curve, duration: duration);
|
_scrollController.animateTo(midScrollOffset, curve: _kScrollCurve, duration: _kScrollDuration);
|
||||||
} else {
|
} else {
|
||||||
// One one section card is showing: scroll one page forward or back.
|
// One one section card is showing: scroll one page forward or back.
|
||||||
final double centerX = _headingPageController.position.viewportDimension / 2.0;
|
final double centerX = _headingPageController.position.viewportDimension / 2.0;
|
||||||
final int newPageIndex = xOffset > centerX ? pageIndex + 1 : pageIndex - 1;
|
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(
|
new Positioned(
|
||||||
top: statusBarHeight,
|
top: statusBarHeight,
|
||||||
left: 0.0,
|
left: 0.0,
|
||||||
child: const IconTheme(
|
child: new IconTheme(
|
||||||
data: const IconThemeData(color: Colors.white),
|
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 'icons.dart';
|
||||||
import 'theme.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 material design back button.
|
||||||
///
|
///
|
||||||
/// A [BackButton] is an [IconButton] with a "back" icon appropriate for the
|
/// 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], which automatically uses a [BackButton] in its
|
||||||
/// [AppBar.leading] slot when appropriate.
|
/// [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
|
/// * [IconButton], which is a more general widget for creating buttons with
|
||||||
/// icons.
|
/// icons.
|
||||||
/// * [CloseButton], an alternative which may be more appropriate for leaf
|
/// * [CloseButton], an alternative which may be more appropriate for leaf
|
||||||
@ -36,27 +67,14 @@ class BackButton extends StatelessWidget {
|
|||||||
/// target platform.
|
/// target platform.
|
||||||
const BackButton({ Key key }) : super(key: key);
|
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
|
@override
|
||||||
Widget build(BuildContext context) {
|
Widget build(BuildContext context) {
|
||||||
return new IconButton(
|
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
|
tooltip: 'Back', // TODO(ianh): Figure out how to localize this string
|
||||||
onPressed: () {
|
onPressed: () {
|
||||||
Navigator.of(context).maybePop();
|
Navigator.of(context).maybePop();
|
||||||
},
|
}
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -32,4 +32,31 @@ void main() {
|
|||||||
|
|
||||||
expect(find.text('Home'), findsOneWidget);
|
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);
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
Loading…
Reference in New Issue
Block a user