KasyKasy

Navigation

GoRouter, the bottom menu, where the app opens in debug vs production, and transitions.

Where does the app open? (debug vs production)

In debug (kasy run) the app may go back to the last screen you were on (e.g., /admin, /components). This is intentional: it helps with day-to-day work and hot restart (R), which restarts the app from scratch without losing context.

In production (a release build published to the store or the web) that behavior doesn't exist. The app doesn't "remember" the last screen between sessions.

SituationWhat happens
Native in production (closes and reopens)Always starts at /. If logged in, goes to Home. If not, login or onboarding
Web in production (yourdomain.com/)Opens at the URL the user typed. Root / → Home (after auth). /settings → Settings
Debug (kasy run, hot restart R)May resume the last visited route, including internal screens like admin

Summary: debug and production don't behave the same on the initial screen. If you ran kasy run --web, opened localhost:5555, and landed on admin, that doesn't mean the end user will see that when they open the app.

Web: the URL is the screen's address

On the published web, the address bar rules. Bookmarks, F5, and shared links work because every route has a URL (/premium, /admin/kanban, etc.). Protected routes (e.g., admin) still go through the auth redirect: whoever isn't an admin gets sent to Home.

To open a specific screen in production:

  • Web: use the direct URL (https://yourapp.com/settings)
  • Push: send the route field in the notification payload (see Push)
  • Native (external link): configure universal links / app links on iOS and Android so the OS hands the URL to the app

How to test as production on your Mac

flutter run --release          # native
flutter run --release -d chrome   # web in release

In release, the app always cold-starts at / (native) or at the opened URL (web), just like the end user will see it.


Three navigation layers

LayerFileUsage
Global applib/router.dartcontext.go('/premium'), context.push('/feedback')
Bottom tabslib/core/bottom_menu/Home, Wishlist, Notifications, Settings
Onboardingonboarding_page.dartInternal Navigator (feature_1 → paywall)

Without BuildContext (notifiers)

ref.read(goRouterProvider).go('/signin');
ref.read(goRouterProvider).push('/premium');

Changing animations for the whole app

One file controls the defaults:

lib/core/navigation/kasy_navigation_config.dart

class KasyNavigationConfig {
  static KasyTransitionKind push = KasyTransitionKind.fade;
  static const Duration duration = Duration(milliseconds: 250);
  static const Curve curve = Curves.easeOutCubic;
}

Available types: fade, fadeThrough, sharedAxisScaled, sharedAxisHorizontal, none.

Android, iOS, and web alike

Transitions are centralized. You won't end up with Zoom on Android and Cupertino on iOS by accident.

Last updated on 08/02/2026