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 {
/// [CupertinoSliverNavigationBar]'s `leading` slot when
/// `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
/// the previous [CupertinoPageRoute.title]. If [previousPageTitle] is specified,
/// it will be shown instead.
......@@ -1200,6 +1204,7 @@ class CupertinoNavigationBarBackButton extends StatelessWidget {
const CupertinoNavigationBarBackButton({
this.color,
this.previousPageTitle,
this.onPressed,
}) : _backChevron = null,
_backLabel = null;
......@@ -1209,7 +1214,8 @@ class CupertinoNavigationBarBackButton extends StatelessWidget {
this._backChevron,
this._backLabel,
) : previousPageTitle = null,
color = null;
color = null,
onPressed = null;
/// The [Color] of the back button.
///
......@@ -1223,6 +1229,15 @@ class CupertinoNavigationBarBackButton extends StatelessWidget {
/// previous routes are both [CupertinoPageRoute]s.
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 _backLabel;
......@@ -1230,10 +1245,12 @@ class CupertinoNavigationBarBackButton extends StatelessWidget {
@override
Widget build(BuildContext context) {
final ModalRoute<dynamic> currentRoute = ModalRoute.of(context);
assert(
currentRoute?.canPop == true,
'CupertinoNavigationBarBackButton should only be used in routes that can be popped',
);
if (onPressed == null) {
assert(
currentRoute?.canPop == true,
'CupertinoNavigationBarBackButton should only be used in routes that can be popped',
);
}
TextStyle actionTextStyle = CupertinoTheme.of(context).textTheme.navActionTextStyle;
if (color != null) {
......@@ -1269,7 +1286,13 @@ class CupertinoNavigationBarBackButton extends StatelessWidget {
),
),
padding: EdgeInsets.zero,
onPressed: () { Navigator.maybePop(context); },
onPressed: () {
if (onPressed != null) {
onPressed();
} else {
Navigator.maybePop(context);
}
},
);
}
}
......@@ -1321,8 +1344,7 @@ class _BackLabel extends StatelessWidget {
Key key,
@required this.specifiedPreviousTitle,
@required this.route,
}) : assert(route != null),
super(key: key);
}) : super(key: key);
final String specifiedPreviousTitle;
final ModalRoute<dynamic> route;
......@@ -1355,7 +1377,7 @@ class _BackLabel extends StatelessWidget {
Widget build(BuildContext context) {
if (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;
// There is no timing issue because the previousTitle Listenable changes
// happen during route modifications before the ValueListenableBuilder
......
......@@ -895,16 +895,131 @@ void main() {
});
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(
const CupertinoApp(
home: CupertinoNavigationBarBackButton(),
home: Placeholder(),
),
);
final dynamic exception = tester.takeException();
expect(exception, isAssertionError);
expect(exception.toString(), contains('CupertinoNavigationBarBackButton should only be used in routes that can be popped'));
});
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 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 {
......
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