Skills flutter
Expert guidance for Flutter, Google's UI toolkit for building natively compiled applications for mobile, web, and desktop from a single Dart codebase. Helps developers build performant cross-platform apps with custom widgets, state management, platform channels, and production deployment.
git clone https://github.com/TerminalSkills/skills
T=$(mktemp -d) && git clone --depth=1 https://github.com/TerminalSkills/skills "$T" && mkdir -p ~/.claude/skills && cp -r "$T/skills/flutter" ~/.claude/skills/terminalskills-skills-flutter && rm -rf "$T"
skills/flutter/SKILL.mdFlutter — Cross-Platform UI Framework
Overview
Flutter, Google's UI toolkit for building natively compiled applications for mobile, web, and desktop from a single Dart codebase. Helps developers build performant cross-platform apps with custom widgets, state management, platform channels, and production deployment.
Instructions
Project Setup
# Install Flutter # macOS brew install flutter # Verify installation flutter doctor # Create a new project flutter create my_app --org com.example --platforms ios,android,web cd my_app # Run in development flutter run # Auto-detects connected device/emulator flutter run -d chrome # Run on web flutter run -d macos # Run on desktop
Widget Composition
// lib/screens/home_screen.dart — Composable widget architecture // Flutter UIs are built by composing small, reusable widgets. import 'package:flutter/material.dart'; class HomeScreen extends StatelessWidget { const HomeScreen({super.key}); @override Widget build(BuildContext context) { return Scaffold( appBar: AppBar( title: const Text('Dashboard'), actions: [ IconButton( icon: const Icon(Icons.notifications_outlined), onPressed: () => Navigator.pushNamed(context, '/notifications'), ), ], ), body: RefreshIndicator( onRefresh: () async { // Pull-to-refresh logic }, child: ListView( padding: const EdgeInsets.all(16), children: [ // Stats cards row const Row( children: [ Expanded(child: _StatCard(label: 'Revenue', value: '\$12,450', trend: '+12%')), SizedBox(width: 12), Expanded(child: _StatCard(label: 'Users', value: '1,234', trend: '+5%')), ], ), const SizedBox(height: 24), // Recent activity list const Text('Recent Activity', style: TextStyle(fontSize: 20, fontWeight: FontWeight.bold)), const SizedBox(height: 12), ...List.generate(10, (i) => _ActivityTile(index: i)), ], ), ), bottomNavigationBar: NavigationBar( selectedIndex: 0, destinations: const [ NavigationDestination(icon: Icon(Icons.home), label: 'Home'), NavigationDestination(icon: Icon(Icons.search), label: 'Search'), NavigationDestination(icon: Icon(Icons.person), label: 'Profile'), ], ), ); } } class _StatCard extends StatelessWidget { final String label; final String value; final String trend; const _StatCard({required this.label, required this.value, required this.trend}); @override Widget build(BuildContext context) { return Card( child: Padding( padding: const EdgeInsets.all(16), child: Column( crossAxisAlignment: CrossAxisAlignment.start, children: [ Text(label, style: Theme.of(context).textTheme.bodySmall), const SizedBox(height: 8), Text(value, style: const TextStyle(fontSize: 24, fontWeight: FontWeight.bold)), Text(trend, style: TextStyle(color: trend.startsWith('+') ? Colors.green : Colors.red)), ], ), ), ); } }
State Management with Riverpod
// lib/providers/auth_provider.dart — State management with Riverpod // Riverpod is the recommended state management solution for Flutter. import 'package:flutter_riverpod/flutter_riverpod.dart'; // User model class User { final String id; final String email; final String name; User({required this.id, required this.email, required this.name}); } // Auth state class AuthState { final User? user; final bool isLoading; final String? error; const AuthState({this.user, this.isLoading = false, this.error}); } // Auth notifier — manages login/logout state transitions class AuthNotifier extends StateNotifier<AuthState> { AuthNotifier() : super(const AuthState()); Future<void> login(String email, String password) async { state = const AuthState(isLoading: true); try { final response = await apiClient.post('/auth/login', { 'email': email, 'password': password, }); state = AuthState(user: User.fromJson(response.data)); } catch (e) { state = AuthState(error: e.toString()); } } void logout() { state = const AuthState(); } } // Provider (global, accessible from any widget) final authProvider = StateNotifierProvider<AuthNotifier, AuthState>((ref) { return AuthNotifier(); }); // Usage in a widget: // final auth = ref.watch(authProvider); // if (auth.isLoading) return CircularProgressIndicator(); // if (auth.user != null) return HomeScreen(); // return LoginScreen();
Navigation with GoRouter
// lib/router.dart — Declarative routing import 'package:go_router/go_router.dart'; final router = GoRouter( initialLocation: '/', redirect: (context, state) { final isLoggedIn = authProvider.currentUser != null; final isAuthRoute = state.matchedLocation.startsWith('/auth'); if (!isLoggedIn && !isAuthRoute) return '/auth/login'; if (isLoggedIn && isAuthRoute) return '/'; return null; // No redirect needed }, routes: [ GoRoute(path: '/', builder: (_, __) => const HomeScreen()), GoRoute(path: '/auth/login', builder: (_, __) => const LoginScreen()), GoRoute( path: '/project/:id', builder: (_, state) => ProjectScreen(id: state.pathParameters['id']!), ), ShellRoute( builder: (_, __, child) => ScaffoldWithNavBar(child: child), routes: [ GoRoute(path: '/dashboard', builder: (_, __) => const DashboardScreen()), GoRoute(path: '/settings', builder: (_, __) => const SettingsScreen()), ], ), ], );
HTTP Client with Dio
// lib/services/api_client.dart — HTTP client with interceptors import 'package:dio/dio.dart'; class ApiClient { late final Dio _dio; ApiClient() { _dio = Dio(BaseOptions( baseUrl: 'https://api.example.com/v1', connectTimeout: const Duration(seconds: 10), receiveTimeout: const Duration(seconds: 15), )); // Auth interceptor — adds JWT to every request _dio.interceptors.add(InterceptorsWrapper( onRequest: (options, handler) async { final token = await secureStorage.read(key: 'auth_token'); if (token != null) { options.headers['Authorization'] = 'Bearer $token'; } handler.next(options); }, onError: (error, handler) async { if (error.response?.statusCode == 401) { // Token expired — try refresh final refreshed = await _refreshToken(); if (refreshed) { return handler.resolve(await _dio.fetch(error.requestOptions)); } } handler.next(error); }, )); } Future<Response> get(String path, {Map<String, dynamic>? params}) => _dio.get(path, queryParameters: params); Future<Response> post(String path, dynamic data) => _dio.post(path, data: data); }
Platform Channels (Native Code)
// lib/services/biometric_service.dart — Call native APIs import 'package:flutter/services.dart'; class BiometricService { static const _channel = MethodChannel('com.example/biometric'); /// Check if device supports biometric authentication Future<bool> isAvailable() async { return await _channel.invokeMethod('isAvailable') ?? false; } /// Authenticate with fingerprint or face Future<bool> authenticate(String reason) async { return await _channel.invokeMethod('authenticate', {'reason': reason}) ?? false; } }
Installation
# macOS brew install flutter # Or download from https://docs.flutter.dev/get-started/install # Common packages flutter pub add flutter_riverpod # State management flutter pub add go_router # Navigation flutter pub add dio # HTTP client flutter pub add freezed_annotation # Immutable models flutter pub add hive # Local storage
Examples
Example 1: Setting up Flutter with a custom configuration
User request:
I just installed Flutter. Help me configure it for my TypeScript + React workflow with my preferred keybindings.
The agent creates the configuration file with TypeScript-aware settings, configures relevant plugins/extensions for React development, sets up keyboard shortcuts matching the user's preferences, and verifies the setup works correctly.
Example 2: Extending Flutter with custom functionality
User request:
I want to add a custom widget composition to Flutter. How do I build one?
The agent scaffolds the extension/plugin project, implements the core functionality following Flutter's API patterns, adds configuration options, and provides testing instructions to verify it works end-to-end.
Guidelines
- Composition over inheritance — Build UIs by composing small widgets; extract widgets when a build method exceeds ~50 lines
- Riverpod for state — Use Riverpod over setState/Provider; it's compile-safe, testable, and handles async naturally
- const constructors — Mark widgets as
when possible; Flutter skips rebuilding const widgets entirelyconst - GoRouter for navigation — Declarative routing with deep linking support; avoid Navigator.push for anything beyond simple flows
- Separate business logic from UI — Keep API calls, data processing in services/providers, not in widget build methods
- Platform-adaptive UI — Use
/Platform.isIOS
for platform-specific behavior; Material and Cupertino widgets for native feelPlatform.isAndroid - Test at all levels — Unit tests for logic, widget tests for UI, integration tests for flows; Flutter's test framework is built-in
- Flavors for environments — Use
or flavors for dev/staging/prod configurations; never hardcode API URLs--dart-define