İlk Flutter uygulamanız

1. Giriş

Flutter, Google'ın tek bir kod tabanından mobil, web ve masaüstü için uygulamalar oluşturmaya yönelik kullanıcı arayüzü araç setidir. Bu codelab'de aşağıdaki Flutter uygulamasını oluşturacaksınız:

Uygulama, "newstay", "lightstream", "mainbrake" veya "graypine" gibi kulağa hoş gelen adlar oluşturur. Kullanıcı, bir sonraki adı isteyebilir, mevcut adı favorileyebilir ve ayrı bir sayfada favori adların listesini inceleyebilir. Uygulama, farklı ekran boyutlarına uyumludur.

Neler öğreneceksiniz?

  • Flutter'ın işleyiş şekliyle ilgili temel bilgiler
  • Flutter'da düzenler oluşturma
  • Kullanıcı etkileşimlerini (ör. düğmeye basma) uygulama davranışıyla ilişkilendirme
  • Flutter kodunuzu düzenli tutma
  • Uygulamanızı duyarlı hale getirme (farklı ekranlar için)
  • Uygulamanızın görünümü ve tarzını tutarlı hale getirme

Doğrudan ilgi çekici bölümlere atlayabilmek için temel bir iskeletle başlarsınız.

e9c6b402cd8003fd.png

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.

228c71510a8e868.png

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.

16695777c07f18e5.png

Örneğin, bir Flutter uygulaması geliştirmek için Windows dizüstü bilgisayar kullandığınızı varsayalım. Geliştirme hedefiniz olarak Android'i seçerseniz genellikle Windows dizüstü bilgisayarınıza USB kablosuyla bir Android cihaz bağlarsınız ve geliştirme aşamasındaki uygulamanız bu bağlı Android cihazda çalışır. Geliştirme hedefi olarak Windows'u da seçebilirsiniz. Bu durumda, geliştirme aşamasındaki uygulamanız düzenleyicinizle birlikte Windows uygulaması olarak çalışır.

Geliştirme hedefiniz olarak web'i seçmek cazip gelebilir. Bu seçimin dezavantajı, Flutter'ın en kullanışlı geliştirme özelliklerinden birini (Durumlu Sıcak Yeniden Yükleme) kaybetmenizdir. Flutter, web uygulamalarını sıcak yeniden yükleyemez.

Hemen seçiminizi yapın. Unutmayın: Uygulamanızı daha sonra istediğiniz zaman diğer işletim sistemlerinde çalıştırabilirsiniz. Bununla birlikte, kafanızda net bir geliştirme hedefi olması sonraki adımı daha kolaylaştırır.

Flutter'ı yükleme

Flutter SDK'sının nasıl yükleneceğiyle ilgili en güncel talimatlar her zaman docs.flutter.dev adresindedir.

Flutter web sitesindeki talimatlar yalnızca SDK'nın kurulumunu değil, geliştirme hedefine ilişkin araçları ve düzenleyici eklentilerini de kapsar. Bu codelab için yalnızca aşağıdakileri yüklemeniz gerektiğini unutmayın:

  1. Flutter SDK'sı
  2. Flutter eklentisi yüklü Visual Studio Code
  3. Seçtiğiniz geliştirme hedefi için gereken yazılım (ör. Windows'u hedeflemek için Visual Studio veya macOS'i hedeflemek için Xcode)

Bir sonraki bölümde ilk Flutter projenizi oluşturacaksınız.

Şimdiye kadar sorun yaşadıysanız bu sorulardan ve yanıtlardan (StackOverflow'dan) bazılarını sorun giderme konusunda faydalı bulabilirsiniz.

Sık Sorulan Sorular

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.

260a7d97f9678005.png

Flutter, proje klasörünüzü oluşturur ve VS Code bu klasörü açar.

Artık 3 dosyanın içeriğinin üzerine uygulamanın temel iskeletini yazarak bu dosyaları değiştireceksiniz.

İlk uygulamayı kopyalama ve yapıştırma

VS Code'un sol bölmesinde Gezgin'in seçili olduğundan emin olun ve pubspec.yaml dosyasını açın.

e2a5bab0be07f4f7.png

Bu dosyanın içeriğini aşağıdakiyle değiştirin:

pubspec.yaml

name: namer_app
description: A new Flutter project.

publish_to: 'none' # Remove this line if you wish to publish to pub.dev

version: 0.0.1+1

environment:
  sdk: ^3.6.0

dependencies:
  flutter:
    sdk: flutter

  english_words: ^4.0.0
  provider: ^6.1.2

dev_dependencies:
  flutter_test:
    sdk: flutter

  flutter_lints: ^5.0.0

flutter:
  uses-material-design: true

pubspec.yaml dosyası, uygulamanızla ilgili temel bilgileri (ör. mevcut sürümü, bağımlılıkları ve birlikte gönderilecek öğeler) belirtir.

Ardından projede başka bir yapılandırma dosyası (analysis_options.yaml) açın.

a781f218093be8e0.png

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

e54c671c9bb4d23d.png

Bu dosyanın içeriğini aşağıdakiyle değiştirin:

lib/main.dart

import 'package:english_words/english_words.dart';
import 'package:flutter/material.dart';
import 'package:provider/provider.dart';

void main() {
  runApp(MyApp());
}

class MyApp extends StatelessWidget {
  const MyApp({super.key});

  @override
  Widget build(BuildContext context) {
    return ChangeNotifierProvider(
      create: (context) => MyAppState(),
      child: MaterialApp(
        title: 'Namer App',
        theme: ThemeData(
          useMaterial3: true,
          colorScheme: ColorScheme.fromSeed(seedColor: Colors.deepOrange),
        ),
        home: MyHomePage(),
      ),
    );
  }
}

class MyAppState extends ChangeNotifier {
  var current = WordPair.random();
}

class MyHomePage extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    var appState = context.watch<MyAppState>();

    return Scaffold(
      body: Column(
        children: [
          Text('A random idea:'),
          Text(appState.current.asLowerCase),
        ],
      ),
    );
  }
}

