Skip to main content

RoutesLocationBuilder

https://github.com/slovnicki/beamer/tree/master/examples/location_builders/lib [sarthak's learn_beamer repo] (https://github.com/cosmicsarthak/learn_beamer)

use RoutesLocationBuilder for 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:

  1. routeInformationParser: it always take BeamerParser()
  2. routerDelegate: for simplicity, I am using a varaible routerDelegate which is declared separately as final
    • it can have different types of routes:
      1. direct: '/screen': (context, state, data) => Screen(),
      2. for parameters
      3. for queries
  3. backButtonDispatcher: Integration of Android's back button is achieved by setting a backButtonDispatcher in MaterialApp.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 the MaterialApp.router(..)

I am using a varaible routerDelegate which is declared separately as final

declaring only 'direct' routes
  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<..,..>>

pathParameters only takes Map<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.dart
  • books_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​

  • pathParameters only takes Map<String, String>
  • we need the 'id' from 'id': '1' for the URL parameter
  • also by Map we access the corresponding CHANGING values by calling the NON-CHANGING Common values such as id, title, author
    • by by currentBook['author'] or currentBook['id'] I can get access to different cossponding values by just using methods of Iterable such as
by by currentBook['author'] or currentBook['id'] I can get access to different cossponding values by just using methods of Iterable such as

using the 'booksList' from this data model inside 'books_menu.dart' to iterate through all the values of the 'Map<String,String>' and then using 'id' & 'title' wherever we need
      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()`
),
models/books_list.dart
// 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>>

  1. importing the booksList from the models/books_list.dart
  2. iterating through the booksList using .map(..) then finally again converting back to List using .toList()
  3. calling beamToNamed(..) ⏩⏩ onTap: () => Beamer.of(context).beamToNamed('/booksMenu/${el['id']}'),
  4. converting the .map into .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 child inside BeamPage(child:.., key:.., title:..) of the parameter URL inside main.dart

  • decalring the final variable of type Map<String, String>
    • this variable will be required in the constructor of this class
    • it will used to store the Map<..,..> given while calling the BookDetailsScreen(currentBook: ..)
  • const BookDetailsScreen({Key? key,required this.currentBook,});
    • when calling the BookDetailsScreen(currentBook: ..) the currentBook MUST be given as its a "required" Named Paramater,
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​

So, for almost every usecases like multi-beamers, preserve state beamers, query-parameters we need the BeamerLocationBuilder