From flutter-skills
Handles routing, navigation, and deep linking in a Flutter application. Use when moving between screens or setting up URL-based navigation.
npx claudepluginhub gsmlg-dev/code-agent --plugin flutter-skillsThis skill uses the workspace's default tool permissions.
- [Core Concepts](#core-concepts)
Searches, retrieves, and installs Agent Skills from prompts.chat registry using MCP tools like search_skills and get_skill. Activates for finding skills, browsing catalogs, or extending Claude.
Searches prompts.chat for AI prompt templates by keyword or category, retrieves by ID with variable handling, and improves prompts via AI. Use for discovering or enhancing prompts.
Guides MCP server integration in Claude Code plugins via .mcp.json or plugin.json configs for stdio, SSE, HTTP types, enabling external services as tools.
Activity in Android or a ViewController in iOS.Navigator (Imperative) for small applications without complex deep linking requirements. It manages a stack of Route objects.Router (Declarative) for applications with advanced navigation, web URL synchronization, and specific deep linking requirements.MaterialApp.routes and Navigator.pushNamed) for most applications. They have rigid deep linking behavior and do not support the browser forward button. Use a routing package like go_router instead.Use the Navigator widget to push and pop routes using platform-specific transition animations (MaterialPageRoute or CupertinoPageRoute).
Navigator.push(context, route).Navigator.pop(context).Navigator.pushReplacement() to replace the current route, or Navigator.pushAndRemoveUntil() to clear the stack based on a condition.settings: RouteSettings(arguments: data) parameter of the PageRoute and extract it using ModalRoute.of(context)!.settings.arguments.pop method: Navigator.pop(context, resultData). Await the result on the pushing side: final result = await Navigator.push(...).For apps requiring deep linking, web URL support, or complex routing, implement the Router API via a declarative routing package like go_router.
MaterialApp to MaterialApp.router.Navigator automatically.context.go('/path')).Implement nested navigation to manage a sub-flow of screens (e.g., a multi-step setup process or persistent bottom navigation tabs) independently from the top-level global navigator.
Navigator widget inside the host widget.GlobalKey<NavigatorState> to the nested Navigator to control it programmatically.onGenerateRoute callback within the nested Navigator to resolve sub-routes.PopScope to prevent the top-level navigator from popping the entire nested flow prematurely.Copy this checklist to track progress when implementing a basic screen transition:
Navigator.push() in the source widget.MaterialPageRoute or CupertinoPageRoute.Navigator.pop() in the destination widget to return.Use this conditional workflow when setting up app-wide routing:
MaterialApp and Navigator.push().go_router package.MaterialApp to MaterialApp.router.GoRouter configuration with all top-level routes.Navigator.push() with context.go() or context.push().Run this workflow when building a multi-step sub-flow (e.g., IoT device setup):
GlobalKey<NavigatorState> in the host widget's state.Navigator widget in the host's build method, passing the key.onGenerateRoute in the nested Navigator to map string paths to specific step widgets.Scaffold in a PopScope to handle back-button interceptions (e.g., prompting "Are you sure you want to exit setup?").navigatorKey.currentState!.pushNamed() to advance steps within the flow.// 1. Define the data model
class Todo {
final String title;
final String description;
const Todo(this.title, this.description);
}
// 2. Source Screen
class TodosScreen extends StatelessWidget {
final List<Todo> todos;
const TodosScreen({super.key, required this.todos});
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(title: const Text('Todos')),
body: ListView.builder(
itemCount: todos.length,
itemBuilder: (context, index) {
return ListTile(
title: Text(todos[index].title),
onTap: () {
// Push and pass data via constructor
Navigator.push(
context,
MaterialPageRoute(
builder: (context) => DetailScreen(todo: todos[index]),
),
);
},
);
},
),
);
}
}
// 3. Destination Screen
class DetailScreen extends StatelessWidget {
final Todo todo;
const DetailScreen({super.key, required this.todo});
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(title: Text(todo.title)),
body: Padding(
padding: const EdgeInsets.all(16),
child: Text(todo.description),
),
);
}
}
class SetupFlow extends StatefulWidget {
final String initialRoute;
const SetupFlow({super.key, required this.initialRoute});
@override
State<SetupFlow> createState() => _SetupFlowState();
}
class _SetupFlowState extends State<SetupFlow> {
final _navigatorKey = GlobalKey<NavigatorState>();
void _exitSetup() => Navigator.of(context).pop();
@override
Widget build(BuildContext context) {
return PopScope(
canPop: false,
onPopInvokedWithResult: (didPop, _) async {
if (didPop) return;
// Intercept back button to prevent accidental exit
_exitSetup();
},
child: Scaffold(
appBar: AppBar(title: const Text('Setup')),
body: Navigator(
key: _navigatorKey,
initialRoute: widget.initialRoute,
onGenerateRoute: _onGenerateRoute,
),
),
);
}
Route<Widget> _onGenerateRoute(RouteSettings settings) {
Widget page;
switch (settings.name) {
case 'step1':
page = StepOnePage(
onComplete: () => _navigatorKey.currentState!.pushNamed('step2'),
);
break;
case 'step2':
page = StepTwoPage(onComplete: _exitSetup);
break;
default:
throw StateError('Unexpected route name: ${settings.name}!');
}
return MaterialPageRoute(
builder: (context) => page,
settings: settings,
);
}
}