flutter

SKILL.md

Flutter

Cross-platform UI framework for mobile, web, and desktop.

When to Use

  • Cross-platform mobile apps
  • Single codebase for iOS/Android
  • Custom UI with animations
  • Rapid prototyping

Quick Start

import 'package:flutter/material.dart';

void main() => runApp(const MyApp());

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

  
  Widget build(BuildContext context) {
    return MaterialApp(
      home: Scaffold(
        appBar: AppBar(title: const Text('My App')),
        body: const Center(child: Text('Hello Flutter!')),
      ),
    );
  }
}

Core Concepts

Widgets & State

class Counter extends StatefulWidget {
  const Counter({super.key});

  
  State<Counter> createState() => _CounterState();
}

class _CounterState extends State<Counter> {
  int _count = 0;

  
  Widget build(BuildContext context) {
    return Column(
      children: [
        Text('Count: $_count'),
        ElevatedButton(
          onPressed: () => setState(() => _count++),
          child: const Text('Increment'),
        ),
      ],
    );
  }
}

Riverpod State Management

import 'package:flutter_riverpod/flutter_riverpod.dart';

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

class CounterNotifier extends StateNotifier<int> {
  CounterNotifier() : super(0);

  void increment() => state++;
}

// Usage
class MyWidget extends ConsumerWidget {
  
  Widget build(BuildContext context, WidgetRef ref) {
    final count = ref.watch(counterProvider);
    return Text('Count: $count');
  }
}

Common Patterns

Navigation with GoRouter

final router = GoRouter(
  routes: [
    GoRoute(
      path: '/',
      builder: (context, state) => const HomeScreen(),
      routes: [
        GoRoute(
          path: 'user/:id',
          builder: (context, state) => UserScreen(
            id: state.pathParameters['id']!,
          ),
        ),
      ],
    ),
  ],
);

// Navigate
context.go('/user/123');
context.push('/user/123');

Async Data

final userProvider = FutureProvider.autoDispose.family<User, String>(
  (ref, userId) async {
    return ref.read(apiProvider).fetchUser(userId);
  },
);

// In widget
ref.watch(userProvider(userId)).when(
  data: (user) => UserCard(user: user),
  loading: () => const CircularProgressIndicator(),
  error: (error, stack) => Text('Error: $error'),
);

Best Practices

Do:

  • Use const constructors
  • Prefer StatelessWidget
  • Use Riverpod for state
  • Follow widget composition

Don't:

  • Put logic in build methods
  • Use setState for global state
  • Create deep widget trees
  • Ignore key parameters

Troubleshooting

Issue Cause Solution
Widget not updating Missing setState Use proper state management
Overflow error Unbounded size Add constraints
Hot reload fails State corruption Hot restart

References

Weekly Installs
2
GitHub Stars
7
First Seen
Feb 10, 2026
Installed on
mcpjam2
claude-code2
replit2
junie2
windsurf2
zencoder2