Skip to content
Back to skills

Flutter Patterns

ASecurity

Flutter/Dart: widgets, state mgmt (Riverpod/Bloc), navigation, platform channels. Triggers: Flutter, Dart, widget, Riverpod, Bloc, pubspec, hot reload.

  • 177 stars
  • 0 votes
  • 0 copies
  • 1 view
  • Added May 27, 2026
data-aigotestingperformance

Security analysis

A100/100

Scanned May 27, 2026

npx -y skills add softspark/ai-toolkit --skill flutter-patterns --agent claude-code

Installs into .claude/skills of the current project.

Are you the author of Flutter Patterns?

Add the live security badge to your README. It updates with every re-scan.

Security grade badge for Flutter Patterns
[![Security: A — Skills Directory](https://www.skillsdirectory.com/api/skills/softspark-flutter-patterns/badge)](https://www.skillsdirectory.com/skills/softspark-flutter-patterns)

More formats (shields.io, HTML) on the badges page. Keep it an A: scan every change in CI with Pro.

Download with Pro
SKILL.md
---
name: flutter-patterns
description: "Flutter/Dart: widgets, state mgmt (Riverpod/Bloc), navigation, platform channels. Triggers: Flutter, Dart, widget, Riverpod, Bloc, pubspec, hot reload."
effort: medium
user-invocable: false
allowed-tools: Read
---

# Flutter Patterns Skill

## Project Structure

```
lib/
├── main.dart
├── app.dart
├── core/
│   ├── constants/
│   ├── errors/
│   ├── network/
│   └── utils/
├── features/
│   └── feature_name/
│       ├── data/
│       │   ├── models/
│       │   ├── repositories/
│       │   └── sources/
│       ├── domain/
│       │   ├── entities/
│       │   └── usecases/
│       └── presentation/
│           ├── bloc/
│           ├── pages/
│           └── widgets/
└── shared/
    ├── widgets/
    └── theme/
```

---

## State Management

### BLoC Pattern
```dart
// Event
abstract class AuthEvent {}
class LoginRequested extends AuthEvent {
  final String email;
  final String password;
  LoginRequested(this.email, this.password);
}

// State
abstract class AuthState {}
class AuthInitial extends AuthState {}
class AuthLoading extends AuthState {}
class AuthSuccess extends AuthState {
  final User user;
  AuthSuccess(this.user);
}
class AuthFailure extends AuthState {
  final String message;
  AuthFailure(this.message);
}

// BLoC
class AuthBloc extends Bloc<AuthEvent, AuthState> {
  AuthBloc() : super(AuthInitial()) {
    on<LoginRequested>(_onLoginRequested);
  }

  Future<void> _onLoginRequested(
    LoginRequested event,
    Emitter<AuthState> emit,
  ) async {
    emit(AuthLoading());
    try {
      final user = await authRepository.login(event.email, event.password);
      emit(AuthSuccess(user));
    } catch (e) {
      emit(AuthFailure(e.toString()));
    }
  }
}
```

### Riverpod
```dart
final userProvider = FutureProvider<User>((ref) async {
  final repository = ref.watch(userRepositoryProvider);
  return repository.getUser();
});

// Usage
Consumer(
  builder: (context, ref, child) {
    final userAsync = ref.watch(userProvider);
    return userAsync.when(
      data: (user) => Text(user.name),
      loading: () => CircularProgressIndicator(),
      error: (e, s) => Text('Error: $e'),
    );
  },
)
```

---

## Navigation (GoRouter)

```dart
final router = GoRouter(
  routes: [
    GoRoute(
      path: '/',
      builder: (context, state) => HomeScreen(),
      routes: [
        GoRoute(
          path: 'details/:id',
          builder: (context, state) => DetailsScreen(
            id: state.pathParameters['id']!,
          ),
        ),
      ],
    ),
  ],
);

// Navigation
context.go('/details/123');
context.push('/details/123');
context.pop();
```

---

## Widget Patterns

### Responsive Layout
```dart
class ResponsiveLayout extends StatelessWidget {
  final Widget mobile;
  final Widget? tablet;
  final Widget desktop;

  @override
  Widget build(BuildContext context) {
    return LayoutBuilder(
      builder: (context, constraints) {
        if (constraints.maxWidth < 600) {
          return mobile;
        } else if (constraints.maxWidth < 1200) {
          return tablet ?? desktop;
        }
        return desktop;
      },
    );
  }
}
```

### Sliver Patterns
```dart
CustomScrollView(
  slivers: [
    SliverAppBar(
      floating: true,
      title: Text('Title'),
    ),
    SliverPadding(
      padding: EdgeInsets.all(16),
      sliver: SliverList(
        delegate: SliverChildBuilderDelegate(
          (context, index) => ListTile(title: Text('Item $index')),
          childCount: 100,
        ),
      ),
    ),
  ],
)
```

---

## Performance

### const Constructors
```dart
// Good
const Text('Hello');
const SizedBox(height: 8);

// Widget with const constructor
class MyWidget extends StatelessWidget {
  const MyWidget({super.key});
}
```

### RepaintBoundary
```dart
RepaintBoundary(
  child: ComplexAnimatedWidget(),
)
```

### Image Optimization
```dart
Image.network(
  url,
  cacheWidth: 200,  // Resize in memory
  cacheHeight: 200,
)
```

---

## Testing

### Widget Testing
```dart
testWidgets('Counter increments', (tester) async {
  await tester.pumpWidget(MyApp());

  expect(find.text('0'), findsOneWidget);

  await tester.tap(find.byIcon(Icons.add));
  await tester.pump();

  expect(find.text('1'), findsOneWidget);
});
```

### BLoC Testing
```dart
blocTest<AuthBloc, AuthState>(
  'emits [loading, success] when login succeeds',
  build: () => AuthBloc(),
  act: (bloc) => bloc.add(LoginRequested('email', 'pass')),
  expect: () => [AuthLoading(), isA<AuthSuccess>()],
);
```

---

## Best Practices

- ✅ Use const constructors
- ✅ Extract widgets to separate files
- ✅ Use named routes
- ✅ Implement proper error handling
- ✅ Use proper state management
- ❌ Avoid setState for complex state
- ❌ Don't nest too many widgets
- ❌ Avoid magic numbers

Attribution

Is this your skill, or is something wrong with this listing? Request removal or report an issue. Author removals are honored within 72 hours.

Comments

Loading comments…