Pandangan mendalam tentang StatefulWidget Flutter dan Siklus Hidupnya.

Anda akan menggunakan StatefulWidget. Jika Anda akan menggunakan Flutter, Anda akan menggunakan StatefulWidget. Banyak dari mereka. Bersama. Satu di atas yang lain. Ini tidak bisa dihindari. Saat Anda semakin mempelajari Flutter, aplikasi Anda akan menjadi lebih rumit…dengan lebih banyak Widget. Widget Stateful Lainnya. Widget Stateless tidak pernah berubah. Widget Stateful dapat berubah sebagai respons terhadap interaksi pengguna atau peristiwa lainnya. 'Status' widget ini diwakili oleh objek kelas terpisah yang disebut, ya… Status. Objek State terdiri dari nilai-nilai yang dapat berubah. Objek State berisi 'keadaan yang dapat diubah' pada widget yang terkait — objek ini menyimpan nilai yang dapat berubah seiring waktu. Ketika nilai-nilai tersebut telah berubah, sering kali StatefulWidget terkait dibuat ulang.

Belajar Dengan Contoh

Di banyak artikel saya, saya mencoba menggunakan contoh yang sudah ada dari situs web Flutter milik Google untuk menyampaikan konsep, Widget tertentu, dll. Dalam artikel ini, saya akan menggunakan 'aplikasi contoh' yang dibuat untuk Anda setiap kali Anda membuat ' Proyek Flutter Baru...'. Di bawah ini, saya telah mengisolasi StatefulWidget dan objek State yang ditemukan dalam kode yang dihasilkan. Komentar dan kode lain dihapus agar singkatnya.

Saya Suka Tangkapan Layar. Klik Untuk Intisari.

Seperti biasa, saya lebih suka menggunakan tangkapan layar daripada intisari untuk menunjukkan konsep daripada hanya menampilkan kode di artikel saya. Sejujurnya saya merasa mereka lebih mudah untuk diajak bekerja sama. Namun, Anda dapat mengeklik atau mengetuk tangkapan layar ini untuk melihat kode yang diwakilinya secara ringkas atau di Github. Ironisnya, lebih baik membaca artikel tentang pengembangan seluler ini di komputer Anda daripada di ponsel Anda. Selain itu, kami memprogram di komputer kami — bukan di ponsel kami. Untuk sekarang.

Tidak Ada Gambar Bergerak Tidak Ada Media Sosial

Catatan, akan ada file gifdi artikel ini yang menunjukkan aspek topik yang dibahas. Namun, dikatakan bahwa file giftidak dapat dilihat saat membaca artikel ini di platform seperti Instagram, Facebook, dll. Harap perhatikan hal ini dan mungkin baca artikel ini di medium.com

Mari kita mulai.

Bangun Layar

Saat Anda menjalankan kode yang dihasilkan itu, Anda mendapatkan aplikasi penghitung yang bagus. Saat Anda melihat layar seperti pada tangkapan layar di atas, Anda mengetahui fungsi build() untuk objek State, _MyHomePageSate, telah berjalan dan widget Scaffold telah dikembalikan. Di dalam widget Scaffold itu, masih banyak widget lainnya. Saya menghitung delapan begitu saja. Semuanya telah berjalan dan sekarang ditampilkan di layar. Perhatikan panah merah di bawah ini. Anda dapat melihat bahwa setelah ditampilkan, satu widget kini ditempatkan dengan pengendali peristiwa untuk melakukan sesuatu jika dan ketika widget tersebut ditekan.

Atur Untuk Membangun Kembali

Tahukah Anda apa yang dilakukan fungsi setState() pada objek State? Ia melakukan banyak hal, namun satu hal khusus yang kami minati adalah ia menyebabkan fungsi build() objek State juga berjalan. Hasilnya, dalam kasus ini, fungsi anonim kemudian berjalan dan menambah variabel instan objek State yang disebut, _counter. Framework Flutter kemudian diberitahu untuk memanggil kembali fungsi build() objek State tersebut dan sekarang menampilkan nilai terbaru dari variabel instance. Setiap kali fungsi setState() objek State dipanggil, fungsi build()-nya akan segera dipanggil setelahnya.

Hitung Bangunan Anda

Jadi, jika Anda menekan tombol biru mengambang itu, Anda akan melihat angka 1 di layar. Fungsi build() objek State dipanggil lagi — menampilkan properti bidang atau variabel instance, _counter, dengan nilai integer barunya sebesar 1. Layar yang Anda lihat di depan Anda adalah secara harafiah 'dibangun kembali' dari konten fungsi build() objek State. Mengerti sejauh ini?

Mari kita lihat lebih dekat ini. Ada sejumlah fungsi print() yang kini diperkenalkan ke kode. Untuk menekankan kapan setiap kelas dipakai (yaitu ketika setiap widget dibuat), saya telah secara eksplisit mendefinisikan konstruktor untuk objek StatefulWidget dan objek State — menyediakan fungsi print() di masing-masing kelas. Ada perintah cetak yang ditempatkan di lokasi lain yang menarik untuk menunjukkan 'siklus hidup' StatefulWidget dan objek State-nya. Klik pada tangkapan layar di bawah untuk mendapatkan salinan Anda sendiri. Anda kemudian dapat mengikutinya.

Apa yang Terjadi Saat Startup

Jadi, dengan adanya fungsi print() tersebut, kita dapat dengan mudah melihat apa yang terjadi saat aplikasi penghitung pertama kali dijalankan. Seperti yang diharapkan, dengan melihat layar konsol di bawah, dua kelas yang membentuk objek StatefulWidget dan State dibuat, lalu fungsi build() objek State dipanggil untuk menampilkan layar yang dihasilkan. Cukup mudah sejauh ini.

Tekan tombolnya. Lihat Apa yang Terjadi.

Tekan tombol biru mengambang sekali, dan Anda mendapatkan yang berikut di layar konsol. Masuk akal. Seperti yang Anda ketahui, memanggil fungsi setState() objek State akan menyebabkan fungsi build() objek State dipanggil lagi. Terlihat bagus.

Anda cukup mengerti idenya. Setiap kali Anda menekan tombol, Anda akan melihat fungsi build() objek State dipanggil lagi — menampilkan nilai dalam variabel instance objek State, _counter. Di bawah ini adalah tangkapan layar layar konsol saat tombol ditekan enam kali. Cukup mudah.

Inisialisasi Negara

Sebelum kita melanjutkan, mari kita mundur sedikit dan melihat lebih jauh objek State tersebut. Biasanya, sebagian besar logika aplikasi Anda akan ditemukan, diakses, dan dijalankan di objek State aplikasi Anda. Anda akan menemukan diri Anda mendefinisikan logika ini di kelas yang membentuk objek State Anda. Oleh karena itu, cara yang umum untuk menginisialisasi logika tersebut adalah pada fungsi initState() objek State. Mari kita tunjukkan hal ini sekarang dengan memodifikasi aplikasi penghitung lama kita dan menginisialisasi, dalam hal ini, variabel instannya, _counter, dengan nilai integer 0 di dalam initState( ) fungsi.

Dengan menggunakan contoh sederhana ini, saya ingin menyampaikan beberapa praktik umum yang ditemukan dalam aplikasi yang lebih kompleks — salah satunya adalah penyediaan nilai default ke variabel instan tersebut dalam fungsi initState(). Fungsi ini dipanggil hanya ketika objek State pertama kali dibuat, sehingga aplikasi akan berperilaku sama seperti sebelumnya. Catatan, perintah print juga akan ditempatkan di dalam fungsi initState().

Semuanya berperilaku sama. Namun, sekarang Anda melihat baris tambahan di layar konsol. Sekali lagi, Anda akan melihat baris ini hanya sekali ketika objek State pertama kali dipakai.

Ini Akan Menjadi Rumit

Oke, mari kita kembali ke sana. Sejauh ini, hal ini sangat sederhana. Ini adalah aplikasi yang sangat sederhana. Namun, aplikasi Anda tidak akan sesederhana itu. Ini akan menjadi lebih rumit. Mari kita lihat apakah saya dapat memperkenalkan kompleksitas tersebut sambil tetap menggunakan konsep aplikasi penghitung sederhana.

Dua Layar; Tiga Penghitung

Jadi sekarang saya akan memberi Anda versi lain dari aplikasi penghitung itu. Dalam versi ini, ada tiga penghitung di dua layar terpisah! Banyak hal telah berubah dalam versi ini — seperti yang Anda lihat di aplikasi yang lebih kompleks. Pertama dan terpenting, dibandingkan dengan aplikasi penghitung asli, Anda akan melihat StatefulWidgets memulai StatefulWidgets lainnya.

Di bawah ini adalah tangkapan layar dari aplikasi penghitung yang lebih rumit ini. Ketuk atau klik tangkapan layar untuk mendapatkan salinannya sendiri. Anda dapat melihat objek State yang asli, _MyHomePageState, sekarang memiliki counternya sendiri?! Selain itu, StatefulWidget baru yang disebut, _FirstPage, kini menampilkan layar asli!

Selanjutnya dalam kode, Anda akan menemukan StatefulWidget 'Halaman Kedua' yang akan menampilkan penghitung terpisah di layar terpisah. Sekali lagi, ini dibumbui dengan perintah cetak sehingga kita dapat mengikuti 'urutan kejadian' untuk widget tersebut.

Jadi, dengan melihat layar konsol, Anda dapat melihat perintah cetak menyorot urutan pembuatan widget dan objek Status serta urutan pemanggilan fungsinya. Misalnya, saat layar beranda pertama kali ditampilkan, Anda dapat melihat objek State yang terlibat terlebih dahulu memanggil fungsi initState() sebelum akhirnya memanggil fungsi build()-nya. Ketika masuk ke layar utama, yang dimaksud adalah objek State, _FirstPageState, dengan widget di fungsi build() yang menampilkan layar pertama atau halaman pertama.

Ikuti 'garis panah' satu demi satu, dan Anda dapat melihat logika yang terlibat saat menampilkan layar pertama. Setiap StatelessWidget dan StatefulWidget dibuat satu demi satu. Dan dengan setiap StatefulWidget, Anda dapat melihat objek State terkait dibuat dan fungsinya dipanggil.

Ke Yang Kedua

Sekarang, sambil menonton layar konsol, jika Anda mengetuk tombol, Halaman Kedua, Anda akan melihatnya menuju ke StatefulWidget ketiga dalam aplikasi bernama, Halaman Kedua. Itu menciptakan widget itu; itu menciptakan objek State yang menyertainya bernama, _SecondPageState. Ia melewati fungsi initState() objek State dan akhirnya ke fungsi build() untuk menampilkan layar kedua. Tapi seperti yang Anda lihat di konsol, itu belum selesai.

Harap abaikan paragraf pertama di bawah ini. Sekarang hal tersebut tidak terjadi lagi.

Tapi Jangan Lupakan Yang Pertama

Framework Flutter, setelah menampilkan layar kedua, lalu memanggil objek State di layar pertama, _FirstPageState, sekali lagi! Kali ini untuk memanggil fungsi deactivate() objek State tersebut. Tentu saja, seperti yang Anda lihat, kita masih belum selesai. Flutter kemudian memanggil fungsi build() dari objek State pertama di aplikasi, _MyHomePageState , untuk benar-benar membuat ulang StatefulWidget, FirstPage, Anda dapat melihat konstruktornya dipanggil lagi. Terakhir, ada lagi panggilan ke fungsi build() di objek State, _FirstPageState. Sekarang, mengapa framework Flutter melakukan semua itu?? Kita akan membahasnya lain kali. Untuk saat ini, apakah Anda memperhatikan ada beberapa hal yang hilang dalam urutan panggilan tersebut?

Di Flutter, Widget dibuat ulang setiap saat. Selalu. Setiap kali Anda menjalankan fungsi build(), Anda membuat ulang Widget yang ada di dalamnya. Biasakan diri dengan fakta itu, tapi apakah Anda memperhatikan apa yang tidak disebut dalam rangkaian kejadian itu? Itu benar! Anda tidak melihat objek State 'pertama' dibuat ulang dan atau fungsi initState() dipanggil lagi! Saya telah menempatkannya di tempat yang seharusnya dalam urutan di bawah ini, tetapi kenyataannya, mereka tidak dipanggil. Objek State, _FirstPageState, dibiarkan hanya untuk menjalankan fungsi build() lagi. Jadi mengapa mereka tidak dipanggil?

Ngomong-ngomong, objek State pertama memanggil fungsi build() namun tidak ditampilkan karena mekanisme perutean Flutter. Saat ini kita sedang berada pada “hamparan terpisah pada tumpukan rute” dan disajikan dengan StatefulWidget 'Halaman Kedua' dan konten dari fungsi build() objek State-nya.

Negara Tetap Ada

Faktanya tetap bahwa objek Status 'Halaman Pertama' tetap dipertahankan bahkan ketika StatefulWidget-nya dihancurkan dan dibuat kembali. Objek-objek negara dan, yang lebih penting, isi di dalam objek-objek negara dipertahankan sementara kerangka di sekelilingnya dibangun kembali berulang kali. Itulah yang saya ingin Anda hargai saat ini. Secara desain, konstruktor objek State dan initState() tidak diaktifkan lagi dalam rangkaian kejadian seperti itu. Mari kita lihat contoh lainnya.

Jumlah Judul

Sekarang, di aplikasi penghitung ini, Anda akan melihat bahwa ada tombol 'Penghitung Halaman Beranda' yang ditampilkan di dekat bagian bawah layar saat Anda pertama kali memulainya. Dengan setiap klik tombol itu, penghitung yang ditampilkan di bilah judul aplikasi akan bertambah. Terlihat cukup sederhana, bukan? Namun, mari kita lihat apa saja yang terlibat.

Ingat, di aplikasi ini, saat Anda mengeklik tombol, fungsi setState() akan dipanggil. Artinya, fungsi build() akan segera dipanggil setelahnya. Ingatlah hal itu saat kami melanjutkan.

Bangun Rumah Anda

Anda dapat melihat ketika menekan tombol 'Penghitung Halaman Beranda', penghitung yang ditentukan kembali ke objek Status, _MyHomePageSate, bertambah. Selanjutnya, setState() objek State tersebut dipanggil, dan Anda tahu apa maksudnya.

Jadi, kembali ke objek State, _MyHomePageSate, fungsi build()-nya akan segera dipanggil untuk menyampaikan nilai 'penghitung' baru dalam judul aplikasi. Lihat panah merah di bawah.

Konsol Tahu

Sekali lagi, ketika tombol 'Penghitung Halaman Beranda' ditekan, Anda dapat melihat kembali di Halaman Beranda, fungsi build() objek State-nya dipanggil (lihat gambar di atas) dan dengan demikian memperbarui bilah judul untuk aplikasi. Tentu saja, karena fungsi _MyHomePageState build() dijalankan, StatefulWidget, FirstPage, dibuat ulang (konstruktornya disebut) dan build< objek State-nya /strong>() fungsi dipanggil lagi secara bergantian. Namun sekali lagi, objek State itu sendiri tidak diciptakan kembali. Kalau tidak, dibiarkan begitu saja. Lihat cara kerjanya?

Tunggu sebentar!

Mari kita coba penghitung di layar kedua. Urutan kejadian yang membawa kita bisa dilihat pada screenshot yang kita lihat di bawah ini. Aplikasi dimulai, dan tombol berlabel, ‘Halaman Kedua’, ditekan. Halaman kedua ditampilkan di layar (melalui kelas Navigator), dan tombol biru mengambangnya ditekan sekali. Terakhir, Anda dapat melihat, objek State, _SecondPageState, memiliki fungsi build() yang dipanggil untuk kemudian 'menggambar ulang' layar untuk menampilkan angka 1.

Terlihat cukup mudah, bukan? Dengan setiap penekanan tombol, objek State, _SecondPageState, akan memanggil fungsi setState() dan build() secara bergantian.

Kembali Ke Pertama

Nah sekarang, mari kembali ke layar pertama. Karena kita telah menggunakan kelas Navigator untuk membuka dan menampilkan layar kedua, kita dapat menggunakan Navigator lagi untuk mundur kembali 'tumpukan rute widget' dan kembali ke layar pertama menggunakan perintah, “Navigator.pop (konteks);”. Sekarang, lihat layar konsol di bawah.

Yang Pertama Pergi

Lihatlah apa yang terjadi ketika aplikasi kembali ke layar pertama! Aplikasi ini sebenarnya membuat ulang StatefulWidget 'Halaman Pertama' lagi. Pertama, ia memanggil kembali fungsi deactivate() dari objek State Halaman Pertama, _HomePageState build() kemudian dipanggil (sehingga untuk membangun StatefulWidget 'Halaman Pertama' lagi ). Anda dapat melihat bahwa ini karena konstruktornya kemudian menyala. Terakhir, objek State StatefulWidget tersebut (jika tidak disentuh) akan mengaktifkan fungsi build() lagi. Wah! Banyak hal yang terjadi di sana, ya? Tapi bukan itu saja!

Pop Menjadi Negara Bagian

Dengan dihapusnya widget 'Halaman Kedua' dari tumpukan perutean, widget tersebut akan dihapus dari memori — begitu pula objek Statusnya. Anda dapat melihat fungsi deactivate() pada objek Status 'Halaman Kedua' dipanggil (lihat di bawah), serta fungsi lainnya. Salah satu yang mungkin baru bagi Anda. Fungsi buang(). Artinya, objek State telah dilepaskan dari memori dan, seperti rekan StatefulWidget-nya, kini menjadi kandidat untuk pengumpulan sampah platform. Perhatikan, 'sumber daya berat' apa pun seperti file terbuka dan atau Aliran terbuka harus ditutup secara eksplisit di fungsi nonaktifkan() atau di fungsi buang(). (Beberapa orang mengatakan yang terbaik adalah menggunakan nonaktifkan() vs buang()).

Jadi, jika Anda kembali ke Halaman Kedua, Anda akan menemukan hitungannya kembali ke nol. StatefulWidget dan objek State yang menyertainya telah dibuat lagi ketika Anda kembali ke Halaman Kedua melalui kelas Navigator.

Andalkan Rumah

Kita bisa melanjutkan hal ini. Apa yang terjadi, misalnya, jika penghitung Halaman Beranda pada bilah judul di layar pertama bertambah saat kita berada di Halaman Kedua? Baiklah, mari kita lihat. Di bawah ini adalah urutan ketika ‘Home Page Counter’ pada Halaman Kedua ditekan tiga kali berturut-turut. Kami kemudian kembali ke layar pertama. Kita dapat melihat, dengan setiap penekanan tombol itu, StatefulWidget layar pertama dibuat ulang. Tentu saja, hal ini karena objek State pada Halaman Beranda sedang menjalankan fungsi build() (untuk memperbarui bilah judul). Hasilnya, StatefulWidget layar pertama dibuat ulang dan fungsi build() objek State yang terkait juga dipanggil. Terakhir, ketika kita kembali ke layar pertama, kita melihat title bar memang ditampilkan dengan nilai counter 3.

Negara Kunci

Pernahkah Anda memperhatikan tombol tambahan di layar kedua? Tombol ‘Kunci Baru’ itu? Tahukah Anda apa fungsinya? Baiklah, aku akan memberitahumu. Ini menciptakan kembali objek State di layar pertama! Layar pertama; bukan layar kedua. Saya baru saja selesai memberi tahu Anda bahwa objek Status di layar pertama dibiarkan saja, dan sekarang saya memberi tahu Anda hal ini. Bisakah kamu mempercayainya?

Sekarang, mengapa Anda ingin 'menghapus' objek State di layar? Karena, dan saya dapat mengatakan ini berdasarkan pengalaman, Anda mungkin ingin menyetel ulang 'nilai yang dapat diubah' dalam objek State karena satu dan lain alasan — dan Flutter dengan mudah mengizinkannya.

Saat Anda menekan tombol 'Kunci Baru', Anda dapat melihat kode di bawah ini bahwa yang terjadi adalah variabel pribadi diberi nilai Kunci unik baru. Melakukan hal ini akan memberikan efek yang kita cari. Kembali ke layar pertama, penghitung dikembalikan ke nol. Itu karena objek Negara telah dibuat ulang.

Variabel tersebut adalah variabel tingkat tinggi — ditentukan dalam file dart dan bukan dalam kelas tertentu. Sekarang tidak ada alasan khusus untuk melakukan hal ini. Faktanya, ini bisa dengan mudah didefinisikan sebagai variabel instan di objek State, _MyHomePageState. Terlepas dari itu, perhatikan bahwa ini didefinisikan dalam file pada waktu kompilasi dan kemudian ditetapkan ke StatefulWidget, _FirstPage. Di bawah ini Anda dapat melihat urutan yang terjadi setelah tombol 'Kunci Baru' ditekan.

Anda dapat melihat pada tangkapan layar di atas ketika Anda mengklik tombol 'Kunci Baru', StatefulWidget layar pertama, FirstPage, dibuat ulang seperti biasa. Namun, ada sesuatu yang berubah. Objek State _FirstPageState juga dibuat ulang. Sekarang urutan yang disorot dengan panah merah agak membingungkan, tapi kita akan membahasnya. Dimulai dengan panah merah pertama, kita melihat 'instance asli' dari objek State yang memanggil fungsi deactivate()-nya. Tiga panah berikutnya menggambarkan 'instance baru' dari objek State yang sedang dibuat dan memanggil fungsi build()-nya. Panah terakhir menunjuk kembali ke 'instance asli' objek State saat ia memanggil fungsi dispose() dan bersiap untuk didaur ulang.

Perhatikan, bahwa fungsi dispose() dapat muncul kapan saja setelah pemanggilan fungsi deactivate() — tepat setelahnya atau lebih lama lagi. Itu sebabnya orang-orang menyarankan agar Anda mengosongkan sumber daya 'batas waktu' di fungsi deactivate() yang secara konsisten dan dapat diprediksi dipanggil ketika objek State diganti dengan instance baru.

Jadi, ketika tombol 'Halaman Pertama' ditekan untuk kembali ke layar pertama, kita tahu bahwa StatefulWidget layar pertama selalu dibuat ulang. Namun, kali ini, ia diberi 'kunci unik' baru. Dengan melakukan hal ini, framework akan membuang objek State yang asli untuk membuat objek State yang baru. Jadi begitulah. Selain menguji widget, ada alasan lain untuk menetapkan kunci pada widget. Tim Flutter, tentu saja, memiliki video tentang kapan menggunakan Keys dan kemungkinan besar akan memberikan lebih banyak wawasan.

Dapatkan Siklus Hidup

Harap perhatikan bahwa artikel gratis, Siklus Hidup Aplikasi Flutter, adalah bacaan yang sangat bagus. Ini memperkenalkan kelas, , yangakan Anda gunakan untuk mengimplementasikan event handler 'siklus hidup' lebih lanjut. Bagi Anda yang memiliki latar belakang Android akan mengenali event handler ini seperti yang ditemukan di kelas Activity. Di bawah ini adalah tangkapan layar dari beberapa event handler panggilan balik yang tersedia untuk Anda.

Itu sudah cukup untuk saat ini. Beberapa hal yang perlu diingat ketika bekerja dengan banyak StatefulWidget Anda — dan Statusnya.

Bersulang.

→ Cerita Lainnya oleh Greg Perry