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

favorite

pub Awesome Flutter

GitHub commit activity GitHub Repo stars GitHub forks

GitHub closed issues GitHub closed pull requests

GitHub contributors Discord

Buy Me A Coffee

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:

Comments
  • Navigation notification

    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 
    opened by Maleandr 20
  • [enhancement] restructure BeamerRouterDelegate

    [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 you prepare() 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 a PatternLocationBuilder so I can use Pattern 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 to prepare() createState etc...

    for example I would wrap LocationBuilder within BeamerRouterDelegate with another function called LocationInitializer (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 call createState!(Uri.parse(somePath)) and the same with prepare() 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 times

    Another 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 a typedef NavigatorBuilder = Widget Function(BuildContext context, Key key, pages, observers,... etc) so users can have more fine control, you will pass all parameters from build function it just gives users more flexibility in the long run. And maybe try to avoid using Builder widget as its limiting sometimes when you need to force minimum size, at least I had some problems with it

    UPDATE: https://github.com/slovnicki/beamer/blob/39f96295b20a23bba4a46818d576e608399dd8b4/package/lib/src/beamer_router_delegate.dart#L378 another good example, the need to remember to call notifyListeners() every time.. just have a handlePop function inside BeamLocation that does the magic... and I WOULD STRONGLY SUGGEST to make state read only from outside BeamLocation... that way you'll avoid copying code to many places or remember to do something after before etc...

    refactor proposal discussion 
    opened by shovelmn12 19
  • [Question] BottomNav and IndexedStack, how to move to a specific plage and browser back button  ?

    [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

    name: 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
    
    question discussion help 
    opened by dleurs 18
  • Url not updating after beaming

    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 
    opened by jeremiahlukus 18
  • May be bug? (bottom navigation with details pages on top)

    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 the Books 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());
    
    
    discussion 
    opened by nikitadol 17
  • Question: How to implement bottom navigation so I could navigate by deeplink to concrete tab without widget rebuilding

    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.

    opened by IlyaMax 16
  • Beamer (+Bloc) skipping builder override on page transition

    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 the builder override, and give the standard Provider.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 
    opened by BirdSamuel 15
  • PopToNamed does not work in some case

    PopToNamed does not work in some case

    popToNamed does not work in some cases, for instance, two BeamLocation, [ IndexLocation, AccountLocation ] with IndexLocation 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 the BeamStateHistory. 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 on BeamBack or BackButtonPressed on Android. But instead, the App closes.

    https://user-images.githubusercontent.com/27495055/118402585-715e7c80-b662-11eb-98b1-e2797d2c5dc4.mp4

    discussion help 
    opened by natintosh 12
  • Drawer close one time but not twice

    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 
    opened by stephane-segning 11
  • [question][web] Do you have any idea how to handle smooth transition of subpages?

    [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 
    opened by mordivgor 11
  • BeamGuards must support Riverpod 1.0.0-dev.6.

    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: '/',
            ),
          ];
    
    opened by Patrick386 10
  • NotFound on hot reload

    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?

    opened by vixez 0
  • [Q] BeamLocation - BeamerDelegate - RoutesLocationBuilder - routes

    [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 OK

    I 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

    opened by julienmiclo 0
  • [Discussion] Beamer Flutter's Favorite 👏👏🎉🎉

    [Discussion] Beamer Flutter's Favorite 👏👏🎉🎉

    Good job @slovnicki thanks to your work Beamer in now Flutter Favorite 👏🎉

    image

    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 ?

    opened by dleurs 1
  • Beamer seems to drop/ignore hash or pound signs in urls

    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:

    1. Use beamer
    2. Navigate to a url such as /home/page#section
    3. 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.

    opened by point-source 1
  • How to beam to tab in BottomNavigationBar?

    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 as routes: /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!

    opened by vixez 0
  • Losing page state when navigating to another

    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!

    opened by GlaucoMendes 0
Releases(v1.5.2)
  • v1.5.2(Aug 16, 2022)

  • v1.5.1(Aug 16, 2022)

  • v1.5.0(Jun 11, 2022)

    • Added: onUpdate method to BeamLocation which is called after initState and on every update. (#507, #88c8537). See this example that demonstrates its possible usage.
    • Added: isEqualTo extension method to RouteInformation (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 after popToNamed parameter has been used in a beamToNamed call (#521)

    Documentation

    Examples

    Ecosystem

    Source code(tar.gz)
    Source code(zip)
  • v1.4.1+1(Apr 13, 2022)

  • v1.4.1(Apr 11, 2022)

  • v1.4.0+1(Feb 26, 2022)

  • v1.4.0(Feb 24, 2022)

    CHANGELOG

    • Added: Relative beaming, i.e. being able to call beamToNamed('path') instead of beamToNamed('/my/path') if we're already at /my
    • Added: New BeamPageTypes; slideRightTransition, slideLeftTransition and slideTopTransition (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 during beamBack
    • Fixed: Insufficiently detailed automatic BeamPage.key in some RoutesLocationBuilder.routes usage of *

    Examples

    Source code(tar.gz)
    Source code(zip)
  • v1.3.0(Feb 6, 2022)

    CHANGELOG

    • Added: strictPathPatterns to BeamLocation which will do only exact matches of URI and path pattern (5810c9c)
    • Added: updateListenable to BeamerDelegate which will trigger update when it notifies (f0ccfd7)
    • Added: Support for replacing the browser history entry when using beamToReplacement* or specifying replaceRouteInformation: true
    • Added: Support for setting opaque property on BeamPage (ggirotto, #446)
    • Added: initializeFromParent property to BeamerDelegate to overcome the limitation of updateFromParent 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 and popBeamLocation context extensions (marcguilera, #461, 1045ab3)
    • Updated: The entire guarding flow (thanks mat100payette for important feedback and contribution)

    Examples

    Source code(tar.gz)
    Source code(zip)
  • v1.2.0(Jan 5, 2022)

  • 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 and comment_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(tar.gz)
    Source code(zip)
  • v1.0.0(Dec 3, 2021)

    CHANGELOG

    • BREAKING: "top-level state", the one in BeamerDelegate is now RouteInformation instead of BeamState
      • BeamerDelegate.state doesn't exist anymore and is replaced with BeamerDelegate.configuration which is RouteInformation and not BeamState
      • locationBuilder now works with RouteInformation and BeamConfiguration instead of BeamState
      • super() constructor on BeamLocation now takes optional RouteInformation instead of BeamState
      • in order to continue using custom BeamLocations with BeamState state, generic type has to be specified; class MyBeamLocation extends BeamLocation<BeamState>
    • BREAKING: pathBlueprints is renamed to pathPatterns
      • BeamLocation.pathPatterns is List<Pattern>
      • BeamState.pathBlueprintSegments renamed to BeamState.pathPatternSegments
      • BeamGuard.pathBlueprints renamed to BeamGuard.pathPatterns
    • BREAKING: SimpleLocationBuilder is renamed to RoutesLocationBuilder
      • also the SimpleBeamLocation is renamed to RoutesBeamLocation
      • routes values now additionally receive data
    • BREAKING: beamStateHistory and beamLocationHistory have been replaced with beamingHistory that is a List<BeamLocation> and each BeamLocation has history that is List<HistoryElement> where HistoryElement holds RouteInformation and BeamParameters.
    • BREAKING: BeamerDelegate.listener has been renamed to BeamerDelegate.routeListener
    • BREAKING: The property pageRouteBuilder in BeamPage is replaced with a new property routeBuilder which works with any RouteBuilder not just PageRouteBuilder.
    • BREAKING: BeamGuard beamTo receives the origin and target BeamLocations alongside BuildContext.
    • BREAKING: BeamGuard beamToNamed is now a function that receives the origin and target BeamLocations and returns a String.
    • 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 can pop.
    • Add: BeamPage.routePop that can be used for onPopPage instead of default pathsegmentPop
    • Add: BeamerDelegate.buildListener, which is called after the pages are built.
    • Add: fullScreenDialog property to BeamPage
    • Add: flutter_lints
    • Add: a presentation resource about beamer
    • Fix: clearing history range when using popToNamed
    • Fix: passing BeamPage.title to CupertinoPageRoutes
    Source code(tar.gz)
    Source code(zip)
  • v0.14.1(Jun 21, 2021)

  • v0.14.0(Jun 8, 2021)

    CHANGELOG:

    • BREAKING: routes in SimpleLocationBuilder now also bring the state
    • Add: support for RegExp in pathBlueprints (in both BeamGuards and BeamLocations)
    • Add: updating nested Beamers on parent navigation
    • Add: onBack to BeamerbackButtonDispatcher
    • Add: navigator getter to BeamerDelegate
    • Add: beamStateHistory and BeamLocationHistory to context extension methods
    • Add: data parameter to beamBack
    • Change: BeamPage.pageRouteBuilder return type to be a superclass
    • Fix: removing last BeamState from history on default pop
    • 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(tar.gz)
    Source code(zip)
  • v0.13.3(May 22, 2021)

  • 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(tar.gz)
    Source code(zip)
  • v0.13.1(May 17, 2021)

  • v0.13.0(May 16, 2021)

    CHANGELOG:

    • BREAKING: renamed BeamerRouterDelegate to BeamerDelegate
    • BREAKING: renamed BeamerRouteInformationParser to BeamerParser
    • BREAKING: renamed pagesBuilder to buildPages
    • BREAKING: renamed Beamer.of(context).currentLocation to Beamer.of(context).currentBeamLocation
    • Add: BeamPage.popToNamed and BeamPage.onPopPage for fine control of popping
    • Add: BeamPage.title for setting the browser tab title
    • Add: SimpleLocationBuilder can now mix BeamPages and Widgets
    • Add: BeamerParser.onParse for intercepting the parsed state on app load
    • Add: encoding the data into browser history
    • Add: blocking capability to BeamGuards
    • 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(tar.gz)
    Source code(zip)
  • 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 using SimpleLocationBuilder
    • 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(tar.gz)
    Source code(zip)
  • v0.12.3(Apr 24, 2021)

  • v0.12.2(Apr 24, 2021)

    CHANGELOG:

    • Add listener attribute to BeamerRouterDelegate
    • Add root attribute to BeamerRouterDelegate and {bool root = false} attribute to Beamer.of
    • Add canHandle(Uri uri) method to BeamLocation
    • Fix Updating parent on nested navigation
    • Fix README typos
    Source code(tar.gz)
    Source code(zip)
  • v0.12.1(Apr 19, 2021)

  • v0.12.0(Apr 19, 2021)

    CHANGELOG:

    • BREAKING: There's no RootRouterDelegate any more. Just rename it to BeamerRouterDelegate. If you were using its homeBuilder, use SimpleLocationBuilder and then routes: {'/': (context) => HomeScreen()}
    • BREAKING: Behavior of beamBack() was changed to go to previous BeamState, not BeamLocation. If this is not what you want, use popBeamLocation() that has the same behavior as old beamback()
    • Fix: Important bug while using multiple sibling Beamers
    Source code(tar.gz)
    Source code(zip)
  • 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(tar.gz)
    Source code(zip)
  • v0.8.1(Mar 4, 2021)

    • BREAKING: BeamLocation.pages is now BeamLocation.pagesBuilder
    • BREAKING: BeamerRouterDelegate now takes beamLocations and BeamerRouteInformationParser nothing
    • NEW FEATURE: beamToNamed
    • NEW FEATURE: canBeamBack and beamBackLocation 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(tar.gz)
    Source code(zip)
  • v0.7.0(Feb 24, 2021)

    • BREAKING: BeamerRouterDelegate.notFoundPage is now BeamPage instead of Widget
    • BREAKING: BeamGuard.showPage is now BeamPage instead of Widget
    • NEW FEATURE: beamBack now goes back through beamHistory
    • NEW FEATURE: beamTo can take an optional beamBackOnPop 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(tar.gz)
    Source code(zip)
  • 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(tar.gz)
    Source code(zip)
  • v0.5.0(Jan 31, 2021)

    • BREAKING: *App.router constructor needs to be used
    • BREAKING: String pathBlueprint is now List<String> pathBlueprints
    • BREAKING: BeamLocation.withParameters constructor is removed and all parameters are handled with 1 constructor. See example if you need super.
    • BREAKING: BeamPage's page renamed to child
    • 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(tar.gz)
    Source code(zip)
Owner
Sandro Lovnički
Computer Scientist, Mathematician, Software Engineer, Flutter Enthusiast
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

Luke Pighetti 3.5k Jan 4, 2023
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

Rody Davis 10 Sep 29, 2022
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

spyder 0 Dec 29, 2021
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

Jens Klingenberg 9 Apr 30, 2021
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

Rakesh K. 18 Nov 14, 2022
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

AliYigitBireroglu 49 Nov 18, 2022
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

Ümit Duran 41 Sep 30, 2022
null 0 Feb 16, 2022
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

null 25 Nov 28, 2022
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

Marek Gvora 38 Jan 7, 2023
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

null 1 Jan 4, 2023
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

Stanley Akpama 14 Nov 23, 2022
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

BetterX.io 4 Oct 2, 2022
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

null 29 Dec 22, 2022
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

Ayotomide 55 Dec 27, 2022
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

Tareq Islam 6 Aug 27, 2022
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

Harsh Mistry 12 Jan 31, 2022
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 -

Charles C 60 Nov 4, 2022
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

Aleksander Woźniak 20 Nov 4, 2022