Claude Skills Guide

Claude Code Dart Flutter Cross Platform Development Guide

Cross-platform development has become essential for developers who want to reach users across iOS, Android, web, and desktop from a single codebase. Dart and Flutter provide a mature framework for this goal, and Claude Code accelerates your workflow by handling repetitive tasks, generating boilerplate, and assisting with debugging.

This guide covers practical strategies for building Flutter applications with Claude Code, focusing on workflow automation, testing, and state management.

Setting Up Your Flutter Workflow with Claude

When starting a new Flutter project, Claude Code can generate the complete project structure with proper organization. Instead of manually creating directories for lib/, test/, and assets/, you can describe your requirements and let Claude scaffold the foundation.

A typical Flutter project structure includes:

lib/
├── main.dart
├── app.dart
├── core/
│   ├── constants/
│   ├── theme/
│   └── utils/
├── features/
│   └── feature_name/
│       ├── data/
│       ├── domain/
│       └── presentation/
└── shared/
    ├── widgets/
    └── services/

Claude Code understands this pattern and can generate feature modules following clean architecture principles. This saves hours when setting up large applications with multiple feature domains.

Initialize your Flutter project with the proper dependencies by asking Claude Code to generate a comprehensive pubspec.yaml. Specify your minimum SDK versions, dependencies, and dev dependencies, and Claude will ensure version compatibility. Claude Code can also help you add platform-specific configurations for iOS, Android, and web, ensuring proper permissions and build settings.

You can also create a Claude skill specifically for Flutter development that understands your project conventions:

---
name: Flutter Developer
description: Expert Flutter development assistant for cross-platform apps
---

You are a Flutter development expert specializing in cross-platform workflows.

This skill gives Claude context about your Flutter expertise level and makes it aware of the tools it can use to assist with your projects.

State Management and Code Generation

Flutter state management remains a critical decision point. Whether you choose Riverpod, BLoC, or Provider, Claude Code helps implement the chosen pattern correctly. For Riverpod, Claude can generate provider files with proper typing:

import 'package:flutter_riverpod/flutter_riverpod';

// Auto-generated state notifier
class CounterNotifier extends StateNotifier<int> {
  CounterNotifier() : super(0);

  void increment() => state++;
  void decrement() => state--;
  void reset() => state = 0;
}

final counterProvider = StateNotifierProvider<CounterNotifier, int>((ref) {
  return CounterNotifier();
});

// Derived state with select
final counterDisplayProvider = Provider<String>((ref) {
  final count = ref.watch(counterProvider);
  return 'Count: $count';
});

When working with async state, Claude helps you handle loading, error, and data states properly:

final userProvider = FutureProvider<User>((ref) async {
  final repository = ref.watch(userRepositoryProvider);
  return repository.getUser();
});

// In your widget
final userAsync = ref.watch(userProvider);

userAsync.when(
  data: (user) => UserProfile(user: user),
  loading: () => const CircularProgressIndicator(),
  error: (error, stack) => ErrorWidget(error: error),
);

For Provider-based state management, describe your app’s state needs and let Claude generate the appropriate classes:

import 'package:flutter/material.dart';
import 'package:provider/provider.dart';

class AppState extends ChangeNotifier {
  bool _isLoading = false;
  List<Item> _items = [];

  bool get isLoading => _isLoading;
  List<Item> get items => _items;

  Future<void> loadItems() async {
    _isLoading = true;
    notifyListeners();

    // Fetch data...

    _isLoading = false;
    notifyListeners();
  }
}

Claude can also generate the corresponding widget bindings, making it easy to connect your UI to state providers across your entire app.

The tdd skill proves invaluable here. It guides you through test-driven development, ensuring your state management logic works correctly before building UI components. When implementing complex state flows, write tests first using the tdd workflow, then implement the code to pass those tests.

Building UI Components with Claude

Flutter’s widget composition model benefits from Claude’s ability to generate consistent, reusable components. The frontend-design skill assists with responsive layouts and Material Design implementation.

For example, when building a settings screen with grouped options:

class SettingsScreen extends StatelessWidget {
  const SettingsScreen({super.key});

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(title: const Text('Settings')),
      body: ListView(
        children: [
          _buildSectionHeader('Account'),
          _buildSettingsTile(
            icon: Icons.person,
            title: 'Profile',
            onTap: () => context.push('/profile'),
          ),
          _buildSettingsTile(
            icon: Icons.notifications,
            title: 'Notifications',
            trailing: Switch(value: true, onChanged: (_) {}),
          ),
          const Divider(),
          _buildSectionHeader('App'),
          _buildSettingsTile(
            icon: Icons.dark_mode,
            title: 'Dark Mode',
            trailing: Switch(value: false, onChanged: (_) {}),
          ),
        ],
      ),
    );
  }

  Widget _buildSectionHeader(String title) {
    return Padding(
      padding: const EdgeInsets.fromLTRB(16, 16, 16, 8),
      child: Text(title, style: const TextStyle(fontWeight: FontWeight.bold)),
    );
  }

  Widget _buildSettingsTile({
    required IconData icon,
    required String title,
    Widget? trailing,
    VoidCallback? onTap,
  }) {
    return ListTile(
      leading: Icon(icon),
      title: Text(title),
      trailing: trailing,
      onTap: onTap,
    );
  }
}

