İlk Flutter uygulamanız

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.

e9c6b402cd8003fd.png

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

228c71510a8e868.png

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.

16695777c07f18e5.png

Ö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:

  1. Flutter SDK'sı
  2. Flutter eklentisiyle Visual Studio Kodu
  3. 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

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.

260a7d97f9678005.png

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.

e2a5bab0be07f4f7.png

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.

a781f218093be8e0.png

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.

e54c671c9bb4d23d.png

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 b0a5d0200af5985d.png 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:

f96e7dfb0937d7f4.png

İ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

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 koda MyApp bölümünden bakın). Bu işlem, uygulamadaki tüm widget'ların durumu yönetmesine olanak tanır. d9b6ecac5494a6ff.png

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:

  1. 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.
  2. MyHomePage, uygulamanın mevcut durumundaki değişiklikleri watch yöntemini kullanarak izler.
  3. 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 widget Scaffold olur. Bu codelab'de Scaffold 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.
  4. 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.
  5. Bu Text widget'ını ilk adımda değiştirdiniz.
  6. Bu ikinci Text widget'ı appState sınıfındaki tek üyeye (current) (WordPair) erişir. WordPair, asPascalCase veya asSnakeCase gibi birçok yardımcı alıcı sağlar. Burada asLowerCase kullanılır, ancak alternatiflerden birini tercih ederseniz bunu şimdi değiştirebilirsiniz.
  7. Flutter kodunun sondaki virgülleri nasıl yoğun bir şekilde kullandığına dikkat edin. children, bu Column 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.

3dd8a9d8653bdc56.png

Ç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:

2bbee054d81a3127.png

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:

  1. 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

  1. İmlecinizi yeniden düzenlemek istediğiniz kod parçasına (bu örnekte Text) getirin ve Ctrl+. (Win/Linux) veya Cmd+. (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.

6031adbc0a11e16b.png

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:

a136f7682c204ea1.png

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ıfta bodyMedium (orta boyutlu standart metinler için), caption (resimlerin başlıkları için) veya headlineLarge (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 olarak null 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 durumda displayMedium kesinlikle null değildir. Bunun nedeni, bu codelab'in kapsamı dışındadır.)
  • displayMedium üzerinde copyWith() 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:

2405e9342d28c193.png

İ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 imlecinizi copyWith() parantezinin içine yerleştirin ve Ctrl+Shift+Space (Win/Linux) veya Cmd+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 hepsinin onPrimary 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.

d1fad7944fb890ea.png

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.

b555d4c7f5000edf.png

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:

455688d93c30d154.png

İsterseniz bu sayıyı biraz daha değiştirebilirsiniz.

  • BigCard üzerindeki Text 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 ile ElevatedButton arasında bir SizedBox(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:

3d53d2b071e2f372.png

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.

e6b01a8c90df8ffa.png

İş 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çin favorites 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ıza WordPair 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 sonra notifyListeners(); öğ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ü.

3d53d2b071e2f372.png

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.

e6b01a8c90df8ffa.png

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.

252f7c4a212c94d2.png

İ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.

f62c54f5401a187.png

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.

388bc25fe198c54a.png

Değişiklikleri inceleyin.

  • Öncelikle, MyHomePage içeriğinin tamamının yeni bir widget'a (GeneratorPage) çıkarıldığına dikkat edin. Eski MyHomePage widget'ının çıkarılmayan tek bölümü Scaffold.
  • Yeni MyHomePage, iki alt öğesi olan bir Row içeriyor. İlk widget SafeArea, ikincisi ise Expanded 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çin NavigationRail 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ğerini print() ile oluşturuyor.
  • Row öğesinin ikinci alt öğesi Expanded 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 örnekte SafeArea) ve diğer widget'lar alanın mümkün olduğunca büyük kısmını kaplayacağı (bu örnekte Expanded) 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 isterseniz SafeArea widget'ını başka bir Expanded ile sarmalamayı deneyin. Elde edilen düzen aşağıdaki gibi görünür:

6bbda6c1835a1ae.png

  • 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 bir Container, kapsayıcının içinde ise GeneratorPage 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.

e52d9c0937cc0823.jpeg

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:

  1. Yeni selectedIndex değişkenini kullanıma sunuyor ve 0 olarak başlatıyorsunuz.
  2. Bu yeni değişkeni, şimdiye kadar mevcut olan sabit kodlu 0 yerine NavigationRail tanımında kullanıyorsunuz.
  3. onDestinationSelected geri çağırması çağrıldığında, yeni değeri yalnızca konsola yazdırmak yerine bir setState() çağrısı içinde selectedIndex öğesine atarsınız. Bu çağrı, daha önce kullanılan notifyListeners() 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:

  1. Kod, Widget türünde yeni bir değişken (page) bildirir.
  2. Ardından, bir Switch ifadesi, selectedIndex içindeki mevcut değere göre page cihazına bir ekran atar.
  3. Henüz FavoritesPage olmadığından Placeholder; 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.

5685cf886047f6ec.png

  1. 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.

a8873894c32e0d0b.png

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:

  1. _MyHomePageState ürününün build yönteminde imlecinizi Scaffold üzerine getirin.
  2. Ctrl+. (Windows/Linux) veya Cmd+. (Mac) ile Yeniden Düzenleme menüsünü çağırın.
  3. Builder ile Sar'ı seçin ve Enter tuşuna basın.
  4. LayoutBuilder klasörüne yeni eklenen Builder öğesinin adını değiştirin.
  5. 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çe MyHomePage 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ğinizde ListView widget'ını kullanın.
  • MyAppState örneğine context.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) ve onTap (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:

f0444bba08f205aa.png

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.

252f7c4a212c94d2.png

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.

d6e3d5f736411f13.png

İş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.