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

1. Giriş

Dart 3, dile desenler ekliyor. Bu, yeni bir dilbilgisi kategorisidir. Dart kodu yazmanın bu yeni yolunun yanı sıra, aşağıdakiler de dahil olmak üzere çeşitli dil geliştirmeleri yapıldı:

  • Farklı türlerdeki verileri paketlemek için kayıtlar,
  • Erişimi kontrol etmek için sınıf değiştiriciler ve
  • Yeni switch ifadeleri ve if-case ifadeleri.

Bu özellikler, Dart kodu yazarken sahip olduğunuz seçenekleri genişletir. Bu codelab'de, kodunuzu daha kompakt, akıcı ve esnek hale getirmek için bu işlevleri nasıl kullanacağınızı öğreneceksiniz.

Bu codelab'de Flutter ve Dart hakkında bilgi sahibi olduğunuz varsayılır. Bilgilerinizin biraz paslandığını düşünüyorsanız aşağıdaki kaynaklarla temel bilgilerinizi tazeleyebilirsiniz:

Ne oluşturacaksınız?

Bu codelab'de, Flutter'da bir JSON belgesini 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, başlıklar ve paragraflar gibi doküman verileri bulunur. Verileri, Flutter widget'larınızın ihtiyaç duyduğu her yerde aktarılıp açılabilmesi için kayıtlara düzgün bir şekilde yerleştirmek üzere kod yazarsınız.

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 ayırmak için kalıpların nasıl kullanılacağını da göreceksiniz.

Bu codelab'de oluşturacağınız son uygulama; başlık, son değiştirilme tarihi, üstbilgiler ve paragraflar içeren bir doküman.

Neler öğreneceksiniz?

  • Farklı türlerde birden fazla değer depolayan bir kayıt oluşturma
  • Kayıt kullanarak bir işlevden birden fazla değer döndürme
  • Kayıtlardaki ve diğer nesnelerdeki verileri eşleştirmek, doğrulamak ve yapılarını bozmak için desenleri kullanma.
  • Desene göre eşleşen değerleri yeni veya mevcut değişkenlere nasıl bağlayacağınız açıklanmaktadır.
  • Yeni anahtar ifadesi özelliklerini, anahtar ifadelerini ve if-case ifadelerini kullanma
  • Her durumun bir switch ifadesinde veya switch ifadesinde ele alınmasını sağlamak 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 ayarlayın.
  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

Desenler, kayıtlar ve diğer yeni özelliklere geçmeden önce tüm kodunuzu yazacağınız bir Flutter projesi oluşturun.

Flutter projesi oluşturma

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

Oluşturulan projeyi gösteren VS Code

Minimum SDK sürümünü ayarlama

  • Projenizin SDK sürümü kısıtlamasını Dart 3 veya sonraki sürümlere 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:

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

Sonraki adımlarda bu iki dosyayı da değiştirmeye devam edeceksiniz.

