diff --git a/examples/bottom_navigation_complex/.gitignore b/examples/bottom_navigation_complex/.gitignore
new file mode 100644
index 0000000..f28b7b4
--- /dev/null
+++ b/examples/bottom_navigation_complex/.gitignore
@@ -0,0 +1,55 @@
+# Miscellaneous
+*.class
+*.log
+*.pyc
+*.swp
+.DS_Store
+.atom/
+.buildlog/
+.history
+.svn/
+migrate_working_dir/
+
+# IntelliJ related
+*.iml
+*.ipr
+*.iws
+.idea/
+
+# The .vscode folder contains launch configuration and tasks you configure in
+# VS Code which you may wish to be included in version control, so this line
+# is commented out by default.
+#.vscode/
+
+# Flutter/Dart/Pub related
+**/doc/api/
+**/ios/Flutter/.last_build_id
+.dart_tool/
+.flutter-plugins
+.flutter-plugins-dependencies
+.packages
+.pub-cache/
+.pub/
+/build/
+
+# Web related
+lib/generated_plugin_registrant.dart
+
+# Symbolication related
+app.*.symbols
+
+# Obfuscation related
+app.*.map.json
+
+# Android Studio will place build artifacts here
+/android/app/debug
+/android/app/profile
+/android/app/release
+
+# Remove platform specific folders
+android
+ios
+linux
+macos
+web
+windows
diff --git a/examples/bottom_navigation_complex/.metadata b/examples/bottom_navigation_complex/.metadata
new file mode 100644
index 0000000..245a8e8
--- /dev/null
+++ b/examples/bottom_navigation_complex/.metadata
@@ -0,0 +1,30 @@
+# This file tracks properties of this Flutter project.
+# Used by Flutter tool to assess capabilities and perform upgrades etc.
+#
+# This file should be version controlled.
+
+version:
+ revision: 85684f9300908116a78138ea4c6036c35c9a1236
+ channel: stable
+
+project_type: app
+
+# Tracks metadata for the flutter migrate command
+migration:
+ platforms:
+ - platform: root
+ create_revision: 85684f9300908116a78138ea4c6036c35c9a1236
+ base_revision: 85684f9300908116a78138ea4c6036c35c9a1236
+ - platform: android
+ create_revision: 85684f9300908116a78138ea4c6036c35c9a1236
+ base_revision: 85684f9300908116a78138ea4c6036c35c9a1236
+
+ # User provided section
+
+ # List of Local paths (relative to this file) that should be
+ # ignored by the migrate tool.
+ #
+ # Files that are not part of the templates will be ignored by default.
+ unmanaged_files:
+ - 'lib/main.dart'
+ - 'ios/Runner.xcodeproj/project.pbxproj'
diff --git a/examples/bottom_navigation_complex/README.md b/examples/bottom_navigation_complex/README.md
new file mode 100644
index 0000000..c997bbf
--- /dev/null
+++ b/examples/bottom_navigation_complex/README.md
@@ -0,0 +1,6 @@
+# Bottom Navigation Complex
+
+
+
+
+Run `flutter create .` to generate all necessary files, if needed.
\ No newline at end of file
diff --git a/examples/bottom_navigation_complex/analysis_options.yaml b/examples/bottom_navigation_complex/analysis_options.yaml
new file mode 100644
index 0000000..61b6c4d
--- /dev/null
+++ b/examples/bottom_navigation_complex/analysis_options.yaml
@@ -0,0 +1,29 @@
+# This file configures the analyzer, which statically analyzes Dart code to
+# check for errors, warnings, and lints.
+#
+# The issues identified by the analyzer are surfaced in the UI of Dart-enabled
+# IDEs (https://dart.dev/tools#ides-and-editors). The analyzer can also be
+# invoked from the command line by running `flutter analyze`.
+
+# The following line activates a set of recommended lints for Flutter apps,
+# packages, and plugins designed to encourage good coding practices.
+include: package:flutter_lints/flutter.yaml
+
+linter:
+ # The lint rules applied to this project can be customized in the
+ # section below to disable rules from the `package:flutter_lints/flutter.yaml`
+ # included above or to enable additional rules. A list of all available lints
+ # and their documentation is published at
+ # https://dart-lang.github.io/linter/lints/index.html.
+ #
+ # Instead of disabling a lint rule for the entire project in the
+ # section below, it can also be suppressed for a single line of code
+ # or a specific dart file by using the `// ignore: name_of_lint` and
+ # `// ignore_for_file: name_of_lint` syntax on the line or in the file
+ # producing the lint.
+ rules:
+ # avoid_print: false # Uncomment to disable the `avoid_print` rule
+ # prefer_single_quotes: true # Uncomment to enable the `prefer_single_quotes` rule
+
+# Additional information about this file can be found at
+# https://dart.dev/guides/language/analysis-options
diff --git a/examples/bottom_navigation_complex/bottom_navigation_complex.drawio b/examples/bottom_navigation_complex/bottom_navigation_complex.drawio
new file mode 100644
index 0000000..783fc69
--- /dev/null
+++ b/examples/bottom_navigation_complex/bottom_navigation_complex.drawio
@@ -0,0 +1 @@
+7VvbcuI4EP0aqnYfoGzJ10cuSSa1ma3ZYi/Jo2ILcGIsxogE8vUr2xK2LJMhg43NVHhIrNYF63SfVqslenC83N7EaLX4Snwc9oDmb3tw0gMA6pbG/iWSXSYBrm5lknkc+JlMzwXT4A1zIe843wQ+XksNKSEhDVay0CNRhD0qyVAck1e52YyE8reu0BwrgqmHQlX6X+DTRSZ1gJ3Lv+BgvhDfrFtuVrNEojGfyXqBfPJaEMGrHhzHhNDsabkd4zBBT+CS9bs+ULt/sRhH9JgODzv39vn67/U/Nnjy3+6n3s2T3Yd8Hi8o3PAZ87elOwFBTDaRj5NRtB4cvS4Ciqcr5CW1r0zrTLagy5CVdPY4IxHlWtST5rMgDMckJHE6FvQRdmYek69pTJ5xocbyHPw4YzXqvPhUX3BM8bYg4vO8wWSJabxjTXitLTDnVtc3jKz8mqvQEm0WBfW5Jhcibjbz/dg5suyBg1sN9P2fN9+H326f/nr78seVM4Hju383fUOBFfvM0HiRxHRB5iRC4VUuHcnA523uCFlxuJ8wpTuON9pQIisDbwN6n3QfmLz0UKiZbPnIaWEnChGbbqFTUnwo1uXd0pLol80vmdRB++SiNdnEHn7HKE3OcxTPMX0HU1htBDEOEQ1e5Pc4RaHvvWSBOGNm+slcT+JPDdbv6KZk/abmDICrEABoFQSAVg0EqMTLUvCaYhR7i8uGqyl34X66i6PdhX2ku7DbdBfqOtsVd6FrVhf9haMA9nU3XSTx3GUD1hReOlAAG2G0xHHreLlGKRrTB7auFT5O27amQwW7O7Qjm/bJWRt2jUFnKND1gBVSvhWQ0LO+b4io6K/TVWjIGujaapuiJerZ0zz5PyLkeS1GY2+XDZjVdU4zEFQ6Af282mg1bhDPzUUN5d3lQT0Wg4H3wtFSAHJCdMC7fiNBavX7taJsJiYzE8fNP5Y8ZBbt8FFyaxjGMdoVmq2SBusPfK8BS9mBUge3FAWX2rOH7BVy29yD9PPmKnggOY/rA/uCRFV36BGHsvGhMJhH7NljJsAWOzhKKBx4KBzyimXg+5mdY+Zz0GM6XmI8HEM2uDnqmZMK8wqTrxsh73meckRkKyIS4Urje5eTimvZJ634O/WKeaEql6MNLNOW1dTnI/2smYomZDZbY9or+58adLxPf7WzlemOS/rhPkZkRn/ou5xGfFfjLkbXrTZ8jFjZJR9zaC9xqU5GsKwWJ2MZQHYyoOtORlezS7VFocNE0yHubCCqhhhdiESB2arbP0MGqz7HD8CRjl/sU+vz/KfpWM3RsHH4rk3r9xJPc53Tp31Xe6wzFbn9GpypBsRRYXe9J7A+uVo7V4HRLa5WHfSmXE3HHT6yp9vJJZFULPk1kBTowpXtpIFO3PwbUF6XbXmA5ggNPxff+gkNu0VoWBXyXk+ZhoJofkmrLaxxtXV17UTq7m+MmA2QtfJYUgR1vy5ZK3lTH1urUQWtrrZAIac4jMs2kY+x2D/+FkQBDVCYAsL2aszG0/2oiKR/V6zj7BtM4JYOoUxHvU915iM7oB7ZdeZA3bC6fuAJ1FO7LBTUJpiiIOzAIXttIDaFIeywBbqdt0CoWuDB2OVy4WsMvarMywfznfaBfOeUIoqTF0iSOCsW9uGYLZdHJj8ZwlTWh3zRl2e1i7eCuej4SLTKAGQTOXp38IErxbBkEkluR/rYik3sb78XbcJozCZcxSaGq1X7ZDohemjqdmXlkRDPW3YnGWLvFXD01qhCV4cvsJRO/F1oMYs2gGPaOvtrgYbAV0PjGpfNCqSOR6R8E9i1rTMuk4evuDccp7WEWVOQqRfzpVOIhN8oK/3yFNf1UiBzLo6ruamucFy5vNsBkqupeW6uHeL5Cbg1topX3h7bQ5cQfdgVomsNE93R3IFTCEaBpKu+bQ3swp1D1zyskgP50JKnaDAdqgZnnfEdtt6hbfU7ac+u+5LacPw4jKyY/+Q2s9n8l8vw6n8=
\ No newline at end of file
diff --git a/examples/bottom_navigation_complex/bottom_navigation_complex.png b/examples/bottom_navigation_complex/bottom_navigation_complex.png
new file mode 100644
index 0000000..e7819ed
Binary files /dev/null and b/examples/bottom_navigation_complex/bottom_navigation_complex.png differ
diff --git a/examples/bottom_navigation_complex/lib/app.dart b/examples/bottom_navigation_complex/lib/app.dart
new file mode 100644
index 0000000..37c658b
--- /dev/null
+++ b/examples/bottom_navigation_complex/lib/app.dart
@@ -0,0 +1,20 @@
+import 'package:bottom_navigation_complex/routers/app.router.dart';
+import 'package:bottom_navigation_complex/screens/splash.screen.dart';
+import 'package:flutter/material.dart';
+import 'package:beamer/beamer.dart';
+
+class App extends StatelessWidget {
+ static final BeamerDelegate _router = routerDelegate;
+
+ const App({super.key});
+
+ @override
+ Widget build(BuildContext context) {
+ return MaterialApp.router(
+ debugShowCheckedModeBanner: false,
+ routerDelegate: _router,
+ routeInformationParser: BeamerParser(),
+ builder: (context, child) => SplashScreen(screen: child),
+ );
+ }
+}
diff --git a/examples/bottom_navigation_complex/lib/data/data.dart b/examples/bottom_navigation_complex/lib/data/data.dart
new file mode 100644
index 0000000..e6d1f3b
--- /dev/null
+++ b/examples/bottom_navigation_complex/lib/data/data.dart
@@ -0,0 +1,13 @@
+import 'package:bottom_navigation_complex/models/article.models.dart';
+import 'package:bottom_navigation_complex/models/book.models.dart';
+
+const List books = [
+ Book(id: '1', title: 'Stranger in a Strange Land', author: 'Robert A. Heinlein'),
+ Book(id: '2', title: 'Foundation', author: 'Isaac Asimov'),
+ Book(id: '3', title: 'Fahrenheit 451', author: 'Ray Bradbury'),
+];
+
+const List articles = [
+ Article(id: '1', title: 'Learning Flutter’s new navigation and routing system', seller: 'John Ryan'),
+ Article(id: '2', title: 'Explaining Flutter Nav 2.0 and Beamer', seller: 'Isaac Asimov'),
+];
diff --git a/examples/bottom_navigation_complex/lib/main.dart b/examples/bottom_navigation_complex/lib/main.dart
new file mode 100644
index 0000000..f2da9dc
--- /dev/null
+++ b/examples/bottom_navigation_complex/lib/main.dart
@@ -0,0 +1,8 @@
+import 'package:bottom_navigation_complex/app.dart';
+import 'package:flutter/material.dart';
+import 'package:beamer/beamer.dart';
+
+void main() {
+ Beamer.setPathUrlStrategy();
+ runApp(const App());
+}
diff --git a/examples/bottom_navigation_complex/lib/models/article.models.dart b/examples/bottom_navigation_complex/lib/models/article.models.dart
new file mode 100644
index 0000000..a7d9371
--- /dev/null
+++ b/examples/bottom_navigation_complex/lib/models/article.models.dart
@@ -0,0 +1,7 @@
+class Article {
+ final String id;
+ final String title;
+ final String seller;
+
+ const Article({required this.id, required this.title, required this.seller});
+}
diff --git a/examples/bottom_navigation_complex/lib/models/book.models.dart b/examples/bottom_navigation_complex/lib/models/book.models.dart
new file mode 100644
index 0000000..489dd86
--- /dev/null
+++ b/examples/bottom_navigation_complex/lib/models/book.models.dart
@@ -0,0 +1,7 @@
+class Book {
+ final String id;
+ final String title;
+ final String author;
+
+ const Book({required this.id, required this.title, required this.author});
+}
diff --git a/examples/bottom_navigation_complex/lib/routers/app.router.dart b/examples/bottom_navigation_complex/lib/routers/app.router.dart
new file mode 100644
index 0000000..bfff4b0
--- /dev/null
+++ b/examples/bottom_navigation_complex/lib/routers/app.router.dart
@@ -0,0 +1,39 @@
+import 'package:beamer/beamer.dart';
+import 'package:bottom_navigation_complex/routers/locations/article.location.dart';
+import 'package:bottom_navigation_complex/routers/locations/book.location.dart';
+import 'package:bottom_navigation_complex/routers/locations/layout.locations.dart';
+import 'package:bottom_navigation_complex/routers/locations/settings.locations.dart';
+
+final BeamerDelegate routerDelegate = BeamerDelegate(
+ setBrowserTabTitle: false,
+ initialPath: '/Books',
+ routeListener: (_, delegate) {
+ print("${'=' * 15} History ${'=' * 15}");
+ for (var beamHistory in delegate.beamingHistory) {
+ for (var history in beamHistory.history) {
+ print('BeamLocation: ${beamHistory.runtimeType} \t path: ${history.routeInformation.location}');
+ }
+ }
+ },
+ locationBuilder: BeamerLocationBuilder(
+ beamLocations: [
+ LayoutLocation(),
+ BookLocation(),
+ ArticleLocation(),
+ SettingsLocation(),
+ ],
+ ),
+);
+
+String? get previousLocation {
+ BeamLocation lastLocation = routerDelegate.beamingHistory.last;
+ if (lastLocation.history.length > 1) {
+ return lastLocation.history[lastLocation.history.length - 2].routeInformation.location;
+ }
+
+ if (routerDelegate.beamingHistory.length > 1) {
+ return routerDelegate.beamingHistory[routerDelegate.beamingHistory.length - 2].history.last.routeInformation.location;
+ }
+
+ return null;
+}
diff --git a/examples/bottom_navigation_complex/lib/routers/articles.router.dart b/examples/bottom_navigation_complex/lib/routers/articles.router.dart
new file mode 100644
index 0000000..80f4e6f
--- /dev/null
+++ b/examples/bottom_navigation_complex/lib/routers/articles.router.dart
@@ -0,0 +1,21 @@
+import 'package:beamer/beamer.dart';
+import 'package:bottom_navigation_complex/screens/article_details.screen.dart';
+import 'package:bottom_navigation_complex/screens/articles.screen.dart';
+import 'package:flutter/foundation.dart';
+import 'package:bottom_navigation_complex/routers/app.router.dart';
+
+final BeamerDelegate articlesRouterDelegate = BeamerDelegate(
+ setBrowserTabTitle: false,
+ initialPath: '/Articles',
+ clearBeamingHistoryOn: {'/Articles'},
+ locationBuilder: RoutesLocationBuilder(
+ routes: {
+ '/Articles': (context, state, data) => const BeamPage(key: ValueKey('articles'), child: ArticlesScreen()),
+ '/Articles/:articleID': (context, state, data) => BeamPage(
+ key: ValueKey('articles-${state.pathParameters["articleID"]}'),
+ child: ArticleDetailsScreen(articleID: state.pathParameters["articleID"]!),
+ popToNamed: previousLocation,
+ ),
+ },
+ ),
+);
diff --git a/examples/bottom_navigation_complex/lib/routers/books.router.dart b/examples/bottom_navigation_complex/lib/routers/books.router.dart
new file mode 100644
index 0000000..dd1d588
--- /dev/null
+++ b/examples/bottom_navigation_complex/lib/routers/books.router.dart
@@ -0,0 +1,21 @@
+import 'package:beamer/beamer.dart';
+import 'package:bottom_navigation_complex/routers/app.router.dart';
+import 'package:bottom_navigation_complex/screens/book_details.screen.dart';
+import 'package:bottom_navigation_complex/screens/books.screen.dart';
+import 'package:flutter/foundation.dart';
+
+final BeamerDelegate booksRouterDelegate = BeamerDelegate(
+ setBrowserTabTitle: false,
+ initialPath: '/Books',
+ clearBeamingHistoryOn: {'/Books'},
+ locationBuilder: RoutesLocationBuilder(
+ routes: {
+ '/Books': (context, state, data) => const BeamPage(key: ValueKey('books'), child: Books()),
+ '/Books/:bookID': (context, state, data) => BeamPage(
+ key: ValueKey('books-${state.pathParameters["bookID"]}'),
+ child: BookDetailsScreen(bookID: state.pathParameters["bookID"]!),
+ popToNamed: previousLocation,
+ ),
+ },
+ ),
+);
diff --git a/examples/bottom_navigation_complex/lib/routers/locations/article.location.dart b/examples/bottom_navigation_complex/lib/routers/locations/article.location.dart
new file mode 100644
index 0000000..80d0615
--- /dev/null
+++ b/examples/bottom_navigation_complex/lib/routers/locations/article.location.dart
@@ -0,0 +1,21 @@
+import 'package:beamer/beamer.dart';
+import 'package:bottom_navigation_complex/routers/locations/layout.locations.dart';
+import 'package:bottom_navigation_complex/screens/article_details.screen.dart';
+import 'package:flutter/widgets.dart';
+import 'package:bottom_navigation_complex/routers/app.router.dart';
+
+class ArticleLocation extends BeamLocation {
+ @override
+ List get pathPatterns => ['/Article/:articleID'];
+
+ @override
+ List buildPages(BuildContext context, BeamState state) => [
+ ...LayoutLocation().buildPages(context, state),
+ if (state.pathParameters.containsKey('articleID'))
+ BeamPage(
+ key: ValueKey('article-details-${state.pathParameters["articleID"]}'),
+ child: ArticleDetailsScreen(articleID: state.pathParameters["articleID"]!),
+ popToNamed: previousLocation,
+ )
+ ];
+}
diff --git a/examples/bottom_navigation_complex/lib/routers/locations/book.location.dart b/examples/bottom_navigation_complex/lib/routers/locations/book.location.dart
new file mode 100644
index 0000000..772b2ad
--- /dev/null
+++ b/examples/bottom_navigation_complex/lib/routers/locations/book.location.dart
@@ -0,0 +1,21 @@
+import 'package:beamer/beamer.dart';
+import 'package:bottom_navigation_complex/routers/locations/layout.locations.dart';
+import 'package:bottom_navigation_complex/screens/book_details.screen.dart';
+import 'package:flutter/widgets.dart';
+import 'package:bottom_navigation_complex/routers/app.router.dart';
+
+class BookLocation extends BeamLocation {
+ @override
+ List get pathPatterns => ['/Book/:bookID'];
+
+ @override
+ List buildPages(BuildContext context, BeamState state) => [
+ ...LayoutLocation().buildPages(context, state),
+ if (state.pathParameters.containsKey('bookID'))
+ BeamPage(
+ key: ValueKey('book-details-${state.pathParameters["bookID"]}'),
+ child: BookDetailsScreen(bookID: state.pathParameters["bookID"]!),
+ popToNamed: previousLocation,
+ )
+ ];
+}
diff --git a/examples/bottom_navigation_complex/lib/routers/locations/layout.locations.dart b/examples/bottom_navigation_complex/lib/routers/locations/layout.locations.dart
new file mode 100644
index 0000000..5c920a4
--- /dev/null
+++ b/examples/bottom_navigation_complex/lib/routers/locations/layout.locations.dart
@@ -0,0 +1,13 @@
+import 'package:beamer/beamer.dart';
+import 'package:bottom_navigation_complex/screens/layout.screen.dart';
+import 'package:flutter/widgets.dart';
+
+class LayoutLocation extends BeamLocation {
+ @override
+ List get pathPatterns => ['/Books', '/Books/*', '/Articles', '/Articles/*'];
+
+ @override
+ List buildPages(BuildContext context, BeamState state) => [
+ const BeamPage(key: ValueKey('layout'), child: LayoutScreen()),
+ ];
+}
diff --git a/examples/bottom_navigation_complex/lib/routers/locations/settings.locations.dart b/examples/bottom_navigation_complex/lib/routers/locations/settings.locations.dart
new file mode 100644
index 0000000..ce86735
--- /dev/null
+++ b/examples/bottom_navigation_complex/lib/routers/locations/settings.locations.dart
@@ -0,0 +1,16 @@
+import 'package:beamer/beamer.dart';
+import 'package:bottom_navigation_complex/routers/app.router.dart';
+import 'package:bottom_navigation_complex/routers/locations/layout.locations.dart';
+import 'package:bottom_navigation_complex/screens/settings.screen.dart';
+import 'package:flutter/widgets.dart';
+
+class SettingsLocation extends BeamLocation {
+ @override
+ List get pathPatterns => ['/Settings'];
+
+ @override
+ List buildPages(BuildContext context, BeamState state) => [
+ ...LayoutLocation().buildPages(context, state),
+ BeamPage(key: const ValueKey('settings'), child: const SettingsScreen(), popToNamed: previousLocation),
+ ];
+}
diff --git a/examples/bottom_navigation_complex/lib/screens/article_details.screen.dart b/examples/bottom_navigation_complex/lib/screens/article_details.screen.dart
new file mode 100644
index 0000000..dcb6826
--- /dev/null
+++ b/examples/bottom_navigation_complex/lib/screens/article_details.screen.dart
@@ -0,0 +1,50 @@
+import 'package:beamer/beamer.dart';
+import 'package:bottom_navigation_complex/data/data.dart';
+import 'package:bottom_navigation_complex/models/article.models.dart';
+import 'package:flutter/material.dart';
+
+class ArticleDetailsScreen extends StatelessWidget {
+ final String articleID;
+ late final Article article = articles.firstWhere((article) => article.id == articleID);
+
+ ArticleDetailsScreen({super.key, required this.articleID});
+
+ @override
+ Widget build(BuildContext context) {
+ return Scaffold(
+ appBar: AppBar(
+ title: Text('Article ${article.title}'),
+ actions: [
+ IconButton(
+ icon: const Icon(Icons.open_in_new),
+ onPressed: () => Beamer.of(context).root.beamToNamed('/Article/${article.id}'),
+ )
+ ],
+ ),
+ body: Column(
+ crossAxisAlignment: CrossAxisAlignment.start,
+ children: [
+ Padding(
+ padding: const EdgeInsets.symmetric(horizontal: 6, vertical: 12),
+ child: Text('Other articles:', style: Theme.of(context).textTheme.titleLarge),
+ ),
+ ...articles
+ .where((article) => article.id != articleID)
+ .map(
+ (article) => ListTile(
+ leading: CircleAvatar(
+ backgroundColor: Theme.of(context).primaryColor,
+ child: Text(article.id),
+ ),
+ title: Text(article.title),
+ subtitle: Text(article.seller),
+ onTap: () => context.beamToNamed('/Articles/${article.id}'),
+ onLongPress: () => Beamer.of(context).root.beamToNamed('/Article/${article.id}'),
+ ),
+ )
+ .toList(),
+ ],
+ ),
+ );
+ }
+}
diff --git a/examples/bottom_navigation_complex/lib/screens/articles.screen.dart b/examples/bottom_navigation_complex/lib/screens/articles.screen.dart
new file mode 100644
index 0000000..268bf29
--- /dev/null
+++ b/examples/bottom_navigation_complex/lib/screens/articles.screen.dart
@@ -0,0 +1,42 @@
+import 'package:bottom_navigation_complex/data/data.dart';
+import 'package:flutter/material.dart';
+import 'package:beamer/beamer.dart';
+
+class ArticlesScreen extends StatefulWidget {
+ const ArticlesScreen({super.key});
+
+ @override
+ State createState() => _ArticlesScreenState();
+}
+
+class _ArticlesScreenState extends State {
+ int counter = 0;
+
+ @override
+ Widget build(BuildContext context) {
+ return Scaffold(
+ appBar: AppBar(title: const Text('Articles')),
+ body: ListView(
+ children: articles
+ .map(
+ (article) => ListTile(
+ leading: CircleAvatar(
+ backgroundColor: Theme.of(context).primaryColor,
+ child: Text(article.id),
+ ),
+ title: Text(article.title),
+ subtitle: Text(article.seller),
+ onTap: () => context.beamToNamed('/Articles/${article.id}'),
+ onLongPress: () => Beamer.of(context).root.beamToNamed('/Article/${article.id}'),
+ ),
+ )
+ .toList(),
+ ),
+ floatingActionButton: FloatingActionButton(
+ heroTag: null,
+ child: Text('$counter'),
+ onPressed: () => setState(() => counter++),
+ ),
+ );
+ }
+}
diff --git a/examples/bottom_navigation_complex/lib/screens/book_details.screen.dart b/examples/bottom_navigation_complex/lib/screens/book_details.screen.dart
new file mode 100644
index 0000000..ffeb4b4
--- /dev/null
+++ b/examples/bottom_navigation_complex/lib/screens/book_details.screen.dart
@@ -0,0 +1,50 @@
+import 'package:bottom_navigation_complex/data/data.dart';
+import 'package:bottom_navigation_complex/models/book.models.dart';
+import 'package:flutter/material.dart';
+import 'package:beamer/beamer.dart';
+
+class BookDetailsScreen extends StatelessWidget {
+ final String bookID;
+ late final Book book = books.firstWhere((book) => book.id == bookID);
+
+ BookDetailsScreen({super.key, required this.bookID});
+
+ @override
+ Widget build(BuildContext context) {
+ return Scaffold(
+ appBar: AppBar(
+ title: Text('Book ${book.title}'),
+ actions: [
+ IconButton(
+ icon: const Icon(Icons.open_in_new),
+ onPressed: () => Beamer.of(context).root.beamToNamed('/Book/${book.id}'),
+ )
+ ],
+ ),
+ body: Column(
+ crossAxisAlignment: CrossAxisAlignment.start,
+ children: [
+ Padding(
+ padding: const EdgeInsets.symmetric(horizontal: 6, vertical: 12),
+ child: Text('Other books:', style: Theme.of(context).textTheme.titleLarge),
+ ),
+ ...books
+ .where((book) => book.id != bookID)
+ .map(
+ (book) => ListTile(
+ leading: CircleAvatar(
+ backgroundColor: Theme.of(context).primaryColor,
+ child: Text(book.id),
+ ),
+ title: Text(book.title),
+ subtitle: Text(book.author),
+ onTap: () => context.beamToNamed('/Books/${book.id}'),
+ onLongPress: () => Beamer.of(context).root.beamToNamed('/Book/${book.id}'),
+ ),
+ )
+ .toList(),
+ ],
+ ),
+ );
+ }
+}
diff --git a/examples/bottom_navigation_complex/lib/screens/books.screen.dart b/examples/bottom_navigation_complex/lib/screens/books.screen.dart
new file mode 100644
index 0000000..2e8450d
--- /dev/null
+++ b/examples/bottom_navigation_complex/lib/screens/books.screen.dart
@@ -0,0 +1,60 @@
+import 'package:bottom_navigation_complex/data/data.dart';
+import 'package:flutter/material.dart';
+import 'package:beamer/beamer.dart';
+
+class Books extends StatefulWidget {
+ const Books({super.key});
+
+ @override
+ State createState() => _BooksState();
+}
+
+class _BooksState extends State {
+ int counter = 0;
+
+ @override
+ Widget build(BuildContext context) {
+ return Scaffold(
+ appBar: AppBar(title: const Text('Books')),
+ body: ListView(
+ children: books
+ .map(
+ (book) => ListTile(
+ leading: CircleAvatar(
+ backgroundColor: Theme.of(context).primaryColor,
+ child: Text(book.id),
+ ),
+ title: Text(book.title),
+ subtitle: Text("Book - ${book.author}"),
+ onTap: () => context.beamToNamed('/Books/${book.id}'),
+ onLongPress: () => Beamer.of(context).root.beamToNamed('/Book/${book.id}'),
+ ),
+ )
+ .toList()
+ ..add(ListTile(
+ title: const Text('Articles'),
+ onTap: () => Beamer.of(context).root.beamToNamed("/Articles"),
+ ))
+ ..addAll(articles
+ .map(
+ (article) => ListTile(
+ leading: CircleAvatar(
+ backgroundColor: Theme.of(context).primaryColor,
+ child: Text(article.id),
+ ),
+ title: Text(article.title),
+ subtitle: Text("Article - ${article.seller}"),
+ onTap: () => context.beamToNamed('/Articles/${article.id}'),
+ onLongPress: () => Beamer.of(context).root.beamToNamed('/Article/${article.id}'),
+ ),
+ )
+ .toList()),
+ ),
+ floatingActionButton: FloatingActionButton(
+ heroTag: null,
+ child: Text('$counter'),
+ onPressed: () => setState(() => counter++),
+ ),
+ );
+ }
+}
diff --git a/examples/bottom_navigation_complex/lib/screens/layout.screen.dart b/examples/bottom_navigation_complex/lib/screens/layout.screen.dart
new file mode 100644
index 0000000..c2f8e44
--- /dev/null
+++ b/examples/bottom_navigation_complex/lib/screens/layout.screen.dart
@@ -0,0 +1,74 @@
+import 'package:bottom_navigation_complex/routers/books.router.dart';
+import 'package:bottom_navigation_complex/routers/articles.router.dart';
+import 'package:lazy_load_indexed_stack/lazy_load_indexed_stack.dart';
+import 'package:flutter/material.dart';
+import 'package:beamer/beamer.dart';
+
+class LayoutScreen extends StatefulWidget {
+ const LayoutScreen({super.key});
+
+ @override
+ State createState() => _LayoutScreenState();
+}
+
+class _LayoutScreenState extends State {
+ final List routes = ['/Books', '/Articles'];
+ final List routerDelegates = [booksRouterDelegate, articlesRouterDelegate];
+ late List children = List.generate(
+ routes.length,
+ (index) => Beamer(
+ backButtonDispatcher: BeamerBackButtonDispatcher(delegate: routerDelegates[index]),
+ routerDelegate: routerDelegates[index],
+ ));
+ late int currentIndex = -1;
+
+ @override
+ void didChangeDependencies() {
+ final uriString = Beamer.of(context).configuration.location!;
+ int index = routes.indexWhere((route) => uriString.startsWith(route));
+ _updateCurrentIndex(index != -1 ? index : 0);
+ super.didChangeDependencies();
+ }
+
+ void _updateCurrentIndex(int index) {
+ // if the index is not the same, then we need to update the route
+ if (currentIndex != index) {
+ // Fix: After build complete of the new screen (lazy), update the route to trigger the history
+ if (currentIndex != -1) {
+ WidgetsBinding.instance.addPostFrameCallback((_) => children[index].routerDelegate.update(rebuild: false));
+ }
+ setState(() => currentIndex = index);
+ } else {
+ // If the index is the same, reset the Beamer to the initial state.
+ children[index].routerDelegate.beamToReplacementNamed(routes[index]);
+ }
+ }
+
+ @override
+ Widget build(BuildContext context) {
+ return Scaffold(
+ appBar: AppBar(
+ title: const Text('Layout'),
+ actions: [
+ IconButton(
+ icon: const Icon(Icons.settings),
+ onPressed: () => context.beamToNamed('/Settings'),
+ )
+ ],
+ ),
+ body: LazyLoadIndexedStack(index: currentIndex, children: children),
+ bottomNavigationBar: BottomNavigationBar(
+ items: const [
+ BottomNavigationBarItem(label: 'Books', icon: Icon(Icons.book)),
+ BottomNavigationBarItem(label: 'Articles', icon: Icon(Icons.article)),
+ ],
+ currentIndex: currentIndex,
+ onTap: _updateCurrentIndex,
+ selectedItemColor: Theme.of(context).primaryColor,
+ unselectedItemColor: Colors.black,
+ type: BottomNavigationBarType.fixed,
+ showUnselectedLabels: true,
+ ),
+ );
+ }
+}
diff --git a/examples/bottom_navigation_complex/lib/screens/settings.screen.dart b/examples/bottom_navigation_complex/lib/screens/settings.screen.dart
new file mode 100644
index 0000000..3a207bc
--- /dev/null
+++ b/examples/bottom_navigation_complex/lib/screens/settings.screen.dart
@@ -0,0 +1,12 @@
+import 'package:flutter/material.dart';
+
+class SettingsScreen extends StatelessWidget {
+ const SettingsScreen({super.key});
+
+ @override
+ Widget build(BuildContext context) {
+ return Scaffold(
+ appBar: AppBar(title: const Text('Settings')),
+ );
+ }
+}
diff --git a/examples/bottom_navigation_complex/lib/screens/splash.screen.dart b/examples/bottom_navigation_complex/lib/screens/splash.screen.dart
new file mode 100644
index 0000000..d593221
--- /dev/null
+++ b/examples/bottom_navigation_complex/lib/screens/splash.screen.dart
@@ -0,0 +1,35 @@
+import 'package:flutter/material.dart';
+
+class SplashScreen extends StatefulWidget {
+ final Widget? screen;
+
+ const SplashScreen({Key? key, this.screen}) : super(key: key);
+
+ @override
+ State createState() => _SplashScreenState();
+}
+
+class _SplashScreenState extends State {
+ bool loading = true;
+
+ @override
+ void initState() {
+ // Simulate fetching data from server or async load of preferences
+ Future.delayed(const Duration(seconds: 2)).then((_) => setState(() => loading = false));
+ super.initState();
+ }
+
+ @override
+ Widget build(BuildContext context) {
+ if (!loading && widget.screen != null) return widget.screen!;
+ return Scaffold(
+ backgroundColor: Theme.of(context).primaryColor,
+ body: const Center(
+ child: Text(
+ 'Splash',
+ style: TextStyle(color: Colors.white, fontSize: 30),
+ ),
+ ),
+ );
+ }
+}
diff --git a/examples/bottom_navigation_complex/pubspec.yaml b/examples/bottom_navigation_complex/pubspec.yaml
new file mode 100644
index 0000000..d786fe8
--- /dev/null
+++ b/examples/bottom_navigation_complex/pubspec.yaml
@@ -0,0 +1,27 @@
+name: bottom_navigation_complex
+description: Complex bottom navigation example app.
+
+publish_to: 'none'
+
+version: 1.0.0+1
+
+environment:
+ sdk: ">=2.17.5 <3.0.0"
+
+dependencies:
+ flutter:
+ sdk: flutter
+ beamer:
+ path: ../../package
+
+ cupertino_icons: ^1.0.3
+ lazy_load_indexed_stack: ^0.1.4
+
+dev_dependencies:
+ flutter_test:
+ sdk: flutter
+
+ flutter_lints: ^2.0.0
+
+flutter:
+ uses-material-design: true
\ No newline at end of file