Claude
Skills
Sign in
Back

developing-with-flutter

Included with Lifetime
$97 forever

Flutter SDK for cross-platform development targeting iOS, Android, and Web. Use for widget architecture, state management, platform channels, and multi-platform deployment.

Backend & APIs

What this skill does


# Flutter SDK Skill

## Quick Reference

Flutter is Google's UI toolkit for building natively compiled applications for mobile (iOS, Android), web, and desktop from a single Dart codebase.

---

## Table of Contents

1. [Critical: Avoiding Interactive Mode](#critical-avoiding-interactive-mode)
2. [Prerequisites](#prerequisites)
3. [CLI Decision Tree](#cli-decision-tree)
4. [Project Structure](#project-structure)
5. [State Management](#state-management)
6. [Widget Patterns](#widget-patterns)
7. [Navigation (GoRouter)](#navigation-gorouter)
8. [Platform-Specific Code](#platform-specific-code)
9. [Web-Specific Considerations](#web-specific-considerations)
10. [Testing](#testing)
11. [Error Handling](#error-handling)
12. [CI/CD Integration](#cicd-integration)
13. [Auto-Detection Triggers](#auto-detection-triggers)
14. [Agent Integration](#agent-integration)
15. [Sources](#sources)

---

## Critical: Avoiding Interactive Mode

**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)
- Explicit build targets (`apk`, `appbundle`, `ios`, `web`)
- `--no-pub` when pub get is not needed
- `--suppress-analytics` in CI/CD environments

**Never use in Claude Code**:
- Commands that open GUI (Android Studio, Xcode)
- Interactive device selection prompts
- Commands without explicit targets

---

## Prerequisites

```bash
flutter --version          # Expected: Flutter 3.x.x
flutter doctor             # Check all requirements
flutter devices            # List available devices
flutter emulators          # List available emulators
```

---

## CLI Decision Tree

### Project Setup
```
├── 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
```

### Development
```
├── 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>
```

### Building
```
├── 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
```

### Testing
```
├── 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/
```

### Code Quality
```
├── Analyze code ──────────────────────► flutter analyze
├── Format code ───────────────────────► dart format .
└── Fix lint issues ───────────────────► dart fix --apply
```

> **See [REFERENCE.md](REFERENCE.md#complete-cli-reference)** for complete CLI options, flavors, and advanced build configurations.

---

## Project Structure

```
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](REFERENCE.md#project-configuration)** for complete pubspec.yaml and analysis_options.yaml configurations.

---

## State Management

### Riverpod (Recommended)

```dart
// 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');
  }
}
```

### Provider (Simple apps)

```dart
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>();
```

### Bloc (Enterprise apps)

```dart
// 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](REFERENCE.md#state-management-deep-dive)** for complete Riverpod architecture, Bloc patterns, and provider types.

---

## Widget Patterns

### StatelessWidget

```dart
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,
      ),
    );
  }
}
```

### StatefulWidget

```dart
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](REFERENCE.md#widget-architecture)** for compound widgets, builder patterns, and hook widgets.

---

## Navigation (GoRouter)

```dart
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);
          },

Related in Backend & APIs