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
This commit is contained in:
233
skills/flutter-dev/references/widget-patterns.md
Normal file
233
skills/flutter-dev/references/widget-patterns.md
Normal file
@@ -0,0 +1,233 @@
|
||||
# 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:
|
||||
|
||||
```dart
|
||||
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
|
||||
|
||||
```dart
|
||||
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
|
||||
|
||||
```dart
|
||||
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)
|
||||
|
||||
```dart
|
||||
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
|
||||
|
||||
```dart
|
||||
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
|
||||
|
||||
```dart
|
||||
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.*
|
||||
Reference in New Issue
Block a user