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
sdkmanager
aracı bulunamadı hatasıyla nasıl başa çıkabilirim? - "
cmdline-tools
bileş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.
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 ekleyeceksiniz.- Durum sınıfı
ChangeNotifier
sı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,
ChangeNotifierProvider
kullanılarak oluşturulur ve uygulamanın tamamına sağlanır (MyApp
bö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. MyHomePage
yöntemiyle uygulamanın mevcut durumundaki değişiklikleri izler.watch
- Her
build
yö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'deScaffold
ile ç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
Text
widget'ını değiştirdiniz. - Bu ikinci
Text
widget'ıappState
alır ve bu sınıfın tek üyesi olancurrent
'ye (WordPair
olan) erişir.WordPair
,asPascalCase
veyaasSnakeCase
gibi çeşitli yararlı alıcılar sağlar. BuradaasLowerCase
kullanı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
, buColumn
parametre 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.displayMedium
dokü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 olaraknull
olabilir. Bu uygulamayı yazdığınız programlama dili olan Dart, null güvenlidir. Bu nedenle, potansiyel olaraknull
olan 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. (displayMedium
bu 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,
Card
widget'ı hakkında daha fazla değişiklik yapabilirsiniz. Örneğin,elevation
parametresinin değerini artırarak kartın gölgesini büyütebilirsiniz. - Renklerle deneme yapmayı deneyin.
theme.colorScheme.primary
dışında.secondary
,.surface
ve daha pek çok sanatçı var. Bu renklerin hepsininonPrimary
karşı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.
Text
widget'ınıBigCard
bö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)
ileElevatedButton
arasınaBigCard
widget'ı da ekleyebilirsiniz. Bu şekilde, iki widget arasında biraz daha fazla ayrım olur.SizedBox
widget'ı 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:
MyAppState
adlı mülkefavorites
adlı 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,WordPair
dışında bir şey eklemeye çalışırsanız uygulamanızı çalıştırmayı bile reddeder. Böylece,favorites
listesinde 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,
MyHomePage
içindeki tüm içeriklerinGeneratorPage
adlı yeni bir widget'a çıkarıldığını fark edin. EskiMyHomePage
widget'ının çıkarılmayan tek kısmıScaffold
. - Yeni
MyHomePage
öğesinde iki çocuklu birRow
bulunuyor. Birinci widgetSafeArea
, ikinci widget iseExpanded
widget'ı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: false
satırınıNavigationRail
bölümündetrue
olarak 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
selectedIndex
değ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
onDestinationSelected
ile 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 öğesiExpanded
widget'ı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.Expanded
widget'ları "açgözlü" olarak düşünebilirsiniz. Bu widget'ın rolünü daha iyi anlamak istiyorsanızSafeArea
widget'ını başka birExpanded
ile 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
Expanded
widget'ı, mevcut tüm yatay alanı kendi aralarında bölüyor. Expanded
widget'ının içinde renkli birContainer
, kapsayıcının içinde iseGeneratorPage
bulunur.
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ıp0
olarak başlatıyorsunuz. - Bu yeni değişkeni,
NavigationRail
tanımında şimdiye kadar kullanılan sabit kodlu0
yerine kullanırsınız. onDestinationSelected
geri ç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,
page
türünde yeni bir değişken (Widget
) bildirir. - Ardından, bir switch ifadesi,
selectedIndex
içindeki geçerli değere görepage
öğesine bir ekran atar. - Henüz
FavoritesPage
olmadığı içinPlaceholder
simgesini 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,
selectedIndex
0 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ınbuild
yö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ıLayoutBuilder
olarak 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
Column
istediğinizdeListView
widget'ı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
for
döngülerinin kullanılmasına izin verir. Örneğin,messages
dizesi 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
ListTile
widget'ı 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.