In my application, at the beginning, I have 3 pages with the following routes:
routerDelegate = BeamerDelegate(
initialPath: initialPath,
locationBuilder: RoutesLocationBuilder(
routes: {
'/signup': (_, __, ___) => const SignupScreenWidget(),
'/verifyCode': (_, __, ___) => const VerifyCodeRootScreenWidget(),
'/home': (_, __, ___) => const HomeScreen(),
},
),
);
Inside the Home page, I defined the following BeamLocations
, and since I have another BeamLocation
inside the ChefRootWidget
that has its own specific links, when I go to the following path:
/home/chefMainWidget/chefStoreHomePage/chefStoreMenu
There is no problem returning to the previous page by clicking on the back icon from chefStoreMenu
to chefStoreHomePage
. However, after touching the back button on the phone, the return occurs again, i.e., instead of returning to chefStoreHomePage
, I am directly taken to the home page, which is wrong, and the page should be displayed. Therefore, both the home and chefStoreHomePage
pages have their own specific BeamLocations
.
The BeamLocation of the Home page is:
class HomeScreenTab extends BeamLocation<BeamState> {
HomeScreenTab(super.routeInformation);
@override
List<String> get pathPatterns => [
'/home/chefMainWidget/:storeId',
];
@override
List<BeamPage> buildPages(BuildContext context, BeamState state) {
List<BeamPage> pages = [];
pages.add(
const BeamPage(
key: ValueKey('/home'),
type: BeamPageType.noTransition,
child: HomeRootScreenWidget(),
),
);
if (state.uri.pathSegments.length > 1) {
String key = '';
Widget? screen;
switch (state.uri.pathSegments[1]) {
case 'chefMainWidget':
final storeId = state.pathParameters['storeId'];
key = '/home/chefMainWidget-$storeId-${DateTime.now()}';
screen = storeId == null ? null : ChefRootWidget(storeId: int.tryParse(storeId));
break;
}
if (screen != null) {
pages.add(BeamPage(
key: ValueKey(key),
type: BeamPageType.slideRightTransition,
child: screen,
));
}
}
return pages;
}
}
and ChefRootWidget
class:
class ChefRootWidget extends StatefulWidget {
final int? storeId;
const ChefRootWidget({super.key, required this.storeId});
@override
State<StatefulWidget> createState() => _ChefRootWidget();
}
class _ChefRootWidget extends State<ChefRootWidget> {
int get storeId => widget.storeId!;
late List<BeamerDelegate> _routerDelegates;
@override
void initState() {
_routerDelegates = [
BeamerDelegate(
initialPath: '/home/chefMainWidget/chefStoreHomePage',
locationBuilder: (routeInformation, _) {
if (routeInformation.uri.toString().contains('/home/chefMainWidget/chefStoreHomePage')) {
return ChefStoreBeamer(routeInformation, storeId);
}
return NotFound(path: routeInformation.uri.toString());
},
),
];
super.initState();
}
@override
Widget build(BuildContext context) {
return Scaffold(
body: Beamer(
routerDelegate: _routerDelegates[0],
),
);
}
}
The BeamLocation of the chefStoreHomePage page is:
class ChefStoreBeamer extends BeamLocation<BeamState> {
final int storeId;
ChefStoreBeamer(super.routeInformation, this.storeId);
@override
List<String> get pathPatterns => [
'/home/chefMainWidget/chefStoreHomePage/chefStoreMenu/:menuId',
];
@override
List<BeamPage> buildPages(BuildContext context, BeamState state) {
List<BeamPage> pages = [];
pages.add(
BeamPage(
key: ValueKey('/home/chefMainWidget/chefStoreHomePage-$storeId-${DateTime.now()}'),
type: BeamPageType.slideRightTransition,
child: ChefStoreHomePageWidget(storeId: storeId),
),
);
if (state.uri.pathSegments.contains('chefStoreMenu')) {
final menuId = state.pathParameters['menuId'];
if (menuId != null) {
pages.add(
BeamPage(
key: ValueKey('/home/chefMainWidget/chefStoreHomePage/chefStoreMenu-$menuId-${DateTime.now()}'),
type: BeamPageType.slideRightTransition,
child: ChefMenuWidget(menuId: int.tryParse(menuId)),
),
);
}
}
return pages;
}
}
What I do is use Beamer.of(context).beamToNamed
to move between pages, for example:
Beamer.of(context).beamToNamed('/home/chefMainWidget/chefStoreHomePage/chefStoreMenu/$menuId');
import 'package:beamer/beamer.dart';
import 'package:flutter/material.dart';
void main() {
runApp(MyApp());
}
class ALocation extends BeamLocation<BeamState> {
ALocation(super.routeInformation);
@override
List<String> get pathPatterns => ['/a/child-root'];
@override
List<BeamPage> buildPages(BuildContext context, BeamState state) {
List<BeamPage> pages = [];
pages.add(
const BeamPage(
key: ValueKey('/a'),
type: BeamPageType.noTransition,
child: RootScreen(),
),
);
if (state.pathPatternSegments.contains('child-root')) {
pages.add(
const BeamPage(
key: ValueKey('/a/child-root'),
type: BeamPageType.noTransition,
child: ChildScreen(),
),
);
}
return pages;
}
}
class RootBeamerScreen extends StatefulWidget {
const RootBeamerScreen({super.key});
@override
State<RootBeamerScreen> createState() => _RootBeamerScreenState();
}
class _RootBeamerScreenState extends State<RootBeamerScreen> {
final _routerDelegates = [
BeamerDelegate(
initialPath: '/a',
locationBuilder: (routeInformation, _) {
if (routeInformation.uri.toString().contains('/a')) {
return ALocation(routeInformation);
}
return NotFound(path: routeInformation.uri.toString());
},
),
];
@override
Widget build(BuildContext context) {
return Scaffold(
body: Beamer(
routerDelegate: _routerDelegates[0],
),
);
}
}
class RootScreen extends StatelessWidget {
const RootScreen({Key? key}) : super(key: key);
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: const Text('root'),
),
body: Center(
child: Column(
mainAxisSize: MainAxisSize.min,
children: <Widget>[
const Padding(padding: EdgeInsets.all(4)),
TextButton(
onPressed: () => Beamer.of(context).beamToNamed('/a/child-root'),
child: const Text('View child-root'),
),
],
),
),
);
}
}
class ChildLocation extends BeamLocation<BeamState> {
ChildLocation(super.routeInformation);
@override
List<String> get pathPatterns => ['/child-root/detail'];
@override
List<BeamPage> buildPages(BuildContext context, BeamState state) {
List<BeamPage> pages = [];
pages.add(
const BeamPage(
key: ValueKey('/child-root'),
type: BeamPageType.noTransition,
child: ChildDetailWidget(),
),
);
if (state.pathPatternSegments.contains('detail')) {
pages.add(
const BeamPage(
key: ValueKey('/child-root/detail'),
type: BeamPageType.noTransition,
child: DetailScreen(),
),
);
}
return pages;
}
}
class DetailScreen extends StatelessWidget {
const DetailScreen({super.key});
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(title: const Text('child screen')),
body: const SizedBox(
width: double.infinity,
height: double.infinity,
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
crossAxisAlignment: CrossAxisAlignment.center,
children: [
Text('Yooohooooooooo'),
],
),
),
);
}
}
class ChildScreen extends StatefulWidget {
const ChildScreen({Key? key}) : super(key: key);
@override
State<StatefulWidget> createState() => _ChildScreen();
}
class _ChildScreen extends State<ChildScreen> {
final _routerDelegates = [
BeamerDelegate(
initialPath: '/child-root',
locationBuilder: (routeInformation, _) {
if (routeInformation.uri.toString().contains('/child-root')) {
return ChildLocation(routeInformation);
}
return NotFound(path: routeInformation.uri.toString());
},
),
];
@override
Widget build(BuildContext context) {
return Scaffold(
body: Beamer(
routerDelegate: _routerDelegates[0],
),
);
}
}
class ChildDetailWidget extends StatelessWidget {
const ChildDetailWidget({super.key});
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(title: const Text('child screen')),
body: SizedBox(
width: double.infinity,
height: double.infinity,
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
crossAxisAlignment: CrossAxisAlignment.center,
children: [
TextButton(
onPressed: () =>
Beamer.of(context).beamToNamed('/child-root/detail'),
child: const Text('Click here'),
),
],
),
),
);
}
}
class MyApp extends StatelessWidget {
MyApp({super.key});
final routerDelegate = BeamerDelegate(
initialPath: '/a',
locationBuilder: RoutesLocationBuilder(
routes: {
'*': (context, state, data) => const RootBeamerScreen(),
},
),
);
@override
Widget build(BuildContext context) {
return MaterialApp.router(
debugShowCheckedModeBanner: false,
theme: ThemeData(primarySwatch: Colors.indigo),
routerDelegate: routerDelegate,
routeInformationParser: BeamerParser(),
backButtonDispatcher: BeamerBackButtonDispatcher(
delegate: routerDelegate,
),
);
}
}
You just need to use parameter beamBackOnPop: true
in this line of code inside your ChildDetailWidget
widget Beamer.of(context).beamToNamed('/child-root/detail',beamBackOnPop: true)
.
What's happening is that when you click app bar's back button then it's popped up from android's stack but because the default value of beamBackOnPop is false, beamer doesn't beam back and it still has DetailScreen
in it's stack (but visible view is ChildDetailWidget
) . After that when you click android's back button then beamer which still has DetailScreen
, beams back to ChildDetailWidget
(which was already previously visible).
They have mentioned in the examples docs. Look for Deep Location example there where they state the following
Deep Location: you can instantly beam to a location in your app that has many pages stacked (deep linking) and then pop them one by one or simply beamBack to where you came from. Note that beamBackOnPop parameter of beamToNamed might be useful here to override AppBar's pop with beamBack.