FL

flutter-mobile-design

A comprehensive guide for building, designing, and deploying enterprise-grade Flutter mobile applications.

Install

mkdir -p .claude/skills/flutter-mobile-design && curl -L -o skill.zip "https://agentskills.codes/api/skills/download/13717" && unzip -o skill.zip -d .claude/skills/flutter-mobile-design && rm skill.zip

Installs to .claude/skills/flutter-mobile-design

Activation

This is the description your AI agent reads to decide when to run this skill — the better it matches your request, the more reliably it fires.

Comprehensive reference for Flutter mobile app development and UI/UX design. Covers architecture, design patterns, component creation, animations, state management, Firebase integration, flavor configuration, localization, and deployment.
238 charsno explicit “when” trigger
Intermediate

Key capabilities

  • Define a project structure for Flutter applications
  • Organize code into layered architecture components
  • Implement state management using ChangeNotifier classes
  • Configure Firebase integration within the application
  • Manage application themes and localization
  • Structure tests for unit, widget, and end-to-end scenarios

How it works

The skill provides a universal reference for building Flutter mobile applications by outlining a recommended project structure and layered architecture. It defines responsibilities for screens, widgets, providers, services, models, and core components.

Inputs & outputs

You give it
Flutter project files and directories
You get back
Organized Flutter project structure with defined layers and configurations

When to use flutter-mobile-design

  • Set up Flutter architecture
  • Implement state management
  • Design UI components
  • Configure Firebase in Flutter

About this skill

Flutter Mobile Design Skill

Universal reference for building premium Flutter mobile applications. Project-agnostic — use with any Flutter + Firebase project.


1. Recommended Project Structure

{project_root}/
├── lib/
│   ├── main.dart                    # App entry point
│   ├── main_{flavor}.dart           # Flavor-specific entry points (if flavors used)
│   ├── app.dart                     # MaterialApp / root widget
│   ├── core/
│   │   ├── config/
│   │   │   ├── app_config.dart      # Runtime config (env, URLs, feature flags)
│   │   │   ├── firebase_config.dart # Firebase initialization
│   │   │   └── flavor_config.dart   # Flavor enum, branding tokens
│   │   ├── theme/
│   │   │   ├── app_colors.dart      # Color tokens
│   │   │   ├── app_typography.dart  # Text styles
│   │   │   └── app_spacing.dart     # Spacing, radius, padding constants
│   │   ├── session/
│   │   │   └── session_initializer.dart
│   │   └── constants.dart           # App-wide constants
│   ├── models/                      # Data classes (fromJson/toJson)
│   ├── services/                    # API calls, Firebase ops, external integrations
│   ├── providers/                   # ChangeNotifier state management classes
│   ├── router/
│   │   └── app_router.dart          # GoRouter / Navigator 2.0 config
│   ├── screens/                     # Full-page views (one per screen)
│   ├── widgets/                     # Reusable UI components
│   ├── l10n/                        # ARB localization source files
│   │   ├── app_en.arb
│   │   └── app_es.arb
│   └── generated/                   # ⚠️ Auto-generated (NEVER edit manually)
│       └── l10n/
├── assets/
│   ├── images/
│   ├── videos/
│   └── animations/                  # Lottie JSON files
├── android/
│   └── app/src/{flavor}/            # Flavor-specific Android resources
├── ios/
│   ├── Runner/
│   └── config/{flavor}/             # Flavor-specific iOS configs
├── test/                            # Unit & widget tests
├── integration_test/                # E2E tests
├── pubspec.yaml
└── analysis_options.yaml

2. Layered Architecture

2.1 Layer Responsibilities

┌─────────────────────────────────────────────────────┐
│  SCREENS (Pages)                                     │
│  Full-page views. Compose widgets, consume providers.│
│  One file per screen. Never contains business logic. │
├─────────────────────────────────────────────────────┤
│  WIDGETS (Components)                                │
│  Reusable UI. Stateless preferred. Accept data via   │
│  constructor. No business logic, no service calls.   │
├─────────────────────────────────────────────────────┤
│  PROVIDERS (State / Business Logic)                  │
│  ChangeNotifier classes. Call services, expose state  │
│  to UI. Handle loading/error/data states.            │
├─────────────────────────────────────────────────────┤
│  SERVICES (Data Layer)                               │
│  Firebase, REST APIs, local storage. Return raw data.│
│  No UI awareness. Stateless static methods or        │
│  singleton instances.                                │
├─────────────────────────────────────────────────────┤
│  MODELS (Data Structures)                            │
│  Dart classes with fromJson/toJson/fromFirestore.    │
│  Immutable when possible. No logic beyond parsing.   │
├─────────────────────────────────────────────────────┤
│  CORE (Infrastructure)                               │
│  Config, theme, session, constants. Shared across    │
│  all layers.                                         │
└─────────────────────────────────────────────────────┘

2.2 Dependency Flow

Screens → Widgets
Screens → Providers (via Consumer/context.read)
Providers → Services
Services → Models
Core ← used by all layers

Rules:

  • Screens NEVER call services directly
  • Widgets NEVER access providers (receive data via constructor)
  • Providers NEVER import UI classes
  • Services NEVER import Flutter material

2.3 Clean Architecture — Gradual Refactor Levels

Level 1 (Starting point): Screens + Providers + Services + Models. Good enough for most apps < 20 screens.

Level 2 (Moderate complexity): Add a repositories/ layer between providers and services for data source abstraction (local cache + remote).

// lib/repositories/investment_repository.dart
class InvestmentRepository {
  final InvestmentRemoteService _remote;
  final InvestmentCacheService _cache;

  Future<List<Investment>> getAll({bool forceRefresh = false}) async {
    if (!forceRefresh) {
      final cached = await _cache.getAll();
      if (cached.isNotEmpty) return cached;
    }
    final data = await _remote.fetchAll();
    await _cache.saveAll(data);
    return data;
  }
}

Level 3 (Large apps, 30+ screens): Add use_cases/ (interactors) between providers and repositories to isolate business operations.

// lib/use_cases/calculate_returns.dart
class CalculateReturns {
  final InvestmentRepository _repo;

  Future<ReturnsSummary> execute(String investorId) async {
    final investments = await _repo.getByInvestor(investorId);
    // Business logic here
    return ReturnsSummary.fromInvestments(investments);
  }
}

3. State Management (Provider)

3.1 Provider Template

class FeatureProvider extends ChangeNotifier {
  List<Item> _items = [];
  bool _isLoading = false;
  String? _error;

  // Getters — never expose internal state directly
  List<Item> get items => List.unmodifiable(_items);
  bool get isLoading => _isLoading;
  String? get error => _error;
  bool get hasData => _items.isNotEmpty;

  Future<void> load() async {
    _isLoading = true;
    _error = null;
    notifyListeners();

    try {
      _items = await FeatureService.getAll();
    } catch (e) {
      _error = e.toString();
    } finally {
      _isLoading = false;
      notifyListeners();
    }
  }

  void clear() {
    _items = [];
    _error = null;
    notifyListeners();
  }
}

3.2 Provider Registration

// In main.dart or app.dart
MultiProvider(
  providers: [
    ChangeNotifierProvider(create: (_) => AuthProvider()),
    ChangeNotifierProvider(create: (_) => InvestmentProvider()),
    // Add providers as features grow
  ],
  child: const MyApp(),
)

3.3 Consuming in UI

// ✅ Consumer — rebuilds only this subtree
Consumer<FeatureProvider>(
  builder: (context, provider, child) {
    if (provider.isLoading) return const LoadingSkeleton();
    if (provider.error != null) return ErrorState(message: provider.error!);
    if (!provider.hasData) return const EmptyState();
    return ItemList(items: provider.items);
  },
)

// ✅ context.read — for one-time actions (button taps)
ElevatedButton(
  onPressed: () => context.read<FeatureProvider>().load(),
  child: const Text('Refresh'),
)

// ❌ NEVER use context.watch in callbacks
// ❌ NEVER use setState for shared state

4. Flavor / Environment Configuration

4.1 Flavor Enum

