Dart modellerini ve kayıtlarını ayrıntılı şekilde inceleyin

1. Giriş

Dart 3, yeni bir dil bilgisi kategorisi olan dile kalıplar ekler. Dart kodu yazmanın bu yeni yolunun dışında, aralarında Android'in de bulunduğu bir dizi başka dil geliştirmesi de yapılmıştır.

  • Farklı türlerdeki verileri gruplandırmak için kayıtları,
  • Erişimi kontrol etmek için sınıf değiştiricileri ve
  • yeni switch ifadeleri ve if-case ifadeleri oluşturun.

Bu özellikler Dart kodu yazarken daha fazla seçeneğe sahip olmanızı sağlar. Bu codelab'de, bu kodları kullanarak kodunuzu daha kompakt, sade ve esnek hale nasıl getireceğinizi öğreneceksiniz.

Bu codelab'de Flutter ve Dart hakkında bilginiz olduğu varsayılır. Biraz pasif hissediyorsanız aşağıdaki kaynaklardan yararlanarak temel bilgilerinizi tazeleyebilirsiniz:

Neler oluşturacaksınız?

Bu codelab'de, Flutter'da JSON dokümanı görüntüleyen bir uygulama oluşturulur. Uygulama, harici bir kaynaktan gelen JSON'u simüle eder. JSON dosyasında değişiklik tarihi, başlık, üstbilgi ve paragraflar gibi doküman verileri bulunur. Verileri kayıtlara düzgün bir şekilde paketlemek için kod yazarsınız. Böylece, Flutter widget'larınızın ihtiyacı olan her yerde aktarılıp paketten çıkarılabilir.

Ardından, değer bu kalıpla eşleştiğinde uygun widget'ı oluşturmak için kalıpları kullanırsınız. Ayrıca verileri yerel değişkenlere dönüştürmek için kalıpları nasıl kullanacağınızı da göreceksiniz.

Bu codelab'de oluşturduğunuz son uygulama; başlığı, son değiştirilme tarihi, başlıkları ve paragrafları olan bir doküman.

Neler öğreneceksiniz?

  • Farklı türlerde birden fazla değeri depolayan bir kayıt oluşturma.
  • Kayıt kullanarak işlevden birden çok değer döndürme.
  • Kayıtlardan ve diğer nesnelerden verileri eşleştirmek, doğrulamak ve yapılandırmak için kalıpları kullanma
  • Kalıp eşleşen değerleri yeni veya mevcut değişkenlere bağlama.
  • Yeni "geçiş ifadesi" özelliklerini, değiştirme ifadelerini ve "if-case" ifadelerini kullanma.
  • Her destek kaydının bir "switch" veya "geçiş" ifadesinde işlendiğinden emin olmak için kapsamlılık kontrolünden yararlanma

2. Ortamınızı ayarlama

  1. Flutter SDK'sını yükleyin.
  2. Visual Studio Code (VS Code) gibi bir düzenleyici kurun.
  3. En az bir hedef platform (iOS, Android, Masaüstü veya web tarayıcısı) için Platform kurulumu adımlarını uygulayın.

3. Projeyi oluşturma

Kalıplar, kayıtlar ve diğer yeni özelliklerle ilgili ayrıntılara girmeden önce, tüm kodunuzu yazacağınız basit bir Flutter projesi oluşturun.

Flutter projesi oluşturma

  1. patterns_codelab adında yeni bir proje oluşturmak için flutter create komutunu kullanın. --empty işareti, yine de kaldırmanız gerekeceği lib/main.dart dosyasında standart sayaç uygulamasının oluşturulmasını engeller.
flutter create --empty patterns_codelab
  1. Ardından, VS Code'u kullanarak patterns_codelab dizinini açın.
code patterns_codelab

"flutter create" ile oluşturulan projeyi gösteren VS Code'un ekran görüntüsü komutuna ekleyin.

Minimum SDK sürümünü belirleme

  • Projenizin SDK sürümü kısıtlamasını Dart 3 veya sonraki bir sürüme bağlı olacak şekilde ayarlayın.

pubspec.yaml

environment:
  sdk: ^3.0.0

4. Projeyi oluşturma

Bu adımda, iki Dart dosyası oluşturur veya güncellersiniz:

  • Uygulama için widget'ları içeren main.dart dosyası ve
  • Uygulama verilerini sağlayan data.dart dosyası.

Sonraki adımlarda bu dosyaların ikisini de değiştirmeye devam edeceksiniz.

Uygulamaya ilişkin verileri tanımlama

  • lib/data.dart adlı yeni bir dosya oluşturun ve bu dosyaya şu kodu ekleyin:

lib/data.dart

import 'dart:convert';

class Document {
  final Map<String, Object?> _json;
  Document() : _json = jsonDecode(documentJson);
}

const documentJson = '''
{
  "metadata": {
    "title": "My Document",
    "modified": "2023-05-10"
  },
  "blocks": [
    {
      "type": "h1",
      "text": "Chapter 1"
    },
    {
      "type": "p",
      "text": "Lorem ipsum dolor sit amet, consectetur adipiscing elit."
    },
    {
      "type": "checkbox",
      "checked": false,
      "text": "Learn Dart 3"
    }
  ]
}
''';

