Unverified Commit 1cd88c35 authored by xster's avatar xster Committed by GitHub

Let CupertinoNavigationBarBackButton take a custom onPressed (#32469)

parent f82c83b3
...@@ -1189,6 +1189,10 @@ class _NavigationBarStaticComponents { ...@@ -1189,6 +1189,10 @@ class _NavigationBarStaticComponents {
/// [CupertinoSliverNavigationBar]'s `leading` slot when /// [CupertinoSliverNavigationBar]'s `leading` slot when
/// `automaticallyImplyLeading` is true. /// `automaticallyImplyLeading` is true.
/// ///
/// When manually inserted, the [CupertinoNavigationBarBackButton] should only
/// be used in routes that can be popped unless a custom [onPressed] is
/// provided.
///
/// Shows a back chevron and the previous route's title when available from /// Shows a back chevron and the previous route's title when available from
/// the previous [CupertinoPageRoute.title]. If [previousPageTitle] is specified, /// the previous [CupertinoPageRoute.title]. If [previousPageTitle] is specified,
/// it will be shown instead. /// it will be shown instead.
...@@ -1200,6 +1204,7 @@ class CupertinoNavigationBarBackButton extends StatelessWidget { ...@@ -1200,6 +1204,7 @@ class CupertinoNavigationBarBackButton extends StatelessWidget {
const CupertinoNavigationBarBackButton({ const CupertinoNavigationBarBackButton({
this.color, this.color,
this.previousPageTitle, this.previousPageTitle,
this.onPressed,
}) : _backChevron = null, }) : _backChevron = null,
_backLabel = null; _backLabel = null;
...@@ -1209,7 +1214,8 @@ class CupertinoNavigationBarBackButton extends StatelessWidget { ...@@ -1209,7 +1214,8 @@ class CupertinoNavigationBarBackButton extends StatelessWidget {
this._backChevron, this._backChevron,
this._backLabel, this._backLabel,
) : previousPageTitle = null, ) : previousPageTitle = null,
color = null; color = null,
onPressed = null;
/// The [Color] of the back button. /// The [Color] of the back button.
/// ///
...@@ -1223,6 +1229,15 @@ class CupertinoNavigationBarBackButton extends StatelessWidget { ...@@ -1223,6 +1229,15 @@ class CupertinoNavigationBarBackButton extends StatelessWidget {
/// previous routes are both [CupertinoPageRoute]s. /// previous routes are both [CupertinoPageRoute]s.
final String previousPageTitle; final String previousPageTitle;
/// An override callback to perform instead of the default behavior which is
/// to pop the [Navigator].
///
/// It can, for instance, be used to pop the platform's navigation stack
/// instead of Flutter's [Navigator].
///
/// Defaults to null.
final VoidCallback onPressed;
final Widget _backChevron; final Widget _backChevron;
final Widget _backLabel; final Widget _backLabel;
...@@ -1230,10 +1245,12 @@ class CupertinoNavigationBarBackButton extends StatelessWidget { ...@@ -1230,10 +1245,12 @@ class CupertinoNavigationBarBackButton extends StatelessWidget {
@override @override
Widget build(BuildContext context) { Widget build(BuildContext context) {
final ModalRoute<dynamic> currentRoute = ModalRoute.of(context); final ModalRoute<dynamic> currentRoute = ModalRoute.of(context);
assert( if (onPressed == null) {
currentRoute?.canPop == true, assert(
'CupertinoNavigationBarBackButton should only be used in routes that can be popped', currentRoute?.canPop == true,
); 'CupertinoNavigationBarBackButton should only be used in routes that can be popped',
);
}
TextStyle actionTextStyle = CupertinoTheme.of(context).textTheme.navActionTextStyle; TextStyle actionTextStyle = CupertinoTheme.of(context).textTheme.navActionTextStyle;
if (color != null) { if (color != null) {
...@@ -1269,7 +1286,13 @@ class CupertinoNavigationBarBackButton extends StatelessWidget { ...@@ -1269,7 +1286,13 @@ class CupertinoNavigationBarBackButton extends StatelessWidget {
), ),
), ),
padding: EdgeInsets.zero, padding: EdgeInsets.zero,
onPressed: () { Navigator.maybePop(context); }, onPressed: () {
if (onPressed != null) {
onPressed();
} else {
Navigator.maybePop(context);
}
},
); );
} }
} }
...@@ -1321,8 +1344,7 @@ class _BackLabel extends StatelessWidget { ...@@ -1321,8 +1344,7 @@ class _BackLabel extends StatelessWidget {
Key key, Key key,
@required this.specifiedPreviousTitle, @required this.specifiedPreviousTitle,
@required this.route, @required this.route,
}) : assert(route != null), }) : super(key: key);
super(key: key);
final String specifiedPreviousTitle; final String specifiedPreviousTitle;
final ModalRoute<dynamic> route; final ModalRoute<dynamic> route;
...@@ -1355,7 +1377,7 @@ class _BackLabel extends StatelessWidget { ...@@ -1355,7 +1377,7 @@ class _BackLabel extends StatelessWidget {
Widget build(BuildContext context) { Widget build(BuildContext context) {
if (specifiedPreviousTitle != null) { if (specifiedPreviousTitle != null) {
return _buildPreviousTitleWidget(context, specifiedPreviousTitle, null); return _buildPreviousTitleWidget(context, specifiedPreviousTitle, null);
} else if (route is CupertinoPageRoute<dynamic>) { } else if (route is CupertinoPageRoute<dynamic> && !route.isFirst) {
final CupertinoPageRoute<dynamic> cupertinoRoute = route; final CupertinoPageRoute<dynamic> cupertinoRoute = route;
// There is no timing issue because the previousTitle Listenable changes // There is no timing issue because the previousTitle Listenable changes
// happen during route modifications before the ValueListenableBuilder // happen during route modifications before the ValueListenableBuilder
......
...@@ -895,16 +895,131 @@ void main() { ...@@ -895,16 +895,131 @@ void main() {
}); });
testWidgets('CupertinoNavigationBarBackButton shows an error when placed in a route that cannot be popped', (WidgetTester tester) async { testWidgets('CupertinoNavigationBarBackButton shows an error when placed in a route that cannot be popped', (WidgetTester tester) async {
await tester.pumpWidget(
const CupertinoApp(
home: CupertinoNavigationBarBackButton(),
),
);
final dynamic exception = tester.takeException();
expect(exception, isAssertionError);
expect(exception.toString(), contains('CupertinoNavigationBarBackButton should only be used in routes that can be popped'));
});
testWidgets('CupertinoNavigationBarBackButton with a custom onPressed callback can be placed anywhere', (WidgetTester tester) async {
bool backPressed = false;
await tester.pumpWidget(
CupertinoApp(
home: CupertinoNavigationBarBackButton(
onPressed: () => backPressed = true,
),
),
);
expect(tester.takeException(), isNull);
expect(find.text(String.fromCharCode(CupertinoIcons.back.codePoint)), findsOneWidget);
await tester.tap(find.byType(CupertinoNavigationBarBackButton));
expect(backPressed, true);
});
testWidgets(
'Manually inserted CupertinoNavigationBarBackButton still automatically '
'show previous page title when possible',
(WidgetTester tester) async {
await tester.pumpWidget( await tester.pumpWidget(
const CupertinoApp( const CupertinoApp(
home: CupertinoNavigationBarBackButton(), home: Placeholder(),
), ),
); );
final dynamic exception = tester.takeException(); tester.state<NavigatorState>(find.byType(Navigator)).push(
expect(exception, isAssertionError); CupertinoPageRoute<void>(
expect(exception.toString(), contains('CupertinoNavigationBarBackButton should only be used in routes that can be popped')); title: 'An iPod',
}); builder: (BuildContext context) {
return const CupertinoPageScaffold(
navigationBar: CupertinoNavigationBar(),
child: Placeholder(),
);
},
)
);
await tester.pump();
await tester.pump(const Duration(milliseconds: 500));
tester.state<NavigatorState>(find.byType(Navigator)).push(
CupertinoPageRoute<void>(
title: 'A Phone',
builder: (BuildContext context) {
return const CupertinoNavigationBarBackButton();
},
)
);
await tester.pump();
await tester.pump(const Duration(milliseconds: 500));
expect(find.widgetWithText(CupertinoButton, 'An iPod'), findsOneWidget);
}
);
testWidgets(
'CupertinoNavigationBarBackButton onPressed overrides default pop behavior',
(WidgetTester tester) async {
bool backPressed = false;
await tester.pumpWidget(
const CupertinoApp(
home: Placeholder(),
),
);
tester.state<NavigatorState>(find.byType(Navigator)).push(
CupertinoPageRoute<void>(
title: 'An iPod',
builder: (BuildContext context) {
return const CupertinoPageScaffold(
navigationBar: CupertinoNavigationBar(),
child: Placeholder(),
);
},
)
);
await tester.pump();
await tester.pump(const Duration(milliseconds: 500));
tester.state<NavigatorState>(find.byType(Navigator)).push(
CupertinoPageRoute<void>(
title: 'A Phone',
builder: (BuildContext context) {
return CupertinoPageScaffold(
navigationBar: CupertinoNavigationBar(
leading: CupertinoNavigationBarBackButton(
onPressed: () => backPressed = true,
),
),
child: const Placeholder(),
);
},
)
);
await tester.pump();
await tester.pump(const Duration(milliseconds: 500));
await tester.tap(find.byType(CupertinoNavigationBarBackButton));
await tester.pump();
await tester.pump(const Duration(milliseconds: 500));
// The second page is still on top and didn't pop.
expect(find.text('A Phone'), findsOneWidget);
// Custom onPressed called.
expect(backPressed, true);
}
);
} }
class _ExpectStyles extends StatelessWidget { class _ExpectStyles extends StatelessWidget {
......
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