1. Giriş
Flutter, Google'ın tek bir kod tabanından mobil, web ve masaüstü için uygulamalar oluşturmaya yarayan kullanıcı arayüzü araç setidir. Bu codelab'de aşağıdaki Flutter uygulamasını oluşturacaksınız:
Uygulama, "newstay", "lightstream", "mainbrake" veya "graypine" gibi kulağa hoş gelen adlar oluşturur. Kullanıcı, sonraki adı isteyebilir, mevcut adı favorilerine ekleyebilir ve favori listesini ayrı bir sayfada inceleyebilir. Uygulama, farklı ekran boyutlarına duyarlıdır.
Neler öğreneceksiniz?
- Flutter'ın işleyiş şekliyle ilgili temel bilgiler
- Flutter'da düzen oluşturma
- Kullanıcı etkileşimlerini (ör. düğmeye basma) uygulama davranışına bağlama
- Flutter kodunuzu düzenli tutma
- Uygulamanızı duyarlı hale getirme (farklı ekranlar için)
- Uygulamanızın tutarlı bir görünüm ve tarzda olmasını sağlama
Doğrudan ilginç kısımlara geçebilmeniz için temel bir iskeletle başlayacaksınız.

Filip, bu codelab'in tamamını adım adım açıklıyor.
Laboratuvarı başlatmak için İleri'yi tıklayın.
2. Flutter ortamınızı ayarlama
Düzenleyici
Bu codelab'i olabildiğince basit hale getirmek için geliştirme ortamınız olarak Visual Studio Code'u (VS Code) kullanacağınızı varsayıyoruz. Ücretsizdir ve tüm büyük platformlarda çalışır.
Android Studio, diğer IntelliJ IDE'leri, Emacs, Vim veya Notepad++ gibi istediğiniz düzenleyiciyi kullanabilirsiniz. Bunların hepsi Flutter ile çalışır.
Talimatlarda varsayılan olarak VS Code'a özgü kısayollar kullanıldığından bu codelab için VS Code'u kullanmanızı öneririz. "X işlemini yapmak için düzenleyicinizde uygun işlemi yapın" gibi ifadeler yerine "burayı tıklayın" veya "bu tuşa basın" gibi ifadeler kullanmak daha kolaydır.

Geliştirme hedefi seçme
Flutter, çok platformlu bir araç setidir. Uygulamanız aşağıdaki işletim sistemlerinden herhangi birinde çalışabilir:
- iOS
- Android
- Windows
- macOS
- Linux
- web
Ancak öncelikli olarak geliştireceğiniz tek bir işletim sistemi seçmek yaygın bir uygulamadır. Bu, "geliştirme hedefiniz"dir. Uygulamanızın geliştirme sırasında üzerinde çalıştığı işletim sistemidir.

Örneğin, bir Flutter uygulaması geliştirmek için Windows dizüstü bilgisayar kullandığınızı varsayalım. Geliştirme hedefiniz olarak Android'i seçerseniz genellikle bir Android cihazı USB kablosuyla Windows dizüstü bilgisayarınıza bağlarsınız ve geliştirme aşamasındaki uygulamanız bu bağlı Android cihazda çalışır. Ancak geliştirme hedefi olarak Windows'u da seçebilirsiniz. Bu durumda, geliştirme aşamasındaki uygulamanız düzenleyicinizin yanında bir Windows uygulaması olarak çalışır.
Geliştirme hedefiniz olarak web'i seçmek cazip gelebilir. Bu seçimin dezavantajı, Flutter'ın en kullanışlı geliştirme özelliklerinden biri olan Stateful Hot Reload'u kaybetmenizdir. Flutter, web uygulamalarını anında yeniden yükleyemez.
Şimdi seçiminizi yapın. Uygulamanızı daha sonra diğer işletim sistemlerinde de çalıştırabileceğinizi unutmayın. Ancak aklınızda net bir geliştirme hedefi olması bir sonraki adımı kolaylaştırır.
Flutter'ı yükleme
Flutter SDK'nın nasıl yükleneceğine dair en güncel talimatları her zaman docs.flutter.dev adresinde bulabilirsiniz.
Flutter web sitesindeki talimatlar, yalnızca SDK'nın kendisinin yüklenmesini değil, geliştirme hedefiyle ilgili araçları ve düzenleyici eklentilerini de kapsar. Bu codelab için yalnızca aşağıdakileri yüklemeniz gerektiğini unutmayın:
- Flutter SDK
- Flutter eklentisiyle Visual Studio Code
- Seçtiğiniz geliştirme hedefi için gereken yazılım (ör. Windows'u hedeflemek için Visual Studio veya macOS'i hedeflemek için Xcode)
Bir sonraki bölümde ilk Flutter projenizi oluşturacaksınız.
Şimdiye kadar sorun yaşadıysanız sorun giderme konusunda StackOverflow'daki bazı soru ve yanıtlardan yararlanabilirsiniz.
Sık Sorulan Sorular
- Flutter SDK'nın yolunu nasıl bulabilirim?
- Flutter komutu bulunamadığında ne yapmalıyım?
- "Başlangıç kilidinin serbest bırakılması için başka bir Flutter komutu bekleniyor" sorununu nasıl düzeltebilirim?
- Flutter'a Android SDK yüklememin nerede olduğunu nasıl söyleyebilirim?
flutter doctor --android-licensesçalıştırılırken Java hatasıyla nasıl başa çıkabilirim?- Android
sdkmanageraracı bulunamadı hatasıyla nasıl başa çıkarım? - "
cmdline-toolsbileşeni eksik" hatasıyla nasıl başa çıkabilirim? - Apple Silicon (M1) üzerinde CocoaPods'u nasıl çalıştırırım?
- VS Code'da kaydederken otomatik biçimlendirmeyi nasıl devre dışı bırakabilirim?
3. Proje oluşturma
İlk Flutter projenizi oluşturma
Visual Studio Code'u başlatın ve komut paletini açın (F1 veya Ctrl+Shift+P ya da Shift+Cmd+P ile). "flutter new" yazmaya başlayın. Flutter: New Project (Flutter: Yeni Proje) komutunu seçin.
Ardından Uygulama'yı ve projenizi oluşturacağınız klasörü seçin. Bu, ana dizininiz veya C:\src\ gibi bir şey olabilir.
Son olarak projenize bir ad verin. namer_app veya my_awesome_namer gibi bir ifade.

Flutter, proje klasörünüzü oluşturur ve VS Code bu klasörü açar.
Artık 3 dosyanın içeriğini uygulamanın temel iskeletiyle üzerine yazacaksınız.
İlk uygulamayı kopyalayıp yapıştırma
VS Code'un sol bölmesinde Explorer'ın (Gezgin) seçili olduğundan emin olun ve pubspec.yaml dosyasını açın.

Bu dosyanın içeriğini şununla değiştirin:
pubspec.yaml
name: namer_app
description: "A new Flutter project."
publish_to: "none"
version: 0.1.0
environment:
sdk: ^3.9.0
dependencies:
flutter:
sdk: flutter
english_words: ^4.0.0
provider: ^6.1.5
dev_dependencies:
flutter_test:
sdk: flutter
flutter_lints: ^6.0.0
flutter:
uses-material-design: true
pubspec.yaml dosyası, uygulamanızla ilgili temel bilgileri (ör. mevcut sürümü, bağımlılıkları ve gönderileceği öğeler) belirtir.
Ardından, projede başka bir yapılandırma dosyası açın analysis_options.yaml.

