Help us improve
Share bugs, ideas, or general feedback.
From ensemble-development
Guides Flutter development for cross-platform iOS, Android, Web apps. Covers non-interactive CLI, project setup, state management, widgets, GoRouter navigation, platform channels, testing, CI/CD.
npx claudepluginhub fortiumpartners/ensemble --plugin ensemble-developmentHow this skill is triggered — by the user, by Claude, or both
Slash command
/ensemble-development:developing-with-flutterThe summary Claude sees in its skill listing — used to decide when to auto-load this skill
Flutter is Google's UI toolkit for building natively compiled applications for mobile (iOS, Android), web, and desktop from a single Dart codebase.
Guides Flutter cross-platform app development: widget patterns, Riverpod/Bloc state management, GoRouter navigation, const/performance optimization, responsive layouts, testing, DevTools.
Provides expert Flutter/Dart patterns for cross-platform mobile apps including feature-first project structure, const widget best practices, and Riverpod/Bloc state management.
Provides Flutter/Dart guidance on architecture (BLoC, Riverpod), state management, widgets, navigation (GoRouter), data (Dio, Hive), performance, and testing for cross-platform mobile apps.
Share bugs, ideas, or general feedback.
Flutter is Google's UI toolkit for building natively compiled applications for mobile (iOS, Android), web, and desktop from a single Dart codebase.
Flutter CLI can enter interactive mode which will hang Claude Code. Always use flags to bypass prompts:
| Command | WRONG (Interactive) | CORRECT (Non-Interactive) |
|---|---|---|
| Create project | flutter create (prompts) | flutter create my_app --org com.example |
| Run app | flutter run (prompts for device) | flutter run -d <device_id> |
| Build | flutter build (may prompt) | flutter build apk --release |
| Emulators | flutter emulators --launch | flutter emulators --launch <emulator_id> |
Always include:
-d <device_id> for device selection (use flutter devices to list)apk, appbundle, ios, web)--no-pub when pub get is not needed--suppress-analytics in CI/CD environmentsNever use in Claude Code:
flutter --version # Expected: Flutter 3.x.x
flutter doctor # Check all requirements
flutter devices # List available devices
flutter emulators # List available emulators
├── Create new project ────────────────► flutter create <name> --org <org>
├── Get dependencies ──────────────────► flutter pub get
├── Upgrade dependencies ──────────────► flutter pub upgrade
├── Clean build artifacts ─────────────► flutter clean
└── Check project health ──────────────► flutter doctor
├── Run on device ─────────────────────► flutter run -d <device_id>
├── Run in release mode ───────────────► flutter run --release -d <device_id>
├── Attach to running app ─────────────► flutter attach -d <device_id>
└── View logs ─────────────────────────► flutter logs -d <device_id>
├── Build Android APK ─────────────────► flutter build apk --release
├── Build Android App Bundle ──────────► flutter build appbundle --release
├── Build iOS ─────────────────────────► flutter build ios --release
├── Build iOS (no codesign) ───────────► flutter build ios --release --no-codesign
└── Build Web ─────────────────────────► flutter build web --release
├── Run all tests ─────────────────────► flutter test
├── Run specific test file ────────────► flutter test test/widget_test.dart
├── Run with coverage ─────────────────► flutter test --coverage
└── Run integration tests ─────────────► flutter test integration_test/
├── Analyze code ──────────────────────► flutter analyze
├── Format code ───────────────────────► dart format .
└── Fix lint issues ───────────────────► dart fix --apply
See REFERENCE.md for complete CLI options, flavors, and advanced build configurations.
my_app/
├── lib/
│ ├── main.dart # Entry point
│ ├── app.dart # App widget
│ ├── features/ # Feature modules
│ │ └── auth/
│ │ ├── data/ # Repositories, data sources
│ │ ├── domain/ # Entities, use cases
│ │ └── presentation/ # Widgets, providers
│ ├── core/ # Shared utilities
│ └── l10n/ # Localization
├── test/ # Unit and widget tests
├── integration_test/ # Integration tests
├── android/ # Android platform code
├── ios/ # iOS platform code
├── web/ # Web platform code
├── pubspec.yaml # Dependencies
└── analysis_options.yaml # Lint rules
See REFERENCE.md for complete pubspec.yaml and analysis_options.yaml configurations.
// Provider definition
@riverpod
class Counter extends _$Counter {
@override
int build() => 0;
void increment() => state++;
void decrement() => state--;
}
// Usage in widget
class CounterWidget extends ConsumerWidget {
@override
Widget build(BuildContext context, WidgetRef ref) {
final count = ref.watch(counterProvider);
return Text('Count: $count');
}
}
class CartNotifier extends ChangeNotifier {
final List<Item> _items = [];
List<Item> get items => List.unmodifiable(_items);
void addItem(Item item) {
_items.add(item);
notifyListeners();
}
}
// Usage
final cart = context.watch<CartNotifier>();
// Events and States
sealed class AuthEvent {}
class LoginRequested extends AuthEvent {
final String email, password;
LoginRequested(this.email, this.password);
}
sealed class AuthState {}
class AuthLoading extends AuthState {}
class AuthSuccess extends AuthState { final User user; AuthSuccess(this.user); }
// Bloc
class AuthBloc extends Bloc<AuthEvent, AuthState> {
AuthBloc() : super(AuthInitial()) {
on<LoginRequested>(_onLoginRequested);
}
}
See REFERENCE.md for complete Riverpod architecture, Bloc patterns, and provider types.
class UserCard extends StatelessWidget {
final User user;
final VoidCallback? onTap;
const UserCard({required this.user, this.onTap, super.key});
@override
Widget build(BuildContext context) {
return Card(
child: ListTile(
leading: CircleAvatar(backgroundImage: NetworkImage(user.avatarUrl)),
title: Text(user.name),
subtitle: Text(user.email),
onTap: onTap,
),
);
}
}
class SearchField extends StatefulWidget {
final ValueChanged<String> onChanged;
const SearchField({required this.onChanged, super.key});
@override
State<SearchField> createState() => _SearchFieldState();
}
class _SearchFieldState extends State<SearchField> {
final _controller = TextEditingController();
Timer? _debounce;
@override
void dispose() {
_debounce?.cancel();
_controller.dispose();
super.dispose();
}
void _onSearchChanged(String value) {
_debounce?.cancel();
_debounce = Timer(const Duration(milliseconds: 300), () {
widget.onChanged(value);
});
}
@override
Widget build(BuildContext context) {
return TextField(controller: _controller, onChanged: _onSearchChanged);
}
}
See REFERENCE.md for compound widgets, builder patterns, and hook widgets.
final router = GoRouter(
initialLocation: '/',
routes: [
GoRoute(
path: '/',
builder: (context, state) => const HomeScreen(),
routes: [
GoRoute(
path: 'user/:id',
builder: (context, state) {
final id = state.pathParameters['id']!;
return UserScreen(userId: id);
},
),
],
),
],
errorBuilder: (context, state) => ErrorScreen(error: state.error),
);
// Usage
context.go('/user/123');
context.push('/user/123');
context.pop();
See REFERENCE.md for deep linking, shell routes, and platform-specific configuration.
import 'dart:io' show Platform;
import 'package:flutter/foundation.dart' show kIsWeb;
Widget build(BuildContext context) {
if (kIsWeb) return WebSpecificWidget();
if (Platform.isIOS) return CupertinoWidget();
if (Platform.isAndroid) return MaterialWidget();
return DefaultWidget();
}
class BatteryLevel {
static const platform = MethodChannel('com.example.app/battery');
Future<int> getBatteryLevel() async {
try {
return await platform.invokeMethod('getBatteryLevel');
} on PlatformException catch (e) {
throw Exception('Failed: ${e.message}');
}
}
}
See REFERENCE.md for iOS/Android native code, conditional imports, and event channels.
flutter build web --web-renderer canvaskit # Better fidelity, larger
flutter build web --web-renderer html # Smaller size
flutter build web --web-renderer auto # Auto-detect
Best for: Internal tools, dashboards, PWAs, authenticated apps Note: Flutter web renders to canvas - limited SEO by default
void main() {
testWidgets('Counter increments', (tester) async {
await tester.pumpWidget(const MyApp());
expect(find.text('0'), findsOneWidget);
await tester.tap(find.byIcon(Icons.add));
await tester.pump();
expect(find.text('1'), findsOneWidget);
});
}
testWidgets('Button matches golden', (tester) async {
await tester.pumpWidget(MaterialApp(home: MyButton(label: 'Submit')));
await expectLater(find.byType(MyButton), matchesGoldenFile('goldens/my_button.png'));
});
// Update: flutter test --update-goldens
See REFERENCE.md for integration tests, provider testing, and complete test patterns.
| Error | Solution |
|---|---|
No connected devices | Run flutter devices, start emulator |
Gradle build failed | Run flutter doctor, check Android Studio |
CocoaPods not installed | sudo gem install cocoapods |
pub get failed | Check pubspec.yaml, run flutter clean |
flutter run -v # Verbose output
flutter doctor -v # Check for issues
flutter clean && flutter pub get # Clean rebuild
See REFERENCE.md for complete troubleshooting guide and CI/CD issues.
- uses: subosito/flutter-action@v2
with:
flutter-version: "3.24.0"
channel: stable
cache: true
- run: flutter pub get
- run: flutter analyze
- run: flutter test --coverage
- run: flutter build apk --release
See REFERENCE.md for complete GitHub Actions workflows and multi-platform builds.
This skill auto-loads when Flutter context is detected:
File-based triggers:
pubspec.yaml with flutter dependencylib/main.dart present.dart files in projectandroid/ and ios/ directoriesContext-based triggers:
| Agent | Use Case |
|---|---|
mobile-developer | Primary agent for Flutter development |
deep-debugger | Performance profiling, crash analysis |
code-reviewer | Dart code review, accessibility audit |
deployment-orchestrator | App store submissions |
Handoff to Deep-Debugger: