AgentStack
SKILL verified MIT Self-run

Flutter Patterns

skill-softspark-ai-toolkit-flutter-patterns · by softspark

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

No reviews yet
0 installs
21 views
0.0% view→install

Install

$ agentstack add skill-softspark-ai-toolkit-flutter-patterns

✓ scanned · ✓ verified — works with Claude Code, Cursor, and more.

Security review

✓ Passed

No issues found. Passed automated security review. · v0.1.0 How review works →

  • Prompt-injection patterns
  • Secret / credential exfiltration
  • Dangerous shell & filesystem operations
  • Untrusted network calls
  • Known-malicious package signatures

What it can access

  • Network access No
  • Filesystem access No
  • Shell / process execution No
  • Environment & secrets No
  • Dynamic code execution No

From automated source analysis of v0.1.0. “Used” means the capability is present in the source — more access means more to trust, not that it’s unsafe.

Are you the author of Flutter Patterns? Claim this listing to set pricing, connect Stripe payouts, and keep 70% of every sale.
Sign up to claim

About

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

// 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 {
  AuthBloc() : super(AuthInitial()) {
    on(_onLoginRequested);
  }

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

Riverpod

final userProvider = FutureProvider((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)

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

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  ListTile(title: Text('Item $index')),
          childCount: 100,
        ),
      ),
    ),
  ],
)

Performance

const Constructors

// Good
const Text('Hello');
const SizedBox(height: 8);

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

RepaintBoundary

RepaintBoundary(
  child: ComplexAnimatedWidget(),
)

Image Optimization

Image.network(
  url,
  cacheWidth: 200,  // Resize in memory
  cacheHeight: 200,
)

Testing

Widget Testing

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

blocTest(
  'emits [loading, success] when login succeeds',
  build: () => AuthBloc(),
  act: (bloc) => bloc.add(LoginRequested('email', 'pass')),
  expect: () => [AuthLoading(), isA()],
);

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

Source & license

This open-source skill is cataloged on AgentStack and links to its original source — we do not rehost the code.

Install and usage instructions live in the source repository linked above.

Reviews

No reviews yet — be the first.

Versions

  • v0.1.0 Imported from the upstream source.