1. Giriş
Flutter, Google'ın tek bir kod tabanından mobil, web ve masaüstü için uygulamalar oluşturmaya yönelik 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ı favorilere ekleyebilir ve favori adlar listesini ayrı bir sayfada inceleyebilir. Uygulama, farklı ekran boyutlarına duyarlıdır.
Öğrenecekleriniz
- 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şlarsınız.

Filip, 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ı kurma
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, 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ında anında yeniden yükleme yapamaz.
Seçiminizi şimdi yapın. Uygulamanızı daha sonra istediğiniz zaman diğer işletim sistemlerinde çalıştırabileceğinizi unutmayın. Ancak, net bir geliştirme hedefi belirlemek sonraki adımı kolaylaştırır.
Flutter'ı yükleme
Flutter SDK'nın nasıl yükleneceğiyle ilgili en güncel talimatları her zaman docs.flutter.dev adresinde bulabilirsiniz.
Flutter web sitesindeki talimatlar yalnızca SDK'nın yüklenmesini değil, aynı zamanda 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'sı
- Flutter eklentisiyle Visual Studio Code
- Seçtiğiniz geliştirme hedefi için gereken yazılım (örneğin, Windows'u hedeflemek için Visual Studio veya macOS'i hedeflemek için Xcode)
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 bulurum?
- 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 çıkabilirim? - "
cmdline-toolsbileşeni eksik" hatasıyla nasıl başa çıkarım? - 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 (F1 veya Ctrl+Shift+P veya Shift+Cmd+P ile) açın. "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 ad verin. namer_app veya my_awesome_namer gibi bir şey.

Flutter artık proje klasörünüzü oluşturur ve VS Code bu klasörü açar.
Artık 3 dosyanın içeriğinin üzerine uygulamanın temel iskeletini 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 aşağıdakiyle 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 birlikte 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 girişiniz olduğundan analizciye rahat 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 aşağıdakiyle 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 tamamını 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 görünmüyor:

İlk sıcak yeniden yükleme
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ü durumlu sıcak yeniden yükleme özelliğinin işleyişidir. 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 olur?
- 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 öğesinin 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 hızlandırılmış kursu
Hata Ayıklama Konsolu'nu izlemek eğlenceli olsa da düğmenin daha anlamlı bir işlem 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 haliyle 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 bilgiyi sonraki bölümlerde bulabilirsiniz), 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 izleyeceğiz. Flutter'da uygulama durumunu yönetmenin birçok etkili yolu vardır. En kolay açıklanabilenlerden biri, bu uygulamanın kullandığı yaklaşım olan ChangeNotifier'dır.
MyAppStateUygulamanı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 ekleyeceksiniz.- 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 sayede, uygulamadaki herhangi bir widget durumu elde edebilir.

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, yani daha önce değiştirdiğiniz widget'ı inceleyelim. Aşağıdaki her numaralı satır, yukarıdaki kodda bir satır numarası yorumuyla eşlenir:
- 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. MyHomePageyöntemiyle uygulamanın mevcut durumundaki değişiklikleri izler.watch- Her
buildyöntemi bir widget veya (daha yaygın olarak) iç içe yerleştirilmiş bir widget ağacı döndürmelidir. Bu örnekte ü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 bulunan faydalı bir widget'tı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. Sütun, varsayılan olarak alt öğelerini görsel olarak en üste yerleştirir. Yakında bu sütunu ortalayacak şekilde değiştireceksiniz.- İlk adımda bu
Textwidget'ını değiştirdiniz. - Bu ikinci
Textwidget'ıappStatealır ve bu sınıfın tek üyesi olancurrent'ye (WordPairolan) 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ığını fark etmişsinizdir.
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ğlayacaksı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()(MyAppState izleyen herkesin bilgilendirilmesini sağlayan bir ChangeNotifier) yöntemi) olarak da bilinir.
Geriye yalnızca düğmenin geri çağırma işlevinden 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.
Bir 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.

Çok iyi değil. Uygulamanın merkezinde yer alan, 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 şu şekilde görünüyor: Text(appState.current.asLowerCase). Bu satırı daha karmaşık bir hale getirmek 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'ın tamamını ifade etmiyor.
Şimdi Refactor (Yeniden düzenle) menüsünü açın. VS Code'da bunu 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 parça koduna (bu örnekte
Text) taşıyın 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) oluşturur. 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 işlemine rağmen çalışmaya devam ettiğine dikkat 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'ı 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 8.0 değerinden artırın. Örneğin, daha geniş bir dolgu için 20 gibi bir karakter kullanın.
Ardından, bir üst seviyeye gidin. İ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, üst widget'ı belirtmenize olanak tanır. "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 fazla öne çıkması için daha canlı bir renkle boyayın. Tutarlı bir renk düzeni kullanmak her zaman iyi bir fikir olduğundan rengi seçmek için uygulamanın Theme özelliğini 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ır:

