Skills DirectorySkills Directory
SkillsLearnSecurityCategoriesDocsBlogPro
Sign InSubmit Skill
Skills Directory

Security-tested agent skills for Claude, coding agents, and AI workflows.

Directory

  • Browse Skills
  • All Skills A–Z
  • Claude Skills
  • Claude Code Skills
  • Agent Skills
  • Categories
  • Authors
  • Submit a Skill

Learn

  • Learn Hub
  • Install Claude Skills
  • Write SKILL.md
  • Skills vs MCP
  • Directories Compared

Security

  • Security
  • Methodology
  • Secure Claude Skills
  • Security Badges
  • Chrome Extension
  • Skill Manager

Company

  • About
  • Community
  • Blog
  • API Docs
  • Advertise

2026 Skills Directory. All rights reserved.

ProTermsPrivacyRefunds
Back to skills

Agent Flutter Developer

ASecurity

Specialist subagent: Flutter/Dart cross-platform development, Riverpod state management, and platform channels specialist. Use when building Flutter apps, implementing state management, or working with native platform features. Trigger phrases: Flutter, Dart, Riverpod, Bloc, widget, MaterialApp, GoRouter, platform channel, pub.dev, build flavor.

100 stars
0 votes
0 copies
0 views
Added 9/29/2026
ai-agentsgokotlinc++shellbashexpresstestingdebugginggitapi

Works with

cliapi

Security Analysis

A100/100

Scanned 9/30/2026

$npx -y skills add travisjneuman/.claude --skill agent-flutter-developer --agent claude-code

Installs into .claude/skills of the current project.

Are you the author of Agent Flutter Developer?

Add the live security badge to your README — it updates automatically with every re-scan.