İçeriğini aşağıdakiyle değiştirin:
analysis_options.yaml
include: package:flutter_lints/flutter.yaml
linter:
rules:
avoid_print: false
prefer_const_constructors_in_immutables: false
prefer_const_constructors: false
prefer_const_literals_to_create_immutables: false
prefer_final_fields: false
unnecessary_breaks: true
use_key_in_widget_constructors: false
Bu dosya, Flutter'ın kodunuzu analiz ederken ne kadar katı olması gerektiğini belirler. Bu, Flutter'a ilk adımınız olduğu için analizciye kolay davranmasını söylüyorsunuz. Bu ayarı daha sonra istediğiniz zaman değiştirebilirsiniz. Hatta gerçek bir üretim uygulaması yayınlamaya yaklaştıkça analiz aracını bu değerden daha katı hale getirmek isteyeceksiniz.
Son olarak, lib/ dizinindeki main.dart dosyasını açın.

Bu dosyanın içeriğini şununla değiştirin:
lib/main.dart
import 'package:english_words/english_words.dart';
import 'package:flutter/material.dart';
import 'package:provider/provider.dart';
void main() {
runApp(MyApp());
}
class MyApp extends StatelessWidget {
const MyApp({super.key});
@override
Widget build(BuildContext context) {
return ChangeNotifierProvider(
create: (context) => MyAppState(),
child: MaterialApp(
title: 'Namer App',
theme: ThemeData(
colorScheme: ColorScheme.fromSeed(seedColor: Colors.deepOrange),
),
home: MyHomePage(),
),
);
}
}
class MyAppState extends ChangeNotifier {
var current = WordPair.random();
}
class MyHomePage extends StatelessWidget {
@override
Widget build(BuildContext context) {
var appState = context.watch<MyAppState>();
return Scaffold(
body: Column(
children: [Text('A random idea:'), Text(appState.current.asLowerCase)],
),
);
}
}
Bu 50 satırlık kod, uygulamanın şu ana kadarki tüm kodunu oluşturuyor.
Sonraki bölümde, uygulamayı hata ayıklama modunda çalıştırın ve geliştirmeye başlayın.
4. Düğme ekleme
Bu adım, yeni bir kelime eşleşmesi oluşturmak için Sonraki düğmesini ekler.
Uygulamayı başlatma
Öncelikle lib/main.dart uygulamasını açın ve hedef cihazınızın seçili olduğundan emin olun. VS Code'un sağ alt köşesinde, mevcut hedef cihazı gösteren bir düğme bulunur. Değiştirmek için tıklayın.
lib/main.dart açıkken VS Code penceresinin sağ üst köşesindeki "oynat"
düğmesini bulup tıklayın.
Yaklaşık bir dakika sonra uygulamanız hata ayıklama modunda başlatılır. Henüz çok fazla şey yok:

First Hot Reload
lib/main.dart bölümünün en altına, ilk Text nesnesindeki dizeye bir şey ekleyin ve dosyayı kaydedin (Ctrl+S veya Cmd+S ile). Örneğin:
lib/main.dart
// ...
return Scaffold(
body: Column(
children: [
Text('A random AWESOME idea:'), // ← Example change.
Text(appState.current.asLowerCase),
],
),
);
// ...
Uygulamanın hemen değiştiğini ancak rastgele kelimenin aynı kaldığını fark edin. Bu, Flutter'ın ünlü stateful Hot Reload özelliğinin çalışmasıdır. Kaynak dosyada değişiklikleri kaydettiğinizde anında yeniden yükleme tetiklenir.
Sık Sorulan Sorular
- VSCode'da anında yeniden yükleme çalışmıyorsa ne yapmalıyım?
- VSCode'da hızlı yeniden yükleme için "r" tuşuna basmam gerekir mi?
- Hot Reload, web'de çalışır mı?
- "Hata ayıklama" banner'ını nasıl kaldırabilirim?
Düğme ekleme
Ardından, ikinci Text örneğinin hemen altına, Column bölümünün en altına bir düğme ekleyin.
lib/main.dart
// ...
return Scaffold(
body: Column(
children: [
Text('A random AWESOME idea:'),
Text(appState.current.asLowerCase),
// ↓ Add this.
ElevatedButton(
onPressed: () {
print('button pressed!');
},
child: Text('Next'),
),
],
),
);
// ...
Değişikliği kaydettiğinizde uygulama tekrar güncellenir: Bir düğme görünür ve bu düğmeyi tıkladığınızda VS Code'daki Debug Console'da button pressed! (düğmeye basıldı) mesajı gösterilir.
5 dakikada Flutter hakkında hızlı bir başlangıç
Hata Ayıklama Konsolu'nu izlemek eğlenceli olsa da düğmenin daha anlamlı bir iş yapmasını istersiniz. Ancak bu konuya geçmeden önce, lib/main.dart bölümündeki kodu daha yakından inceleyerek nasıl çalıştığını anlayın.
lib/main.dart
// ...
void main() {
runApp(MyApp());
}
// ...
Dosyanın en üstünde main() işlevini bulabilirsiniz. Mevcut biçiminde yalnızca Flutter'a MyApp içinde tanımlanan uygulamayı çalıştırmasını söyler.
lib/main.dart
// ...
class MyApp extends StatelessWidget {
const MyApp({super.key});
@override
Widget build(BuildContext context) {
return ChangeNotifierProvider(
create: (context) => MyAppState(),
child: MaterialApp(
title: 'Namer App',
theme: ThemeData(
colorScheme: ColorScheme.fromSeed(seedColor: Colors.deepOrange),
),
home: MyHomePage(),
),
);
}
}
// ...
MyApp sınıfı StatelessWidget sınıfını genişletir. Widget'lar, her Flutter uygulamasını oluştururken kullandığınız öğelerdir. Gördüğünüz gibi, uygulamanın kendisi bile bir widget'tır.
MyApp içindeki kod, uygulamanın tamamını kurar. Uygulama genelinde durumu oluşturur (bu konuyla ilgili daha fazla bilgiye ilerleyen bölümlerde yer verilecektir), uygulamayı adlandırır, görsel temayı tanımlar ve uygulamanızın başlangıç noktası olan "ana sayfa" widget'ını ayarlar.
lib/main.dart
// ...
class MyAppState extends ChangeNotifier {
var current = WordPair.random();
}
// ...
Ardından, MyAppState sınıfı uygulamanın durumunu tanımlar. Bu, Flutter'a ilk adımınız olduğu için bu codelab'de basit ve odaklanmış bir yaklaşım izlenecektir. Flutter'da uygulama durumunu yönetmenin birçok güçlü yolu vardır. En kolay açıklanabilenlerden biri, bu uygulamanın kullandığı yaklaşım olan ChangeNotifier'dır.
MyAppState, uygulamanın çalışması için gereken verileri tanımlar. Şu anda yalnızca mevcut rastgele kelime çiftini içeren tek bir değişken içerir. Bunu daha sonra ekleyebilirsiniz.- Durum sınıfı
ChangeNotifiersınıfını genişletir. Bu nedenle, kendi değişiklikleri hakkında diğerlerini bilgilendirebilir. Örneğin, mevcut kelime çifti değişirse uygulamadaki bazı widget'ların bunu bilmesi gerekir. - Durum,
ChangeNotifierProviderkullanılarak oluşturulur ve uygulamanın tamamına sağlanır (MyAppbölümündeki yukarıdaki koda bakın). Bu, uygulamadaki herhangi bir widget'ın durumu elde etmesine olanak tanır.

lib/main.dart
// ...
class MyHomePage extends StatelessWidget {
@override
Widget build(BuildContext context) { // ← 1
var appState = context.watch<MyAppState>(); // ← 2
return Scaffold( // ← 3
body: Column( // ← 4
children: [
Text('A random AWESOME idea:'), // ← 5
Text(appState.current.asLowerCase), // ← 6
ElevatedButton(
onPressed: () {
print('button pressed!');
},
child: Text('Next'),
),
], // ← 7
),
);
}
}
// ...
Son olarak, MyHomePage simgesi, daha önce değiştirdiğiniz widget'ı gösterir. Aşağıdaki her numaralı satır, yukarıdaki koddaki bir satır numarası yorumuyla eşleşir:
- Her widget, widget'ın koşulları her değiştiğinde otomatik olarak çağrılan bir
build()yöntemi tanımlar. Böylece widget her zaman güncel kalır. MyHomePagewatchyöntemiyle uygulamanın mevcut durumundaki değişiklikleri izler.- Her
buildyöntemi bir widget veya (daha yaygın olarak) iç içe yerleştirilmiş bir widget ağacı döndürmelidir. Bu örnekte, en üst düzey widgetScaffold'dır. Bu codelab'deScaffoldile çalışmayacaksınız ancak bu widget, gerçek dünyadaki Flutter uygulamalarının büyük çoğunluğunda bulunur ve faydalıdır. Column, Flutter'daki en temel düzen widget'larından biridir. İstenen sayıda çocuğu alır ve yukarıdan aşağıya doğru bir sütuna yerleştirir. Varsayılan olarak sütun, alt öğelerini görsel olarak en üste yerleştirir. Yakında bu sütunu ortalayacaksınız.- İlk adımda bu
Textwidget'ını değiştirdiniz. - Bu ikinci
Textwidget'ıappStatealır ve bu sınıfın tek üyesi olancurrent'ye (WordPair) erişir.WordPair,asPascalCaseveyaasSnakeCasegibi çeşitli yararlı alıcılar sağlar. BuradaasLowerCasekullanıyoruz ancak alternatiflerden birini tercih ederseniz bunu şimdi değiştirebilirsiniz. - Flutter kodunda sondaki virgüllerin yoğun olarak kullanıldığına dikkat edin.
children, buColumnparametre listesinin son (ve aynı zamanda tek) üyesi olduğundan bu virgülün burada olması gerekmez. Ancak genellikle sondaki virgülleri kullanmak iyi bir fikirdir: Bu virgüller, daha fazla üye eklemeyi kolaylaştırır ve Dart'ın otomatik biçimlendiricisinin oraya yeni bir satır eklemesi için ipucu görevi görür. Daha fazla bilgi için Kod biçimlendirme konusuna bakın.
Ardından, düğmeyi duruma bağlarsınız.
İlk davranışınız
MyAppState bölümüne gidin ve getNext yöntemi ekleyin.
lib/main.dart
// ...
class MyAppState extends ChangeNotifier {
var current = WordPair.random();
// ↓ Add this.
void getNext() {
current = WordPair.random();
notifyListeners();
}
}
// ...
Yeni getNext() yöntemi, current değerini yeni bir rastgele WordPair ile yeniden atar. Ayrıca, notifyListeners() (ChangeNotifier yöntemi) çağrılır. Bu yöntem, MyAppState izleyen herkesin bilgilendirilmesini sağlar.
Geriye yalnızca düğmenin geri aramasından getNext yöntemini çağırmak kalır.
lib/main.dart
// ...
ElevatedButton(
onPressed: () {
appState.getNext(); // ← This instead of print().
},
child: Text('Next'),
),
// ...
Kaydedin ve uygulamayı hemen deneyin. Sonraki düğmesine her bastığınızda yeni bir rastgele kelime çifti oluşturulmalıdır.
Sonraki bölümde, kullanıcı arayüzünü daha güzel hale getireceksiniz.
5. Uygulamayı daha güzel hale getirme
Uygulama şu anda bu şekilde görünüyor.

Kötü. Uygulamanın temel unsuru olan rastgele oluşturulmuş kelime çifti daha görünür olmalıdır. Sonuçta kullanıcılarımızın bu uygulamayı kullanmasının temel nedeni bu! Ayrıca, uygulama içerikleri tuhaf bir şekilde ortalanmamış ve uygulamanın tamamı sıkıcı bir şekilde siyah beyaz.
Bu bölüm, uygulamanın tasarımı üzerinde çalışarak bu sorunları ele alır. Bu bölümün nihai hedefi şuna benzer:

Widget ayıklama
Mevcut kelime çiftini göstermekten sorumlu satır artık şöyle görünüyor: Text(appState.current.asLowerCase). Bu satırı daha karmaşık bir şeye dönüştürmek için ayrı bir widget'a çıkarmanız önerilir. Kullanıcı arayüzünüzün ayrı mantıksal bölümleri için ayrı widget'lar kullanmak, Flutter'da karmaşıklığı yönetmenin önemli bir yoludur.
Flutter, widget'ları ayıklamak için yeniden düzenleme yardımcı programı sunar. Ancak bu yardımcı programı kullanmadan önce, ayıklanan satırın yalnızca ihtiyacı olanlara eriştiğinden emin olun. Şu anda satır appState öğesine erişiyor ancak aslında yalnızca mevcut kelime çiftinin ne olduğunu bilmesi gerekiyor.
Bu nedenle, MyHomePage widget'ını aşağıdaki gibi yeniden yazın:
lib/main.dart
// ...
class MyHomePage extends StatelessWidget {
@override
Widget build(BuildContext context) {
var appState = context.watch<MyAppState>();
var pair = appState.current; // ← Add this.
return Scaffold(
body: Column(
children: [
Text('A random AWESOME idea:'),
Text(pair.asLowerCase), // ← Change to this.
ElevatedButton(
onPressed: () {
appState.getNext();
},
child: Text('Next'),
),
],
),
);
}
}
// ...
Güzel. Text widget'ı artık appState'nin tamamını ifade etmiyor.
Şimdi Refactor (Yeniden düzenle) menüsünü çağırın. VS Code'da bu işlemi iki şekilde yapabilirsiniz:
- Yeniden düzenlemek istediğiniz kod parçasını (bu örnekte
Text) sağ tıklayın ve açılır menüden Yeniden düzenle...'yi seçin.
VEYA
- İmlecinizi yeniden düzenlemek istediğiniz kod parçasına (bu örnekte
Text) getirin veCtrl+.(Win/Linux) veyaCmd+.(Mac) tuşuna basın.
Refactor (Yeniden düzenle) menüsünde Extract Widget'ı (Widget'ı ayıkla) seçin. BigCard gibi bir ad atayın ve Enter simgesini tıklayın.
Bu işlem, geçerli dosyanın sonuna otomatik olarak yeni bir sınıf (BigCard) ekler. Sınıf aşağıdaki gibi görünür:
lib/main.dart
// ...
class BigCard extends StatelessWidget {
const BigCard({super.key, required this.pair});
final WordPair pair;
@override
Widget build(BuildContext context) {
return Text(pair.asLowerCase);
}
}
// ...
Uygulamanın bu yeniden düzenleme sırasında bile çalışmaya devam ettiğini fark edin.
Kart ekleme
Şimdi bu yeni widget'ı, bu bölümün başında hayal ettiğimiz cesur kullanıcı arayüzü parçası haline getirme zamanı.
BigCard sınıfını ve bu sınıfın içindeki build() yöntemini bulun. Daha önce olduğu gibi, Text widget'ında Refactor (Yeniden düzenle) menüsünü açın. Ancak bu kez widget'ı ayıklamayacaksınız.
Bunun yerine Dolguyla Sar'ı seçin. Bu işlem, Text widget'ının etrafında Padding adlı yeni bir üst widget oluşturur. Kaydettikten sonra rastgele kelimenin daha fazla boşluk içerdiğini görürsünüz.
Dolgu değerini varsayılan değer olan 8.0'dan artırın. Örneğin, daha geniş dolgu için 20 gibi bir karakter kullanın.
Ardından, bir üst seviyeye geçin. İmlecinizi Padding widget'ının üzerine getirin, Refactor (Yeniden düzenle) menüsünü açın ve Wrap with widget... (Widget ile sarmala...) seçeneğini belirleyin.
Bu sayede üst widget'ı belirleyebilirsiniz. "Kart" yazıp Enter tuşuna basın.
Bu, Padding widget'ını ve dolayısıyla Text widget'ını Card widget'ı ile sarmalar.
lib/main.dart
// ...
class BigCard extends StatelessWidget {
const BigCard({super.key, required this.pair});
final WordPair pair;
@override
Widget build(BuildContext context) {
return Card(
child: Padding(
padding: const EdgeInsets.all(20),
child: Text(pair.asLowerCase),
),
);
}
}
// ...
Uygulama artık aşağıdaki gibi görünecektir:

Tema ve stil
Kartın daha çok dikkat çekmesi için daha canlı bir renkle boyayın. Tutarlı bir renk şeması kullanmak her zaman iyi bir fikir olduğundan rengi seçmek için uygulamanın Theme simgesini kullanın.
BigCard'nın build() yönteminde aşağıdaki değişiklikleri yapın.
lib/main.dart
// ...
@override
Widget build(BuildContext context) {
final theme = Theme.of(context); // ← Add this.
return Card(
color: theme.colorScheme.primary, // ← And also this.
child: Padding(
padding: const EdgeInsets.all(20),
child: Text(pair.asLowerCase),
),
);
}
// ...
Bu iki yeni satır çok işe yarar:
- Öncelikle kod,
Theme.of(context)ile uygulamanın mevcut temasını ister. - Ardından, kod kartın rengini temanın
colorSchemeözelliğiyle aynı olacak şekilde tanımlar. Renk şeması birçok renk içeriyor veprimary, uygulamanın en belirgin ve tanımlayıcı rengi.
Kart artık uygulamanın birincil rengiyle boyanıyor:

Bu rengi ve uygulamanın tamamının renk şemasını değiştirmek için MyApp simgesine kadar yukarı kaydırıp ColorScheme için başlangıç rengini değiştirin.
Rengin sorunsuz bir şekilde nasıl canlandığına dikkat edin. Buna örtülü animasyon denir. Birçok Flutter widget'ı, kullanıcı arayüzünün durumlar arasında "atlamaması" için değerler arasında sorunsuz bir şekilde enterpolasyon yapar.
Kartın altındaki yükseltilmiş düğme de renk değiştirir. Değerleri sabit kodlamak yerine uygulama genelinde Theme kullanmanın avantajı budur.
TextTheme
Kartta hâlâ sorun var: Metin çok küçük ve rengi zor okunuyor. Bu sorunu düzeltmek için BigCard'nın build() yönteminde aşağıdaki değişiklikleri yapın.
lib/main.dart
// ...
@override
Widget build(BuildContext context) {
final theme = Theme.of(context);
// ↓ Add this.
final style = theme.textTheme.displayMedium!.copyWith(
color: theme.colorScheme.onPrimary,
);
return Card(
color: theme.colorScheme.primary,
child: Padding(
padding: const EdgeInsets.all(20),
// ↓ Change this line.
child: Text(pair.asLowerCase, style: style),
),
);
}
// ...
Bu değişikliğin nedeni:
theme.textThemesimgesini kullanarak uygulamanın yazı tipi temasına erişebilirsiniz. Bu sınıftabodyMedium(orta boyuttaki standart metinler için),caption(resimlerin altyazıları için) veyaheadlineLarge(büyük başlıklar için) gibi üyeler bulunur.displayMediumözelliği, metin görüntülemek için tasarlanmış büyük bir stildir. Görüntüleme kelimesi burada görüntüleme yazı tipi gibi tipografik anlamda kullanılmaktadır.displayMediumdokümanlarında "görüntüleme stilleri kısa ve önemli metinler için ayrılmıştır" ifadesi yer alıyor. Bu, tam olarak bizim kullanım alanımız.- Temanın
displayMediumözelliği teorik olaraknullolabilir. Bu uygulamayı yazdığınız programlama dili olan Dart, null güvenlidir. Bu nedenle, potansiyel olaraknullolan nesnelerin yöntemlerini çağırmanıza izin vermez. Ancak bu durumda, ne yaptığınızı Dart'a bildirmek için!operatörünü ("bang operatörü") kullanabilirsiniz. (displayMediumbu durumda kesinlikle boş değildir. Bunu nasıl bildiğimiz bu codelab'in kapsamı dışındadır.) copyWith()ondisplayMediumişlevini çağırmak, tanımladığınız değişiklikleri içeren metin stilinin kopyasını döndürür. Bu durumda yalnızca metnin rengini değiştiriyorsunuz.- Yeni rengi almak için uygulamanın temasına tekrar erişirsiniz. Renk şemasının
onPrimaryözelliği, uygulamanın birincil rengi üzerinde kullanıma uygun bir rengi tanımlar.
Uygulama artık aşağıdaki gibi görünecektir:

İsterseniz kartı daha fazla değiştirebilirsiniz. Aşağıdaki önerilerden yararlanabilirsiniz:
copyWith()simgesi, metin stilinde renkten çok daha fazla şeyi değiştirmenize olanak tanır. Değiştirebileceğiniz özelliklerin tam listesini görmek için imlecinizicopyWith()parantezlerinin içine yerleştirin veCtrl+Shift+Space(Win/Linux) veyaCmd+Shift+Space(Mac) tuşuna basın.- Benzer şekilde,
Cardwidget'ı hakkında daha fazla değişiklik yapabilirsiniz. Örneğin,elevationparametresinin değerini artırarak kartın gölgesini büyütebilirsiniz. - Renklerle denemeler yapmayı deneyin.
theme.colorScheme.primarydışında.secondary,.surfaceve daha birçok kişi var. Bu renklerin tümününonPrimarykarşılığı vardır.
Erişilebilirliği artırma
Flutter, uygulamaları varsayılan olarak erişilebilir hale getirir. Örneğin, her Flutter uygulaması, uygulamadaki tüm metinleri ve etkileşimli öğeleri TalkBack ve VoiceOver gibi ekran okuyuculara doğru şekilde gösterir.

Ancak bazen biraz çalışmanız gerekir. Bu uygulamada, ekran okuyucu oluşturulan bazı kelime çiftlerini telaffuz etmekte sorun yaşayabilir. İnsanlar cheaphead kelimesindeki iki kelimeyi tanımlamakta sorun yaşamazken ekran okuyucu, kelimenin ortasındaki ph harflerini f olarak telaffuz edebilir.
Çözüm olarak pair.asLowerCase yerine "${pair.first} ${pair.second}" kullanabilirsiniz. İkincisi, pair içinde bulunan iki kelimeden bir dize (ör. "cheap head") oluşturmak için dize enterpolasyonu kullanır. Birleşik kelime yerine iki ayrı kelime kullanmak, ekran okuyucuların bunları uygun şekilde tanımlamasını sağlar ve görme engelli kullanıcılara daha iyi bir deneyim sunar.
Ancak pair.asLowerCase'nın görsel basitliğini korumak isteyebilirsiniz. Metin widget'ının görsel içeriğini, ekran okuyucular için daha uygun olan semantik bir içerikle geçersiz kılmak için Text'ın semanticsLabel özelliğini kullanın:
lib/main.dart
// ...
@override
Widget build(BuildContext context) {
final theme = Theme.of(context);
final style = theme.textTheme.displayMedium!.copyWith(
color: theme.colorScheme.onPrimary,
);
return Card(
color: theme.colorScheme.primary,
child: Padding(
padding: const EdgeInsets.all(20),
// ↓ Make the following change.
child: Text(
pair.asLowerCase,
style: style,
semanticsLabel: "${pair.first} ${pair.second}",
),
),
);
}
// ...
Artık ekran okuyucular, oluşturulan her kelime çiftini doğru şekilde telaffuz ediyor ancak kullanıcı arayüzü aynı kalıyor. Cihazınızda ekran okuyucu kullanarak bu özelliği deneyin.
Kullanıcı arayüzünü ortalama
Rastgele kelime çifti yeterli görsel öğeyle sunulduğuna göre artık bunu uygulamanın penceresinin/ekranının ortasına yerleştirme zamanı.
Öncelikle, BigCard simgesinin Column simgesinin bir parçası olduğunu unutmayın. Varsayılan olarak sütunlar, alt öğelerini en üste yerleştirir ancak bu durumu geçersiz kılabiliriz. MyHomePage'nın build() yöntemine gidin ve aşağıdaki değişikliği yapın:
lib/main.dart
// ...
class MyHomePage extends StatelessWidget {
@override
Widget build(BuildContext context) {
var appState = context.watch<MyAppState>();
var pair = appState.current;
return Scaffold(
body: Column(
mainAxisAlignment: MainAxisAlignment.center, // ← Add this.
children: [
Text('A random AWESOME idea:'),
BigCard(pair: pair),
ElevatedButton(
onPressed: () {
appState.getNext();
},
child: Text('Next'),
),
],
),
);
}
}
// ...
Bu, çocukları ana (dikey) ekseni boyunca Column içinde ortalar.

Çocuklar, sütunun çapraz ekseni boyunca zaten ortalanmıştır (diğer bir deyişle, yatay olarak ortalanmışlardır). Ancak Column kendisi Scaffold içinde ortalanmamıştır. Bu durumu Widget İnceleyici'yi kullanarak doğrulayabiliriz.
Widget Inspector'ın kendisi bu codelab'in kapsamı dışındadır ancak Column vurgulandığında uygulamanın tüm genişliğini kaplamadığını görebilirsiniz. Yalnızca alt öğelerinin ihtiyaç duyduğu yatay alanı kaplar.
Sütunu doğrudan ortalayabilirsiniz. İmlecinizi Column üzerine getirin, Yeniden düzenle menüsünü (Ctrl+. veya Cmd+. ile) çağırın ve Wrap with Center'ı (Merkezle Sar) seçin.
Uygulama artık aşağıdaki gibi görünecektir:

İsterseniz bu ayarı biraz daha değiştirebilirsiniz.
Textwidget'ını yukarıdakiBigCardbölümünden kaldırabilirsiniz. Kullanıcı arayüzü bu metin olmadan da anlaşılır olduğundan açıklayıcı metne ("Rastgele bir HARİKA fikir:") artık gerek olmadığı söylenebilir. Bu şekilde daha temiz olur.BigCardileElevatedButtonarasınaSizedBox(height: 10)widget'ı da ekleyebilirsiniz. Bu şekilde, iki widget arasında biraz daha fazla ayrım olur.SizedBoxwidget'ı yalnızca yer kaplar ve kendi başına hiçbir şey oluşturmaz. Genellikle görsel "boşluklar" oluşturmak için kullanılır.
İsteğe bağlı değişikliklerle birlikte MyHomePage şu kodu içerir:
lib/main.dart
// ...
class MyHomePage extends StatelessWidget {
@override
Widget build(BuildContext context) {
var appState = context.watch<MyAppState>();
var pair = appState.current;
return Scaffold(
body: Center(
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: [
BigCard(pair: pair),
SizedBox(height: 10),
ElevatedButton(
onPressed: () {
appState.getNext();
},
child: Text('Next'),
),
],
),
),
);
}
}
// ...
Uygulama aşağıdaki gibi görünür:

Sonraki bölümde, oluşturulan kelimeleri favorilere ekleme (veya "beğenme") özelliğini ekleyeceksiniz.
6. İşlev ekleme
Uygulama çalışıyor ve bazen ilginç kelime çiftleri bile sunuyor. Ancak kullanıcı Sonraki'yi tıkladığında her kelime çifti kalıcı olarak kaybolur. En iyi önerileri "hatırlamak" için bir yol (ör. "Beğen" düğmesi) olması daha iyi olur.

İş mantığını ekleme
MyAppState bölümüne gidin ve aşağıdaki kodu ekleyin:
lib/main.dart
// ...
class MyAppState extends ChangeNotifier {
var current = WordPair.random();
void getNext() {
current = WordPair.random();
notifyListeners();
}
// ↓ Add the code below.
var favorites = <WordPair>[];
void toggleFavorite() {
if (favorites.contains(current)) {
favorites.remove(current);
} else {
favorites.add(current);
}
notifyListeners();
}
}
// ...
Değişiklikleri inceleyin:
MyAppStateadlı mülkefavoritesadlı yeni bir mülk eklediniz. Bu özellik boş bir liste ile başlatılır:[].- Ayrıca, listede yalnızca jenerik kullanılarak
<WordPair>[]kelime çiftlerinin yer alabileceğini belirttiniz. Bu, uygulamanızı daha sağlam hale getirir. Dart, uygulamanızaWordPairdışında bir şey eklemeye çalışırsanız uygulamanızı çalıştırmayı bile reddeder. Böylece,favoriteslistesinde hiçbir zaman istenmeyen nesnelerin (ör.null) gizlenemeyeceğini bilerek bu listeyi kullanabilirsiniz.
- Ayrıca, mevcut kelime çiftini favoriler listesinden kaldıran (zaten listede varsa) veya listeye ekleyen (henüz listede yoksa) yeni bir yöntem olan
toggleFavorite()'yı da ekledik. Her iki durumda da kod daha sonranotifyListeners();işlevini çağırır.
Düğmeyi ekleme
"İş mantığı" tamamlandığına göre kullanıcı arayüzü üzerinde tekrar çalışmanın zamanı geldi. "Beğen" düğmesini "Sonraki" düğmesinin soluna yerleştirmek için Row gerekir. Row widget'ı, daha önce gördüğünüz Column widget'ının yatay versiyonudur.
Öncelikle mevcut düğmeyi Row ile sarmalayın. MyHomePage'nın build() yöntemine gidin, imlecinizi ElevatedButton üzerine getirin, Ctrl+. veya Cmd+. ile Yeniden düzenle menüsünü açın ve Satırla sarmala'yı seçin.
Kaydettiğinizde Row öğesinin Column öğesine benzer şekilde davrandığını fark edeceksiniz. Varsayılan olarak, alt öğelerini sola doğru gruplandırır. (Column, çocuklarını en üste yerleştirmiş.) Bu sorunu düzeltmek için öncekiyle aynı yaklaşımı kullanabilirsiniz ancak bu kez mainAxisAlignment ile. Ancak didaktik (öğrenme) amaçlarla mainAxisSize kullanın. Bu, Row öğesine mevcut yatay alanın tamamını kullanmaması gerektiğini söyler.
Aşağıdaki değişikliği yapın:
lib/main.dart
// ...
class MyHomePage extends StatelessWidget {
@override
Widget build(BuildContext context) {
var appState = context.watch<MyAppState>();
var pair = appState.current;
return Scaffold(
body: Center(
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: [
BigCard(pair: pair),
SizedBox(height: 10),
Row(
mainAxisSize: MainAxisSize.min, // ← Add this.
children: [
ElevatedButton(
onPressed: () {
appState.getNext();
},
child: Text('Next'),
),
],
),
],
),
),
);
}
}
// ...
Kullanıcı arayüzü, önceki haline geri döner.

Ardından Beğen düğmesini ekleyin ve toggleFavorite()'ye bağlayın. Kendinizi test etmek için önce aşağıdaki kod bloğuna bakmadan bunu kendiniz yapmaya çalışın.

Aşağıdakiyle tamamen aynı şekilde yapmamanızda bir sakınca yoktur. Hatta büyük bir zorluk istemiyorsanız kalp simgesiyle ilgili endişelenmenize gerek yok.
Başarısız olmanız da tamamen normaldir. Sonuçta Flutter ile ilk saatinizi geçiriyorsunuz.

İkinci düğmeyi MyHomePage öğesine eklemenin bir yolu aşağıda verilmiştir. Bu kez, simge içeren bir düğme oluşturmak için ElevatedButton.icon() oluşturucuyu kullanın. Ayrıca build yönteminin üst kısmında, mevcut kelime çiftinin favorilerde olup olmamasına bağlı olarak uygun simgeyi seçin. Ayrıca, iki düğmeyi biraz ayrı tutmak için SizedBox karakterinin tekrar kullanıldığına dikkat edin.
lib/main.dart
// ...
class MyHomePage extends StatelessWidget {
@override
Widget build(BuildContext context) {
var appState = context.watch<MyAppState>();
var pair = appState.current;
// ↓ Add this.
IconData icon;
if (appState.favorites.contains(pair)) {
icon = Icons.favorite;
} else {
icon = Icons.favorite_border;
}
return Scaffold(
body: Center(
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: [
BigCard(pair: pair),
SizedBox(height: 10),
Row(
mainAxisSize: MainAxisSize.min,
children: [
// ↓ And this.
ElevatedButton.icon(
onPressed: () {
appState.toggleFavorite();
},
icon: Icon(icon),
label: Text('Like'),
),
SizedBox(width: 10),
ElevatedButton(
onPressed: () {
appState.getNext();
},
child: Text('Next'),
),
],
),
],
),
),
);
}
}
// ...
Uygulama aşağıdaki gibi görünmelidir:
Maalesef kullanıcı favorileri göremez. Uygulamamıza tamamen ayrı bir ekran eklemenin zamanı geldi. Bir sonraki bölümde görüşmek üzere.
7. Gezinme sütunu ekleme
Çoğu uygulama, her şeyi tek bir ekrana sığdıramaz. Bu uygulama muhtemelen yapabilir ancak didaktik amaçlarla kullanıcının favorileri için ayrı bir ekran oluşturacaksınız. İki ekran arasında geçiş yapmak için ilk StatefulWidget öğenizi uygulayacaksınız.

Bu adımın özüne en kısa sürede ulaşmak için MyHomePage öğesini 2 ayrı widget'a bölün.
MyHomePage öğesinin tamamını seçin, silin ve aşağıdaki kodla değiştirin:
lib/main.dart
// ...
class MyHomePage extends StatelessWidget {
@override
Widget build(BuildContext context) {
return Scaffold(
body: Row(
children: [
SafeArea(
child: NavigationRail(
extended: false,
destinations: [
NavigationRailDestination(
icon: Icon(Icons.home),
label: Text('Home'),
),
NavigationRailDestination(
icon: Icon(Icons.favorite),
label: Text('Favorites'),
),
],
selectedIndex: 0,
onDestinationSelected: (value) {
print('selected: $value');
},
),
),
Expanded(
child: Container(
color: Theme.of(context).colorScheme.primaryContainer,
child: GeneratorPage(),
),
),
],
),
);
}
}
class GeneratorPage extends StatelessWidget {
@override
Widget build(BuildContext context) {
var appState = context.watch<MyAppState>();
var pair = appState.current;
IconData icon;
if (appState.favorites.contains(pair)) {
icon = Icons.favorite;
} else {
icon = Icons.favorite_border;
}
return Center(
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: [
BigCard(pair: pair),
SizedBox(height: 10),
Row(
mainAxisSize: MainAxisSize.min,
children: [
ElevatedButton.icon(
onPressed: () {
appState.toggleFavorite();
},
icon: Icon(icon),
label: Text('Like'),
),
SizedBox(width: 10),
ElevatedButton(
onPressed: () {
appState.getNext();
},
child: Text('Next'),
),
],
),
],
),
);
}
}
// ...
Kaydettiğinizde kullanıcı arayüzünün görsel tarafının hazır olduğunu görürsünüz ancak bu taraf çalışmaz. Gezinme çubuğunda ♥︎ (kalp) simgesini tıklamanın herhangi bir etkisi olmaz.

Değişiklikleri inceleyin.
- Öncelikle
MyHomePageiçindeki tüm içeriklerinGeneratorPageadlı yeni bir widget'a çıkarıldığını unutmayın. EskiMyHomePagewidget'ının yalnızcaScaffoldkısmı ayıklanmadı. - Yeni
MyHomePage, iki çocuklu birRowiçeriyor. İlk widgetSafeArea, ikinci widget iseExpandedwidget'ıdır. SafeArea, alt öğesinin donanım çentiği veya durum çubuğu tarafından gizlenmemesini sağlar. Bu uygulamada, gezinme düğmelerinin örneğin bir mobil durum çubuğu tarafından gizlenmesini önlemek için widgetNavigationRailöğesini sarar.extended: falsesatırınıNavigationRailbölümündetrueolarak değiştirebilirsiniz. Bu işlem, simgelerin yanındaki etiketleri gösterir. Gelecekteki bir adımda, uygulama yeterli yatay alana sahip olduğunda bunu otomatik olarak nasıl yapacağınızı öğreneceksiniz.- Geçiş çubuğunda, ilgili simgeleri ve etiketleriyle birlikte iki hedef (Ana Sayfa ve Favoriler) bulunur. Ayrıca mevcut
selectedIndexdeğerini de tanımlar. Sıfır dizini seçildiğinde ilk hedef, bir dizini seçildiğinde ikinci hedef seçilir. Şimdilik sıfır olarak sabit kodlanmıştır. - Geçiş çubuğu, kullanıcının
onDestinationSelectedile hedeflerden birini seçtiğinde ne olacağını da tanımlar. Şu anda uygulama, yalnızcaprint()ile istenen dizin değerini çıkış olarak veriyor. Rowöğesinin ikinci alt öğesiExpandedwidget'ıdır. Genişletilmiş widget'lar satır ve sütunlarda son derece kullanışlıdır. Bazı öğelerin yalnızca ihtiyaç duydukları kadar alan kapladığı (bu örnekteSafeArea) ve diğer widget'ların kalan alanın mümkün olduğunca büyük bir kısmını kapladığı (bu örnekteExpanded) düzenler oluşturmanıza olanak tanır.Expandedwidget'ları "açgözlü" olarak düşünebilirsiniz. Bu widget'ın rolünü daha iyi anlamak istiyorsanızSafeAreawidget'ını başka birExpandedile sarmayı deneyin. Elde edilen düzen şu şekilde görünür:

- İki
Expandedwidget'ı, gezinme çubuğunun yalnızca solda küçük bir alana ihtiyacı olmasına rağmen mevcut tüm yatay alanı aralarında paylaşıyor. Expandedwidget'ının içinde renkli birContainer, kapsayıcının içinde iseGeneratorPagebulunur.
Durum bilgisiz ve durum bilgili widget'lar
Şimdiye kadar MyAppState, eyaletinizdeki tüm ihtiyaçlarınızı karşılıyordu. Bu nedenle, şimdiye kadar yazdığınız tüm widget'lar durumsuzdur. Kendi değişebilir durumlarını içermezler. Widget'ların hiçbiri kendi başına değişemez. Değişiklikler MyAppState üzerinden yapılmalıdır.
Bu durum değişmek üzere.
Gezinme çubuğunun selectedIndex değerini tutmanın bir yolunu bulmanız gerekir. Ayrıca, bu değeri onDestinationSelected geri çağırma işlevinden değiştirebilmek de istiyorsunuz.
selectedIndex'ı MyAppState'ün başka bir özelliği olarak ekleyebilirsiniz. Bu yöntem işe yarayacaktır. Ancak her widget değerlerini bu durumda saklasaydı uygulama durumunun kısa sürede mantıksız bir şekilde büyüyeceğini tahmin edebilirsiniz.

