setState() — ang pangunahing pamamaraan ng State sa Flutter, na nagpapaalam sa framework tungkol sa pagbabago ng data at nagpapasimula ng muling pagtatayo ng interface. Ayon sa opisyal na dokumentasyon ng Flutter (Flutter.dev, 2026), ang setState ay ang pangunahing mekanismo ng reaktibiti sa StatefulWidget: kung hindi ito tatawagin, hindi malalaman ng UI ang mga pagbabago sa mga field ng State at mananatili sa dating estado. Ang pamamaraan ay tumatanggap ng VoidCallback, sa loob nito binabago ng developer ang mga nababagong field, pagkatapos ay awtomatikong tinatawag ng Flutter ang build para sa muling pagtatayo ng widget.
Pangunahing Punto
setState() — isang built-in na pamamaraan ng klase ng State sa Flutter, na idinisenyo upang ipaalam sa framework na nagbago ang panloob na estado ng widget at kailangan ang muling pagtatayo ng UI. Kung hindi tatawagin ang setState, hindi alam ng Flutter ang mga pagbabago — kahit na binago ang mga field ng State, mananatiling hindi nagbabago ang interface hanggang sa susunod na sapilitang muling pagtatayo ng magulang.
Lagda ng pamamaraan: void setState(VoidCallback fn). Ang callback ay isinasagawa nang sabay-sabay (synchronously) sa loob ng setState at pagkatapos lamang ng pagkumpleto nito, ang State ay minarkahan bilang dirty. Ginagarantiyahan nito na ang lahat ng pagbabago ay inilalapat nang atomiko bago ang muling pagtatayo. Ayon sa Dart Language Specification (Dart Team, 2026), ang atomicity ng setState ay pumipigil sa kondisyon ng lahi, kung saan maaaring makita ng build ang bahagyang na-update na estado.
Ang setState ay hindi tumatanggap ng mga argumento, hindi nagbabalik ng halaga at hindi maaaring i-override. Ito ay isang final (sealed) na pamamaraan ng klase ng State. Hindi mababago ng developer ang pag-uugali nito — magagamit lamang ito ayon sa layunin. Ang pagtatangkang tumawag ng setState sa labas ng State (halimbawa, mula sa ibang klase) ay imposible, dahil ang pamamaraan ay idineklara sa klase ng State.
Isang karaniwang maling paniniwala — isipin na ang setState mismo ang nagbabago ng estado. Hindi ito totoo. Ang setState ay tumatawag lamang sa ipinasa na callback (kung saan binabago ng developer ang mga field) at pagkatapos ay nagpapahiwatig sa framework tungkol sa pangangailangan ng build. Ang callback ay sapilitan — ang pagpasa ng null o walang laman na callback ay magdudulot ng error.
Ang mekanismo ng trabaho ng setState() ay maaaring hatiin sa apat na yugto. Una — pagtawag sa pamamaraan na may callback. Pangalawa — sabay-sabay na pagpapatupad ng callback, sa loob nito binabago ang mga field ng State. Pangatlo — ang State ay minarkahan bilang dirty sa espesyal na field na _dirty. Pang-apat — sa dulo ng kasalukuyang microtask, tinatahak ng Flutter ang lahat ng dirty na elemento at tinatawag ang kanilang build sa pagkakasunud-sunod ng paglitaw sa puno.
Mahalagang detalye: hindi agad tinatawag ng setState ang build. Gumagamit ang Flutter ng diskarte sa batch na pag-update: lahat ng dirty na elemento ay kinokolekta at itinatayong muli sa isang frame. Nangangahulugan ito na kung ang setState ay tinawag nang maraming beses sa loob ng parehong synchronous block, ang build ay isasagawa nang isang beses lamang — pagkatapos makumpleto ang lahat ng pagbabago. Pinipigilan ng naturang pag-optimize ang maraming muling pagtatayo bawat frame.
Ayon sa Flutter Engine Team (Google, 2025), ang mekanismo ng dirty-flag ay batay sa pagtawid sa BuildOwner._dirtyElements. Ang bawat dirty na StatefulElement ay idinadagdag sa listahan at pinoproseso sa yugto ng pag-update ng frame. Kung ang widget ay tinanggal mula sa puno bago ang pagproseso, awtomatiko itong hindi kasama sa listahan ng mga dirty na elemento.
Pangunahing halimbawa ng setState() na may pagtaas ng counter. Nagpapakita ng tamang paggamit: pagbabago ng field sa loob ng callback:
class _CounterState extends State<CounterWidget> {
int _count = 0;
void _increment() {
setState(() {
_count++; // mutasyon ng field sa loob ng callback
});
}
@override
Widget build(BuildContext context) {
return ElevatedButton(
onPressed: _increment,
child: Text('$_count'),
);
}
}
Halimbawa na may text field at controller — setState() para sa pamamahala ng visibility ng password:
class _PasswordFieldState extends State<PasswordField> {
bool _obscured = true;
final _controller = TextEditingController();
void _toggleVisibility() {
setState(() {
_obscured = !_obscured;
});
}
@override
Widget build(BuildContext context) {
return TextField(
controller: _controller,
obscureText: _obscured,
decoration: InputDecoration(
suffixIcon: IconButton(
icon: Icon(_obscured ? Icons.visibility : Icons.visibility_off),
onPressed: _toggleVisibility,
),
),
);
}
@override
void dispose() {
_controller.dispose();
super.dispose();
}
}
Sa halimbawang ito, setState() ay binabago lamang ang boolean field na _obscured, na nagiging sanhi ng muling pagtatayo ng TextField na may bagong icon at mode ng pagpapakita. Ang text controller ay hindi muling ginagawa — ito ay sinisimulan nang isang beses sa initState at pinakawalan sa dispose.
Kung kailangan baguhin ang maraming field, ang lahat ng pagbabago ay isinasagawa sa loob ng isang setState. Ginagarantiyahan nito na makikita ng build ang isang pare-parehong estado:
setState(() {
_isLoading = false;
_items = newItems;
_error = null;
});
Tatlong field ang binabago sa isang callback — ang build ay isasagawa nang isang beses at makikita ang lahat ng pagbabago nang sabay-sabay. Kung bawat tawag ay hiwalay na setState, ang build ay isasagawa pa rin nang isang beses dahil sa batch processing ng mga dirty na elemento.
Isa sa pinakamahalagang nuances ng setState() — ang pag-uugali nito sa mga asynchronous na operasyon. Ang callback ng setState ay isinasagawa nang sabay-sabay, ngunit kung ang await ay tinawag sa loob nito, ang code pagkatapos ng await ay isasagawa pagkatapos ng pagkumpleto ng setState. Nangangahulugan ito na ang mga pagbabago ng field pagkatapos ng await ay hindi mahuhuli ng kasalukuyang setState.
Tamang diskarte: ang asynchronous na operasyon ay isinasagawa sa labas ng setState, at ang setState ay tinatawag pagkatapos ng pagkumpleto nito. Ang lahat ng code sa pagitan ng pagtanggap ng resulta at pagtawag ng setState ay isinasagawa sa synchronous na konteksto pagkatapos ng await:
// TAMA: await sa labas ng setState
Future<void> _loadData() async {
final result = await ApiService.fetchData();
setState(() {
_data = result;
_isLoading = false;
});
}
// MALI: await sa loob ng setState — walang garantiya ng pag-update
void _loadDataWrong() {
setState(() async {
_data = await ApiService.fetchData(); // bumabalik ang setState bago matapos ang await
_isLoading = false; // ang code na ito ay hindi nahuhuli ng setState
});
}
Ayon sa dokumentasyon ng Flutter (Dart async patterns, 2026), ang pagpasa ng async-callback sa setState ay isang antipattern, dahil ang setState ay umaasa ng VoidCallback (synchronous function), habang ang async function ay nagbabalik ng Future na hindi pinapansin. Ang mga pagbabago pagkatapos ng unang await sa naturang callback ay hindi mapoproseso nang tama ng framework.
Bago tumawag ng setState() pagkatapos ng asynchronous na operasyon, palaging suriin ang mounted:
if (mounted) {
setState(() => _data = data);
}
Kung ang widget ay tinanggal mula sa puno sa panahon ng pagpapatupad ng asynchronous na operasyon, ang mounted ay magiging false at ang setState ay hindi tatawagin. Pinipigilan nito ang exception at pagtagas ng mapagkukunan.
setState() — isang maginhawa ngunit potensyal na magastos na mekanismo, kung gagamitin nang walang pag-iisip. Bawat tawag ng setState ay muling itinatayo ang buong widget at lahat ng inapo nito (kung hindi sila const). Sa malalalim na puno o sa madalas na tawag, ito ay maaaring humantong sa pagbaba ng FPS.
Mga pangunahing diskarte sa pag-optimize: bawasan ang lugar ng muling pagtatayo (ilipat ang nagbabagong bahagi ng UI sa magkakahiwalay na StatefulWidget), gamitin ang const para sa hindi nagbabagong mga inapo at iwasan ang pagtawag ng setState sa mga magulang na widget kung maliit na UX detalye lamang ang nagbago. Kung ang estado ay ina-update na may mataas na dalas (animation, stream ng data), isaalang-alang ang AnimatedBuilder o ValueListenableBuilder.
Ayon sa Flutter Performance Best Practices (Flutter.dev, Pebrero 2026), ang profiling ng mga totoong application ay nagpapakita na hanggang 40% ng lahat ng tawag sa setState ay maaaring palitan ng const na anak na widget o reactive na builder (StreamBuilder, FutureBuilder). Binabawasan nito ang average na oras ng pagtatayo ng frame ng 15–25%.
| Scenario | Alternatibo | Bentahe |
|---|---|---|
| Animasyon | AnimatedBuilder | Muling itinatayo lamang ang widget na ina-animate |
| Stream ng data | StreamBuilder | Tumutugon sa bawat elemento ng stream |
| Resulta sa hinaharap | FutureBuilder | Namamahala sa mga estado ng pag-load/error |
| Lokal na halaga | ValueListenableBuilder | Tumutugon sa pagbabago ng isang halaga |
Sa kabila ng versatility ng setState(), sa malalaking proyekto ito ay pangunahing ginagamit para sa lokal na estado. Para sa global o pinagsamang estado, ginagamit ang mga espesyalisadong solusyon, na bawat isa ay pumapalit o bumabalot sa setState.
Ang Provider ay gumagamit ng ChangeNotifier + notifyListeners bilang analog ng setState, ngunit may kakayahang mag-subscribe ang maraming widget. Ang Bloc ay gumagamit ng Streams — nagbabago ang estado sa pamamagitan ng pagdaragdag ng mga kaganapan sa StreamController. Pinagsasama ng Riverpod ang mga diskarte, na nag-aalok ng parehong lokal (StateProvider) at asynchronous (AsyncNotifier) na pamamahala nang walang pagbubuklod sa StatefulWidget. Lahat ng tatlong diskarte ay nag-aalis ng pangangailangan na manu-manong tumawag ng setState — ang pag-update ng UI ay awtomatikong nangyayari kapag nagbago ang data.
Ayon sa Flutter Community Survey 2025 (Flutter Foundation, Disyembre 2025), 74% ng mga developer ay gumagamit ng hindi bababa sa isang tool sa pamamahala ng estado bukod sa setState. Samantala, 92% ay patuloy na gumagamit ng setState para sa lokal na data ng text field, checkbox o simpleng counter — ito ay itinuturing na best practice.
Ang una at pinakamapanganib na pagkakamali — pagtawag ng setState pagkatapos ng dispose. Nagsimula ang asynchronous na operasyon sa initState, umalis ang user sa screen, tinanggal ang widget at ang callback ng asynchronous na operasyon ay tumatawag ng setState — ang application ay nag-crash na may exception. Solusyon — palaging suriin ang mounted bago tumawag.
Pangalawang pagkakamali — pagtawag ng setState sa loob ng build. Ito ay humahantong sa walang katapusang loop: build → setState → dirty → build → setState → ... Hindi binablock ng Flutter ang naturang tawag (makakakuha ka ng StackOverflowError). Ang setState ay maaari lamang tawagan bilang tugon sa isang kaganapan (pagpindot ng button, pagkumpleto ng Future, pagtanggap ng data mula sa isang stream).
Pangatlong pagkakamali — pagbabago ng mga field ng State nang hindi tinatawag ang setState. Ang developer ay sumulat ng _count++ at inaasahan na ang UI ay maa-update. Hindi awtomatikong masusubaybayan ng Flutter ang mga pagbabago sa field — kailangan nito ng malinaw na senyales sa pamamagitan ng setState. Ito ay isang pangunahing pagkakaiba mula sa mga reactive framework tulad ng Vue.js, kung saan ang pagbabago ng data ay awtomatikong nagti-trigger ng pag-update.
Pang-apat na pagkakamali — pagtawag ng setState na may asynchronous na callback (async-lambda). Tulad ng inilarawan sa seksyon tungkol sa asynchronisidad, ang mga pagbabago pagkatapos ng await ay hindi mahuhuli, na humahantong sa mga bug na mahirap i-reproduce. Gumamit ng synchronous na callback at tawagan ang setState pagkatapos ng await.
mounted sa mga asynchronous na callbackMga Madalas Itanong
setState() ay nagpapaalam sa Flutter na ang panloob na data ng StatefulWidget ay nagbago at ang UI ay kailangang itayong muli. Ang pamamaraan ay tumatanggap ng callback, isinasagawa ito nang sabay-sabay, minamarkahan ang widget bilang dirty at pinaplano ang pagtawag ng build sa susunod na frame.
Ang UI ay hindi maa-update. Hindi awtomatikong sinusubaybayan ng Flutter ang mga pagbabago sa field. Ang halaga ng field ay magbabago sa memorya, ngunit ang widget ay mananatili sa dating estado hanggang sa susunod na sapilitang muling pagtatayo ng magulang.
Hindi maaari. Ito ay hahantong sa walang katapusang loop: build ay tumatawag ng setState, na minamarkahan ang widget bilang dirty at muling tinatawag ang build. Hindi binablock ng Flutter ang ganitong sitwasyon — ang application ay mag-crash na may StackOverflowError.
Ang build ay isasagawa nang isang beses. Kinokolekta ng Flutter ang lahat ng dirty na elemento at itinatayong muli ang mga ito nang batch sa dulo ng frame. Ang pangalawang setState bago ang pagproseso ng una ay nagdaragdag lamang ng elemento sa parehong listahan ng mga dirty na elemento — walang paulit-ulit na build.
mounted — isang boolean flag na nagpapakita na ang widget ay nasa puno pa rin. Kung pagkatapos ng asynchronous na operasyon ay tinawag mo ang setState nang hindi sinusuri ang mounted, at ang widget ay tinanggal na — ang application ay mag-crash na may exception na „setState called after dispose”.
Buod
Gagawa kami ng mobile application na turnkey
Gumagawa ang IT Sectr ng mga iOS at Android application para sa mga startup at negosyo mula noong 2017. Magpapayo kami sa iyo at magmumungkahi ng pinakamahusay na solusyon.
Basahin din