Uygulamanın verilerini tanımlama

  • Yeni bir dosya oluşturun lib/data.dart ve aşağıdaki 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"
    }
  ]
}
''';

Giriş/çıkış akışı veya HTTP isteği gibi harici bir kaynaktan veri alan bir program düşünün. Bu codelab'de, gelen JSON verilerini documentJson değişkeninde çok satırlı bir dizeyle taklit ederek 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 ekleyeceksiniz. Bu sınıf, oluşturucusunda _json alanını tanımlar ve başlatır.

Uygulamayı çalıştırma

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

  1. Uygulama için bir başlangıç noktası oluşturmak üzere 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(),
      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ünü temalandırmak için Materyal Tasarım'ın en son sürümünü kurar.
  • DocumentScreen, Scaffold widget'ını kullanarak sayfanın görsel düzenini sağlar.
  1. Her şeyin sorunsuz ç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ır ve hata ayıkla&quot; düğmesi

  1. Varsayılan olarak Flutter, kullanılabilir hedef platformu seçer. Hedef platformu değiştirmek için durum çubuğunda geçerli platformu seçin:

VS Code&#39;daki hedef platform seçici

title ve body öğelerinin DocumentScreen widget'ında tanımlandığı boş bir çerçeve görürsünüz:

Bu adımda oluşturulan uygulama.

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

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üne 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 içinde, 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 türünde, 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.

Kayıt alanlarına erişme

  1. DocumentScreen widget'ında, kaydınızı alıp değerlerine erişebilmek için build yönteminde metadata getter 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: [                                        // And the following line.
          Center(child: Text('Last modified ${metadataRecord.modified}')),
        ],
      ),
    );
  }
}

metadata getter yöntemi, metadataRecord yerel değişkenine atanan 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 bir yoludur.

Bu kayıtta oluşturulan alanlara erişmek için kayıtların yerleşik getter söz dizimini kullanabilirsiniz.

  • Konum alanını (title gibi adı olmayan bir alan) almak için kayıttaki alıcı 'ı kullanın. Bu işlev yalnızca adlandırılmamış alanları döndürür.
  • modified gibi adlandırılmış alanların konuma dayalı alıcısı yoktur. Bu nedenle, adını doğrudan (ör. metadataRecord.modified) kullanabilirsiniz.

Konumsal bir alanın alıcı yönteminin 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 hızlı yeniden yükleme yapın. VS Code Dart eklentisi, bir dosyayı her kaydettiğinizde hızlı yeniden yükleme yapar.

Başlığı ve değiştirilme tarihini gösteren uygulama ekran görüntüsü.

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

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

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

6. Desenlerle eşleştirme ve yapı bozma

Kayıtlar, farklı veri türlerini verimli bir şekilde toplayabilir ve kolayca paylaşabilir. Artık desenleri kullanarak kodunuzu iyileştirebilirsiniz.

Bir kalıp, bir veya daha fazla değerin alabileceği bir yapıyı (ör. plan) temsil eder. Kalıpların eşleşip eşleşmediğini belirlemek için gerçek değerlerle karşılaştırma yapılır.

Bazı kalıplar eşleştiğinde, içindeki verileri çekerek eşleşen değeri yapılandırılmış hale getirir. Yapı bozma, bir nesnedeki değerleri yerel değişkenlere atamak veya bunlar üzerinde daha fazla eşleştirme yapmak için açmanıza olanak tanır.

Bir kaydı yerel değişkenlere ayırma

  1. build yöntemini DocumentScreen olarak yeniden düzenleyerek metadata yöntemini çağırın ve desen değişkeni bildirimi 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 from here...
      body: Column(children: [Center(child: Text('Last modified $modified'))]),
    );                                                       // To here.
  }
}

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

  • Sonuç, iki alanlı bir kayıt olduğu ve bu alanlardan birinin adı modified olduğu için ifade alt kalıpla eşleşir.
  • Eşleşme nedeniyle değişken bildirimi kalıbı, ifadeyi yapılandırarak değerlerine erişir ve bunları aynı tür ve adlara sahip yeni yerel değişkenlere (String title ve DateTime modified) bağlar.

Bir alanın adı ile onu dolduran değişken aynı olduğunda kullanılan bir kısaltma vardır. 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'))]),
    );
  }
}

Değişken kalıbının :modified söz dizimi, modified: modified için kısa yazım şeklidir. Farklı bir ada sahip yeni bir yerel değişken istiyorsanız bunun yerine modified: localModified yazabilirsiniz.

  1. Önceki adımdaki sonucu görmek için sıcak yeniden yükleme yapın. Davranış tam olarak aynıdır. Yalnızca kodunuzu daha kısa hale getirdiniz.

7. Verileri çıkarmak için kalıpları kullanma

Belirli bağlamlarda kalıplar yalnızca eşleşip yapıyı bozmakla kalmaz, aynı zamanda kalıbın eşleşip eşleşmemesine bağlı olarak kodun ne yaptığı hakkında bir karar da verebilir. Bunlara çürütülebilir kalıplar denir.

Son adımda kullandığınız değişken bildirimi kalıbı kesin bir kalıptır: Değer, kalıpla eşleşmelidir. Aksi takdirde hata oluşur ve yapı bozma işlemi gerçekleşmez. Herhangi bir değişken bildirimi veya ataması yapıldığını düşünün. Aynı türde değillerse bir değişkene değer atayamazsınız.

Öte yandan, çürütülebilir 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şmemesine bağlı olarak kontrol akışını etkilemek için tasarlanmıştır.
  • Eşleşmiyorlarsa yürütmeyi hatayla kesmezler, yalnızca bir sonraki ifadeye geçerler.
  • Yalnızca eşleştiğinde kullanılabilen değişkenleri yapılandırabilir ve bağlayabilir.

JSON değerlerini kalıpsız okuma

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

  • metadata'nın önceki sürümünü, değerleri _json haritasından okuyan bir sürümle değiştirin. 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, verilerin kalıplar kullanılmadan doğru şekilde yapılandırıldığını doğrular. Daha sonraki bir adımda, daha az kod kullanarak aynı doğrulamayı yapmak için kalıp eşleştirme özelliğini kullanırsınız. Başka bir işlem yapmadan önce üç kontrol gerçekleştirir:

  • JSON, beklediğiniz yapı verilerini içeriyor: if (_json.containsKey('metadata'))
  • Veriler, beklediğiniz türde olmalıdır: if (metadataJson is Map)
  • Verilerin boş olmadığını (bu, önceki kontrolde örtülü olarak onaylanır).

Harita kalıbı kullanarak JSON değerlerini okuma

Çürütülebilir bir kalıpla, harita kalıbı kullanarak JSON'un beklenen yapıya sahip olduğunu doğrulayabilirsiniz.

  • metadata'nın önceki 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 case {                                         // Modify from here...
      'metadata': {'title': String title, 'modified': String localModified},
    }) {
      return (title, modified: DateTime.parse(localModified));
    } else {
      throw const FormatException('Unexpected JSON');
    }                                                        // to here.
  }
}

Burada, Dart 3'te kullanıma sunulan yeni bir if-statement türü olan if-case'i görüyorsunuz. Case gövdesi yalnızca case kalıbı _json içindeki verilerle eşleşirse yürütülür. Bu eşleşme, gelen JSON'ı doğrulamak için metadata'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'] da bir harita türüdür.
  • _json['metadata'], title ve modified anahtarlarını içerir.
  • title ve localModified dizelerdir ve boş değildir.

Değer eşleşmezse desen çürütülür (yürütme devam etmez) ve else ifadesine geçilir. Eşleşme başarılı olursa desen, haritadaki title ve modified değerlerini yapılandırır ve yeni yerel değişkenlere bağlar.

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

8. Uygulamayı daha fazla desene hazırlama

Şimdiye kadar JSON verilerinin metadata bölümünü ele aldınız. Bu adımda, blocks listesindeki verileri işlemek ve uygulamanızda oluşturmak için işletme mantığınızı biraz daha ayrıntılı hale getirirsiniz.

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

Veri depolayan bir sınıf oluşturma

  • JSON verilerindeki bloklardan birinin verilerini okumak ve depolamak için kullanılan data.dart'ye 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');
    }
  }
}

Fabrika oluşturucu fromJson(), daha önce gördüğünüz harita deseniyle aynı if-case'i kullanır.

JSON verilerinin, kalıpta bulunmayan checked adlı ek bir bilgiye sahip olmasına rağmen beklenen kalıba benzediğini görürsünüz. Bunun nedeni, bu tür kalıpları ("harita kalıpları" olarak adlandırılır) kullandığınızda yalnızca kalıpta tanımladığınız belirli şeyleri dikkate almaları ve verilerdeki diğer her şeyi yoksaymalarıdır.

Block nesnelerinin listesini döndürme

  • Ardından, Document sınıfına getBlocks() adlı yeni bir işlev ekleyin. getBlocks(), JSON'ı 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 kullandığınız Block nesnelerinin listesini döndürür. Tanıdık bir if-case ifadesi doğrulama gerçekleştirir ve blocks meta verilerinin değerini blocksJson adlı yeni bir List'ye dönüştürür (desenler olmadan dönüştürmek için toList() yöntemini kullanmanız gerekir).

Liste değişmezi, yeni listeyi Block nesneleriyle doldurmak için bir collection for içerir.

Bu bölümde, bu codelab'de daha önce denemediğiniz desenle ilgili özellikler tanıtılmamaktadır. Sonraki adımda, kullanıcı arayüzünüzdeki liste öğelerini oluşturmaya hazırlanır.

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

Artık bir if-case ifadesi ve çürütülebilir kalıplar kullanarak JSON verilerinizi başarılı bir şekilde yapılandırabilir ve yeniden oluşturabilirsiniz. Ancak if-case, kalıplarla birlikte gelen kontrol akışı yapılarındaki geliştirmelerden yalnızca biridir. Şimdi, çürütülebilir kalıplarla ilgili bilginizi switch ifadelerine uygulayın.

Anahtar ifadeleriyle desenleri kullanarak neyin oluşturulacağını kontrol etme

  • main.dart içinde, her bloğun stilini type alanına göre 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 case ifadesinde sabit dize kalıbı kullanılır. block.type, sabit değer h1'ye eşitse kalıp eşleşir.
  2. İkinci case ifadesi, alt kalıpları olarak iki sabit dize kalıbı içeren bir mantıksal VEYA kalıbı kullanır. block.type, p veya checkbox alt kalıplarından biriyle eşleşirse kalıp eşleşir.
  1. Son durum, _ joker karakter kalıbıdır. Switch ifadelerindeki joker karakterler diğer her şeyle eşleşir. Bunlar, switch ifadelerinde hâlâ izin verilen default ifadeleriyle aynı şekilde davranır (yalnızca biraz daha ayrıntılıdır).

Joker karakter kalıpları, kalıba izin verilen her yerde kullanılabilir. Örneğin, değişken bildirimi 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 nesneleri görüntülendikten sonra daha fazla anahtar özelliği hakkında bilgi edineceksiniz.

Doküman içeriğini görüntüleme

Block nesnelerinin listesini içeren bir yerel değişken oluşturmak için DocumentScreen widget'ının build yönteminde getBlocks() işlevini çağırın.

  1. DocumentationScreen içindeki 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 blok listesindeki her öğe için bir BlockWidget widget'ı oluşturur.

  1. Uygulamayı çalıştırın. Ardından ekranda blokların göründüğünü görmelisiniz:

JSON verilerinin &quot;bloklar&quot; bölümündeki içeriği gösteren uygulama.

10. Anahtar ifadeleri kullanma

Kalıplar, switch ve case'ye birçok özellik ekler. Dart, switch ifadeleri sayesinde bu ifadelerin daha fazla yerde kullanılabilmesini sağlar. Bir dizi durum, doğrudan bir değişken atamasına veya döndürme ifadesine değer sağlayabilir.

Anahtar ifadesini anahtar ifadesine dönüştürme

Dart analiz aracı, kodunuzda değişiklik yapmanıza yardımcı olacak yardımlar sunar.

  1. İmlecinizi önceki bölümdeki switch ifadesine taşıyın.
  2. Mevcut destekleri görüntülemek için ampulü tıklayın.
  3. Convert to switch expression (Anahtar ifadesine dönüştür) yardımını seçin.

VS Code&#39;da &quot;switch ifadesine dönüştür&quot; yardımı kullanılabilir.

Bu kodun yeni sürümü şu şekilde 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),
    );
  }
}

Bir switch ifadesi, switch ifadesine benzer ancak case anahtar kelimesini ortadan kaldırır ve kalıbı case gövdesinden ayırmak için => kullanır. Switch ifadelerinden farklı olarak, switch ifadeleri bir değer döndürür ve bir ifadenin kullanılabildiği her yerde kullanılabilir.

11. Nesne kalıplarını kullanma

Dart, nesne yönelimli bir dil olduğundan desenler tüm nesneler için geçerlidir. Bu adımda, kullanıcı arayüzünüzün tarih oluşturma mantığını geliştirmek için bir nesne kalıbı etkinleştirir ve nesne özelliklerini yapılandırılmış hale getirirsiniz.

Özellikleri nesne desenlerinden çıkarma

Bu bölümde, son değiştirilme tarihinin nasıl görüntülendiğini kalıpları kullanarak iyileştirirsiniz.

  • main.dart öğesine formatDate yöntemini 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, difference değerine (Duration nesnesi) göre seçim yapan bir switch ifadesi döndürür. today ile JSON verilerindeki modified değeri arasındaki zaman aralığını gösterir.

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

İlk üç örnekte, inDays nesne özelliğiyle eşleşmek ve ilgili dizeyi döndürmek için sabit alt desenler 0, 1 ve -1 kullanılır.

Son iki örnekte, bugün, dün ve yarının dışındaki süreler ele alınır:

  • isNegative özelliği, boolean sabit kalıbıylatrue eşleşiyorsa (yani değişiklik tarihi geçmişteyse) gün önce gösterilir.
  • Bu durum farkı yakalamazsa sürenin pozitif bir gün sayısı olması gerekir (isNegative: false ile açıkça doğrulamaya gerek yoktur). Bu nedenle, değiştirme tarihi gelecektedir ve günden itibaren gösterilir.

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

  • 7 günden uzun süreleri belirlemek için biçimlendirme işlevinize iki yeni durum ekleyin. Böylece kullanıcı arayüzü bu süreleri hafta olarak gösterebilir:

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 kod, koruma ifadelerini tanıtır:

  • Koruma ifadesi, bir durum kalıbından sonra when anahtar kelimesini kullanır.
  • Bunlar if-case'lerde, switch ifadelerinde ve switch ifadelerinde kullanılabilir.
  • Yalnızca eşleştirildikten sonra bir desene koşul eklerler.
  • Koruma koşulu yanlış olarak değerlendirilirse tüm kalıp çürütülür ve yürütme bir sonraki duruma geçer.

Yeni biçimlendirilmiş tarihi kullanıcı arayüzüne ekleme

  1. Son olarak, formatDate işlevini kullanmak için DocumentScreen içindeki 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 anında yeniden yükleme yapın:

formatDate() işlevini kullanarak &quot;Son değiştirilme: 2 hafta önce&quot; dizesini gösteren uygulama.

12. Kapsamlı anahtarlama için bir sınıfı kapatma

Son anahtarın sonunda joker karakter veya varsayılan durum kullanmadığınıza dikkat edin. Değerlerin başarısız olabileceği durumlar için her zaman bir durum eklemek iyi bir uygulama olsa da, tanımladığınız durumların tüm olası değerleri kapsadığını bildiğiniz için bu gibi basit örneklerde sorun yoktur.inDays

Bir switch ifadesindeki her durum işlendiğinde buna kapsamlı switch denir. Örneğin, bool türünde bir anahtarın true ve false durumları varsa bu anahtarın açılması kapsamlıdır. Numaralandırmalar, sabit sayıda sabit değeri temsil ettiğinden, numaralandırmanın her bir değeri için durumlar olduğunda enum türünü etkinleştirmek kapsamlıdır.

Dart 3, yeni sınıf değiştiricisi sealed ile kapsamlılık kontrolünü nesnelere ve sınıf hiyerarşilerine genişletti. Block sınıfınızı kapalı bir üst sınıf olarak yeniden düzenleyin.

Alt sınıfları oluşturma

  • data.dart içinde, Block'i genişleten üç yeni sınıf oluşturun: HeaderBlock, ParagraphBlock ve CheckboxBlock:

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'daki farklı type değerlerine karşılık gelir: 'h1', 'p' ve 'checkbox'.

Üst sınıfı kapatma

  • Block sınıfını sealed olarak işaretleyin. Ardından, if-case ifadesini, JSON'da belirtilen type değerine karşılık gelen alt sınıfı döndüren bir switch 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, yalnızca aynı kitaplıkta bu sınıfı genişletebileceğiniz veya uygulayabileceğiniz anlamına gelen bir sınıf değiştiricisidir. Analiz aracı bu sınıfın alt türlerini bildiğinden, bir anahtar bunlardan birini kapsamadığında ve kapsamlı olmadığında hata bildirir.

Widget'ları görüntülemek için bir anahtar 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),
          ],
        ),
      },
    );
  }
}

BlockWidget'nın ilk sürümünde, TextStyle döndürmek için Block nesnesinin bir alanını etkinleştirdiniz. Artık Block nesnesinin bir örneğini değiştirip alt sınıflarını temsil eden nesne kalıplarıyla eşleştirerek nesnenin özelliklerini çıkarabilirsiniz.

Dart analiz aracı, Block sınıfını kapalı sınıf yaptığınız için her alt sınıfın switch ifadesinde işlendiğini kontrol edebilir.

Ayrıca, burada bir switch ifadesi kullanmanın, sonucu daha önce gereken ayrı bir dönüş ifadesi yerine doğrudan child öğesine iletmenize olanak tanıdığını da unutmayın.

  1. Onay kutusu JSON verilerinin ilk kez oluşturulduğunu görmek için hızlı yeniden yükleme yapın:

&quot;Learn Dart 3&quot; onay kutusunu gösteren uygulama

13. Tebrikler

Desenler, kayıtlar, gelişmiş switch ve case ile sealed sınıflarını başarıyla denediniz. Çok fazla bilgi verdin ancak bu özelliklerin sadece yüzeyini çizdin. Kalıplar hakkında daha fazla bilgi 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şimi, davranışlardaki olasılıkları sınırsız hale getirir. Ancak bu tür reklamlar kolayca görülebilir.

Desenleri kullanarak Flutter'da içerikleri görüntülemenin her türlü yolunu düşünebilirsiniz. Kalıpları kullanarak kullanıcı arayüzünüzü birkaç satır kodla oluşturmak için verileri güvenli bir şekilde ayıklayabilirsiniz.

Yapabilecekleriniz

  • Dart dokümanlarının Dil bölümünde desenler, kayıtlar, gelişmiş switch ve case'ler ile sınıf değiştiricilerle ilgili dokümanlara göz atın.

Referans belgeler

Adım adım tam örnek kodu flutter/codelabs deposunda inceleyin.

Her yeni özelliğin ayrıntılı spesifikasyonları için orijinal tasarım belgelerine göz atın: