RoutesLocationBuilder
https://github.com/slovnicki/beamer/tree/master/examples/location_builders/lib [sarthak's learn_beamer repo] (https://github.com/cosmicsarthak/learn_beamer)
use
RoutesLocationBuilderfor straight navigation needing only back and forward when we don't want to preserve state of the previous naviagtion.
MaterialApp.router
Two important properties of MaterialApp.router when used inside RoutesLocationBuilder are:
routeInformationParser: it always takeBeamerParser()routerDelegate: for simplicity, I am using a varaiblerouterDelegatewhich is declared separately asfinal- it can have different types of routes:
- direct:
'/screen': (context, state, data) => Screen(), - for
parameters - for
queries
- direct:
- it can have different types of routes:
backButtonDispatcher: Integration of Android's back button is achieved by setting a backButtonDispatcher inMaterialApp.router. This dispatcher needs a reference to the same BeamerDelegate that is set for routerDelegate.
return MaterialApp.router(
routeInformationParser: BeamerParser(),
routerDelegate: routerDelegate, // here variable `routerDelegate` is assigned to the property [routerDelegate] so this 'variable' will be used in `backButtonDispatcher`
backButtonDispatcher: BeamerBackButtonDispatcher(delegate: routerDelegate), // here deligate: THE SAME variable that is assigned to routerDeligate
///
);
understanding BeamPage
TO ADO!
🔶 SIMPLE naviagtion WITHOUT parameters or queries
1. routerDelegate
returns BeamPage( key:.., title:.., child: Widget() ),
was declared as
routerDelegate: routerDelegate,inside theMaterialApp.router(..)
I am using a varaible routerDelegate which is declared separately as final
final routerDelegate = BeamerDelegate(
locationBuilder: RoutesLocationBuilder(
routes: {
'/': (context, state, data) {
return const BeamPage(
// `{ return BeamPage(child:.., key:.., title:..) }` can be replaced by `=>`
key: ValueKey('home'), // NOT THE URL, this is just used by the BeamPage internally
title: 'Home',
child: Home(),
);
},
'/screen': (context, state, data) => BeamPage(
// `=>` can be replaced by `{ return BeamPage(child:.., key:.., title:..) }`
key: const ValueKey('books'), // NOT THE URL, this is just used by the BeamPage internally
title: 'Books Menu',
child: Screen(),
),
},
),
);
2. Beamer.of(context).beamToNamed('/..');
using the routes inside widgets
beamToNamed
ElevatedButton(
onPressed: () {
Beamer.of(context).beamToNamed('/nextpage');
},
child: const Text('go to NEXT page'),
),
3. Beamer.of(context).beamBack();
going back to the PREVIOUS page
beamBack
ElevatedButton(
onPressed: () {
Beamer.of(context).beamBack();
},
child: const Text('BACK'),
),
🔶 parameters in the URL/URI using pathParameters
parameters from List<Map<..,..>>
pathParametersonly takesMap<String, String>, so we must create a Map<String, String> which will contain an'id' : '..1..'so that this'id'can be matched with the id of the ...
1. create a data model with List<Map<String,String>>
this is a data model where I am declaring the
List<Map<String,String>>which will be used in:
main.dartbooks_menu.dart
used for the pathParameters in main.dart
'/booksMenu/:bookID': (context, state, data) {
final currentBook = booksList.firstWhere(
(el) => (el['id'] ==
(context.currentBeamLocation.state as BeamState)
.pathParameters['bookID']),
);
return BeamPage(
key: ValueKey(currentBook['id']),
title: '${currentBook['id']} || ${currentBook['title']}',
child: BookDetailsScreen(
currentBook: currentBook,
),
);
}
Why List<Map<String,String>> , why not anything else
pathParametersonly takesMap<String, String>- we need the 'id' from
'id': '1'for the URL parameter - also by
Mapwe access the corresponding CHANGING values by calling the NON-CHANGING Common values such asid,title,author- by by
currentBook['author']orcurrentBook['id']I can get access to different cossponding values by just using methods ofIterablesuch as
- by by
by by currentBook['author'] or currentBook['id'] I can get access to different cossponding values by just using methods of Iterable such as
ListView(
// importing the `booksList` from the `models/books_list.dart` and then iterating through the booksList using `.map(..)` then finally again converting back to List using `.toList()`
children: booksList
.map(
(el) => ListTile(// `el` is the 'current element' of type `Map<String, String>>`
onTap: () => Beamer.of(context).beamToNamed(
'/booksMenu/${el['id']}'), // by calling `beamertoNamed`, going to the 'main.dart' having `'/booksMenu/:bookID'`, there it will comapre this el's `id` with the 'id' inside the the `List<Map<..,..>` it has got there like this ↴
/// `'/booksMenu/:bookID'` has -- `final currentBook = booksList.firstWhere((el) => (el['id'] == (context.currentBeamLocation.state as BeamState).pathParameters['bookID']),);`
title: Text(el['title']!),
),
)
// its inside the ListView so, wee MUST convert `.map` into `.toList` // without `.toList`- ERROR: The argument type 'Iterable<ListTile>' can't be assigned to the parameter type 'List<Widget>' .. 'dartargument_type_not_assignable'
.toList(), // contexting `.map(..)` into List by `.toList()`
),
// the 1st part is always same in this `Map<String, String>` and can be used like `currentBook['id']` or `currentBook['author']` to get the different correspondin values
const List<Map<String, String>> booksList = [
{
'id': '1',
'title': 'Stranger in a Strange Land',
'author': 'Robert A. Heinlein',
},
{
'id': '2',
'title': 'Foundation',
'author': 'Isaac Asimov',
},
{
'id': '3',
'title': 'Fahrenheit 451',
'author': 'Ray Bradbury',
},
];
2. creating Menu screen
creating Menu screen using the data model having the
List<Map<String, String>>
- importing the
booksListfrom themodels/books_list.dart - iterating through the
booksListusing.map(..)then finally again converting back to List using.toList() - calling
beamToNamed(..)⏩⏩onTap: () => Beamer.of(context).beamToNamed('/booksMenu/${el['id']}'), - converting the
.mapinto.toList
import 'package:learn_beamer/models/books_list.dart';
class BooksMenu extends StatelessWidget {
const BooksMenu({super.key});
Widget build(BuildContext context) {
return Scaffold(
body: ListView(
children: booksList // importing the `booksList` from the `models/books_list.dart` and then iterating through the booksList using `.map(..)` then finally again converting back to List using `.toList()`
.map(
(el) => ListTile( // `el` is the 'current element' of type `Map<String, String>>`
onTap: () => Beamer.of(context).beamToNamed(
'/booksMenu/${el['id']}'), // by calling `beamertoNamed`, going to the 'main.dart' having `'/booksMenu/:bookID'`, there it will comapre this el's `id` with the 'id' inside the the `List<Map<..,..>` it has got there like this ↴
/// `'/booksMenu/:bookID'` has -- `final currentBook = booksList.firstWhere((el) => (el['id'] == (context.currentBeamLocation.state as BeamState).pathParameters['bookID']),);`
title: Text(el['title']!),
), // its inside the ListView so, wee MUST convert `.map` into `.toList` // without `.toList`- ERROR: The argument type 'Iterable<ListTile>' can't be assigned to the parameter type 'List<Widget>' .. 'dartargument_type_not_assignable'
).toList(), // contexting `.map(..)` into List by `.toList()`
),
);
}
}
3. ✅✅ creating the parameter URL /screen/:bookID' with BeamPage & firsWhere ✅✅
final routerDelegate = BeamerDelegate(
locationBuilder: RoutesLocationBuilder(
routes: {
'/': (context, state, data) {
return const BeamPage(
// `{ return BeamPage(child:.., key:.., title:..) }` can be replaced by `=>`
key: ValueKey('home'), // NOT THE URL, this is just used by the BeamPage internally
title: 'Home',
child: Home(),
);
},
'/booksMenu': (context, state, data) => const BeamPage(
// `=>` can be replaced by `{ return BeamPage(child:.., key:.., title:..) }`
key: ValueKey('books'), // NOT THE URL, this is just used by the BeamPage internally
title: 'Books Menu',
child: BooksMenu(),
),
/// using `pathParameters` for getting a book from a screen having List of books
/// here taking the books from /booksMenu and then going to the individual book's `BookDetailsScreen` screen
'/booksMenu/:bookID': (context, state, data) {
// I need the url for one book from the List `booksList` // `el` is the "current element" of the For-Each loop of the `List<Map<String,Strin>>` .. that means the `el` is a `Map<String,String>` representing the current element of the 'booksList' whcih is of type `List<Map<String,String>>`
final currentBook = booksList.firstWhere(
(el) => (el['id'] ==
(context.currentBeamLocation.state as BeamState)
.pathParameters['bookID']),
); // here `bookID` is taken from ` '/booksMenu/:bookID' ` as was declared above in the "URL for paramter"
/// ⏫ if the `el['id']` matches with `pathParameters[bookID]` then, the `currentBook` will get the value of the `el`[whcih is the current Map<..,..> in the List<Map<String, String>]
return BeamPage(
/// `currentBook` now is a `Map<String,String>` , so `currentBook` will be used to get the 'key, 'title', & most importantly `'child' Widget`
key: ValueKey(currentBook['id']),
// 👆 using `currentBook` from above which is a `Map<String, String>` and the Map<..,..> has `id` as its 1st part and the 2nd part is being acccessed here which is the value of the currenBook
title: '${currentBook['id']} || ${currentBook['title']}',
child: BookDetailsScreen(
currentBook: currentBook,
),
);
}
},
),
);
4. creating the individual screen
this screen will be used as
childinsideBeamPage(child:.., key:.., title:..)of theparameter URLinside main.dart
- decalring the final variable of type
Map<String, String>- this variable will be
requiredin the constructor of this class - it will used to store the Map<..,..> given while calling the
BookDetailsScreen(currentBook: ..)
- this variable will be
const BookDetailsScreen({Key? key,required this.currentBook,});- when calling the
BookDetailsScreen(currentBook: ..)thecurrentBookMUST be given as its a "required" Named Paramater,
- when calling the
class BookDetailsScreen extends StatelessWidget {
final Map<String, String> currentBook; // this wiil be given with the `BookDetailsScreen(currentBook: ..)` , so that this will used for creating the Screen with the 'id', 'name', and others from the Mapa
const BookDetailsScreen({ /* constructor */
Key? key,
required this.currentBook, // when calling the `BookDetailsScreen(currentBook: ..)` the `currentBook` MUST be given as its "required" Named Paramater,
}); // i.e. `BookDetailsScreen()` will always get a `Map<String, String>>`,as `curretBook` is of type `Map<String, String>` as decalred aboves
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: Text(currentBook['title']!),
),
body: Center(
child: Column(
children: [
Text(
'Author is: ${currentBook['author']!}', // using the `currentBook` of type `Map<String, String>` which is declared above and was given value by being called as `BookDetailsScreen(currentBook: ..)`
style: const TextStyle(color: Colors.red, fontSize: 50),
),
Text(currentBook[
'id']!), // using the `currentBook` of type `Map<String, String>` which is declared above and was given value by being called as `BookDetailsScreen(currentBook: ..)`
ElevatedButton(
onPressed: () {
Beamer.of(context)
.beamBack(); // to go back to the PREVIOUS screen
},
child: const Text('Back to Book Menu'),
)
],
),
),
);
}
}
🔶 queryParameters
queryParameterscan not be used withRoutesLocationBuilder, we MUST USEBeamerLocationBuilder
So, for almost every usecases like multi-beamers, preserve state beamers, query-parameters we need the
BeamerLocationBuilder