Bu codelab hakkında
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ı favorilere ekleyebilir 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 bağlama
- 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ğine dair 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)
Sonraki bölümde ilk Flutter projenizi oluşturacaksınız.
Şimdiye kadar sorun yaşadıysanız StackOverflow'daki bu sorulardan ve yanıtlardan bazıları sorun giderme konusunda size yardımcı olabilir.
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 Java hatasıyla nasıl başa çıkabilirim?- Android
sdkmanager
aracı bulunamadı sorununu nasıl çözebilirim? - "
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ğini uygulamanın temel iskeletiyle üzerine yazarak 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.8.0
dependencies:
flutter:
sdk: flutter
english_words: ^4.0.0
provider: ^6.1.5
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(
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: [
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.
Uygulamanız yaklaşık bir dakika sonra 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 olsa da düğmenin daha anlamlı bir şey yapmasını istersiniz. Ancak bu konuya geçmeden önce, işleyiş şeklini anlamak için lib/main.dart
dosyasında bulunan 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(
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 fazla bilgiyi aşağıda bulabilirsiniz), 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, bir
ChangeNotifierProvider
kullanılarak oluşturulur ve uygulamanın tamamına sağlanır (MyApp
içindeki 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
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
, buColumn
parametre listesinin son (ve aynı zamanda tek) üyesi olduğu için bu virgülün burada bulunması gerekmez. 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.
Ardından 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ı haline getirmenin zamanı geldi.
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öreceksiniz.
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.
lib/main.dart
// ...
class BigCard extends StatelessWidget {
const BigCard({super.key, required this.pair});
final WordPair pair;
@override
Widget build(BuildContext context) {
return Card(
child: Padding(
padding: const EdgeInsets.all(20),
child: Text(pair.asLowerCase),
),
);
}
}
// ...
Uygulama şu şekilde görünür:
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 tamamının renk şemasını değiştirebilirsiniz.
Rengin nasıl sorunsuz bir şekilde animasyonlandığını 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 okunaklı değil. Bu sorunu düzeltmek için 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.
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ı, TalkBack ve VoiceOver gibi ekran okuyuculara uygulamadaki tüm metinleri ve etkileşimli öğeleri 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.
Bir çözüm, pair.asLowerCase
değerini "${pair.first} ${pair.second}"
ile değiştirmektir. İkincisi, pair
içinde bulunan 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. Sütunlar varsayılan olarak alt öğelerini en üstte toplar ancak bunu 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) gizlenemeyeceğini bilirsiniz.
- 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
öğesinin mevcut yatay alanın tamamını kaplamaması gerektiğini belirtir.
Aşağıdaki değişikliği yapın:
lib/main.dart
// ...
class MyHomePage extends StatelessWidget {
@override
Widget build(BuildContext context) {
var appState = context.watch<MyAppState>();
var pair = appState.current;
return Scaffold(
body: Center(
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: [
BigCard(pair: pair),
SizedBox(height: 10),
Row(
mainAxisSize: MainAxisSize.min, // ← Add this.
children: [
ElevatedButton(
onPressed: () {
appState.getNext();
},
child: Text('Next'),
),
],
),
],
),
),
);
}
}
// ...
Kullanıcı arayüzü eski haline döner.
Ardından Beğen düğmesini ekleyin ve toggleFavorite()
öğesine bağlayın. Kendinize bir meydan okuma olarak önce aşağıdaki kod bloğuna bakmadan bunu kendi başınıza yapmayı deneyin.
Aşağıdaki şekilde tam olarak aynı şekilde yapmasanız da olur. Gerçekten zor 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()
yapıcısını 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
içindekiextended: 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ı widget'ların 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 solda 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 değiştirilmelidir.
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
'ü 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ğırın. Ardından StatefulWidget'e dönüştür'ü seçin.
IDE, sizin için yeni bir sınıf oluşturur: _MyHomePageState
. 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. Metin kelimesi kelimesine taşındı. build
yönteminde 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.NavigationRail
tanımında, şimdiye kadar bulunan sabit kodlu0
yerine bu yeni değişkeni 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
'ın build
yönteminin en üstüne, return Scaffold
öğesinin hemen önüne 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
değişkenine bir ekran atar. - Henüz
FavoritesPage
olmadığındanPlaceholder
'ü kullanın. Bu kullanışlı widget, yerleştirdiğiniz her yere ç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 sayede, ileride oluşabilecek hataları önleyebilirsiniz. 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.- Oluşturucu 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
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ı olmalıdır.
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şmezleri içinde
for
döngülerinin kullanılmasına izin verir. Ö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. Bunun dezavantajı, 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 verir. 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. İş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 bağlama
- 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.