A Flutter package to help you handle your application routing and synchronize it with browser URL. Beamer uses the power of Router and implements all the underlying logic for you. To find out more, follow these links:
A routing package that lets you navigate through guarded page stacks and URLs using the Router and Navigator's Pages API, aka "Navigator 2.0".
Overview
Comments
-
Navigation notification
We are using beamer with MaterialApp.router, where we build navigation menu for users. We use some menu index to determine, which menu item is selected (essentially BottomBar). Everything works smoothly, however when user types url directly to browser e.g /foo/1 there is no way how to notify MenuWidget about this change, so menu is stucked with old index.
Is there a way how to propagate changes to path, when user types directly to browser? I think this is only web issue, because this won't happen in mobile development.
bug documentation question help -
[enhancement] restructure BeamerRouterDelegate
After digging in the code a bit I finally realised why I have so many problems. Because stuff are mutable you can access it from everywhere and change it everywhere so when you wrote
BeamerLocationBuilder
you can change the state of each location from there. or youprepare()
location when you return it from location builder. All that make things much more complicated to extend or finding errors especially when whoever is implementing them need to know or remember to do so. I for example built aPatternLocationBuilder
so I can usePattern
class and find the right path now something like:class PatternLocationBuilder extends BeamerLocationBuilder { PatternLocationBuilder({ required Map<Pattern, BeamLocation> routes, }) : super( locationBuilder: (state) => // find right path using pattern.matchAsPrefix(state.uri.toString()) }
But I didn't know I need to call prepare createState etc..
If you run all of those methods within your
RouterDelegate
we can avoid a situation where code is copied multiple times (e.g prepare createState etc...) then the code I wrote would work and I wouldn't need to remember toprepare()
createState
etc...for example I would wrap
LocationBuilder
withinBeamerRouterDelegate
with another function calledLocationInitializer
(or something like that).// global function or static within the class T defaultCreateState<T>( Uri uri, { Map<String, dynamic> data = const <String, dynamic>{}, }) => BeamState.fromUri(uri, data: data) as T; BeamPage defaultNotFoundBuilder(BeamState state) => BeamPage( child: Container(child: Center(child: Text('Not found'))), ); typedef LocationNoFoundBuilder = BeamPage Function(BeamState state); // maybe not state just URI typedef LocationInitializer = BeamLocation Function(BeamLocation location); typedef StringLocationBuilder = BeamLocation Function(String url); // or can use URI typedef StateBuilder<T> = T Function(Uri uri, { Map<String, dynamic> data, }); final LocationBuilder locationBuilder; final LocationInitializer locationInitializer; final StateBuilder<T> createState; // should be final, why is it nullable now? if by mistake this createState = null happens we will get lots of problems final LocationNoFoundBuilder notFoundBuilder; // no need for notFoundPage notFoundRedirect those can just be some function, redirectNotFoundBuilder for example StringLocationBuilder get _locationBuilder => (String url, { Map<String, dynamic> data = const <String, dynamic>{}, }) => locationInitializer(locationBuilder(createState(Uri.parse(url), data: data))); BeamerRouterDelegate({ required this.locationBuilder, this.locationInitializer = someDefaultInitializer, this.createState = defaultCreateState, this.notFoundBuilder = defaultNotFoundBuilder, this.initialPath = '/', this.createState, this.preferUpdate = true, this.removeDuplicateHistory = true, this.guards = const <BeamGuard>[], this.navigatorObservers = const <NavigatorObserver>[], this.transitionDelegate = const DefaultTransitionDelegate(), this.onPopPage, }) { _currentLocation = _locationBuilder(initialPath); }
then you can always call
_locationBuilder
with whatever path in string form and all the rest is done.. if you check your code you have at least 2 - 3 location where you callcreateState!(Uri.parse(somePath))
and the same withprepare()
and the same with some other stuff. my point is by doing something like that you make the code simpler and less prone to errors like I had...just to make my point stronger, same code called 7 times: https://github.com/slovnicki/beamer/blob/39f96295b20a23bba4a46818d576e608399dd8b4/package/lib/src/beamer_router_delegate.dart#L45 https://github.com/slovnicki/beamer/blob/39f96295b20a23bba4a46818d576e608399dd8b4/package/lib/src/beamer_router_delegate.dart#L60 https://github.com/slovnicki/beamer/blob/39f96295b20a23bba4a46818d576e608399dd8b4/package/lib/src/beamer_router_delegate.dart#L238 https://github.com/slovnicki/beamer/blob/39f96295b20a23bba4a46818d576e608399dd8b4/package/lib/src/beamer_router_delegate.dart#L240 https://github.com/slovnicki/beamer/blob/39f96295b20a23bba4a46818d576e608399dd8b4/package/lib/src/beamer_router_delegate.dart#L368 https://github.com/slovnicki/beamer/blob/39f96295b20a23bba4a46818d576e608399dd8b4/package/lib/src/beamer_router_delegate.dart#L428 https://github.com/slovnicki/beamer/blob/39f96295b20a23bba4a46818d576e608399dd8b4/package/lib/src/beamer_router_delegate.dart#L496
I can do the same with
prepare()
createState
etc.. being called different places with same copied line multiple timesAnother plus is to try and get all the classes to have a
const
constructor :) but need to investigate more for that. One more plus is to have atypedef NavigatorBuilder = Widget Function(BuildContext context, Key key, pages, observers,... etc)
so users can have more fine control, you will pass all parameters frombuild
function it just gives users more flexibility in the long run. And maybe try to avoid usingBuilder
widget as its limiting sometimes when you need to force minimum size, at least I had some problems with itUPDATE: https://github.com/slovnicki/beamer/blob/39f96295b20a23bba4a46818d576e608399dd8b4/package/lib/src/beamer_router_delegate.dart#L378 another good example, the need to remember to call
refactor proposal discussionnotifyListeners()
every time.. just have ahandlePop
function insideBeamLocation
that does the magic... and I WOULD STRONGLY SUGGEST to make state read only from outsideBeamLocation
... that way you'll avoid copying code to many places or remember to do something after before etc... -
[Question] BottomNav and IndexedStack, how to move to a specific plage and browser back button ?
Hello, I tried the example of beamer with bottom navigation and multiple Beamer with IndexedStack, and I don't know to move directly to a book detail screen from articles screen (or to an article detail screen for books screen).
I tried created a globalKey in AppScreen, created this function :
void moveTab(int index) { setState(() => _currentIndex = index); _routerDelegates[_currentIndex] .parent ?.updateRouteInformation(_routerDelegates[_currentIndex].currentLocation.state.uri); }
and using
AppScreen.globalKey.currentState?.moveTab(1);
. With this, I can change bottom nav section, but i could not go to the right section directly, like /books/1 or /articles/1, using the function below after moveTav() :Beamer.of(context).currentLocation.update((state) => state.copyWith( pathBlueprintSegments: ['books', ':bookId'], pathParameters: {'bookId': '1'}, ));
This might be related to #226
Nevertheless, this package is really good ! Thank you very much @slovnicki for you incredible work <3
https://user-images.githubusercontent.com/58068925/116758589-ae92ff80-aa10-11eb-93f0-7ebb8e35f6dc.mp4
flutter --version Flutter 2.2.0-10.1.pre • channel beta • https://github.com/flutter/flutter.git Framework • revision 0941968447 (2 weeks ago) • 2021-04-15 12:01:02 -0700 Engine • revision d2a2e93510 Tools • Dart 2.13.0 (build 2.13.0-211.6.beta)
main.yaml
import 'package:flutter/material.dart'; import 'package:beamer/beamer.dart'; // DATA const List<Map<String, String>> books = [ { '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', }, ]; const List<Map<String, String>> articles = [ { 'id': '1', 'title': 'Article 1', 'author': 'Author 1', }, { 'id': '2', 'title': 'Article 2', 'author': 'Author 2', }, ]; // SCREENS class BooksScreen extends StatelessWidget { @override Widget build(BuildContext context) { return Scaffold( appBar: AppBar( title: Text('Books'), actions: [ IconButton( onPressed: () { AppScreen.globalKey.currentState?.moveTab(1); Beamer.of(context).currentLocation.update((state) => state.copyWith( pathBlueprintSegments: ['books', ':bookId'], pathParameters: {'bookId': '1'}, )); }, icon: Icon(Icons.star)) ], ), body: ListView( children: books .map((book) => ListTile( title: Text(book['title']!), subtitle: Text(book['author']!), onTap: () => Beamer.of(context).currentLocation.update( (state) => state.copyWith( pathBlueprintSegments: ['books', ':bookId'], pathParameters: {'bookId': book['id']!}, ), ), )) .toList(), ), ); } } class BookDetailsScreen extends StatelessWidget { BookDetailsScreen({ required this.bookId, }) : book = books.firstWhere((book) => book['id'] == bookId); final String bookId; final Map<String, String> book; @override Widget build(BuildContext context) { return Scaffold( appBar: AppBar(title: Text('Book: ${book['title']}')), body: Text('Author: ${book['author']}'), ); } } class ArticlesScreen extends StatelessWidget { final items = List<String>.generate(100, (i) => "Item $i"); @override Widget build(BuildContext context) { return Scaffold( appBar: AppBar( title: Text('Articles'), actions: [ IconButton( onPressed: () { AppScreen.globalKey.currentState?.moveTab(1); Beamer.of(context).currentLocation.update((state) => state.copyWith( pathBlueprintSegments: ['books', ':bookId'], pathParameters: {'bookId': '1'}, )); }, icon: Icon(Icons.star)) ], ), body: Column( children: [ Expanded( flex: 2, child: ListView( children: articles .map((article) => ListTile( title: Text(article['title']!), subtitle: Text(article['author']!), onTap: () => Beamer.of(context).currentLocation.update((state) => state.copyWith( pathBlueprintSegments: ['articles', ':articleId'], pathParameters: {'articleId': article['id']!}, )), )) .toList(), ), ), Expanded( child: SizedBox(), ), Expanded( flex: 2, child: ListView.builder( itemCount: items.length, itemBuilder: (context, index) { return ListTile( title: Text('${items[index]}'), ); }, ), ), ], ), ); } } class ArticleDetailsScreen extends StatelessWidget { ArticleDetailsScreen({ required this.articleId, }) : article = articles.firstWhere((article) => article['id'] == articleId); final String articleId; final Map<String, String> article; @override Widget build(BuildContext context) { return Scaffold( appBar: AppBar(title: Text('Article: ${article['title']}')), body: Text('Author: ${article['author']}'), ); } } // LOCATIONS class AppLocation extends BeamLocation { AppLocation(state) : super(state); @override List<String> get pathBlueprints => ['/*']; @override List<BeamPage> pagesBuilder(BuildContext context, BeamState state) => [ BeamPage( key: ValueKey('app-${state.uri}'), child: AppScreen(beamState: state), ), ]; } class BooksLocation extends BeamLocation { BooksLocation(BeamState state) : super(state); @override List<String> get pathBlueprints => ['/books/:bookId']; @override List<BeamPage> pagesBuilder(BuildContext context, BeamState state) => [ BeamPage( key: ValueKey('books'), child: BooksScreen(), ), if (state.pathParameters.containsKey('bookId')) BeamPage( key: ValueKey('book-${state.pathParameters['bookId']}'), child: BookDetailsScreen( bookId: state.pathParameters['bookId']!, ), ), ]; } class ArticlesLocation extends BeamLocation { ArticlesLocation(BeamState state) : super(state); @override List<String> get pathBlueprints => ['/articles/:articleId']; @override List<BeamPage> pagesBuilder(BuildContext context, BeamState state) => [ BeamPage( key: ValueKey('articles'), child: ArticlesScreen(), ), if (state.pathParameters.containsKey('articleId')) BeamPage( key: ValueKey('articles-${state.pathParameters['articleId']}'), child: ArticleDetailsScreen( articleId: state.pathParameters['articleId']!, ), ), ]; } // APP class AppScreen extends StatefulWidget { static final GlobalKey<_AppScreenState> globalKey = GlobalKey(); AppScreen({required this.beamState}) : super(key: globalKey); final BeamState beamState; @override _AppScreenState createState() => _AppScreenState(); } class _AppScreenState extends State<AppScreen> { final _routerDelegates = [ BeamerRouterDelegate( locationBuilder: (state) => ArticlesLocation(state), ), BeamerRouterDelegate( locationBuilder: (state) => BooksLocation(state), ), ]; late int _currentIndex; void moveTab(int index) { setState(() => _currentIndex = index); _routerDelegates[_currentIndex] .parent ?.updateRouteInformation(_routerDelegates[_currentIndex].currentLocation.state.uri); } @override void initState() { super.initState(); _currentIndex = widget.beamState.uri.path.contains('books') ? 1 : 0; } @override Widget build(BuildContext context) { return Scaffold( body: IndexedStack( index: _currentIndex, children: [ Beamer(routerDelegate: _routerDelegates[0]), Beamer(routerDelegate: _routerDelegates[1]), ], ), bottomNavigationBar: BottomNavigationBar( currentIndex: _currentIndex, items: [ BottomNavigationBarItem(label: 'A', icon: Icon(Icons.article)), BottomNavigationBarItem(label: 'B', icon: Icon(Icons.book)), ], onTap: (index) { moveTab(index); }, ), ); } } class MyApp extends StatefulWidget { @override State<StatefulWidget> createState() => MyAppState(); } class MyAppState extends State<MyApp> { var _routerDelegate = BeamerRouterDelegate( locationBuilder: (state) => AppLocation(state), ); @override Widget build(BuildContext context) { return MaterialApp.router( routeInformationParser: BeamerRouteInformationParser(), routerDelegate: _routerDelegate, ); } } void main() { runApp(MyApp()); }
pubspec.yaml
question discussion helpname: beam_indexed description: A new Flutter project. # The following line prevents the package from being accidentally published to # pub.dev using `pub publish`. This is preferred for private packages. publish_to: 'none' # Remove this line if you wish to publish to pub.dev # The following defines the version and build number for your application. # A version number is three numbers separated by dots, like 1.2.43 # followed by an optional build number separated by a +. # Both the version and the builder number may be overridden in flutter # build by specifying --build-name and --build-number, respectively. # In Android, build-name is used as versionName while build-number used as versionCode. # Read more about Android versioning at https://developer.android.com/studio/publish/versioning # In iOS, build-name is used as CFBundleShortVersionString while build-number used as CFBundleVersion. # Read more about iOS versioning at # https://developer.apple.com/library/archive/documentation/General/Reference/InfoPlistKeyReference/Articles/CoreFoundationKeys.html version: 1.0.0+1 environment: sdk: ">=2.12.0 <3.0.0" dependencies: flutter: sdk: flutter beamer: 0.12.4 # The following adds the Cupertino Icons font to your application. # Use with the CupertinoIcons class for iOS style icons. cupertino_icons: ^1.0.2 dev_dependencies: flutter_test: sdk: flutter # For information on the generic Dart part of this file, see the # following page: https://dart.dev/tools/pub/pubspec # The following section is specific to Flutter. flutter: # The following line ensures that the Material Icons font is # included with your application, so that you can use the icons in # the material Icons class. uses-material-design: true # To add assets to your application, add an assets section, like this: # assets: # - images/a_dot_burr.jpeg # - images/a_dot_ham.jpeg # An image asset can refer to one or more resolution-specific "variants", see # https://flutter.dev/assets-and-images/#resolution-aware. # For details regarding adding assets from package dependencies, see # https://flutter.dev/assets-and-images/#from-packages # To add custom fonts to your application, add a fonts section here, # in this "flutter" section. Each entry in this list should have a # "family" key with the font family name, and a "fonts" key with a # list giving the asset and other descriptors for the font. For # example: # fonts: # - family: Schyler # fonts: # - asset: fonts/Schyler-Regular.ttf # - asset: fonts/Schyler-Italic.ttf # style: italic # - family: Trajan Pro # fonts: # - asset: fonts/TrajanPro.ttf # - asset: fonts/TrajanPro_Bold.ttf # weight: 700 # # For details regarding fonts from package dependencies, # see https://flutter.dev/custom-fonts/#from-packages
-
Url not updating after beaming
It seems like the only way to update the url is to use: https://github.com/slovnicki/beamer/blob/master/example/lib/main.dart#L58
However this isnt navigating to the page it just changes the url so i have to
Beamer.of(context).beamTo(MyLocation());
Under it to go navigate there. Is this a bug? or am i missing something
help example -
May be bug? (bottom navigation with details pages on top)
I slightly modified the
bottom_navigation_multiple_beamers
example to be able to open the screen on top of everything. On theBooks
screen, you can click on the button at the top right.And when you press the button back - everything breaks down. Is this a bug or am I doing something wrong?
Code
import 'package:flutter/material.dart'; import 'package:beamer/beamer.dart'; // DATA const List<Map<String, String>> books = [ { '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', }, ]; const List<Map<String, String>> articles = [ { 'id': '1', 'title': 'Explaining Flutter Nav 2.0 and Beamer', 'author': 'Toby Lewis', }, { 'id': '2', 'title': 'Flutter Navigator 2.0 for mobile dev: 101', 'author': 'Lulupointu', }, { 'id': '3', 'title': 'Flutter: An Easy and Pragmatic Approach to Navigator 2.0', 'author': 'Marco Muccinelli', }, ]; // SCREENS class BooksScreen extends StatelessWidget { @override Widget build(BuildContext context) { return Scaffold( appBar: AppBar( title: Text('Books'), actions: [ IconButton( onPressed: () => Beamer.of( context, root: true, ).beamToNamed('/test'), icon: Icon(Icons.extension), ), ], ), body: ListView( children: books .map( (book) => ListTile( title: Text(book['title']!), subtitle: Text(book['author']!), onTap: () => context.beamToNamed('/books/${book['id']}'), ), ) .toList(), ), ); } } class BookDetailsScreen extends StatelessWidget { const BookDetailsScreen({required this.book}); final Map<String, String> book; @override Widget build(BuildContext context) { return Scaffold( appBar: AppBar( title: Text(book['title']!), ), body: Padding( padding: const EdgeInsets.all(8.0), child: Text('Author: ${book['author']}'), ), ); } } class ArticlesScreen extends StatelessWidget { @override Widget build(BuildContext context) { return Scaffold( appBar: AppBar(title: Text('Articles')), body: ListView( children: articles .map( (article) => ListTile( title: Text(article['title']!), subtitle: Text(article['author']!), onTap: () => context.beamToNamed('/articles/${article['id']}'), ), ) .toList(), ), ); } } class ArticleDetailsScreen extends StatelessWidget { const ArticleDetailsScreen({required this.article}); final Map<String, String> article; @override Widget build(BuildContext context) { return Scaffold( appBar: AppBar( title: Text(article['title']!), ), body: Padding( padding: const EdgeInsets.all(8.0), child: Text('Author: ${article['author']}'), ), ); } } // LOCATIONS class BooksLocation extends BeamLocation<BeamState> { BooksLocation(RouteInformation routeInformation) : super(routeInformation); @override List<String> get pathBlueprints => ['/books/:bookId']; @override List<BeamPage> buildPages(BuildContext context, BeamState state) => [ BeamPage( key: ValueKey('books'), title: 'Books', type: BeamPageType.noTransition, child: BooksScreen(), ), if (state.pathParameters.containsKey('bookId')) BeamPage( key: ValueKey('book-${state.pathParameters['bookId']}'), title: books.firstWhere((book) => book['id'] == state.pathParameters['bookId'])['title'], child: BookDetailsScreen( book: books.firstWhere( (book) => book['id'] == state.pathParameters['bookId']), ), ), ]; } class ArticlesLocation extends BeamLocation<BeamState> { ArticlesLocation(RouteInformation routeInformation) : super(routeInformation); @override List<String> get pathBlueprints => ['/articles/:articleId']; @override List<BeamPage> buildPages(BuildContext context, BeamState state) => [ BeamPage( key: ValueKey('articles'), title: 'Articles', type: BeamPageType.noTransition, child: ArticlesScreen(), ), if (state.pathParameters.containsKey('articleId')) BeamPage( key: ValueKey('articles-${state.pathParameters['articleId']}'), title: articles.firstWhere((article) => article['id'] == state.pathParameters['articleId'])['title'], child: ArticleDetailsScreen( article: articles.firstWhere((article) => article['id'] == state.pathParameters['articleId']), ), ), ]; } // APP class AppScreen extends StatefulWidget { @override _AppScreenState createState() => _AppScreenState(); } class _AppScreenState extends State<AppScreen> { late int currentIndex; final routerDelegates = [ BeamerDelegate( initialPath: '/books', locationBuilder: (routeInformation) { if (routeInformation.location!.contains('books')) { return BooksLocation(routeInformation); } return NotFound(path: routeInformation.location!); }, ), BeamerDelegate( initialPath: '/articles', locationBuilder: (routeInformation) { if (routeInformation.location!.contains('articles')) { return ArticlesLocation(routeInformation); } return NotFound(path: routeInformation.location!); }, ), ]; void _setStateListener() => setState(() {}); @override void didChangeDependencies() { super.didChangeDependencies(); Beamer.of(context).addListener(_setStateListener); } @override Widget build(BuildContext context) { final uriString = Beamer.of(context).configuration.location!; currentIndex = uriString.contains('books') ? 0 : 1; routerDelegates[currentIndex].active = true; routerDelegates[1 - currentIndex].active = false; routerDelegates[currentIndex].update( configuration: RouteInformation(location: uriString), rebuild: false, ); return Scaffold( body: IndexedStack( index: currentIndex, children: [ Beamer( routerDelegate: routerDelegates[0], ), Container( color: Colors.blueAccent, padding: const EdgeInsets.all(32.0), child: Beamer( routerDelegate: routerDelegates[1], ), ), ], ), bottomNavigationBar: BottomNavigationBar( currentIndex: currentIndex, items: [ BottomNavigationBarItem(label: 'Books', icon: Icon(Icons.book)), BottomNavigationBarItem(label: 'Articles', icon: Icon(Icons.article)), ], onTap: (index) { if (index != currentIndex) { routerDelegates[currentIndex].active = false; routerDelegates[1 - currentIndex].active = true; setState(() => currentIndex = index); routerDelegates[currentIndex].update(rebuild: false); } }, ), ); } @override void dispose() { Beamer.of(context).removeListener(_setStateListener); super.dispose(); } } class TestScreen extends StatelessWidget { @override Widget build(BuildContext context) { return Scaffold( appBar: AppBar( title: Text('Test screen'), ), ); } } class MyApp extends StatelessWidget { final routerDelegate = BeamerDelegate( initialPath: '/books', locationBuilder: SimpleLocationBuilder( routes: { '*': (context, state) => AppScreen(), '/test': (context, state) => BeamPage( key: const ValueKey(TestScreen), child: TestScreen(), ), }, ), ); @override Widget build(BuildContext context) { return MaterialApp.router( debugShowCheckedModeBanner: false, routerDelegate: routerDelegate, routeInformationParser: BeamerParser(), backButtonDispatcher: BeamerBackButtonDispatcher( delegate: routerDelegate, ), ); } } void main() => runApp(MyApp());
-
Question: How to implement bottom navigation so I could navigate by deeplink to concrete tab without widget rebuilding
Example: I have two tabs. I have data loader screen on first tab and one nested route atop. So path is something like /tab1/route1. I want to go by deeplink to the second tab + one nested route atop. So path should be /tab2/route2. How can I achieve that without rebuilding whole tree because it leads to data reloading each time I go by deeplink.
-
Beamer (+Bloc) skipping builder override on page transition
Hello!
I'll try and give as much info as I can:
I have a beamer page set up (calling PageA) that has a few subpages, all needing access to a bloc (BlocA.) Most of the rest of the application doesn't need the BlocA access, so I overrode the
builder
method to provide it for this set of pages, like so:class PageALocation extends BeamLocation { @override List<String> get pathBlueprints => [ '/page-a-1', '/page-a-1/one', '/page-a-1/two', ]; @override Widget builder(BuildContext context, Widget navigator) { return BlocProvider<BlocA>( create: (context) { return BlocA(); }, child: navigator, ); }
Everything works fine down PageA, subpages and all. When navigating out of PageA (to PageB, PageC, etc.) that all use an entirely new BeamLocation, that's where my issue comes up.
The new PageBLocation beamer will take over, start to
buildPages
, and the original PageA will re-construct its'build
method, not have access to thebuilder
override, and give the standardProvider.of<BlocA> can't be found in this context
.Does the tree get swapped over before completely destroying the original page? It just seems like on that transitioning frame, the
builder
method gets skipped over. I could be wrong about that.Thanks for reading!
bug help wanted question discussion -
PopToNamed does not work in some case
popToNamed
does not work in some cases, for instance, twoBeamLocation
,[ IndexLocation, AccountLocation ]
withIndexLocation
having a blueprint of[ '/index' ]
and
AccountLocation
has[ '/accounts', accounts/:accountId', '/accounts/onboarding', '/accounts/add', ]
as its blueprint.
Now currently at path
/accounts/onboarding
with its state currently the only item on theBeamStateHistory
. On a skip button pressed, I navigated to a new page as shown here.void onSkipButtonPressed() { Beamer.of(context).beamToNamed( '/accounts', popToNamed: '/index', ); Beamer.of(context).clearBeamStateHistory(); }
with the expectation of removing
/accounts/onboarding
from the stack, with only/accounts
left and navigate to/index
onBeamBack
orBackButtonPressed
on Android. But instead, the App closes.https://user-images.githubusercontent.com/27495055/118402585-715e7c80-b662-11eb-98b1-e2797d2c5dc4.mp4
discussion help -
Drawer close one time but not twice
I my App, I have a Drawer:
Widget build(BuildContext context) { return Scaffold( appBar: AppBar(), body: Container( child: Center( child: Column( mainAxisSize: MainAxisSize.min, children: [ Text('Home'), ], ), ), ), drawer: Drawer( child: _MyDrawer(), ), ); }
Inside _MyDrawer, I have multiple lines of ListTile like this:
ListTile( ... title: Text('Tontine name'), onTap: () { Navigator.of(context).pop(); }, ... );
The first time I tap on a
ListTile
, it closes normally. The second time however it shows me this error:======== Exception caught by gesture =============================================================== The following RangeError was thrown while handling a gesture: RangeError (index): Invalid value: Valid value range is empty: -1 When the exception was thrown, this was the stack: #0 List.[] (dart:core-patch/growable_array.dart:254:60) #1 List.removeLast (dart:core-patch/growable_array.dart:325:20) #2 _defaultOnPopPage (package:beamer/src/beam_page.dart:33:45) #3 BeamerDelegate.build.<anonymous closure>.<anonymous closure> (package:beamer/src/beamer_delegate.dart:582:53) #4 NavigatorState.pop (package:flutter/src/widgets/navigator.dart:5020:28) ... Handler: "onTap" Recognizer: TapGestureRecognizer#3330a debugOwner: GestureDetector state: ready won arena finalPosition: Offset(121.1, 324.0) finalLocalPosition: Offset(121.1, 32.0) button: 1 sent tap down ==============
Please help me find a way through this.
bug help -
[question][web] Do you have any idea how to handle smooth transition of subpages?
Hello,
I am asking here, because you handled this package very well so you might have answer for that (or I will put this question in official flutter issues) :)
When I am using nested routing in web, where only part of the screen is being changed (without scaffold), then I have such situation:
https://user-images.githubusercontent.com/54472480/112757908-f676cf00-8feb-11eb-9253-dc5e11bd21a5.mp4
The situation is a little annoying (because for 1 second I see two pages overlapped), but the worse situation is when I am disabling "mobile" animation of navigation:
https://user-images.githubusercontent.com/54472480/112757917-fc6cb000-8feb-11eb-9c1b-d342e94917bf.mp4
Could be something connected to this plugin or it is rather flutter problem/limitation?
question help -
BeamGuards must support Riverpod 1.0.0-dev.6.
BeamGuards must support Riverpod 1.0.0-dev.6.
class Example extends ConsumerWidget { @override Widget build(BuildContext context, WidgetRef ref) { final count = ref.watch(counterProvider); return Text(count.toString()); } }
Ex:
@override List<BeamGuard> get guards => [ BeamGuard( pathBlueprints: ['/$nLOGIN_PAGE'], check: (BuildContext context, WidgetRef ref, BeamLocation<BeamState> location) => ref.read(authUserNotifierProvider).isGoogleSignIn != true, beamToNamed: '/', ), ];
-
NotFound on hot reload
Describe the bug When I navigate to a specific route, and then hot reload it shows the
NotFound
screen. However before the hot reload it showed the correct screen.Beamer version: 1.5.3
To Reproduce Route
const String kHomeIncentiveDetailConfirmOrderRoute = '/home/incentive/$kIncentiveIdRoute/confirm-order'; // Parameters const String kIncentiveId = 'incentiveId'; const String kIncentiveIdRoute = ':$kIncentiveId';
Location
class TabIncentivesLocation extends BeamLocation<BeamState> { TabIncentivesLocation(RouteInformation routeInformation) : super(routeInformation); @override List<String> get pathPatterns => [ kHomeIncentivesTabRoute, kHomeIncentiveDetailRoute, kHomeIncentiveDetailConfirmOrderRoute, '$kHomeRoute/*', ]; @override List<BeamPage> buildPages(BuildContext context, BeamState state) => [ const BeamPage( key: ValueKey('incentives'), type: BeamPageType.noTransition, child: TabIncentives(), ), if (state.pathParameters.containsKey(kIncentiveId) && !state.uri.pathSegments.contains('confirm-order')) BeamPage( key: ValueKey( 'incentive/${state.pathParameters[kIncentiveId]}/detail'), type: BeamPageType.material, child: TabIncentivesDetail( id: int.parse(state.pathParameters[kIncentiveId]!), ), ), if (state.pathParameters.containsKey(kIncentiveId) && state.uri.pathSegments.contains('confirm-order')) BeamPage( key: ValueKey( 'incentive/${state.pathParameters[kIncentiveId]}/confirm-order'), type: BeamPageType.material, child: TabIncentivesConfirmOrder( id: int.parse(state.pathParameters[kIncentiveId]!), ), ), ]; }
Navigating:
Beamer.of(context).beamToNamed( kHomeIncentiveDetailConfirmOrderRoute.replaceAll( kIncentiveIdRoute, '${incentive.id}', ), data: incentive, );
Expected behavior Hot reloading does not result in
NotFound
.Additional context This does not happen on every route, but this one is consistent. What am I doing wrong?
-
[Q] BeamLocation - BeamerDelegate - RoutesLocationBuilder - routes
Hello, some one can help me? I can't find my answer in the documentation or in github issues.
I will try to explain my problem: I have a BeamerDelegate with RoutesLocationBuilder like this (3 routes):
BeamerDelegate( locationBuilder: RoutesLocationBuilder( routes: { '/folders': (context, state, data) => const Screen1(), '/folders/:folderID': (context, state, data) => const Screen2(folderID: state.pathParameters['folderID']), '/folders/images/:imageID': (context, state, data) => const Screen3(imageID: state.pathParameters['imageID']), } ) );
When i go on:
/folders
=> screen1 OK/folders/3
=> screen2 with folderID 3 OK/folders/images/2
=> go on screen2 with folderID null NOT OKI try to invert /folder/:folderID and /folders/images/:imageID but same result.
I have also a pathPatterns in a BeamLocation with
[ '/home', '/folders', '/folders/:folderID', '/folders/images/:imageID', ]
If you need more example or code, tell me!
Thank you
-
[Discussion] Beamer Flutter's Favorite 👏👏🎉🎉
Good job @slovnicki thanks to your work Beamer in now Flutter Favorite 👏🎉
I have not seen any news about this event, but it seems Beamer has been added to Flutter's favorite recently.
So now, for navigation 2.0, go_router and beamer are the only two Flutter favorite packages ?
-
Beamer seems to drop/ignore hash or pound signs in urls
Describe the bug A clear and concise description of what the bug is.
Beamer version: 1.5.3
To Reproduce Steps to reproduce the behavior:
- Use beamer
- Navigate to a url such as /home/page#section
- Observe that the BeamState passed to your buildPages method contains no information about the "#section" portion of the url
Expected behavior Include this url information so that the app can act on it
Desktop (please complete the following information):
- OS: macOS
- Version 13.0
Smartphone (please complete the following information):
- Device: Emulator
- OS: iOS, Android
Additional context This is being used in the context of a deep link. I click an email verification link that directs to a hosted server. The server returns a redirect url which deep links into the app. This url includes a hash sign with critical data after it. I do not have control over the way the server generates the redirect so I cannot remove this symbol.
-
How to beam to tab in BottomNavigationBar?
Hello,
I would like to beam to a specific tab, but for some reason I can't get it to work. This does not do anything:
Beamer.of(context) .beamToNamed(kHomeRecognitionTabRoute);
It only logs in the main delegate asroutes: /home/recognition
kHomeRoute
:/home
kHomeRecognitionTabRoute
:/home/recognition
This is my main delegate
final routerDelegate = BeamerDelegate( initialPath: kSplashRoute, routeListener: (RouteInformation routeInformation, BeamerDelegate delegate) { Logger().info( routeInformation.location ?? '', category: 'routes', ); }, transitionDelegate: const NoAnimationTransitionDelegate(), locationBuilder: (routeInformation, _) { if (routeInformation.location == null) { return NotFound(); } switch (routeInformation.location) { case kSplashRoute: return SplashLocation(routeInformation); case kLoginDomainRoute: return LoginLocation(routeInformation); } if (routeInformation.location!.startsWith(kHomeRoute)) { return HomeLocation(routeInformation); } return NotFound(); }, );
HomeLocation
class HomeLocation extends BeamLocation<BeamState> { HomeLocation(RouteInformation routeInformation) : super(routeInformation); @override List<String> get pathPatterns => [ kHomeRoute, ]; @override List<BeamPage> buildPages(BuildContext context, BeamState state) => [ const BeamPage( key: ValueKey('home'), title: 'Home', type: BeamPageType.scaleTransition, child: HomeScreen(), ) ]; }
HomeScreen
final beamerHomeKey = GlobalKey<BeamerState>(); final routerDelegate = BeamerDelegate( locationBuilder: RoutesLocationBuilder( routes: { kHomeRoute: (context, state, data) => const HomeTabs(), kHomeNotificationsRoute: (context, state, data) => const BeamPage( child: NotificationsScreen(), type: BeamPageType.slideRightTransition, ), }, ), ); class HomeScreen extends ConsumerStatefulWidget { const HomeScreen({super.key}); @override HomeScreenState createState() => HomeScreenState(); } class HomeScreenState extends ConsumerState<HomeScreen> { @override Widget build(BuildContext context) { return Scaffold( appBar: const CustomAppBar(), body: Beamer( key: beamerHomeKey, routerDelegate: routerDelegate, ), ); } }
HomeTabs
final _routerDelegates = [ BeamerDelegate( initialPath: kHomeHomeTabRoute, routeListener: (RouteInformation routeInformation, BeamerDelegate delegate) { Logger().info( routeInformation.location ?? '', category: 'routes_home_tabs', ); }, locationBuilder: (routeInformation, _) { return TabHomeLocation(routeInformation); }, ), BeamerDelegate( initialPath: kHomeRecognitionTabRoute, locationBuilder: (routeInformation, _) { return TabRecognitionLocation(routeInformation); }, ), BeamerDelegate( initialPath: kHomeIncentivesTabRoute, locationBuilder: (routeInformation, _) { return TabIncentivesLocation(routeInformation); }, ), BeamerDelegate( initialPath: kHomeMoreTabRoute, locationBuilder: (routeInformation, _) { return TabMoreLocation(routeInformation); }, ), ]; class HomeTabs extends ConsumerStatefulWidget { const HomeTabs({Key? key}) : super(key: key); @override HomeState createState() => HomeState(); } class HomeState extends ConsumerState<HomeTabs> { int currentIndex = 0; @override Widget build(BuildContext context) { var tr = ref.watch(localizationProvider).translations; return Scaffold( body: IndexedStack( index: currentIndex, children: [ Beamer( routerDelegate: _routerDelegates[0], ), Beamer( routerDelegate: _routerDelegates[1], ), Beamer( routerDelegate: _routerDelegates[2], ), Beamer( routerDelegate: _routerDelegates[3], ), ], ), bottomNavigationBar: BottomNavigationBar( currentIndex: currentIndex, items: [ BottomNavigationBarItem( label: tr['global.home'], icon: const Icon( Icons.home, ), ), BottomNavigationBarItem( label: tr['global.recognitions'], icon: const Icon( Icons.handshake, ), ), BottomNavigationBarItem( label: tr['global.gifts'], icon: const Icon( Icons.card_giftcard, ), ), BottomNavigationBarItem( label: tr['global.more'], icon: const Icon( Icons.person, ), ), ], onTap: (index) { if (index != currentIndex) { setState(() => currentIndex = index); _routerDelegates[currentIndex].update(rebuild: false); } }, ), ); } }
What am I doing wrong so I can navigate to the tab from anywhere?
Thanks!
-
Losing page state when navigating to another
Description When navigating from page 1 to page 2, everything goes perfectly. When navigating from page 2 to page 3, everything goes perfectly.
When going back from page 3 to page 2, page 2 loses state, no transition occurs.
The only solution I found was to use the '/page2/page3/' route instead of '/page3'. But it is not a good solution for my case.
Minimum reproduction:
import 'package:beamer/beamer.dart'; import 'package:flutter/material.dart'; void main() { runApp(MyApp()); } class MyApp extends StatelessWidget { MyApp({super.key}); final routerDelegate = BeamerDelegate( locationBuilder: RoutesLocationBuilder( routes: { '/': (context, state, data) { return const BeamPage( key: ValueKey('page1-key'), child: Page1(), ); }, '/page2': (context, state, data) { return const BeamPage( key: ValueKey('page2-key'), child: Material( child: Page2(), ), ); }, '/page3': (context, state, data) { return const BeamPage( key: ValueKey('page3-key'), child: Material( child: Page3(), ), ); }, }, ), ); @override Widget build(BuildContext context) { return MaterialApp.router( title: 'Flutter Demo', theme: ThemeData( primarySwatch: Colors.blue, ), routerDelegate: routerDelegate, routeInformationParser: BeamerParser(), backButtonDispatcher: BeamerBackButtonDispatcher( delegate: routerDelegate, alwaysBeamBack: true, fallbackToBeamBack: true, ), ); } } class Page1 extends StatelessWidget { const Page1({super.key}); @override Widget build(BuildContext context) { return Scaffold( appBar: AppBar(), body: InkWell( onTap: () { Beamer.of(context).beamToNamed('/page2'); }, child: const Text('Page 1'), ), ); } } class Page2 extends StatelessWidget { const Page2({super.key}); @override Widget build(BuildContext context) { return Scaffold( appBar: AppBar(), body: SingleChildScrollView( child: Column( children: [ const SizedBox(height: 100), InkWell( onTap: () { Beamer.of(context).beamToNamed('/page3', beamBackOnPop: true); }, child: const Text('Page 2'), ), const SizedBox(height: 1000), ], ), ), ); } } class Page3 extends StatelessWidget { const Page3({super.key}); @override Widget build(BuildContext context) { return Scaffold( appBar: AppBar(), body: const Text('Page 3'), ); } }
Can someone help me? Thank you very much!
Releases(v1.5.2)
-
v1.5.2(Aug 16, 2022)
-
v1.5.1(Aug 16, 2022)
- Fixed: Lagging
BeamGuard
check when usingBeamerLocationBuilder
(#532)
Source code(zip)
- Fixed: Lagging
-
v1.5.0(Jun 11, 2022)
- Added:
onUpdate
method toBeamLocation
which is called afterinitState
and on everyupdate
. (#507, #88c8537). See this example that demonstrates its possible usage. - Added:
isEqualTo
extension method toRouteInformation
(intentionally not overriding the equality operator) - Fixed: Possible wrong URL after global rebuild with multiple child Beamers (#523)
- Fixed: Consistency of calls to
BeamLocation.updateState
(#484) - Fixed: Resetting
popConfiguration
afterpopToNamed
parameter has been used in abeamToNamed
call (#521)
Documentation
- Added explanation of some specific parameters at Nested Navigation section in README (#514)
Examples
- Improved books_blos example with usage of new
BeamLocation.onUpdate
Ecosystem
- Added a beam_location brick
- Various improvements on website (devj3ns)
Source code(zip)
- Added:
-
v1.4.1+1(Apr 13, 2022)
- Fixed: formatting of
beamer.dart
file
Source code(zip)
- Fixed: formatting of
-
v1.4.1(Apr 11, 2022)
CHANGELOG
- Fixed: Deeper routes matching with asterisk (cgaisl, #494, #502)
- Fixed: Nested navigation crash with guards (svsk417, #490)
- Fixed: Breaking out of
popToNamed
loop (Goddchen, #500)
Documentation
- Added website app; a starting point for extensive, self-explanatory Beamer documentation at beamer.dev
- Fixed typos and added doc comment for accessing root Beamer (gazialankus)
Source code(zip)
-
v1.4.0+1(Feb 26, 2022)
CHANGELOG
Documentation
- Various README improvements and tweaks
Source code(zip)
-
v1.4.0(Feb 24, 2022)
CHANGELOG
- Added: Relative beaming, i.e. being able to call
beamToNamed('path')
instead ofbeamToNamed('/my/path')
if we're already at/my
- Added: New
BeamPageType
s;slideRightTransition
,slideLeftTransition
andslideTopTransition
(Shiba-Kar, #477) - Improved: Interactions between nested and sibling Beamers by having children automatically take priority and more carefully handle locally not found cases
- Fixed: Unnecessary update of
ChangeNotifier
custom state (jmysliv, #475) - Fixed:: Target
BeamLocation
initialization duringbeamBack
- Fixed: Insufficiently detailed automatic
BeamPage.key
in someRoutesLocationBuilder.routes
usage of*
Examples
- Added a new example: multiple_beamers
- Improved and simplified bottom_navigation_multiple_beamers example
- Fixed updating Beamer in authentication_riverpod example
Source code(zip)
- Added: Relative beaming, i.e. being able to call
-
v1.3.0(Feb 6, 2022)
CHANGELOG
- Added:
strictPathPatterns
toBeamLocation
which will do only exact matches of URI and path pattern (5810c9c) - Added:
updateListenable
toBeamerDelegate
which will triggerupdate
when it notifies (f0ccfd7) - Added: Support for replacing the browser history entry when using
beamToReplacement*
or specifyingreplaceRouteInformation: true
- Added: Support for setting
opaque
property onBeamPage
(ggirotto, #446) - Added:
initializeFromParent
property toBeamerDelegate
to overcome the limitation ofupdateFromParent
and give finer control over parent interaction (340b474) - Fixed: Browser back/forward buttons behavior, i.e. browser history
- Fixed: Initialization of target
BeamLocation
when beaming back (#463) - Updated: Return values for
beamBack
andpopBeamLocation
context extensions (marcguilera, #461, 1045ab3) - Updated: The entire guarding flow (thanks mat100payette for important feedback and contribution)
Examples
- Fixed some edge cases in advanced_books example (jpangburn, #451)
- Updated authentication_bloc example guard usage (df194e8)
Source code(zip)
- Added:
-
v1.2.0(Jan 5, 2022)
CHANGELOG
- Fixed: Using the
initialPath
instead of parent's path on nestedBeamerDelegate
during initialization from parent when theupdateFromParent
is set tofalse
(samdogg7)
Documentation:
- Added a section about Page Keys to README (Goddchen)
- Added a sentence about browser's back button to README
- Fixed and improved grammar in doc comments (ggirotto)
Examples:
- Fixed analyzer warnings (Goddchen)
- Updated authentication_bloc example to bloc v8 (Lorenzohidalgo)
Source code(zip)
- Fixed: Using the
-
v1.1.0(Dec 18, 2021)
CHANGELOG
Most of this release is matuella's directly and indirectly contributions. Many thanks!
- Add: a link to Medium article for "Migrating" section in README
- Add: lint rules
prefer_single_quotes
,always_use_package_imports
,omit_local_variable_types
,prefer_final_locals
andcomment_references
. - Fix: disposing histories on
beamBack
(#417) - Fix: updating history when setting
state
manually (#420) - Deprecate: unused
BeamerDelegate.preferUpdate
- Improve: tests setup (Thanks cedvdb)
Source code(zip)
-
v1.0.0(Dec 3, 2021)
CHANGELOG
- BREAKING: "top-level state", the one in
BeamerDelegate
is nowRouteInformation
instead ofBeamState
BeamerDelegate.state
doesn't exist anymore and is replaced withBeamerDelegate.configuration
which isRouteInformation
and notBeamState
locationBuilder
now works withRouteInformation
andBeamConfiguration
instead ofBeamState
super()
constructor onBeamLocation
now takes optionalRouteInformation
instead ofBeamState
- in order to continue using custom
BeamLocation
s withBeamState
state, generic type has to be specified;class MyBeamLocation extends BeamLocation<BeamState>
- BREAKING:
pathBlueprints
is renamed topathPatterns
BeamLocation.pathPatterns
isList<Pattern>
BeamState.pathBlueprintSegments
renamed toBeamState.pathPatternSegments
BeamGuard.pathBlueprints
renamed toBeamGuard.pathPatterns
- BREAKING:
SimpleLocationBuilder
is renamed toRoutesLocationBuilder
- also the
SimpleBeamLocation
is renamed toRoutesBeamLocation
routes
values now additionally receivedata
- also the
- BREAKING:
beamStateHistory
andbeamLocationHistory
have been replaced withbeamingHistory
that is aList<BeamLocation>
and eachBeamLocation
hashistory
that isList<HistoryElement>
whereHistoryElement
holdsRouteInformation
andBeamParameters
. - BREAKING:
BeamerDelegate.listener
has been renamed toBeamerDelegate.routeListener
- BREAKING: The property
pageRouteBuilder
inBeamPage
is replaced with a new propertyrouteBuilder
which works with anyRouteBuilder
not justPageRouteBuilder
. - BREAKING:
BeamGuard
beamTo
receives the origin and targetBeamLocation
s alongsideBuildContext
. - BREAKING:
BeamGuard
beamToNamed
is now a function that receives the origin and targetBeamLocation
s and returns aString
. - Add: guard_riverpod example
- Add: firebase_core example
- Add: firebase_auth example
- Add: change_notifier_custom_state example
- Add:
BeamerBackButtonDispatcher.alwaysBeamBack
to make Android back button always go to previous route, even if it canpop
. - Add:
BeamPage.routePop
that can be used foronPopPage
instead of defaultpathsegmentPop
- Add:
BeamerDelegate.buildListener
, which is called after the pages are built. - Add:
fullScreenDialog
property toBeamPage
- Add: flutter_lints
- Add: a presentation resource about beamer
- Fix: clearing history range when using
popToNamed
- Fix: passing
BeamPage.title
toCupertinoPageRoute
s
Source code(zip)
- BREAKING: "top-level state", the one in
-
v0.14.1(Jun 21, 2021)
CHANGELOG:
- Add:
updateParent
(defaulttrue
) toBeamerDelegate
- Fix: ignoring query on initial path
Source code(zip)
- Add:
-
v0.14.0(Jun 8, 2021)
CHANGELOG:
- BREAKING:
routes
inSimpleLocationBuilder
now also bring thestate
- Add: support for
RegExp
inpathBlueprints
(in bothBeamGuard
s andBeamLocation
s) - Add: updating nested
Beamer
s on parent navigation - Add:
onBack
toBeamerbackButtonDispatcher
- Add:
navigator
getter toBeamerDelegate
- Add:
beamStateHistory
andBeamLocationHistory
tocontext
extension methods - Add:
data
parameter tobeamBack
- Change:
BeamPage.pageRouteBuilder
return type to be a superclass - Fix: removing last
BeamState
from history on defaultpop
- Fix:
BeamGuard
behavior on query parameters - Fix: ignoring trailing
/
in URIs - Fix: keeping
data
as long as possible when beaming - Improved Android back button behavior
- Improved bottom navigation examples
- Improved README
Source code(zip)
- BREAKING:
-
v0.13.3(May 22, 2021)
CHANGELOG:
- Add: authentication_riverpod example
- Add: "Tips and Common Issues" to README
- Fix: Drawer pop
- Fix:
beamBackOnPop:true
while beaming - Make
beamStateHistory
andbeamLocationHistory
public
Source code(zip)
-
v0.13.2(May 18, 2021)
CHANGELOG:
- Add:
BeamerDelegate.notFoundRedirectNamed
- Add: public static
BeamPage.defaultOnPopPage
- Fix: top-level guard updating URL
- Improved guards example
- Improved README Quick Start
Source code(zip)
- Add:
-
v0.13.1(May 17, 2021)
CHANGELOG:
- Fix: correctly updating delegate after applying guards
Source code(zip)
-
v0.13.0(May 16, 2021)
CHANGELOG:
- BREAKING: renamed
BeamerRouterDelegate
toBeamerDelegate
- BREAKING: renamed
BeamerRouteInformationParser
toBeamerParser
- BREAKING: renamed
pagesBuilder
tobuildPages
- BREAKING: renamed
Beamer.of(context).currentLocation
toBeamer.of(context).currentBeamLocation
- Add:
BeamPage.popToNamed
andBeamPage.onPopPage
for fine control of popping - Add:
BeamPage.title
for setting the browser tab title - Add:
SimpleLocationBuilder
can now mixBeamPage
s andWidget
s - Add:
BeamerParser.onParse
for intercepting the parsed state on app load - Add: encoding the
data
into browser history - Add: blocking capability to
BeamGuard
s - Add: slide and scale transitions to
BeamPageType
- Add: optional
transitionDelegate
for beaming - Fix: cascade guarding
- Fix: delegate listener not being called always
- All examples improved and migrated to null-safety
- Improved documentation
Source code(zip)
- BREAKING: renamed
-
v0.12.4(Apr 27, 2021)
CHANGELOG:
- Add
Beamer.setPathUrlStrategy()
for removing#
from URL - Add persistent auth state on browser refresh in authentication_bloc example
- Fix detection of
NotFound
when usingSimpleLocationBuilder
- Fix updating route on guard actions
- Fix not pushing
BeamState
in history if it's on top - Fix taking
currentLocation
on setting initial path
Source code(zip)
- Add
-
v0.12.3(Apr 24, 2021)
CHANGELOG
- Add authentication_bloc example
- Fix
SimpleBeamLocation
ignoring query - Fix updating delegate state on location state change
Source code(zip)
-
v0.12.2(Apr 24, 2021)
CHANGELOG:
- Add
listener
attribute toBeamerRouterDelegate
- Add
root
attribute toBeamerRouterDelegate
and{bool root = false}
attribute toBeamer.of
- Add
canHandle(Uri uri)
method toBeamLocation
- Fix Updating parent on nested navigation
- Fix README typos
Source code(zip)
- Add
-
v0.12.1(Apr 19, 2021)
CHANGELOG:
- Fix updating browser history
Source code(zip)
-
v0.12.0(Apr 19, 2021)
CHANGELOG:
- BREAKING: There's no
RootRouterDelegate
any more. Just rename it toBeamerRouterDelegate
. If you were using itshomeBuilder
, useSimpleLocationBuilder
and thenroutes: {'/': (context) => HomeScreen()}
- BREAKING: Behavior of
beamBack()
was changed to go to previousBeamState
, notBeamLocation
. If this is not what you want, usepopBeamLocation()
that has the same behavior as oldbeamback()
- Fix: Important bug while using multiple sibling
Beamer
s
Source code(zip)
- BREAKING: There's no
-
v0.9.1(Mar 10, 2021)
CHANGELOG
- Add removing duplicates in
beamHistory
+BeamerRouterDelegate.removeDuplicateHistory
- Add implicit updates of current location +
BeamerRouterDelegate.preferUpdate
- Add more Beamer extensions to
BuildContext
- Remove the need for
back_button_interceptor
package (not that it's not good, but we realized it can be implemented more naturally) - Fix removing the last empty path segment when it's the only one
Source code(zip)
- Add removing duplicates in
-
v0.8.1(Mar 4, 2021)
- BREAKING:
BeamLocation.pages
is nowBeamLocation.pagesBuilder
- BREAKING:
BeamerRouterDelegate
now takesbeamLocations
andBeamerRouteInformationParser
nothing - NEW FEATURE:
beamToNamed
- NEW FEATURE:
canBeamBack
andbeamBackLocation
helpers - NEW FEATURE:
BeamGuard.onCheckFailed
- NEW FEATURE:
stacked
parameter for beaming - Add: back_button_interceptor package automatic
beamBack
on Android back button - Add more details to README: Key Concepts
- Add invite to Discord community for beamer help/discussion/chat
Source code(zip)
- BREAKING:
-
v0.7.0(Feb 24, 2021)
- BREAKING:
BeamerRouterDelegate.notFoundPage
is nowBeamPage
instead ofWidget
- BREAKING:
BeamGuard.showPage
is nowBeamPage
instead ofWidget
- NEW FEATURE:
beamBack
now goes back throughbeamHistory
- NEW FEATURE:
beamTo
can take an optionalbeamBackOnPop
boolean - NEW FEATURE:
BeamLocation.builder
can be used to provide something to the entire location - NEW EXAMPLE: location_builder
- NEW EXAMPLE: animated_rail
- tweaks and improvements to the documentation
Source code(zip)
- BREAKING:
-
v0.6.0(Feb 7, 2021)
- NEW FEATURE: Guards
- NEW FEATURE: Beamer as a Widget (see Bottom Navigation example)
- Add examples/ for every gif in README
- Add state to Beamer
Source code(zip)
-
v0.5.0(Jan 31, 2021)
- BREAKING:
*App.router
constructor needs to be used - BREAKING:
String pathBlueprint
is nowList<String> pathBlueprints
- BREAKING:
BeamLocation.withParameters
constructor is removed and all parameters are handled with 1 constructor. See example if you needsuper
. - BREAKING:
BeamPage
'spage
renamed tochild
- NEW FEATURE:
BeamLocation
can support multiple and arbitrary long path blueprints - NEW FEATURE:
notFoundPage
- Add more complex books example
- Add more doc comments
- Remove the need for
routerDelegate
to take locations
Source code(zip)
- BREAKING:
Owner
Sandro Lovnički
Fluro is a Flutter routing library that adds flexible routing options like wildcards, named parameters and clear route definitions.
English | Português The brightest, hippest, coolest router for Flutter. Features Simple route navigation Function handlers (map to a function instead
Flutter file based routing - File based routing and nested layouts for Flutter
Flutter File Based Routing I was inspired by the routing in remix.run with neste
I created a welcome page, login page and signup page using Flutter
welcome_page UI design for welcome page, signUp page & Login page by Joy Obor Getting Started This project is a starting point for a Flutter applicati
This is a flutter app which uses the Bitrise Api(https://api-docs.bitrise.io/) to show the bitrise projects and builds and lets you download your artifacts.
Bitrise Artifact Downloader Introduction ??♂️ This is a flutter app which uses the Bitrise Api(https://api-docs.bitrise.io/) to show the bitrise proj
Memebaaz is a video/images sharing app, Anyone can share short videos and images through app, the media will go through admin's approval.
MemeBaaz - Memes & Short Videos App Memebaaz is a Video/images Sharing App, Anyone can share short videos and images through app, the media will go th
A very easy-to-use navigation tool/widget for having iOS 13 style stacks.
cupertino_stackview A very easy-to-use navigation tool/widget for having iOS 13 style stacks. It is highly recommended to read the documentation and r
Valorant Guide app: a small demo application to demonstrate Flutter application tech-stacks
Valorant Guide Design Design by: Malik Abimanyu App Valorant Guide app is a smal
Counter provider - App para practicar el disenio de layouts con widgets mas comunes containers, stacks, row/columns, text fields
App para practicar el disenio de layouts con widgets mas comunes containers, sta
An extension to the bloc state management library which lets you create State Machine using a declarative API
An extension to the bloc state management library which lets you create State Machine using a declarative API
Git+ is your ultimate GitLab mobile app that lets you interact with your projects like as if you were using desktop.
Git+ for GitLab Git+ is your ultimate GitLab mobile app that lets you interact with your projects like as if you were using desktop. Git+ lets you see
A CLI tool and Dart package that can scrape file and directory URLs from h5ai instances.
h5ai scraper A CLI tool and Dart package that can scrape file and directory URLs from h5ai instances. Usage This tool requires the Dart SDK. It can be
A lightweight flutter package to linkify texts containing urls, emails and hashtags
linkfy_text A lightweight flutter package to linkify texts containing urls, emails and hashtags. Usage To use this package, add linkfy_text as a depen
Package provides light widgets [for Linkify, Clean] and extensions for strings that contain bad words/URLs/links/emails/phone numbers
Package provides light widgets [for Linkify, Clean] and extensions for strings that contain bad words/URLs/links/emails/phone numbers
Android App written with Flutter/Dart to navigate medium.com without limitations.
Medium Unlimited An Android application written with Flutter/Dart to read medium.com without limitations. Features Read medium without reading limits
Chance Dart is a free Open Source project that lets you create random strings, integers, and other things to help with tiresome tasks, especially when building automated tests or wherever else you require anything random.
Chance Dart Random generator helper for Dart Homepage • Documentation Overview Chance Dart is a free Open Source project that lets you create random s
Flutter getx template - A Flutter Template using GetX package for State management, routing and Dependency Injection
Flutter GetX Template (GetX, Dio, MVVM) This Flutter Template using GetX package
A Video and Audio player that can play from local assets, local files and network URLs with the powerful controls
Video/Audio Player in Flutter with Powerful controls How can we play videos in Flutter? There is a library directly from the Flutter team simply calle
Low-level link (text, URLs, emails) parsing library in Dart
linkify Low-level link (text, URLs, emails) parsing library in Dart. Required Dart >=2.12 (has null-safety support). Flutter library. Pub - API Docs -
Easy to use text widget for Flutter apps, which converts inlined urls into working, clickable links
LinkText Easy to use text widget for Flutter apps, which converts inlined URLs into clickable links. Allows custom styling. Usage LinkText widget does