// lib/core/config/flavor_config.dart
enum AppFlavor {
  development,
  staging,
  production;

  // Per-flavor values
  String get firebaseFunctionsBaseUrl => switch (this) {
    development => 'http://localhost:5001/{project_id}/us-central1',
    staging => 'https://us-central1-{staging_project_id}.cloudfunctions.net',
    production => 'https://us-central1-{prod_project_id}.cloudfunctions.net',
  };

  String get appName => switch (this) {
    development => '{App Name} DEV',
    staging => '{App Name} STG',
    production => '{App Name}',
  };
}

4.2 Client/Brand Flavor (Multi-tenant)

For apps serving multiple clients with different branding:

// lib/core/config/brand_config.dart
enum AppBrand {
  brandA,
  brandB;

  // These values change per client
  String get displayName => switch (this) {
    brandA => 'Brand A Name',
    brandB => 'Brand B Name',
  };

  Color get primaryColor => switch (this) {
    brandA => const Color(0xFF1A237E),
    brandB => const Color(0xFF004D40),
  };

  Color get accentColor => switch (this) {
    brandA => const Color(0xFFFFB300),
    brandB => const Color(0xFF00BFA5),
  };

  String get logoAsset => switch (this) {
    brandA => 'assets/images/logo_brand_a.png',
    brandB => 'assets/images/logo_brand_b.png',
  };

  String get firebaseProjectId => switch (this) {
    brandA => '{firebase_project_id_a}',
    brandB => '{firebase_project_id_b}',
  };
}

4.3 Runtime Configuration with --dart-define

// lib/core/config/app_config.dart
class AppConfig {
  static late final AppFlavor flavor;
  static late final AppBrand brand;

  static void initialize() {
    // Read from --dart-define at compile time
    const flavorStr = String.fromEnvironment('FLAVOR', defaultValue: 'development');
    const brandStr = String.fromEnvironment('BRAND', defaultValue: 'brandA');

    flavor = AppFlavor.values.firstWhere(
      (f) => f.name == flavorStr,
      orElse: () => AppFlavor.development,
    );

    brand = AppBrand.values.firstWhere(
      (b) => b.name == brandStr,
      orElse: () => AppBrand.brandA,
    );
  }

  static String get functionsBaseUrl => flavor.firebaseFunctionsBaseUrl;
  static String get appName => '${brand.displayName}${flavor != AppFlavor.production ? ' (${flavor.name.toUpperCase()})' : ''}';
}

4.4 Running with Flavors

# Development + Brand A
flutter run --dart-define=FLAVOR=development --dart-define=BRAND=brandA

# Production + Brand B
flutter run --release --dart-define=FLAVOR=production --dart-define=BRAND=brandB

# Build APK for specific flavor
flutter build apk --release \
  --dart-define=FLAVOR=production \
  --dart-define=BRAND=brandB

4.5 Flavor-Specific Entry Points (Alternative)

// lib/main_brand_a.dart
void main() {
  AppConfig.initializeWith(brand: AppBrand.brandA, flavor: AppFlavor.production);
  runApp(const MyApp());
}

// lib/main_brand_b.dart
void main() {
  AppConfig.initializeWith(b

---

*Content truncated.*

When not to use it

  • When the project does not use Flutter
  • When the project does not use Firebase
  • When not building mobile applications

Limitations

  • The skill is project-agnostic and provides general guidance, not specific implementation code.
  • It focuses on Flutter and Firebase projects.
  • The skill does not provide specific versions for dependencies, requiring manual verification.

How it compares

This approach standardizes project organization and dependency flow, unlike ad-hoc development that can lead to inconsistent structures and tangled dependencies.

Compared to similar skills

flutter-mobile-design side by side with the closest alternatives in the catalog.

SkillInstallsUpdatedSafetyDifficulty
flutter-mobile-design (this skill)02moReviewIntermediate
mobile-design1494moReviewIntermediate
figma-import03moNo flagsAdvanced
iOS Animation Graphics Skill05moNo flagsIntermediate

Try saying

Example prompts that trigger this skill in your AI assistant.

Search skills

Search the agent skills registry