Flutter Kurulumu ve İlk Proje: SDK, Android Studio, Xcode, Emulator, flutter doctor ve Hot Reload
Flutter'ı Windows'ta Android için, macOS'ta Android ve iOS için kurmak, flutter doctor çıktısını okumak ve bookstore_app'i material_ui ile oluşturmak bu yazının konusu. Hot reload'un state'i koruyup hot restart'ın sıfırladığını gerçek bir koşuyla gösteriyor, pubspec.yaml'ı csproj ve package.json ile eşliyor, emulator olmadan widget testi ve web build'iyle doğrulama yapıyoruz.
Sürüm notu: Bu yazıdaki örnekler Flutter 3.47.8 (Dart 3.13.7) ve material_ui 1.11.0 ile denenmiştir. Web build'i Playwright 1.65'in headless Chromium'unda kontrol edildi. Android emulator, iOS simulator ve gerçek cihaz adımları bu yazı için çalıştırılmadı; dokümana göre anlatılıyor ve yerlerinde belirtiliyor.
Önce Tahmin Et
Sitedeki serilerin ortak örnek uygulaması Bookstore'un (bir online kitapçı) mobil uygulamasını bu yazıda oluşturuyoruz. İlk ekran basit: üstte "Bookstore" başlığı, ortada "Books in cart: 0" yazısı, sağ altta sepete kitap ekleyen bir buton.
Uygulama flutter run ile debug modda çalışırken butona iki kez bastın; ekranda "Books in cart: 2" yazıyor. Şimdi kodda bu metni "In your cart: 2 book(s)" olacak şekilde değiştirip terminalde r'ye basıyorsun (hot reload). Ekranda ne yazar: yeni metin ve 2 mi, yeni metin ve 0 mı, yoksa eski metin mi? Sonra R'ye basarsan (hot restart) ne değişir? Cevabı tahmin et; yazının ortasında uygulamayı gerçekten çalıştırıp ölçeceğiz.
Hangi İşletim Sisteminde Ne Geliştirebilirsin?
Flutter SDK Windows, macOS ve Linux'ta çalışır, ama hangi platform için build alabileceğin işletim sistemine bağlı. iOS uygulamasını build etmek, simulator'de çalıştırmak ve imzalamak Xcode gerektirir; Xcode yalnız macOS'ta çalışır. Bu yüzden:
- Windows: Android (emulator ve gerçek cihaz), web ve Windows desktop. iOS yok.
- macOS: Android, iOS, web ve macOS desktop. Seri için en kapsamlı ortam.
- Linux: Android, web ve Linux desktop.
Okurların çoğu gibi Windows'ta çalışıyorsan serinin Android tarafını eksiksiz takip edebilirsin. iOS'u görmek istediğinde bir Mac'e ya da macOS çalıştıran bir CI makinesine ihtiyacın olacak; yayınlama yazısında GitHub Actions'ın macOS runner'larıyla iOS build'i almayı ele alacağız. Mac tarafında bir not: Flutter, Intel işlemcili Mac'lere desteği kademeli olarak kapatıyor ve macOS 27 zaten Apple Silicon istiyor. Yeni bir Mac alacaksan bu kararı kolaylaştırıyor.
SDK Kurulumu ve flutter doctor
Flutter dokümanı iki kurulum yolu veriyor. Önerilen yol Visual Studio Code (Microsoft'un ücretsiz kod editörü): Flutter eklentisini kurup komut paletinden yeni bir proje başlatmak istediğinde eklenti SDK'yı indirmeyi, PATH'e eklemeyi ve kurulumu doğrulamayı kendisi yapıyor. İkinci yol manuel: işletim sistemine uygun stable SDK arşivini indirip bir klasöre açmak ve bin klasörünü PATH'e eklemek. Windows'ta SDK'yı C:\src\flutter gibi boşluk içermeyen ve yönetici yetkisi istemeyen bir yere açmak sonradan çıkabilecek sorunları azaltıyor; C:\Program Files kötü bir seçim.
Android için Android Studio gerekiyor, çünkü Android SDK, emulator ve build araçlarını o kuruyor. SDK Manager'dan API 36 platformunu, Build-Tools, Command-line Tools, Platform-Tools, Emulator, CMake ve NDK (Android'de C/C++ kodu derlemek için gereken araç seti) paketlerini seçmen isteniyor. Ardından lisansları kabul etmek için:
flutter doctor --android-licenses
Bu komut Android SDK lisanslarını tek tek gösterip onay istiyor; bu yazı için çalıştırmadık, çünkü kurulum zaten tamamdı. Emulator için Device Manager'dan bir AVD (Android Virtual Device; Android emulator'ünün sanal cihaz tanımı) oluşturuyorsun; bilgisayarının mimarisine uygun (x64 ya da Arm64) sistem imajını seçmek ve grafik hızlandırmayı donanıma almak emulator'ün kullanılabilir hızda çalışması için şart.
macOS'ta iOS için Xcode'u kurduktan sonra komut satırı araçlarını seçip ilk açılış adımlarını çalıştırmak, lisansı kabul etmek ve iOS platformunu (simulator runtime'ı dahil) indirmek gerekiyor:
sudo sh -c 'xcode-select -s /Applications/Xcode.app/Contents/Developer && xcodebuild -runFirstLaunch'
sudo xcodebuild -license
xcodebuild -downloadPlatform iOS
Native kod içeren bazı plugin'ler hâlâ CocoaPods istediği için kurulum sayfası CocoaPods'u da kurmanı söylüyor. Bu adımlar sistem ayarlarını değiştirdiği ve lisans onayı istediği için bu yazının ortamında yeniden çalıştırılmadı.
Kurulumun durumunu tek komutla görürsün:
flutter doctor
Doctor summary (to see all details, run flutter doctor -v):
[✓] Flutter (Channel stable, 3.47.8, on macOS 27, locale tr-TR)
[✓] Android toolchain - develop for Android devices (Android SDK version 36.0.0)
[✓] Xcode - develop for iOS and macOS (Xcode 27.2)
[✓] Chrome - develop for the web
[✓] Connected device (3 available)
[✓] Network resources
• No issues found!
İlk satırdaki işletim sistemi bilgisini kısalttık; geri kalanı gerçek çıktı. Her satır bir validator: [✓] sorun yok, [!] uyarı (çoğu zaman eksik bir lisans ya da eski bir araç), [✗] o platform için build alamazsın demek. Windows'ta Xcode satırı yerine Windows desktop için "Visual Studio - develop Windows apps" satırı çıkar; o satırdaki uyarı yalnız Windows desktop hedefini etkiler, Android'i değil. Bu makinede Android Studio satırı görünmüyor, çünkü komutu izole bir kullanıcı ayar klasörüyle çalıştırdık ve doctor Android Studio'yu o klasörden arıyor; Android build'i için gereken Android toolchain satırı tamam. Bir satırda sorun varsa flutter doctor -v her validator'ın neye baktığını ve hangi yolu bulduğunu gösterir.
Editör olarak Visual Studio Code ya da Android Studio kullanabilirsin; ikisinin de Flutter ve Dart eklentileri hot reload, widget inspector ve widget preview'ları destekliyor. Visual Studio ve Rider'dan gelen bir .NET geliştiricisi için Visual Studio Code genelde daha hafif bir başlangıç.
bookstore_app Projesini Oluşturmak
Serinin uygulaması bookstore_app; Dart paket adları snake_case olmak zorunda, bookstore-app geçersiz bir ad. Hedef platformları baştan sınırlıyoruz: Android ve iOS ana hedef, web yalnız tarayıcıda hızlı kontrol için.
flutter create --org com.example --platforms android,ios,web bookstore_app
Creating project bookstore_app...
Resolving dependencies in `bookstore_app`...
Downloading packages...
Got dependencies in `bookstore_app`.
Wrote 82 files.
All done!
--org uygulama kimliğinin önekini belirliyor: Android'de applicationId ve iOS'ta bundle identifier com.example.bookstore_app oldu. Bu değer mağazaya yüklendikten sonra değiştirilemez; gerçek bir uygulamada şirketin ters yazılmış alan adını kullanırsın. Oluşan yapının önemli kısmı:
bookstore_app/
lib/main.dart
test/widget_test.dart
android/
ios/
web/
pubspec.yaml
pubspec.lock
analysis_options.yaml
lib/main.dart uygulamanın giriş noktası (main ve runApp), test/widget_test.dart örnek bir widget testi. android/ bir Gradle projesi (applicationId, minSdk, imzalama), ios/ bir Xcode projesi (bundle id, Info.plist, deployment target), web/ ise index.html ve manifest'i tutuyor. pubspec.yaml paket adını, sürümü, bağımlılıkları ve asset'leri, pubspec.lock çözülmüş paket sürümlerini, analysis_options.yaml lint kurallarını (flutter_lints) içeriyor.
Dart kodunun tamamı lib/ altında yaşar. android/ ve ios/ klasörleri gerçek Gradle ve Xcode projeleri; Flutter onları build sırasında kullanır ve izin, ikon, uygulama adı, imzalama gibi platform ayarlarını buralarda yaparsın. Çoğu gün bu klasörlere dokunmazsın, ama silmezsin de: içlerindeki ayarlar senin.
Minimum platform sürümleri
Hangi Android ve iOS sürümlerini desteklediğini oluşan dosyalarda görebilirsin. android/app/build.gradle.kts değerleri Flutter'ın Gradle plugin'inden alıyor:
minSdk = flutter.minSdkVersion
targetSdk = flutter.targetSdkVersion
Flutter 3.47.8'in Gradle plugin'inde bu değerler minSdkVersion = 24, targetSdkVersion = 36 ve compileSdkVersion = 36. iOS tarafında ios/Runner.xcodeproj/project.pbxproj içinde IPHONEOS_DEPLOYMENT_TARGET = 15.0 yazıyor. Bu ayrıntı önemli: 3.47 duyurusu iOS minimumunun 13'ten 15'e çıktığını söylüyor, ama Flutter'ın iOS yayınlama dokümanı yazım gününde hâlâ "iOS 13 ve sonrası" diyor. Oluşturulan projedeki değer ve desteklenen platformlar sayfası 15'i doğruluyor; dokümanın o cümlesi geride kalmış.
pubspec.yaml: csproj ve package.json'ın Karşılığı
pubspec.yaml, .NET'teki .csproj'un ve npm'deki package.json'ın yaptığı işi tek dosyada yapıyor. Yorumları çıkarılmış hâli:
name: bookstore_app
description: "Bookstore customer mobile app (catalog, book details, cart, orders, sign-in)."
publish_to: 'none'
version: 1.0.0+1
environment:
sdk: ^3.13.7
dependencies:
flutter:
sdk: flutter
cupertino_icons: ^1.0.8
material_ui: ^1.11.0
dev_dependencies:
flutter_test:
sdk: flutter
flutter_lints: ^6.0.0
flutter:
uses-material-design: true
version: 1.0.0+1 iki parçalı: +'dan önceki kısım kullanıcının gördüğü sürüm (Android'de versionName, iOS'ta CFBundleShortVersionString), sonraki kısım mağazanın her yüklemede artmasını beklediği build numarası (versionCode, CFBundleVersion). environment.sdk, .NET'teki TargetFramework'e benzer ama bir aralık belirtir. ^1.11.0 "1.11.0 ve üstü, 2.0.0'ın altı" demek; npm'deki caret ile aynı anlam. dev_dependencies yalnız geliştirmede ve testte kullanılan paketler, yani .NET'te test projesine eklediğin paketlerin karşılığı.
pubspec.lock her paketin çözülmüş tam sürümünü tutar. Uygulama projelerinde commit'lenir, böylece CI ve ekipteki herkes aynı sürümlerle build alır; .NET'te packages.lock.json, npm'de package-lock.json ne yapıyorsa o. Paket eklemek için dosyayı elle düzenlemek yerine:
flutter pub add material_ui
Resolving dependencies...
Downloading packages...
cupertino_icons 1.0.9 (2.0.0 available)
+ cupertino_ui 1.1.6
+ flutter_localizations 0.0.0 from sdk flutter
+ intl 0.20.3
material_color_utilities 0.13.0 (0.13.1 available)
+ material_ui 1.11.0
meta 1.18.3 (1.19.0 available)
test_api 0.7.12 (0.7.15 available)
vector_math 2.4.0 (2.4.4 available)
Changed 4 dependencies!
5 packages have newer versions incompatible with dependency constraints.
Try `flutter pub outdated` for more information.
+ ile başlayan satırlar yeni eklenen paketler; dikkat edilecek nokta material_ui'nin cupertino_ui, flutter_localizations ve intl'i de getirmesi. Transitive (dolaylı) bağımlılıklar NuGet'teki gibi otomatik çözülür. "available" satırları, Flutter SDK'nın sabitlediği bazı paketlerin daha yeni sürümleri olduğunu söylüyor; bunlar SDK ile birlikte güncellenir, elle yükseltmeye çalışma.
material_ui ve İlk Ekran
Flutter 3.47.8'in flutter create template'i hâlâ package:flutter/material.dart import'uyla geliyor. İlk yazıda material_ui paketini kullanma kararı vermiştik; import'ları elle değiştirmek yerine Flutter'ın hazırladığı otomatik düzeltmeyi çalıştırıyoruz:
dart fix --apply --code=migrate_design_widgets
Computing fixes in bookstore_app...
Applying fixes...
lib/main.dart
migrate_design_widgets - 1 fix
test/widget_test.dart
migrate_design_widgets - 1 fix
2 fixes made in 2 files.
Bir tuzak: --code vermeden çalıştırılan dart fix --dry-run aynı projede "Nothing to fix!" dedi. Bu düzeltme genel dart fix taramasına dahil değil; kodu açıkça vermen gerekiyor. Template'in sayaç örneğini silip Bookstore'un ilk ekranını üç dosyaya böldük. lib/main.dart yalnız uygulamayı başlatıyor:
import 'package:material_ui/material_ui.dart';
import 'app.dart';
void main() {
runApp(const BookstoreApp());
}
lib/app.dart uygulamanın kökü; tema ve ilk ekran burada:
import 'package:material_ui/material_ui.dart';
import 'ui/home/widgets/home_screen.dart';
class BookstoreApp extends StatelessWidget {
const BookstoreApp({super.key});
@override
Widget build(BuildContext context) {
return MaterialApp(
title: 'Bookstore',
theme: ThemeData(colorScheme: ColorScheme.fromSeed(seedColor: Colors.indigo)),
home: const HomeScreen(),
);
}
}
Klasör yapısı Flutter'ın resmî mimari rehberindeki düzeni izliyor: ekranlar lib/ui/<feature>/widgets/ altında, ileride ViewModel'ler lib/ui/<feature>/view_models/, repository ve service'ler lib/data/ altına gelecek. HomeScreen şimdilik kendi sayacını tutan bir StatefulWidget:
import 'package:flutter/widget_previews.dart';
import 'package:material_ui/material_ui.dart';
class HomeScreen extends StatefulWidget {
const HomeScreen({super.key});
@override
State<HomeScreen> createState() => _HomeScreenState();
}
class _HomeScreenState extends State<HomeScreen> {
int _booksInCart = 0;
void _addBook() {
setState(() => _booksInCart++);
}
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(title: const Text('Bookstore')),
body: Center(
child: Text(
'Books in cart: $_booksInCart',
style: Theme.of(context).textTheme.headlineSmall,
),
),
floatingActionButton: FloatingActionButton(
onPressed: _addBook,
tooltip: 'Add book',
child: const Icon(Icons.add_shopping_cart),
),
);
}
}
@Preview(name: 'Home screen')
Widget homeScreenPreview() => const MaterialApp(home: HomeScreen());
StatefulWidget, setState ve BuildContext'i altıncı yazıda ayrıntılı açacağız. Şimdilik bilmen gereken: _booksInCart değeri widget'ın kendisinde değil, ona bağlı State object'inde yaşıyor ve setState çağrısı Flutter'a "bu ekranı yeniden çiz" diyor. Angular'daki bir component'in field'ını değiştirip change detection'ı tetiklemeye benziyor.
Hot Reload, Hot Restart ve Tam Yeniden Başlatma
Normalde flutter run ile uygulamayı Android emulator'de ya da iOS simulator'de açarsın. Bu yazının ortamında emulator ve simulator çalıştırılmadı; aynı debug döngüsünü Flutter'ın web-server cihazıyla kurduk ve ekranı Playwright'ın headless Chromium'uyla açıp butona bir script ile tıkladık:
flutter run -d web-server --web-hostname 127.0.0.1 --web-port 52818
Launching lib/main.dart on Web Server in debug mode...
Waiting for connection from debug service on Web Server... 12,5s
lib/main.dart is being served at http://127.0.0.1:52818
Flutter run key commands.
r Hot reload. 🔥🔥🔥
R Hot restart.
h List all available interactive commands.
d Detach (terminate "flutter run" but leave application running).
c Clear the screen
q Quit (terminate the application on the device).
Script butona iki kez bastı, sonra home_screen.dart'taki metni 'In your cart: $_booksInCart book(s)' olarak değiştirip önce r, sonra R gönderdi. Ekrandan okunan metinler ve flutter run'ın çıktısı:
before edit : Books in cart: 2
after r : In your cart: 2 book(s)
after R : In your cart: 0 book(s)
Performing hot reload... 80ms
Reloaded application in 80ms.
Performing hot restart... 65ms
Restarted application in 65ms.
"Önce Tahmin Et"in cevabı: hot reload yeni kodu yükleyip widget ağacını yeniden build etti ama State object'ine dokunmadı; sayaç 2'de kaldı, metin değişti. Hot restart ise yeni kodu yükleyip uygulamayı baştan başlattı; main() yeniden çalıştı, state sıfırlandı.
Hot reload'un neyi yeniden çalıştırmadığını bilmek önemli. main() ve initState() tekrar çalışmaz; global değişkenlerin ve static field'ların ilk değerleri state sayılır, değiştirdiğinde hot restart gerekir. Bir enum'u class'a çevirmek ya da bir generic tipin parametre sayısını değiştirmek hot reload ile uygulanmaz. Kotlin, Swift gibi native kodu ya da pubspec.yaml'a yeni bir plugin eklemek ise uygulamanın tamamen durdurulup yeniden build edilmesini (tam yeniden başlatma) ister. Hot reload yalnız debug modda var; profile ve release build'lerde yok.
Widget Previews: tek bir widget'ı izole görmek
Yukarıdaki @Preview annotation'ı 3.47'de stable olan Widget Previews içindir. package:flutter/widget_previews.dart'tan gelir; top-level ya da static bir fonksiyona, ya da zorunlu parametresi olmayan public bir constructor'a konur. flutter widget-preview start ya da Visual Studio Code ve Android Studio'daki "Flutter Widget Preview" paneli, uygulamayı baştan çalıştırmadan bu widget'ları ayrı ayrı gösterir; size, brightness, textScaleFactor gibi parametrelerle aynı widget'ı farklı koşullarda yan yana görebilirsin. Previewer Flutter web ile çalıştığı ve bir tarayıcı açtığı için bu ortamda başlatılmadı; annotation'ın derlendiğini flutter analyze doğruladı. Kısıtlarına dikkat: preview içindeki kod dart:io ya da native plugin API'lerini çağıramaz.
Emulator Olmadan Doğrulamak: Widget Testi ve Web Build
Her değişiklikte emulator açmak zorunda değilsin. Flutter'ın widget testleri cihaz olmadan, hızlıca çalışır. Template'in sayaç testini Bookstore ekranına göre yeniden yazdık:
import 'package:bookstore_app/app.dart';
import 'package:flutter_test/flutter_test.dart';
import 'package:material_ui/material_ui.dart';
void main() {
testWidgets('tapping the button adds a book to the cart', (tester) async {
await tester.pumpWidget(const BookstoreApp());
expect(find.text('Books in cart: 0'), findsOneWidget);
await tester.tap(find.byIcon(Icons.add_shopping_cart));
await tester.pump();
expect(find.text('Books in cart: 1'), findsOneWidget);
});
}
flutter analyze
flutter test
Analyzing bookstore_app...
No issues found! (ran in 4.8s)
00:00 +0: loading test/widget_test.dart
00:00 +0: tapping the button adds a book to the cart
00:00 +1: All tests passed!
Test, uygulamayı gerçek cihazdaki gibi build edip butona dokunuyor; tek fark ekranın görünmemesi. Test yazısında bunu ViewModel'ler ve sahte repository'lerle genişleteceğiz. İkinci kontrol, release modda bir web build'i:
flutter build web
Compiling lib/main.dart for the Web...
Wasm dry run succeeded. Consider building and testing your application with the `--wasm` flag. See docs for more info: https://docs.flutter.dev/platform-integration/web/wasm
Use --no-wasm-dry-run to disable these warnings.
Font asset "CupertinoIcons.ttf" was tree-shaken, reducing it from 257628 to 1472 bytes (99.4% reduction). Tree-shaking can be disabled by providing the --no-tree-shake-icons flag when building your app.
Font asset "MaterialIcons-Regular.otf" was tree-shaken, reducing it from 1645184 to 7916 bytes (99.5% reduction). Tree-shaking can be disabled by providing the --no-tree-shake-icons flag when building your app.
Compiling lib/main.dart for the Web... 17,2s
✓ Built build/web
Icon font'larının tree shaking ile yüzde 99'dan fazla küçülmesi, release build'in kullanılmayan kodu ve ikonları attığını gösteriyor. build/web'i yerel bir HTTP sunucusundan yayınlayıp Playwright ile açtığımızda:
title: Bookstore
flutter-view elements: 1
native <button> elements before semantics: 0
semantics text found: Books in cart: 0
after click: Books in cart: 1
console errors: 0
İlk yazıdaki iddianın ölçülmüş hâli bu: sayfada tek bir HTML butonu yok, her şey Flutter'ın çizdiği flutter-view içinde. Metni ve butonu bulabilmek için önce Flutter'ın semantics ağacını açtırmamız gerekti; bu ağaç ekran okuyucuların ve test araçlarının uygulamayı "görmesini" sağlıyor. Mobilde aynı ağaç TalkBack ve VoiceOver'a gidiyor.
Üretimde Dikkat
Flutter SDK sürümünü ekipte sabitle. Herkesin kendi makinesinde farklı bir stable sürüm olması, aynı commit'in bir makinede derlenip diğerinde derlenmemesine yol açar. pubspec.yaml'daki environment bölümüne flutter: '>=3.47.8 <3.48.0' gibi bir aralık eklemek (Dart 3.9'dan beri pub bu aralığın üst sınırını da dikkate alıyor) ve CI'da sürümü açıkça seçmek bunu önler. .NET'teki global.json'un yaptığı işe benziyor.
applicationId ve bundle identifier'ı ilk günden doğru seç. com.example ile mağazaya yükleme yapılamaz ve yüklendikten sonra kimlik değiştirilemez; değiştirmek yeni bir uygulama yayınlamak demek.
android/local.properties ve ios/Flutter/Generated.xcconfig makineye özgü yollar içerir. Template'in .gitignore'u bunları zaten dışarıda bırakıyor; repo'ya eklenmediklerinden emin ol, aksi hâlde başka bir geliştiricinin build'i senin makinendeki SDK yolunu arar.
Sık Düşülen Tuzaklar
Birinci tuzak, hot reload'dan sonra "değişiklik görünmüyor" diye saatler kaybetmek. Değiştirdiğin satır initState'te, main()'de ya da bir global değişkenin ilk değerindeyse hot reload onu çalıştırmaz. Şüphelendiğinde R ile hot restart yap.
İkinci tuzak, flutter doctor'daki her uyarıyı engel sanmak. Windows'ta Visual Studio satırındaki uyarı yalnız Windows desktop build'ini etkiler; Android geliştiriyorsan görmezden gelebilirsin. Önemli olan, hedeflediğin platformun satırının yeşil olması.
Üçüncü tuzak, Android emulator'ü yazılım grafikle çalıştırmak. Device Manager'da grafik hızlandırma donanıma alınmazsa emulator o kadar yavaşlar ki uygulamanın yavaş olduğunu sanırsın.
Dördüncü tuzak, eski Material import'una geri dönmek. İnternetten kopyaladığın bir örnek package:flutter/material.dart import ediyorsa, aynı dosyada material_ui ile karıştırmak çakışan tip adlarına yol açar. Örneği aldıktan sonra dart fix --apply --code=migrate_design_widgets'i tekrar çalıştır.
Küçük Bir Deneme
★☆☆ Kendi makinende bookstore_app'i oluştur, flutter doctor -v çıktısında Android toolchain satırının hangi SDK yolunu bulduğuna bak ve uygulamayı bir Android emulator'de çalıştır.
★★☆ Uygulama çalışırken ColorScheme.fromSeed'deki rengi Colors.teal yap ve hot reload ile dene. Sonra aynı değişikliği main()'de runApp'e farklı bir widget vererek yap. Hangisi hot reload ile görünüyor, hangisi hot restart istiyor?
★★★ HomeScreen için iki preview daha ekle: biri brightness: Brightness.dark, diğeri textScaleFactor: 2.0 ile. Büyük yazı boyutunda başlıkta ya da butonda bir taşma görüyor musun? Görüyorsan bunu layout yazısında nasıl çözeceğimizi not et.
Bir sonraki yazıda Flutter'ı bir süreliğine bırakıp Dart'a geçiyoruz: C# bilen biri için tipler, var/final/const farkı, sound null safety, collection'lar ve record'lar; Bookstore'un Book ve Author modellerinin ilk hâli.