G/Ç akışı veya HTTP isteği gibi harici bir kaynaktan veri alan bir program düşünün. Bu codelab'de, gelen JSON verileriyle alay ederek documentJson değişkenindeki çok satırlı bir dizeyle bu daha gerçekçi kullanım alanını basitleştireceksiniz.

JSON verileri Document sınıfında tanımlanır. Bu codelab'in ilerleyen bölümlerinde, ayrıştırılmış JSON'dan veri döndüren işlevler ekleyebilirsiniz. Bu sınıf, kurucusunda _json alanını tanımlar ve başlatır.

Uygulamayı çalıştırın

flutter create komutu, lib/main.dart dosyasını varsayılan Flutter dosya yapısının bir parçası olarak oluşturur.

  1. Uygulamanın başlangıç noktası oluşturmak için main.dart içeriğini aşağıdaki kodla değiştirin:

lib/main.dart

import 'package:flutter/material.dart';

import 'data.dart';

void main() {
  runApp(const DocumentApp());
}

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

  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      theme: ThemeData(useMaterial3: true),
      home: DocumentScreen(
        document: Document(),
      ),
    );
  }
}

class DocumentScreen extends StatelessWidget {
  final Document document;

  const DocumentScreen({
    required this.document,
    super.key,
  });

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(
        title: const Text('Title goes here'),
      ),
      body: const Column(
        children: [
          Center(
            child: Text('Body goes here'),
          ),
        ],
      ),
    );
  }
}

Uygulamaya aşağıdaki iki widget'ı eklediniz:

  • DocumentApp, kullanıcı arayüzü teması eklemek için Materyal Tasarım'ın en son sürümünü oluşturuyor.
  • DocumentScreen, Scaffold widget'ını kullanarak sayfanın görsel düzenini sağlar.
  1. Her şeyin düzgün bir şekilde çalıştığından emin olmak için Çalıştır ve Hata Ayıkla'yı tıklayarak uygulamayı ana makinenizde çalıştırın:

&quot;Çalıştırma ve hata ayıklama&quot; resmi &quot;Çalıştır ve hata ayıklama&quot; bölümünde bulunan düğme bölümüne gidin.

  1. Flutter varsayılan olarak uygun hedef platformu seçer. Hedef platformu değiştirmek için Durum Çubuğu'nda geçerli platformu seçin:

VS Code&#39;daki hedef platform seçicinin ekran görüntüsü.

DocumentScreen widget'ında tanımlı title ve body öğelerinin bulunduğu boş bir çerçeve göreceksiniz:

Bu adımda oluşturulan uygulamanın ekran görüntüsü.

5. Kayıt oluşturma ve geri verme

Bu adımda, bir işlev çağrısından birden fazla değer döndürmek için kayıtları kullanırsınız. Ardından, değerlere erişmek ve bunları kullanıcı arayüzünde yansıtmak için DocumentScreen widget'ında bu işlevi çağırırsınız.

Kayıt oluşturma ve döndürme

  • data.dart ürününde, Doküman sınıfına metadata adlı ve kayıt döndüren yeni bir getter yöntemi ekleyin:

lib/data.dart

import 'dart:convert';

class Document {
  final Map<String, Object?> _json;
  Document() : _json = jsonDecode(documentJson);

  (String, {DateTime modified}) get metadata {           // Add from here...
    const title = 'My Document';
    final now = DateTime.now();

    return (title, modified: now);
  }                                                      // to here.
}

Bu işlevin dönüş türü, biri String ve diğeri DateTime türünde olmak üzere iki alan içeren bir kayıttır.

Döndürme ifadesi, iki değeri parantez içine alarak ((title, modified: now)) yeni bir kayıt oluşturur.

İlk alan konumsal ve adsız, ikinci alan ise modified olarak adlandırılmıştır.

Erişim kaydı alanları

  1. Kaydınızı almak ve değerlerine erişmek için DocumentScreen widget'ında build yönteminde metadata alıcı yöntemini çağırın:

lib/main.dart

class DocumentScreen extends StatelessWidget {
  final Document document;

  const DocumentScreen({
    required this.document,
    super.key,
  });

  @override
  Widget build(BuildContext context) {
    final metadataRecord = document.metadata;              // Add this line.

    return Scaffold(
      appBar: AppBar(
        title: Text(metadataRecord.$1),                    // Modify this line,
      ),
      body: Column(
        children: [
          Center(
            child: Text(
              'Last modified ${metadataRecord.modified}',  // And this one.
            ),
          ),
        ],
      ),
    );
  }
}

metadata alıcı yöntemi, metadataRecord yerel değişkenine atanmış bir kayıt döndürür. Kayıtlar, tek bir işlev çağrısından birden çok değer döndürmenin ve bunları bir değişkene atamanın kolay ve kolay bir yoludur.

Bu kayıtta oluşturulan her bir alana erişmek için kayıtların yerleşik alıcı söz dizimidir.

  • Konumsal bir alan (title gibi adsız bir alan) almak için kayıttaki alıcıyı $<num> kullanın. Bu işlem yalnızca adsız alanları döndürür.
  • modified gibi adlandırılmış alanların konum alıcısı olmadığından metadataRecord.modified gibi adını doğrudan kullanabilirsiniz.

Konumsal bir alan için alıcının adını belirlemek için $1 ile başlayın ve adlandırılmış alanları atlayın. Örneğin:

var record = (named: 'v', 'y', named2: 'x', 'z');
print(record.$1);                               // prints y
print(record.$2);                               // prints z
  1. Uygulamada gösterilen JSON değerlerini görmek için yeniden yükleyin. VS Code Dart eklentisi, bir dosyayı her kaydettiğinizde çalışır durumda yeniden yüklenir.

Uygulamanın başlığını ve değiştirilme tarihini gösteren ekran görüntüsü.

Her alanın aslında kendi türünü koruduğunu görebilirsiniz.

  • Text() yöntemi, ilk bağımsız değişkeni olarak bir Dizeyi alır.
  • modified alanı bir DateTime'dır ve dize interpolasyonu kullanılarak String alanına dönüştürülür.

Farklı veri türlerini döndürmenin diğer tür güvenli yolu, daha ayrıntılı bir sınıf tanımlamaktır.

6. Kalıplarla eşleştirme ve yapılandırma

Kayıtlar farklı veri türlerini etkili bir şekilde toplayabilir ve kolayca aktarabilir. Şimdi de kalıpları kullanarak kodunuzu iyileştirin.

Kalıp, bir veya daha fazla değerin alabileceği yapıyı (ör. şema gibi) temsil eder. Kalıplar, eşleşip eşleşmediklerini belirlemek için gerçek değerlerle karşılaştırılır.

Bazı kalıplar eşleştiklerinde, eşleşen değeri bu değerden çekerek yapılandırılmış hale getirir. Yapılandırma işlemi, bir nesnedeki değerleri yerel değişkenlere atamak için paketten çıkarmanıza veya bu değerler üzerinde daha fazla eşleştirme yapmanıza olanak tanır.

Bir kaydı yerel değişkenlere dönüştürme

  1. metadata yöntemini çağırmak için DocumentScreen öğesinin build yöntemini yeniden düzenleyin ve bu yöntemi dize kalıbı değişkeni bildirimini başlatmak için kullanın:

lib/main.dart

class DocumentScreen extends StatelessWidget {
  final Document document;

  const DocumentScreen({
    required this.document,
    super.key,
  });

  @override
  Widget build(BuildContext context) {
    final (title, modified: modified) = document.metadata;   // Modify

    return Scaffold(
      appBar: AppBar(
        title: Text(title),                                  // Modify
      ),
      body: Column(
        children: [
          Center(
            child: Text(
              'Last modified $modified',                     // Modify
            ),
          ),
        ],
      ),
    );
  }
}

(title, modified: modified) kayıt kalıbı, metadata tarafından döndürülen kaydın alanlarıyla eşleşen iki değişken kalıbı içerir.

  • Sonuç, biri modified adlı iki alan içeren bir kayıt olduğundan ifade alt kalıpla eşleşir.
  • Değişken bildirim kalıbı, eşleştikleri için ifadeyi bozar, değerlerine erişir ve bunları aynı tür ve adlardaki yeni yerel değişkenlere (String title ve DateTime modified) bağlar.

Bir alanın adı ile onu dolduran değişkenin aynı olması için kullanılan bir kısaltma bulunur. DocumentScreen öğesinin build yöntemini aşağıdaki gibi yeniden düzenleyin.

lib/main.dart

class DocumentScreen extends StatelessWidget {
  final Document document;

  const DocumentScreen({
    required this.document,
    super.key,
  });

  @override
  Widget build(BuildContext context) {
    final (title, :modified) = document.metadata;            // Modify

    return Scaffold(
      appBar: AppBar(
        title: Text(title),
      ),
      body: Column(
        children: [
          Center(
            child: Text(
              'Last modified $modified',
            ),
          ),
        ],
      ),
    );
  }
}

:modified değişken kalıbının söz dizimi modified: modified ifadesinin kısaltmasıdır. Farklı bir ada sahip yeni bir yerel değişken istiyorsanız bunun yerine modified: localModified yazabilirsiniz.

  1. Önceki adımla aynı sonucu görmek için sayfayı yeniden yükleyin. Davranış tamamen aynıdır; kodunuzu daha kısa ve öz hale getirdiniz.

7. Verileri ayıklamak için kalıplar kullanma

Belirli bağlamlarda, kalıplar yalnızca eşleşmek ve yıkım sağlamakla kalmaz, aynı zamanda kalıbın eşleşip eşleşmediğine bağlı olarak kodun ne yapacağı hakkında da bir karar verebilir. Bunlara yeniden değerlendirilebilir kalıplar denir.

Son adımda kullandığınız değişken bildirim kalıbı çürütilemez bir kalıptır: Değerin kalıpla eşleşmesi gerekir; aksi takdirde bu bir hatadır ve yapılandırma işlemi gerçekleşmez. Herhangi bir değişken beyanını veya atamayı düşünün; aynı türde olmayan değişkenlere değer atayamazsınız.

Öte yandan, yeniden yapılandırılabilir kalıplar kontrol akışı bağlamlarında kullanılır:

  • Karşılaştırdıkları bazı değerlerin eşleşmeyeceğini beklerler.
  • Değerin eşleşip eşleşmediğine bağlı olarak kontrol akışını etkilemesi amaçlanır.
  • Eşleşmiyorlarsa bir hata ile yürütmeyi kesintiye uğratmazlar, yalnızca sonraki ifadeye geçerler.
  • Yalnızca kullanılabilir olan değişkenleri yapılandırıp bağlayabilirler.

JSON değerlerini kalıplar olmadan okuma

Bu bölümde, kalıpların JSON verileriyle çalışmanıza nasıl yardımcı olabileceğini görmek için verileri kalıp eşleştirme olmadan okuyacaksınız.

  • metadata özelliğinin önceki sürümünü, _json haritasındaki değerleri okuyan bir sürümle değiştirin. Bu metadata sürümünü kopyalayıp Document sınıfına yapıştırın:

lib/data.dart

class Document {
  final Map<String, Object?> _json;
  Document() : _json = jsonDecode(documentJson);

  (String, {DateTime modified}) get metadata {
    if (_json.containsKey('metadata')) {                     // Modify from here...
      final metadataJson = _json['metadata'];
      if (metadataJson is Map) {
        final title = metadataJson['title'] as String;
        final localModified =
            DateTime.parse(metadataJson['modified'] as String);
        return (title, modified: localModified);
      }
    }
    throw const FormatException('Unexpected JSON');          // to here.
  }
}

Bu kod, kalıplar kullanılmadan verilerin doğru şekilde yapılandırıldığını doğrular. Daha sonraki bir adımda, aynı doğrulamayı daha az kod kullanarak gerçekleştirmek için kalıp eşleştirmeyi kullanacaksınız. Başka herhangi bir işlem yapmadan önce üç kontrol gerçekleştirir:

  • JSON, beklediğiniz veri yapısını içerir: if (_json.containsKey('metadata'))
  • Veriler beklediğiniz türe sahip: if (metadataJson is Map)
  • Verilerin boş olmadığı, önceki kontrolde dolaylı olarak onaylanmıştır.

Harita kalıbı kullanarak JSON değerlerini okuma

Yenilenebilir bir kalıpla, JSON'un beklenen yapıya sahip olduğunu bir harita kalıbı kullanarak doğrulayabilirsiniz.

  • Önceki metadata sürümünü şu kodla değiştirin:

lib/data.dart

class Document {
  final Map<String, Object?> _json;
  Document() : _json = jsonDecode(documentJson);

  (String, {DateTime modified}) get metadata {
    if (_json                                                // Modify from here...
        case {
          'metadata': {
            'title': String title,
            'modified': String localModified,
          }
        }) {
      return (title, modified: DateTime.parse(localModified));
    } else {
      throw const FormatException('Unexpected JSON');
    }                                                        // to here.
  }
}