Bu 50 satır kod, uygulamanın şimdiye kadarki tamamıdır.

Sonraki bölümde, uygulamayı hata ayıklama modunda çalıştırın ve geliştirmeye başlayın.

4. Düğme ekleme

Bu adımda, yeni bir kelime eşleştirmesi oluşturmak için bir Sonraki düğmesi eklenir.

Uygulamayı başlatın

Öncelikle lib/main.dart'ü açın ve hedef cihazınızı seçtiğinizden emin olun. VS Code'un sağ alt köşesinde, mevcut hedef cihazı gösteren bir düğme bulunur. Değiştirmek için tıklayın.

lib/main.dart açıkken VS Code penceresinin sağ üst köşesindeki "oynat" b0a5d0200af5985d.png düğmesini bulup tıklayın.

Yaklaşık bir dakika sonra uygulamanız hata ayıklama modunda başlatılır. Henüz çok fazla şey yok:

f96e7dfb0937d7f4.png

İ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

Düğme ekleme

Ardından, Column öğesinin en altına, ikinci Text örneğinin hemen altına bir düğme ekleyin.

lib/main.dart

// ...

    return Scaffold(
      body: Column(
        children: [
          Text('A random AWESOME idea:'),
          Text(appState.current.asLowerCase),

          // ↓ Add this.
          ElevatedButton(
            onPressed: () {
              print('button pressed!');
            },
            child: Text('Next'),
          ),

        ],
      ),
    );

// ...

Değişikliği kaydettiğinizde uygulama tekrar güncellenir: Bir düğme görünür ve bu düğmeyi tıkladığınızda VS Code'daki Hata Ayıklama Konsolu'nda düğmeye basıldı mesajı gösterilir.

5 dakikada Flutter hızlandırılmış kursu

Hata Ayıklama Konsolu'nu izlemek ne kadar eğlenceli olursa olsun düğmenin daha anlamlı bir şey yapmasını istersiniz. Ancak bu konuya geçmeden önce, işleyiş şeklini anlamak için lib/main.dart kodundaki kodu daha yakından inceleyin.

lib/main.dart

// ...

void main() {
  runApp(MyApp());
}

// ...

Dosyanın en üstünde main() işlevini görürsünüz. Mevcut haliyle, Flutter'a yalnızca MyApp içinde tanımlanan uygulamayı çalıştırmasını söyler.

lib/main.dart

// ...

class MyApp extends StatelessWidget {
  const MyApp({super.key});

  @override
  Widget build(BuildContext context) {
    return ChangeNotifierProvider(
      create: (context) => MyAppState(),
      child: MaterialApp(
        title: 'Namer App',
        theme: ThemeData(
          useMaterial3: true,
          colorScheme: ColorScheme.fromSeed(seedColor: Colors.deepOrange),
        ),
        home: MyHomePage(),
      ),
    );
  }
}

// ...

MyApp sınıfı, StatelessWidget sınıfını genişletir. Widget'lar, her Flutter uygulamasını oluşturduğunuz öğelerdir. Gördüğünüz gibi uygulamanın kendisi bile bir widget'tır.

MyApp içindeki kod, uygulamanın tamamını kurar. Uygulama genelindeki durumu oluşturur (bu konu hakkında daha sonra daha fazla bilgi verilecektir), uygulamayı adlandırır, görsel temayı tanımlar ve uygulamanızın başlangıç noktası olan "ana ekran" widget'ını ayarlar.

lib/main.dart

// ...

class MyAppState extends ChangeNotifier {
  var current = WordPair.random();
}

// ...

Ardından MyAppState sınıfı, uygulamanın durumunu tanımlar. Flutter'a ilk girişiminiz olduğu için bu kod laboratuvarını basit ve odaklanmış tutacağız. Flutter'da uygulama durumunu yönetmenin birçok güçlü yolu vardır. Açıklaması en kolay olanlardan biri, bu uygulamanın benimsediği yaklaşım olan ChangeNotifier.

  • MyAppState, uygulamanın çalışması için ihtiyaç duyduğu verileri tanımlar. Şu anda yalnızca mevcut rastgele kelime çiftini içeren tek bir değişken içeriyor. Daha sonra bu listeye ekleme yapabilirsiniz.
  • Durum sınıfı ChangeNotifier'ü genişletir. Diğerlerini kendi değişiklikleri hakkında bildirebilir. Örneğin, mevcut kelime çifti değişirse uygulamadaki bazı widget'ların bunu bilmesi gerekir.
  • Durum, ChangeNotifierProvider kullanılarak oluşturulur ve uygulamanın tamamına sağlanır (MyApp'teki yukarıdaki koda bakın). Bu sayede uygulamadaki tüm widget'lar durumu elde edebilir. 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örebilirsiniz. Aşağıdaki her numaralı satır, yukarıdaki koddaki bir satır numarası yorumuyla eşlenir:

  1. 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.
  2. MyHomePage, watch yöntemini kullanarak uygulamanın mevcut durumundaki değişiklikleri izler.
  3. 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 widget Scaffold'tir. Bu kod laboratuvarında Scaffold ile çalışmayacaksınız ancak faydalı bir widget olan Scaffold, gerçek Flutter uygulamalarının büyük çoğunluğunda bulunur.
  4. 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.
  5. İlk adımda bu Text widget'ını değiştirdiniz.
  6. Bu ikinci Text widget'ı, appState değerini alır ve bu sınıfın tek üyesi olan current'ye (WordPair) erişir. WordPair, asPascalCase veya asSnakeCase gibi çeşitli yararlı alıcı sağlar. Burada asLowerCase kullanıyoruz ancak alternatiflerden birini tercih ederseniz bunu hemen değiştirebilirsiniz.
  7. Flutter kodunda son virgüllerin yoğun şekilde kullanıldığına dikkat edin. children, söz konusu Column parametre listesinin son (ve aynı zamanda tek) üyesi olduğu için bu virgül burada olmamalıdır. Yine de genellikle son virgül kullanmak iyi bir fikirdir: Daha fazla üye eklemeyi kolaylaştırır ve Dart'ın otomatik biçimlendiricisine buraya yeni bir satır koyması için ipucu verir. Daha fazla bilgi için Kod biçimlendirme bölümüne bakın.

Ardından düğmeyi duruma bağlayın.

İlk davranışınız

MyAppState bölümüne gidip getNext yöntemi ekleyin.

lib/main.dart

// ...

class MyAppState extends ChangeNotifier {
  var current = WordPair.random();

  //  Add this.
  void getNext() {
    current = WordPair.random();
    notifyListeners();
  }
}

// ...

Yeni getNext() yöntemi, current değerini yeni bir rastgele WordPair değerine yeniden atar. Ayrıca notifyListeners()(MyAppState'yi izleyen herkesin bilgilendirilmesini sağlayan bir ChangeNotifier) yöntemi) çağrılır.

Geriye kalan tek şey, düğmenin geri çağırma işlevinden getNext yöntemini çağırmaktır.

lib/main.dart

// ...

    ElevatedButton(
      onPressed: () {
        appState.getNext();  // ← This instead of print().
      },
      child: Text('Next'),
    ),

// ...

Uygulamayı kaydedip hemen deneyin. Sonraki düğmesine her bastığınızda yeni bir rastgele kelime çifti oluşturulur.

Bir sonraki bölümde, kullanıcı arayüzünü daha güzel hale getireceksiniz.

5. Uygulamayı daha güzel hale getirin

Uygulama şu anda bu şekilde görünüyor.

3dd8a9d8653bdc56.png

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:

2bbee054d81a3127.png

Widget'ı ayıklama

Mevcut kelime çiftini göstermekten sorumlu satır artık şu şekilde görünür: Text(appState.current.asLowerCase). Daha karmaşık bir şeye dönüştürmek için bu satırı ayrı bir widget'a ayırabilirsiniz. Kullanıcı arayüzünüzün mantıksal olarak ayrı bölümleri için ayrı widget'lar kullanmak, Flutter'da karmaşıklığı yönetmenin önemli bir yoludur.

Flutter, widget'ları ayıklamak için bir yeniden düzenleme yardımcısı sağlar ancak bunu kullanmadan önce, ayıklanan satırın yalnızca ihtiyaç duyduğu öğelere eriştiğinden emin olun. Şu anda satır appState değerine erişiyor ancak aslında yalnızca mevcut kelime çiftinin ne olduğunu bilmesi gerekiyor.

Bu nedenle, MyHomePage widget'ını aşağıdaki gibi yeniden yazın:

lib/main.dart

// ...

class MyHomePage extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    var appState = context.watch<MyAppState>();  
    var pair = appState.current;                 //  Add this.

    return Scaffold(
      body: Column(
        children: [
          Text('A random AWESOME idea:'),
          Text(pair.asLowerCase),                //  Change to this.
          ElevatedButton(
            onPressed: () {
              appState.getNext();
            },
            child: Text('Next'),
          ),
        ],
      ),
    );
  }
}

// ...

Güzel. Text widget'ı artık appState'un tamamını ifade etmiyor.

Şimdi Yeniden Düzenle menüsünü açın. VS Code'de bunu iki şekilde yapabilirsiniz:

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

  1. İmlecinizi yeniden yapılandırmak istediğiniz kod parçasına (bu örnekte Text) getirin ve Ctrl+. (Win/Linux) veya Cmd+. (Mac) tuşlarına basın.

Yeniden Düzenle menüsünde Widget'ı Ayıkla'yı seçin. BigCard gibi bir ad atayın ve Enter simgesini tıklayın.

Bu işlem, geçerli dosyanın sonuna otomatik olarak yeni bir sınıf (BigCard) oluşturur. Sınıf aşağıdaki gibi görünür:

lib/main.dart

// ...

class BigCard extends StatelessWidget {
  const BigCard({
    super.key,
    required this.pair,
  });

  final WordPair pair;

  @override
  Widget build(BuildContext context) {
    return Text(pair.asLowerCase);
  }
}

// ...

Bu yeniden düzenleme sırasında uygulamanın çalışmaya devam ettiğini fark edin.

Kart ekleme

Şimdi bu yeni widget'ı, bölümün başında hayal ettiğimiz cesur kullanıcı arayüzü parçasına dönüştürme zamanı.

BigCard sınıfını ve içindeki build() yöntemini bulun. Önceki gibi, Text widget'ında Yeniden Düzenle menüsünü açın. Ancak bu sefer widget'ı ayıklayamazsınız.

Bunun yerine Dolguyla Sarmal'ı seçin. Bu işlem, Text widget'ının etrafında Padding adlı yeni bir üst widget oluşturur. Kaydettikten sonra, rastgele kelimenin daha fazla boşluk içerdiğini görürsünüz.

Dolguyu 8.0 olan varsayılan değerden artırın. Örneğin, daha fazla dolgu için 20 gibi bir karakter kullanın.

Ardından bir üst seviyeye gidin. İmlecinizi Padding widget'ının üzerine getirin, Yeniden Düzenle menüsünü açın ve Widget ile sarma...'yı seçin.

Bu sayede üst widget'ı belirtebilirsiniz. "Kart" yazıp Enter tuşuna basın.

Bu işlem, Padding widget'ını ve dolayısıyla Text'u bir Card widget'ı ile sarar.

6031adbc0a11e16b.png

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 ve primary, uygulamanın en belirgin, tanımlayıcı rengidir.

Kart, uygulamanın birincil rengiyle boyanır:

a136f7682c204ea1.png

MyApp'e gidip ColorScheme için ana renk rengini değiştirerek bu rengi ve uygulamanın tümünün renk şemasını değiştirebilirsiniz.

Rengin nasıl sorunsuz bir şekilde animasyonlu olarak değiştiğini fark edin. Buna örtülü animasyon denir. Birçok Flutter widget'ı, kullanıcı arayüzünün durumlar arasında "atlamaması" için değerler arasında sorunsuz bir şekilde ara değer oluşturur.

Kartın altındaki yükseltilmiş düğmenin rengi de değişir. Değerleri sabit kodlamak yerine uygulama genelinde bir Theme kullanmanın gücü budur.

TextTheme

Kartta hâlâ bir sorun var: Metin çok küçük ve rengi okunamıyor. Bu sorunu düzeltmek için BigCard'ın build() yönteminde aşağıdaki değişiklikleri yapın.

lib/main.dart

// ...

  @override
  Widget build(BuildContext context) {
    final theme = Theme.of(context);
    //  Add this.
    final style = theme.textTheme.displayMedium!.copyWith(
      color: theme.colorScheme.onPrimary,
    );

    return Card(
      color: theme.colorScheme.primary,
      child: Padding(
        padding: const EdgeInsets.all(20),
        //  Change this line.
        child: Text(pair.asLowerCase, style: style),
      ),
    );
  }

// ...

Bu değişikliğin nedeni:

  • theme.textTheme,'ü kullanarak uygulamanın yazı tipi temasına erişebilirsiniz. Bu sınıfta bodyMedium (orta boyutlu standart metinler için), caption (resim altyazılar için) veya headlineLarge (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 olarak null 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 üzerinde copyWith() ç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:

2405e9342d28c193.png

İ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 imlecinizi copyWith() parantezlerinin içine yerleştirin ve Ctrl+Shift+Space (Win/Linux) veya Cmd+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 renklerin onPrimary eşdeğerlerine sahiptir.

Erişilebilirliği iyileştirme

Flutter, uygulamaları varsayılan olarak erişilebilir hale getirir. Örneğin, her Flutter uygulaması, uygulamadaki tüm metinleri ve etkileşimli öğeleri TalkBack ve VoiceOver gibi ekran okuyuculara doğru şekilde gösterir.

d1fad7944fb890ea.png

Ancak bazen biraz çalışma gerekir. Bu uygulamada ekran okuyucu, oluşturulan bazı kelime çiftlerini telaffuz ederken sorun yaşayabilir. İnsanlar cheaphead (ucuza satan) kelimesindeki iki kelimeyi tanımlamakta sorun yaşamasa da ekran okuyucular kelimenin ortasındaki ph harflerini f olarak telaffuz edebilir.

Basit bir çözüm, pair.asLowerCase değerini "${pair.first} ${pair.second}" ile değiştirmektir. İkincisi, pair içindeki iki kelimeden bir dize ("cheap head" gibi) oluşturmak için dize ekleme işlevini kullanır. Bir birleşik kelime yerine iki ayrı kelime kullanmak, ekran okuyucuların bunları uygun şekilde tanımlamasını sağlar ve görme engelli kullanıcılara daha iyi bir deneyim sunar.

Ancak pair.asLowerCase'ün görsel sadeliğini korumak isteyebilirsiniz. Metin widget'ının görsel içeriğini ekran okuyucular için daha uygun olan semantik bir içerikle geçersiz kılmak üzere Text'nin semanticsLabel mülkünü kullanın:

lib/main.dart

// ...

  @override
  Widget build(BuildContext context) {
    final theme = Theme.of(context);
    final style = theme.textTheme.displayMedium!.copyWith(
      color: theme.colorScheme.onPrimary,
    );

    return Card(
      color: theme.colorScheme.primary,
      child: Padding(
        padding: const EdgeInsets.all(20),

        //  Make the following change.
        child: Text(
          pair.asLowerCase,
          style: style,
          semanticsLabel: "${pair.first} ${pair.second}",
        ),
      ),
    );
  }

// ...

Artık ekran okuyucular, oluşturulan her kelime çiftini doğru şekilde telaffuz ediyor ancak kullanıcı arayüzü aynı kalıyor. Cihazınızda ekran okuyucu kullanarak bu özelliği deneyebilirsiniz.

Kullanıcı arayüzünü ortala

Artık rastgele kelime çiftini yeterince görsel bir şekilde sunduğunuza göre, uygulamanın penceresinin/ekranının ortasına yerleştirme zamanı geldi.

Öncelikle, BigCard'ün bir Column parçası olduğunu unutmayın. Varsayılan olarak sütunlar, alt öğelerini üste toplar ancak bunu kolayca geçersiz kılabiliriz. MyHomePage'nin build() yöntemine gidin ve aşağıdaki değişikliği yapın:

lib/main.dart

// ...

class MyHomePage extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    var appState = context.watch<MyAppState>();
    var pair = appState.current;

    return Scaffold(
      body: Column(
        mainAxisAlignment: MainAxisAlignment.center,  //  Add this.
        children: [
          Text('A random AWESOME idea:'),
          BigCard(pair: pair),
          ElevatedButton(
            onPressed: () {
              appState.getNext();
            },
            child: Text('Next'),
          ),
        ],
      ),
    );
  }
}