Bazı durumlar yalnızca tek bir widget için geçerlidir. Bu nedenle, ilgili widget'ta kalmalıdır.
StatefulWidget türünde bir widget girin. Bu widget'ta State bulunur. Öncelikle MyHomePage öğesini durumlu bir widget'a dönüştürün.
İmlecinizi MyHomePage'nın ilk satırına (class MyHomePage... ile başlayan satır) getirin ve Ctrl+. veya Cmd+. tuşlarını kullanarak Refactor (Yeniden düzenle) menüsünü açın. Ardından Convert to StatefulWidget'ı (Durumlu widget'a dönüştür) seçin.
IDE, sizin için yeni bir sınıf oluşturur: _MyHomePageState. Bu sınıf State sınıfını genişletir ve bu nedenle kendi değerlerini yönetebilir. (Kendiliğinden değişebilir.) Ayrıca, eski, durum bilgisiz widget'taki build yönteminin widget'ta kalmak yerine _MyHomePageState'a taşındığını da unutmayın. Bu yöntem, kelimesi kelimesine taşındı. build yöntemi içinde hiçbir şey değişmedi. Artık yalnızca başka bir yerde yaşıyor.
setState
Yeni durumlu widget'ın yalnızca bir değişkeni izlemesi gerekir: selectedIndex. _MyHomePageState'da aşağıdaki 3 değişikliği yapın:
lib/main.dart
// ...
class _MyHomePageState extends State<MyHomePage> {
var selectedIndex = 0; // ← Add this property.
@override
Widget build(BuildContext context) {
return Scaffold(
body: Row(
children: [
SafeArea(
child: NavigationRail(
extended: false,
destinations: [
NavigationRailDestination(
icon: Icon(Icons.home),
label: Text('Home'),
),
NavigationRailDestination(
icon: Icon(Icons.favorite),
label: Text('Favorites'),
),
],
selectedIndex: selectedIndex, // ← Change to this.
onDestinationSelected: (value) {
// ↓ Replace print with this.
setState(() {
selectedIndex = value;
});
},
),
),
Expanded(
child: Container(
color: Theme.of(context).colorScheme.primaryContainer,
child: GeneratorPage(),
),
),
],
),
);
}
}
// ...
Değişiklikleri inceleyin:
- Yeni bir değişken (
selectedIndex) tanımlayıp0olarak başlatırsınız. - Bu yeni değişkeni,
NavigationRailtanımında şimdiye kadar kullanılan sabit kodlu0yerine kullanırsınız. onDestinationSelectedgeri çağırma işlevi çağrıldığında yeni değeri yalnızca konsola yazdırmak yerinesetState()çağrısı içindeselectedIndexdeğişkenine atarsınız. Bu çağrı, daha önce kullanılannotifyListeners()yöntemine benzer. Kullanıcı arayüzünün güncellenmesini sağlar.
Gezinme çubuğu artık kullanıcı etkileşimine yanıt veriyor. Ancak sağdaki genişletilmiş alan aynı kalır. Bunun nedeni, hangi ekranın görüntüleneceğini belirlemek için kodda selectedIndex kullanılmamasıdır.
selectedIndex özelliğini kullanma
Aşağıdaki kodu _MyHomePageState yönteminin en üstüne, build yönteminden hemen önce return Scaffold yerleştirin:
lib/main.dart
// ...
Widget page;
switch (selectedIndex) {
case 0:
page = GeneratorPage();
break;
case 1:
page = Placeholder();
break;
default:
throw UnimplementedError('no widget for $selectedIndex');
}
// ...
Şu kod parçasını inceleyin:
- Kod,
Widgettüründe yeni birpagedeğişkeni bildirir. - Ardından, bir switch ifadesi,
pagedeğişkenineselectedIndexdeğişkenindeki mevcut değere göre bir ekran atar. - Henüz
FavoritesPageolmadığı içinPlaceholder'ı kullanın. Bu kullanışlı widget, yerleştirdiğiniz her yere çapraz bir dikdörtgen çizer ve kullanıcı arayüzünün o bölümünü tamamlanmamış olarak işaretler.

- Hızlı hata ilkesini uygulayan switch ifadesi,
selectedIndex0 veya 1 değilse hata vermeyi de sağlar. Bu sayede ileride hataların oluşması önlenir. Gezinme çubuğuna yeni bir hedef ekler ve bu kodu güncellemeyi unutursanız program, geliştirme aşamasında kilitlenir (bu durumda, neden çalışmadığını tahmin etmenize veya hatalı kodu üretime yayınlamanıza izin verilmez).
page artık sağda göstermek istediğiniz widget'ı içerdiğine göre, başka hangi değişikliğin yapılması gerektiğini tahmin edebilirsiniz.
Tek bir değişiklik kaldıktan sonraki _MyHomePageState:
lib/main.dart
// ...
class _MyHomePageState extends State<MyHomePage> {
var selectedIndex = 0;
@override
Widget build(BuildContext context) {
Widget page;
switch (selectedIndex) {
case 0:
page = GeneratorPage();
break;
case 1:
page = Placeholder();
break;
default:
throw UnimplementedError('no widget for $selectedIndex');
}
return Scaffold(
body: Row(
children: [
SafeArea(
child: NavigationRail(
extended: false,
destinations: [
NavigationRailDestination(
icon: Icon(Icons.home),
label: Text('Home'),
),
NavigationRailDestination(
icon: Icon(Icons.favorite),
label: Text('Favorites'),
),
],
selectedIndex: selectedIndex,
onDestinationSelected: (value) {
setState(() {
selectedIndex = value;
});
},
),
),
Expanded(
child: Container(
color: Theme.of(context).colorScheme.primaryContainer,
child: page, // ← Here.
),
),
],
),
);
}
}
// ...
Uygulama artık GeneratorPage ile yakında Favoriler sayfası olacak yer tutucu arasında geçiş yapıyor.
Yanıt verme
Ardından, gezinme çubuğunu duyarlı hale getirin. Yani, yeterli alan olduğunda etiketlerin otomatik olarak gösterilmesini sağlayın (extended: true kullanarak).

Flutter, uygulamalarınızı otomatik olarak duyarlı hale getirmenize yardımcı olan çeşitli widget'lar sunar. Örneğin, Wrap, Row veya Column'a benzer bir widget'tır. Dikey veya yatay alan yeterli olmadığında alt öğeleri otomatik olarak bir sonraki "satıra" (çalıştırma olarak adlandırılır) kaydırır. FittedBox adlı bir widget vardır. Bu widget, çocuğunuza ait öğeyi, belirtimlerinize göre kullanılabilir alana otomatik olarak sığdırır.
Ancak NavigationRail, her bağlamda ne kadar alanın yeterli olduğunu bilemediği için yeterli alan olduğunda etiketleri otomatik olarak göstermez. Bu kararı geliştirici olarak siz verirsiniz.
Örneğin, etiketleri yalnızca MyHomePage en az 600 piksel genişliğinde olduğunda göstermeye karar verdiğinizi varsayalım.
Bu durumda kullanılacak widget LayoutBuilder'dır. Bu özellik, widget ağacınızı ne kadar boş alanınız olduğuna bağlı olarak değiştirmenize olanak tanır.
Gerekli değişiklikleri yapmak için VS Code'da Flutter'ın Refactor (Yeniden Düzenle) menüsünü kullanın. Ancak bu sefer durum biraz daha karmaşık:
_MyHomePageState'nınbuildyönteminde imleciniziScaffoldüzerine getirin.Ctrl+.(Windows/Linux) veyaCmd+.(Mac) ile Refactor (Yeniden Düzenle) menüsünü açın.- Builder ile sarmala'yı seçin ve Enter tuşuna basın.
- Yeni eklenen
Builderöğesinin adınıLayoutBuilderolarak değiştirin. - Geri çağırma parametresi listesini
(context)olarak değiştirin.(context, constraints)
Kısıtlamalar her değiştiğinde LayoutBuilder'ın builder geri çağırma işlevi çağrılır. Bu durum örneğin aşağıdaki durumlarda ortaya çıkar:
- Kullanıcı, uygulamanın penceresini yeniden boyutlandırırsa
- Kullanıcının telefonunu dikey moddan yatay moda veya tam tersi yönde döndürmesi
MyHomePageöğesinin yanındaki bazı widget'ların boyutu büyüdüğündeMyHomePageöğesinin kısıtlamaları küçülür.
Artık kodunuz, mevcut constraints sorgulanarak etiketin gösterilip gösterilmeyeceğine karar verebilir. _MyHomePageState'nın build yönteminde aşağıdaki tek satırlık değişikliği yapın:
lib/main.dart
// ...
class _MyHomePageState extends State<MyHomePage> {
var selectedIndex = 0;
@override
Widget build(BuildContext context) {
Widget page;
switch (selectedIndex) {
case 0:
page = GeneratorPage();
break;
case 1:
page = Placeholder();
break;
default:
throw UnimplementedError('no widget for $selectedIndex');
}
return LayoutBuilder(builder: (context, constraints) {
return Scaffold(
body: Row(
children: [
SafeArea(
child: NavigationRail(
extended: constraints.maxWidth >= 600, // ← Here.
destinations: [
NavigationRailDestination(
icon: Icon(Icons.home),
label: Text('Home'),
),
NavigationRailDestination(
icon: Icon(Icons.favorite),
label: Text('Favorites'),
),
],
selectedIndex: selectedIndex,
onDestinationSelected: (value) {
setState(() {
selectedIndex = value;
});
},
),
),
Expanded(
child: Container(
color: Theme.of(context).colorScheme.primaryContainer,
child: page,
),
),
],
),
);
});
}
}
// ...
Artık uygulamanız ekran boyutu, yön ve platform gibi ortamlara yanıt veriyor. Başka bir deyişle, duyarlıdır.
Geriye kalan tek iş, bu Placeholder öğesini gerçek bir Favoriler ekranıyla değiştirmektir. Bu konu bir sonraki bölümde ele alınmıştır.
8. Yeni sayfa ekle
Favoriler sayfası yerine kullandığımız Placeholder widget'ını hatırlıyor musunuz?
Bu sorunu düzeltmenin zamanı geldi.
Kendinize güveniyorsanız bu adımı kendi başınıza yapmayı deneyin. Amacınız, favorites listesini yeni bir durum bilgisiz widget'ta (FavoritesPage) göstermek ve ardından Placeholder yerine bu widget'ı göstermektir.
İşte birkaç ipucu:
- Kaydırılan bir
ColumnistediğinizdeListViewwidget'ını kullanın. MyAppStateörneğinecontext.watch<MyAppState>()kullanarak herhangi bir widget'tan erişebileceğinizi unutmayın.- Yeni bir widget da denemek istiyorsanız
ListTile,title(genellikle metin için),leading(simgeler veya avatarlar için) veonTap(etkileşimler için) gibi özelliklere sahiptir. Ancak, bildiğiniz widget'larla benzer efektler elde edebilirsiniz. - Dart, koleksiyon değişmezleri içinde
fordöngülerinin kullanılmasına izin verir. Örneğin,messagesdize listesi içeriyorsa aşağıdaki gibi bir kodunuz olabilir:

Öte yandan, işlevsel programlamaya daha aşina iseniz Dart, messages.map((m) => Text(m)).toList() gibi kodlar yazmanıza da olanak tanır. Elbette, her zaman bir widget listesi oluşturabilir ve build yöntemi içinde bu listeye zorunlu olarak ekleme yapabilirsiniz.
Favoriler sayfasını kendiniz eklemenin avantajı, kendi kararlarınızı vererek daha fazla şey öğrenmenizdir. Dezavantajı, henüz kendi başınıza çözemeyeceğiniz sorunlarla karşılaşabilirsiniz. Unutmayın: Başarısızlık, öğrenmenin en önemli unsurlarından biridir. İlk saatte Flutter geliştirmeyi mükemmel bir şekilde yapmanız beklenmez ve siz de kendinizden bunu beklememelisiniz.

Aşağıda, favoriler sayfasını uygulamanın bir yolu verilmiştir. Uygulama şekli, kodu denemeniz, kullanıcı arayüzünü iyileştirmeniz ve kendi tarzınıza göre düzenlemeniz için size ilham verecektir.
Yeni FavoritesPage sınıfı:
lib/main.dart
// ...
class FavoritesPage extends StatelessWidget {
@override
Widget build(BuildContext context) {
var appState = context.watch<MyAppState>();
if (appState.favorites.isEmpty) {
return Center(
child: Text('No favorites yet.'),
);
}
return ListView(
children: [
Padding(
padding: const EdgeInsets.all(20),
child: Text('You have '
'${appState.favorites.length} favorites:'),
),
for (var pair in appState.favorites)
ListTile(
leading: Icon(Icons.favorite),
title: Text(pair.asLowerCase),
),
],
);
}
}
Widget'ın işlevi:
- Uygulamanın mevcut durumunu alır.
- Favoriler listesi boşsa ortalanmış bir mesaj gösterilir: Henüz favori yok.
- Aksi takdirde, kaydırılabilir bir liste gösterilir.
- Liste, bir özetle başlar (örneğin, 5 favoriniz var.).
- Kod daha sonra tüm favorileri yineler ve her biri için bir
ListTilewidget'ı oluşturur.
Şimdi yapmanız gereken tek şey Placeholder widget'ını FavoritesPage ile değiştirmek. İşte bu kadar.
Bu uygulamanın son kodunu GitHub'daki codelab deposunda bulabilirsiniz.
9. Sonraki adımlar
Tebrikler!
Muhteşem görünüyorsunuz! Column ve iki Text widget'ı içeren işlevsel olmayan bir iskeleti alıp duyarlı ve keyifli küçük bir uygulamaya dönüştürdünüz.

İşlediğimiz konular
- Flutter'ın işleyiş şekliyle ilgili temel bilgiler
- Flutter'da düzen oluşturma
- Kullanıcı etkileşimlerini (ör. düğmeye basma) uygulama davranışına bağlama
- Flutter kodunuzu düzenli tutma
- Uygulamanızı duyarlı hale getirme
- Uygulamanızın tutarlı bir görünüm ve tarzda olmasını sağlama
Sırada ne var?
- Bu laboratuvar sırasında yazdığınız uygulamayla daha fazla deneme yapın.
- Animasyonlu listeler, gradyanlar, çapraz geçişler ve daha fazlasını nasıl ekleyebileceğinizi görmek için aynı uygulamanın bu gelişmiş sürümünün koduna göz atın.
- flutter.dev/learn adresine giderek öğrenme yolculuğunuzu takip edin.