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ı, bir sonraki adı isteyebilir, mevcut adı favorileyebilir ve ayrı bir sayfada favori adların listesini inceleyebilir. Uygulama, farklı ekran boyutlarına uyumludur.
Neler öğreneceksiniz?
- Flutter'ın işleyiş şekliyle ilgili temel bilgiler
- Flutter'da düzenler oluşturma
- Kullanıcı etkileşimlerini (ör. düğmeye basma) uygulama davranışıyla ilişkilendirme
- Flutter kodunuzu düzenli tutma
- Uygulamanızı duyarlı hale getirme (farklı ekranlar için)
- Uygulamanızın görünümü ve tarzını tutarlı hale getirme
Doğrudan ilgi çekici bölümlere atlayabilmek için temel bir iskeletle başlarsınız.
Filip, sizi kod laboratuvarının tamamında yönlendirecek.
Laboratuvarı başlatmak için İleri'yi tıklayın.
2. Flutter ortamınızı ayarlama
Düzenleyici
Bu codelab'i olabildiğince basit hale getirmek için geliştirme ortamınız olarak Visual Studio Code'u (VS Code) kullanacağınızı varsayıyoruz. Ücretsiz olan bu uygulama tüm büyük platformlarda çalışır.
İstediğiniz düzenleyiciyi kullanabilirsiniz: Android Studio, diğer IntelliJ IDE'ler, Emacs, Vim veya Notepad++. Bunların hepsi Flutter ile çalışır.
Talimatlar varsayılan olarak VS Code'a özgü kısayollara yönlendirdiğinden bu codelab için VS Code kullanmanızı öneririz. "X işlemini yapmak için düzenleyicinizde uygun işlemi yapın" gibi bir ifade 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 birincil olarak uygulama geliştireceğiniz tek bir işletim sistemi seçmek yaygın bir uygulamadır. Bu, "geliştirme hedefiniz"dir. Yani uygulamanızın geliştirme sırasında çalıştığı işletim sistemidir.
Örneğin, bir Flutter uygulaması geliştirmek için Windows dizüstü bilgisayar kullandığınızı varsayalım. Geliştirme hedefiniz olarak Android'i seçerseniz genellikle Windows dizüstü bilgisayarınıza USB kablosuyla bir Android cihaz bağlarsınız ve geliştirme aşamasındaki uygulamanız bu bağlı Android cihazda çalışır. Geliştirme hedefi olarak Windows'u da seçebilirsiniz. Bu durumda, geliştirme aşamasındaki uygulamanız düzenleyicinizle birlikte 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 birini (Durumlu Sıcak Yeniden Yükleme) kaybetmenizdir. Flutter, web uygulamalarını sıcak yeniden yükleyemez.
Hemen seçiminizi yapın. Unutmayın: Uygulamanızı daha sonra istediğiniz zaman diğer işletim sistemlerinde çalıştırabilirsiniz. Bununla birlikte, kafanızda net bir geliştirme hedefi olması sonraki adımı daha kolaylaştırır.
Flutter'ı yükleme
Flutter SDK'sının nasıl yükleneceğiyle ilgili en güncel talimatlar her zaman docs.flutter.dev adresindedir.
Flutter web sitesindeki talimatlar yalnızca SDK'nın kurulumunu değil, geliştirme hedefine ilişkin 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 eklentisi yüklü Visual Studio Code
- Seçtiğiniz geliştirme hedefi için gereken yazılım (ör. Windows'u hedeflemek için Visual Studio veya macOS'i hedeflemek için Xcode)
Bir sonraki bölümde ilk Flutter projenizi oluşturacaksınız.
Şimdiye kadar sorun yaşadıysanız bu sorulardan ve yanıtlardan (StackOverflow'dan) bazılarını sorun giderme konusunda faydalı bulabilirsiniz.
Sık Sorulan Sorular
- Flutter SDK'sının yolunu nasıl bulabilirim?
- Flutter komutu bulunamadığında ne yapmalıyım?
- "Başlangıç kilidini kaldırmak için başka bir flutter komutu bekleniyor" sorununu nasıl düzeltebilirim?
- Flutter'a Android SDK yüklememin yerini nasıl bildiririm?
flutter doctor --android-licenses
'ı çalıştırırken oluşan Java hatasını nasıl düzeltebilirim?- Android
sdkmanager
aracı bulunamadı hatasıyla nasıl başa çıkabilirim? - "
cmdline-tools
bileşeni eksik" hatasıyla nasıl başa çıkabilirim? - CocoaPods'u Apple Silicon (M1) üzerinde nasıl çalıştırırım?
- VS Code'da kaydetme sırasında 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: Yeni Proje komutunu seçin.
Ardından Uygulama'yı ve ardından projenizi oluşturacağınız klasörü seçin. Bu, ana dizininiz veya C:\src\
gibi bir şey olabilir.
Son olarak projenize bir ad verin. namer_app
veya my_awesome_namer
gibi bir şey.
Flutter, 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 yazarak bu dosyaları değiştireceksiniz.
İlk uygulamayı kopyalama ve yapıştırma
VS Code'un sol bölmesinde Gezgin'in 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' # Remove this line if you wish to publish to pub.dev
version: 0.0.1+1
environment:
sdk: ^3.6.0
dependencies:
flutter:
sdk: flutter
english_words: ^4.0.0
provider: ^6.1.2
dev_dependencies:
flutter_test:
sdk: flutter
flutter_lints: ^5.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önderilecek öğeler) belirtir.
Ardından projede başka bir yapılandırma dosyası (analysis_options.yaml
) açın.
İç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. Flutter'a ilk kez giriş yaptığınız için analizöre biraz daha esnek davranmasını söylüyorsunuz. Bu ayarı daha sonra değiştirebilirsiniz. Gerçek bir üretim uygulaması yayınlamaya yaklaştıkça analiz aracını bundan daha katı hale getirmek isteyebilirsiniz.
Son olarak, lib/
dizininin altındaki 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(
useMaterial3: true,
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ır kod, uygulamanın şimdiye kadarki tamamıdır.
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ımda, yeni bir kelime eşleştirmesi oluşturmak için bir Sonraki düğmesi eklenir.
Uygulamayı başlatın
Öncelikle lib/main.dart
'ü açın ve hedef cihazınızı seçtiğinizden emin olun. VS Code'un sağ alt köşesinde, mevcut hedef cihazı gösteren bir düğme bulunur. Değiştirmek için tıklayın.
lib/main.dart
açıkken VS Code penceresinin sağ üst köşesindeki "oynat" düğmesini bulup tıklayın.
Yaklaşık bir dakika sonra uygulamanız hata ayıklama modunda başlatılır. Henüz çok fazla şey yok:
İlk Sıcak Yeniden Yükleme
lib/main.dart
öğesinin en altında, ilk Text
nesnesinde bulunan dizeye bir şey ekleyin ve dosyayı (Ctrl+S
veya Cmd+S
ile) kaydedin. Ö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ü durum bilgisine sahip sıcak yeniden yükleme özelliğinin kullanıldığı bir örnektir. Bir kaynak dosyaya yaptığınız değişiklikleri kaydettiğinizde hızlı yeniden yükleme tetiklenir.
Sık Sorulan Sorular
- VSCode'da Hızlı Yeniden Yükleme çalışmıyorsa ne olur?
- VSCode'de anında yeniden yükleme için "r" tuşuna basmam gerekir mi?
- Hızlı Yeniden Yükleme web'de çalışır mı?
- "Hata ayıklama" banner'ını nasıl kaldırabilirim?
Düğme ekleme
Ardından, Column
öğesinin en altına, ikinci Text
örneğinin hemen 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 Hata Ayıklama Konsolu'nda düğmeye basıldı mesajı gösterilir.
5 dakikada Flutter hızlandırılmış kursu
Hata Ayıklama Konsolu'nu izlemek ne kadar eğlenceli olursa olsun düğmenin daha anlamlı bir şey yapmasını istersiniz. Ancak bu konuya geçmeden önce, işleyiş şeklini anlamak için lib/main.dart
kodundaki kodu daha yakından inceleyin.
lib/main.dart
// ...
void main() {
runApp(MyApp());
}
// ...
Dosyanın en üstünde main()
işlevini görürsünüz. Mevcut haliyle, Flutter'a yalnızca 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(
useMaterial3: true,
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şturduğunuz öğelerdir. Gördüğünüz gibi uygulamanın kendisi bile bir widget'tır.
MyApp
içindeki kod, uygulamanın tamamını kurar. Uygulama genelindeki durumu oluşturur (bu konu hakkında daha sonra daha fazla bilgi verilecektir), uygulamayı adlandırır, görsel temayı tanımlar ve uygulamanızın başlangıç noktası olan "ana ekran" 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. Flutter'a ilk girişiminiz olduğu için bu kod laboratuvarını basit ve odaklanmış tutacağız. Flutter'da uygulama durumunu yönetmenin birçok güçlü yolu vardır. Açıklaması en kolay olanlardan biri, bu uygulamanın benimsediği yaklaşım olan ChangeNotifier
.
MyAppState
, uygulamanın çalışması için ihtiyaç duyduğu verileri tanımlar. Şu anda yalnızca mevcut rastgele kelime çiftini içeren tek bir değişken içeriyor. Daha sonra bu listeye ekleme yapabilirsiniz.- Durum sınıfı
ChangeNotifier
'ü genişletir. Diğerlerini kendi değişiklikleri hakkında bildirebilir. Ö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
'teki yukarıdaki koda bakın). Bu sayede uygulamadaki tüm widget'lar 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, daha önce değiştirdiğiniz widget'ı (MyHomePage
) görebilirsiniz. Aşağıdaki her numaralı satır, yukarıdaki koddaki 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
,watch
yöntemini kullanarak uygulamanın mevcut durumundaki değişiklikleri izler.- Her
build
yöntemi bir widget veya (daha yaygın olarak) iç içe yerleştirilmiş bir widget ağacı döndürmelidir. Bu durumda üst düzey widgetScaffold
'tir. Bu kod laboratuvarındaScaffold
ile çalışmayacaksınız ancak faydalı bir widget olanScaffold
, gerçek Flutter uygulamalarının büyük çoğunluğunda bulunur. Column
, Flutter'daki en temel düzen widget'larından biridir. İstediğiniz sayıda çocuğu alır ve bunları yukarıdan aşağıya doğru bir sütuna yerleştirir. Sütun, varsayılan olarak alt öğelerini görsel olarak en üstte yerleştirir. Yakında sütunu ortalayacak şekilde bu ayarı değiştireceksiniz.- İlk adımda bu
Text
widget'ını değiştirdiniz. - Bu ikinci
Text
widget'ı,appState
değerini alır ve bu sınıfın tek üyesi olancurrent
'ye (WordPair
) erişir.WordPair
,asPascalCase
veyaasSnakeCase
gibi çeşitli yararlı alıcı sağlar. BuradaasLowerCase
kullanıyoruz ancak alternatiflerden birini tercih ederseniz bunu hemen değiştirebilirsiniz. - Flutter kodunda son virgüllerin yoğun şekilde kullanıldığına dikkat edin.
children
, söz konusuColumn
parametre listesinin son (ve aynı zamanda tek) üyesi olduğu için bu virgül burada olmamalıdır. Yine de genellikle son virgül kullanmak iyi bir fikirdir: Daha fazla üye eklemeyi kolaylaştırır ve Dart'ın otomatik biçimlendiricisine buraya yeni bir satır koyması için ipucu verir. Daha fazla bilgi için Kod biçimlendirme bölümüne bakın.
Ardından düğmeyi duruma bağlayın.
İlk davranışınız
MyAppState
bölümüne gidip 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
değerine yeniden atar. Ayrıca notifyListeners()
(MyAppState
'yi izleyen herkesin bilgilendirilmesini sağlayan bir ChangeNotifier)
yöntemi) çağrılır.
Geriye kalan tek şey, düğmenin geri çağırma işlevinden getNext
yöntemini çağırmaktır.
lib/main.dart
// ...
ElevatedButton(
onPressed: () {
appState.getNext(); // ← This instead of print().
},
child: Text('Next'),
),
// ...
Uygulamayı kaydedip hemen deneyin. Sonraki düğmesine her bastığınızda yeni bir rastgele kelime çifti oluşturulur.
Bir sonraki bölümde, kullanıcı arayüzünü daha güzel hale getireceksiniz.
5. Uygulamayı daha güzel hale getirin
Uygulama şu anda bu şekilde görünüyor.
Pek iyi değil. Uygulamanın ana unsuru olan rastgele oluşturulan kelime çifti daha görünür olmalıdır. Sonuçta kullanıcılarımızın bu uygulamayı kullanmalarının ana nedeni budur. Ayrıca, uygulama içerikleri merkezden garip bir şekilde kaymış ve uygulamanın tamamı sıkıcı bir şekilde siyah beyaz.
Bu bölümde, uygulamanın tasarımı üzerinde çalışarak bu sorunlar ele alınır. Bu bölümün nihai hedefi aşağıdaki gibidir:
Widget'ı ayıklama
Mevcut kelime çiftini göstermekten sorumlu satır artık şu şekilde görünür: Text(appState.current.asLowerCase)
. Daha karmaşık bir şeye dönüştürmek için bu satırı ayrı bir widget'a ayırabilirsiniz. Kullanıcı arayüzünüzün mantıksal olarak ayrı 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 bir yeniden düzenleme yardımcısı sağlar ancak bunu kullanmadan önce, ayıklanan satırın yalnızca ihtiyaç duyduğu öğelere eriştiğinden emin olun. Şu anda satır appState
değerine 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
'un tamamını ifade etmiyor.
Şimdi Yeniden Düzenle menüsünü açın. VS Code'de bunu iki şekilde yapabilirsiniz:
- Yeniden yapılandırmak istediğiniz kod parçasını (bu örnekte
Text
) sağ tıklayın ve açılır menüden Yeniden yapılandır...'ı seçin.
VEYA
- İmlecinizi yeniden yapılandırmak istediğiniz kod parçasına (bu örnekte
Text
) getirin veCtrl+.
(Win/Linux) veyaCmd+.
(Mac) tuşlarına basın.
Yeniden Düzenle menüsünde Widget'ı Ayıkla'yı 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);
}
}
// ...
Bu yeniden düzenleme sırasında uygulamanın çalışmaya devam ettiğini fark edin.
Kart ekleme
Şimdi bu yeni widget'ı, bölümün başında hayal ettiğimiz cesur kullanıcı arayüzü parçasına dönüştürme zamanı.
BigCard
sınıfını ve içindeki build()
yöntemini bulun. Önceki gibi, Text
widget'ında Yeniden Düzenle menüsünü açın. Ancak bu sefer widget'ı ayıklayamazsınız.
Bunun yerine Dolguyla Sarmal'ı seçin. Bu işlem, Text
widget'ının etrafında Padding
adlı yeni bir üst widget oluşturur. Kaydettikten sonra, rastgele kelimenin daha fazla boşluk içerdiğini görürsünüz.
Dolguyu 8.0
olan varsayılan değerden artırın. Örneğin, daha fazla dolgu için 20
gibi bir karakter kullanın.
Ardından bir üst seviyeye gidin. İmlecinizi Padding
widget'ının üzerine getirin, Yeniden Düzenle menüsünü açın ve Widget ile sarma...'yı seçin.
Bu sayede üst widget'ı belirtebilirsiniz. "Kart" yazıp Enter tuşuna basın.
Bu işlem, Padding
widget'ını ve dolayısıyla Text
'u bir Card
widget'ı ile sarar.
Tema ve stil
Kartın daha belirgin olmasını sağlamak için daha zengin 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
simgesini kullanın.
BigCard
'nin 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 sayıda işlevi yerine getirir:
- Kod ilk olarak
Theme.of(context)
ile uygulamanın mevcut temasını ister. - Ardından kod, kartın rengini temanın
colorScheme
mülküyle aynı olacak şekilde tanımlar. Renk şemasında birçok renk bulunur veprimary
, uygulamanın en belirgin, tanımlayıcı rengidir.
Kart, uygulamanın birincil rengiyle boyanır:
MyApp
'e gidip ColorScheme
için ana renk rengini değiştirerek bu rengi ve uygulamanın tümünün renk şemasını değiştirebilirsiniz.
Rengin nasıl sorunsuz bir şekilde animasyonlu olarak değiştiğini fark 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 ara değer oluşturur.
Kartın altındaki yükseltilmiş düğmenin rengi de değişir. Değerleri sabit kodlamak yerine uygulama genelinde bir Theme
kullanmanın gücü budur.
TextTheme
Kartta hâlâ bir sorun var: Metin çok küçük ve rengi okunamıyor. Bu sorunu düzeltmek için BigCard
'ı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,
'ü kullanarak uygulamanın yazı tipi temasına erişebilirsiniz. Bu sınıftabodyMedium
(orta boyutlu standart metinler için),caption
(resim altyazılar için) veyaheadlineLarge
(büyük başlıklar için) gibi öğeler bulunur.displayMedium
mülkü, görüntü metni için tasarlanmış büyük bir stildir. Görüntülü kelimesi burada görüntülü yazı tipi gibi yazım anlamında kullanılmaktadır.displayMedium
ile ilgili dokümanda, "görüntüleme stilleri kısa ve önemli metinler için ayrılmıştır" ifadesi yer alıyor. Bu, tam olarak bizim kullanım alanımız.- Temanın
displayMedium
özelliği teorik olaraknull
olabilir. Bu uygulamayı yazdığınız programlama dili olan Dart, null değerine karşı güvenlidir. Bu nedenle,null
olabilecek nesnelerin yöntemlerini çağırmanıza izin vermez. Ancak bu durumda, Dart'a ne yaptığınızı bildiğinizden emin olmak için!
operatörünü ("bang operatörü") kullanabilirsiniz. (displayMedium
bu durumda kesinlikle boş değildir. Bunun nedeni bu codelab'in kapsamı dışındadır.) displayMedium
üzerindecopyWith()
çağrısı yaptığınızda, tanımladığınız değişiklikleri içeren metin stilinin bir kopyası döndürülür. Bu durumda yalnızca metnin rengini değiştirmiş olursunuz.- Yeni rengi almak için uygulamanın temasına tekrar erişin. Renk düzeninin
onPrimary
mülkü, uygulamanın birincil renginin üzerinde kullanıma uygun bir renk tanımlar.
Uygulamanız aşağıdaki gibi görünecektir:
İsterseniz kartı daha da değiştirebilirsiniz. Aşağıdaki önerilerden yararlanabilirsiniz:
copyWith()
, metin stilinde yalnızca rengi değil, çok daha fazlasını 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 şeyi değiştirebilirsiniz. Örneğin,elevation
parametresinin değerini artırarak kartın gölgesini büyütebilirsiniz. - Renklerle denemeler yapmayı deneyin.
theme.colorScheme.primary
dışında.secondary
,.surface
ve daha birçok seçenek var. Bu renklerinonPrimary
eşdeğerlerine sahiptir.
Erişilebilirliği iyileştirme
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ışma gerekir. Bu uygulamada ekran okuyucu, oluşturulan bazı kelime çiftlerini telaffuz ederken sorun yaşayabilir. İnsanlar cheaphead (ucuza satan) kelimesindeki iki kelimeyi tanımlamakta sorun yaşamasa da ekran okuyucular kelimenin ortasındaki ph harflerini f olarak telaffuz edebilir.
Basit bir çözüm, pair.asLowerCase
değerini "${pair.first} ${pair.second}"
ile değiştirmektir. İkincisi, pair
içindeki iki kelimeden bir dize ("cheap head"
gibi) oluşturmak için dize ekleme işlevini kullanır. Bir birleşik kelime yerine iki ayrı kelime kullanmak, ekran okuyucuların bunları uygun şekilde tanımlamasını sağlar ve görme engelli kullanıcılara daha iyi bir deneyim sunar.
Ancak pair.asLowerCase
'ün görsel sadeliğ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 üzere Text
'nin semanticsLabel
mülkünü 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 deneyebilirsiniz.
Kullanıcı arayüzünü ortala
Artık rastgele kelime çiftini yeterince görsel bir şekilde sunduğunuza göre, uygulamanın penceresinin/ekranının ortasına yerleştirme zamanı geldi.
Öncelikle, BigCard
'ün bir Column
parçası olduğunu unutmayın. Varsayılan olarak sütunlar, alt öğelerini üste toplar ancak bunu kolayca geçersiz kılabiliriz. MyHomePage
'nin 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 işlem, çocukları Column
içinde ana (dikey) eksen boyunca merkeze yerleştirir.
Alt öğeler sütunun çapraz ekseni boyunca zaten ortalanmıştır (yani yatay olarak ortalanmışlardır). Ancak Column
kendisi Scaffold
içinde hizalanmamıştır. Widget İnceleyici'yi kullanarak bunu doğrulayabiliriz.
Widget Denetleyici bu kod laboratuvarının kapsamı dışındadır ancak Column
vurgulandığında uygulamanın tüm genişliğini kaplamadığını görebilirsiniz. Column
yalnızca alt öğelerinin ihtiyaç duyduğu kadar yatay alan kaplar.
Sütunu ortalamanız yeterlidir. İmlecinizi Column
üzerine getirin, Yeniden Düzenle menüsünü (Ctrl+.
veya Cmd+.
ile) açın ve Ortayla Sar'ı seçin.
Uygulamanız aşağıdaki gibi görünecektir:
İsterseniz bu ayarı biraz daha değiştirebilirsiniz.
BigCard
'un üzerindekiText
widget'ını kaldırabilirsiniz. Kullanıcı arayüzü bu açıklama olmadan da anlaşılır olduğu için açıklayıcı metnin ("A random AWESOME idea:") artık gerekli olmadığı söylenebilir. Bu şekilde daha temiz olur.BigCard
ileElevatedButton
arasına birSizedBox(height: 10)
widget da ekleyebilirsiniz. Bu sayede iki widget arasında biraz daha fazla boşluk olur.SizedBox
widget'ı yalnızca yer kaplar ve tek 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 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 her tıkladığında her kelime çifti sonsuza kadar kaybolur. En iyi önerileri "hatırlama"nın bir yolu olsaydı (ör. "Beğen" düğmesi) daha iyi olurdu.
İş mantığını ekleme
MyAppState
bölümüne gidip 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
alanınafavorites
adlı yeni bir mülk eklediniz. Bu mülk, boş bir listeyle başlatılır:[]
.- Ayrıca, listenin yalnızca kelime çiftleri içerebileceğini belirttiniz:
<WordPair>[]
, genel terimler kullanılarak. Bu, uygulamanızın daha sağlam olmasına yardımcı olur. Dart,WordPair
dışında bir şey eklemeye çalıştığınızda uygulamanızı çalıştırmayı bile reddeder. Bu sayede,favorites
listesini kullanırken içinde hiçbir zaman istenmeyen nesnelerin (null
gibi) gizlenmeyeceğinden emin olabilirsiniz.
- Ayrıca, mevcut kelime çiftini favoriler listesinden kaldıran (listedeki bir kelime çiftiyse) veya ekleyen (listedeki bir kelime çifti değilse) yeni bir yöntem (
toggleFavorite()
) eklediniz. Her iki durumda da kod daha sonranotifyListeners();
işlevini çağırır.
Düğmeyi ekleme
"İş mantığı" tamamlandığında, kullanıcı arayüzü üzerinde tekrar çalışma zamanı gelmiştir. "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 eşdeğeridir.
Öncelikle mevcut düğmeyi bir Row
içine alın. MyHomePage
sınıfının build()
yöntemine gidin, imlecinizi ElevatedButton
üzerine getirin, Ctrl+.
veya Cmd+.
ile Yeniden Düzenle menüsünü açın ve Satıra Sar'ı seçin.
Kaydettiğinizde Row
'ün Column
'e benzer şekilde davrandığını görürsünüz. Varsayılan olarak, alt öğelerini sola toplar. (Column
, alt öğelerini üstte topladı.) Bu sorunu düzeltmek için öncekiyle aynı yaklaşımı mainAxisAlignment
ile kullanabilirsiniz. Ancak eğitici (öğretici) amaçlarla mainAxisSize
simgesini kullanın. Bu, Row
'e mevcut yatay alanın tamamını kullanmamasını söyler.
Aşağıdaki değişikliği yapın:
lib/main.dart
// ...
class MyHomePage extends StatelessWidget {
@override
Widget build(BuildContext context) {
var appState = context.watch<MyAppState>();
var pair = appState.current;
return Scaffold(
body: Center(
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: [
BigCard(pair: pair),
SizedBox(height: 10),
Row(
mainAxisSize: MainAxisSize.min, // ← Add this.
children: [
ElevatedButton(
onPressed: () {
appState.getNext();
},
child: Text('Next'),
),
],
),
],
),
),
);
}
}
// ...
Kullanıcı arayüzü önceki haline geri döner.
Ardından Beğen düğmesini ekleyin ve toggleFavorite()
öğesine bağlayın. Kendinize bir meydan okuma olarak önce aşağıdaki kod bloğuna bakmadan bunu kendiniz yapmayı deneyin.
Aşağıdaki şekilde tam olarak aynı şekilde yapmasanız da olur. Gerçekten zorlu bir deneyim yaşamak istemiyorsanız kalp simgesinden endişelenmeyin.
Başarısız olmanız da sorun değil. Sonuçta Flutter'ı kullanmaya ilk başladığınız saat bu.
MyHomePage
öğesine ikinci düğmeyi eklemenin bir yolu aşağıda verilmiştir. Bu kez, simge içeren bir düğme oluşturmak için ElevatedButton.icon()
kurucusunu kullanın. build
yönteminin üst kısmında, mevcut kelime çiftinin favoriler arasında olup olmadığına bağlı olarak uygun simgeyi seçin. Ayrıca, iki düğmeyi biraz ayırmak için tekrar SizedBox
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 ekleme zamanı geldi. Bir sonraki bölümde görüşmek üzere.
7. Gezinme çubuğu ekleme
Çoğu uygulama, her şeyi tek bir ekrana sığdıramaz. Bu uygulama muhtemelen yapabilir ancak eğitim amaçlı olarak 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
'inizi uygulayacaksınız.
Bu adımın özüne en kısa sürede ulaşmak için MyHomePage
'ü 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'),
),
],
),
],
),
);
}
}
// ...
Kayıt edildiğ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ğundaki ♥︎ (kalp) simgesini tıkladığımda hiçbir şey olmuyor.
Değişiklikleri inceleyin.
- Öncelikle,
MyHomePage
içeriğinin tamamının yeni bir widget'a (GeneratorPage
) ayıklandığını görebilirsiniz. EskiMyHomePage
widget'ının ayıklanmayan tek parçasıScaffold
. - Yeni
MyHomePage
, iki çocuk içeren birRow
içeriyor. İlk widgetSafeArea
, ikinci widget iseExpanded
widget'ıdır. SafeArea
, alt öğesinin bir donanım çentiği veya durum çubuğu tarafından gizlenmemesini sağlar. Bu uygulamada widget, gezinme düğmelerinin örneğin mobil durum çubuğu tarafından gizlenmesini önlemek içinNavigationRail
etrafında sarılır.- NavigationRail'deki
extended: false
satırınıtrue
olarak değiştirebilirsiniz. Bu sayede simgelerin yanında etiketleri görebilirsiniz. Uygulamada yeterli yatay alan olduğunda bunu otomatik olarak nasıl yapacağınızı sonraki bir adımda öğreneceksiniz. - Gezinme çubuğunda, ilgili simge ve etiketleriyle birlikte iki hedef (Ana Sayfa ve Favoriler) bulunur. Ayrıca mevcut
selectedIndex
değerini de tanımlar. Seçilen sıfır dizini ilk hedefi, seçilen bir dizini ikinci hedefi seçer ve bu şekilde devam eder. Şu anda sıfır olarak sabitlenmiştir. - Gezinme çubuğu, kullanıcı
onDestinationSelected
ile hedeflerden birini seçtiğinde ne olacağını da tanımlar. Şu anda uygulama, istenen dizin değerini yalnızcaprint()
ile döndürüyor. Row
öğesinin ikinci alt öğesiExpanded
widget'ıdır. Genişletilmiş widget'lar satır ve sütunlarda son derece kullanışlıdır. Bazı alt öğelerin yalnızca ihtiyaç duydukları kadar yer kapladığı (bu örnekteSafeArea
) ve diğer widget'ların kalan alanı mümkün olduğunca kapladığı (bu örnekteExpanded
) düzenler oluşturmanıza olanak tanır.Expanded
widget'larını "açgözlü" olarak düşünebilirsiniz. Bu widget'ın rolünü daha iyi anlamak istiyorsanızSafeArea
widget'ını başka birExpanded
widget'ın içine yerleştirmeyi deneyin. Elde edilen düzen şöyle görünür:
- Gezinme çubuğu için yalnızca sol tarafta küçük bir alan gerekirken iki
Expanded
widget, mevcut tüm yatay alanı aralarında paylaştırmış. Expanded
widget'ının içinde renkli birContainer
, kapsayıcı içinde iseGeneratorPage
vardır.
Durum bilgisiz ve durum bilgisine sahip widget'lar
Şimdiye kadar MyAppState
, tüm eyalet ihtiyaçlarınızı karşılıyordu. Bu nedenle, şimdiye kadar yazdığınız tüm widget'lar devre dışı. Kendilerine ait değişken bir durum içermezler. Widget'ların hiçbiri kendisini değiştiremez. MyAppState
üzerinden geçmeleri gerekir.
Bu durum yakında değişecek.
Gezinme çubuğu selectedIndex
değerini tutmanız gerekir. Ayrıca bu değeri onDestinationSelected
geri çağırma işlevinden de değiştirebilmek istiyorsunuz.
MyAppState
'ın başka bir özelliği olarak selectedIndex
'yi ekleyebilirsiniz. Bu yöntem kesinlikle işe yarar. Ancak her widget kendi değerlerini burada depolasa uygulama durumunun çok hızlı bir şekilde aşırı boyutlara ulaşacağını tahmin edebilirsiniz.
Bazı durumlar yalnızca tek bir widget ile alakalı olduğundan bu widget'ta kalmalıdır.
State
içeren bir widget türü olan StatefulWidget
değerini girin. Öncelikle MyHomePage
öğesini durum bilgisine sahip bir widget'a dönüştürün.
İmlecinizi MyHomePage
'ü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 StatefulWidget'e dönüştür'ü seçin.
IDE, sizin için yeni bir sınıf (_MyHomePageState
) oluşturur. Bu sınıf State
'ü genişletir ve bu nedenle kendi değerlerini yönetebilir. (Kendini değiştirebilir.) Ayrıca, eski ve durum bilgisi olmayan widget'taki build
yönteminin, widget'ta kalmak yerine _MyHomePageState
içine taşındığını da unutmayın. Bu yöntem olduğu gibi taşındı. build
yönteminin içinde hiçbir şey değişmedi. Artık başka bir yerde yaşıyor.
setState
Yeni durum bilgisine sahip widget'ın yalnızca bir değişkeni (selectedIndex
) izlemesi gerekir. _MyHomePageState
dosyasında 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:
selectedIndex
adlı yeni bir değişken tanıtır ve bu değişkeni0
olarak başlatırsınız.- Bu yeni değişkeni,
NavigationRail
tanımında şimdiye kadar bulunan 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 yerine birsetState()
çağrısı içindeselectedIndex
'a atarsınız. Bu çağrı, daha önce kullanılannotifyListeners()
yöntemine benzer. Kullanıcı arayüzünün güncellendiğinden emin olur.
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österileceğini belirlemek için selectedIndex
kullanmamasıdır.
selectedIndex değerini kullanın
Aşağıdaki kodu _MyHomePageState
öğesinin build
yönteminin en üstüne, return Scaffold
öğesinden hemen önce yerleştirin:
lib/main.dart
// ...
Widget page;
switch (selectedIndex) {
case 0:
page = GeneratorPage();
break;
case 1:
page = Placeholder();
break;
default:
throw UnimplementedError('no widget for $selectedIndex');
}
// ...
Aşağıdaki kod parçasını inceleyin:
- Kodda,
Widget
türüne sahip yeni bir değişken (page
) tanımlanır. - Ardından, bir switch ifadesi
selectedIndex
'taki mevcut değere görepage
için bir ekran atar. - Henüz
FavoritesPage
olmadığından,Placeholder
kullanın. Bu kullanışlı widget, yerleştirdiğiniz her yerde çapraz bir dikdörtgen çizerek kullanıcı arayüzünün bu bölümünü tamamlanmamış olarak işaretler.
- Hızlı hata ilkesiyle birlikte kullanılan switch ifadesi,
selectedIndex
0 veya 1 değilse hata da atar. Bu, ileride oluşabilecek hataları önlemeye yardımcı olur. Gezinme çubuğuna yeni bir hedef eklerseniz ve bu kodu güncellemeyi unutursanız program geliştirme aşamasında kilitlenir (böylece, neden çalışmadığını tahmin etmenize veya üretime hatalı bir kod yayınlamanıza izin verilmez).
page
, sağ tarafta göstermek istediğiniz widget'ı içerdiğine göre, yapılması gereken diğer değişikliği tahmin edebilirsiniz.
Kalan tek değişiklikten sonra _MyHomePageState
şöyle görünür:
lib/main.dart
// ...
class _MyHomePageState extends State<MyHomePage> {
var selectedIndex = 0;
@override
Widget build(BuildContext context) {
Widget page;
switch (selectedIndex) {
case 0:
page = GeneratorPage();
break;
case 1:
page = Placeholder();
break;
default:
throw UnimplementedError('no widget for $selectedIndex');
}
return Scaffold(
body: Row(
children: [
SafeArea(
child: NavigationRail(
extended: false,
destinations: [
NavigationRailDestination(
icon: Icon(Icons.home),
label: Text('Home'),
),
NavigationRailDestination(
icon: Icon(Icons.favorite),
label: Text('Favorites'),
),
],
selectedIndex: selectedIndex,
onDestinationSelected: (value) {
setState(() {
selectedIndex = value;
});
},
),
),
Expanded(
child: Container(
color: Theme.of(context).colorScheme.primaryContainer,
child: page, // ← Here.
),
),
],
),
);
}
}
// ...
Uygulama artık GeneratorPage
ile yakında Favoriler sayfası olacak yer tutucu arasında geçiş yapıyor.
Yanıt verme
Ardından gezinme çubuğunu duyarlı hale getirin. Yani, etiketler için yeterli alan olduğunda etiketleri otomatik olarak göstermesini (extended: true
kullanarak) sağlayın.
Flutter, uygulamalarınızı otomatik olarak duyarlı hale getirmenize yardımcı olan çeşitli widget'lar sağlar. Örneğin, Wrap
, Row
veya Column
'ye benzer bir widget'tır. Yeterli dikey veya yatay alan olmadığında alt öğeleri otomatik olarak bir sonraki "satıra" ("satır" olarak adlandırılır) sarmalayan bir widget'tır. FittedBox
, çocuğunu spesifikasyonlarınıza göre mevcut alana otomatik olarak sığdıran bir widget'tır.
Ancak NavigationRail
, her bağlamda yeterli alanın ne olduğunu bilmediği için yeterli alan olduğunda etiketleri otomatik olarak göstermez. Bu kararı geliştirici olarak size bırakıyoruz.
Yalnızca MyHomePage
en az 600 piksel genişliğindeyse etiketleri göstermeye karar verdiğinizi varsayalım.
Bu örnekte kullanılacak widget LayoutBuilder
'tir. Bu sayede, widget ağacınızı ne kadar boş alanınız olduğuna bağlı olarak değiştirebilirsiniz.
Gerekli değişiklikleri yapmak için VS Code'daki Flutter'ın Yeniden Düzenle menüsünü tekrar kullanın. Ancak bu sefer biraz daha karmaşık:
_MyHomePageState
sınıfınınbuild
yönteminde, imleciniziScaffold
üzerine getirin.Ctrl+.
(Windows/Linux) veyaCmd+.
(Mac) tuşlarıyla Yeniden Düzenle menüsünü açın.- Geliştirici ile sarma'yı 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)
yerine(context, constraints)
olarak değiştirin.
LayoutBuilder
'ı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ır
- Kullanıcı telefonunu dikey moddan yatay moda veya tam tersi şekilde döndürür
MyHomePage
'ün yanındaki bazı widget'lar büyüyerekMyHomePage
'ün kısıtlamalarını küçültüyor- vb.
Artık kodunuz, mevcut constraints
değerini sorgulayarak etiketin gösterilip gösterilmeyeceğine karar verebilir. _MyHomePageState
sınıfı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 çevresel faktörlere yanıt veriyor. Başka bir deyişle, duyarlı.
Geriye kalan tek iş, bu Placeholder
öğesini gerçek bir Favoriler ekranıyla değiştirmek. 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ı.
Maceraperverseniz bu adımı kendiniz yapmayı deneyin. Amacınız, favorites
listesini yeni bir durum bilgisi olmayan widget'ta (FavoritesPage
) göstermek ve ardından Placeholder
yerine bu widget'ı göstermektir.
Aşağıda birkaç ipucu verilmiştir:
- Kaydırmalı bir
Column
kullanmak istediğinizdeListView
widget'ını kullanın. context.watch<MyAppState>()
kullanarak herhangi bir widget'tanMyAppState
örneğine erişmeyi unutmayın.- Yeni bir widget da denemek istiyorsanız
ListTile
'tetitle
(genellikle metin için),leading
(simgeler veya avatarlar için) veonTap
(etkileşimler için) gibi özellikler bulunur. Ancak, bildiğiniz widget'larla da benzer efektler elde edebilirsiniz. - Dart, koleksiyon değişmez değerleri içinde
for
döngülerinin kullanılmasına olanak tanır. Örneğin,messages
bir dize listesi içeriyorsa aşağıdaki gibi bir kodunuz olabilir:
Öte yandan, işlevsel programlamaya daha aşina iseniz Dart, messages.map((m) => Text(m)).toList()
gibi kodlar da yazmanıza olanak tanır. Elbette dilediğiniz zaman widget listesi oluşturabilir ve build
yönteminin içine zorunlu olarak ekleyebilirsiniz.
Favoriler sayfasını kendiniz eklemenin avantajı, kendi kararlarınızı vererek daha fazla bilgi edinmenizdir. Dezavantajı ise henüz kendi başınıza çözemediğiniz sorunlarla karşılaşmanızdır. Başarısız olmanın normal olduğunu ve öğrenmenin en önemli unsurlarından biri olduğunu unutmayın. Flutter geliştirmeyi ilk saatinizde mükemmel şekilde öğrenmenizi kimse beklemez ve siz de bunu beklememelisiniz.
Aşağıda, favoriler sayfasını uygulamanın bir yolu verilmiştir. Uygulama şekli, kodla oynamanıza (umarım) ilham verecektir. Böylece kullanıcı arayüzünü iyileştirip kendi haline getirebilirsiniz.
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 ortada şu mesaj gösterilir: Henüz favori yok.
- Aksi takdirde, (kaydırılabilir) bir liste gösterilir.
- Liste bir özetle başlar (örneğin, 5 favoriniz var*.*).
- Ardından kod, tüm favorileri iteratif olarak tarar ve her biri için bir
ListTile
widget oluşturur.
Şimdi tek yapmanız gereken Placeholder
widget'ını FavoritesPage
ile değiştirmek. Ve işte bu kadar.
Bu uygulamanın nihai kodunu GitHub'daki codelab deposundan edinebilirsiniz.
9. Sonraki adımlar
Tebrikler!
Harikasın. Column
ve iki Text
widget'ı içeren işlevsel olmayan bir iskeleti, duyarlı ve keyifli bir küçük uygulamaya dönüştürdünüz.
İşlediğimiz konular
- Flutter'ın işleyiş şekliyle ilgili temel bilgiler
- Flutter'da düzenler oluşturma
- Kullanıcı etkileşimlerini (ör. düğmeye basma) uygulama davranışıyla ilişkilendirme
- Flutter kodunuzu düzenli tutma
- Uygulamanızı duyarlı hale getirme
- Uygulamanızın görünümü ve tarzını tutarlı hale getirme
Sonra ne olur?
- Bu laboratuvar sırasında yazdığınız uygulamayla daha fazla deneme yapın.
- Animasyonlu listeler, degradeler, geçiş efektleri ve daha fazlasını nasıl ekleyeceğinizi öğrenmek için aynı uygulamanın bu gelişmiş sürümünün koduna bakın.
- flutter.dev/learn adresine giderek öğrenme yolculuğunuzu takip edin.