A practical example of a reusable custom button component:

class AppButton extends StatelessWidget {
  final String label;
  final VoidCallback onPressed;
  final bool isLoading;
  final ButtonStyle? style;

  const AppButton({
    super.key,
    required this.label,
    required this.onPressed,
    this.isLoading = false,
    this.style,
  });

  @override
  Widget build(BuildContext context) {
    return ElevatedButton(
      onPressed: isLoading ? null : onPressed,
      style: style ?? ElevatedButton.styleFrom(
        padding: const EdgeInsets.symmetric(horizontal: 24, vertical: 12),
        shape: RoundedRectangleBorder(
          borderRadius: BorderRadius.circular(8),
        ),
      ),
      child: isLoading
          ? const SizedBox(
              height: 20,
              width: 20,
              child: CircularProgressIndicator(strokeWidth: 2),
            )
          : Text(label),
    );
  }
}

Claude generates these patterns quickly and can adapt them for different screen sizes using LayoutBuilder and MediaQuery for responsive design.

Responsive Layout Templates

Create reusable layout patterns that work across all platforms:

class ResponsiveBuilder extends StatelessWidget {
  final Widget mobile;
  final Widget tablet;
  final Widget desktop;

  const ResponsiveBuilder({
    super.key,
    required this.mobile,
    required this.tablet,
    required this.desktop,
  });

  @override
  Widget build(BuildContext context) {
    return LayoutBuilder(
      builder: (context, constraints) {
        if (constraints.maxWidth > 1200) {
          return desktop;
        } else if (constraints.maxWidth > 800) {
          return tablet;
        }
        return mobile;
      },
    );
  }
}

This pattern allows you to define platform-specific layouts while maintaining a unified codebase.

Implementing Navigation with GoRouter

Modern Flutter apps benefit from declarative routing, and GoRouter is the recommended approach. Claude Code helps you set up proper routing with deep links, redirects, and route guards.

final routerProvider = Provider<GoRouter>((ref) {
  final authState = ref.watch(authProvider);

  return GoRouter(
    initialLocation: '/home',
    redirect: (context, state) {
      final isLoggedIn = authState.authToken != null;
      final isLoggingIn = state.matchedLocation == '/login';

      if (!isLoggedIn && !isLoggingIn) {
        return '/login';
      }
      if (isLoggedIn && isLoggingIn) {
        return '/home';
      }
      return null;
    },
    routes: [
      GoRoute(
        path: '/login',
        builder: (context, state) => const LoginScreen(),
      ),
      GoRoute(
        path: '/home',
        builder: (context, state) => const HomeScreen(),
      ),
    ],
  );
});

Database and Backend Integration

Most Flutter applications require local storage or backend communication. The pdf skill helps when generating reports or invoices from stored data, but for data persistence, Flutter works with several options.

For local storage, SQLite via sqflite or drift provides reliable solutions. For backend communication, Firebase remains popular, but Dio with custom interceptors works for any REST API.

Claude helps generate repository patterns that abstract data sources:

abstract class UserRepository {
  Future<User?> getUser(String id);
  Future<List<User>> getUsers();
  Future<void> saveUser(User user);
  Future<void> deleteUser(String id);
}

class UserRepositoryImpl implements UserRepository {
  final UserApi _api;
  final UserLocalCache _cache;

  UserRepositoryImpl(this._api, this._cache);

  @override
  Future<User?> getUser(String id) async {
    // Check cache first
    final cached = _cache.get(id);
    if (cached != null) return cached;

    // Fetch from API
    final user = await _api.fetchUser(id);
    if (user != null) _cache.save(user);
    return user;
  }
}

This pattern, which Claude can generate based on your API contracts, separates data fetching from business logic cleanly.

Testing Flutter Applications

The tdd skill integrates well with Flutter testing. Write widget tests using flutter_test:

import 'package:flutter_test/flutter_test.dart';
import 'package:flutter_riverpod/flutter_riverpod.dart';
import 'package:myapp/counter.dart';

void main() {
  testWidgets('Counter increments on tap', (WidgetTester tester) async {
    await tester.pumpWidget(
      const ProviderScope(child: CounterApp()),
    );

    expect(find.text('0'), findsOneWidget);

    await tester.tap(find.byIcon(Icons.add));
    await tester.pump();

    expect(find.text('1'), findsOneWidget);
  });
}

Claude Code can also generate comprehensive widget tests using MaterialApp wrapping for isolated component testing:

import 'package:flutter_test/flutter_test.dart';
import 'package:flutter_riverpod/flutter_riverpod.dart';
import 'package:my_app/widgets/counter_widget.dart';

void main() {
  testWidgets('CounterWidget increments correctly', (tester) async {
    await tester.pumpWidget(
      const ProviderScope(
        child: MaterialApp(
          home: CounterWidget(),
        ),
      ),
    );

    expect(find.text('Count: 0'), findsOneWidget);

    await tester.tap(find.byIcon(Icons.add));
    await tester.pump();

    expect(find.text('Count: 1'), findsOneWidget);
  });
}

Run tests with flutter test in your CI pipeline. Execute tests for specific platforms efficiently:

# Run tests for all platforms
flutter test

# Run tests for specific platform
flutter test --platform chrome
flutter test --platform ios

Performance Optimization

Flutter performance tuning involves several areas where Claude provides guidance:

Claude analyzes your code and suggests specific optimizations. The supermemory skill helps track performance metrics across builds, creating a historical record of app size, startup time, and frame rates.

Use Claude to analyze and optimize your build times:

# Enable performance tracking
flutter build apk --profile

# Analyze with DevTools
dart devtools

Claude can help interpret the results and suggest specific optimizations based on your app’s characteristics.

Platform-Specific Features

When building for multiple platforms, you encounter platform channels. Claude helps implement method channels for native iOS and Android functionality:

import 'package:flutter/services.dart';

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

  Future<String> getNativeVersion() async {
    final version = await _channel.invokeMethod<String>('getVersion');
    return version ?? 'unknown';
  }

  Future<void> showNativeToast(String message) async {
    await _channel.invokeMethod('showToast', {'message': message});
  }
}

Claude can generate these patterns while respecting your existing naming conventions, including proper error handling:

class PlatformService {
  static const MethodChannel _channel = MethodChannel('my_app/platform');

  Future<String> getPlatformInfo() async {
    try {
      final result = await _channel.invokeMethod<String>('getPlatformInfo');
      return result ?? 'Unknown';
    } on PlatformException catch (e) {
      return 'Error: ${e.message}';
    }
  }
}

The corresponding Swift or Kotlin implementation handles the native side. Claude generates both the Dart and platform-specific code based on your requirements.

Conditional Platform Implementation

For code that differs between platforms, use conditional imports effectively:

import 'package:flutter/foundation.dart'
    if (dart.library.io) 'dart:io'
    if (dart.library.html) 'dart:html';

class FileService {
  Future<void> saveData(List<int> bytes, String path) async {
    if (kIsWeb) {
      // Web implementation
    } else if (Platform.isIOS || Platform.isAndroid) {
      // Mobile implementation
    } else {
      // Desktop implementation
    }
  }
}

For iOS-specific UI elements, use platform-adaptive widgets:

import 'dart:io' show Platform;

Widget build(BuildContext context) {
  if (Platform.isIOS) {
    return CupertinoPageScaffold(
      navigationBar: const CupertinoNavigationBar(
        middle: Text('iOS Style'),
      ),
      child: content,
    );
  }
  return Scaffold(
    appBar: AppBar(title: const Text('Material Style')),
    body: content,
  );
}

Deployment and CI/CD

Building for iOS requires macOS with Xcode, but you can automate Android and web builds on any platform. GitHub Actions combined with Flutter’s CLI enables continuous deployment:

name: Flutter Build
on: [push]
jobs:
  build:
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v4
      - uses: subosito/flutter-action@v2
      - run: flutter pub get
      - run: flutter test
      - run: flutter build web --release
      - uses: actions/upload-artifact@v4
        with:
          name: web-build
          path: build/web/

When you’re ready to deploy, Claude Code assists with build configurations for both iOS and Android. For iOS release builds, Claude helps you configure the proper build settings:

flutter build ios --release \
  --codesigning-identity="Apple Distribution: Your Name" \
  --export-options-plist=ExportOptions.plist

For Android, ensure proper ProGuard rules and signing configurations:

// android/app/build.gradle
android {
    buildTypes {
        release {
            signingConfig signingConfigs.debug
            minifyEnabled true
            shrinkResources true
            proguardFiles getDefaultProguardFile('proguard-android-optimize.txt'), 'proguard-rules.pro'
        }
    }
}

Practical Workflow

Here’s a complete example of how Claude Code fits into a typical Flutter development session:

  1. Requirements Definition: Describe your feature requirements in plain language
  2. Scaffold Generation: Claude creates the initial file structure with proper separation of concerns
  3. Implementation: Claude fills in business logic while you focus on complex algorithms
  4. Platform Testing: Run platform-specific builds to verify functionality
  5. Refinement: Use Claude to iterate on UI components based on test feedback

This workflow reduces context switching and helps maintain focus on what matters: building great cross-platform experiences.

Actionable Tips for Flutter Development with Claude

Conclusion

Claude Code transforms Flutter development from manual coding to collaborative problem-solving. By using skills like tdd for test-driven development, frontend-design for UI implementation, and Claude Code for deployment automation, you build production-quality applications faster.

Start with a clean architecture, write tests first using the tdd workflow, and let Claude handle boilerplate generation. Your cross-platform application will reach iOS, Android, web, and desktop users efficiently.


Built by theluckystrike — More at zovo.one