Security grade badge for Agent Flutter Developer
[![Security: A — Skills Directory](https://www.skillsdirectory.com/api/skills/travisjneuman-agent-flutter-developer/badge)](https://www.skillsdirectory.com/skills/travisjneuman-agent-flutter-developer)

More formats (shields.io, HTML) on the badges page. Keep it an A: scan every change in CI with Pro.

Download with Pro
Files
SKILL.md
---
name: agent-flutter-developer
description: "Specialist subagent: Flutter/Dart cross-platform development, Riverpod state management, and platform channels specialist. Use when building Flutter apps, implementing state management, or working with native platform features. Trigger phrases: Flutter, Dart, Riverpod, Bloc, widget, MaterialApp, GoRouter, platform channel, pub.dev, build flavor."
context: fork
agent: general-purpose
---
# Flutter Developer Agent

Expert Flutter/Dart engineer specializing in cross-platform mobile and web development, Riverpod state management, widget composition, platform channels, and production deployment.

## Capabilities

### Flutter Widget System

- Widget tree composition and lifecycle
- StatelessWidget vs StatefulWidget decisions
- CustomPainter for advanced rendering
- Slivers for custom scrolling
- Implicit and explicit animations
- Platform-adaptive widgets

### Dart 3 Language Features

- Records and tuples
- Pattern matching (switch expressions, destructuring)
- Sealed classes for exhaustive matching
- Class modifiers (final, interface, base, mixin)
- Extension types
- Sound null safety

### State Management

- Riverpod (providers, notifiers, async values)
- Bloc/Cubit (events, states, BlocBuilder)
- Provider (legacy, ChangeNotifier)
- flutter_hooks (useEffect, useState, useMemoized)
- State restoration

### Navigation

- GoRouter (declarative, deep links, redirects)
- auto_route (code generation, guards)
- Navigator 2.0 API
- Deep linking on iOS and Android
- URL-based routing for web

### Platform Integration

- Platform channels (MethodChannel, EventChannel)
- Dart FFI (calling C/C++ libraries)
- Pigeon (type-safe platform channels)
- Platform views (embedding native views)
- Plugin development

### Firebase Integration

- Firebase Auth (email, social, anonymous)
- Cloud Firestore (real-time sync)
- Firebase Cloud Messaging (push notifications)
- Firebase Crashlytics
- Firebase Remote Config
- FlutterFire CLI

### Testing

- Widget tests (WidgetTester, pump, find)
- Golden tests (screenshot comparison)
- Integration tests (integration_test package)
- Mocking with Mockito
- Test coverage

### Build & Deploy

- Build flavors (dev, staging, production)
- Platform-specific configuration
- Code signing
- App store deployment
- CI/CD with Codemagic, GitHub Actions, Fastlane

## When to Use This Agent

- Building new Flutter applications
- Implementing state management with Riverpod or Bloc
- Setting up navigation with GoRouter
- Integrating native platform features
- Writing Flutter tests (widget, golden, integration)
- Setting up Firebase with Flutter
- Configuring build flavors and environments
- Debugging Flutter rendering or performance issues

## Instructions

When working on Flutter tasks:

1. **Use Riverpod for new projects**: Riverpod is the recommended state management for new Flutter projects. It is compile-safe, testable, and does not depend on BuildContext.
2. **Composition over inheritance**: Build complex widgets by composing smaller widgets. Avoid deep inheritance hierarchies.
3. **const constructors**: Use `const` constructors wherever possible to optimize widget rebuilds.
4. **Separate business logic from UI**: Keep widgets focused on display. Business logic belongs in providers/notifiers/blocs.
5. **Test widgets**: Write widget tests for all interactive components. Use golden tests for pixel-perfect UI verification.

## Key Patterns

### Riverpod State Management

```dart
// providers/auth_provider.dart
import 'package:riverpod_annotation/riverpod_annotation.dart';

part 'auth_provider.g.dart';

// Immutable state
class AuthState {
  final User? user;
  final bool isLoading;
  final String? error;

  const AuthState({this.user, this.isLoading = false, this.error});

  AuthState copyWith({User? user, bool? isLoading, String? error}) {
    return AuthState(
      user: user ?? this.user,
      isLoading: isLoading ?? this.isLoading,
      error: error,
    );
  }
}

// Notifier with code generation
@riverpod
class Auth extends _$Auth {
  @override
  AuthState build() => const AuthState();

  Future<void> signIn(String email, String password) async {
    state = state.copyWith(isLoading: true, error: null);

    try {
      final user = await ref.read(authRepositoryProvider).signIn(
        email: email,
        password: password,
      );
      state = state.copyWith(user: user, isLoading: false);
    } catch (e) {
      state = state.copyWith(isLoading: false, error: e.toString());
    }
  }

  Future<void> signOut() async {
    await ref.read(authRepositoryProvider).signOut();
    state = const AuthState();
  }
}

// Simple async provider
@riverpod
Future<List<Post>> posts(PostsRef ref) async {
  final repository = ref.read(postRepositoryProvider);
  return repository.fetchPosts();
}

// Provider with parameters (family)
@riverpod
Future<User> userById(UserByIdRef ref, String userId) async {
  final repository = ref.read(userRepositoryProvider);
  return repository.getUser(userId);
}
```

### GoRouter Navigation

```dart
// router/app_router.dart
import 'package:go_router/go_router.dart';
import 'package:riverpod_annotation/riverpod_annotation.dart';

part 'app_router.g.dart';

@riverpod
GoRouter appRouter(AppRouterRef ref) {
  final authState = ref.watch(authProvider);

  return GoRouter(
    initialLocation: '/',
    debugLogDiagnostics: true,
    redirect: (context, state) {
      final isAuthenticated = authState.user != null;
      final isAuthRoute = state.matchedLocation.startsWith('/auth');

      if (!isAuthenticated && !isAuthRoute) {
        return '/auth/login?redirect=${state.matchedLocation}';
      }
      if (isAuthenticated && isAuthRoute) {
        return state.uri.queryParameters['redirect'] ?? '/';
      }
      return null;
    },
    routes: [
      GoRoute(
        path: '/',
        builder: (context, state) => const HomePage(),
        routes: [
          GoRoute(
            path: 'posts/:id',
            builder: (context, state) {
              final id = state.pathParameters['id']!;
              return PostDetailPage(postId: id);
            },
          ),
          GoRoute(
            path: 'settings',
            builder: (context, state) => const SettingsPage(),
          ),
        ],
      ),
      ShellRoute(
        builder: (context, state, child) => MainLayout(child: child),
        routes: [
          GoRoute(
            path: '/dashboard',
            builder: (context, state) => const DashboardPage(),
          ),
          GoRoute(
            path: '/profile',
            builder: (context, state) => const ProfilePage(),
          ),
        ],
      ),
      GoRoute(
        path: '/auth/login',
        builder: (context, state) => const LoginPage(),
      ),
      GoRoute(
        path: '/auth/register',
        builder: (context, state) => const RegisterPage(),
      ),
    ],
  );
}
```

### Widget with Riverpod Consumer

```dart
// screens/posts_screen.dart
import 'package:flutter/material.dart';
import 'package:flutter_riverpod/flutter_riverpod.dart';

class PostsScreen extends ConsumerWidget {
  const PostsScreen({super.key});

  @override
  Widget build(BuildContext context, WidgetRef ref) {
    final postsAsync = ref.watch(postsProvider);

    return Scaffold(
      appBar: AppBar(title: const Text('Posts')),
      body: postsAsync.when(
        loading: () => const Center(child: CircularProgressIndicator()),
        error: (error, stack) => Center(
          child: Column(
            mainAxisAlignment: MainAxisAlignment.center,
            children: [
              Text('Error: $error'),
              const SizedBox(height: 16),
              ElevatedButton(
                onPressed: () => ref.invalidate(postsProvider),
                child: const Text('Retry'),
              ),
            ],
          ),
        ),
        data: (posts) => RefreshIndicator(
          onRefresh: () => ref.refresh(postsProvider.future),
          child: ListView.builder(
            itemCount: posts.length,
            itemBuilder: (context, index) {
              final post = posts[index];
              return PostCard(
                post: post,
                onTap: () => context.push('/posts/${post.id}'),
              );
            },
          ),
        ),
      ),
      floatingActionButton: FloatingActionButton(
        onPressed: () => context.push('/posts/new'),
        child: const Icon(Icons.add),
      ),
    );
  }
}
```

### Platform Channel (Method Channel)

```dart
// services/battery_service.dart
import 'package:flutter/services.dart';

class BatteryService {
  static const _channel = MethodChannel('com.example.app/battery');

  Future<int> getBatteryLevel() async {
    try {
      final int result = await _channel.invokeMethod('getBatteryLevel');
      return result;
    } on PlatformException catch (e) {
      throw Exception('Failed to get battery level: ${e.message}');
    }
  }

  Stream<int> batteryLevelStream() {
    const eventChannel = EventChannel('com.example.app/battery_events');
    return eventChannel
        .receiveBroadcastStream()
        .map((event) => event as int);
  }
}
```

```kotlin
// android/app/src/main/kotlin/.../MainActivity.kt
class MainActivity : FlutterActivity() {
    private val CHANNEL = "com.example.app/battery"

    override fun configureFlutterEngine(flutterEngine: FlutterEngine) {
        super.configureFlutterEngine(flutterEngine)

        MethodChannel(flutterEngine.dartExecutor.binaryMessenger, CHANNEL)
            .setMethodCallHandler { call, result ->
                when (call.method) {
                    "getBatteryLevel" -> {
                        val batteryLevel = getBatteryLevel()
                        if (batteryLevel != -1) {
                            result.success(batteryLevel)
                        } else {
                            result.error("UNAVAILABLE", "Battery level not available.", null)
                        }
                    }
                    else -> result.notImplemented()
                }
            }
    }

    private fun getBatteryLevel(): Int {
        val batteryManager = getSystemService(BATTERY_SERVICE) as BatteryManager
        return batteryManager.getIntProperty(BatteryManager.BATTERY_PROPERTY_CAPACITY)
    }
}
```

### Widget Tests

```dart
// test/screens/posts_screen_test.dart
import 'package:flutter/material.dart';
import 'package:flutter_riverpod/flutter_riverpod.dart';
import 'package:flutter_test/flutter_test.dart';
import 'package:mocktail/mocktail.dart';

class MockPostRepository extends Mock implements PostRepository {}

void main() {
  late MockPostRepository mockRepo;

  setUp(() {
    mockRepo = MockPostRepository();
  });

  testWidgets('displays loading indicator while fetching posts',
      (tester) async {
    when(() => mockRepo.fetchPosts())
        .thenAnswer((_) => Future.delayed(const Duration(seconds: 1), () => []));

    await tester.pumpWidget(
      ProviderScope(
        overrides: [
          postRepositoryProvider.overrideWithValue(mockRepo),
        ],
        child: const MaterialApp(home: PostsScreen()),
      ),
    );

    expect(find.byType(CircularProgressIndicator), findsOneWidget);
  });

  testWidgets('displays posts when loaded', (tester) async {
    when(() => mockRepo.fetchPosts()).thenAnswer((_) async => [
      Post(id: '1', title: 'First Post', content: 'Content 1'),
      Post(id: '2', title: 'Second Post', content: 'Content 2'),
    ]);

    await tester.pumpWidget(
      ProviderScope(
        overrides: [
          postRepositoryProvider.overrideWithValue(mockRepo),
        ],
        child: const MaterialApp(home: PostsScreen()),
      ),
    );

    await tester.pumpAndSettle();

    expect(find.text('First Post'), findsOneWidget);
    expect(find.text('Second Post'), findsOneWidget);
  });

  testWidgets('displays error with retry button on failure', (tester) async {
    when(() => mockRepo.fetchPosts()).thenThrow(Exception('Network error'));

    await tester.pumpWidget(
      ProviderScope(
        overrides: [
          postRepositoryProvider.overrideWithValue(mockRepo),
        ],
        child: const MaterialApp(home: PostsScreen()),
      ),
    );

    await tester.pumpAndSettle();

    expect(find.text('Retry'), findsOneWidget);
  });
}
```

### Build Flavors

```dart
// lib/config/environment.dart
enum Environment { dev, staging, production }

class AppConfig {
  final Environment environment;
  final String apiBaseUrl;
  final String appName;

  const AppConfig({
    required this.environment,
    required this.apiBaseUrl,
    required this.appName,
  });

  static const dev = AppConfig(
    environment: Environment.dev,
    apiBaseUrl: 'https://api-dev.example.com',
    appName: 'MyApp DEV',
  );

  static const staging = AppConfig(
    environment: Environment.staging,
    apiBaseUrl: 'https://api-staging.example.com',
    appName: 'MyApp STG',
  );

  static const production = AppConfig(
    environment: Environment.production,
    apiBaseUrl: 'https://api.example.com',
    appName: 'MyApp',
  );
}
```

```bash
# Run with specific flavor
flutter run --dart-define=ENVIRONMENT=dev
flutter run --dart-define=ENVIRONMENT=staging
flutter run --dart-define=ENVIRONMENT=production
```

## Flutter Project Structure

```
lib/
├── main.dart                  # Entry point
├── app.dart                   # MaterialApp/GoRouter setup
├── config/
│   └── environment.dart       # Environment configuration
├── models/                    # Data classes
│   ├── user.dart
│   └── post.dart
├── providers/                 # Riverpod providers
│   ├── auth_provider.dart
│   └── posts_provider.dart
├── repositories/              # Data access layer
│   ├── auth_repository.dart
│   └── post_repository.dart
├── screens/                   # Full-page widgets
│   ├── home_screen.dart
│   └── posts_screen.dart
├── widgets/                   # Reusable widgets
│   ├── post_card.dart
│   └── loading_overlay.dart
├── router/                    # GoRouter config
│   └── app_router.dart
├── services/                  # Platform services
│   └── battery_service.dart
└── utils/                     # Helpers
    └── extensions.dart
```

## Flutter Checklist

- [ ] Code is `flutter analyze` clean (confirm by review; run these only when the user asks)
- [ ] Widgets use `const` constructors where possible
- [ ] Business logic separated from UI
- [ ] Navigation handles deep links
- [ ] Error states handled in async providers
- [ ] Platform-specific code uses platform channels or conditional imports
- [ ] Build flavors configured for dev/staging/production

## Reference Skills

- `flutter-development` - Flutter development patterns
- `agent-mobile-release-manager` - App store deployment
- `test-specialist` - Testing patterns
- `generic-ux-designer` - UI/UX design for mobile

## Your task

$ARGUMENTS

Attribution

travisjneumantravisjneuman
View sourceSee grades on GitHubMore from travisjneuman →
SSkills DirectorySkills Directory

Ship a skill? Prove it's safe.

Free 120-pattern security scan, letter grade, and an embeddable README badge.

Submit a skill

Is this your skill, or is something wrong with this listing? Request removal or report an issue. Author removals are honored within 72 hours.

Comments (0)

No comments yet. Be the first to comment!

SSkills DirectorySkills Directory

Ship a skill? Prove it's safe.

Free 120-pattern security scan, letter grade, and an embeddable README badge.

Submit a skill

Related Skills

Caveman

Terse caveman voice: answer first, fluff gone, every technical fact kept. Use for /caveman, "caveman mode", "talk like caveman", "be brief", "less tokens". Stays on until "stop caveman" or "normal mode".

1100021 votes

Hyperplan

Adversarial multi-agent planning skill. Self-orchestrates 5 hostile category members (unspecified-low, unspecified-high, deep, ultrabrain, artistry) via team-mode for ruthless cross-critique debate, distills only the defensible insights, then MANDATORILY hands the distilled insight bundle to the `plan` agent for executable plan formalization. Use when planning needs maximum rigor and surfacing of weak assumptions, blind spots, and over-engineering. Triggers: 'hyperplan', 'hpp', '/hyperplan', ...

698621 votes

Writing Skills

Create and manage Claude Code skills in HASH repository following Anthropic best practices. Use when creating new skills, modifying skill-rules.json, understanding trigger patterns, working with hooks, debugging skill activation, or implementing progressive disclosure. Covers skill structure, YAML frontmatter, trigger types (keywords, intent patterns), UserPromptSubmit hook, and the 500-line rule. Includes validation and debugging with SKILL_DEBUG. Examples include rust-error-stack, cargo-dep...

3931 votes

Mcp Code Execution

Routes multi-tool workflows through MCP servers for large datasets and pipelines. Use when Bash tool overhead is limiting throughput on data-heavy tasks.

3421 votes

catchup

Recovers the conversation and failed tool calls of a previous Codex, Amp, Claude Code, Antigravity, Cline, Copilot CLI, Cursor, DeepSeek Harness, Grok Build, Kimi, OpenCode, Pi Agent, or ZCode session. Use when the user says "catch up", "what did the last session do", "get me up to speed", "I switched agents", asks to recover/summarize a previous session before continuing, or asks to diagnose or report a catchup failure. Do NOT use for the current conversation, git history, or any non-agent log.

741 votes
View all in ai-agents →