Back to list
abhishekbrt

flutter-architecture

by abhishekbrt

glow state

0🍴 0📅 Jan 11, 2026

SKILL.md


name: flutter-architecture description: Feature-first architecture patterns for scalable Flutter apps. Covers project structure, dependency injection with Riverpod, repository pattern, and clean architecture layers. Use when setting up new projects, creating features, or making structural decisions.

Flutter Architecture

Core Principle

Feature-First, Layer-Second: Group by feature (auth, home, profile), then by layer (data, domain, presentation) within each feature.

When to Use What

DecisionGuidance
New projectStart with core/ + first feature folder
New featureCreate features/{name}/ with data/domain/presentation
Shared widgetOnly in core/widgets/ if used by 3+ features
Shared logiccore/ for network, error handling, providers

Detailed Guides

TopicGuideUse When
Project Structurefeature-first-structure.mdSetting up folders, creating features
Dependency Injectiondependency-injection.mdRiverpod DI, provider dependencies, testing
Repository Patternrepository-pattern.mdData layer, caching, error handling

Quick Reference

Feature Module Template

features/auth/
├── data/
│   ├── datasources/
│   │   ├── auth_remote_source.dart
│   │   └── auth_local_source.dart
│   ├── models/
│   │   └── user_model.dart           # JSON serialization
│   ├── repositories/
│   │   └── auth_repository_impl.dart  # Implements interface
│   └── providers/
│       └── auth_repository_provider.dart  # Riverpod provider
├── domain/
│   ├── entities/
│   │   └── user.dart                  # Pure business object
│   └── repositories/
│       └── auth_repository.dart       # Interface (abstract class)
└── presentation/
    ├── providers/
    │   └── auth_provider.dart         # State management (Riverpod)
    ├── screens/
    │   └── login_screen.dart
    └── widgets/
        └── login_form.dart

Interface-First Pattern (TDD)

// 1. Define interface in domain/repositories/
abstract class AuthRepository {
  Future<User> login(String email, String password);
  Future<void> logout();
  Future<User?> getCurrentUser();
}

// 2. Create implementation shell in data/repositories/
class AuthRepositoryImpl implements AuthRepository {
  final AuthRemoteSource _remoteSource;
  final AuthLocalSource _localSource;

  AuthRepositoryImpl({
    required AuthRemoteSource remoteSource,
    required AuthLocalSource localSource,
  }) : _remoteSource = remoteSource,
       _localSource = localSource;

  @override
  Future<User> login(String email, String password) {
    throw UnimplementedError(); // RED phase
  }
  
  // ... other methods
}

Provider Registration (Riverpod)

// features/auth/data/providers/auth_repository_provider.dart
import 'package:riverpod_annotation/riverpod_annotation.dart';

part 'auth_repository_provider.g.dart';

@riverpod
AuthRepository authRepository(Ref ref) {
  return AuthRepositoryImpl(
    remoteSource: ref.watch(authRemoteSourceProvider),
    localSource: ref.watch(authLocalSourceProvider),
  );
}

// features/auth/presentation/providers/auth_provider.dart
@riverpod
class Auth extends _$Auth {
  @override
  FutureOr<User?> build() async {
    final repo = ref.watch(authRepositoryProvider);
    return repo.getCurrentUser();
  }

  Future<void> login(String email, String password) async {
    state = const AsyncLoading();
    state = await AsyncValue.guard(() async {
      return ref.read(authRepositoryProvider).login(email, password);
    });
  }

  Future<void> logout() async {
    await ref.read(authRepositoryProvider).logout();
    state = const AsyncData(null);
  }
}

Alternative: BLoC with GetIt

For complex event-driven features, use BLoC pattern with GetIt:

// core/di/injection.dart (alternative approach)
import 'package:get_it/get_it.dart';

final sl = GetIt.instance;

void configureDependencies() {
  // Core
  sl.registerLazySingleton<ApiClient>(() => ApiClient());
  
  // Feature: Auth
  _initAuth();
}

void _initAuth() {
  sl.registerLazySingleton<AuthRemoteSource>(
    () => AuthRemoteSource(client: sl()),
  );
  sl.registerLazySingleton<AuthRepository>(
    () => AuthRepositoryImpl(remoteSource: sl(), localSource: sl()),
  );
  sl.registerFactory<AuthBloc>(
    () => AuthBloc(repository: sl()),
  );
}

State Management Choice

ScenarioUse
Most featuresRiverpod AsyncNotifierProvider
Simple sync stateRiverpod NotifierProvider
Complex event flowsBLoC (with GetIt DI)

See state-management/SKILL.md for detailed guidance.

Anti-Patterns

AvoidInstead
Widget directly calling APIWidget → Provider → Repository → DataSource
Business logic in widgetsMove to Notifier/BLoC
Concrete class dependenciesDepend on abstract interfaces
Circular feature dependenciesExtract shared code to core/
God objects (one class does everything)Single responsibility per class
Passing ref to business classesInject dependencies directly

Score

Total Score

50/100

Based on repository quality metrics

SKILL.md

SKILL.mdファイルが含まれている

+20
LICENSE

ライセンスが設定されている

0/10
説明文

100文字以上の説明がある

0/10
人気

GitHub Stars 100以上

0/15
最近の活動

3ヶ月以内に更新がある

0/10
フォーク

10回以上フォークされている

0/5
Issue管理

オープンIssueが50未満

+5
言語

プログラミング言語が設定されている

+5
タグ

1つ以上のタグが設定されている

0/5

Reviews

💬

Reviews coming soon