Burada, yeni bir tür "if" deyimi (Dart 3'te kullanıma sunulan) görürsünüz. if-case Destek kaydı gövdesi yalnızca destek kaydı kalıbının _json içindeki verilerle eşleşmesi durumunda yürütülür. Bu eşleşme, gelen JSON'u doğrulamak için metadata ürününün ilk sürümünde yazdığınız kontrollerin aynısını gerçekleştirir. Bu kod aşağıdakileri doğrular:

  • _json bir Harita türüdür.
  • _json, metadata anahtarı içeriyor.
  • _json, boş değil.
  • _json['metadata'] aynı zamanda bir harita türüdür.
  • _json['metadata'], title ve modified anahtarlarını içeriyor.
  • title ve localModified dizedir ve boş değildir.

Değer eşleşmezse model refutes (yürütmeye devam etmeyi reddeder) ve else ifadesine gider. Eşleştirme başarılı olursa kalıp, title ve modified değerlerini haritadan yapılandırır ve yeni yerel değişkenlere bağlar.

Kalıpların tam listesi için özellik spesifikasyonunun Kalıplar bölümündeki tabloya bakın.

8. Uygulamayı daha fazla kalıp için hazırlayın

Şu ana kadar JSON verilerinin metadata kısmını ele almışsınız. Bu adımda, blocks listesindeki verileri işleyip uygulamanıza oluşturmak için iş mantığınızı biraz daha hassaslaştıracaksınız.

{
  "metadata": {
    // ...
  },
  "blocks": [
    {
      "type": "h1",
      "text": "Chapter 1"
    },
    // ...
  ]
}

Verileri depolayan bir sınıf oluşturma

  • data.dart hizmetine, JSON verilerindeki bloklardan birine ilişkin verileri okumak ve depolamak için kullanılan yeni bir sınıf (Block) ekleyin.

lib/data.dart

class Block {
  final String type;
  final String text;
  Block(this.type, this.text);

  factory Block.fromJson(Map<String, dynamic> json) {
    if (json case {'type': final type, 'text': final text}) {
      return Block(type, text);
    } else {
      throw const FormatException('Unexpected JSON format');
    }
  }
}

fromJson() fabrika oluşturucusu, daha önce gördüğünüz bir harita deseniyle aynı "if-case" özelliğini kullanır.

Anahtarlardan biri (checked) kalıpta dikkate alınmasa da json değerinin harita deseniyle eşleştiğine dikkat edin. Harita kalıpları, harita nesnesinde bulunan ve kalıpta açıkça hesaba katılmayan tüm girişleri yoksayar.

Blok nesnelerin listesini döndürme

  • Sonra, Document sınıfına yeni bir işlev (getBlocks()) ekleyin. getBlocks(), JSON'u Block sınıfının örneklerine ayrıştırır ve kullanıcı arayüzünüzde oluşturulacak blokların listesini döndürür:

lib/data.dart

class Document {
  final Map<String, Object?> _json;
  Document() : _json = jsonDecode(documentJson);

  (String, {DateTime modified}) get metadata {
    if (_json
        case {
          'metadata': {
            'title': String title,
            'modified': String localModified,
          }
        }) {
      return (title, modified: DateTime.parse(localModified));
    } else {
      throw const FormatException('Unexpected JSON');
    }
  }

  List<Block> getBlocks() {                                  // Add from here...
    if (_json case {'blocks': List blocksJson}) {
      return [for (final blockJson in blocksJson) Block.fromJson(blockJson)];
    } else {
      throw const FormatException('Unexpected JSON format');
    }
  }                                                          // to here.
}

getBlocks() işlevi, daha sonra kullanıcı arayüzünü oluşturmak için kullanacağınız Block nesnelerinin listesini döndürür. Tanıdık bir if-case ifadesi, doğrulamayı gerçekleştirir ve blocks meta verilerinin değerini blocksJson adlı yeni bir List öğesine yayınlar (kalıplar olmadan yayınlamak için toList() yöntemine ihtiyacınız olur).

Değişmez liste, yeni listeyi Block nesneyle doldurmak için bir koleksiyon içerir.

Bu bölümde, bu codelab'de henüz denemediğiniz kalıplarla ilgili hiçbir özellik tanıtılmıyor. Sonraki adımda, kullanıcı arayüzünüzdeki liste öğelerini oluşturmaya hazırlanırsınız.

9. Dokümanı görüntülemek için desenler kullanma

Artık bir if-case ifadesi ve refuable kalıpları kullanarak JSON verilerinizi başarıyla yapılandırıp yeniden oluşturuyorsunuz. Ancak "if-case", kalıplarla gelen akış yapılarını kontrol etmek için yapılan geliştirmelerden yalnızca biridir. Şimdi, çürütülebilir kalıplar hakkındaki bilginizi kullanarak ifadeleri değiştirebilirsiniz.

Switch ifadeleriyle kalıplar kullanarak nelerin oluşturulduğunu kontrol etme

  • main.dart ürününde, her blokun type alanına göre stilini belirleyen yeni bir widget (BlockWidget) oluşturun.

lib/main.dart

class BlockWidget extends StatelessWidget {
  final Block block;

  const BlockWidget({
    required this.block,
    super.key,
  });

  @override
  Widget build(BuildContext context) {
    TextStyle? textStyle;
    switch (block.type) {
      case 'h1':
        textStyle = Theme.of(context).textTheme.displayMedium;
      case 'p' || 'checkbox':
        textStyle = Theme.of(context).textTheme.bodyMedium;
      case _:
        textStyle = Theme.of(context).textTheme.bodySmall;
    }

    return Container(
      margin: const EdgeInsets.all(8),
      child: Text(
        block.text,
        style: textStyle,
      ),
    );
  }
}

build yöntemindeki Switch ifadesi, block nesnesinin type alanını açar.

  1. İlk olgu ifadesinde sabit dize kalıbı kullanılır. block.type, h1 sabit değerine eşitse kalıp eşleşir.
  2. İkinci olgu ifadesi, alt kalıp olarak iki sabit dize kalıbına sahip bir mantıksal-veya kalıp kullanır. block.type, p veya checkbox alt kalıplarından biriyle eşleşirse kalıp eşleşir.
  1. Son kullanım, joker karakter kalıbıdır (_). Anahtar kelimelerdeki joker karakterler diğer tüm karakterlerle eşleşir. Bunlar, geçiş ifadelerinde hâlâ izin verilen (biraz daha ayrıntılıdır) default ifadeleriyle aynı şekilde davranırlar.

Joker karakter kalıpları, bir kalıpa izin verilen her yerde kullanılabilir (ör. değişken bildirim kalıbında: var (title, _) = document.metadata;)

Bu bağlamda joker karakter herhangi bir değişkeni bağlamaz. İkinci alanı siler.

Sonraki bölümde, Block nesnelerini görüntüledikten sonra değiştirme hakkında daha fazla bilgi edineceksiniz.

Doküman içeriğini göster

DocumentScreen widget'ının build yönteminde getBlocks() yöntemini çağırarak Block nesnelerinin listesini içeren bir yerel değişken oluşturun.

  1. DocumentationScreen içerisindeki mevcut build yöntemini bu sürümle değiştirin:

lib/main.dart

class DocumentScreen extends StatelessWidget {
  final Document document;

  const DocumentScreen({
    required this.document,
    super.key,
  });

  @override
  Widget build(BuildContext context) {
    final (title, :modified) = document.metadata;
    final blocks = document.getBlocks();                           // Add this line

    return Scaffold(
      appBar: AppBar(
        title: Text(title),
      ),
      body: Column(
        children: [
          Text('Last modified: $modified'),                        // Modify from here
          Expanded(
            child: ListView.builder(
              itemCount: blocks.length,
              itemBuilder: (context, index) {
                return BlockWidget(block: blocks[index]);
              },
            ),
          ),                                                       // to here.
        ],
      ),
    );
  }
}

BlockWidget(block: blocks[index]) satırı, getBlocks() yönteminden döndürülen bloklar listesindeki her bir öğe için bir BlockWidget widget'ı oluşturur.

  1. Uygulamayı çalıştırın, daha sonra ekranda blokları göreceksiniz:

&quot;Engellemeler&quot;deki içeriği gösteren uygulamanın ekran görüntüsü bölümüne ekleyin.

10. Geçiş ifadelerini kullanma

Kalıplar, switch ve case özelliklerine pek çok özellik ekler. Dart, bunların daha fazla yerde kullanılabilmesini sağlamak için ifadeler arasında geçiş yapmıştır. Vakalar, doğrudan bir değişken atamasına veya döndürülen deyime bir değer sağlayabilir.

Switch deyimini bir geçiş ifadesine dönüştürün

Dart analiz aracı, kodunuzda değişiklik yapmanıza yardımcı olacak destekler sağlar.

  1. İmlecinizi önceki bölümde bulunan değiştirme ifadesine getirin.
  2. Mevcut asistleri görüntülemek için ampulü tıklayın.
  3. Geçiş ifadesine dönüştür yardımını seçin.

&quot;Geçiş ifadesi için dönüştür&quot;ün ekran görüntüsü desteği mevcuttur.

Bu kodun yeni sürümü aşağıdaki gibi görünür:

lib/main.dart

class BlockWidget extends StatelessWidget {
  final Block block;

  const BlockWidget({
    required this.block,
    super.key,
  });

  @override
  Widget build(BuildContext context) {
    TextStyle? textStyle;                                          // Modify from here
    textStyle = switch (block.type) {
      'h1' => Theme.of(context).textTheme.displayMedium,
      'p' || 'checkbox' => Theme.of(context).textTheme.bodyMedium,
      _ => Theme.of(context).textTheme.bodySmall
    };                                                             // to here.

    return Container(
      margin: const EdgeInsets.all(8),
      child: Text(
        block.text,
        style: textStyle,
      ),
    );
  }
}

Switch ifadesi, Switch deyimine benzer. Ancak case anahtar kelimesini ortadan kaldırır ve kalıbı destek kaydı gövdesinden ayırmak için => değerini kullanır. Switch ifadelerinin aksine, Switch ifadeleri bir değer döndürür ve ifadenin kullanılabileceği her yerde kullanılabilir.

11. Nesne kalıplarını kullanma

Dart, nesne odaklı bir dil olduğundan kalıplar tüm nesneler için geçerlidir. Bu adımda, kullanıcı arayüzünüzün tarih oluşturma mantığını iyileştirmek için bir nesne kalıbını etkinleştirecek ve nesne özelliklerini yapılandıracaksınız.

Nesne kalıplarından özellikleri ayıklama

Bu bölümde, kalıplar kullanılarak son değiştirilme tarihinin görüntülenme şeklini iyileştireceksiniz.

  • formatDate yöntemini main.dart öğesine ekleyin:

lib/main.dart

String formatDate(DateTime dateTime) {
  final today = DateTime.now();
  final difference = dateTime.difference(today);

  return switch (difference) {
    Duration(inDays: 0) => 'today',
    Duration(inDays: 1) => 'tomorrow',
    Duration(inDays: -1) => 'yesterday',
    Duration(inDays: final days, isNegative: true) => '${days.abs()} days ago',
    Duration(inDays: final days) => '$days days from now',
  };
}

Bu yöntem, bir Duration nesnesi olan difference değerini açan bir geçiş ifadesi döndürür. JSON verilerindeki today ile modified değeri arasındaki süreyi temsil eder.

Switch ifadesinin her durumu, nesnenin inDays ve isNegative özelliklerinde alıcıları çağırarak eşleşen bir nesne kalıbı kullanmaktadır. Söz dizimi, bir Duration nesnesi oluşturuyor gibi görünüyor, ancak aslında difference nesnesindeki alanlara erişiyor.

İlk üç örnekte, inDays nesne özelliğini eşleştirmek ve karşılık gelen dizeyi döndürmek için 0, 1 ve -1 sabit alt kalıpları kullanılır.

Son iki destek kaydı bugün, dün ve yarının ötesindeki süreleri kapsar:

  • isNegative özelliği, boole sabit kalıbıtrue ile eşleşirse (değişiklik tarihi geçmişteyse) gün önce gösterilir.
  • Bu durumda fark söz konusu değilse süre pozitif bir gün sayısı olmalıdır (isNegative: false ile açıkça doğrulama yapmaya gerek yoktur). Değişiklik tarihi gelecektedir ve bugünden sonraki gün sayısını gösterir.

Haftalar için biçimlendirme mantığı ekleme

  • 7 günden uzun süreleri tanımlamak için biçimlendirme işlevinize iki yeni durum ekleyin. Böylece, kullanıcı arayüzü bunları hafta olarak görüntüleyebilir:

lib/main.dart

String formatDate(DateTime dateTime) {
  final today = DateTime.now();
  final difference = dateTime.difference(today);

  return switch (difference) {
    Duration(inDays: 0) => 'today',
    Duration(inDays: 1) => 'tomorrow',
    Duration(inDays: -1) => 'yesterday',
    Duration(inDays: final days) when days > 7 => '${days ~/ 7} weeks from now', // Add from here
    Duration(inDays: final days) when days < -7 =>
      '${days.abs() ~/ 7} weeks ago',                                            // to here.
    Duration(inDays: final days, isNegative: true) => '${days.abs()} days ago',
    Duration(inDays: final days) => '$days days from now',
  };
}

Bu kodda Guard ifadeler kullanıma sunulmuştur:

  • Koruma ifadesi, büyük/küçük harf kalıbından sonra when anahtar kelimesini kullanır.
  • Bunlar; şart-durumları, değiştirme ifadeleri ve değiştirme ifadelerinde kullanılabilir.
  • Bir kalıba yalnızca eşleştirildikten sonra koşul eklerler.
  • Guard ifadesi yanlış olarak değerlendirilirse kalıbın tamamı reddedilir ve yürütme bir sonraki destek kaydına devam eder.

Yeni biçimlendirilen tarihi kullanıcı arayüzüne ekleyin

  1. Son olarak, formatDate işlevini kullanmak için DocumentScreen ürününde build yöntemini güncelleyin:

lib/main.dart

class DocumentScreen extends StatelessWidget {
  final Document document;

  const DocumentScreen({
    required this.document,
    super.key,
  });

  @override
  Widget build(BuildContext context) {
    final (title, :modified) = document.metadata;
    final formattedModifiedDate = formatDate(modified);            // Add this line
    final blocks = document.getBlocks();

    return Scaffold(
      appBar: AppBar(
        title: Text(title),
      ),
      body: Column(
        children: [
          Text('Last modified: $formattedModifiedDate'),           // Modify this line
          Expanded(
            child: ListView.builder(
              itemCount: blocks.length,
              itemBuilder: (context, index) {
                return BlockWidget(block: blocks[index]);
              },
            ),
          ),
        ],
      ),
    );
  }
}
  1. Uygulamanızdaki değişiklikleri görmek için sayfayı yeniden yükleyin:

Uygulamanın &quot;Son değiştirilme: 2 hafta önce&quot; dizesini görüntüleyen ekran görüntüsü biçimini kullanabilirsiniz.

12. Kapsamlı geçiş için bir sınıfı müdafaa edin

Son geçişin sonunda joker karakter veya varsayılan büyük/küçük harf kullanmadığınıza dikkat edin. Ulaşabilecek değerler için her zaman bir durum eklemek iyi bir uygulama olsa da tanımladığınız durumların inDays potansiyel olarak alabileceği tüm olası değerleri hesaba kattığını bildiğiniz için bunun gibi basit bir örnekte sorun yaratmaz.

Bir geçişteki tüm durumlar ele alındığında, buna kapsamlı geçiş adı verilir. Örneğin, true ve false destek kayıtları olduğunda bool türünü açmak tam kapsamlıdır. Enumlar sabit değerlerden oluşan sabit sayıları temsil ettiğinden, numaralandırma değerlerinin her biri için bir durum olduğunda enum türünü açma işlemi kapsamlıdır.

Dart 3, yeni sınıf değiştiricisi sealed ile kapsamlılık denetimini nesneler ve sınıf hiyerarşilerini kapsayacak şekilde genişletti. Block sınıfınızı mühürlü bir süper sınıf olarak yeniden düzenleyin.

Alt sınıf oluşturma

  • data.dart ürününde, Block süresini uzatan üç yeni sınıf (HeaderBlock, ParagraphBlock ve CheckboxBlock) oluşturun:

lib/data.dart

class HeaderBlock extends Block {
  final String text;
  HeaderBlock(this.text);
}

class ParagraphBlock extends Block {
  final String text;
  ParagraphBlock(this.text);
}

class CheckboxBlock extends Block {
  final String text;
  final bool isChecked;
  CheckboxBlock(this.text, this.isChecked);
}

Bu sınıfların her biri, orijinal JSON'deki farklı type değerlerine karşılık gelir: 'h1', 'p' ve 'checkbox'.

Süper sınıfı karşılayın

  • Block sınıfını sealed olarak işaretleyin. Ardından, if-case ifadesini, JSON'de belirtilen type öğesine karşılık gelen alt sınıfı döndüren bir anahtar ifadesi olarak yeniden düzenleyin:

lib/data.dart

sealed class Block {
  Block();

  factory Block.fromJson(Map<String, Object?> json) {
    return switch (json) {
      {'type': 'h1', 'text': String text} => HeaderBlock(text),
      {'type': 'p', 'text': String text} => ParagraphBlock(text),
      {'type': 'checkbox', 'text': String text, 'checked': bool checked} =>
        CheckboxBlock(text, checked),
      _ => throw const FormatException('Unexpected JSON format'),
    };
  }
}

sealed anahtar kelimesi, bir sınıf değiştiricidir. Bu, bu sınıfı yalnızca aynı kitaplıkta genişletebileceğiniz veya uygulayabileceğiniz anlamına gelir. Analiz aracı bu sınıfın alt türlerini bildiğinden, bu türlerden herhangi birini kapsamayan ve tüm öğeleri kapsamayan bir anahtar olduğunda hata bildirir.

Widget'ları görüntülemek için geçiş ifadesi kullanın

  1. main.dart içindeki BlockWidget sınıfını, her durum için nesne kalıplarını kullanan bir anahtar ifadesiyle güncelleyin:

lib/main.dart

class BlockWidget extends StatelessWidget {
  final Block block;

  const BlockWidget({
    required this.block,
    super.key,
  });

  @override
  Widget build(BuildContext context) {
    return Container(
      margin: const EdgeInsets.all(8),
      child: switch (block) {
        HeaderBlock(:final text) => Text(
            text,
            style: Theme.of(context).textTheme.displayMedium,
          ),
        ParagraphBlock(:final text) => Text(text),
        CheckboxBlock(:final text, :final isChecked) => Row(
            children: [
              Checkbox(value: isChecked, onChanged: (_) {}),
              Text(text),
            ],
          ),
      },
    );
  }
}

İlk BlockWidget sürümünüzde, TextStyle döndürmek için Block nesnesinin bir alanını açtınız. Şimdi, Block nesnesinin bir örneğini değiştirip alt sınıflarını temsil eden nesne kalıplarıyla eşleştirme yapacak ve işlemde nesnenin özelliklerini çıkaracaksınız.

Dart analizcisi, Block öğesini mühürlü bir sınıf yaptığınız için her bir alt sınıfın anahtar ifadesinde işlenip işlenmediğini kontrol edebilir.

Ayrıca burada bir Switch ifadesi kullandığınızda, daha önce gereken ayrı bir döndürme ifadesinin aksine sonucu doğrudan child öğesine iletebileceğinizi unutmayın.

  1. İlk kez oluşturulan onay kutusu JSON verilerini görmek için yoğun yeniden yükleyin:

&quot;Dart 3&#39;ü Öğrenin&quot; onay kutusunu görüntüleyen uygulamanın ekran görüntüsü

13. Tebrikler

Kalıplar, kayıtlar, gelişmiş geçiş ve büyük/küçük harf kullanımı ve mühürlü sınıflarla başarılı bir şekilde denemeler yaptınız. Pek çok bilgiyi ele almışsınız, ancak bu özelliklerin yalnızca küçük bir kısmını çizdiniz. Kalıplar hakkında daha fazla bilgi edinmek için özellik spesifikasyonuna bakın.

Farklı kalıp türleri, görünebilecekleri farklı bağlamlar ve alt kalıpların olası iç içe yerleştirilmesi, davranıştaki olasılıkları sonsuz hale getirir. Ancak bu reklamları kolayca görebilirsiniz.

Flutter'da içerik görüntülemenin çeşitli yollarını kalıplar kullanarak düşünebilirsiniz. Kalıpları kullanarak verileri güvenli bir şekilde ayıklayarak kullanıcı arayüzünüzü birkaç satır kodla oluşturabilirsiniz.

Sırada ne var?

  • Dart dokümanlarının Dil bölümünde bulunan kalıplar, kayıtlar, gelişmiş geçiş, destek kayıtları ve sınıf değiştiricilerle ilgili belgelere göz atın.

Referans belgeler

Örnek kodun tamamını flutter/codelabs deposunda adım adım inceleyin.

Her yeni özellikle ilgili ayrıntılı teknik özellikler için orijinal tasarım dokümanlarına göz atın: