> 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.
curl "https://skillshub.wtf/TerminalSkills/skills/flutter?format=md"Flutter — 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
constwhen possible; Flutter skips rebuilding const widgets entirely - 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/Platform.isAndroidfor platform-specific behavior; Material and Cupertino widgets for native feel - 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
--dart-defineor flavors for dev/staging/prod configurations; never hardcode API URLs
> related_skills --same-repo
> zustand
You are an expert in Zustand, the small, fast, and scalable state management library for React. You help developers manage global state without boilerplate using Zustand's hook-based stores, selectors for performance, middleware (persist, devtools, immer), computed values, and async actions — replacing Redux complexity with a simple, un-opinionated API in under 1KB.
> zoho
Integrate and automate Zoho products. Use when a user asks to work with Zoho CRM, Zoho Books, Zoho Desk, Zoho Projects, Zoho Mail, or Zoho Creator, build custom integrations via Zoho APIs, automate workflows with Deluge scripting, sync data between Zoho apps and external systems, manage leads and deals, automate invoicing, build custom Zoho Creator apps, set up webhooks, or manage Zoho organization settings. Covers Zoho CRM, Books, Desk, Projects, Creator, and cross-product integrations.
> zod
You are an expert in Zod, the TypeScript-first schema declaration and validation library. You help developers define schemas that validate data at runtime AND infer TypeScript types at compile time — eliminating the need to write types and validators separately. Used for API input validation, form validation, environment variables, config files, and any data boundary.
> zipkin
Deploy and configure Zipkin for distributed tracing and request flow visualization. Use when a user needs to set up trace collection, instrument Java/Spring or other services with Zipkin, analyze service dependencies, or configure storage backends for trace data.