Flutter dan Dart 3: Records, Patterns, serta Pertanyaan Interview Tingkat Lanjut
Panduan lengkap fitur Dart 3 meliputi records, pattern matching, dan sealed classes beserta contoh kode praktis dan pertanyaan interview teknis Flutter tingkat lanjut tahun 2026.

Dart 3 telah membawa perubahan mendasar dalam ekosistem pengembangan Flutter. Tiga fitur utama yang diperkenalkan -- records, pattern matching, dan sealed classes -- bukan sekadar pemanis sintaksis. Ketiganya mengubah cara pengembang menyusun struktur data, mengelola state, dan memastikan keamanan tipe pada waktu kompilasi. Bagi para pengembang yang mempersiapkan interview teknis Flutter, penguasaan ketiga konsep ini kini menjadi faktor pembeda yang signifikan. Artikel ini mengulas setiap fitur secara mendalam, menunjukkan cara menggabungkannya dalam skenario nyata, serta menyajikan pertanyaan-pertanyaan yang kerap muncul dalam sesi interview.
Dart 3 menghadirkan records (tipe data struktural ringan), pattern matching yang exhaustive, dan sealed classes. Ketiga mekanisme ini memungkinkan pemodelan state yang kompleks dengan keamanan tipe yang dijamin oleh compiler, tanpa memerlukan library pihak ketiga.
Records: Tipe Data Struktural yang Ringan
Sebelum Dart 3 hadir, mengembalikan beberapa nilai dari sebuah fungsi mengharuskan pengembang membuat class khusus, menggunakan Map, atau mengandalkan Tuple dari library eksternal. Records menyelesaikan masalah ini secara native. Sebuah record merupakan tipe anonim, immutable, dan memiliki structural equality yang dapat memuat field posisional maupun bernama.
Record posisional cocok untuk return value sederhana di mana urutan sudah cukup untuk mengidentifikasi setiap nilai. Untuk struktur yang lebih kompleks, field bernama meningkatkan keterbacaan secara signifikan dan mengurangi risiko tertukarnya parameter.
// Returning multiple values with a positional record
(String, int) parseUserInput(String raw) {
final parts = raw.split(':');
return (parts[0].trim(), int.parse(parts[1]));
}
// Named fields improve readability for complex returns
({String name, String email, bool isVerified}) fetchUserProfile(String id) {
// Simulated database lookup
return (
name: 'Alice Chen',
email: 'alice@example.com',
isVerified: true,
);
}Aspek fundamental dari records terletak pada structural equality yang dimilikinya. Dua record dengan nilai yang sama akan dianggap equal tanpa perlu mengimplementasikan operator == atau menggunakan package seperti equatable. Sifat ini sangat menyederhanakan penulisan unit test dan perbandingan state.
// Structural equality — no need for custom == operator
void main() {
final a = (1, 'hello');
final b = (1, 'hello');
print(a == b); // true — records compare by value
// Named fields also support equality
final profile1 = (name: 'Alice', role: 'admin');
final profile2 = (name: 'Alice', role: 'admin');
print(profile1 == profile2); // true
}Records tidak memiliki identitas objek. Tidak dimungkinkan untuk menambahkan method, mengimplementasikan interface, atau memperluas melalui inheritance. Apabila data perlu disertai logika bisnis, class tetap menjadi pilihan yang tepat. Records dirancang untuk transportasi data, bukan enkapsulasi perilaku.
Pattern Matching: Destructuring untuk Kode yang Lebih Jelas
Pattern matching di Dart 3 melampaui sekadar destructuring. Fitur ini memungkinkan pengembang mengekstrak nilai, memverifikasi tipe, dan menerapkan guard clause, semuanya dalam sintaksis deklaratif yang diverifikasi pada waktu kompilasi.
Destructuring record menghilangkan akses berulang melalui indeks atau nama field dalam kode. Pattern list dengan operator rest (...) menawarkan cara yang elegan untuk mengekstrak elemen-elemen pertama dari sebuah koleksi.
// Destructuring a record with pattern matching
void main() {
final (name, email, isVerified) = fetchUserProfile('u-123');
// name, email, isVerified are now local variables
// List patterns with rest operator
final scores = [98, 87, 92, 76, 84];
final [first, second, ...remaining] = scores;
print('Top two: $first, $second'); // 98, 87
print('Others: $remaining'); // [92, 76, 84]
}Switch expression di Dart 3 merupakan evolusi signifikan dari switch statement tradisional. Switch expression mengembalikan nilai, mendukung guard clause dengan kata kunci when, dan mampu menangani pattern matching berdasarkan tipe objek. Compiler memverifikasi bahwa semua kasus telah ditangani, sehingga menghilangkan satu kategori bug yang berkaitan dengan kasus yang terlewat.
// Switch expression with guard clauses
String classifyScore(int score) => switch (score) {
>= 90 => 'Excellent',
>= 80 => 'Good',
>= 70 => 'Average',
>= 60 => 'Below Average',
_ => 'Needs Improvement',
};
// Object pattern matching with type checking
String describeValue(Object value) => switch (value) {
int n when n < 0 => 'Negative integer: $n',
int n => 'Positive integer: $n',
String s when s.isEmpty => 'Empty string',
String s => 'String of length ${s.length}',
List l when l.isEmpty => 'Empty list',
List l => 'List with ${l.length} elements',
_ => 'Unknown type',
};Switch expression ini secara efektif menggantikan rangkaian if-else dalam berbagai konteks, khususnya untuk pengelolaan state dan pembangunan widget dalam aplikasi Flutter.
Sealed Classes: Exhaustiveness yang Dijamin Compiler
Sealed classes mendefinisikan himpunan subtipe yang tertutup. Compiler mengetahui seluruh varian yang mungkin, sehingga dapat memverifikasi bahwa setiap switch expression mencakup semua kasus. Subtipe yang terlewat akan memicu error kompilasi, bukan bug di production.
Mekanisme ini sangat tepat untuk memodelkan berbagai state dalam sebuah aplikasi. Contoh berikut mendefinisikan empat state yang mungkin dalam alur autentikasi.
// Sealed class defining all possible authentication states
sealed class AuthState {}
class AuthInitial extends AuthState {}
class AuthLoading extends AuthState {}
class AuthAuthenticated extends AuthState {
final String userId;
final String displayName;
AuthAuthenticated({required this.userId, required this.displayName});
}
class AuthError extends AuthState {
final String message;
final int? statusCode;
AuthError({required this.message, this.statusCode});
}Ketika sebuah widget mengonsumsi state tersebut, exhaustive switch menjamin bahwa setiap varian ditangani. Apabila state kelima ditambahkan di kemudian hari (misalnya AuthMfaRequired), compiler akan segera menandai semua bagian kode yang perlu diperbarui.
// Exhaustive switch — compiler error if a case is missing
Widget buildAuthUI(AuthState state) => switch (state) {
AuthInitial() => const LoginPrompt(),
AuthLoading() => const CircularProgressIndicator(),
AuthAuthenticated(
displayName: final name
) => Text('Welcome, $name'),
AuthError(
message: final msg,
statusCode: final code,
) => ErrorBanner(
message: msg,
code: code,
),
};Kombinasi sealed class dan exhaustive switch menghilangkan kebutuhan akan clause default atau wildcard _ yang bersifat generik. Setiap jalur eksekusi bersifat eksplisit, yang meningkatkan maintainability sekaligus menjadi dokumentasi implisit dari kode itu sendiri.
Menggabungkan Records, Patterns, dan Sealed Classes
Kekuatan sesungguhnya dari Dart 3 terungkap ketika ketiga fitur ini digunakan secara bersamaan. Sebuah tipe result generik yang menggunakan sealed class dapat mengenkapsulasi baik data sukses (dengan metadata dalam bentuk record) maupun data error.
// Generic sealed result type for API operations
sealed class ApiResult<T> {}
class ApiSuccess<T> extends ApiResult<T> {
final T data;
final ({int statusCode, Map<String, String> headers}) metadata;
ApiSuccess(this.data, {required this.metadata});
}
class ApiFailure<T> extends ApiResult<T> {
final String error;
final int? statusCode;
ApiFailure(this.error, {this.statusCode});
}Pada sisi widget, pattern matching memungkinkan penanganan setiap kombinasi state dan data secara presisi. Nested pattern mengekstrak metadata dari record sekaligus memfilter berdasarkan kode HTTP, sementara guard clause membedakan antara list kosong dan list yang berisi data.
// Consuming the sealed result with pattern matching
Widget buildProductList(ApiResult<List<Product>> result) => switch (result) {
ApiSuccess(
data: final products,
metadata: (statusCode: 200, headers: _),
) when products.isNotEmpty => ListView.builder(
itemCount: products.length,
itemBuilder: (_, i) => ProductCard(products[i]),
),
ApiSuccess(data: final products) when products.isEmpty =>
const EmptyState(message: 'No products found'),
ApiFailure(statusCode: 401) =>
const AuthExpiredBanner(),
ApiFailure(error: final msg) =>
ErrorDisplay(message: msg),
};Pola ini menghilangkan pengecekan tipe secara manual, type casting, dan branch else yang tidak teruji. Setiap jalur eksekusi memiliki tipe yang jelas dan terverifikasi pada waktu kompilasi.
Pattern matching juga berlaku untuk ekstraksi data yang aman dari struktur JSON, sebuah kasus penggunaan yang sangat umum saat memproses respons API.
// If-case for null-safe extraction
void processUser(Map<String, dynamic> json) {
if (json case {'name': String name, 'age': int age}) {
// name and age are non-nullable here
print('$name is $age years old');
} else {
print('Invalid user data');
}
}Konstruksi if-case secara bersamaan memverifikasi keberadaan key, memeriksa tipe datanya, dan mengekstrak nilai ke dalam variabel lokal yang non-nullable. Pola ini secara efektif menggantikan rangkaian pemanggilan containsKey dan type casting manual yang rentan terhadap error.
Siap menguasai wawancara Flutter Anda?
Berlatih dengan simulator interaktif, flashcards, dan tes teknis kami.
Pertanyaan Interview Teknis Tingkat Lanjut
Pertanyaan-pertanyaan berikut sering diajukan dalam interview teknis Flutter untuk mengevaluasi pemahaman mendalam tentang fitur-fitur Dart 3.
Apa perbedaan antara record dan class di Dart 3?
Record merupakan tipe anonim dengan structural equality yang bersifat immutable secara bawaan. Dua record dengan nilai yang sama dianggap equal. Sebaliknya, class memiliki identitas objek: dua instance dengan nilai yang sama tidak dianggap equal secara default (kecuali operator == dan hashCode diimplementasikan secara eksplisit). Records tidak dapat memiliki method, inheritance, maupun mengimplementasikan interface. Records sebaiknya digunakan untuk transportasi data dan pengembalian nilai ganda, sedangkan class tetap lebih tepat untuk logika bisnis yang memerlukan enkapsulasi.
Mengapa sealed classes lebih disukai dibandingkan enum untuk pengelolaan state?
Enum tidak dapat membawa data yang spesifik untuk setiap varian. Sebuah AuthState dalam bentuk enum tidak memungkinkan pengaitan userId hanya pada varian authenticated. Sealed classes menggabungkan exhaustiveness checking (seperti enum) dengan kemampuan membawa data heterogen per varian. Compiler menjamin bahwa setiap subtipe ditangani dalam switch expression.
Bagaimana pattern matching berinteraksi dengan null safety?
Pattern matching di Dart 3 terintegrasi secara native dengan sistem null safety. Konstruksi if-case dengan type pattern mengekstrak variabel yang dijamin non-nullable tanpa perlu menggunakan operator bang (!). Map pattern memverifikasi keberadaan key dan tipe nilainya secara bersamaan, sehingga menghilangkan akses yang tidak aman terhadap struktur data dinamis.
Apa keunggulan switch expression dibandingkan switch statement?
Switch expression mengembalikan nilai, sehingga dapat digunakan dalam assignment, return value, dan parameter constructor. Switch expression memaksa exhaustiveness: compiler menolak kompilasi apabila ada kasus yang tidak tercakup. Switch statement tradisional tidak menawarkan kedua jaminan tersebut. Dalam konteks Flutter, switch expression sangat sesuai untuk pola builder di mana setiap state harus mengembalikan sebuah widget.
Bagaimana pengaruh records Dart 3 terhadap performa aplikasi?
Records dialokasikan di heap seperti halnya class, namun structural equality yang dimilikinya menghindari alokasi objek perantara untuk perbandingan. Compiler dapat mengoptimalkan record posisional berukuran kecil secara lebih agresif dibandingkan class yang setara. Dalam praktiknya, dampak terhadap performa dapat diabaikan untuk sebagian besar kasus penggunaan. Keuntungan utama terletak pada pengurangan boilerplate dan peningkatan keterbacaan, yang pada akhirnya mengurangi bug dan mempercepat proses pengembangan.
Strategi Migrasi ke Dart 3
Bagi tim yang mengelola codebase yang sudah ada, migrasi ke fitur-fitur Dart 3 dapat dilakukan secara bertahap. Tidak ada keharusan untuk mengonversi semuanya sekaligus.
Langkah pertama adalah mengidentifikasi fungsi-fungsi yang mengembalikan Map, List<dynamic>, atau tuple buatan sendiri untuk digantikan dengan records. Refactoring ini bersifat lokal dan tidak memerlukan perubahan berantai di seluruh codebase.
Langkah kedua menargetkan hierarki class yang digunakan untuk memodelkan state terbatas. Class abstract dengan kumpulan subtipe yang sudah diketahui merupakan kandidat ideal untuk diubah menjadi sealed classes. Konversi ini memicu error kompilasi di setiap tempat yang belum menangani suatu kasus, sehingga mengungkap branch yang terlewat.
Langkah ketiga menggantikan rangkaian if-else dan switch statement dengan switch expression yang dilengkapi pattern matching. Transformasi ini meningkatkan keterbacaan dan menambahkan verifikasi exhaustiveness oleh compiler.
Dart 3.10 memperkenalkan dot shorthands, sebuah sintaksis ringkas yang memungkinkan penghilangan nama tipe ketika konteks sudah membuatnya jelas. Misalnya, Color color = .red sebagai pengganti Color color = Color.red. Fitur ini semakin mengurangi noise sintaksis dan terintegrasi secara natural dengan pattern matching.
Kesimpulan
Dart 3 menghadirkan perubahan fundamental yang mentransformasi arsitektur aplikasi Flutter. Berikut poin-poin utama yang perlu dikuasai untuk interview maupun praktik pengembangan sehari-hari:
- Records menyediakan tipe data struktural ringan dengan perbandingan berdasarkan nilai, menghilangkan kebutuhan class boilerplate untuk pengembalian nilai ganda
- Pattern matching menawarkan destructuring deklaratif dengan verifikasi tipe, guard clause, dan exhaustiveness yang dijamin compiler
- Sealed classes mendefinisikan himpunan subtipe tertutup, menjadikan mustahil untuk melewatkan sebuah kasus dalam switch expression
- Kombinasi ketiga fitur ini memungkinkan pemodelan alur kompleks (autentikasi, panggilan API, navigasi) dengan keamanan tipe yang menyeluruh
- Ekstraksi null-safe melalui
if-casemenggantikan pengecekan key secara manual dan type casting yang tidak aman dalam pemrosesan JSON - Migrasi dari kode Dart 2 dapat dilakukan secara inkremental: records untuk return value ganda, sealed classes untuk state terbatas, switch expression untuk percabangan kondisional
Mulai berlatih!
Uji pengetahuan Anda dengan simulator wawancara dan tes teknis kami.
Tag
Bagikan
Artikel terkait

20 Pertanyaan Wawancara Flutter Teratas untuk Developer Mobile
Persiapkan wawancara Flutter dengan 20 pertanyaan yang paling sering ditanyakan. Widget, state management, Dart, arsitektur, dan praktik terbaik dijelaskan secara detail dengan contoh kode.

State Management Flutter: Riverpod vs BLoC - Panduan Perbandingan Lengkap
Perbandingan mendalam antara Riverpod dan BLoC untuk state management di Flutter. Arsitektur, performa, kemudahan pengujian, dan kasus penggunaan untuk memilih solusi terbaik.

Flutter: Membangun Aplikasi Lintas Platform Pertama
Panduan lengkap untuk membangun aplikasi mobile lintas platform dengan Flutter dan Dart. Widget, manajemen state, navigasi, dan praktik terbaik untuk pemula.