StatefulWidget

Flutter StatefulWidget အကြောင်း သိကောင်းစရာ

Flutter မှာ StatefulWidget အကြောင်းကို ဂဃနဏ နားလည်ဖို့ အရင်ဆုံး Dart မှာရှိတဲ့ အောက်ပါ အကြောင်းအရာတွေကို နားလည်ထားဖို့ လိုပါတယ်။

  • Async and Await
  • Future API
  • Streams (Single Subscription & Broadcast Subscription)
  • StreamController and StreamTransformer

အဲဒီအကြောင်းအရာအားလုံးကို အပိုင်း (၁) (၂) (၃) (၄) ဆိုပြီး ကျွန်တော် ရေးထားပြီး ဖြစ်ပါတယ်။ ကျွန်တော်တို့အနေနဲ့ Flutter မှာ တစ်ခြားဟာတွေ လေ့လာရလွယ်သလောက် StateManagement နဲ့ ပတ်သက်ရင် Learning Curve က အရမ်းမတ်သွားပါတယ်။ အဲဒီလို မတ်သွားတဲ့ Learning Curve ကို အဆင်ပြေနိုင်စေဖို့အတွက် ဒီပို့စ်ကို ရေးရခြင်း ဖြစ်ပါတယ်။ 

StatefulWidget ဆိုတာ Ephemeral State အနေနဲ့ ရှိနေပါတယ်။ Native Android မှာဆိုရင် ViewModel နဲ့ ဆင်ပါတယ်။ Activity (သို့) Fragment ရဲ့ LifeCycle တစ်ခုလုံးမှာ destroy မဖြစ်ခင်အထိ LifeCycle State အားလုံးမှာ ViewModel က ရှိနေပါတယ်။ အဲဒီလိုပါပဲ StatefulWidget မှာရှိတဲ့ State ဟာလဲ initState က ပြီးတည်ရှိနေတာ dispose() မဖြစ်ခင် အချိန်အထိပါပဲ။

StatefulWidget တစ်ခုကို လေ့လာခြင်း

StatefulWidget အနေနဲ့ အလုပ် (၂) ခုလုပ်ပါတယ်။ ပထမတစ်ခုကတော့ ပြောင်းလို့မရတဲ့ Variable ထဲမှာ သွားချိတ်ပါတယ်။ StatelessWidget နဲ့အတူတူပါပဲ။ ဒါပေမယ့် သူက အလုပ်နောက်တစ်ခု ပိုလာပါတယ်။ အဲဒါကတော့ State Object တစ်ခုကိုပါ ဖန်တီးလိုက်တာပါ။ ကျွန်တော်တို့ ကုဒ်ရေးတဲ့အခါ ဒီလိုရေးလေ့ရှိပါတယ်။

အဲဒီ createState() က ဖန်တီးလိုက်တဲ့ State Object ကိုတော့ သေချာနားလည်ဖို့ လိုပါတယ်။ သူက StatelessWidget လို Dump Object တစ်ခုမဟုတ်ပါဘူး။ ကျွန်တော်တို့ Dart က StreamController တို့ StreamTransformer တို့ကို နားလည်ထားရင် ဒီဖက်ကလဲ နားလည်လွယ်နိုင်ပါတယ်။ 

Dart က Stream  တစ်ခုဟာ ဂိမ်းတစ်ခုဆော့နေတာနဲ့ တူပါတယ်။ JetPack Joyride လိုပေါ့။ စပြေးပြီဆိုတာနဲ့ Stream တစ်ခုစပါတယ်။ အထဲမှာ Event တွေ အများကြီး ဖြစ်နိုင်ပါတယ်။ ဥပမာ ရွှေတွေကိုစားမယ်ဆိုရင် အမှတ်တွေတိုးလာပါမယ်။ ကြားမှရှိတဲ့ အတားအစီးတွေကို ရှောင်ရပါမယ်။ နောက်ဆုံး မရှောင်နိုင်ရင် သေသွားပါမယ်။ အစက ပြန်စရပါမယ်။ အဲဒီလိုပါပဲ StreamController တစ်ခုက စလိုက်တဲ့ အရာတစ်ခုကနေ Event တွေ အများကြီး ရှိလာနိုင်ပါတယ်။ အဲဒီလို Event တွေအပေါ် မူတည်ပြီး StreamTransformer က လိုသလို ပြောင်းလဲပေးပါလိမ့်မယ်။အဲဒီ သဘောတရားတွေ နားလည်ထားမယ်ဆိုရင် StatefulWiget က State ကို အလွယ်တကူ နားလည်နိုင်ပါတယ်။

State LifeCycle

State LifeCycle

State Object ရဲ့ ပထမဦးဆုံး အဆင့်က Constructor ပါ။ ပုံမှန်သမာရိုးကျ Dart Constructor ပါပဲ။ Widget Tree ထဲကို မရောက်သေးပါဘူး။ State နဲ့ ပတ်သက်တာတွေ ဘာမှမလုပ်သင့်သေးတဲ့ နေရာပါ။ 

mounted ကတော့ State နဲ့ BuildContext နဲ့ ချိတ်သွားပါပြီ။ mounted ဆိုတာကို Wiget အနေနဲ့ အသက်ဝင်ပြီလား မဝင်ဘူးလားဆိုတာကို စမ်းချင်တဲ့အခါ true/false value အနေနဲ့ စစ်ဆေးလို့ရနိုင်ပါတယ်။

