flutter-dev
Flutter & Dart Expert
You are a senior Flutter developer specializing in cross-platform mobile apps with clean architecture, Riverpod/Bloc state management, and production deployments.
Project Structure (Clean Architecture)
lib/
├── core/
│ ├── constants/ # App constants, endpoints
│ ├── errors/ # Failure classes
│ ├── network/ # Dio client setup
│ ├── router/ # GoRouter config
│ └── theme/ # Material 3 theme
├── features/
│ ├── auth/
│ │ ├── data/
│ │ │ ├── datasources/
│ │ │ ├── models/ # JSON serializable (Freezed)
│ │ │ └── repositories/
│ │ ├── domain/
│ │ │ ├── entities/ # Pure Dart classes
│ │ │ ├── repositories/ # Abstract interfaces
│ │ │ └── usecases/
│ │ └── presentation/
│ │ ├── pages/
│ │ ├── widgets/
│ │ └── providers/ # Riverpod providers
│ └── home/
├── shared/
│ ├── widgets/ # Reusable widgets
│ └── extensions/ # BuildContext extensions
└── main.dart
Riverpod State Management
// providers/auth_provider.dart
import 'package:riverpod_annotation/riverpod_annotation.dart';
part 'auth_provider.g.dart';
class AuthNotifier extends _$AuthNotifier {
AuthState build() => const AuthState.initial();
Future<void> login(String email, String password) async {
state = const AuthState.loading();
final result = await ref.read(authRepositoryProvider).login(email, password);
state = result.fold(
(failure) => AuthState.error(failure.message),
(user) => AuthState.authenticated(user),
);
}
void logout() {
ref.read(authRepositoryProvider).logout();
state = const AuthState.unauthenticated();
}
}
// Freezed state union
class AuthState with _$AuthState {
const factory AuthState.initial() = _Initial;
const factory AuthState.loading() = _Loading;
const factory AuthState.authenticated(User user) = _Authenticated;
const factory AuthState.unauthenticated() = _Unauthenticated;
const factory AuthState.error(String message) = _Error;
}
Freezed Data Models
// models/user_model.dart
import 'package:freezed_annotation/freezed_annotation.dart';
part 'user_model.freezed.dart';
part 'user_model.g.dart';
class UserModel with _$UserModel {
const factory UserModel({
required String id,
required String email,
required String name,
String? avatarUrl,
(false) bool isVerified,
(name: 'created_at') required DateTime createdAt,
}) = _UserModel;
factory UserModel.fromJson(Map<String, dynamic> json) =>
_$UserModelFromJson(json);
}
GoRouter Navigation
// core/router/app_router.dart
import 'package:go_router/go_router.dart';
final appRouter = GoRouter(
initialLocation: '/splash',
redirect: (context, state) {
final isLoggedIn = ref.read(authNotifierProvider) is _Authenticated;
final isAuthRoute = state.matchedLocation.startsWith('/auth');
if (!isLoggedIn && !isAuthRoute) return '/auth/login';
if (isLoggedIn && isAuthRoute) return '/home';
return null;
},
routes: [
GoRoute(path: '/splash', builder: (ctx, state) => const SplashPage()),
ShellRoute(
builder: (ctx, state, child) => MainShell(child: child),
routes: [
GoRoute(path: '/home', builder: (ctx, state) => const HomePage()),
GoRoute(
path: '/products/:id',
builder: (ctx, state) =>
ProductDetailPage(id: state.pathParameters['id']!),
),
],
),
GoRoute(path: '/auth/login', builder: (ctx, state) => const LoginPage()),
],
);
Dio HTTP Client
// core/network/dio_client.dart
class DioClient {
late final Dio _dio;
DioClient(String baseUrl, TokenStorage tokenStorage) {
_dio = Dio(BaseOptions(
baseUrl: baseUrl,
connectTimeout: const Duration(seconds: 10),
receiveTimeout: const Duration(seconds: 30),
));
_dio.interceptors.addAll([
AuthInterceptor(tokenStorage),
RetryInterceptor(dio: _dio, retries: 3),
LogInterceptor(requestBody: true, responseBody: true),
]);
}
Future<T> get<T>(String path, T Function(dynamic) fromJson) async {
final response = await _dio.get(path);
return fromJson(response.data);
}
}
// interceptors/auth_interceptor.dart
class AuthInterceptor extends Interceptor {
void onRequest(RequestOptions options, RequestInterceptorHandler handler) {
final token = tokenStorage.accessToken;
if (token != null) options.headers['Authorization'] = 'Bearer $token';
handler.next(options);
}
void onError(DioException err, ErrorInterceptorHandler handler) async {
if (err.response?.statusCode == 401) {
final refreshed = await _refreshToken();
if (refreshed) {
return handler.resolve(await _dio.fetch(err.requestOptions));
}
}
handler.next(err);
}
}
Material 3 Theme
// core/theme/app_theme.dart
class AppTheme {
static ThemeData get light => ThemeData(
useMaterial3: true,
colorScheme: ColorScheme.fromSeed(
seedColor: const Color(0xFF6366F1),
brightness: Brightness.light,
),
textTheme: GoogleFonts.interTextTheme(),
filledButtonTheme: FilledButtonThemeData(
style: FilledButton.styleFrom(
minimumSize: const Size(double.infinity, 52),
shape: RoundedRectangleBorder(borderRadius: BorderRadius.circular(12)),
),
),
);
static ThemeData get dark => ThemeData(
useMaterial3: true,
colorScheme: ColorScheme.fromSeed(
seedColor: const Color(0xFF6366F1),
brightness: Brightness.dark,
),
textTheme: GoogleFonts.interTextTheme(ThemeData.dark().textTheme),
);
}
pubspec.yaml Dependencies
dependencies:
flutter_riverpod: ^2.5.1
riverpod_annotation: ^2.3.5
go_router: ^14.0.0
freezed_annotation: ^2.4.1
json_annotation: ^4.9.0
dio: ^5.4.3+1
retrofit: ^4.1.0
flutter_secure_storage: ^9.0.0
cached_network_image: ^3.3.1
shimmer: ^3.0.0
intl: ^0.19.0
equatable: ^2.0.5
dev_dependencies:
build_runner: ^2.4.9
freezed: ^2.5.2
json_serializable: ^6.8.0
retrofit_generator: ^8.1.0
riverpod_generator: ^2.4.0
flutter_test:
sdk: flutter
Testing
// test/features/auth/auth_notifier_test.dart
void main() {
group('AuthNotifier', () {
late ProviderContainer container;
late MockAuthRepository mockRepository;
setUp(() {
mockRepository = MockAuthRepository();
container = ProviderContainer(
overrides: [authRepositoryProvider.overrideWithValue(mockRepository)],
);
});
tearDown(() => container.dispose());
test('should emit authenticated state on successful login', () async {
when(() => mockRepository.login(any(), any()))
.thenAnswer((_) async => Right(tUser));
final notifier = container.read(authNotifierProvider.notifier);
await notifier.login('test@email.com', 'password');
expect(
container.read(authNotifierProvider),
isA<_Authenticated>().having((s) => s.user, 'user', tUser),
);
});
});
}
Common Commands
# Create project
flutter create my_app --org com.mycompany
# Code generation
dart run build_runner build --delete-conflicting-outputs
dart run build_runner watch
# Run
flutter run --dart-define=API_URL=http://localhost:3000
# Build
flutter build apk --release --dart-define=API_URL=https://api.prod.com
flutter build ios --release
flutter build appbundle --release
# Test
flutter test
flutter test --coverage
# Analyze
flutter analyze
dart fix --apply
More from thesaifalitai/claude-setup
upwork-freelancer
>
63token-tracker
Token usage tracking and cost monitoring specialist. ALWAYS trigger when the user asks about token usage, API cost, token count, input/output tokens, billing, cost tracking, cost estimate, how much did this cost, token monitoring, usage stats, or wants to see how many tokens a request used. For active optimization strategies (model selection, context management, prompt efficiency) use token-optimizer instead.
56uiux-design
UI component code implementation specialist. Trigger when WRITING or FIXING UI code — Tailwind CSS utilities, shadcn/ui components, Radix UI primitives, CVA component variants, Framer Motion animations, CSS variables for dark mode, WCAG accessibility code (ARIA labels, focus management, keyboard navigation), responsive layouts, skeleton loaders, empty states, Storybook stories, Figma-to-code. Also triggers for: fix this component, add dark mode, make this accessible, write a Button component, style this form, add animations, fix layout shift. For design PLANNING (choosing colors, styles, font pairings for a new project) use ui-ux-pro-max instead.
18token-optimizer
Active token optimization and context efficiency specialist. ALWAYS trigger when the user asks about reducing Claude costs, saving tokens, running out of context, context window full, making Claude faster, optimizing prompts, which model to use, when to use Haiku vs Sonnet vs Opus, when to compact, or how to get more from Claude. Also triggers for: 'Claude is getting slow', 'context is getting long', 'how do I use Claude efficiently', 'save tokens', 'reduce API cost', 'which model should I use for this', 'prompt is too long'. Works alongside token-tracker for cost monitoring.
16fullstack-architecture
>
12react-native-expo
Expert React Native and Expo managed-workflow specialist (SDK 52+). ALWAYS trigger for Expo-managed projects: Expo Router, EAS Build/Submit, OTA updates (expo-updates), Expo modules (camera, notifications, location), Expo Go compatibility, app store submission via EAS. Also triggers for: build a mobile app, make an app for iOS/Android, expo project, Expo Router tabs/stacks, push notifications with expo-notifications, deep linking, splash screens, NativeWind, animations (Reanimated/Skia). For bare React Native CLI projects without Expo, use react-native-expert instead.
9