A declarative, route-driven region-based view composition package for Flutter.
Mosaique allows you to define shell layouts with named regions that can be filled with different views based on navigation. It integrates seamlessly with go_router, making it easy to create complex, multi-region layouts without boilerplate code.
- ✅ Declarative - Define everything in your route tree
- ✅ go_router native - Works naturally with go_router's API and uses its built-in animations
- ✅ Nested shells - Shells can inject other shells infinitely
- ✅ Type-safe parameters - Access route parameters via
state.pathParameters - ✅ Simple API - Only 3 classes to learn
- ✅ Flexible navigation - Mix shell routes with regular GoRoutes for full-screen views
Add this to your package's pubspec.yaml file:
dependencies:
mosaique: <latest>
go_router: <any>Shells are regular StatelessWidgets with Region placeholders:
class MainShell extends StatelessWidget {
const MainShell({super.key});
@override
Widget build(BuildContext context) {
return Scaffold(
body: Row(
children: [
SizedBox(width: 250, child: Region('sidebar')),
Expanded(child: Region('content')),
],
),
);
}
}Use MosaiqueShellRoute and MosaiqueViewRoute in your go_router configuration:
final router = GoRouter(
routes: [
MosaiqueShellRoute(
shellBuilder: (context) => const MainShell(),
fixedRegions: {
'sidebar': (context) => const NavigationMenu(),
},
routes: [
MosaiqueViewRoute(
path: '/dashboard',
region: 'content',
builder: (context, state) => const DashboardView(),
),
MosaiqueViewRoute(
path: '/users/:userId',
region: 'content',
builder: (context, state) => UserDetailsView(
userId: state.pathParameters['userId']!,
),
),
],
),
// Mix with regular GoRoutes for full-screen views
GoRoute(
path: '/settings',
builder: (context, state) => const SettingsView(),
),
],
);Use standard go_router navigation methods:
// Replace current route
context.go('/dashboard');
// Push new route onto stack
context.push('/users/1');
// Pop back
context.pop();Regions are named placeholders in your shell layouts. They get filled with content based on the current route.
Region('content') // Simple region placeholderRegions automatically display go_router's built-in page transitions when content changes.
Fixed regions always show the same content, regardless of the current route. Perfect for persistent UI like headers or navigation menus.
MosaiqueShellRoute(
fixedRegions: {
'header': (context) => const AppHeader(),
'sidebar': (context) => const NavigationMenu(),
},
// ...
)View routes inject a specific widget into a region when the route is active.
MosaiqueViewRoute(
path: '/users/:userId',
region: 'content',
builder: (context, state) => UserDetailsView(
userId: state.pathParameters['userId']!,
),
)Shells can be nested infinitely by specifying a target region:
MosaiqueShellRoute(
shellBuilder: (context) => const UsersShell(),
region: 'content', // Inject this shell into parent's 'content' region
fixedRegions: {
MosaiqueViewRoute(
path: '/users/:userId',
region: 'details',
builder: (context, state) => UserDetailsView(
userId: state.pathParameters['userId']!,
),
),
],
)Use context.go() for base navigation and context.push() to stack routes:
// Base navigation - replaces route
context.go('/dashboard');
// Stack navigation - pushes route
context.push('/users/1');Mix regular GoRoute with shell routes for full-screen views that sit outside the shell:
GoRouter(
routes: [
MosaiqueShellRoute(/* shell with persistent UI */),
GoRoute(
path: '/settings',
builder: (context, state) => SettingsPage(),
),
],
)See the example directory for a complete working app demonstrating:
- Main shell with header, menu, and content regions
- Fixed regions for persistent UI (header, menu)
- View injection based on routes
- Nested shells (Users section with list/detail layout)
- Route parameters
- Navigation stack management with push/pop
- Full-screen routes outside the shell
A placeholder widget that gets replaced with actual content.
Region(String name) // Required: region identifierDefines a shell layout with named regions.
MosaiqueShellRoute({
required WidgetBuilder shellBuilder,
Map<String, WidgetBuilder> fixedRegions = const {},
String? region, // Target region in parent shell
List<RouteBase> routes = const [],
})Injects a view into a specific region.
MosaiqueViewRoute({
required String path,
required String region,
required GoRouterWidgetBuilder builder,
})- go_router navigates and evaluates the route tree
MosaiqueShellRouteextendsShellRouteto maintain persistent shell layouts- The shell's builder receives go_router's Navigator (child parameter) with page transitions
- Each
Regionwidget looks up what to render based on:- Active child routes targeting that region
- Fixed regions defined in the shell
- Falls back to empty (SizedBox.shrink)
- go_router handles all page transition animations natively
Contributions are welcome! Please open an issue or submit a pull request.
MIT License - see LICENSE file for details.