MCP HubMCP Hub
Zurück zu Fähigkeiten

moai-lang-flutter

modu-ai
Aktualisiert Yesterday
153 Ansichten
424
78
424
Auf GitHub ansehen
Andereflutterdartriverpodcross-platformmobiledesktop

Über

Diese Claude-Spezialisierung konzentriert sich auf die Entwicklung mit Flutter 3.24+ und Dart 3.5+, mit Schwerpunkt auf modernem State Management mit Riverpod und deklarativer Navigation mittels go_router. Sie ist für den Bau plattformübergreifender Anwendungen für Mobilgeräte, Desktop und Web konzipiert. Nutzen Sie diese Spezialisierung für Anleitungen zu adaptiven Layouts, den neuesten Sprachfunktionen von Dart und zur Integration plattformspezifischer Funktionalitäten.

Schnellinstallation

Claude Code

Empfohlen
Primär
npx skills add modu-ai/moai-adk
Plugin-BefehlAlternativ
/plugin add https://github.com/modu-ai/moai-adk
Git CloneAlternativ
git clone https://github.com/modu-ai/moai-adk.git ~/.claude/skills/moai-lang-flutter

Kopieren Sie diesen Befehl und fügen Sie ihn in Claude Code ein, um diese Fähigkeit zu installieren

Dokumentation

Quick Reference (30 seconds)

Flutter/Dart Development Expert - Dart 3.5+, Flutter 3.24+ with modern patterns.

Auto-Triggers: Flutter projects (.dart files, pubspec.yaml), cross-platform apps, widget development

Core Capabilities:

  • Dart 3.5: Patterns, records, sealed classes, extension types
  • Flutter 3.24: Widget tree, Material 3, adaptive layouts
  • Riverpod: State management with code generation
  • go_router: Declarative navigation and deep linking
  • Platform Channels: Native iOS/Android integration
  • Testing: flutter_test, widget_test, integration_test

Implementation Guide (5 minutes)

Dart 3.5 Language Features

Pattern Matching with Sealed Classes:

sealed class Result<T> {
  const Result();
}
class Success<T> extends Result<T> {
  final T data;
  const Success(this.data);
}
class Failure<T> extends Result<T> {
  final String error;
  const Failure(this.error);
}

// Exhaustive switch expression
String handleResult(Result<User> result) => switch (result) {
  Success(:final data) => 'User: ${data.name}',
  Failure(:final error) => 'Error: $error',
};

// Guard clauses in patterns
String describeUser(User user) => switch (user) {
  User(age: var a) when a < 18 => 'Minor',
  User(age: var a) when a >= 65 => 'Senior',
  User(name: var n, age: var a) => '$n, age $a',
};

Records and Destructuring:

typedef UserRecord = ({String name, int age, String email});

// Multiple return values
(String name, int age) parseUser(Map<String, dynamic> json) {
  return (json['name'] as String, json['age'] as int);
}

// Destructuring
void processUser(Map<String, dynamic> json) {
  final (name, age) = parseUser(json);
  print('$name is $age years old');
}

// Record patterns in collections
void processUsers(List<UserRecord> users) {
  for (final (:name, :age, :email) in users) {
    print('$name ($age): $email');
  }
}

Extension Types:

extension type UserId(String value) {
  factory UserId.generate() => UserId(Uuid().v4());
  bool get isValid => value.isNotEmpty;
}

extension type Email(String value) {
  bool get isValid => value.contains('@') && value.contains('.');
  String get domain => value.split('@').last;
}

Riverpod State Management

Provider Definitions:

import 'package:riverpod_annotation/riverpod_annotation.dart';
part 'providers.g.dart';

@riverpod
UserRepository userRepository(Ref ref) {
  return UserRepository(ref.read(dioProvider));
}

@riverpod
Future<User> user(Ref ref, String userId) async {
  return ref.watch(userRepositoryProvider).getUser(userId);
}

@riverpod
class UserNotifier extends _$UserNotifier {
  @override
  FutureOr<User?> build() => null;

  Future<void> loadUser(String id) async {
    state = const AsyncLoading();
    state = await AsyncValue.guard(
      () => ref.read(userRepositoryProvider).getUser(id),
    );
  }

  Future<void> updateUser(User user) async {
    state = const AsyncLoading();
    state = await AsyncValue.guard(
      () => ref.read(userRepositoryProvider).updateUser(user),
    );
  }
}

@riverpod
Stream<List<Message>> messages(Ref ref, String chatId) {
  return ref.watch(chatRepositoryProvider).watchMessages(chatId);
}

Widget Integration:

class UserScreen extends ConsumerWidget {
  final String userId;
  const UserScreen({required this.userId, super.key});

  @override
  Widget build(BuildContext context, WidgetRef ref) {
    final userAsync = ref.watch(userProvider(userId));

    return Scaffold(
      appBar: AppBar(title: const Text('User Profile')),
      body: userAsync.when(
        data: (user) => UserProfile(user: user),
        loading: () => const Center(child: CircularProgressIndicator()),
        error: (error, stack) => Center(
          child: Column(
            mainAxisAlignment: MainAxisAlignment.center,
            children: [
              Text('Error: $error'),
              ElevatedButton(
                onPressed: () => ref.invalidate(userProvider(userId)),
                child: const Text('Retry'),
              ),
            ],
          ),
        ),
      ),
    );
  }
}

StatefulWidget with Riverpod:

class EditUserScreen extends ConsumerStatefulWidget {
  const EditUserScreen({super.key});
  @override
  ConsumerState<EditUserScreen> createState() => _EditUserScreenState();
}

class _EditUserScreenState extends ConsumerState<EditUserScreen> {
  final _formKey = GlobalKey<FormState>();
  late TextEditingController _nameController;

  @override
  void initState() {
    super.initState();
    _nameController = TextEditingController();
  }

  @override
  void dispose() {
    _nameController.dispose();
    super.dispose();
  }

  @override
  Widget build(BuildContext context) {
    ref.listen(userNotifierProvider, (prev, next) {
      next.whenOrNull(
        error: (e, _) => ScaffoldMessenger.of(context).showSnackBar(
          SnackBar(content: Text('Error: $e')),
        ),
      );
    });

    final isLoading = ref.watch(userNotifierProvider).isLoading;

    return Form(
      key: _formKey,
      child: Column(
        children: [
          TextFormField(
            controller: _nameController,
            validator: (v) => v?.isEmpty ?? true ? 'Required' : null,
          ),
          ElevatedButton(
            onPressed: isLoading ? null : () async {
              if (_formKey.currentState!.validate()) {
                await ref.read(userNotifierProvider.notifier)
                    .updateUser(User(name: _nameController.text));
              }
            },
            child: isLoading
                ? const CircularProgressIndicator()
                : const Text('Save'),
          ),
        ],
      ),
    );
  }
}

go_router Navigation

Router Configuration:

final router = GoRouter(
  initialLocation: '/',
  routes: [
    GoRoute(
      path: '/',
      name: 'home',
      builder: (context, state) => const HomeScreen(),
      routes: [
        GoRoute(
          path: 'user/:id',
          name: 'user-detail',
          builder: (context, state) => UserDetailScreen(
            userId: state.pathParameters['id']!,
          ),
        ),
      ],
    ),
    ShellRoute(
      builder: (context, state, child) => MainShell(child: child),
      routes: [
        GoRoute(
          path: '/feed',
          pageBuilder: (_, __) => const NoTransitionPage(child: FeedScreen()),
        ),
        GoRoute(
          path: '/search',
          pageBuilder: (_, __) => const NoTransitionPage(child: SearchScreen()),
        ),
        GoRoute(
          path: '/profile',
          pageBuilder: (_, __) => const NoTransitionPage(child: ProfileScreen()),
        ),
      ],
    ),
  ],
  redirect: (context, state) {
    final isLoggedIn = authNotifier.isLoggedIn;
    final isLoggingIn = state.matchedLocation == '/login';
    if (!isLoggedIn && !isLoggingIn) return '/login';
    if (isLoggedIn && isLoggingIn) return '/';
    return null;
  },
  errorBuilder: (context, state) => ErrorScreen(error: state.error),
);

// Navigation methods
void navigateToUser(BuildContext context, String userId) {
  context.go('/user/$userId');
}

void goBack(BuildContext context) {
  if (context.canPop()) context.pop();
  else context.go('/');
}

Platform Channels

Dart Implementation:

class NativeBridge {
  static const _channel = MethodChannel('com.example.app/native');
  static const _eventChannel = EventChannel('com.example.app/events');

  Future<String> getPlatformVersion() async {
    try {
      final version = await _channel.invokeMethod<String>('getPlatformVersion');
      return version ?? 'Unknown';
    } on PlatformException catch (e) {
      throw NativeBridgeException('Failed: ${e.message}');
    }
  }

  Future<void> shareContent({required String text, String? title}) async {
    await _channel.invokeMethod('share', {
      'text': text,
      if (title != null) 'title': title,
    });
  }

  Stream<BatteryState> watchBatteryState() {
    return _eventChannel.receiveBroadcastStream().map((event) {
      final data = event as Map<dynamic, dynamic>;
      return BatteryState(
        level: data['level'] as int,
        isCharging: data['isCharging'] as bool,
      );
    });
  }

  void setupMethodCallHandler() {
    _channel.setMethodCallHandler((call) async {
      switch (call.method) {
        case 'onNativeEvent':
          // Handle native event
          return true;
        default:
          throw MissingPluginException('Not implemented: ${call.method}');
      }
    });
  }
}

class BatteryState {
  final int level;
  final bool isCharging;
  const BatteryState({required this.level, required this.isCharging});
}

Widget Patterns

Adaptive Layouts:

class AdaptiveScaffold extends StatelessWidget {
  final Widget child;
  final List<NavigationDestination> destinations;
  final int selectedIndex;
  final ValueChanged<int> onDestinationSelected;

  const AdaptiveScaffold({
    required this.child,
    required this.destinations,
    required this.selectedIndex,
    required this.onDestinationSelected,
    super.key,
  });

  @override
  Widget build(BuildContext context) {
    final width = MediaQuery.sizeOf(context).width;

    if (width < 600) {
      return Scaffold(
        body: child,
        bottomNavigationBar: NavigationBar(
          selectedIndex: selectedIndex,
          onDestinationSelected: onDestinationSelected,
          destinations: destinations,
        ),
      );
    }

    if (width < 840) {
      return Scaffold(
        body: Row(children: [
          NavigationRail(
            selectedIndex: selectedIndex,
            onDestinationSelected: onDestinationSelected,
            destinations: destinations.map((d) => NavigationRailDestination(
              icon: d.icon, selectedIcon: d.selectedIcon, label: Text(d.label),
            )).toList(),
          ),
          const VerticalDivider(thickness: 1, width: 1),
          Expanded(child: child),
        ]),
      );
    }

    return Scaffold(
      body: Row(children: [
        NavigationDrawer(
          selectedIndex: selectedIndex,
          onDestinationSelected: onDestinationSelected,
          children: destinations.map((d) => NavigationDrawerDestination(
            icon: d.icon, selectedIcon: d.selectedIcon ?? d.icon, label: Text(d.label),
          )).toList(),
        ),
        const VerticalDivider(thickness: 1, width: 1),
        Expanded(child: child),
      ]),
    );
  }
}

Testing

Widget Test Example:

void main() {
  testWidgets('UserScreen displays data', (tester) async {
    final container = ProviderContainer(overrides: [
      userRepositoryProvider.overrideWithValue(MockUserRepository()),
    ]);

    await tester.pumpWidget(
      UncontrolledProviderScope(
        container: container,
        child: const MaterialApp(home: UserScreen(userId: '1')),
      ),
    );

    expect(find.byType(CircularProgressIndicator), findsOneWidget);
    await tester.pumpAndSettle();
    expect(find.text('Test User'), findsOneWidget);
  });
}

For comprehensive testing patterns, see examples.md.

Advanced Patterns

For comprehensive coverage including:

  • Clean Architecture with Riverpod
  • Isolates for compute-heavy operations
  • Custom render objects and painting
  • FFI and platform-specific plugins
  • Performance optimization and profiling

See: reference.md and examples.md

Context7 Library Mappings

Flutter/Dart Core:

  • /flutter/flutter - Flutter framework
  • /dart-lang/sdk - Dart SDK

State Management:

  • /rrousselGit/riverpod - Riverpod state management
  • /felangel/bloc - BLoC pattern

Navigation and Storage:

  • /flutter/packages - go_router and official packages
  • /cfug/dio - HTTP client
  • /isar/isar - NoSQL database

Works Well With

  • moai-lang-swift - iOS native integration for platform channels
  • moai-lang-kotlin - Android native integration for platform channels
  • moai-domain-backend - API integration and backend communication
  • moai-quality-security - Mobile security best practices
  • moai-essentials-debug - Flutter debugging and DevTools

Version: 1.0.0 Last Updated: 2025-12-07 Status: Production Ready

GitHub Repository

modu-ai/moai-adk
Pfad: src/moai_adk/templates/.claude/skills/moai-lang-flutter
agentic-aiagentic-codingagentic-workflowclaudeclaudecodevibe-coding

Verwandte Skills

mobile-testing

Andere

This Claude Skill provides comprehensive mobile testing for iOS and Android applications, covering gestures, sensors, permissions, and device fragmentation. Use it when testing native, hybrid, or mobile web apps to ensure quality across 1000+ device variants. It helps define device coverage matrices and test key platform differences.

Skill ansehen

moai-domain-mobile-app

Testen

This Claude Skill provides enterprise mobile development expertise for React Native 0.76+, Flutter 3.24+, and Capacitor 6.x cross-platform frameworks. It focuses on implementing robust patterns, comprehensive testing, and CI/CD automation for production-ready mobile applications. Use this skill for guidance on mobile architecture, performance optimization, and deployment strategies.

Skill ansehen

rust-desktop-applications

Entwicklung

This skill helps developers build cross-platform desktop applications using Rust, primarily through the Tauri framework or native GUI alternatives. It's ideal for creating performant apps requiring system integration, small bundle sizes, and high performance. The guidance covers project setup, IPC, state management, and cross-platform testing and deployment.

Skill ansehen

moai-domain-mobile-app

Testen

This Claude Skill provides enterprise mobile development expertise for React Native 0.76+, Flutter 3.24+, and Capacitor 6.x frameworks. It focuses on cross-platform patterns, testing strategies, and CI/CD automation for production-ready applications. Use this skill for guidance on modern mobile development workflows and deployment best practices.

Skill ansehen