Nusanet Developers
Published in

Nusanet Developers

Flutter: StatefulBuilder

How to use widget StatefulBuilder

Pengenalan

Pada tulisan kali ini kita akan membahas mengenai salah satu widget yang bernama StatefulBuilder . Dari namanya saja pasti kita sudah tahu apa fungsi dari widget tersebut. Jadi, StatefulBuilder berfungsi untuk membuat state tersendiri didalam builder. Fungsi dari widget ini menurut saya cukup bagus ya karena widget ini seperti membuat state tersendiri sehingga state-nya itu terpisah dari parent-nya dan kadang kalau widget-nya yang ingin kita buat itu cukup sederhana saya pikir lebih baik pakai StatefulBuilder aja sih. Ketimbang harus buat class sendiri yang extends ke StatefulWidget .

Jadi, untuk contoh projek kali ini kita akan menggunakan salah satu design yang ada di material design miliknya si Google.

https://material.io/components/dialogs

Bisa kita lihat bahwa pada design diatas sebenarnya sangat sederhana banget ya. Jadi, kalau teman-teman sudah paham di Flutter pasti sudah bisa membayangkan kalau untuk membuat design diatas kita pasti akan membuat sebuah widget Stateful tersendiri untuk dialog tersebut agar ketika di-setState (user pilih options-nya) maka, parent-nya tidak ikut ter-build juga. Jadi, di tulisan kali ini saya akan buat 2 versi. Versi pertamanya kita akan pakai class tersendiri khusus untuk dialog-nya dan versi keduanya kita akan pakai StatefulBuilder.

Untuk langkah pertamanya silakan buat projek baru dengan nama belajar_stateful_builder.

Buka file main.dart dan ubah kode didalamnya menjadi seperti berikut.

import 'package:flutter/material.dart';

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

class MyApp extends StatelessWidget {
const MyApp({Key? key}) : super(key: key);

@override
Widget build(BuildContext context) {
return MaterialApp(
title: 'Belajar Stateful Builder',
theme: ThemeData(
primarySwatch: Colors.blue,
),
home: const MyHomePage(),
);
}
}

class MyHomePage extends StatefulWidget {
const MyHomePage({Key? key}) : super(key: key);

@override
State<MyHomePage> createState() => _MyHomePageState();
}

class _MyHomePageState extends State<MyHomePage> {
final listRingtones = <String>[
'None',
'Callisto',
'Ganymede',
'Luna',
'Mash-up',
'School\'s out',
'Zen too',
];
late String selectedRingtone;

@override
void initState() {
selectedRingtone = listRingtones.first;
super.initState();
}

@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: const Text('Belajar Stateful Builder'),
),
body: InkWell(
onTap: () {
// TODO: tampilkan dialog untuk pilih phone ringtone
},
child: Padding(
padding: const EdgeInsets.all(16.0),
child: SizedBox(
width: double.infinity,
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
mainAxisSize: MainAxisSize.min,
children: [
Text(
'Phone ringtone',
style: Theme.of(context).textTheme.bodyText2?.copyWith(
fontWeight: FontWeight.w500,
),
),
Text(
selectedRingtone,
style: Theme.of(context).textTheme.bodyText2?.copyWith(
color: Colors.grey,
),
),
],
),
),
),
),
);
}
}

Berikut adalah output dari kode diatas.

Tampilan utama

Untuk versi pertamanya kita akan membuat dialog-nya menggunakan class StatefulWidget. Untuk membuatnya silakan kita buka file main.dart dan tambahkan kode berikut.

class DialogPhoneRingtone extends StatefulWidget {
final List<String> listRingtones;
final String defaultSelectedRingtone;

const DialogPhoneRingtone({
Key? key,
required this.listRingtones,
required this.defaultSelectedRingtone,
}) : super(key: key);

@override
State<DialogPhoneRingtone> createState() => _DialogPhoneRingtoneState();
}

class _DialogPhoneRingtoneState extends State<DialogPhoneRingtone> {
late String selectedRingtone;

@override
void initState() {
selectedRingtone = widget.defaultSelectedRingtone;
super.initState();
}

@override
Widget build(BuildContext context) {
final mediaQueryData = MediaQuery.of(context);
final widthScreen = mediaQueryData.size.width;
final heightScreen = mediaQueryData.size.height;
return AlertDialog(
title: const Text(
'Phone ringtone',
textAlign: TextAlign.start,
),
titlePadding: const EdgeInsets.only(
left: 16,
top: 16,
),
contentPadding: const EdgeInsets.all(16),
content: Column(
mainAxisSize: MainAxisSize.min,
children: [
SizedBox(
width: widthScreen / 1.2,
height: heightScreen / 2,
child: ListView(
shrinkWrap: true,
children: widget.listRingtones.map((element) {
return Row(
children: [
Radio<String>(
value: element,
groupValue: selectedRingtone,
onChanged: (_) {
setState(() {
selectedRingtone = element;
});
},
),
Text(
element,
style: Theme.of(context).textTheme.caption?.copyWith(
color: Colors.grey[900],
),
maxLines: 1,
overflow: TextOverflow.ellipsis,
),
],
);
}).toList(),
),
),
Row(
mainAxisAlignment: MainAxisAlignment.end,
children: [
TextButton(
child: const Text('CANCEL'),
style: TextButton.styleFrom(
tapTargetSize: MaterialTapTargetSize.shrinkWrap,
),
onPressed: () {
Navigator.pop(context);
},
),
TextButton(
child: const Text('OK'),
style: TextButton.styleFrom(
tapTargetSize: MaterialTapTargetSize.shrinkWrap,
),
onPressed: () {
Navigator.pop(context, selectedRingtone);
},
),
],
),
],
),
);
}
}

