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

Dart'ın kalıplarını ve kayıtlarını inceleme

Bu codelab hakkında

subjectSon güncelleme Nis 4, 2025
account_circleYazan: John Ryan and Marya Belanger

1. Giriş

Dart 3, dile yeni bir dil bilgisi kategorisi olan kalıpları ekler. Dart kodu yazmanın bu yeni yolunun yanı sıra aşağıdakiler gibi başka dil geliştirmeleri de vardır:

  • Farklı türde 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.

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

Bu kod laboratuvarının hedef kitlesi, Flutter ve Dart hakkında bilgi sahibi olan kullanıcılardır. Bilgilerinizi tazelemek istiyorsanız aşağıdaki kaynaklardan yararlanabilirsiniz:

Ne oluşturacaksınız?

Bu kod laboratuvarında, Flutter'da JSON belgesi 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, üstbilgiler ve paragraflar gibi doküman verileri bulunur. Flutter widget'larınızın ihtiyaç duyduğu her yerde aktarılıp paketten çıkarılabilmesi için verileri kayıtlara düzgün bir şekilde paketleyecek 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şkenler halinde yapılandırmamak için kalıpların nasıl kullanıldığını da göreceksiniz.

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

Neler öğreneceksiniz?

  • Farklı türlerde birden fazla değer depolayan bir kayıt oluşturma.
  • Bir işlevden kayıt kullanarak birden çok değer döndürme.
  • Kayıtlardaki ve diğer nesnelerdeki verileri eşleştirmek, doğrulamak ve yapısını kaldırmak için desenleri kullanma.
  • Desenle eşleşen değerleri yeni veya mevcut değişkenlere bağlama.
  • Yeni switch ifadesi özelliklerini, switch ifadelerini ve if-case ifadelerini kullanma
  • Her durumun bir switch ifadesi veya switch ifadesinde ele alındığından emin olmak için kapsamlı kontrol özelliğinden nasıl yararlanılır?

2. Ortamınızı ayarlama

  1. Flutter SDK'yı 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

Kalıplar, kayıtlar ve diğer yeni özellikleri incelemeden önce, tüm kodunuzu yazacağınız basit bir Flutter projesi oluşturmak için biraz zaman ayırın.

Flutter projesi oluşturma

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

"flutter create" komutuyla oluşturulan projeyi gösteren VS Code ekran görüntüsü.

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 dosyaların ikisini de değiştirmeye devam edeceksiniz.

Uygulamanın verilerini tanımlama

  • lib/data.dart adlı yeni bir dosya oluşturun ve dosyaya 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"
    }
  ]
}
''';

G/Ç 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ılan JSON'dan veri döndüren işlevler ekleyeceksiniz. Bu sınıf, _json alanını oluşturucuda 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(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ünün temasını belirlemek 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:

Sol taraftaki etkinlik çubuğunun &quot;Çalıştır ve hata ayıkla&quot; bölümünde bulunan &quot;Çalıştır ve hata ayıkla&quot; düğmesinin resmi.

  1. Flutter varsayılan olarak kullanılabilir olan hedef platformu seçer. Hedef platformu değiştirmek için durum çubuğunda mevcut platformu seçin:

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

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

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

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 dosyasında, Document sınıfına metadata adlı ve kayıt döndüren yeni bir alıcı 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üne, diğeri DateTime türüne sahip iki alana sahip bir kayıttır.

Döndürme ifadesi, iki değeri paranteze (title, modified: now) alarak 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şim

  1. DocumentScreen widget'ında, kaydınızı alıp değerlerine erişebilmek için build yöntemindeki 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 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 hafif ve kolay bir yoludur.

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

  • Konumsal bir alanı (title gibi adsız bir alan) almak için kayıtta $<num> alıcısını kullanın. Bu işlem yalnızca adlandırılmamış alanları döndürür.
  • modified gibi adlandırılmış alanların konumsal alıcıları yoktur. Bu nedenle, adını doğrudan kullanabilirsiniz (ör. metadataRecord.modified).

Konumsal bir alan için alıcı adının belirlenmesi amacıyla $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. JSON değerlerini uygulamada görmek için anında yeniden yükleyin. VS Code Dart eklentisi, her dosya kaydettiğinizde anında yeniden yüklenir.

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

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

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

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

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

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

Desen, bir veya daha fazla değerin alabileceği bir yapıyı (ör. plan) 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ştiğinde verileri çıkararak eşleşen değeri yapısını bozar. Yapıyı bölme, bir nesnenin değerlerini yerel değişkenlere atamak veya bunlarla daha fazla eşleştirme yapmak için paketini açmanıza olanak tanır.

Bir kaydı yerel değişkenler halinde yapılandırma

  1. DocumentScreen sınıfının build yöntemini, metadata'yi çağıracak ve kalıp değişkeni beyanını başlatmak için kullanacak şekilde yeniden yapılandı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 (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ıp içerir.

  • Sonuç, biri modified adlı iki alana sahip bir kayıt olduğundan ifade alt kalıpla eşleşir.
  • Eşleştiklerinden, değişken beyanı kalıbı ifadenin yapısını bozar, 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şkenin aynı olduğu durumlar için kısa bir yöntem vardır. DocumentScreen sınıfının build yöntemini aşağıdaki gibi yeniden yapılandı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 (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 kısaltmasıdır. Farklı bir ada sahip yeni bir yerel değişken istiyorsanız bunun yerine modified: localModified yazabilirsiniz.

  1. Önceki adımdakiyle aynı sonucu görmek için anında yeniden yükleyin. Davranış tam olarak aynıdır; yalnızca kodunuzu daha kısa hale getirdiniz.

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

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

Son adımda kullandığınız değişken beyanı kalıbı reddedilemez 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 beyanını veya atamasını düşünün. Aynı türde değilse bir değişkene değer atayamazsınız.

Öte yandan, reddedilebilir 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 kullanılırlar.
  • Eşleşmezlerse bir hatayla yürütmeyi kesintiye uğratmazlar, sadece bir sonraki ifadeye geçerler.
  • Eşleştiğinde yalnızca kullanılabilen değişkenlerin yapısını bozabilir ve bu değişkenleri bağlayabilir

JSON değerlerini kalıp 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 okursunuz.

  • metadata'ün önceki sürümünü, _json haritasındaki değerleri okuyan bir sürümle değiştirin. metadata'nin bu 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ıp kullanmadan verilerin doğru şekilde yapılandırıldığını doğrular. Daha sonraki bir adımda, daha az kod kullanarak aynı doğrulamayı gerçekleştirmek için kalıp eşleştirmeyi kullanırsınız. 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ürdedir: if (metadataJson is Map)
  • Verilerin boş olmadığı (önceki kontrolde dolaylı olarak onaylanır).

Harita kalıbı kullanarak JSON değerlerini okuma

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

  • metadata'nin ö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                                                // 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, Dart 3'te kullanıma sunulan yeni bir tür if-ifadesi (if-case) görüyorsunuz. Destek kaydı metni yalnızca destek kaydı kalıbı _json'teki verilerle eşleşirse yürütülür. Bu eşleşme, gelen JSON'u doğrulamak için metadata işlevinin ilk sürümünde yazdığınız kontrolleri gerçekleştirir. Bu kod aşağıdakileri doğrular:

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

Değer eşleşmezse desen reddedilir (yürütmeye devam etmeyi reddeder) ve else yan tümcesine geçilir. Eşleşme başarılı olursa kalıp, haritadaki title ve modified değerlerinin yapısını bozar ve bunları 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 kalıba hazırlayın

Ş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ş mantığınızı biraz daha hassaslaştırırsınız.

{
  "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 sınıfına Block adlı yeni bir sınıf 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 kurucusu fromJson(), daha önce gördüğünüz harita deseniyle aynı if-case'i kullanır.

JSON verilerinin, checked adlı ve kalıpta bulunmayan ek bir bilgi parçasına sahip olmasına rağmen beklenen kalıba benzediğini göreceksiniz. 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 yok saymalarıdır.

Engelleme nesnelerinin listesini döndürme

  • Ardından, Document sınıfına getBlocks() adlı yeni bir işlev ekleyin. getBlocks(), JSON'u Block sınıfının örneklerine ayı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 bir listesini döndürür. Bilinen bir if-case ifadesi doğrulama yapar ve blocks meta verilerinin değerini blocksJson adlı yeni bir List değerine dönüştürür (desenler olmadan dönüştürmek için toList() yöntemine ihtiyacınız vardır).

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

Bu bölümde, bu kod laboratuvarındaki denemediğiniz kalıplarla ilgili özellikler tanıtılmaz. Sonraki adımda, liste öğelerini kullanıcı arayüzünüzde oluşturmaya hazırlanırsınız.

9. Belgeyi göstermek için kalıpları kullanma

Artık bir if-case ifadesi ve çürütülebilir kalıplar kullanarak JSON verilerinizi başarıyla yapısını bozabilir ve yeniden oluşturabilirsiniz. Ancak if-case, kalıplarla birlikte gelen akış yapılarını kontrol etmeye yönelik geliştirmelerden yalnızca biridir. Şimdi, reddedilebilir kalıplarla ilgili bilginizi ifade değiştirme ifadelerine uygulayın.

Switch ifadeleri ile kalıpları kullanarak nelerin oluşturulacağını kontrol etme

  • main.dart alanında, 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 ifadesi sabit dize kalıbı kullanır. block.type, sabit değer h1 ile eşitse kalıp eşleşir.
  2. İkinci durum 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 örnek ise _ olan bir joker karakter kalıbıdır. Geçiş durumlarındaki joker karakterler diğer her şeyle eşleşir. Bunlar, switch ifadelerinde hâlâ izin verilen default yan tümceleriyle aynı şekilde çalışır (yalnızca biraz daha ayrıntılıdır).

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

Bu bağlamda joker karakter herhangi bir değişkene bağlanmaz. İkinci alan atılır.

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

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

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

  1. DocumentationScreen içindeki mevcut build yöntemini şu 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 listesinde her öğe için bir BlockWidget widget'ı oluşturur.

  1. Uygulamayı çalıştırdığınızda ekranda bloklar gösterilir:

JSON verilerinin &quot;blocks&quot; bölümündeki içeriği gösteren uygulamanın ekran görüntüsü.

10. Switch ifadelerini kullanma

Kalıplar, switch ve case'a birçok özellik ekler. Dart, bu ifadeleri daha fazla yerde kullanılabilir hale getirmek için switch ifadelerine sahiptir. Bir dizi durum, doğrudan bir değişken atamasına veya dönüş beyanına değer sağlayabilir.

switch ifadesini switch ifadesine dönüştürme

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

  1. İmlecinizi önceki bölümdeki switch ifadesine getirin.
  2. Mevcut yardımları görüntülemek için ampul simgesini tıklayın.
  3. Dönüşüm ifadesi olarak dönüştür yardımını seçin.

VS Code&#39;da bulunan &quot;anahtar ifadeye dönüştür&quot; yardımının ekran görüntüsü.

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,
      ),
    );
  }
}

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

11. Nesne kalıplarını kullanma

Dart, nesne yönelimli 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ı etkinleştirir ve nesne özelliklerini yapısını bozarsınız.

Nesne kalıplarından özellikleri ayıklama

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

  • formatDate yöntemini main.dart'a 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ğerini (Duration nesnesi) açan bir anahtar ifadesi döndürür. today ile JSON verilerindeki modified değeri arasındaki süreyi temsil eder.

switch ifadesinin her bir durumu, nesnenin inDays ve isNegative özelliklerinde alıcıları çağırarak eşleşen bir nesne kalıbı kullanır. Söz dizimi, bir Duration nesnesi oluşturacak gibi görünse de aslında difference nesnesinde bulunan alanlara erişiyor.

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

Son iki durum, bugün, dün ve yarın dışındaki süreleri ele alır:

  • isNegative mülkü boole sabit kalıbıyla eşleşirse (değişiklik tarihi geçmişteyse) gün önce ifadesi gösterilir.true
  • Bu durum farkı yakalamıyorsa süre pozitif bir gün sayısı olmalıdır (isNegative: false ile açıkça doğrulamanız gerekmez). Bu nedenle, değişiklik tarihi gelecektedir ve şu andan itibaren gün sayısı olarak gösterilir.

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ö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 kodda koruyucu yan tümceler tanıtılmaktadır:

  • Koruyucu yan tümce, bir durum deseninden sonra when anahtar kelimesini kullanır.
  • Bunlar; if-case, switch ifadeleri ve switch ifadelerinde kullanılabilir.
  • Yalnızca eşleşmeden sonra bir kalıba koşul eklenir.
  • Koruyucu yan tümce false olarak değerlendirilirse kalıbın tamamı reddedilir ve yürütme işlemi bir sonraki duruma geçer.

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

  1. Son olarak, DocumentScreen işlevindeki build yöntemini formatDate işlevini kullanacak şekilde 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şlevi kullanılarak &quot;Son değiştirilme tarihi: 2 hafta önce&quot; dizesini gösteren uygulamanın ekran görüntüsü.

12. Sınıfları kapsamlı geçiş için mühürleme

Son geçişin sonunda joker karakter veya varsayılan büyük/küçük harf kullanmadığınıza dikkat edin. Her zaman geçersiz olabilecek değerler için bir durum eklemek iyi bir uygulama olsa da, tanımladığınız durumların inDays'nin olası tüm değerlerini hesaba kattığını bildiğiniz için bu gibi basit bir örnekte bunu yapmanıza gerek yoktur.

Bir anahtardaki her durum ele alındığında buna kapsamlı anahtar denir. Örneğin, true ve false için durumlar olduğunda bool türünü etkinleştirmek kapsamlı bir işlemdir. Enum'ler sabit değerler için sabit bir sayı temsil ettiğinden, enum değerlerinin her biri için de durum olduğunda enum türünü etkinleştirmek kapsamlı bir işlemdir.

Dart 3, yeni sınıf değiştirici sealed ile kapsamlı kontrol özelliğini nesneler ve sınıf hiyerarşileri için genişletti. Block sınıfınızı mühürlü bir üst sınıf olarak yeniden yapılandırın.

Alt sınıfları oluşturma

  • data.dart içinde, Block'i genişleten üç 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'daki farklı type değerlerine ('h1', 'p' ve 'checkbox') karşılık gelir.

Üst sınıfı mühürleme

  • 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 yapılandırın:

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ştiricisidir. Yani bu sınıfı yalnızca aynı kitaplıkta genişletebilir veya uygulayabilirsiniz. Analizör bu sınıfın alt türlerini bildiğinden, bir anahtar bunlardan birini kapsamazsa ve kapsamlı değilse hata bildirir.

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

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

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 işlevinin ilk sürümünde, TextStyle döndürmek için bir Block nesnesinin alanını etkinleştirdiniz. Ardından, Block nesnesinin bir örneğini değiştirir ve alt sınıflarını temsil eden nesne kalıplarıyla eşleştirirsiniz. Bu süreçte nesnenin özelliklerini ayıklayabilirsiniz.

Dart analizörü, Block sınıfını kapalı bir sınıf haline getirdiğiniz için her alt sınıfın switch ifadesinde işlenip işlenmediğini kontrol edebilir.

Ayrıca, burada bir switch ifadesi kullanmanız durumunda sonucu doğrudan child öğesine iletebileceğinizi de unutmayın. Daha önce ayrı bir return ifadesi kullanmanız gerekiyordu.

  1. Onay kutusu JSON verilerinin ilk kez oluşturulduğunu görmek için sayfayı anında yeniden yükleyin:

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

13. Tebrikler

Kalıpları, kayıtları, gelişmiş switch ve case'i ve mühürlü sınıfları başarıyla denediniz. Çok fazla bilgi verdiniz ancak bu özelliklerin yüzeyini zar zor çizdiniz. 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 potansiyel olarak iç içe yerleştirilmesi, davranıştaki olasılıkları sonsuz hale getirir. Ancak bunları kolayca görebilirsiniz.

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

Sırada ne var?

  • Dart dokümanlarının Dil bölümündeki kalıplar, kayıtlar, gelişmiş switch ve cases ifadeleri ve sınıf değiştiricilerle ilgili belgelere göz atın.

Referans dokümanları

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

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