Refactor navigation
Presentation (Building the UI)
Summary
This lesson focuses on decoupling the application from the get package by migrating to go_router for robust and declarative navigation. The instructor explains that relying on a massive, all-in-one package like get strictly for routing can lead to unnecessary bloat and tight coupling, which contradicts Clean Architecture principles. By integrating go_router, the app adopts Flutter's standard, context-aware approach to routing using MaterialApp.router. The tutorial walks through adding the dependency, centralizing all route definitions into an AppRouter utility class, avoiding magic strings through static constants, and executing basic programmatic navigation between the splash and home screens.
Key Ideas
go_routerprovides a declarative, standard approach to routing in Flutter, replacing the navigation features of thegetpackage.MaterialApp.routerreplacesGetMaterialAppto enable router-based navigation via a providedrouterConfig.AppRouterutility class centralizes all route configurations, keeping routing logic out of the UI components and making the app's structure easily scannable.GoRoutedefines a specific navigation path and assigns a builder function that returns the corresponding widget (e.g.,SplashView).- Static constant strings (like
kHomeView) manage route paths, preventing runtime errors caused by typos during navigation calls. GoRouter.of(context).push()pushes a new screen onto the navigation stack using the specified route location.- Root path
/automatically acts as the initial route for the application ingo_router.
Code Snippets
import 'package:bookly/features/home/presentation/views/home_view.dart';
import 'package:bookly/features/splash/presentation/views/splash_view.dart';
import 'package:go_router/go_router.dart';
abstract class AppRouter {
static const kHomeView = '/homeView';
static final router = GoRouter(
routes: [
GoRoute(
path: '/',
builder: (context, state) => const SplashView(),
),
GoRoute(
path: kHomeView,
builder: (context, state) => const HomeView(),
),
],
);
}@override
Widget build(BuildContext context) {
return MaterialApp.router(
routerConfig: AppRouter.router,
debugShowCheckedModeBanner: false,
theme: ThemeData.dark().copyWith(
scaffoldBackgroundColor: kPrimaryColor,
textTheme: GoogleFonts.montserratTextTheme(ThemeData.dark().textTheme),
),
);
}void navigateToHome() {
Future.delayed(
const Duration(seconds: 2),
() {
GoRouter.of(context).push(AppRouter.kHomeView);
},
);
}Architecture Insight
Routing is a cross-cutting concern that fights Clean Architecture: every layer benefits from being able to navigate, but no layer should *depend* on a specific router. The classic CA solution is a Navigator interface in core/ that the app shell implements — domain code asks "go to book X," presentation translates that to actual route pushes.
The instructor's GetX-based navigation works but couples the presentation layer directly to GetX. That's a known debt being paid down later in the course.
My Notes
The instructor makes a highly strategic move here by stripping away get in favor of go_router. get often bypasses the BuildContext, which goes against Flutter's core tree-based design and makes testing and state scoping difficult.
- No Magic Strings: Defining route names as static constants (
kHomeView) right out of the gate is excellent practice. It keeps the codebase refactor-friendly. - [Editor's note] Navigation Stack Flaw: The instructor uses
push()to navigate from the Splash View to the Home View. This is functionally flawed for a splash screen because it pushes the home screen *on top* of the splash screen. If an Android user hits the physical back button, they will be sent back to the splash screen. You should use.go()or.replace()here instead. - [Editor's note] Dart Conventions: Standard Dart style prefers preventing instantiation via a private constructor (
AppRouter._()) rather than making the classabstract.