Lalu, kita ubah kode todo berikut ini.

onTap: () async {
// TODO: tampilkan dialog untuk pilih phone ringtone
},

Menjadi seperti berikut.

final resultSelectedRingtone = await showDialog<String?>(
context: context,
builder: (context) {
return DialogPhoneRingtone(
listRingtones: listRingtones,
defaultSelectedRingtone: selectedRingtone,
);
},
);
if (resultSelectedRingtone != null) {
setState(() {
selectedRingtone = resultSelectedRingtone;
});
}

Sekarang coba jalankan kembali aplikasinya. Dan seharusnya didalam aplikasinya kita sudah bisa pilih phone ringtone-nya.

Versi 1 menggunakan class StatefulWidget

Nah, untuk versi keduanya kita akan membuat tampilan yang sama hanya saja kita menggunakan StatefulBuilder . Untuk membuatnya silakan kita buka kembali file main.dart dan tambahkan function berikut kedalam class _MyHomePageState.

Widget buildDialogPhoneRingtone() {
return StatefulBuilder(
builder: (context, setState) {
debugPrint('build stateful builder');
final mediaQueryData = MediaQuery.of(context);
final widthScreen = mediaQueryData.size.width;
final heightScreen = mediaQueryData.size.height;
return AlertDialog(
title: const Text(
'Phone ringtone',
textAlign: TextAlign.start,
),
titlePadding: const EdgeInsets.only(
left: 16,
top: 16,
),
contentPadding: const EdgeInsets.all(16),
content: Column(
mainAxisSize: MainAxisSize.min,
children: [
SizedBox(
width: widthScreen / 1.2,
height: heightScreen / 2,
child: ListView(
shrinkWrap: true,
children: listRingtones.map((element) {
return Row(
children: [
Radio<String>(
value: element,
groupValue: selectedRingtone,
onChanged: (_) {
setState(() {
selectedRingtone = element;
});
},
),
Text(
element,
style: Theme.of(context).textTheme.caption?.copyWith(
color: Colors.grey[900],
),
maxLines: 1,
overflow: TextOverflow.ellipsis,
),
],
);
}).toList(),
),
),
Row(
mainAxisAlignment: MainAxisAlignment.end,
children: [
TextButton(
child: const Text('CANCEL'),
style: TextButton.styleFrom(
tapTargetSize: MaterialTapTargetSize.shrinkWrap,
),
onPressed: () {
Navigator.pop(context);
},
),
TextButton(
child: const Text('OK'),
style: TextButton.styleFrom(
tapTargetSize: MaterialTapTargetSize.shrinkWrap,
),
onPressed: () {
Navigator.pop(context, selectedRingtone);
},
),
],
),
],
),
);
},
);
}

Lalu, kita ubah kode berikut.

context: context,
builder: (context) {
return DialogPhoneRingtone(
listRingtones: listRingtones,
defaultSelectedRingtone: selectedRingtone,
);

},

Menjadi seperti berikut.

context: context,
builder: (context) {
return buildDialogPhoneRingtone();
},

Kemudian, biar kelihatan proses build state dari class _MyHomePageState kita perlu tambahkan kode debugging-nya didalam method build .

@override
Widget build(BuildContext context) {
debugPrint('build parent');
return Scaffold(
...

Sekarang coba kita jalankan lagi aplikasinya maka, outputnya akan tetap sama.

Versi 2 menggunakan StatefulBuilder

Nah, bisa kita lihat di video diatas dibagian log console-nya bahwa widget StatefulBuilder menggunakan state tersendiri sehingga ketika melakukan setState didalam StatefulBuilder maka, state dari parent-nya tidak akan ter-build ulang.

Kesimpulan

Jadi, kesimpulannya adalah widget StatefulBuilder bisa membantu kita untuk membuat widget stateful tanpa harus buat class yang terpisah. Tapi, perlu dicatat ya bahwa jika widget stateful yang kita pakai itu ingin reusable atau bisa dipakai secara berulang-ulang di halaman lain maka, saya lebih sarankan pakai yang buat class sendiri daripada pakai StatefulBuilder . Untuk source code lengkapnya bisa kamu lihat di Github ya.

--

--

Stories and insights from the developers in Nusanet

Get the Medium app

A button that says 'Download on the App Store', and if clicked it will lead you to the iOS App store
A button that says 'Get it on, Google Play', and if clicked it will lead you to the Google Play store