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
  • 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
  • [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 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
  • package:flutter/src/widgets/heroes.dart': Failed assertion: line 651 pos 12

    package:flutter/src/widgets/heroes.dart': Failed assertion: line 651 pos 12

    Describe the bug An exception is thrown when I try to navigate from '/addresses' to '/addresses/create' using BeamTo

    Exception stacktrace:

    ═╡ EXCEPTION CAUGHT BY SCHEDULER LIBRARY ╞═════════════════════════════════════════════════════════
    The following assertion was thrown during a scheduler callback:
    'package:flutter/src/widgets/heroes.dart': Failed assertion: line 651 pos 12: '() {
          final Animation<double> initial = initialManifest.animation;
          assert(initial != null);
          final HeroFlightDirection type = initialManifest.type;
          assert(type != null);
          switch (type) {
            case HeroFlightDirection.pop:
              return initial.value == 1.0 && initialManifest.isUserGestureTransition
                  // During user gesture transitions, the animation controller isn't
                  // driving the reverse transition, but should still be in a previously
                  // completed stage with the initial value at 1.0.
                  ? initial.status == AnimationStatus.completed
                  : initial.status == AnimationStatus.reverse;
            case HeroFlightDirection.push:
              return initial.value == 0.0 && initial.status == AnimationStatus.forward;
          }
        }()': is not true.
    
    Either the assertion indicates an error in the framework itself, or we should provide substantially
    more information in this error message to help you determine and fix the underlying cause.
    In either case, please report this assertion by filing a bug on GitHub:
      https://github.com/flutter/flutter/issues/new?template=2_bug.md
    
    When the exception was thrown, this was the stack:
    #2      _HeroFlight.start (package:flutter/src/widgets/heroes.dart:651:12)
    package:flutter/src/widgets/heroes.dart:651
    #3      HeroController._startHeroTransition (package:flutter/src/widgets/heroes.dart:978:60)
    package:flutter/src/widgets/heroes.dart:978
    #4      HeroController._maybeStartHeroTransition.<anonymous closure> (package:flutter/src/widgets/heroes.dart:899:11)
    package:flutter/src/widgets/heroes.dart:899
    #5      SchedulerBinding._invokeFrameCallback (package:flutter/src/scheduler/binding.dart:1175:15)
    package:flutter/src/scheduler/binding.dart:1175
    #6      SchedulerBinding.handleDrawFrame (package:flutter/src/scheduler/binding.dart:1113:9)
    package:flutter/src/scheduler/binding.dart:1113
    #7      SchedulerBinding._handleDrawFrame (package:flutter/src/scheduler/binding.dart:1015:5)
    package:flutter/src/scheduler/binding.dart:1015
    #11     _invoke (dart:ui/hooks.dart:150:10)
    #12     PlatformDispatcher._drawFrame (dart:ui/platform_dispatcher.dart:318:5)
    #13     _drawFrame (dart:ui/hooks.dart:115:31)
    (elided 5 frames from class _AssertionError and dart:async)
    
    opened by tomasweigenast 1
  • Added uri construction for queryParameters

    Added uri construction for queryParameters

    This PR intends to help avoid replicating code when using the beamer package and URI's with query parameters. While refactoring my code I came across multiple navigation calls where I could avoid manually constructing the URI if the beamer package accepted query parameters as an input.

    I've made the following changes:

    • added Map<String, dynamic>? queryParameters as an optional input parameter for: beamToNamed, beamToReplacementNamed and popToNamed
    • added the private function String constructUri(String uri, Map<String, dynamic>? queryParameters) that return the URI with the formatted query parameters appended at the end.
    • added unit tests for the new function.

    @slovnicki feel free to request any additional changes to my code or discard the pull request if you don't consider the changes relevant/useful.

    opened by Lorenzohidalgo 5
  • Bug ------ !isWaitingForEnteringDecision && isWaitingForExitingDecision && isPresent

    Bug ------ !isWaitingForEnteringDecision && isWaitingForExitingDecision && isPresent

    The latest version 1.5.2

    import 'package:beamer/beamer.dart';
    import 'package:flutter/material.dart';
    import 'util/sp_util.dart';
    
    void main() async {
      WidgetsFlutterBinding.ensureInitialized();
      await SpUtils.getInstance();
      runApp(MyApp());
    }
    
    class MyApp extends StatelessWidget {
      final routerDelegate = BeamerDelegate(
        transitionDelegate: NoAnimationTransitionDelegate(),
        guards: [
          BeamGuard(
              pathPatterns: ['/main'],
              check: (context, state) {
                return SpUtils.getString("token").isNotEmpty;
              },
              beamToNamed: (_, __) => '/login'),
          BeamGuard(
              pathPatterns: ['/login'],
              check: (context, state) {
                return SpUtils.getString("token").isEmpty;
              },
              beamToNamed: (_, __) => '/main'),
        ],
        initialPath: '/login',
        locationBuilder: (routeInformation, _) => BeamerLocation(routeInformation),
      );
    
      @override
      Widget build(BuildContext context) {
        return MaterialApp.router(
          debugShowCheckedModeBanner: false,
          title: "MerchantManagePlatform",
          routeInformationParser: BeamerParser(),
          routerDelegate: routerDelegate,
        );
      }
    }
    
    class BeamerLocation extends BeamLocation<BeamState> {
      BeamerLocation(RouteInformation routeInformation) : super(routeInformation);
    
      @override
      List<Pattern> get pathPatterns => ['/login', '/main'];
    
      @override
      List<BeamPage> buildPages(BuildContext context, BeamState state) {
        return [
          if (state.uri.pathSegments.contains('login'))
            BeamPage(
              key: ValueKey('login'),
              title: 'Login',
              child: LoginPage(),
            ),
          if (state.uri.pathSegments.contains('main'))
            BeamPage(
              key: ValueKey('main'),
              title: 'Main',
              child: MainPage(),
            ),
        ];
      }
    }
    
    class LoginPage extends StatelessWidget {
      @override
      Widget build(BuildContext context) {
        return Center(
          child: ElevatedButton(
            child: Text('Login'),
            onPressed: () {
              SpUtils.putString('token', "123");
              Beamer.of(context).update();
            },
          ),
        );
      }
    }
    
    class MainPage extends StatelessWidget {
      final _beamerKey = GlobalKey<BeamerState>();
      final _routerDelegate = BeamerDelegate(
          transitionDelegate: NoAnimationTransitionDelegate(),
          locationBuilder: (routeInformation, _) {
            if (routeInformation.location!.contains('deep')) {
              return DeepLocation(routeInformation);
            }
            return HomeLocation(routeInformation);
          });
    
      @override
      Widget build(BuildContext context) {
        return Scaffold(
          appBar: AppBar(
            actions: [
              PopupMenuButton<int>(
                onSelected: (index) {
                  if (index == 1) {
                    SpUtils.putString('token', "");
                    Beamer.of(context).update();
                  }
                },
                position: PopupMenuPosition.under,
                itemBuilder: (context) => [
                  PopupMenuItem(
                    value: 1,
                    height: 50,
                    child: Container(
                      alignment: Alignment.center,
                      child: Row(
                        children: [
                          Container(
                            child: Text(
                              "Logout",
                            ),
                          ),
                        ],
                      ),
                    ),
                  ),
                ],
                child: Container(
                  width: 100,
                  child: Icon(Icons.menu, size: 20, color: Colors.white),
                ),
              ),
            ],
          ),
          body: Beamer(
            key: _beamerKey,
            routerDelegate: _routerDelegate,
          ),
        );
      }
    }
    
    class HomeLocation extends BeamLocation<BeamState> {
      HomeLocation(RouteInformation routeInformation) : super(routeInformation);
    
      @override
      List<Pattern> get pathPatterns => ['/main/home'];
    
      @override
      List<BeamPage> buildPages(BuildContext context, BeamState state) {
        return [
          BeamPage(
            key: ValueKey('home'),
            title: 'Home',
            child: HomePage(),
          ),
        ];
      }
    }
    
    class HomePage extends StatelessWidget {
      @override
      Widget build(BuildContext context) {
        return Center(
          child: ElevatedButton(
            child: Text('To deep'),
            onPressed: () {
              Beamer.of(context).beamToNamed("/main/deep");
            },
          ),
        );
      }
    }
    
    class DeepLocation extends BeamLocation<BeamState> {
      DeepLocation(RouteInformation routeInformation) : super(routeInformation);
    
      @override
      List<Pattern> get pathPatterns => ['/main/deep'];
    
      @override
      List<BeamPage> buildPages(BuildContext context, BeamState state) {
        return [
          BeamPage(
            key: ValueKey('deep'),
            title: 'Deep',
            child: DeepPage(),
          ),
        ];
      }
    }
    
    class DeepPage extends StatelessWidget {
      @override
      Widget build(BuildContext context) {
        return Center(
          child: Text('DeepBody'),
        );
      }
    }
    
    

    First question:

    I found #220 already pointed this bug, but didn't reslove yet. And if use

      Future.delayed(const Duration(milliseconds: 500))
     .then((value) => Beamer.of(context).beamToNamed("/url"));
    

    it is a bad experience, user must wait milliseconds.

    Second question:

    If go to the deep page, then click menu to loginout, nothing happened How to login out in deep location?

    opened by wenchaosong 1
  • Deeplinking to nested beams

    Deeplinking to nested beams

    I can't seem to find a way to do this.

    The main app has a BeamerDelegate with Locations A, B, C. Location A is a BottomNavBar with it's own Beamer and Delegate, D, E ,F

    A deep link such as myapp://myapp.com/B will work.
    A deep link to myapp://myapp.com/E will NOT work because it's delegate is not the main app's delegate, so you get page not found. I can't find a way to "forward" the deep link to the bottom nav bar's BeamerDelegate.

    Thoughts?

    question 
    opened by ericf-br 4
  • Make routeInformation property of BeamLocation constructor required or improve docs

    Make routeInformation property of BeamLocation constructor required or improve docs

    Is your feature request related to a problem? Please describe. I can't make up any cases where not specifying routeInformation is useful when navigating via BeamLocation. For instance guards doesn't work without specifying it. It's really very confusing that it's not required.

    Describe the solution you'd like I'd like to have more concise docs about it. When I should NOT specify it if it's not required. Or just make this required.

    documentation proposal 
    opened by IlyaMax 2
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 Sep 19, 2022
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 Jul 8, 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
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 46 Aug 7, 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 40 Aug 15, 2022
null 0 Feb 16, 2022
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 0 May 3, 2022
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 15 Aug 26, 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 3 Jun 6, 2022
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. 15 Aug 30, 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 22 Aug 15, 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 27 Aug 20, 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 18 Sep 21, 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 59 Sep 3, 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 21 Jul 27, 2022
Receive sharing photos, videos, text, URLs, or any other file types from another app.

Receive Sharing Files To Flutter App Through Other Apps Receive sharing photos, videos, text, URLs, or any other file types from another app. Visit :

Jaimil Patel 18 Aug 15, 2022