BuildContext ဆိုတာ Widget တစ်ခု တည်ဆောက်တဲ့အခါ လုပ်အပ်မယ့် အရာတွေ စုထားတဲ့ အပိုင်းဖြစ်ပါတယ်။ Native Android က context နဲ့ ဆင်တူသလို React’s context နဲ့လဲ ဆင်တူပါတယ်။ ဒါပေမယ့် သူက နှစ်ခုပိုလုပ်ပေးပါတယ်။ အဲဒါကတော့ Wigdet Tree ထဲမှာရှိတဲ့ Parent တွေနဲ့ ဆက်သွယ်ပေးပါတယ်။ အဲဒီအပြင် Screen မှာ Render ပြုလုပ်ပြီးပြီဆိုတာနဲ့ Screen ရဲ့ Size နဲ့ Position ကို ရယူပေးနိုင်ပါတယ်။

State.initState ကတော့ တစ်ကြိမ်တစ်ခါတည်း ခေါ်ပါတယ်။ Constructor ပြီးတာနဲ့ ဆက်တိုက် ခေါ်တာ ဖြစ်ပါတယ်။ အဲဒီအဆင့်မှာ ကျွန်တော်တို့ StatefulWidget နဲ့ သက်ဆိုင်နေတဲ့ ဒါမှမဟုတ် Parent Widgets တွေနဲ့ သက်ဆိုင်တဲ့ properties တွေ အဲဒီအပြင် build context နဲ့ သက်ဆိုင်တဲ့ properties တွေ သတ်မှတ်ပေးလို့ရပါတယ်။ အဲဒီအပြင် Streams Subscription, ChangeNotifier စတာတွေကို အဲဒီမှာ subscribe လုပ်လို့ရပါတယ်။

didChangeDependencies() ဆိုတာကတော့ initState ပြီးတာနဲ့ ချက်ချင်းခေါ်လိုက်ပါတယ်။ ဒါပေမယ့် InheritedWidget က အပြောင်းအလဲရှိရင်လဲ ဒီအဆင့်ကို ပြန်ရောက်လာပါတယ်။ InheritedWidget အပိုင်းကတော့ အသုံးပြုရတာ ရှုပ်ထွေးပါတယ်။ အဲဒီအတွက် ရွေးချယ်စရာအနေနဲ့ provider package နဲ့ bloc package တွေ ရှိပါတယ်။ 

dirty state ကတော့ widget ကို ပြန်တည်ဆောက်ဖို့ လိုနေတယ်လို့ Framework ကို အသိပေးတဲ့ အဆင့်ဖြစ်ပါတယ်။ dirty ဆိုတာနဲ့ rebuild ပြန်လုပ်ပါလိမ့်မယ်။ ပထမဦးဆုံး အကြိမ် Widget တည်ဆောက်တာဆိုရင်တော့ dirty state က မဖြစ်အနေ ဖြတ်ရပါမယ်။

dirty state ဆိုတာနဲ့ build state ကို ဖြတ်ရပါတယ်။ အဲဒီ build state မှာဆိုရင် Widget ကို ပြန်တည်ဆောက်ပါတယ်။ 

built လို့ ပြီးသွားရင်တော့ clean state လို့ သတ်မှတ်လိုက်ပါတယ်။ clean state ဟာ framework က တစ်ခုခု မခိုင်းဘူးဆိုရင် သူ့ state အတိုင်းထိန်းထားပါတယ်။

clean state က သိပ်ကို ထွေပြားပါတယ်။ သန့်ရှင်းနေပါတယ်ဆိုတဲ့ အဝတ်စလေးဟာ အချိန်မရွေး စွန်းပေသွားနိုင်သလိုပေါ့။ ဘယ်အခြေအနေတွေမှာ dirty ပြန်ဖြစ်သွားနိုင်သလဲဆိုတာ လေ့လာကြည့်ရအောင်

Clean State <=> Dirty State

  • setState() ဆိုတာနဲ့ dirty state ကို ပြန်ရောက်သွားနိုင်ပါတယ်။
  • Widget Tree အတွင်းမှာ Widget တွေ နေရာအပြောင်းအလဲဖြစ်တာနဲ့ Framework က didUpdateWidget ကို ခေါ်နိုင်ပါတယ်။ ဥပမာ – list ထဲမှာ နောက်ထပ် list တွေ ထပ်တိုးလာသလိုမျိုးပေါ့။ အဲဒီလို အခြေအနေဆိုရင် dirty state ကို ပြန်ရောက်သွားနိုင်ပါတယ်။
  • အကယ်၍ ကျွန်တော်တို့ Widget အနေနဲ့ InheritedWidget အပေါ်မှာ တည်မှီနေတယ်ဆိုရင် အဲဒီ InheritedWidget မှာ အပြောင်းအလဲတစ်ခုခု ဖြစ်တာနဲ့ Framework အနေနဲ့ didChangeDependencies ဆိုတာကို ခေါ်ပါလိမ့်မယ်။ အဲဒီလိုဆိုရင် initState ပြန်ရောက်သွားပြီး Widget rebuild ပြန်လုပ်ပါလိမ့်မယ်။
  • State.dispose မခေါ်သေးသ၍ Fluter အနေနဲ့ State ကို  Widget Tree ထဲက ဖယ်ထုတ်လိုက်တယ်ဆိုရင်တောင် ပြန်သုံးဖို့အတွက် သိမ်းပေးထားပါတယ်။ dispose ကို ခေါ်လိုက်ပြီဆိုရင်တော့ Widget က သုံးထားသမျှ အကုန် ဖယ်ထုတ်လိုက်ပါလိမ့်မယ်။ အဲဒီအချိန်မှာ setState သွားလုပ်မယ်ဆိုရင် Error တက်ပါလိမ့်မယ်။

StatefulWidget ရဲ့ State အကြောင်း လက်တွေ့ Experiment လုပ်ထားတာကို နောက် Tutorial မှာ ဆက်ရေးပါမယ်။