The original link: beamer | Flutter Package (Flutter – IO. Cn)
Many shortcomings, generous advice
beamer
beamer.devAir safety support
Handles routing for applications across all platforms and synchronizes browser URL bars and other content. Beamer is based on the Router and implements all the underlying logic.
Quick start
- Quick start
- navigation
- Navigation to return to
- Up (pops page from stack)
- Reverse timing (Beam to previous state)
- Android Back button
- Visit the nearest Beamer
- The use of “the Navigator 1.0”
- The core concept
- BeamLocation
- BeamState
- Custom state
- usage
- BeamLocation list
- Route Map
- guardian
- Nested navigation
- General Precautions
- Tips and FAQs
- The sample
- The migration
- Help and Contact
- contribution
Quick start
The easiest way to use it is to implement it with a RoutesLocationBuilder, which produces the least code. This is a great choice for applications with fewer navigation scenarios or applications with shallow page stacks (i.e. pages rarely stack together).
class MyApp extends StatelessWidget {
final routerDelegate = BeamerDelegate(
locationBuilder: RoutesLocationBuilder(
routes: {
// Return either Widgets or BeamPages if more customization is needed
// Return to Widgets or BeamPages if more customization is required
'/': (context, state, data) => HomeScreen(),
'/books': (context, state, data) => BooksScreen(),
'/books/:bookId': (context, state, data) {
// Take the path parameter of interest from BeamState
// Get path parameters from BeamState
final bookId = state.pathParameters['bookId']! ;// Collect arbitrary data that persists throughout navigation
// Collect arbitrary data that persists throughout the navigation
final info = (data as MyObject).info;
// Use BeamPage to define custom behavior
// Use BeamPage to customize behavior
return BeamPage(
key: ValueKey('book-$bookId'),
title: 'A Book #$bookId',
popToNamed: '/', type: BeamPageType.scaleTransition, child: BookDetailsScreen(bookId, info), ); }},),);@override
Widget build(BuildContext context) {
returnMaterialApp.router( routeInformationParser: BeamerParser(), routerDelegate: routerDelegate, ); }}Copy the code
The RoutesLocationBuilder selects and sorts routes based on the path. For example, navigating to /books/1 matches all three entities in routes and then stacks them together. Navigating to /books matches the first two entities of routes.
The corresponding page is put into navigator.pages, and the BeamerDelegate (reconstructs) the Navigator to display the selected stack of pages on the screen.
Why do we have onelocationBuilder
? BeamLocation
What is? What is its output?
BeamLocation is an entity that determines which pages go to navigator.pages based on its state. The locationBuilder selects the appropriate BeamLocation to further process the received RouteInformation. . This is most by verifying BeamLocation pathPatterns to implement.
The RoutesLocationBuilder returns a special type of BeamLocation – RoutesBeamLocation, which has an implementation for most commonly used navigation scenarios. If the RoutesLocationBuilder does not provide the desired behavior or sufficient customization, you can extend BeamLocation to define and organize behavior for any number of page stacks that enter navigator.Pages.
BeamLocation [英 文], BeamState[英 文]
Navigation is done using “Beam”. Think of it as beam somewhere else in the application. Similar to navigator.of (context).pushreplacementnamed (‘/my-route’), but Beamer is not limited to a single page, or the push itself. BeamLocation creates a stack of pages that will be built when beam hits a page. Beaming feels like using multiple Navigator push/pop methods at the same time.
// Basic beaming
Beamer.of(context).beamToNamed('/books/2');
// Beaming with an extension method on BuildContext
// Use BuildContext's extension method, Beaming
context.beamToNamed('/books/2');
// Beaming with additional data that persist
// throughout navigation withing the same BeamLocation
// Use the same BeamLocation to navigate the data using Beaming.
context.beamToNamed('/book/2', data: MyObject());
Copy the code
There are two types of return: Reverse navigation; Up and reverse timing.
Up (pops page from stack)
The up navigation guides you to the previous page in the current page stack. The popup is known as the Navigator’s Pop /maybePop method. If no other handling is specified, the BackButton return button of the default AppBar calls this method.
Navigator.of(context).maybePop();
Copy the code
Reverse timing (Beam to previous state)
Reversing the sequential navigation will take you to any of the places you visited previously. In the case of deep links (e.g., navigating from /authors/3 to /books/2 rather than /books to /books/2), this is not the same as pop-ups. Beamer maintains a navigational history in beamingHistory, so it can navigate to a previous entry point in beamingHistory. This is called “beam back”. Skin it).
Beamer.of(context).beamBack();
Copy the code
The Android return button integrated with Beam is implemented by setting the backButtonDispatcher in the MaterialApp.router. The dispenser needs a reference to the same BeamerDelegate set for the routerDelegate.
MaterialApp.router(
...
routerDelegate: beamerDelegate,
backButtonDispatcher: BeamerBackButtonDispatcher(delegate: beamerDelegate),
)
Copy the code
BeamerBackButtonDispatcher will first try to pop (pop), if the popup is not available, will beamBack instead. If beamBack returns false (there is no place to return to), Android’s back button will close the app, or perhaps return to the previous app (open the current app via a deep-link). BeamerBackButtonDispatcher can be configured to alwaysBeamBack (means do not try to pop (pop)) or fallbackToBeamBack (meaning not try beamBack).
Visit the nearest Beamer
To access the attributes of the route in the component (for example, bookId for building the BookDetailsScreen), use:
@override
Widget build(BuildContext context) {
final beamState = Beamer.of(context).currentBeamLocation.state as BeamState;
final bookId = beamState.pathParameters['bookId']; . }Copy the code
Note that “Navigator 1.0” (imperative push/pop and similar functions) can be used with Beamer. We’ve already seen Navigator. Pop used to navigate up. This tells us that we are using the same Navigator, just a different API.
Pushing with navigator.of (context).push (or any similar action) does not reflect the state of the BeamLocation, which means that the browser URL does not change. Beamer.of(context).updaterouteInformation (…) To just update the URL. Of course, when using Beamer on mobile, you don’t have this problem because you can’t see the URL.
In general, each navigation scenario should be implementable declarative (defining the page stack) rather than imperative (pushing), but the difficulty of doing this varies.
For intermediate and advanced usage, let’s introduce some core concepts: BeamLocation and BeamState.
The core concept
At the top level, Beamer is a Router wrapper that uses its own implementation of RouterDelegate and RouteInformationParser. Beamer’s goal is to separate the responsibility of building a page stack with multiple classes with different states for navigator.pages, instead of using a single global state for all page stacks.
For example, we want to handle all the profile related page stacks like:
[ ProfilePage ]
(Personal information page),[ ProfilePage, FriendsPage]
(Profile page, friends page),[ ProfilePage, FriendsPage, FriendDetailsPage ]
(Profile page, friend page, friend details page),[ ProfilePage, SettingsPage ]
(Profile page, Settings page),- .
Use some “ProfileHandler” to know which state corresponds to which page stack. Similarly, we want a “ShopHandler” to handle all the store associated page stacks. These pages are as follows:
[ ShopPage ]
(Store page),[ ShopPage, CategoriesPage ]
(Store page, category page),[ ShopPage, CategoriesPage, ItemsPage ]
(Store page, Category page, product page),[ ShopPage, CategoriesPage, ItemsPage, ItemDetailsPage ]
(Store page, Category page, product page, product details page),[ ShopPage, ItemsPage, ItemDetailsPage ]
(Store page, product page, product details page),[ ShopPage, CartPage ]
(Store page, shopping cart page),- .
These “Handlers” are called BeamLocation.
BeamLocation doesn’t work by itself. Decide which BeamLocation to use and how to further process the RouteInformation and build the page for the Navigator when RouteInformation is brought into the application using deep links as the initial state or result of beaming. This is BeamerDelegate locationBuilder work, it will receive RouteInformation, then according to its pathPatterns (path mode) to correct BeamLocation.
BeamLocation then creates and stores its state from RouteInformation, which is used to build a page stack.
BeamLocation
The most important component in Beamer is the BeamLocation, which presents the status of one or more pages.
BeamLocation has three important roles:
- Know which URIs it can handle:
pathPatterns
- Know how to build a page stack:
buildPages
- Keep status (
state
), the state provides a link between the two above.
BeamLocation is an abstract class that needs to be implemented. The purpose of having multiple BeamLocations is to architecturally separate unrelated “places” in an application. For example, BooksLocation handles all pages related to books and ArticlesLocation handles all articles.
Here is an example of a BeamLocation:
class BooksLocation extends BeamLocation<BeamState> {
@override
List<Pattern> get pathPatterns => ['/books/:bookId'];
@override
List<BeamPage> buildPages(BuildContext context, BeamState state) {
final pages = [
const BeamPage(
key: ValueKey('home'),
child: HomeScreen(),
),
if (state.uri.pathSegments.contains('books'))
const BeamPage(
key: ValueKey('books'),
child: BooksScreen(),
),
];
final String? bookIdParameter = state.pathParameters['bookId'];
if(bookIdParameter ! =null) {
final bookId = int.tryParse(bookIdParameter);
pages.add(
BeamPage(
key: ValueKey('book-$bookIdParameter'),
title: 'Book #$bookIdParameter',
child: BookDetailsScreen(bookId: bookId),
),
);
}
returnpages; }}Copy the code
BeamState
BeamState is a prefabricated state that can be used for custom BeamLocation. It maintains various URI attributes such as pathPatternSegments (segments that select a path pattern; a BeamLocation can support multiple such segments), pathParameters, and queryParameters.
Custom state
Any class can be used as the state of a BeamLocation, such as ChangeNotifier. The only requirement is (mix) RouteInformationSerializable BeamLocation state, the latter will be forced to realize fromRouteInformation and toRouteInformation.
Examples of completion are available here.
A custom BooksState:
class BooksState extends ChangeNotifier with RouteInformationSerializable {
BooksState([
bool isBooksListOn = false.int? selectedBookId,
]) : _isBooksListOn = isBooksListOn,
_selectedBookId = selectedBookId;
bool _isBooksListOn;
bool get isBooksListOn => _isBooksListOn;
set isBooksListOn(bool isOn) {
_isBooksListOn = isOn;
notifyListeners();
}
int? _selectedBookId;
int? get selectedBookId => _selectedBookId;
set selectedBookId(int? id) {
_selectedBookId = id;
notifyListeners();
}
void updateWith(bool isBooksListOn, int? selectedBookId) {
_isBooksListOn = isBooksListOn;
_selectedBookId = selectedBookId;
notifyListeners();
}
@override
BooksState fromRouteInformation(RouteInformation routeInformation) {
final uri = Uri.parse(routeInformation.location ?? '/');
if (uri.pathSegments.isNotEmpty) {
_isBooksListOn = true;
if (uri.pathSegments.length > 1) {
_selectedBookId = int.parse(uri.pathSegments[1]); }}return this;
}
@override
RouteInformation toRouteInformation() {
String uriString = ' ';
if (_isBooksListOn) {
uriString += '/books';
}
if(_selectedBookId ! =null) {
uriString += '/$_selectedBookId';
}
return RouteInformation(location: uriString.isEmpty ? '/': uriString); }}Copy the code
Then use the above state’s BeamLocation to find the following. Note that not all of these need to be overridden if the custom state is not ChangeNotifier.
class BooksLocation extends BeamLocation<BooksState> {
BooksLocation(RouteInformation routeInformation) : super(routeInformation);
@override
BooksState createState(RouteInformation routeInformation) =>
BooksState().fromRouteInformation(routeInformation);
@override
void initState() {
super.initState();
state.addListener(notifyListeners);
}
@override
void updateState(RouteInformation routeInformation) {
final booksState = BooksState().fromRouteInformation(routeInformation);
state.updateWith(booksState.isBooksListOn, booksState.selectedBookId);
}
@override
void disposeState() {
state.removeListener(notifyListeners);
super.disposeState();
}
@override
List<Pattern> get pathPatterns => ['/books/:bookId'];
@override
List<BeamPage> buildPages(BuildContext context, BooksState state) {
final pages = [
const BeamPage(
key: ValueKey('home'),
child: HomeScreen(),
),
if (state.isBooksListOn)
const BeamPage(
key: ValueKey('books'),
child: BooksScreen(),
),
];
if(state.selectedBookId ! =null) {
pages.add(
BeamPage(
key: ValueKey('book-${state.selectedBookId}'),
title: 'Book #${state.selectedBookId}',
child: BookDetailsScreen(bookId: state.selectedBookId),
),
);
}
returnpages; }}Copy the code
When using custom BooksState, you can make full use of the declarative by writing:
onTap: () {
final state = context.currentBeamLocation.state as BooksState;
state.selectedBookId = 3;
},
Copy the code
Note that Beamer.of(context).beamtonamed (‘/books/3’) produces the same result.
usage
To use Beamer (or any Router), you must construct the *App component with the.Router constructor (see Router documentation for more). Along with the general properties of all * apps, we must provide:
routeInformationParser
Parse the incoming URI.routerDelegate
Control (re) buildNavigator
。
Here we use the BeamerParser and BeamerDelegate implementations in Beamer to pass the desired LocationBuilder to both. In its simplest form, the LocationBuilder is just a function that takes the current RouteInformation (and BeamParameters (not important here)) and returns a BeamLocation based on a URI or other state attribute.
class MyApp extends StatelessWidget {
final routerDelegate = BeamerDelegate(
locationBuilder: (routeInformation, _) {
if(routeInformation.location! .contains('books')) {
return BooksLocation(routeInformation);
}
returnHomeLocation(routeInformation); });@override
Widget build(BuildContext context) {
returnMaterialApp.router( routerDelegate: routerDelegate, routeInformationParser: BeamerParser(), backButtonDispatcher: BeamerBackButtonDispatcher(delegate: routerDelegate), ); }}Copy the code
If we don’t want to customize a locationBuilder function, there are two other options available.
BeamLocation list
The BeamerLocationBuilder can specify a list of BeamLocations. The builder will automatically select the correct BeamLocation based on its pathPatterns.
final routerDelegate = BeamerDelegate(
locationBuilder: BeamerLocationBuilder(
beamLocations: [
HomeLocation(),
BooksLocation(),
],
),
);
Copy the code
Route Map
We can specify a routing Map to the RoutesLocationBuilder, as mentioned in Quick Start. This completely removes the need to customize BeamLocation, but also provides a minimal amount of customization. However, wildcards and path parameters are supported along with all other options.
final routerDelegate = BeamerDelegate(
locationBuilder: RoutesLocationBuilder(
routes: {
'/': (context, state, data) => HomeScreen(),
'/books': (context, state, data) => BooksScreen(),
'/books/:bookId': (context, state, data) =>
BookDetailsScreen(
bookId: state.pathParameters['bookId'],),},),);Copy the code
guardian
To guard specific routes, for example to prevent unauthorized user access, the global BeamGuard can be set via the Beamerdelegate.Guards property. A common example is BeamGuard to guard any non-/ login route and then redirect to /login if the user is not authorized.
BeamGuard(
// on which path patterns (from incoming routes) to perform the check
// Check by path mode (incoming routes)
pathPatterns: ['/login'].// perform the check on all patterns that **don't** have a match in pathPatterns
// Check all unmatched paths
guardNonMatching: true.// return false to redirect
// Return false to redirect
check: (context, location) => context.isUserAuthenticated(),
// where to redirect on a false check
// The redirection position when false is returned
beamToNamed: (origin, target) => '/login'.)Copy the code
Note the use of guardNonMatching in this example. This is important because daemons (there are many daemons, each with a different aspect) run recursively on the output of the previous daemon until a “safe” route is reached. A common mistake is to install a daemon with pathBlueprints:[‘*’] to guard all, but all also includes /login (which is a “safe” route), thus falling into an infinite loop:
- check
/login
- Unauthorized User
- Beam to
/login
- check
/login
- Unauthorized User
- Beam to
/login
- .
Of course, don’t use guardNonMatching. Sometimes we just want to guard a small number of explicitly specified routes. There is a guard with the same role as above, implemented by default as guardNonMatching: false:
BeamGuard(
pathBlueprints: ['/profile/*'.'/orders/*'],
check: (context, location) => context.isUserAuthenticated(),
beamToNamed: (origin, target) => '/login'.)Copy the code
When you need nested navigation, you can place Beamer anywhere in the component tree for nested navigation. There is no limit to how many Beamer there are in an app. A common usage scenario is the bottom navigation bar (see the example), as follows:
class MyApp extends StatelessWidget {
final routerDelegate = BeamerDelegate(
initialPath: '/books',
locationBuilder: RoutesLocationBuilder(
routes: {
'/ *': (context, state, data) {
final beamerKey = GlobalKey<BeamerState>();
returnScaffold( body: Beamer( key: beamerKey, routerDelegate: BeamerDelegate( locationBuilder: BeamerLocationBuilder( beamLocations: [ BooksLocation(), ArticlesLocation(), ], ), ), ), bottomNavigationBar: BottomNavigationBarWidget( beamerKey: beamerKey, ), ); }},),);@override
Widget build(BuildContext context) {
returnMaterialApp.router( routerDelegate: routerDelegate, routeInformationParser: BeamerParser(), ); }}Copy the code
General Precautions
-
When extending BeamLocation, you need to implement two methods: pathPatterns and buildPages.
-
BuildPages returns the stack of pages that the Navigator builds when Beam hits. Beamer’s pathPatterns then determine which BeamLocation processes which URI.
-
BeamLocation keeps the query and path schema of the URI in its BeamState. If you want to get path parameters from the browser, the: in pathPatterns is required.
-
-
BeamPage’s Child is any component that renders the application screen/interface.
-
Key is important for Navigatar optimization rebuild. This should be the only value in “page state”.
-
BeamPage creates the MaterialPageRoute by default, but other transformations can be selected by setting BeamPage. Type to an available BeamPageType.
-
Tips and FAQs
- Can be in
runApp()
Before the callBeamer.setPathUrlStrategy()
Remove from the URL#
。 BeamPage.title
The default is to set the title of the browser TAB pageBeamerDelegate.setBrowserTabTitle
Set tofalse
To select remove.- State loss during hot loading
The sample
inhereSee all examples (with GIFs)
- Location BuildersBased on:This articleRecreating the sample application, you can learn a lot about Navigator 2.0 here. This example shows
locationBuilder
All three options of.
-
Advanced Books: As a step forward, more processes have been added to showcase Beamer’s capabilities.
-
Deep Location: In multiple pages that are already stacked, you can beam to a Location immediately in the application and then pop them up one by one or simply beamBack to where the jump came from. Note that the beamBackOnPop parameter of beamToNamed helps to override pop that AppBar using beamBack.
ElevatedButton(
onPressed: () => context.beamToNamed('/a/b/c/d'),
//onPressed: () => context.beamToNamed('/a/b/c/d', beamBackOnPop: true),
child: Text('Beam deep'),),Copy the code
- Provider: Can be overridden
BeamLocation.builder
To provide some data for the entire location, that is, for allpages
(Page).
// In your BeamLocation implementation
@override
Widget builder(BuildContext context, Navigator navigator) {
return MyProvider<MyObject>(
create: (context) => MyObject(),
child: navigator,
);
}
Copy the code
- Guards: Can define global daemons (for example, authorization daemons) or used to keep a specific state secure
BeamLocation.guards
。
// Global daemon in BeamerDelegate
BeamerDelegate(
guards: [
// If the user is not authorized, beam to /login to guard /books and /books/* :
BeamGuard(
pathBlueprints: ['/books'.'/books/*'],
check: (context, location) => context.isAuthenticated,
beamToNamed: (origin, target) => '/login'[...] .Copy the code
// The current daemon in BeamLocation
@override
List<BeamGuard> get guards => [
// If the user tries to enter books/2, the forbidden page is displayed.
BeamGuard(
pathBlueprints: ['/books/2'],
check: (context, location) => false,
showPage: forbiddenPage,
),
];
Copy the code
- Authentication BlocThis example shows how to use it
BeamGuard
和 flutter_blocState management for authorization processes. - Bottom NavigationThis example is used when using the bottom navigation bar
Beamer
Put it in the component tree. - Bottom Navigation With Multiple Beamers: One for each TAB
Beamer
。 - A side drawer bar with Nested Navigation.
Note: In all nested Beamer, the full path must be specified when defining BeamLocation and beam.
- Animated Rail: Example of using the animated_rail package.
The migration
Migrate from 0.14 to 1.0.0
This Medium article explains the changes between the two versions and provides a migration guide. The most notable disruptive changes:
- If you are using
SimpleLocationBuilder
:
Instead of
locationBuilder: SimpleLocationBuilder(
routes: {
'/': (context, state) => MyWidget(),
'/another': (context, state) => AnotherThatNeedsState(state)
}
)
Copy the code
Now it is
locationBuilder: RoutesLocationBuilder(
routes: {
'/': (context, state, data) => MyWidget(),
'/another': (context, state, data) => AnotherThatNeedsState(state)
}
)
Copy the code
- If you use a custom
BeamLocation
:
Instead of
class BooksLocation extends BeamLocation { @override List<Pattern> get pathBlueprints => ['/books/:bookId']; . }Copy the code
Now it is
class BooksLocation extends BeamLocation<BeamState> { @override List<Pattern> get pathPatterns => ['/books/:bookId']; . }Copy the code
Migrate from 0.13 to 0.14
Instead of
locationBuilder: SimpleLocationBuilder( routes: { '/': (context) => MyWidget(), '/another': (context) { final state = context.currentBeamLocation.state; return AnotherThatNeedsState(state); }})Copy the code
Now it is
locationBuilder: SimpleLocationBuilder(
routes: {
'/': (context, state) => MyWidget(),
'/another': (context, state) => AnotherThatNeedsState(state)
}
)
Copy the code
Migrate from 0.12 to 0.13
BeamerRouterDelegate
renameBeamerDelegate
BeamerRouteInformationParser
renameBeamerParser
pagesBuilder
renamebuildPages
Beamer.of(context).currentLocation
renameBeamer.of(context).currentBeamLocation
Migrate from 0.11 to 0.12
- There is no longer
RootRouterDelegate
, just rename it toBeamerDelegate
. If you’re using ithomeBuilder
, the use ofSimpleLocationBuilder
And then toroutes: {'/': (context) => HomeScreen()}
. beamBack
Change the behavior to jump to the previous oneBeamState
Rather thanBeamLocation
. If this is not what you want, usepopBeamLocation()
And the originalbeamback
The behavior is the same.
Migrate from 0.10 to 0.11
BeamerDelegate.beamLocations
Now it islocationBuilder
. Look at theBeamerLocationBuilder
For the simplest migration.Beamer
Now receiveBeamerDelegate
“Rather than receiving it directlyBeamLocations
。buildPages
It can now be carriedstate
。
Migration from 0.9 to 0.10
-
The BeamLocation constructor now accepts only BeamState state. (There is no need to define a special constructor here; if you use beamToNamed, you can call super.)
-
Most of the properties of the original BeamLocation are now in beamLocation.state. Access them via BeamLocation:
pathParameters
Now it isstate.pathParameters
queryParameters
Now it isstate.queryParameters
data
Now it isstate.data
pathSegments
Now it isstate.pathBlueprintSegments
uri
Now it isstate.uri
Migration from 0.7 to 0.8
pages
renameBeamLocation
In thebuildPages
- pass
beamLocations
给BeamerDelegate
Instead ofBeamerParser
. To viewusage
Migrate from 0.4 to 0.5
- Instead of using
Beamer
packagingMaterialApp
, the use of*App.router()
。 String BeamLocation.pathBlueprint
Now it isList<String> BeamLocation.pathBlueprints
- remove
BeamLocation.withParameters
Constructor, all arguments are handled by a constructor. If you needsuper
, see an example. BeamPage.page
Now known asBeamPage.child
。
Help and Contact
Any questions, questions, suggestions, fun ideas… Join us on Discord.
contribution
If you notice any bugs but are not in Issues, create a new issue. If you want to make your own fixes or enhancements, you are very welcome to propose PR, before proposing PR:
- If you want to resolve an existing issue, please first let us know in the comments of the issue.
- If there are other enhancement ideas, create an issue first so we can discuss your ideas.
Look forward to seeing you on our list of respected contributors!
- devj3ns
- ggirotto
- youssefali424
- schultek
- hatem-u
- matuella
- jeduden
- omacranger
- spicybackend
- ened
- AdamBuchweitz
- nikitadol
- gabriel-mocioaca
- piyushchauhan
- britannio
- satyajitghana
- Zambrella
- luketg8
- timshadel
- definev
Perfection of any art is irresistible