Bu rengi ve uygulamanın tamamının renk şemasını değiştirmek için MyApp simgesine kaydırıp ColorScheme simgesinin temel rengini değiştirin.
Rengin nasıl sorunsuz bir şekilde 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.textTheme,özelliğini 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ü kelimesi burada görüntü 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 ifade, kullanım alanımızı tam olarak tanımlıyor.- 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 neden bildiğimiz bu codelab'in kapsamı dışındadır.) displayMediumüzerindecopyWith()iş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şin. Renk düzeninin
onPrimaryözelliği, uygulamanın birincil rengi üzerinde kullanıma uygun bir renk tanımlar.
Uygulama artık aşağıdaki gibi görünmelidir:

İ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 deneme yapmayı deneyin.
theme.colorScheme.primarydışında.secondary,.surfaceve daha pek çok sanatçı var. Bu renklerin hepsininonPrimarykarşı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 bazı oluşturulan 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 görselinin 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 öğesinin Column öğesinin bir parçası olduğunu unutmayın. Sütunlar varsayılan olarak alt öğelerini en üste toplar 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, alt öğeleri 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 zaten 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.
Yalnızca sütunu ortalayabilirsiniz. İmlecinizi Column üzerine getirin, Yeniden düzenle menüsünü (Ctrl+. veya Cmd+. ile) açın ve Wrap with Center'ı (Ortayla sarmala) seçin.
Uygulama artık aşağıdaki gibi görünmelidir:

İsterseniz bu ayarı biraz daha değiştirebilirsiniz.
Textwidget'ınıBigCardbölümünden kaldırabilirsiniz. Kullanıcı arayüzü, açıklayıcı metin olmadan bile anlamlı olduğundan bu metne ("Rastgele bir HARİKA fikir:") artık gerek olmadığı söylenebilir. Bu şekilde daha temiz olur.SizedBox(height: 10)ileElevatedButtonarasınaBigCardwidget'ı 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ı İleri'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 olurdu.

İş 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ıyor:[].- Ayrıca, jenerikler kullanarak listenin yalnızca kelime çiftleri içerebileceğini de belirttiniz:
<WordPair>[]. Bu, uygulamanızı daha sağlam hale getirir. Dart,WordPairdışı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's 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ı (varsayılan olarak alt öğelerini sola yığdığını) görürsünüz. (Column alt öğelerini en üste topladı.) 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ü eski haline 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 kendi başınıza yapmaya çalışın.

Aşağıdakiyle tam olarak aynı şekilde yapmamanızda bir sakınca yoktur. Hatta büyük bir zorluk istemiyorsanız kalp simgesiyle ilgili endişelenmeyin.
Başarısız olmak da tamamen normaldir. Sonuçta Flutter ile ilk saatinizdesiniz.

İkinci düğmeyi MyHomePage'ya eklemenin bir yolu aşağıda açıklanmıştır. Bu kez, simge içeren bir düğme oluşturmak için ElevatedButton.icon() oluşturucusunu 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ığını unutmayın.
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'),
),
],
),
],
),
);
}
}
// ...
Kaydedildiğinde, kullanıcı arayüzünün görsel tarafının hazır olduğunu görürsünüz ancak bu kısım çalışmaz. Gezinme çubuğunda ♥︎ (kalp) simgesini tıkladığınızda hiçbir şey olmaz.

Değişiklikleri inceleyin.
- Öncelikle,
MyHomePageiçindeki tüm içeriklerinGeneratorPageadlı yeni bir widget'a çıkarıldığını fark edin. EskiMyHomePagewidget'ının çıkarılmayan tek kısmıScaffold. - Yeni
MyHomePageöğesinde iki çocuklu birRowbulunuyor. Birinci 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 ayar, simgelerin yanında etiketleri gösterir. Gelecekteki bir adımda, uygulama yeterli yatay alana sahip olduğunda bunu otomatik olarak nasıl yapacağınızı öğreneceksiniz.- Gezinme çubuğunda, ilgili simgeleri ve etiketleriyle birlikte iki hedef (Ana Sayfa ve Favoriler) bulunur. Ayrıca geçerli
selectedIndexdeğerini de tanımlar. Sıfır seçili dizini ilk hedefi, bir seçili dizini ikinci hedefi vb. seçer. Şimdilik sıfır olarak kodlanmıştır. - Gezinme ç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ç duyduğu kadar alan kapladığı (bu örnekteSafeArea) ve diğer widget'ların kalan alanın mümkün olduğunca çoğunu 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 aşağıdaki gibi görünür:

- Gezinme çubuğunun aslında solda küçük bir alana ihtiyacı olmasına rağmen iki
Expandedwidget'ı, mevcut tüm yatay alanı kendi aralarında bölü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 kendini değiştiremez. Değişiklikler MyAppState üzerinden yapılmalıdır.
Bu durum değişmek üzere.
Gezinme çubuğunun selectedIndex değerini tutmak için bir yöntem gerekir. Ayrıca, bu değeri onDestinationSelected geri çağırma işlevinden değiştirebilmek istiyorsunuz.
selectedIndex öğesini MyAppState öğesinin başka bir özelliği olarak ekleyebilirsiniz. Bu yöntem işe yarayacaktır. Ancak her widget değerlerini bu durumda saklarsa uygulama durumunun kısa sürede mantıksız bir şekilde büyüyeceğini tahmin edebilirsiniz.

Bazı durumlar yalnızca tek bir widget ile alakalıdır ve bu nedenle ilgili widget'ta kalmalıdır.
StatefulWidget değerini girin. Bu, State içeren bir widget türüdür. Ö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) getirin ve Ctrl+. veya Cmd+. tuşlarını kullanarak 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 bilgisi içermeyen 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 üzerinde 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 olan
selectedIndex'yı tanıtıp0olarak başlatıyorsunuz. - 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çindeselectedIndexöğesine 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, kodun hangi ekranın görüntüleneceğini belirlemek için selectedIndex kullanmamasıdır.
selectedIndex özelliğini kullanma
Aşağıdaki kodu _MyHomePageState yönteminin en üstüne, return Scaffold'den hemen önce yerleştirin:build
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 kodu inceleyin:
- Kod,
pagetüründe yeni bir değişken (Widget) bildirir. - Ardından, bir switch ifadesi,
selectedIndexiçindeki geçerli değere görepageöğesine bir ekran atar. - Henüz
FavoritesPageolmadığı içinPlaceholdersimgesini kullanın. Bu kullanışlı widget, yerleştirdiğiniz her yere çapraz bir dikdörtgen çizer ve kullanıcı arayüzünün bu bölümünü tamamlanmamış olarak işaretler.

- Hızlı hata ilkesini uygulayan switch ifadesi,
selectedIndex0 veya 1 değilse de hata vermeyi sağlar. Bu sayede ileride hataların oluşması önlenir. Gezinme çubuğuna yeni bir hedef ekleyip 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 rayını 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, yeterli dikey veya yatay alan olmadığında alt öğeleri otomatik olarak bir sonraki "satıra" (çalıştırma olarak adlandırılır) kaydıran Row veya Column'a benzer bir widget'tır. FittedBox adlı bir widget vardır. Bu widget, çocuğunuza ait öğeleri, belirttiğiniz özelliklere göre mevcut alana otomatik olarak yerleştirir.
Ancak NavigationRail, her bağlamda neyin yeterli alan 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 örnekte kullanılacak widget LayoutBuilder. Bu özellik, widget ağacınızı kullanılabilir alanınıza göre 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'nin üzerine getirin.Ctrl+.(Windows/Linux) veyaCmd+.(Mac) ile Refactor (Yeniden Düzenle) menüsünü açın.- Wrap with Builder'ı (Oluşturucu ile Sar) 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)
LayoutBuilder'nın builder geri çağırma işlevi, kısıtlamalar her değiştiğinde ç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 yatay moddan dikey moda döndürmesi
MyHomePage'nın yanındaki bir widget'ın boyutu büyüdüğündeMyHomePage'nın kısıtlamaları küçülüyor.
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 ortamına yanıt veriyor. Başka bir deyişle, duyarlıdır.
Geriye kalan tek iş, bu Placeholder simgesini gerçek bir Favoriler ekranıyla değiştirmektir. Bu konu bir sonraki bölümde ele alınmaktadır.
8. Yeni sayfa ekle
Favoriler sayfası yerine kullandığımız Placeholder widget'ını hatırlıyor musunuz?
Bu sorunu düzeltme zamanı.
Kendinize güveniyorsanız bu adımı kendi başınıza yapmayı deneyin. Amacınız, favorites listesini yeni bir durumsuz widget'ta (FavoritesPage) göstermek ve ardından Placeholder yerine bu widget'ı göstermektir.
Aşağıda birkaç ipucu verilmiştir:
- 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 özellikler sunar. 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,messagesdizesi bir liste 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 eklemeler 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. Kimse sizden ilk saatte Flutter geliştirmeyi öğrenmenizi beklemez. 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 eklenmedi.
- Aksi takdirde, (kaydırılabilir) bir liste gösterilir.
- Liste, bir özetle başlar (örneğin, 5 favoriniz var.).
- Ardından kod, tüm favoriler arasında gezinir 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 nihai kodunu GitHub'daki codelab deposunda bulabilirsiniz.
9. Sonraki adımlar
Tebrikler!
Harikasın! 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
Sonra ne olur?
- 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.