// ...

Bu işlem, çocukları Column içinde ana (dikey) eksen boyunca merkeze yerleştirir.

b555d4c7f5000edf.png

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:

455688d93c30d154.png

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

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

3d53d2b071e2f372.png

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.

e6b01a8c90df8ffa.png

İş 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ına favorites adlı yeni bir mülk eklediniz. Bu mülk, boş bir listeyle başlatılır: [].
  • Ayrıca, listenin yalnızca kelime çiftleri içerebileceğini belirttiniz: <WordPair>[], genel terimler kullanılarak. Bu, uygulamanızın daha sağlam olmasına yardımcı olur. Dart, WordPair dışında bir şey eklemeye çalıştığınızda uygulamanızı çalıştırmayı bile reddeder. Bu sayede, favorites listesini kullanırken içinde hiçbir zaman istenmeyen nesnelerin (null gibi) gizlenmeyeceğinden emin olabilirsiniz.
  • Ayrıca, mevcut kelime çiftini favoriler listesinden kaldıran (listedeki bir kelime çiftiyse) veya ekleyen (listedeki bir kelime çifti değilse) yeni bir yöntem (toggleFavorite()) eklediniz. Her iki durumda da kod daha sonra notifyListeners(); işlevini çağırır.

Düğmeyi ekleme

"İş mantığı" tamamlandığında, kullanıcı arayüzü üzerinde tekrar çalışma zamanı gelmiştir. "Beğen" düğmesini "Sonraki" düğmesinin soluna yerleştirmek için Row gerekir. Row widget'ı, daha önce gördüğünüz Column widget'ının yatay eşdeğeridir.

Öncelikle mevcut düğmeyi bir Row içine alın. MyHomePage sınıfının build() yöntemine gidin, imlecinizi ElevatedButton üzerine getirin, Ctrl+. veya Cmd+. ile Yeniden Düzenle menüsünü açın ve Satıra Sar'ı seçin.

Kaydettiğinizde Row'ün Column'e benzer şekilde davrandığını görürsünüz. Varsayılan olarak, alt öğelerini sola toplar. (Column, alt öğelerini üstte topladı.) Bu sorunu düzeltmek için öncekiyle aynı yaklaşımı mainAxisAlignment ile kullanabilirsiniz. Ancak eğitici (öğretici) amaçlarla mainAxisSize simgesini kullanın. Bu, Row'e mevcut yatay alanın tamamını kullanmamasını söyler.

Aşağıdaki değişikliği yapın:

lib/main.dart

// ...

class MyHomePage extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    var appState = context.watch<MyAppState>();
    var pair = appState.current;

    return Scaffold(
      body: Center(
        child: Column(
          mainAxisAlignment: MainAxisAlignment.center,
          children: [
            BigCard(pair: pair),
            SizedBox(height: 10),
            Row(
              mainAxisSize: MainAxisSize.min,   //  Add this.
              children: [
                ElevatedButton(
                  onPressed: () {
                    appState.getNext();
                  },
                  child: Text('Next'),
                ),
              ],
            ),
          ],
        ),
      ),
    );
  }
}

// ...

Kullanıcı arayüzü önceki haline geri döner.

3d53d2b071e2f372.png

Ardından Beğen düğmesini ekleyin ve toggleFavorite() öğesine bağlayın. Kendinize bir meydan okuma olarak önce aşağıdaki kod bloğuna bakmadan bunu kendiniz yapmayı deneyin.

e6b01a8c90df8ffa.png

Aşağıdaki şekilde tam olarak aynı şekilde yapmasanız da olur. Gerçekten zorlu bir deneyim yaşamak istemiyorsanız kalp simgesinden endişelenmeyin.

Başarısız olmanız da sorun değil. Sonuçta Flutter'ı kullanmaya ilk başladığınız saat bu.

252f7c4a212c94d2.png

MyHomePage öğesine ikinci düğmeyi eklemenin bir yolu aşağıda verilmiştir. Bu kez, simge içeren bir düğme oluşturmak için ElevatedButton.icon() kurucusunu kullanın. build yönteminin üst kısmında, mevcut kelime çiftinin favoriler arasında olup olmadığına bağlı olarak uygun simgeyi seçin. Ayrıca, iki düğmeyi biraz ayırmak için tekrar SizedBox kullanıldığını unutmayın.

lib/main.dart

// ...

class MyHomePage extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    var appState = context.watch<MyAppState>();
    var pair = appState.current;

    //  Add this.
    IconData icon;
    if (appState.favorites.contains(pair)) {
      icon = Icons.favorite;
    } else {
      icon = Icons.favorite_border;
    }

    return Scaffold(
      body: Center(
        child: Column(
          mainAxisAlignment: MainAxisAlignment.center,
          children: [
            BigCard(pair: pair),
            SizedBox(height: 10),
            Row(
              mainAxisSize: MainAxisSize.min,
              children: [

                //  And this.
                ElevatedButton.icon(
                  onPressed: () {
                    appState.toggleFavorite();
                  },
                  icon: Icon(icon),
                  label: Text('Like'),
                ),
                SizedBox(width: 10),

                ElevatedButton(
                  onPressed: () {
                    appState.getNext();
                  },
                  child: Text('Next'),
                ),
              ],
            ),
          ],
        ),
      ),
    );
  }
}

// ...

Uygulama aşağıdaki gibi görünmelidir:

Maalesef kullanıcı favorileri göremez. Uygulamamıza tamamen ayrı bir ekran ekleme zamanı geldi. Bir sonraki bölümde görüşmek üzere.

7. Gezinme çubuğu ekleme

Çoğu uygulama, her şeyi tek bir ekrana sığdıramaz. Bu uygulama muhtemelen yapabilir ancak eğitim amaçlı olarak kullanıcının favorileri için ayrı bir ekran oluşturacaksınız. İki ekran arasında geçiş yapmak için ilk StatefulWidget'inizi uygulayacaksınız.

f62c54f5401a187.png

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.

388bc25fe198c54a.png

Değişiklikleri inceleyin.

  • Öncelikle, MyHomePage içeriğinin tamamının yeni bir widget'a (GeneratorPage) ayıklandığını görebilirsiniz. Eski MyHomePage widget'ının ayıklanmayan tek parçası Scaffold.
  • Yeni MyHomePage, iki çocuk içeren bir Row içeriyor. İlk widget SafeArea, ikinci widget ise Expanded 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çin NavigationRail etrafında sarılır.
  • NavigationRail'deki extended: false satırını true olarak değiştirebilirsiniz. Bu sayede simgelerin yanında etiketleri görebilirsiniz. Uygulamada yeterli yatay alan olduğunda bunu otomatik olarak nasıl yapacağınızı sonraki bir adımda öğreneceksiniz.
  • Gezinme çubuğunda, ilgili simge ve etiketleriyle birlikte iki hedef (Ana Sayfa ve Favoriler) bulunur. Ayrıca mevcut selectedIndex değerini de tanımlar. Seçilen sıfır dizini ilk hedefi, seçilen bir dizini ikinci hedefi seçer ve bu şekilde devam eder. Şu anda sıfır olarak sabitlenmiştir.
  • Gezinme çubuğu, kullanıcı onDestinationSelected ile hedeflerden birini seçtiğinde ne olacağını da tanımlar. Şu anda uygulama, istenen dizin değerini yalnızca print() ile döndürüyor.
  • 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ın kalan alanı mümkün olduğunca kapladığı (bu örnekte Expanded) 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ız SafeArea widget'ını başka bir Expanded widget'ın içine yerleştirmeyi deneyin. Elde edilen düzen şöyle görünür:

6bbda6c1835a1ae.png

  • Gezinme çubuğu için yalnızca sol tarafta küçük bir alan gerekirken iki Expanded widget, mevcut tüm yatay alanı aralarında paylaştırmış.
  • Expanded widget'ının içinde renkli bir Container, kapsayıcı içinde ise GeneratorPage vardır.

Durum bilgisiz ve durum bilgisine sahip widget'lar

Şimdiye kadar MyAppState, tüm eyalet ihtiyaçlarınızı karşılıyordu. Bu nedenle, şimdiye kadar yazdığınız tüm widget'lar devre dışı. Kendilerine ait değişken bir durum içermezler. Widget'ların hiçbiri kendisini değiştiremez. MyAppState üzerinden geçmeleri gerekir.

Bu durum yakında değişecek.

Gezinme çubuğu selectedIndex değerini tutmanız gerekir. Ayrıca bu değeri onDestinationSelected geri çağırma işlevinden de değiştirebilmek istiyorsunuz.

MyAppState'ın başka bir özelliği olarak selectedIndex'yi ekleyebilirsiniz. Bu yöntem kesinlikle işe yarar. Ancak her widget kendi değerlerini burada depolasa uygulama durumunun çok hızlı bir şekilde aşırı boyutlara ulaşacağını tahmin edebilirsiniz.

e52d9c0937cc0823.jpeg

Bazı durumlar yalnızca tek bir widget ile alakalı olduğundan bu widget'ta kalmalıdır.

State içeren bir widget türü olan StatefulWidget değerini girin. Öncelikle MyHomePage öğesini durum bilgisine sahip bir widget'a dönüştürün.

İmlecinizi MyHomePage'ün ilk satırına (class MyHomePage... ile başlayan) getirin ve Ctrl+. veya Cmd+. tuşlarını kullanarak Yeniden Düzenle menüsünü açın. Ardından StatefulWidget'e dönüştür'ü seçin.

IDE, sizin için yeni bir sınıf (_MyHomePageState) oluşturur. Bu sınıf State'ü genişletir ve bu nedenle kendi değerlerini yönetebilir. (Kendini değiştirebilir.) Ayrıca, eski ve durum bilgisi olmayan widget'taki build yönteminin, widget'ta kalmak yerine _MyHomePageState içine taşındığını da unutmayın. Bu yöntem olduğu gibi taşındı. build yönteminin içinde hiçbir şey değişmedi. Artık başka bir yerde yaşıyor.

setState

Yeni durum bilgisine sahip widget'ın yalnızca bir değişkeni (selectedIndex) izlemesi gerekir. _MyHomePageState dosyasında aşağıdaki 3 değişikliği yapın:

lib/main.dart

// ...

class _MyHomePageState extends State<MyHomePage> {

  var selectedIndex = 0;     //  Add this property.

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      body: Row(
        children: [
          SafeArea(
            child: NavigationRail(
              extended: false,
              destinations: [
                NavigationRailDestination(
                  icon: Icon(Icons.home),
                  label: Text('Home'),
                ),
                NavigationRailDestination(
                  icon: Icon(Icons.favorite),
                  label: Text('Favorites'),
                ),
              ],
              selectedIndex: selectedIndex,    //  Change to this.
              onDestinationSelected: (value) {

                //  Replace print with this.
                setState(() {
                  selectedIndex = value;
                });

              },
            ),
          ),
          Expanded(
            child: Container(
              color: Theme.of(context).colorScheme.primaryContainer,
              child: GeneratorPage(),
            ),
          ),
        ],
      ),
    );
  }
}

// ...

Değişiklikleri inceleyin:

  1. selectedIndex adlı yeni bir değişken tanıtır ve bu değişkeni 0 olarak başlatırsınız.
  2. Bu yeni değişkeni, NavigationRail tanımında şimdiye kadar bulunan sabit kodlu 0 yerine kullanırsınız.
  3. onDestinationSelected geri çağırma işlevi çağrıldığında, yeni değeri yalnızca konsola yazdırmak yerine bir setState() çağrısı içinde selectedIndex'a atarsınız. Bu çağrı, daha önce kullanılan notifyListeners() yöntemine benzer. Kullanıcı arayüzünün güncellendiğinden emin olur.

Gezinme çubuğu artık kullanıcı etkileşimine yanıt veriyor. Ancak sağdaki genişletilmiş alan aynı kalır. Bunun nedeni, kodun hangi ekranın gösterileceğini belirlemek için selectedIndex kullanmamasıdır.

selectedIndex değerini kullanın

Aşağıdaki kodu _MyHomePageState öğesinin build yönteminin en üstüne, return Scaffold öğesinden hemen önce yerleştirin:

lib/main.dart

// ...

Widget page;
switch (selectedIndex) {
  case 0:
    page = GeneratorPage();
    break;
  case 1:
    page = Placeholder();
    break;
  default:
    throw UnimplementedError('no widget for $selectedIndex');
}

// ...

Aşağıdaki kod parçasını inceleyin:

  1. Kodda, Widget türüne sahip yeni bir değişken (page) tanımlanır.
  2. Ardından, bir switch ifadesi selectedIndex'taki mevcut değere göre page için bir ekran atar.
  3. Henüz FavoritesPage olmadığından, Placeholder kullanın. Bu kullanışlı widget, yerleştirdiğiniz her yerde çapraz bir dikdörtgen çizerek kullanıcı arayüzünün bu bölümünü tamamlanmamış olarak işaretler.

5685cf886047f6ec.png

  1. Hızlı hata ilkesiyle birlikte kullanılan switch ifadesi, selectedIndex 0 veya 1 değilse hata da atar. Bu, ileride oluşabilecek hataları önlemeye yardımcı olur. Gezinme çubuğuna yeni bir hedef eklerseniz ve bu kodu güncellemeyi unutursanız program geliştirme aşamasında kilitlenir (böylece, neden çalışmadığını tahmin etmenize veya üretime hatalı bir kod yayınlamanıza izin verilmez).

page, sağ tarafta göstermek istediğiniz widget'ı içerdiğine göre, yapılması gereken diğer değişikliği tahmin edebilirsiniz.

Kalan tek değişiklikten sonra _MyHomePageState şöyle görünür:

lib/main.dart

// ...

class _MyHomePageState extends State<MyHomePage> {
  var selectedIndex = 0;

  @override
  Widget build(BuildContext context) {
    Widget page;
    switch (selectedIndex) {
      case 0:
        page = GeneratorPage();
        break;
      case 1:
        page = Placeholder();
        break;
      default:
        throw UnimplementedError('no widget for $selectedIndex');
    }

    return Scaffold(
      body: Row(
        children: [
          SafeArea(
            child: NavigationRail(
              extended: false,
              destinations: [
                NavigationRailDestination(
                  icon: Icon(Icons.home),
                  label: Text('Home'),
                ),
                NavigationRailDestination(
                  icon: Icon(Icons.favorite),
                  label: Text('Favorites'),
                ),
              ],
              selectedIndex: selectedIndex,
              onDestinationSelected: (value) {
                setState(() {
                  selectedIndex = value;
                });
              },
            ),
          ),
          Expanded(
            child: Container(
              color: Theme.of(context).colorScheme.primaryContainer,
              child: page,  //  Here.
            ),
          ),
        ],
      ),
    );
  }
}


// ...

Uygulama artık GeneratorPage ile yakında Favoriler sayfası olacak yer tutucu arasında geçiş yapıyor.

Yanıt verme

Ardından gezinme çubuğunu duyarlı hale getirin. Yani, etiketler için yeterli alan olduğunda etiketleri otomatik olarak göstermesini (extended: true kullanarak) sağlayın.

a8873894c32e0d0b.png

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:

  1. _MyHomePageState sınıfının build yönteminde, imlecinizi Scaffold üzerine getirin.
  2. Ctrl+. (Windows/Linux) veya Cmd+. (Mac) tuşlarıyla Yeniden Düzenle menüsünü açın.
  3. Geliştirici ile sarma'yı seçin ve Enter tuşuna basın.
  4. Yeni eklenen Builder öğesinin adını LayoutBuilder olarak değiştirin.
  5. 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üyerek MyHomePage'ün kısıtlamalarını küçültüyor
  • vb.

Artık kodunuz, mevcut constraints değerini sorgulayarak etiketin gösterilip gösterilmeyeceğine karar verebilir. _MyHomePageState sınıfının build yönteminde aşağıdaki tek satırlık değişikliği yapın:

lib/main.dart

// ...

class _MyHomePageState extends State<MyHomePage> {
  var selectedIndex = 0;

  @override
  Widget build(BuildContext context) {
    Widget page;
    switch (selectedIndex) {
      case 0:
        page = GeneratorPage();
        break;
      case 1:
        page = Placeholder();
        break;
      default:
        throw UnimplementedError('no widget for $selectedIndex');
    }

    return LayoutBuilder(builder: (context, constraints) {
      return Scaffold(
        body: Row(
          children: [
            SafeArea(
              child: NavigationRail(
                extended: constraints.maxWidth >= 600,  //  Here.
                destinations: [
                  NavigationRailDestination(
                    icon: Icon(Icons.home),
                    label: Text('Home'),
                  ),
                  NavigationRailDestination(
                    icon: Icon(Icons.favorite),
                    label: Text('Favorites'),
                  ),
                ],
                selectedIndex: selectedIndex,
                onDestinationSelected: (value) {
                  setState(() {
                    selectedIndex = value;
                  });
                },
              ),
            ),
            Expanded(
              child: Container(
                color: Theme.of(context).colorScheme.primaryContainer,
                child: page,
              ),
            ),
          ],
        ),
      );
    });
  }
}


// ...

Artık uygulamanız ekran boyutu, yön ve platform gibi çevresel faktörlere yanıt veriyor. Başka bir deyişle, duyarlı.

Geriye kalan tek iş, bu Placeholder öğesini gerçek bir Favoriler ekranıyla değiştirmek. Bu konu bir sonraki bölümde ele alınmaktadır.

8. Yeni sayfa ekle

Favoriler sayfası yerine kullandığımız Placeholder widget'ını hatırlıyor musunuz?

Bu sorunu düzeltme zamanı.

Maceraperverseniz bu adımı kendiniz yapmayı deneyin. Amacınız, favorites listesini yeni bir durum bilgisi olmayan widget'ta (FavoritesPage) göstermek ve ardından Placeholder yerine bu widget'ı göstermektir.

Aşağıda birkaç ipucu verilmiştir:

  • Kaydırmalı bir Column kullanmak istediğinizde ListView widget'ını kullanın.
  • context.watch<MyAppState>() kullanarak herhangi bir widget'tan MyAppState örneğine erişmeyi unutmayın.
  • Yeni bir widget da denemek istiyorsanız ListTile'te title (genellikle metin için), leading (simgeler veya avatarlar için) ve onTap (etkileşimler için) gibi özellikler bulunur. Ancak, bildiğiniz widget'larla da benzer efektler elde edebilirsiniz.
  • Dart, koleksiyon değişmez değerleri içinde for döngülerinin kullanılmasına olanak tanır. Örneğin, messages bir dize listesi içeriyorsa aşağıdaki gibi bir kodunuz olabilir:

f0444bba08f205aa.png

Öte yandan, işlevsel programlamaya daha aşina iseniz Dart, messages.map((m) => Text(m)).toList() gibi kodlar da yazmanıza olanak tanır. Elbette dilediğiniz zaman widget listesi oluşturabilir ve build yönteminin içine zorunlu olarak ekleyebilirsiniz.

Favoriler sayfasını kendiniz eklemenin avantajı, kendi kararlarınızı vererek daha fazla bilgi edinmenizdir. Dezavantajı ise henüz kendi başınıza çözemediğiniz sorunlarla karşılaşmanızdır. Başarısız olmanın normal olduğunu ve öğrenmenin en önemli unsurlarından biri olduğunu unutmayın. Flutter geliştirmeyi ilk saatinizde mükemmel şekilde öğrenmenizi kimse beklemez ve siz de bunu beklememelisiniz.

252f7c4a212c94d2.png

Aşağıda, favoriler sayfasını uygulamanın bir yolu verilmiştir. Uygulama şekli, kodla oynamanıza (umarım) ilham verecektir. Böylece kullanıcı arayüzünü iyileştirip kendi haline getirebilirsiniz.

Yeni FavoritesPage sınıfı:

lib/main.dart

// ...

class FavoritesPage extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    var appState = context.watch<MyAppState>();

    if (appState.favorites.isEmpty) {
      return Center(
        child: Text('No favorites yet.'),
      );
    }

    return ListView(
      children: [
        Padding(
          padding: const EdgeInsets.all(20),
          child: Text('You have '
              '${appState.favorites.length} favorites:'),
        ),
        for (var pair in appState.favorites)
          ListTile(
            leading: Icon(Icons.favorite),
            title: Text(pair.asLowerCase),
          ),
      ],
    );
  }
}

Widget'ın işlevi:

  • Uygulamanın mevcut durumunu alır.
  • Favoriler listesi boşsa ortada şu mesaj gösterilir: Henüz favori yok.
  • Aksi takdirde, (kaydırılabilir) bir liste gösterilir.
  • Liste bir özetle başlar (örneğin, 5 favoriniz var*.*).
  • Ardından kod, tüm favorileri iteratif olarak tarar ve her biri için bir ListTile widget oluşturur.

Şimdi tek yapmanız gereken Placeholder widget'ını FavoritesPage ile değiştirmek. Ve işte bu kadar.

Bu uygulamanın nihai kodunu GitHub'daki codelab deposundan edinebilirsiniz.

9. Sonraki adımlar

Tebrikler!

Harikasın. Column ve iki Text widget'ı içeren işlevsel olmayan bir iskeleti, duyarlı ve keyifli bir küçük uygulamaya dönüştürdünüz.

d6e3d5f736411f13.png

İşlediğimiz konular

  • Flutter'ın işleyiş şekliyle ilgili temel bilgiler
  • Flutter'da düzenler oluşturma
  • Kullanıcı etkileşimlerini (ör. düğmeye basma) uygulama davranışıyla ilişkilendirme
  • Flutter kodunuzu düzenli tutma
  • Uygulamanızı duyarlı hale getirme
  • Uygulamanızın görünümü ve tarzını tutarlı hale getirme

Sonra ne olur?

  • Bu laboratuvar sırasında yazdığınız uygulamayla daha fazla deneme yapın.
  • Animasyonlu listeler, degradeler, geçiş efektleri ve daha fazlasını nasıl ekleyeceğinizi öğrenmek için aynı uygulamanın bu gelişmiş sürümünün koduna bakın.