1. Pengantar
Dart 3 memperkenalkan pola ke bahasa pemrograman, sebuah kategori besar tata bahasa yang baru. Selain adanya cara baru untuk menulis kode Dart ini, ada juga beberapa peningkatan bahasa lainnya, termasuk kumpulan data untuk menggabungkan berbagai jenis data, pengubah class untuk mengontrol akses, serta ekspresi switch dan pernyataan if-case baru.
Fitur-fitur ini memberi Anda lebih banyak pilihan saat menulis kode Dart. Dalam codelab ini, Anda akan mempelajari cara menggunakannya untuk membuat kode Anda lebih ringkas, sederhana, dan fleksibel.
Codelab ini berasumsi bahwa Anda cukup familier dengan Flutter dan Dart, meskipun tidak wajib. Sebelum memulai, sebaiknya ingat kembali dasar-dasarnya dengan membuka referensi berikut:
Yang akan Anda bangun
Codelab ini berisi cara membuat aplikasi yang menampilkan dokumen JSON di Flutter. Aplikasi ini membuat simulasi JSON yang berasal dari sumber eksternal. JSON berisi data dokumen seperti tanggal diubah, judul, header, dan paragraf. Anda menulis kode untuk menggabungkan data dengan rapi menjadi kumpulan data, sehingga data tersebut dapat ditransfer ke dan diekstrak di mana pun widget Flutter memerlukannya.
Kemudian, gunakan pola untuk membangun widget yang sesuai ketika nilainya cocok dengan pola tersebut. Anda juga akan melihat cara menggunakan pola untuk mengurai data menjadi variabel lokal.
Yang akan Anda pelajari
- Cara membuat data yang menyimpan beberapa nilai dengan jenis yang berbeda.
- Cara menampilkan beberapa nilai dari sebuah fungsi menggunakan data.
- Cara menggunakan pola untuk mencocokkan, memvalidasi, dan mengurai data dari kumpulan data dan objek lainnya.
- Cara mengikat nilai yang polanya cocok ke variabel baru atau yang sudah ada.
- Cara menggunakan kemampuan pernyataan switch, ekspresi switch, dan pernyataan if-case baru.
- Cara memanfaatkan pemeriksaan seluruh kemungkinan untuk memastikan bahwa setiap kasus tertangani di pernyataan switch atau ekspresi switch.
2. Menyiapkan lingkungan Anda
- Instal Flutter SDK.
- Siapkan editor seperti Visual Studio Code (VS Code).
- Lihat langkah-langkah Penyiapan platform untuk setidaknya satu platform target (iOS, Android, Desktop, atau browser web).
3. Membuat project
Sebelum mulai mempelajari pola, kumpulan data, dan fitur baru lainnya, luangkan waktu untuk menyiapkan lingkungan Anda dan project Flutter sederhana yang kodenya akan Anda buat.
Menggunakan Dart
- Untuk memastikan Anda sudah menggunakan Dart 3, jalankan perintah berikut:
flutter channel stable flutter upgrade dart --version # This should print "Dart SDK version: 3.0.0" or higher
Membuat project Flutter
- Gunakan perintah
flutter create
untuk membuat project baru bernamapatterns_codelab
. Tanda--empty
mencegah pembuatan aplikasi penghitung standar di filelib/main.dart
, yang pada akhirnya perlu Anda hapus.
flutter create --empty patterns_codelab
- Kemudian, buka direktori
patterns_codelab
menggunakan VS Code.
code patterns_codelab
Menentukan versi SDK minimum
- Tentukan batas versi SDK untuk project Anda agar menggunakan Dart 3 atau yang lebih baru.
pubspec.yaml
environment:
sdk: ^3.0.0
4. Menyiapkan project
Di langkah ini, Anda akan membuat dua file Dart:
- File
main.dart
yang berisi widget untuk aplikasi. - File
data.dart
yang menyediakan data aplikasi.
Menentukan data untuk aplikasi
- Buat file baru,
lib/data.dart
, lalu tambahkan kode berikut ke sana:
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"
}
]
}
''';
Bayangkan suatu program yang menerima data dari sumber eksternal, seperti streaming I/O atau permintaan HTTP. Dalam codelab ini, Anda akan menyederhanakan kasus penggunaan yang lebih realistis tersebut dengan meniru data JSON yang masuk dengan string multibaris dalam variabel documentJson
.
Data JSON ditentukan di class Document
, dan dalam codelab ini nanti, Anda akan menambahkan fungsi yang menampilkan data dari JSON yang diurai. Class ini menentukan dan melakukan inisialisasi pada kolom _json
di konstruktornya.
Menjalankan aplikasi
Perintah flutter create
membuat file lib/main.dart
sebagai bagian dari struktur file Flutter default.
- Untuk membuat titik awal aplikasi, ganti konten
main.dart
dengan kode berikut:
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,
Key? key,
}) : super(key: key);
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: const Text('Title goes here'),
),
body: Column(
children: [
Center(
child: Text('Body goes here'),
),
],
),
);
}
}
Tambahkan dua widget berikut ke aplikasi:
DocumentApp
menyiapkan versi terbaru Desain Material untuk penerapan tema UI.DocumentScreen
menyediakan tata letak visual halaman menggunakan widgetScaffold
.
- Untuk memastikan semuanya berfungsi lancar, jalankan aplikasi di mesin host dengan mengklik Run and Debug:
- Secara default, Flutter akan memilih platform target mana pun yang tersedia. Untuk mengubah platform target, pilih platform saat ini di Status Bar:
Anda akan melihat frame kosong dengan elemen title
dan body
yang ditentukan di widget DocumentScreen
:
5. Membuat dan menampilkan kumpulan data
Di langkah ini, Anda akan menggunakan kumpulan data untuk menampilkan beberapa nilai dari panggilan fungsi. Lalu, Anda akan memanggil fungsi tersebut di widget DocumentScreen
agar dapat mengakses nilai dan mencerminkannya di UI.
Membuat dan menampilkan data
- Di
data.dart
, tambahkan fungsi baru ke class Dokumen bernamagetMetadata
yang menampilkan data:
lib/data.dart
(String, {DateTime modified}) getMetadata() {
var title = "My Document";
var now = DateTime.now();
return (title, modified: now);
}
Jenis nilai yang ditampilkan untuk fungsi ini adalah data yang berisi dua kolom, satu dengan jenis String
, dan satunya lagi dengan jenis DateTime
.
Pernyataan return membentuk data baru dengan mengapit dua nilai tersebut dengan tanda kurung, (title, modified: now)
.
Kolom pertama adalah posisi dan tanpa nama, lalu kolom kedua bernama modified
.
Mengakses kolom kumpulan data
- Di widget
DocumentScreen
, panggilgetMetadata()
di metodebuild
untuk mendapatkan kumpulan data Anda dan mengakses nilainya:
lib/main.dart
@override
Widget build(BuildContext context) {
var metadataRecord = document.getMetadata();
return Scaffold(
appBar: AppBar(
title: Text(metadataRecord.$1),
),
body: Column(
children: [
Center(
child: Text(
'Last modified ${metadataRecord.modified}',
),
),
],
),
);
}
Fungsi getMetadata()
menampilkan data, yang ditetapkan ke variabel lokal metadataRecord
. Kumpulan data adalah cara singkat dan mudah untuk menampilkan beberapa nilai dari satu panggilan fungsi lalu menetapkannya ke sebuah variabel.
Untuk mengakses satuan kolom yang ada dalam kumpulan data, Anda dapat menggunakan sintaksis pengambil bawaan dari kumpulan data tersebut.
- Untuk mendapatkan kolom posisi (kolom tanpa nama, seperti
title
), gunakan pengambil$<num>
di kumpulan data tersebut. Tindakan ini hanya akan menampilkan kolom tanpa nama. - Kolom bernama seperti
modified
tidak memiliki pengambil posisi, jadi Anda dapat langsung menggunakan namanya, sepertimetadataRecord.modified
.
Guna menentukan nama pengambil untuk kolom posisi, mulailah dengan $1
lalu lewati kolom bernama. Contoh:
var record = (named: ‘v', ‘y', named2: ‘x', ‘z');
print(record.$1); // prints y
print(record.$2) // prints z
- Lakukan hot reload untuk melihat nilai JSON yang ditampilkan di aplikasi. Plugin Dart VS Code akan melakukan hot reload setiap kali Anda menyimpan file.
Anda dapat melihat bahwa setiap kolom memang mempertahankan jenisnya.
- Metode
Text()
mengambil String sebagai argumen pertamanya. - Kolom
modified
adalah DateTime, dan dikonversi menjadiString
menggunakan interpolasi string.
Cara lain dengan jenis yang aman untuk menampilkan berbagai jenis data adalah dengan menentukan class, yang bentuknya lebih panjang.
6. Mencocokkan dan mengurai dengan pola
Kumpulan data dapat secara efisien mengumpulkan berbagai jenis data dan dengan mudah menyalurkannya. Sekarang, tingkatkan kode Anda menggunakan pola.
Pola mewakili struktur yang dapat diambil oleh satu atau beberapa nilai, seperti cetak biru. Pola akan membandingkan dengan nilai sebenarnya untuk menentukan apakah ada kecocokan.
Beberapa pola, jika menemukan kecocokan, akan mengurai nilai yang cocok dengan mengambil data dari sana. Penguraian memungkinkan Anda mengekstrak nilai dari sebuah objek untuk ditetapkan ke variabel lokal, atau menjalankan pencocokan lebih lanjut.
Mengurai data ke variabel lokal
- Faktorkan ulang metode
build
dariDocumentScreen
untuk memanggilgetMetadata()
dan menggunakannya untuk melakukan inisialisasi pada deklarasi variabel pola:
lib/main.dart
@override
Widget build(BuildContext context) {
var (title, :modified) = document.getMetadata(); // New
return Scaffold(
appBar: AppBar(
title: Text(title), // New
),
body: Column(
children: [
Center(
child: Text(
'Last modified $modified', // New
),
),
],
),
);
}
Pola kumpulan data (title, :modified)
berisi dua pola variabel yang cocok dengan kolom kumpulan data yang ditampilkan oleh getMetadata()
.
- Ekspresinya cocok dengan subpola tersebut karena hasilnya adalah data dengan dua kolom, salah satunya bernama
modified
. - Karena ada kecocokan, pola deklarasi variabel mengurai ekspresi tersebut, guna mengakses nilainya dan mengikatnya ke variabel lokal baru dari jenis dan nama yang sama,
String title
danDateTime modified
.
Sintaksis pola variabel :modified
merupakan bentuk singkat dari modified: modified
. Jika ingin variabel lokal baru dengan nama yang berbeda, Anda dapat menulis modified: localModified
.
- Lakukan hot reload untuk melihat hasil yang sama seperti di langkah sebelumnya. Perilakunya sama persis; Anda hanya mempersingkat kode tersebut.
7. Menggunakan pola untuk mengekstrak data
Dalam konteks tertentu, pola tidak hanya mencocokkan dan mengurai tetapi juga dapat membuat keputusan tentang apa yang dilakukan kode, berdasarkan apakah pola tersebut cocok atau tidak. Pola ini disebut pola refutable.
Pola deklarasi variabel yang Anda gunakan di langkah sebelumnya merupakan pola irrefutable: nilainya harus cocok dengan pola, jika tidak, akan timbul error dan penguraian tidak akan dilakukan. Coba pikirkan tugas atau deklarasi variabel apa pun; Anda tidak dapat menetapkan nilai ke variabel jika jenisnya tidak sama.
Di sisi lain, pola refutable digunakan dalam konteks alur kontrol:
- Pola tersebut memperkirakan bahwa beberapa nilai yang dibandingkan tidak akan cocok.
- Pola tersebut dimaksudkan untuk memengaruhi alur kontrol, berdasarkan apakah nilainya cocok atau tidak.
- Pola tersebut tidak mengganggu eksekusi dengan menampilkan error jika tidak cocok, tetapi hanya akan meneruskan ke pernyataan berikutnya.
- Pola tersebut dapat mengurai dan mengikat variabel yang hanya dapat digunakan jika ada kecocokan
Membaca nilai JSON tanpa pola
Di bagian ini, Anda akan membaca data tanpa pencocokan pola guna melihat bagaimana pola dapat membantu Anda bekerja dengan data JSON.
- Ganti
getMetadata()
versi sebelumnya dengan versi yang membaca nilai dari peta_json
. Salin dan tempel versigetMetadata()
ini ke classDocument
:
lib/data.dart
(String, {DateTime modified}) getMetadata() {
if (_json.containsKey('metadata')) {
var metadataJson = _json['metadata'];
if (metadataJson is Map) {
var title = metadataJson['title'] as String;
var localModified = DateTime.parse(metadataJson['modified'] as String);
return (title, modified: localModified);
}
}
throw const FormatException('Unexpected JSON');
}
Kode ini memvalidasi bahwa data terstruktur dengan benar tanpa menggunakan pola. Di langkah berikutnya, Anda akan menggunakan pencocokan pola untuk melakukan validasi yang sama menggunakan lebih sedikit kode. Perlu dilakukan tiga pemeriksaan sebelum melakukan hal lain:
- JSON berisi struktur data yang Anda harapkan:
if (_json.containsKey('metadata'))
- Data tersebut merupakan jenis yang Anda harapkan:
if (metadataJson is Map)
- Data tersebut bukan null, yang secara implisit telah dipastikan di pemeriksaan sebelumnya.
Membaca nilai JSON menggunakan pola peta
Dengan pola refutable, Anda dapat memverifikasi bahwa JSON memiliki struktur yang diharapkan menggunakan pola peta.
- Ganti
getMetadata()
versi sebelumnya dengan kode ini:
lib/data.dart
(String, {DateTime modified}) getMetadata() {
if (_json
case {
'metadata': {
'title': String title,
'modified': String localModified,
}
}) {
return (title, modified: DateTime.parse(localModified));
} else {
throw const FormatException('Unexpected JSON');
}
}
Di sini, Anda melihat pernyataan if jenis baru (diperkenalkan di Dart 3), if-case. Konten kasus hanya dijalankan jika polanya cocok dengan data di _json
. Kecocokan ini menjalankan pemeriksaan yang sama seperti yang Anda tulis di versi pertama getMetadata()
guna memvalidasi JSON yang masuk. Kode ini memvalidasi hal berikut:
_json
merupakan jenis Peta._json
berisi kuncimetadata
._json
bukan null._json['metadata']
juga merupakan jenis Peta._json['metadata']
berisi kuncititle
danmodified
.title
danlocalModified
merupakan string, bukan null.
Jika nilainya tidak cocok, pola akan menolak (menolak untuk melanjutkan eksekusi) dan beralih ke klausa else
. Jika pencocokan berhasil, pola tersebut akan mengurai nilai title
dan modified
dari peta, lalu mengikatnya ke variabel lokal baru.
Untuk melihat daftar pola lengkap, buka tabel di bagian Pola di spesifikasi fitur.
8. Menyiapkan aplikasi untuk lebih banyak pola
Sejauh ini, Anda menangani data JSON bagian metadata
. Di langkah ini, Anda akan sedikit mempertajam logika bisnis Anda guna menangani data dalam daftar blocks
lalu merendernya ke dalam aplikasi.
{
"metadata": {
// ...
},
"blocks": [
{
"type": "h1",
"text": "Chapter 1"
},
// ...
]
}
Membuat class yang menyimpan data
- Tambahkan class baru,
Block
, kedata.dart
, yang digunakan untuk membaca dan menyimpan data untuk salah satu blok di data JSON.
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': var type, 'text': var text}) {
return Block(type, text);
} else {
throw const FormatException('Unexpected JSON format');
}
}
}
Konstruktor factory fromJson()
menggunakan if-case yang sama dengan pola peta yang pernah Anda lihat sebelumnya.
Perhatikan bahwa json
cocok dengan pola peta, meskipun salah satu kuncinya, checked
, tidak diperhitungkan di pola tersebut. Pola peta mengabaikan semua entri dalam objek peta yang secara eksplisit tidak diperhitungkan di pola tersebut.
Menampilkan daftar objek Blok
- Selanjutnya, tambahkan fungsi baru,
getBlocks()
, ke classDocument
.getBlocks()
akan mengurai JSON menjadi beberapa instance classBlock
dan menampilkan daftar blok untuk dirender di UI Anda:
lib/data.dart
List<Block> getBlocks() {
if (_json case {'blocks': List blocksJson}) {
return <Block>[
for (var blockJson in blocksJson) Block.fromJson(blockJson)
];
} else {
throw const FormatException('Unexpected JSON format');
}
}
Fungsi getBlocks()
menampilkan daftar objek Block
, yang nantinya Anda gunakan untuk membangun UI. Pernyataan if-case yang familier memvalidasi dan mentransmisikan nilai metadata blocks
ke List
baru bernama blocksJson
(tanpa adanya pola, Anda memerlukan metode toList()
untuk melakukan transmisi).
Literal daftar berisi koleksi agar dapat mengisi daftar baru dengan objek Block
.
Bagian ini tidak memperkenalkan fitur terkait pola apa pun yang belum pernah Anda coba dalam codelab ini. Di langkah berikutnya, Anda akan bersiap merender item daftar di UI.
9. Menggunakan pola untuk menampilkan dokumen
Anda kini berhasil mengurai dan merekomposisi data JSON, menggunakan pernyataan if-case dan pola refutable. Namun, if-case hanyalah salah satu peningkatan untuk mengontrol struktur alur yang masuk bersama pola. Sekarang Anda akan menerapkan pengetahuan tentang pola refutable ke pernyataan switch.
Mengontrol apa yang dirender menggunakan pola dengan pernyataan switch
- Di
main.dart
, buat widget baru,BlockWidget
, yang akan menentukan gaya setiap blok berdasarkan kolomtype
-nya.
lib/main.dart
class BlockWidget extends StatelessWidget {
final Block block;
const BlockWidget({
required this.block,
Key? key,
}) : super(key: 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,
),
);
}
}
Pernyataan switch di metode build
mengeksekusi switch di kolom type
dari objek block
.
- Pernyataan kasus pertama menggunakan pola string konstan. Polanya cocok jika
block.type
setara dengan nilai konstanh1
. - Pernyataan kasus kedua menggunakan pola logical-or dengan dua pola string konstan sebagai subpolanya. Polanya cocok jika
block.type
sesuai dengan subpolap
ataucheckbox
.
- Kasus terakhir adalah pola karakter pengganti,
_
. Karakter pengganti di kasus switch cocok dengan yang lain. Karakter ini berperilaku sama seperti klausadefault
, yang masih diizinkan dalam pernyataan switch (hanya saja bentuknya sedikit lebih panjang).
Karakter pengganti dapat digunakan di mana pun pola diizinkan—misalnya, di pola deklarasi variabel: var (title, _) = document.getMetadata();
Dalam konteks ini, karakter pengganti tidak mengikat variabel apa pun. Pola ini menghapus kolom kedua.
Di bagian berikutnya, Anda akan mempelajari fitur switch lainnya setelah menampilkan objek Block
.
Menampilkan konten dokumen
Buat variabel lokal yang berisi daftar objek Block
dengan memanggil getBlocks()
di metode build
widget DocumentScreen
.
- Ganti metode
build
yang sudah ada diDocumentationScreen
dengan versi ini:
lib/main.dart
@override
Widget build(BuildContext context) {
var (title, :modified) = document.getMetadata();
var blocks = document.getBlocks(); // New
return Scaffold(
appBar: AppBar(
title: Text(title),
),
body: Column(
children: [
// New
Text('Last modified: $modified'),
Expanded(
child: ListView.builder(
itemCount: blocks.length,
itemBuilder: (context, index) {
return BlockWidget(block: blocks[index]);
},
),
),
],
),
);
}
Baris BlockWidget(block: blocks[index])
membentuk widget BlockWidget
untuk setiap item dalam daftar blok yang ditampilkan dari metode getBlocks()
.
- Jalankan aplikasi, lalu Anda akan melihat blok tersebut muncul di layar:
10. Menggunakan ekspresi switch
Pola menambahkan banyak kemampuan ke switch
dan case
. Untuk membuatnya dapat digunakan di lebih banyak tempat, Dart memiliki ekspresi switch. Serangkaian kasus dapat langsung memberikan nilai ke pernyataan return atau tugas variabel.
Mengonversi pernyataan switch menjadi ekspresi switch
Penganalisis Dart memberikan bantuan untuk memudahkan Anda membuat perubahan pada kode.
- Arahkan kursor ke pernyataan switch dari bagian sebelumnya.
- Klik ikon bola lampu untuk melihat bantuan yang tersedia.
- Pilih bantuan Convert to switch expression.
Versi baru kode ini terlihat seperti ini:
TextStyle? textStyle;
textStyle = switch (block.type) {
'h1' => Theme.of(context).textTheme.displayMedium,
'p' || 'checkbox' => Theme.of(context).textTheme.bodyMedium,
_ => Theme.of(context).textTheme.bodySmall
};
Ekspresi switch terlihat mirip dengan pernyataan switch, tetapi ekspresi ini menghapus kata kunci case
dan menggunakan =>
untuk memisahkan pola dari konten kasus. Tidak seperti pernyataan switch, ekspresi switch menampilkan nilai dan dapat digunakan di mana pun ekspresi dapat digunakan.
11. Menggunakan pola objek
Dart adalah bahasa pemrograman berorientasi objek, jadi pola ini berlaku untuk semua objek. Di langkah ini, Anda akan mengeksekusi switch di pola objek dan mengurai properti objek untuk meningkatkan logika rendering tanggal di UI Anda.
Mengekstrak properti dari pola objek
Di bagian ini, Anda akan meningkatkan tampilan tanggal terakhir diubah menggunakan pola.
- Tambahkan metode
formatDate
kemain.dart
:
lib/main.dart
String formatDate(DateTime dateTime) {
var today = DateTime.now();
var difference = dateTime.difference(today);
return switch (difference) {
Duration(inDays: 0) => 'today',
Duration(inDays: 1) => 'tomorrow',
Duration(inDays: -1) => 'yesterday',
Duration(inDays: var days, isNegative: true) => '${days.abs()} days ago',
Duration(inDays: var days) => '$days days from now',
};
}
Metode ini menampilkan ekspresi switch yang dieksekusi di nilai difference
, objek Duration
. Metode ini mewakili jangka waktu antara nilai today
dan modified
dari data JSON.
Setiap kasus ekspresi switch menggunakan pola objek yang cocok dengan memanggil pengambil di properti objek inDays
dan isNegative
. Sintaksisnya mungkin terlihat seperti sedang membentuk objek Durasi, tetapi sebenarnya sedang mengakses kolom pada objek difference
.
Tiga kasus pertama menggunakan subpola konstan 0
, 1
, dan -1
agar cocok dengan properti objek inDays
dan menampilkan string yang sesuai.
Dua kasus terakhir menangani durasi selain hari ini, kemarin, dan besok:
- Jika properti
isNegative
cocok dengan pola konstan booleantrue
, artinya tanggal diubahnya ada pada masa lampau, days ago akan ditampilkan. - Jika kasus tersebut tidak menemukan perbedaannya, berarti durasinya harus berupa jumlah hari positif (tanpa perlu memverifikasi secara eksplisit dengan
isNegative: false
), jadi tanggal diubahnya ada pada masa mendatang dan menampilkan days from now.
Menambahkan logika pemformatan untuk minggu
- Tambahkan dua kasus baru ke fungsi pemformatan Anda untuk mengidentifikasi durasi yang lebih lama dari 7 hari, sehingga UI dapat menampilkannya sebagai weeks:
lib/main.dart
String formatDate(DateTime dateTime) {
var today = DateTime.now();
var difference = dateTime.difference(today);
return switch (difference) {
Duration(inDays: 0) => 'today',
Duration(inDays: 1) => 'tomorrow',
Duration(inDays: -1) => 'yesterday',
Duration(inDays: var days) when days > 7 => '${days ~/ 7} weeks from now', // New
Duration(inDays: var days) when days < -7 => '${days.abs() ~/ 7} weeks ago', // New
Duration(inDays: var days, isNegative: true) => '${days.abs()} days ago',
Duration(inDays: var days) => '$days days from now',
};
}
Kode ini memperkenalkan klausa guard:
- Klausa guard menggunakan kata kunci
when
setelah sebuah pola kasus. - Klausa ini dapat digunakan dalam if-case, pernyataan switch, dan ekspresi switch.
- Klausa ini hanya menambahkan kondisi ke sebuah pola setelah dicocokkan.
- Jika hasil evaluasi klausa guard salah, seluruh pola akan ditolak, dan eksekusi akan dilanjutkan ke kasus berikutnya.
Menambahkan tanggal dengan pemformatan baru ke UI
- Terakhir, update metode
build
diDocumentScreen
untuk menggunakan fungsiformatDate
:
lib/main.dart
@override
Widget build(BuildContext context) {
var (title, :modified) = document.getMetadata();
var formattedModifiedDate = formatDate(modified); // New
var blocks = document.getBlocks();
return Scaffold(
appBar: AppBar(
title: Text(title),
),
body: Column(
children: [
Text('Last modified: $formattedModifiedDate'), // New
Expanded(
child: ListView.builder(
itemCount: blocks.length,
itemBuilder: (context, index) =>
BlockWidget(block: blocks[index]),
),
),
],
),
);
}
- Lakukan hot reload untuk melihat perubahan di aplikasi Anda:
12. Mengunci class untuk eksekusi switch menyeluruh
Perhatikan bahwa Anda tidak menggunakan kasus default atau karakter pengganti pada switch terakhir. Meskipun selalu menyertakan kasus untuk nilai yang mungkin gagal adalah praktik yang bagus, hal ini tidak wajib dilakukan untuk contoh sederhana seperti ini karena Anda tahu bahwa kasus yang ditetapkan akan mempertimbangkan semua kemungkinan nilai yang dapat diambil inDays
.
Jika setiap kasus di pernyataan switch telah ditangani, ini disebut switch menyeluruh. Misalnya, eksekusi switch di jenis bool
dianggap menyeluruh saat proses tersebut memiliki kasus untuk true
dan false
. Eksekusi switch di jenis enum
juga bersifat menyeluruh ketika ada kasus untuk setiap nilai enum, karena enum mewakili jumlah tetap dari nilai konstan.
Dart 3 memperluas pemeriksaan seluruh kemungkinan ke objek dan hierarki class dengan pengubah class sealed
baru. Faktorkan ulang class Block
Anda sebagai superclass yang terkunci.
Membuat subclass
- Di
data.dart
, buat tiga class baru—HeaderBlock
,ParagraphBlock
, danCheckboxBlock
—yang memperluasBlock
:
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);
}
Setiap class ini sesuai dengan nilai type
yang berbeda dari JSON asli: 'h1'
, 'p'
, dan 'checkbox'
.
Mengunci superclass
- Tandai class
Block
sebagaisealed
. Kemudian, faktorkan ulang if-case sebagai ekspresi switch yang menampilkan subclass yang sesuai dengantype
yang ditetapkan di JSON:
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'),
};
}
}
Kata kunci sealed
adalah pengubah class yang artinya Anda hanya dapat memperluas atau menerapkan class ini di library yang sama. Karena sudah mengetahui subjenis class ini, penganalisis akan melaporkan error jika switch gagal mencakup salah satunya dan tidak menyeluruh.
Menggunakan ekspresi switch untuk menampilkan widget
- Update class BlockWidget di
main.dart
dengan ekspresi switch yang menggunakan pola objek untuk setiap kasus:
lib/main.dart
class BlockWidget extends StatelessWidget {
final Block block;
const BlockWidget({
required this.block,
Key? key,
}) : super(key: key);
@override
Widget build(BuildContext context) {
return Container(
margin: const EdgeInsets.all(8),
child: switch (block) {
HeaderBlock(:var text) => Text(
text,
style: Theme.of(context).textTheme.displayMedium,
),
ParagraphBlock(:var text) => Text(text),
CheckboxBlock(:var text, :var isChecked) => Row(
children: [
Checkbox(value: isChecked, onChanged: (_) {}),
Text(text),
],
),
},
);
}
}
Di versi pertama BlockWidget
, Anda mengeksekusi switch di kolom objek Block
untuk menampilkan TextStyle
. Sekarang Anda mengeksekusi switch pada instance objek Block
itu sendiri dan mencocokkan dengan pola objek yang mewakili subclass-nya, yang mengekstrak properti objek dalam prosesnya.
Penganalisis Dart dapat memeriksa bahwa setiap subclass ditangani di ekspresi switch karena Anda membuat Block
sebagai class terkunci.
Perhatikan juga bahwa menggunakan ekspresi switch di sini akan memungkinkan Anda meneruskan hasil langsung ke elemen child
, dibandingkan pernyataan return terpisah yang diperlukan sebelumnya.
- Lakukan hot reload untuk melihat kotak centang data JSON dirender untuk pertama kalinya:
13. Selamat
Anda telah berhasil bereksperimen dengan pola, kumpulan data, switch dan kasus yang ditingkatkan, serta class terkunci. Anda telah mendapatkan berbagai pengetahuan—tetapi masih banyak hal lain tentang fitur-fitur ini yang perlu dipelajari. Untuk mengetahui informasi selengkapnya tentang pola, buka spesifikasi fitur.
Berbagai jenis pola, berbagai konteks kemunculannya, dan potensi subpola bertingkat membuat kemungkinan dalam hal perilaku seperti tidak ada batasnya. Namun, itu semua terlihat dengan jelas.
Anda dapat memikirkan berbagai cara untuk menampilkan konten di Flutter menggunakan pola. Dengan menggunakan pola, Anda dapat mengekstrak data secara aman untuk membangun UI dalam beberapa baris kode.
Apa selanjutnya?
- Lihat dokumentasi tentang pola, kumpulan data, switch dan kasus yang ditingkatkan, serta pengubah class di bagian Bahasa dalam dokumentasi Dart.
Dokumen referensi
Lihat contoh lengkap di repositori.
Guna mengetahui spesifikasi mendetail untuk setiap fitur baru, lihat dokumen desain asli: