1. Giriş
Flutter, Google'ın tek bir kod tabanından mobil, web ve masaüstü uygulamaları oluşturmaya yönelik kullanıcı arayüzü araç setidir. Bu codelab'de aşağıdaki Flutter uygulamasını derleyeceksiniz:
Uygulama "newstay", "lightstream", "mainbrake" veya "graypine" gibi hoş görünen adlar oluşturur. Kullanıcı bir sonraki adı isteyebilir, geçerli adı favorilere ekleyebilir ve favorilere eklenen adların listesini ayrı bir sayfada inceleyebilir. Uygulama farklı ekran boyutlarına duyarlıdır.
Neler öğreneceksiniz?
- Flutter'ın çalışma şekliyle ilgili temel bilgiler
- Flutter'da düzen oluşturma
- Kullanıcı etkileşimlerini (düğmelere basma gibi) uygulama davranışına bağlama
- Flutter kodunuzu düzenli tutma
- Uygulamanızı duyarlı hale getirme (farklı ekranlar için)
- Tutarlı bir görünüm ve uygulamanızın hissi
Temel bir yapıyla başlayacaksınız. Böylece, doğrudan ilgi çekici bölümlere atlayabilirsiniz.
Filip size codelab'in tamamını anlatıyor.
Laboratuvarı başlatmak için İleri'yi tıklayın.
2. Flutter ortamınızı ayarlama
Düzenleyici
Bu codelab'i mümkün olduğunca kolaylaştırmak için geliştirme ortamınız olarak Visual Studio Code'u (VS Code) kullanacağınızı varsayıyoruz. Bu hizmet ücretsizdir ve önde gelen tüm platformlarda çalışır.
İstediğiniz düzenleyiciyi kullanabilirsiniz: Android Studio, diğer IntelliJ IDE'ler, Emacs, Vim veya Notepad++. Hepsi Flutter ile çalışır.
Talimatlar varsayılan olarak VS Code'a özel kısayollardan kullanıldığından bu codelab için VS Code'u kullanmanızı öneririz. "Burayı tıklayın" gibi bir şey söylemek daha kolay veya "bu tuşa basın" "X işlemini yapmak için düzenleyicinizde uygun işlemi yapın" gibi bir komutla yazmak yerine
Geliştirme hedefi seçin
Flutter, birden çok platformda kullanılabilen bir araç setidir. Uygulamanız aşağıdaki işletim sistemlerinden herhangi birinde çalışabilir:
- iOS
- Android
- Windows
- macOS
- Linux
- web
Ancak, esasen geliştireceğiniz tek bir işletim sistemi seçmek yaygın bir uygulamadır. Bu, "geliştirme hedefiniz" olup uygulamanızın geliştirme sırasında çalıştırıldığı 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ı Windows dizüstü bilgisayarınıza bir USB kablosuyla bağlarsınız ve geliştirmedeki uygulamanız, o bağlı Android cihazda çalışır. Ancak geliştirme hedefi olarak Windows'u da seçebilirsiniz. Bu durumda, geliştirme süreciniz düzenleyicinizle birlikte bir Windows uygulaması olarak çalışır.
Geliştirme hedefiniz olarak web'i seçmek isteyebilirsiniz. Bu seçimin olumsuz yanı, Flutter'ın en yararlı geliştirme özelliklerinden birini kaybetmenizdir: Durum Bilgili Hot Yeniden Yükleme. Flutter, web uygulamalarını çalışır durumda yeniden yükleyemez.
Seçiminizi hemen yapın. Unutmayın: Uygulamanızı daha sonra istediğiniz zaman diğer işletim sistemlerinde çalıştırabilirsiniz. Sadece aklınızda açık bir geliştirme hedefinin olması bir sonraki adımı daha sorunsuz hale getirir.
Flutter'ı yükleme
Flutter SDK'sının nasıl yükleneceğiyle ilgili en güncel talimatlar her zaman docs.flutter.dev adresinde yer alır.
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 şunları yüklemeniz gerektiğini unutmayın:
- Flutter SDK'sı
- Flutter eklentisiyle Visual Studio Kodu
- Seçtiğiniz geliştirme hedefinin gerektirdiği yazılım (örneğin: Windows'u hedeflemek için Visual Studio veya macOS için Xcode)
Bir sonraki bölümde ilk Flutter projenizi oluşturacaksınız.
Şu ana kadar sorun yaşadıysanız bu sorulardan ve yanıtlardan (StackOverflow'dan) bazıları sorun giderme açısından yararlı olabilir.
Sık Sorulan Sorular
- Flutter SDK'sının yolunu nasıl bulabilirim?
- Flutter komutu bulunamadığında ne yapmalıyım?
- "Başlangıç kilidinin açılması için başka bir fırıldak komutu bekleniyor" sorununu nasıl düzeltebilirim? sorun nedir?
- Flutter'a Android SDK yüklememin nerede olduğunu nasıl söyleyebilirim?
flutter doctor --android-licenses
çalıştırırken Java hatasını nasıl giderebilirim?- Bulunamadı Android
sdkmanager
aracını nasıl ele alabilirim? - "
cmdline-tools
bileşeni eksik" sorununu nasıl çözebilirim? hatası? - Apple Silicon (M1) cihazda CocoaPods'u nasıl çalıştırabilirim?
- VS Code'a kaydederken otomatik biçimlendirmeyi nasıl devre dışı bırakabilirim?
3. Proje oluşturma
İlk Flutter projenizi oluşturun
Visual Studio Code'u başlatın ve komut paletini açın (F1
, Ctrl+Shift+P
veya Shift+Cmd+P
ile). "Yeni flaş" yazmaya başlayın. Flutter: New Project komutunu seçin.
Sonra, Application'ı (Uygulama) ve projenizi oluşturacağınız klasörü seçin. Bu, ana dizininiz veya C:\src\
gibi bir dizin olabilir.
Son olarak projenize bir ad verin. namer_app
veya my_awesome_namer
gibi bir değer.
Flutter artık proje klasörünüzü oluşturur ve VS Code bu klasörü açar.
Şimdi uygulamanın temel bir iskeletini kullanarak 3 dosyanın içeriğinin üzerine yazacaksınız.
Kopyala ve İlk uygulamayı yapıştırın
VS Code'un sol bölmesinde Explorer'in seçili olduğundan emin olun ve pubspec.yaml
dosyasını açın.
Bu dosyanın içeriğini şununla değiştirin:
pubspec.yaml
name: namer_app
description: A new Flutter project.
publish_to: 'none' # Remove this line if you wish to publish to pub.dev
version: 0.0.1+1
environment:
sdk: ^3.1.1
dependencies:
flutter:
sdk: flutter
english_words: ^4.0.0
provider: ^6.0.0
dev_dependencies:
flutter_test:
sdk: flutter
flutter_lints: ^2.0.0
flutter:
uses-material-design: true
pubspec.yaml
dosyası; uygulamanızın mevcut sürümü, bağımlılıkları ve birlikte gönderileceği öğeler gibi uygulamanızla ilgili temel bilgileri belirtir.
Sonra, analysis_options.yaml
projesinde başka bir yapılandırma dosyasını açın.
Dosyanın içeriğini şununla 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, kodunuzu analiz ederken Flutter'ın ne kadar katı olması gerektiğini belirler. Bu, Flutter'a ilk adımınız olduğundan analizciye işini yorulmasını söylüyorsunuz. Bunu daha sonra istediğiniz zaman ayarlayabilirsiniz. Hatta gerçek bir üretim uygulaması yayınlamaya yaklaştıkça analiz aracını bundan daha katı hale getirmek isteyebilirsiniz.
Son olarak, lib/
dizini altındaki main.dart
dosyasını açın.
Bu dosyanın içeriğini şununla değiştirin:
lib/main.dart
import 'package:english_words/english_words.dart';
import 'package:flutter/material.dart';
import 'package:provider/provider.dart';
void main() {
runApp(MyApp());
}
class MyApp extends StatelessWidget {
const MyApp({super.key});
@override
Widget build(BuildContext context) {
return ChangeNotifierProvider(
create: (context) => MyAppState(),
child: MaterialApp(
title: 'Namer App',
theme: ThemeData(
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 kod satırı, şu ana kadar uygulamanın tamamını oluşturmuştur.
Sonraki bölümde uygulamayı hata ayıklama modunda çalıştırıp geliştirmeye başlayın.
4. Düğme ekle
Bu adımda yeni bir kelime eşlemesi oluşturmak için bir İleri düğmesi eklenir.
Uygulamayı başlat
Ö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, geçerli hedef cihazı gösteren bir düğme bulacaksınız. Değiştirmek için tıklayın.
lib/main.dart
açıkken "oynat"ı bulun düğmesini seçip VS Code penceresinin sağ üst köşesinde bulabilirsiniz.
Yaklaşık bir dakika sonra uygulamanız hata ayıklama modunda başlatılır. Henüz ortada pek bir bilgi yok:
İlk Sıcak Yeniden Yükleme
lib/main.dart
öğesinin alt kısmında, ilk Text
nesnedeki dizeye bir şey ekleyin ve dosyayı kaydedin (Ctrl+S
veya Cmd+S
ile). Örnek:
lib/main.dart
// ...
return Scaffold(
body: Column(
children: [
Text('A random AWESOME idea:'), // ← Example change.
Text(appState.current.asLowerCase),
],
),
);
// ...
Uygulamanın nasıl hemen değiştiğine, ancak rastgele kelimenin aynı kaldığına dikkat edin. Karşınızda Flutter'ın meşhur durumlu Hot Yeniden Yükleme'si. Bir kaynak dosyada değişiklikleri kaydettiğinizde, çalışırken yeniden yükleme tetiklenir.
Sık Sorulan Sorular
- Sıkı Yeniden Yükleme VSCode'da çalışmazsa ne olur?
- 'r' tuşuna basmalı mıyım? nasıl çalışır?
- Yoğun Yeniden Yükleme web'de çalışır mı?
- "Hata ayıklama"yı nasıl kaldırırım? banner'ı tıklasın mı?
Düğme ekleme
Daha sonra, Column
öğesinin alt kısmında, 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ız kursu
Debug Console'u izlemek ne kadar eğlenceliyse düğmenin daha anlamlı bir şey yapmasını istiyorsunuz. Ancak bu aşamaya geçmeden önce, nasıl çalıştığını anlamak için lib/main.dart
içindeki koda daha yakından bakın.
lib/main.dart
// ...
void main() {
runApp(MyApp());
}
// ...
Dosyanın en üst kısmında main()
işlevini bulabilirsiniz. Şu anki biçiminde 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ının kullanım kapsamı StatelessWidget
. Widget'lar tüm Flutter uygulamalarını geliştirirken 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ı ayarlar. Uygulama genelinde durum oluşturur (daha sonra buna daha fazla değineceğiz), uygulamaya ad verir, görsel temayı tanımlar ve "ana sayfa"yı ayarlar widget'ı (uygulamanızın başlangıç noktasıdır).
lib/main.dart
// ...
class MyAppState extends ChangeNotifier {
var current = WordPair.random();
}
// ...
Daha sonra, MyAppState
sınıfı uygulamanın iyi...durumunu tanımlar. Bu, Flutter'a ilk adımınız. Bu nedenle, bu codelab'in amacınız yalnızca Flutter'a odaklanmasını sağlamak. Flutter'da uygulama durumunu yönetmenin birçok etkili yolu vardır. Açıklanması en kolay seçeneklerden biri, bu uygulamanın benimsediği yaklaşım olan ChangeNotifier
.
MyAppState
, uygulamanın çalışması için gereken verileri tanımlar. Şu anda, yalnızca geçerli rastgele kelime çiftini içeren tek bir değişken içerir. Bunu daha sonra ekleyeceksiniz.- Eyalet sınıfının kapsamı
ChangeNotifier
'ı kapsar. Bu, başkalarını kendi değişiklikleri hakkında bilgilendirebileceği anlamına gelir. Örneğin, mevcut kelime çifti değişirse uygulamadaki bazı widget'ların bunu bilmesi gerekir. - Eyalet, bir
ChangeNotifierProvider
kullanılarak oluşturulur ve uygulamanın tamamına sağlanır (yukarıdaki kodaMyApp
bölümünden bakın). Bu işlem, uygulamadaki tüm widget'ların durumu yönetmesine olanak tanır.
lib/main.dart
// ...
class MyHomePage extends StatelessWidget {
@override
Widget build(BuildContext context) { // ← 1
var appState = context.watch<MyAppState>(); // ← 2
return Scaffold( // ← 3
body: Column( // ← 4
children: [
Text('A random AWESOME idea:'), // ← 5
Text(appState.current.asLowerCase), // ← 6
ElevatedButton(
onPressed: () {
print('button pressed!');
},
child: Text('Next'),
),
], // ← 7
),
);
}
}
// ...
Son olarak, daha önce değiştirdiğiniz widget'ı MyHomePage
gördüm. Aşağıdaki numaralı satırların her biri, yukarıdaki kodda bulunan satır numarası yorumuyla eşleşir:
- Her widget, widget'ın her zaman güncel olması için, koşulları her değiştiğinde otomatik olarak çağrılan bir
build()
yöntemi tanımlar. MyHomePage
, uygulamanın mevcut durumundaki değişiklikleriwatch
yöntemini kullanarak izler.- Her
build
yöntemi bir widget veya (daha genel olarak) widget'ların iç içe yerleştirilmiş bir ağacını döndürmelidir. Bu durumda, üst düzey widgetScaffold
olur. Bu codelab'deScaffold
ile çalışmayacaksınız. Ancak bu widget kullanışlı bir widget'tır ve gerçek dünyadaki Flutter uygulamalarının büyük çoğunluğunda bulunur. Column
, Flutter'daki en temel düzen widget'larından biridir. Herhangi bir sayıda alt öğe alır ve bunları yukarıdan aşağıya doğru bir sütuna yerleştirir. Varsayılan olarak, sütun alt öğelerini görsel olarak en üste yerleştirir. Yakında bu ayarı, sütun ortalanacak şekilde değiştireceksiniz.- Bu
Text
widget'ını ilk adımda değiştirdiniz. - Bu ikinci
Text
widget'ıappState
sınıfındaki tek üyeye (current
) (WordPair
) erişir.WordPair
,asPascalCase
veyaasSnakeCase
gibi birçok yardımcı alıcı sağlar. BuradaasLowerCase
kullanılır, ancak alternatiflerden birini tercih ederseniz bunu şimdi değiştirebilirsiniz. - Flutter kodunun sondaki virgülleri nasıl yoğun bir şekilde kullandığına dikkat edin.
children
, buColumn
parametre listesinin son (ve aynı zamanda yalnızca) üyesi olduğundan, bu virgülün burada bulunması gerekmez. Yine de sondaki virgülleri kullanmak genellikle iyi bir fikirdir: Daha fazla üye eklemeyi önemsiz hale getirir ve Dart'ın otomatik biçimlendiricisine yeni bir satır eklemesi için bir ipucu da sağlar. Daha fazla bilgi için Kod biçimlendirmesi başlıklı makaleyi inceleyin.
Daha sonra, düğmeyi duruma bağlayacaksınız.
İlk davranışınız
MyAppState
bölümüne gidin ve bir 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()
öğesini(MyAppState
içeriğini izleyen herkesin bilgilendirilmesini sağlayan bir ChangeNotifier)
yöntemi) çağırır.
Geriye kalan tek işlem, 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'),
),
// ...
Kaydedin ve hemen uygulamayı deneyin. İleri düğmesine her bastığınızda yeni bir rastgele kelime çifti oluşturur.
Sonraki bölümde, kullanıcı arayüzünü daha hoş hale getireceksiniz.
5. Uygulamayı daha güzel hale getirin
Uygulama şu anda bu şekilde görünüyor.
Çok iyi değil. Uygulamanın orta kısmı (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 ana nedeni bu. Ayrıca, uygulama içeriği biraz merkezde değil, tamamen siyah ve beyaz.
Bu bölümde, uygulamanın tasarımı üzerinde çalışılarak söz konusu sorunlar ele alınmaktadır. Bu bölümün nihai hedefi şuna benzer:
Widget çıkarma
Mevcut kelime çiftini göstermekten sorumlu satır artık şu şekilde görünüyor: Text(appState.current.asLowerCase)
. Bunu daha karmaşık bir hale getirmek için bu satırı ayrı bir widget'ta ayıklamak iyi bir fikirdir. Kullanıcı arayüzünüzün ayrı mantıksal bölümleri için ayrı widget'ların olması, Flutter'daki karmaşıklığı yönetmenin önemli bir yoludur.
Flutter, widget'ların ayıklanması için bir yeniden düzenleme yardımcısı sağlar. Ancak bu yardımcıyı kullanmadan önce, çıkarılan satırın yalnızca ihtiyaç duyduğu özelliklere eriştiğinden emin olun. Satır şu anda appState
kelimesine erişiyor, ancak yalnızca geçerli 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
öğesinin tamamını belirtmiyor.
Şimdi Yeniden düzenleme menüsünü çağırın. VS Code'da bunu iki yöntemden birini kullanarak yapabilirsiniz:
- Yeniden düzenlemek istediğiniz kod parçasını sağ tıklayın (bu örnekte
Text
) ve açılır menüden Yeniden düzenle... seçeneğini belirleyin.
VEYA
- İmlecinizi yeniden düzenlemek istediğiniz kod parçasına (bu örnekte
Text
) getirin veCtrl+.
(Win/Linux) veyaCmd+.
(Mac) tuşuna basın.
Yeniden Düzenleme menüsünde, Widget'ı Çıkar'ı seçin. BigCard gibi bir ad atayın ve Enter
simgesini tıklayın.
Bu işlem, geçerli dosyanın sonunda otomatik olarak yeni bir sınıf (BigCard
) oluşturur. Sınıf aşağıdakine benzer:
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 bile nasıl devam ettiğine dikkat edin.
Kart Ekle
Şimdi bu yeni widget'ı, bu bölümün başında düşündüğümüz kalın kullanıcı arayüzü parçasına dönüştürme zamanı geldi.
BigCard
sınıfını ve içerdiği build()
yöntemini bulun. Daha önce olduğu gibi Text
widget'ındaki Yeniden düzenleme menüsünü açın. Ancak bu kez widget'ı çıkarmayacaksınız.
Bunun yerine Dolgu ile Sarma'yı seçin. Bu işlem, Text
widget'ının etrafında Padding
adlı yeni bir üst widget oluşturur. Kaydettikten sonra, bu rastgele kelimenin daha fazla nefes alanı olduğunu görürsünüz.
Dolguyu varsayılan 8.0
değerinden farklı bir değere ayarlayın. Örneğin, daha geniş bir dolgu için 20
gibi bir değer kullanın.
Sonra, bir seviye atlayın. İmlecinizi Padding
widget'ının üzerine getirin, Yeniden düzenleme menüsünü açın ve Widget ile kaydır...'ı seçin.
Bu, üst widget'ı belirtmenize olanak tanır. "Kart" yazın ve Enter tuşuna basın.
Bu işlem, Padding
widget'ını ve dolayısıyla Text
öğesini bir Card
widget'ıyla sarmalar.
Tema ve stil
Kartı daha öne çıkarmak için daha zengin bir renge boyayın. Ayrıca, tutarlı bir renk şeması kullanmak her zaman iyi bir fikir olduğundan rengi seçmek için uygulamanın Theme
öğesini kullanın.
BigCard
ürünü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 gerçekten çok işe yarar:
- Kod öncelikle
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çerir.primary
, uygulamanın en belirgin ve tanımlayıcı rengidir.
Kart, uygulamanın birincil rengiyle boyanır:
MyApp
bölümüne gidip orada ColorScheme
için çekirdek rengini değiştirerek bu rengi ve tüm uygulamanın renk şemasını değiştirebilirsiniz.
Rengin yumuşak bir şekilde animasyonuna dikkat edin. Buna dolaylı animasyon adı verilir. Birçok Flutter widget'ı, değerler arasında sorunsuz bir şekilde ara değer belirler. Böylece, kullanıcı arayüzü yalnızca "atlama" kontrol edebilir.
Kartın altındaki yükseltilmiş düğmenin rengi de değişir. Bu, değerleri sabit kodlamak yerine uygulama genelinde Theme
kullanmanın avantajıdır.
TextTheme
Kartta hâlâ bir sorun var: Metin çok küçük ve rengi zor okunuyor. Bu sorunu düzeltmek için BigCard
ürünü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,
kullanarak uygulamanın yazı tipi temasına erişirsiniz. Bu sınıftabodyMedium
(orta boyutlu standart metinler için),caption
(resimlerin başlıkları için) veyaheadlineLarge
(büyük başlıklar için) gibi üyeler bulunur.displayMedium
özelliği, görünen metin için tasarlanmış büyük bir stildir. Görüntüleme kelimesi burada tipografik anlamda (ör. ekran yazı tipinde) kullanılır.displayMedium
dokümanlarında, "görüntülü reklam stilleri kısa ve önemli metinler için ayrılmıştır". Bu, tam olarak bizim kullanım alanımızı temsil etmektedir.- Temanın
displayMedium
özelliği teorik olaraknull
olabilir. Bu uygulamayı yazarken kullandığınız programlama dili Dart boş güvenlidir. Bu nedenle,null
olabilecek nesnelerin yöntemlerini çağırmanıza izin vermez. Ancak bu durumda, Dart'ın ne yaptığınızı bildiğinizden emin olmak için!
operatörünü ("bang operatörü") kullanabilirsiniz. (Bu durumdadisplayMedium
kesinlikle null değildir. Bunun nedeni, bu codelab'in kapsamı dışındadır.) displayMedium
üzerindecopyWith()
işlevinin çağrılması, tanımladığınız değişiklikleri içeren metin stilinin bir kopyasını döndürür. Bu durumda, yalnızca metnin rengini değiştirirsiniz.- Yeni rengi almak için uygulamanın temasına tekrar erişebilirsiniz. Renk şemasının
onPrimary
özelliği, uygulamanın birincil renginde kullanıma uygun bir renk tanımlar.
Uygulama aşağıdakine benzer bir görünümde olacaktır:
İsterseniz kartı daha fazla değiştirebilirsiniz. Aşağıdaki önerilerden yararlanabilirsiniz:
copyWith()
, yalnızca renkten ziyade metin stilini de çok daha fazla değiştirmenize olanak tanır. Değiştirebileceğiniz özelliklerin tam listesini almak için imlecinizicopyWith()
parantezinin içine yerleştirin veCtrl+Shift+Space
(Win/Linux) veyaCmd+Shift+Space
(Mac) tuşlarına 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 genişletebilirsiniz. - Renklerle denemeler yapın.
theme.colorScheme.primary
dışında.secondary
,.surface
ve daha birçok paket daha var. Bu renklerin hepsininonPrimary
eşdeğeri vardır.
Erişilebilirliği iyileştirin
Flutter, uygulamaları varsayılan olarak erişilebilir hale getirir. Örneğin, her Flutter uygulaması, uygulamadaki tüm metin ve etkileşimli öğeleri TalkBack ve VoiceOver gibi ekran okuyuculara doğru şekilde gösterir.
Ancak bazen birtakım işlemler yapmanız gerekir. Bu uygulama söz konusu olduğunda ekran okuyucu, oluşturulmuş bazı kelime çiftlerini telaffuz etme konusunda sorun yaşayabilir. Kullanıcılar cheaphead ifadesini tanımlamakta sorun yaşamasa da bir ekran okuyucu, kelimenin ortasındaki ph harfini f olarak telaffuz edebilir.
pair.asLowerCase
yerine "${pair.first} ${pair.second}"
kullanmak basit bir çözüm olabilir. İkincisi ise pair
içinde yer alan iki kelimeden bir dize ("cheap head"
gibi) oluşturmak için dize interpolasyonu kullanır. Bileşik bir kelime yerine iki ayrı kelime kullanmak, ekran okuyucuların bunları uygun bir şekilde tanımlamasını ve görme engelli kullanıcılara daha iyi bir deneyim sunmasını sağlar.
Ancak, pair.asLowerCase
ürününün görsel basitliğini korumak isteyebilirsiniz. Metin widget'ının görsel içeriğini ekran okuyucular için daha uygun olan bir anlamsal içerikle geçersiz kılmak için Text
ürününü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 bir kelime çiftini doğru telaffuz eder ancak kullanıcı arayüzü aynı kalır. Cihazınızda bir ekran okuyucu kullanarak bu yöntemi deneyin.
Kullanıcı arayüzünü ortalayın
Rastgele kelime çiftinin yeterli görsel özellikle sunulduğuna göre, onu uygulama penceresinin/ekranının ortasına yerleştirmenin zamanı geldi.
Öncelikle, BigCard
öğesinin bir Column
kapsamında olduğunu unutmayın. Varsayılan olarak, sütunlar alt öğelerini en üste getirir ancak bunu kolayca geçersiz kılabiliriz. MyHomePage
ürünü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'),
),
],
),
);
}
}
// ...
Böylece Column
içindeki alt öğeler ana (dikey) ekseninde ortalanır.
Alt öğeler zaten sütunun çapraz ekseninde ortalanmıştır (yani zaten yatay olarak ortalanmıştır). Ancak Column
öğesinin kendisi Scaffold
içinde ortalanmamıştır. Bunu, Widget İnceleyici'yi kullanarak doğrulayabiliriz.
Widget İnceleyici'nin 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 çocuklarının ihtiyaç duyduğu kadar yatay alan kaplar.
Sütunu ortalayabilirsiniz. İmlecinizi Column
simgesinin üzerine getirin, Yeniden düzenleme menüsünü (Ctrl+.
veya Cmd+.
ile) çağırın ve Ortayla kaydır'ı seçin.
Uygulama aşağıdakine benzer bir görünümde olacaktır:
İsterseniz bu sayıyı biraz daha değiştirebilirsiniz.
BigCard
üzerindekiText
widget'ını kaldırabilirsiniz. Kullanıcı arayüzü onsuz da anlamlı olduğundan, açıklayıcı metnin ("Rastgele bir MUHTEŞEM fikir:") artık gerekli olmadığı söylenebilir. Bu şekilde daha temiz kalır.- Ayrıca
BigCard
ileElevatedButton
arasında birSizedBox(height: 10)
widget'ı ekleyebilirsiniz. Bu şekilde, iki widget arasında biraz daha boşluk olur.SizedBox
widget'ı yalnızca yer kaplar ve hiçbir şeyi kendiliğinden 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 da aşağıdaki gibi görünür:
Sonraki bölümde, oluşturulan kelimeleri favorilere ekleme (veya 'beğenme') özelliği ekleyeceksiniz.
6. İşlev ekle
Uygulama çalışıyor ve ara sıra ilginç kelime çiftleri bile sağlıyor. Ancak kullanıcı İleri'yi tıkladığında her kelime çifti kalıcı olarak kaybolur. “Aklıma takılan” bir fikriniz olması bir öneride bulun: "Beğen" gibi bir düğmesini tıklayın.
İş mantığını ekleyin
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
adlı işletme içinfavorites
adlı yeni bir mülk eklediniz. Bu özellik boş bir listeyle başlatıldı:[]
.- Ayrıca, listenin yalnızca şu kelime çiftlerini içerebileceğini de belirttiniz:
<WordPair>[]
(genel ifadeler kullanılarak). Bu şekilde uygulamanızı daha sağlam hale getirirsiniz. UygulamanızaWordPair
dışında bir şey eklemeye çalıştığınızda Dart, uygulamanızı çalıştırmayı bile reddediyor. Dolayısıyla,favorites
listesini kullanarak hiçbir zaman istenmeyen nesnelerin (ör.null
) içinde gizlenmeyeceğini bilirsiniz.
- Ayrıca,
toggleFavorite()
adlı yeni bir yöntem eklediniz. Bu yöntem, geçerli kelime çiftini sık kullanılanlar listesinden kaldırır (zaten varsa) veya ekler (henüz yoksa). Her iki durumda da kod daha sonranotifyListeners();
öğesini çağırır.
Düğmeyi ekleme
"İş mantığı" ile çıkarsa, kullanıcı arayüzü üzerinde tekrar çalışma zamanıdır. "Beğen"i gönderme "Sonraki" düğmesinin solundaki düğmesi için Row
gerekir. Row
widget'ı, daha önce gördüğünüz Column
öğesinin yatay eşdeğeridir.
İlk olarak, mevcut düğmeyi bir Row
içine yerleştirin. MyHomePage
öğesinin build()
yöntemine gidin, imlecinizi ElevatedButton
simgesinin üzerine getirin, Ctrl+.
veya Cmd+.
ile Yeniden düzenleme menüsünü çağırın ve Satırla Kaydır'ı seçin.
Kaydettiğinizde Row
öğesinin, Column
ile benzer şekilde davrandığını fark edersiniz. Varsayılan olarak, alt öğeleri sola kaydırılır. (Column
, alt öğelerini en üste yerleştirdi.) Bu sorunu düzeltmek için mainAxisAlignment
ile önceki yaklaşımın aynısını kullanabilirsiniz. Ancak, Didaktik (öğrenim) için mainAxisSize
kullanın. Bu işlem, Row
ürününe mevcut yatay alanın tamamını kullanmamasını bildirir.
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 konumuna geri döndü.
Ardından Beğen düğmesini ekleyin ve toggleFavorite()
hesabına bağlayın. Meydan okuma için, aşağıdaki kod bloğuna bakmadan bunu kendiniz yapmayı deneyin.
Bunu aşağıda yapıldığı gibi yapmazsanız sorun yaratmaz. Aslında, önemli bir meydan okumayı istemediğiniz sürece kalp simgesi konusunda endişelenmeyin.
Başarısız olmakta hiçbir sakınca yok. Sonuçta Flutter ile ilk bir saatiniz bu görüşmede.
İkinci düğmeyi MyHomePage
öğesine eklemenin bir yolu vardır. Bu kez, simge içeren bir düğme oluşturmak için ElevatedButton.icon()
oluşturucuyu kullanın. Ardından build
yönteminin üstünde, geçerli kelime çiftinin zaten favorilerde olup olmadığına bağlı olarak uygun simgeyi seçin. Ayrıca, iki düğmeyi birbirinden biraz ayırmak için SizedBox
kullanımını tekrar 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 şu şekilde 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 çubuğu ekle
Çoğu uygulama her şeyi tek bir ekrana sığdıramaz. Bu uygulama muhtemelen bunu yapabilir, ancak eğitim açısından 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
uygulamanızı uygularsınız.
Mümkün olan en kısa sürede bu adımın amacına ulaşmak için MyHomePage
öğesini 2 ayrı widget'a bölün.
MyHomePage
alanının 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'),
),
],
),
],
),
);
}
}
// ...
Bunu kaydettiğinizde, kullanıcı arayüzünün görsel tarafının hazır olduğunu görürsünüz ancak bu özellik çalışmıyor. Gezinme çubuğunda ♥︎ (kalp) simgesi tıklandığında herhangi bir işlem yapılmaz.
Değişiklikleri inceleyin.
- Öncelikle,
MyHomePage
içeriğinin tamamının yeni bir widget'a (GeneratorPage
) çıkarıldığına dikkat edin. EskiMyHomePage
widget'ının çıkarılmayan tek bölümüScaffold
. - Yeni
MyHomePage
, iki alt öğesi olan birRow
içeriyor. İlk widgetSafeArea
, ikincisi iseExpanded
widget'ı. SafeArea
, alt öğesinin bir donanım çentiği veya durum çubuğu tarafından engellenmemesini sağlar. Bu uygulamada widget, gezinme düğmelerinin mobil durum çubuğu tarafından gizlenmesini önlemek içinNavigationRail
etrafını kaplar.- NavigationRail'deki
extended: false
satırınıtrue
olarak değiştirebilirsiniz. Bu işlem, simgelerin yanında etiketleri gösterir. Sonraki bir adımda, uygulamada yeterli yatay alan olduğunda bunu otomatik olarak nasıl yapacağınızı öğreneceksiniz. - Gezinme çubuğunda ilgili simgeler ve etiketlerle birlikte iki hedef (Ana Sayfa ve Favoriler) bulunur. Ayrıca geçerli
selectedIndex
öğesini de tanımlar. Sıfır değerine sahip seçili bir dizin ilk hedefi seçer, bir seçili dizin ikinci hedefi seçer ve bu şekilde devam eder. Şimdilik sıfır olarak kodlanmıştır. - Gezinme sütunu, kullanıcı
onDestinationSelected
ile hedeflerden birini seçtiğinde ne olacağını da tanımlar. Uygulama şu anda yalnızca istenen dizin değeriniprint()
ile oluşturuyor. 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 alanın mümkün olduğunca büyük kısmını kaplayacağı (bu örnekteExpanded
) gibi düzenlerin ifade edilmesini sağlar.Expanded
widget'ları hakkında düşünülecek şeylerden biri de "açılmış" olmasıdır. Bu widget'ın rolünü daha iyi anlamak istersenizSafeArea
widget'ını başka birExpanded
ile sarmalamayı deneyin. Elde edilen düzen aşağıdaki gibi görünür:
- Gezinme rayının solda yalnızca küçük bir dilime ihtiyacı olsa da iki
Expanded
widget'ı, mevcut yatay alanın tamamını kendi aralarında böldü. Expanded
widget'ının içinde renkli birContainer
, kapsayıcının içinde iseGeneratorPage
var.
Durum bilgisiz ve durum bilgili widget'lar
MyAppState
şimdiye kadar eyaletle ilgili tüm ihtiyaçlarınızı karşılıyordu. Bu nedenle, şu ana kadar yazdığınız tüm widget'lar durumbilgisizdir. Kendi değiştirilebilir durumlarını içermezler. Widget'ların hiçbiri kendi kendine değişiklik yapamaz, yani MyAppState
üzerinden gitmeleri gerekir.
Bu ayar yakında değişecek.
Gezinme çubuğu selectedIndex
değerini tutmak için bir yönteme ihtiyacınız var. Bu değeri, onDestinationSelected
geri çağırmasından da değiştirebilmek isteyebilirsiniz.
selectedIndex
öğesini başka bir MyAppState
özelliği olarak ekleyebilirsiniz. Bu da işe yarayacaktır. Ancak her widget'ın değerlerini sakladığı takdirde uygulama durumunun hızla artacağını tahmin edebilirsiniz.
Bazı durumlar yalnızca tek bir widget ile alakalı olduğundan bu widget'ta kalması gerekir.
State
içeren bir widget türü olan StatefulWidget
değerini girin. Öncelikle MyHomePage
öğesini durum bilgili bir widget'a dönüştürün.
İmlecinizi MyHomePage
etiketinin ilk satırına (class MyHomePage...
ile başlayan satır) yerleştirin ve Ctrl+.
veya Cmd+.
kullanarak Yeniden Düzenleme menüsünü çağırın. Ardından Convert to StatefulWidget'ı (StatefulWidget'a dönüştür) seçin.
IDE, sizin (_MyHomePageState
) sizin için yeni bir sınıf oluşturur. Bu sınıf State
öğesini genişletir ve dolayısıyla kendi değerlerini yönetebilir. (Kendini değiştirebilir.) Ayrıca eski durum bilgisiz widget'taki build
yönteminin, widget'ta kalmak yerine _MyHomePageState
içine taşındığına da dikkat edin. Dosya tam olarak taşındı. build
yönteminde hiçbir şey değişmedi. Artık başka bir yerde yaşıyor.
setState
Yeni durum bilgili widget'ın yalnızca bir değişkeni izlemesi yeterlidir: selectedIndex
. _MyHomePageState
öğesinde 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
selectedIndex
değişkenini kullanıma sunuyor ve0
olarak başlatıyorsunuz. - Bu yeni değişkeni, şimdiye kadar mevcut olan sabit kodlu
0
yerineNavigationRail
tanımında kullanıyorsunuz. onDestinationSelected
geri çağırması çağrıldığında, yeni değeri yalnızca konsola yazdırmak yerine birsetState()
çağrısı içindeselectedIndex
öğesine atarsınız. Bu çağrı, daha önce kullanılannotifyListeners()
yöntemine benzediğinden kullanıcı arayüzünün güncellenmesini sağlar.
Gezinme sütunu artık kullanıcı etkileşimine yanıt verir. Ancak sağ taraftaki genişletilmiş alan aynı kalır. Bunun nedeni, kodun hangi ekranın gösterileceğini belirlemek için selectedIndex
kullanmamasıdır.
selectedIndex'i kullan
Aşağıdaki kodu _MyHomePageState
öğesinin build
yönteminin en üstüne, return Scaffold
tarihinden hemen önce olacak şekilde 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');
}
// ...
Bu kod parçasını inceleyin:
- Kod,
Widget
türünde yeni bir değişken (page
) bildirir. - Ardından, bir Switch ifadesi,
selectedIndex
içindeki mevcut değere görepage
cihazına bir ekran atar. - Henüz
FavoritesPage
olmadığındanPlaceholder
; nereye yerleştirdiğinizi gösteren çarpılı bir dikdörtgen çizerek kullanıcı arayüzünün o bölümünü tamamlanmamış olarak işaretleyen kullanışlı bir widget.
- Hata sonrası ilkesi uygulandığında,
selectedIndex
değeri 0 veya 1 değilse anahtar ifadesi, hatanın verilmesini de sağlar. Bu, ileride oluşabilecek hataların önlenmesine yardımcı olur. Gezinme çubuğuna yeni bir hedef ekler ve bu kodu güncellemeyi unutursanız program geliştirme aşamasında kilitlenir (işlerin neden çalışmadığını tahmin etmenize veya hatalı bir kodu üretime yayınlamanıza izin vermek yerine).
page
sağ tarafta göstermek istediğiniz widget'ı içerdiğinden, muhtemelen başka hangi değişikliğin gerekli olduğunu tahmin edebilirsiniz.
Kalan tek değişiklikten 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 şimdi GeneratorPage
sayfamız 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. Diğer bir deyişle, yeterince yer olduğunda etiketlerin (extended: true
kullanarak) otomatik olarak gösterilmesini sağlayın.
Flutter, uygulamalarınızı otomatik olarak duyarlı hale getirmenize yardımcı olan çeşitli widget'lar sağlar. Örneğin Wrap
, alt öğeleri otomatik olarak sonraki "satıra" sarmalayan Row
veya Column
benzeri bir widget'tır. ("koşu" olarak adlandırılır), ancak yeterli dikey veya yatay alan olmadığında FittedBox
adlı widget var. Bu widget, çocuğunuzun özelliklerine göre, mevcut alana otomatik olarak yerleştirilecek.
Ancak NavigationRail
, yeterli alan olduğunda etiketleri otomatik olarak göstermez çünkü her bağlamda neyin yeterli olduğunu bilemez. Bu aramayı yapmak, geliştirici olarak size bağlıdır.
Yalnızca MyHomePage
en az 600 piksel genişliğinde olduğunda etiketleri göstermeye karar verdiğinizi varsayalım.
Bu durumda LayoutBuilder
widget'ı kullanılacak. Bu özellik, ne kadar kullanılabilir alanınız olduğuna bağlı olarak widget ağacınızı değiştirebilmenizi sağlar.
Gerekli değişiklikleri yapmak için Flutter'ın VS Code'daki Refactor menüsünü kullanın. Ancak bu sefer durum biraz daha karmaşıktır:
_MyHomePageState
ürünününbuild
yönteminde imleciniziScaffold
üzerine getirin.Ctrl+.
(Windows/Linux) veyaCmd+.
(Mac) ile Yeniden Düzenleme menüsünü çağırın.- Builder ile Sar'ı seçin ve Enter tuşuna basın.
LayoutBuilder
klasörüne yeni eklenenBuilder
öğesinin adını değiştirin.- Geri çağırma parametresi listesini
(context)
yerine(context, constraints)
olarak değiştirin.
Kısıtlamalar her değiştiğinde LayoutBuilder
tarafından builder
geri çağırması yapılır. Bu, örneğin:
- Kullanıcı, uygulamanın penceresini yeniden boyutlandırır
- Kullanıcı, telefonunu dikey moddan yatay moda veya arkaya döndürür
MyHomePage
öğesinin yanındaki bazı widget'ların boyutu büyüdükçeMyHomePage
kısıtlamaları küçülüyor- Bunlara benzer başka hedefleriniz de olabilir.
Kodunuz artık mevcut constraints
öğesini sorgulayarak etiketin gösterilip gösterilmeyeceğine karar verebilir. _MyHomePageState
öğesinin 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,
),
),
],
),
);
});
}
}
// ...
Uygulamanız artık ekran boyutu, yönü ve platform gibi ortama göre tepki veriyor. Diğer bir deyişle, duyarlıdır!
Geriye kalan tek işlem, bu Placeholder
yerine gerçek bir Favoriler ekranı eklemektir. Bu konu bir sonraki bölümde ele alınmış.
8. Yeni sayfa ekle
Favoriler sayfası yerine kullandığımız Placeholder
widget'ını hatırlıyor musunuz?
Bu sorunu düzeltmenin zamanı geldi.
Maceraya düşkün hissediyorsanız bu adımı kendiniz tamamlamaya çalışın. Hedefiniz, favorites
listesini durum bilgisiz yeni bir widget'ta (FavoritesPage
) ve ardından Placeholder
yerine bu widget'ı göstermek.
Birkaç noktayı aşağıda bulabilirsiniz:
- Kaydıran bir
Column
istediğinizdeListView
widget'ını kullanın. MyAppState
örneğinecontext.watch<MyAppState>()
kullanarak herhangi bir widget'tan erişmeyi unutmayın.- Yeni bir widget da denemek isterseniz
ListTile
,title
(genellikle metin için),leading
(simgeler veya avatarlar için) veonTap
(etkileşimler için) gibi özelliklere sahiptir. Ancak, bildiğiniz widget'larla benzer efektler elde edebilirsiniz. - Dart, koleksiyon değiş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 koda sahip olabilirsiniz:
Diğer yandan, işlevsel programlamaya daha aşinaysanız Dart, messages.map((m) => Text(m)).toList()
gibi kodlar yazmanıza da olanak tanır. Elbette istediğiniz zaman widget listesi oluşturabilir ve build
yöntemini kullanarak bu listeye zorunlu olarak ekleme yapabilirsiniz.
Favoriler sayfasını kendiniz eklemenin avantajı, kendi kararlarınızı vererek daha fazla bilgi edinmenizdir. Bunun dezavantajı, henüz kendi başınıza çözemediğiniz bir sorunla karşılaşabilmenizdir. Unutmayın: Başarısız olmak normaldir ve öğrenmenin en önemli unsurlarından biridir. Hiç kimse sizden ilk bir saat içinde Flutter'ı geliştirmenizi beklemiyor, siz de bunu başarmamalısınız.
Ardından, favoriler sayfasını uygulamanın bir yönteminden yararlanabilirsiniz. Kodun uygulanma şekli size kod üzerinde oynama konusunda ilham verir. Kullanıcı arayüzünü geliştirip kendi tarzınızı ortaya koyarsınız.
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 aşağıda açıklanmıştır:
- Uygulamanın mevcut durumunu alır.
- Favori listesi boşsa ortalanmış bir mesaj gösterilir: Henüz favori yok*.*
- Aksi takdirde bir (kaydırılabilir) liste gösterilir.
- Liste bir özetle başlar (örneğin, 5 favoriniz var*.*).
- Kod daha sonra tüm favorileri yineler ve her biri için bir
ListTile
widget'ı oluşturur.
Şu an geriye yalnızca Placeholder
widget'ını bir FavoritesPage
ile değiştirmek yeterli olacaktır. İşte bu kadar!
Bu uygulamanın son kodunu GitHub'daki codelab deposunda bulabilirsiniz.
9. Sonraki adımlar
Tebrikler!
Harikasın! Bir Column
ve iki Text
widget'ıyla işlevsel olmayan bir yapı iskelesi aldınız ve bunu duyarlı, eğlenceli bir küçük uygulamaya dönüştürdünüz.
İşlediğimiz konular
- Flutter'ın çalışma şekliyle ilgili temel bilgiler
- Flutter'da düzen oluşturma
- Kullanıcı etkileşimlerini (düğmelere basma gibi) uygulama davranışına bağlama
- Flutter kodunuzu düzenli tutma
- Uygulamanızı duyarlı hale getirme
- Tutarlı bir görünüm ve uygulamanızın hissi
Sonra ne olur?
- Bu laboratuvarda yazdığınız uygulamayla daha fazla deneme yapın.
- Animasyonlu listeleri, gradyanları, geçişleri ve daha fazlasını nasıl ekleyebileceğinizi görmek 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.