# Mobil Geliştiriciler İçin En Önemli 20 Flutter Mülakat Sorusu > Flutter mülakatlarına en sık sorulan 20 soruyla hazırlanın. Widget yapısı, state management, Dart, mimari ve en iyi uygulamalar detaylı şekilde açıklanmaktadır. - Published: 2026-01-24 - Updated: 2026-04-12 - Author: SharpSkill - Tags: flutter, dart, interview, mobile development, technical interview - Reading time: 15 min --- Flutter mülakatları; framework hakimiyetini, Dart dil bilgisini ve mobil mimari kalıplarını değerlendirmektedir. Bu rehber, temel kavramlardan ileri düzey konulara kadar en sık sorulan 20 soruyu detaylı cevaplar ve kod örnekleriyle kapsamaktadır. > **Mulakat Ipucu** > > Mülakatçılar, "nasıl" yanında "neden" sorusunu da açıklayabilen adayları takdir etmektedir. Her kavram için kullanım senaryolarını ve teknik ödünleşimleri anlamak fark yaratmaktadır. ## Temel Flutter ve Dart Sorulari ### 1. StatelessWidget ile StatefulWidget arasındaki fark nedir? **StatelessWidget**, görünümü yalnızca başlangıç yapılandırmasına bağlı olan değişmez bir widget temsil etmektedir. Bir kez oluşturulduktan sonra asla değişmez. **StatefulWidget** ise zamanla değişebilen değiştirilebilir bir durumu korumakta ve widget yeniden oluşturmalarını tetiklemektedir. ```dart // stateless_example.dart // StatelessWidget: static display, no change after construction class WelcomeMessage extends StatelessWidget { // Final parameter - never changes final String username; const WelcomeMessage({super.key, required this.username}); @override Widget build(BuildContext context) { // Build called once (unless parent rebuilds) return Text('Welcome, $username'); } } ``` ```dart // stateful_example.dart // StatefulWidget: mutable state, can rebuild itself class LikeButton extends StatefulWidget { const LikeButton({super.key}); @override State createState() => _LikeButtonState(); } class _LikeButtonState extends State { // Mutable local state int _likeCount = 0; void _incrementLike() { // setState triggers rebuild with new state setState(() { _likeCount++; }); } @override Widget build(BuildContext context) { return ElevatedButton( onPressed: _incrementLike, child: Text('Likes: $_likeCount'), ); } } ``` Temel kural: varsayılan olarak StatelessWidget kullanılmalı, StatefulWidget yalnızca widget-yerel durum gerektiğinde tercih edilmelidir. ### 2. Flutter'ın widget ağacı nasıl çalışır? Flutter, arayüzü birbiriyle bağlantılı üç ağaç şeklinde düzenlemektedir: **Widget Tree** (değişmez bildirim), **Element Tree** (yaşam döngüsü ve bağlama) ve **Render Tree** (yerleşim ve çizim). ```dart // widget_tree_example.dart // Declarative structure - widgets describe the UI class MyApp extends StatelessWidget { @override Widget build(BuildContext context) { // Each widget creates an Element in the Element Tree return MaterialApp( home: Scaffold( // Scaffold creates multiple RenderObjects body: Center( // Center modifies its child's layout child: Column( children: [ // Each Text has its own RenderParagraph Text('First'), Text('Second'), ], ), ), ), ); } } ``` `setState` çağrıldığında Flutter, yalnızca değişen öğeleri yeniden oluşturmak için eski ve yeni widget ağaçlarını karşılaştırmaktadır. Bu farklılaştırma, anahtarlara ve widget türlerine dayanmaktadır. ### 3. Const constructor nedir ve neden kullanılır? **Const constructor** yapıları, widget'ları çalışma zamanı yerine derleme zamanında oluşturmaktadır. Flutter bu örnekleri yeniden kullanabilmekte ve gereksiz yeniden oluşturmaları önleyerek performansı artırmaktadır. ```dart // const_example.dart class OptimizedScreen extends StatelessWidget { const OptimizedScreen({super.key}); @override Widget build(BuildContext context) { return Column( children: [ // ✅ const: same instance reused on each build const Icon(Icons.star, size: 48), const SizedBox(height: 16), const Text('Static title'), // ❌ Non-const: new instance on each build Icon(Icons.star, color: Theme.of(context).primaryColor), ], ); } } // Custom widget with const constructor class StaticCard extends StatelessWidget { final String title; // All fields must be final for const const StaticCard({super.key, required this.title}); @override Widget build(BuildContext context) { return Card(child: Text(title)); } } ``` Flutter analizörü, `prefer_const_constructors` lint kuralıyla kaçırılan fırsatları işaretlemektedir. ### 4. Flutter'daki farklı Key türlerini açıklayın **Key** yapıları, yeniden düzenlemeler sırasında widget durumunu korumaktadır. Key olmadan Flutter ağaçtaki konuma güvenmekte ve bu durum listeleri yeniden sıralarken hatalara neden olabilmektedir. ```dart // keys_example.dart class TodoList extends StatelessWidget { final List todos; const TodoList({super.key, required this.todos}); @override Widget build(BuildContext context) { return ListView.builder( itemCount: todos.length, itemBuilder: (context, index) { final todo = todos[index]; // ✅ ValueKey: preserves state if order changes return TodoTile( key: ValueKey(todo.id), todo: todo, ); }, ); } } // Different key types for different contexts class KeyExamples extends StatelessWidget { @override Widget build(BuildContext context) { return Column( children: [ // ValueKey: based on a unique value Container(key: ValueKey('unique-id')), // ObjectKey: based on object identity Container(key: ObjectKey(myObject)), // UniqueKey: new key on each build Container(key: UniqueKey()), // GlobalKey: access state from outside Form(key: _formKey), ], ); } } ``` GlobalKey yapıları ayrıca herhangi bir yerden widget durumuna erişim sağlamaktadır, ancak dikkatli kullanılmalıdır. ## Dart Sorulari ### 5. Dart'ta final ile const arasındaki fark nedir? **final**, yalnızca bir kez atanabilen ve çalışma zamanında değerlendirilen bir değişken tanımlamaktadır. **const** ise derleme zamanı sabiti oluşturmakta, değişmez ve yürütme öncesinde değerlendirilmektedir. ```dart // final_const_example.dart class DateExample { // final: value assigned at runtime final DateTime createdAt = DateTime.now(); // const: value known at compile time static const int maxItems = 100; static const String appName = 'MyApp'; // ❌ Error: DateTime.now() not const (runtime value) // static const DateTime timestamp = DateTime.now(); } void demonstrateDifference() { // final: each call can have different value final timestamp1 = DateTime.now(); final timestamp2 = DateTime.now(); print(timestamp1 == timestamp2); // false (different) // const: same instance reused const list1 = [1, 2, 3]; const list2 = [1, 2, 3]; print(identical(list1, list2)); // true (same instance) } ``` Flutter'da statik widget'lar için const, hesaplanmış değerler için final tercih edilmelidir. ### 6. Future ve async/await nasıl çalışır? **Future**, daha sonra mevcut olacak bir değeri temsil etmektedir. **async/await** ise iç içe callback'ler olmadan bu asenkron işlemleri yönetmek için okunabilir bir söz dizimi sunmaktadır. ```dart // async_example.dart class UserRepository { final ApiClient _client; UserRepository(this._client); // Future: promise of a future value Future fetchUser(String id) async { try { // await suspends execution until resolution final response = await _client.get('/users/$id'); return User.fromJson(response); } catch (e) { // Errors propagate normally with async/await throw UserNotFoundException(id); } } // Parallel execution with Future.wait Future> fetchUsers(List ids) async { // All requests start simultaneously final futures = ids.map((id) => fetchUser(id)); // Wait for all to complete return Future.wait(futures); } // Sequential processing Future processSequentially(List ids) async { for (final id in ids) { // Each request waits for the previous one await fetchUser(id); } } } ``` Paralellik için `Future.wait`, sıralı işleme için async döngüler kullanılmaktadır. > **FutureBuilder vs Riverpod** > > FutureBuilder basit senaryolarda işe yaramaktadır, ancak Riverpod (AsyncValue) karmaşık uygulamalarda daha iyi önbellek yönetimi, hata işleme ve yenileme yetenekleri sunmaktadır. ### 7. Stream yapılarını ve kullanım alanlarını açıklayın **Stream** yapıları, asenkron değer dizilerini temsil etmektedir ve gerçek zamanlı olaylar için idealdir: WebSocket bağlantıları, sensör verileri veya kullanıcı etkileşimleri. ```dart // stream_example.dart class MessageService { // StreamController manages creation and broadcasting final _messageController = StreamController.broadcast(); // Expose only the Stream (not the Sink) Stream get messages => _messageController.stream; void addMessage(Message message) { _messageController.sink.add(message); } void dispose() { _messageController.close(); } } // Usage with StreamBuilder class MessageList extends StatelessWidget { final MessageService service; const MessageList({super.key, required this.service}); @override Widget build(BuildContext context) { return StreamBuilder( stream: service.messages, builder: (context, snapshot) { // Handle all possible states if (snapshot.connectionState == ConnectionState.waiting) { return const CircularProgressIndicator(); } if (snapshot.hasError) { return Text('Error: ${snapshot.error}'); } if (!snapshot.hasData) { return const Text('No messages'); } return MessageCard(message: snapshot.data!); }, ); } } ``` `.broadcast()` Stream yapıları birden fazla dinleyiciye izin vermektedir; tek abonelikli Stream'lerden farklı olarak çoklu tüketimi desteklemektedir. ## Mimari ve State Management Sorulari ### 8. State management çözümlerini karşılaştırın: Provider, Riverpod, Bloc Her çözüm farklı ihtiyaçlara hitap etmektedir. **Provider** basitlik ve yerel entegrasyon sunmaktadır. **Riverpod** tür güvenliği ve test edilebilirlik getirmektedir. **Bloc** ise katı olay güdümlü mimari dayatmaktadır. ```dart // riverpod_example.dart // Riverpod: declarative and type-safe approach final userProvider = FutureProvider.autoDispose((ref) async { final repository = ref.watch(userRepositoryProvider); return repository.fetchCurrentUser(); }); class UserProfile extends ConsumerWidget { @override Widget build(BuildContext context, WidgetRef ref) { // AsyncValue handles loading/error/data final userAsync = ref.watch(userProvider); return userAsync.when( loading: () => const CircularProgressIndicator(), error: (error, stack) => Text('Error: $error'), data: (user) => Text(user.displayName), ); } } ``` ```dart // bloc_example.dart // Bloc: explicit events/states separation abstract class AuthEvent {} class LoginRequested extends AuthEvent { final String email; final String password; LoginRequested(this.email, this.password); } 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 error; AuthFailure(this.error); } class AuthBloc extends Bloc { AuthBloc() : super(AuthInitial()) { on(_onLoginRequested); } Future _onLoginRequested( LoginRequested event, Emitter emit, ) async { emit(AuthLoading()); try { final user = await _repository.login(event.email, event.password); emit(AuthSuccess(user)); } catch (e) { emit(AuthFailure(e.toString())); } } } ``` Modern API'si ve kolay test edilebilirliği sayesinde yeni projeler için Riverpod önerilmektedir. ### 9. Flutter'da Clean Architecture nedir? **Clean Architecture**, kodu bağımsız katmanlara ayırmaktadır: Domain (iş mantığı), Data (veri kaynakları) ve Presentation (kullanıcı arayüzü). Bu ayrım, test edilebilirliği ve bakımı kolaylaştırmaktadır. ```dart // domain/entities/user.dart // Entity: pure business object, no framework dependency class User { final String id; final String email; final String name; const User({ required this.id, required this.email, required this.name, }); } // domain/repositories/user_repository.dart // Interface: abstract contract, implementation in Data abstract class UserRepository { Future getUser(String id); Future updateUser(User user); } // domain/usecases/get_user_usecase.dart // Use case: isolated business logic class GetUserUseCase { final UserRepository _repository; GetUserUseCase(this._repository); Future call(String userId) { return _repository.getUser(userId); } } ``` ```dart // data/repositories/user_repository_impl.dart // Concrete implementation with data sources class UserRepositoryImpl implements UserRepository { final UserRemoteDataSource _remoteDataSource; final UserLocalDataSource _localDataSource; UserRepositoryImpl(this._remoteDataSource, this._localDataSource); @override Future getUser(String id) async { try { // Try local cache first final cachedUser = await _localDataSource.getUser(id); if (cachedUser != null) return cachedUser; } catch (_) {} // Fallback to API final user = await _remoteDataSource.fetchUser(id); await _localDataSource.cacheUser(user); return user; } } ``` Domain katmanı hiçbir şeye bağımlı değildir; Data katmanı Domain'e, Presentation katmanı ise her ikisine bağımlıdır. ### 10. Dependency injection nasıl uygulanır? **Dependency injection**, bağımlılıkları dışarıdan sağlayarak bileşenleri birbirinden ayırmaktadır. Riverpod, provider yapısıyla bu konuda üstün performans göstermektedir. ```dart // di_example.dart // Provider definitions (dependencies) final apiClientProvider = Provider((ref) { return ApiClient(baseUrl: Environment.apiUrl); }); final userRepositoryProvider = Provider((ref) { // Automatically injects ApiClient final client = ref.watch(apiClientProvider); return UserRepositoryImpl(client); }); final getUserUseCaseProvider = Provider((ref) { final repository = ref.watch(userRepositoryProvider); return GetUserUseCase(repository); }); // Usage in a widget class UserScreen extends ConsumerWidget { @override Widget build(BuildContext context, WidgetRef ref) { final getUserUseCase = ref.watch(getUserUseCaseProvider); // Use the use case... } } // Tests: easy dependency override void main() { testWidgets('displays user', (tester) async { await tester.pumpWidget( ProviderScope( overrides: [ // Replace with mock for tests userRepositoryProvider.overrideWithValue(MockUserRepository()), ], child: const MyApp(), ), ); }); } ``` En büyük avantaj: testlerde herhangi bir bağımlılık mock ile değiştirilebilmektedir. ## Performans Sorulari ### 11. Yeniden oluşturma performansı nasıl optimize edilir? Yeniden oluşturmaları en aza indirmek performansı artırmaktadır. Temel teknikler: const widget'lar, granüler parçalama ve Riverpod seçicileri. ```dart // rebuild_optimization.dart // ❌ Bad: everything rebuilds on each change class BadExample extends ConsumerWidget { @override Widget build(BuildContext context, WidgetRef ref) { final user = ref.watch(userProvider); return Column( children: [ Text(user.name), Text(user.email), const ExpensiveWidget(), // Rebuilds unnecessarily ], ); } } // ✅ Good: selector to rebuild only if name changes class GoodExample extends ConsumerWidget { @override Widget build(BuildContext context, WidgetRef ref) { // select: rebuilds only if name changes final name = ref.watch(userProvider.select((u) => u.name)); return Text(name); } } // ✅ Good: splitting into smaller widgets class OptimizedScreen extends StatelessWidget { @override Widget build(BuildContext context) { return Column( children: [ const Header(), // Static, never rebuilds const UserNameWidget(), // Rebuilds if name changes const UserEmailWidget(), // Rebuilds if email changes const Footer(), // Static ], ); } } ``` Aşırı yeniden oluşturmaları tespit etmek için DevTools Performance kullanılmalıdır. ### 12. Uzun listeler nasıl optimize edilir? Uzun listeler, ListView.builder ile **tembel yükleme** gerektirmektedir. 20'den fazla öğe için doğrudan children içeren ListView kullanımından kaçınılmalıdır. ```dart // list_optimization.dart class OptimizedList extends StatelessWidget { final List items; const OptimizedList({super.key, required this.items}); @override Widget build(BuildContext context) { // ✅ ListView.builder: builds on demand return ListView.builder( // Fixed height improves scrolling itemExtent: 72, itemCount: items.length, itemBuilder: (context, index) { return ItemTile( // Key to preserve state during scroll key: ValueKey(items[index].id), item: items[index], ); }, ); } } // List widget with infinite loading class InfiniteList extends ConsumerStatefulWidget { @override ConsumerState createState() => _InfiniteListState(); } class _InfiniteListState extends ConsumerState { final _scrollController = ScrollController(); @override void initState() { super.initState(); _scrollController.addListener(_onScroll); } void _onScroll() { // Load more data near the end if (_scrollController.position.pixels >= _scrollController.position.maxScrollExtent - 200) { ref.read(itemsProvider.notifier).loadMore(); } } @override Widget build(BuildContext context) { final items = ref.watch(itemsProvider); return ListView.builder( controller: _scrollController, itemCount: items.length, itemBuilder: (context, index) => ItemTile(item: items[index]), ); } } ``` Cok uzun listeler (1000+ oge) icin `ListView.separated` veya `scrollable_positioned_list` gibi ozel paketler degerlendirilmelidir. > **Gorsel Onbellekleme** > > Gorsel iceren listelerde yeniden yuklemeleri onlemek icin `cached_network_image` kullanilmalidir. Onbelleklenmemis gorseller kaydirma sirasinda takilmalara neden olmaktadir. ### 13. Impeller render motoru nasıl çalışır? **Impeller**, Flutter 3.16+ sürümünden itibaren Skia'nın yerini varsayılan render motoru olarak almaktadır. Shader'ları önceden derleyerek ilk gösterimdeki "jank" sorununu ortadan kaldırmaktadır. ```dart // impeller_benefits.dart // Complex animations benefit from Impeller class SmoothAnimation extends StatefulWidget { @override State createState() => _SmoothAnimationState(); } class _SmoothAnimationState extends State with SingleTickerProviderStateMixin { late AnimationController _controller; @override void initState() { super.initState(); _controller = AnimationController( duration: const Duration(seconds: 2), vsync: this, )..repeat(); } @override Widget build(BuildContext context) { return AnimatedBuilder( animation: _controller, builder: (context, child) { // Impeller: no runtime shader compilation return Transform.rotate( angle: _controller.value * 2 * 3.14159, child: child, ); }, child: const FlutterLogo(size: 100), ); } } ``` Impeller, iOS'ta varsayılan olarak etkindir. Android'de uyumluluk `flutter run --enable-impeller` komutuyla kontrol edilmelidir. ## Navigasyon ve Form Sorulari ### 14. Deep linking ile navigasyon nasıl yönetilir? **Deep linking**, uygulamayı URL aracılığıyla belirli bir ekranda açmaya olanak tanımaktadır. GoRouter bu işlevi yerel olarak desteklemektedir. ```dart // deep_linking.dart final router = GoRouter( routes: [ GoRoute( path: '/products/:productId', builder: (context, state) { // Extract URL parameter final productId = state.pathParameters['productId']!; return ProductScreen(productId: productId); }, ), GoRoute( path: '/search', builder: (context, state) { // Query parameters (?query=flutter) final query = state.uri.queryParameters['query'] ?? ''; return SearchScreen(initialQuery: query); }, ), ], ); // Programmatic navigation void navigateToProduct(BuildContext context, String id) { // go: replaces navigation stack context.go('/products/$id'); // push: adds to stack (allows back) context.push('/products/$id'); // pushNamed with extra for complex data context.pushNamed( 'productDetail', pathParameters: {'productId': id}, extra: ProductData(id: id), ); } ``` Sistem düzeyinde deep linking'i etkinleştirmek için yerel dosyalar (AndroidManifest.xml, Info.plist) yapılandırılmalıdır. ### 15. Formlar nasıl etkili şekilde doğrulanır? Doğrulama; Form, TextFormField ve özel doğrulayıcıları birleştirmektedir. `formz` gibi Zod benzeri paketler yapıya düzen katmaktadır. ```dart // form_validation.dart class RegistrationForm extends StatefulWidget { @override State createState() => _RegistrationFormState(); } class _RegistrationFormState extends State { final _formKey = GlobalKey(); final _emailController = TextEditingController(); final _passwordController = TextEditingController(); final _confirmController = TextEditingController(); @override Widget build(BuildContext context) { return Form( key: _formKey, // Real-time validation autovalidateMode: AutovalidateMode.onUserInteraction, child: Column( children: [ TextFormField( controller: _emailController, decoration: const InputDecoration(labelText: 'Email'), keyboardType: TextInputType.emailAddress, validator: _validateEmail, ), TextFormField( controller: _passwordController, decoration: const InputDecoration(labelText: 'Password'), obscureText: true, validator: _validatePassword, ), TextFormField( controller: _confirmController, decoration: const InputDecoration(labelText: 'Confirm'), obscureText: true, validator: _validateConfirmPassword, ), ElevatedButton( onPressed: _submit, child: const Text('Create account'), ), ], ), ); } String? _validateEmail(String? value) { if (value == null || value.isEmpty) { return 'Email required'; } final emailRegex = RegExp(r'^[\w-\.]+@([\w-]+\.)+[\w-]{2,4}$'); if (!emailRegex.hasMatch(value)) { return 'Invalid email format'; } return null; } String? _validatePassword(String? value) { if (value == null || value.length < 8) { return 'Minimum 8 characters'; } if (!value.contains(RegExp(r'[A-Z]'))) { return 'At least one uppercase letter required'; } if (!value.contains(RegExp(r'[0-9]'))) { return 'At least one number required'; } return null; } String? _validateConfirmPassword(String? value) { if (value != _passwordController.text) { return 'Passwords do not match'; } return null; } void _submit() { if (_formKey.currentState!.validate()) { // Form is valid, proceed } } } ``` `AutovalidateMode.onUserInteraction` modu en iyi kullanıcı deneyimini sunmaktadır: hatalar yalnızca etkileşimden sonra gösterilmektedir. ## Test Sorulari ### 16. Flutter widget'ları nasıl test edilir? Widget testleri, cihaz bağımlılığı olmadan kullanıcı arayüzünü doğrulamaktadır. `flutter_test` paketi gerekli araçları sağlamaktadır. ```dart // widget_test.dart import 'package:flutter_test/flutter_test.dart'; import 'package:flutter_riverpod/flutter_riverpod.dart'; void main() { testWidgets('displays welcome message', (tester) async { // Arrange: build the widget await tester.pumpWidget( const ProviderScope( child: MaterialApp( home: WelcomeScreen(username: 'Alice'), ), ), ); // Assert: verify content expect(find.text('Welcome, Alice'), findsOneWidget); }); testWidgets('button increments counter', (tester) async { await tester.pumpWidget( const MaterialApp(home: CounterScreen()), ); // Initial state expect(find.text('0'), findsOneWidget); // Act: tap the button await tester.tap(find.byType(ElevatedButton)); await tester.pump(); // Rebuild after setState // Assert: counter incremented expect(find.text('1'), findsOneWidget); }); testWidgets('form validates email', (tester) async { await tester.pumpWidget( const MaterialApp(home: LoginForm()), ); // Enter invalid email await tester.enterText( find.byKey(const Key('email-field')), 'invalid-email', ); await tester.tap(find.byType(ElevatedButton)); await tester.pump(); // Verify error message expect(find.text('Invalid email format'), findsOneWidget); }); } ``` `pump()` tek bir kare ileri alırken, `pumpAndSettle()` tüm animasyonların tamamlanmasını beklemektedir. ### 17. Riverpod provider'ları nasıl test edilir? Riverpod, `ProviderContainer` ve override yapıları aracılığıyla test sürecini kolaylaştırmaktadır. ```dart // provider_test.dart import 'package:flutter_riverpod/flutter_riverpod.dart'; import 'package:flutter_test/flutter_test.dart'; import 'package:mocktail/mocktail.dart'; // Repository mock class MockUserRepository extends Mock implements UserRepository {} void main() { late MockUserRepository mockRepository; late ProviderContainer container; setUp(() { mockRepository = MockUserRepository(); container = ProviderContainer( overrides: [ userRepositoryProvider.overrideWithValue(mockRepository), ], ); }); tearDown(() { container.dispose(); }); test('loads user from repository', () async { // Arrange final expectedUser = User(id: '1', name: 'Test'); when(() => mockRepository.getUser('1')) .thenAnswer((_) async => expectedUser); // Act final user = await container.read(userProvider('1').future); // Assert expect(user, expectedUser); verify(() => mockRepository.getUser('1')).called(1); }); test('handles repository error', () async { when(() => mockRepository.getUser(any())) .thenThrow(Exception('Network error')); expect( () => container.read(userProvider('1').future), throwsException, ); }); } ``` Provider testleri UI render gerektirmediği için hızlı çalışmaktadır. ## Dagitim Sorulari ### 18. Farklı ortamlar (dev, staging, prod) nasıl yönetilir? Ortamlar, `.env` dosyaları veya `--dart-define` ile derleme zamanı sabitleri aracılığıyla yapılandırılmaktadır. ```dart // environment.dart enum Environment { dev, staging, prod } class AppConfig { final Environment environment; final String apiUrl; final bool enableAnalytics; const AppConfig._({ required this.environment, required this.apiUrl, required this.enableAnalytics, }); // Predefined configurations static const dev = AppConfig._( environment: Environment.dev, apiUrl: 'https://api-dev.example.com', enableAnalytics: false, ); static const staging = AppConfig._( environment: Environment.staging, apiUrl: 'https://api-staging.example.com', enableAnalytics: true, ); static const prod = AppConfig._( environment: Environment.prod, apiUrl: 'https://api.example.com', enableAnalytics: true, ); // Read from --dart-define static AppConfig fromEnvironment() { const env = String.fromEnvironment('ENV', defaultValue: 'dev'); switch (env) { case 'prod': return prod; case 'staging': return staging; default: return dev; } } } ``` ```bash # terminal # Launch with specific environment flutter run --dart-define=ENV=staging # Production build flutter build apk --dart-define=ENV=prod --release ``` ### 19. Birden fazla uygulama varyantı için flavoring nasıl uygulanır? **Flavoring**, farklı yapılandırmalara (ikon, isim, API) sahip birden fazla varyant (client1, client2) oluşturmaktadır. ```dart // main_client1.dart import 'package:flutter/material.dart'; import 'config/flavor_config.dart'; import 'app.dart'; void main() { FlavorConfig( flavor: Flavor.client1, name: 'App Client 1', apiUrl: 'https://api.client1.com', primaryColor: Colors.blue, ); runApp(const App()); } // config/flavor_config.dart enum Flavor { client1, client2, internal } class FlavorConfig { final Flavor flavor; final String name; final String apiUrl; final Color primaryColor; // Singleton for global access static FlavorConfig? _instance; static FlavorConfig get instance => _instance!; FlavorConfig({ required this.flavor, required this.name, required this.apiUrl, required this.primaryColor, }) { _instance = this; } bool get isProduction => flavor != Flavor.internal; } ``` Her flavor icin yerel Android (build.gradle) ve iOS (xcconfig) dosyalari yapilandirilmalidir. ### 20. Flutter güvenlik en iyi uygulamaları nelerdir? Güvenlik; hassas veri depolama, girdi doğrulama ve tersine mühendisliğe karşı korumayı kapsamaktadır. ```dart // security_example.dart import 'package:flutter_secure_storage/flutter_secure_storage.dart'; class SecureStorage { // Encrypted storage for sensitive data final _storage = const FlutterSecureStorage( aOptions: AndroidOptions(encryptedSharedPreferences: true), iOptions: IOSOptions(accessibility: KeychainAccessibility.first_unlock), ); Future saveToken(String token) async { await _storage.write(key: 'auth_token', value: token); } Future getToken() async { return _storage.read(key: 'auth_token'); } Future deleteToken() async { await _storage.delete(key: 'auth_token'); } } // Input validation class InputValidator { // Injection prevention static String sanitize(String input) { return input .replaceAll(RegExp(r'[<>"\']'), '') .trim(); } // Length validation static bool isValidLength(String input, int min, int max) { return input.length >= min && input.length <= max; } } // SSL pinning protection class SecureApiClient { Dio createSecureClient() { final dio = Dio(); // Add certificate pinning (dio.httpClientAdapter as IOHttpClientAdapter).createHttpClient = () { final client = HttpClient(); client.badCertificateCallback = (cert, host, port) { // Verify certificate fingerprint return _isValidCertificate(cert); }; return client; }; return dio; } } ``` Gizli bilgiler asla koda yazılmamalıdır. Token'lar için `flutter_secure_storage`, üretim derlemeleri için `--obfuscate` kullanılmalıdır. ## Sonuc Bu 20 soru, Flutter mülakatlarının temel alanlarını kapsamaktadır: framework temelleri, Dart hakimiyeti, mimari kalıplar ve üretim ortamı en iyi uygulamaları. Başarı, yalnızca söz dizimini bilmekte değil, altta yatan mekanizmaları derinlemesine anlamakta yatmaktadır. ### Hazirlik Kontrol Listesi - StatelessWidget/StatefulWidget farkını ve kullanım senaryolarını kavrayın - Widget ağacını ve yeniden oluşturma optimizasyonunu anlayın - async/await, Future ve Stream yapılarını alıştırmalarla pratik edin - State management için Riverpod ile bir proje geliştirin - GoRouter ve deep linking konularını öğrenin - Birim testleri ve widget testleri yazın - Çoklu ortam yapılandırmasını kavrayın Kişisel projelerde düzenli pratik yapmak, bu bilgileri pekiştirmenin en etkili yoludur. Burada ele alınan her soru, Flutter framework'ünün inceliklerinde ustalaşmak için gerçek kodla daha derin bir keşfi hak etmektedir. --- Source: SharpSkill (https://sharpskill.dev), tech interview preparation for your real stack. HTML version of this page: https://sharpskill.dev/tr/blog/flutter/flutter-interview-questions