Files
minimax-skills/skills/flutter-dev/references/widget-patterns.md
mljxxx 2995582a5e feat: add flutter-dev skill
Add comprehensive Flutter cross-platform development guide covering:
- Widget patterns and const optimization
- Riverpod/Bloc state management
- GoRouter navigation
- Performance optimization
- Testing strategies
- Platform-specific implementations

Made-with: Cursor
2026-03-26 17:35:55 +08:00

5.1 KiB

Widget Patterns

Flutter widget best practices covering const optimization, responsive layouts, hooks, and sliver patterns.

Optimized Widget Pattern

Always use const constructors for static widgets to prevent unnecessary rebuilds:

class OptimizedCard extends StatelessWidget {
  final String title;
  final VoidCallback onTap;

  const OptimizedCard({
    super.key,
    required this.title,
    required this.onTap,
  });

  @override
  Widget build(BuildContext context) {
    return Card(
      child: InkWell(
        onTap: onTap,
        child: Padding(
          padding: const EdgeInsets.all(16),
          child: Text(title, style: Theme.of(context).textTheme.titleMedium),
        ),
      ),
    );
  }
}

Extracting Const Widgets

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

  @override
  Widget build(BuildContext context) {
    return Column(
      children: const [
        _Header(),
        _Body(),
        _Footer(),
      ],
    );
  }
}

class _Header extends StatelessWidget {
  const _Header();

  @override
  Widget build(BuildContext context) {
    return const Text('Header');
  }
}

Responsive Layout

class ResponsiveLayout extends StatelessWidget {
  final Widget mobile;
  final Widget? tablet;
  final Widget desktop;

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

  static const double mobileBreakpoint = 650;
  static const double desktopBreakpoint = 1100;

  @override
  Widget build(BuildContext context) {
    return LayoutBuilder(
      builder: (context, constraints) {
        if (constraints.maxWidth >= desktopBreakpoint) return desktop;
        if (constraints.maxWidth >= mobileBreakpoint) return tablet ?? mobile;
        return mobile;
      },
    );
  }
}

Breakpoint Reference

Type Width Usage
Mobile < 650pt Single column, bottom nav
Tablet 650-1100pt Two columns, side nav optional
Desktop > 1100pt Multi-column, persistent nav

Custom Hooks (flutter_hooks)

import 'package:flutter_hooks/flutter_hooks.dart';

class CounterWidget extends HookWidget {
  const CounterWidget({super.key});

  @override
  Widget build(BuildContext context) {
    final counter = useState(0);
    final controller = useTextEditingController();
    final isMounted = useIsMounted();

    useEffect(() {
      debugPrint('Widget mounted');
      return () {
        debugPrint('Widget disposed');
      };
    }, const []);

    return Column(
      children: [
        Text('Count: ${counter.value}'),
        ElevatedButton(
          onPressed: () => counter.value++,
          child: const Text('Increment'),
        ),
        TextField(controller: controller),
      ],
    );
  }
}

Common Hooks

Hook Purpose
useState Local state management
useEffect Side effects with cleanup
useMemoized Expensive computation caching
useTextEditingController Text field controller
useAnimationController Animation controller
useFocusNode Focus management
useIsMounted Check if widget is mounted

Sliver Patterns

CustomScrollView(
  slivers: [
    SliverAppBar(
      expandedHeight: 200,
      pinned: true,
      flexibleSpace: FlexibleSpaceBar(
        title: const Text('Title'),
        background: Image.network(imageUrl, fit: BoxFit.cover),
      ),
    ),
    SliverPadding(
      padding: const EdgeInsets.all(16),
      sliver: SliverList(
        delegate: SliverChildBuilderDelegate(
          (context, index) => ListTile(
            key: ValueKey(items[index].id),
            title: Text(items[index].title),
          ),
          childCount: items.length,
        ),
      ),
    ),
    const SliverToBoxAdapter(
      child: Padding(
        padding: EdgeInsets.all(16),
        child: Text('Footer'),
      ),
    ),
  ],
)

Sliver Types

Sliver Usage
SliverAppBar Collapsing app bar
SliverList Lazy list
SliverGrid Lazy grid
SliverToBoxAdapter Single non-sliver widget
SliverPadding Add padding to sliver
SliverFillRemaining Fill remaining space

Key Usage Patterns

ListView.builder(
  itemCount: items.length,
  itemBuilder: (context, index) {
    final item = items[index];
    return Dismissible(
      key: ValueKey(item.id),
      child: ListTile(
        key: ValueKey('tile_${item.id}'),
        title: Text(item.title),
      ),
    );
  },
)
Key Type When to Use
ValueKey Unique ID available
ObjectKey Object identity matters
UniqueKey Force rebuild
GlobalKey Access state across tree

Optimization Checklist

Pattern Implementation
const widgets Add const to static widgets
Keys Use ValueKey for list items
Select ref.watch(provider.select(...))
RepaintBoundary Isolate expensive repaints
ListView.builder Lazy loading for lists
const constructors Always use when possible

Flutter and Material Design are trademarks of Google LLC.