مشخصات مقاله
مدیریت حالت در فلاتر
در این بخش، به آموزش مدیریت حالت در فلاتر میپردازیم و راههای مدیریت آن را بررسی میکنیم. میدانیم که در فلاتر، همه چیز ویجت است. این ویجتها به دو دسته تقسیم میشوند: ویجتهای بیحالت (Stateless) و ویجتهای دارای حالت (Stateful). ویجتهای بیحالت هیچ حالت داخلی ندارند؛ یعنی پس از ساخته شدن، نمیتوان آنها را تغییر داد یا ویرایش کرد مگر اینکه دوباره مقداردهی اولیه شوند. از طرف دیگر، ویجتهای دارای حالت پویا هستند و حالت دارند؛ به این معنا که میتوان آنها را در طول چرخه زندگیشان به راحتی تغییر داد بدون اینکه نیاز به مقداردهی مجدد باشد.
حالت چیست؟
حالت، اطلاعاتی است که هنگام ساخت ویجت قابل خواندن است و ممکن است در طول عمر برنامه تغییر کند یا ویرایش شود. اگر میخواهید ویجت خود را تغییر دهید، باید شیء حالت را بهروزرسانی کنید که این کار با استفاده از تابع setState() در ویجتهای دارای حالت امکانپذیر است. تابع setState() به ما اجازه میدهد تا خصوصیات شیء حالت را تنظیم کنیم که منجر به بازکشیدن رابط کاربری میشود.
مدیریت حالت یکی از فرآیندهای محبوب و ضروری در چرخه زندگی یک برنامه است. طبق مستندات رسمی، فلاتر اعلامی است. این بدین معناست که فلاتر رابط کاربری خود را با بازتاب حالت فعلی برنامهتان میسازد.
انواع مدیریت حالت در فلاتر
مدیریت حالت در فلاتر به دو نوع مفهومی تقسیم میشود:
حالت موقتی (Ephemeral State):
این حالت نیز به عنوان حالت UI یا حالت محلی شناخته میشود. این نوع از حالت مربوط به یک ویجت خاص است، یعنی حالتی که در یک ویجت منفرد نگهداری میشود. در این نوع حالت، نیازی به استفاده از تکنیکهای مدیریت حالت نیست. مثال رایج این حالت، فیلد متنی است.
مثال:
class MyHomepage extends StatefulWidget { @override MyHomepageState createState() => MyHomepageState(); } class MyHomepageState extends State{ String _name = "Peter"; @override Widget build(BuildContext context) { return RaisedButton( child: Text(_name), onPressed: () { setState(() { _name = _name == "Peter" ? "John" : "Peter"; }); }, ); } }
در مثال بالا، _name یک حالت موقتی است. در اینجا، تنها تابع setState() در کلاس StatefulWidget میتواند به _name دسترسی داشته باشد. متد build یک تابع setState() را فراخوانی میکند که تغییراتی در متغیرهای حالت ایجاد میکند. هنگام اجرای این متد، شیء ویجت با یکی جدید جایگزین میشود که مقدار متغیر تغییر یافته را نشان میدهد.
وضعیت برنامه (App State)
وضعیت برنامه با حالت موقتی تفاوت دارد. این نوع حالت، حالتی است که میخواهیم در قسمتهای مختلف برنامهمان به اشتراک بگذاریم و بین جلسات کاربری حفظ شود. بنابراین، این نوع حالت به صورت جهانی قابل استفاده است. گاهی اوقات به عنوان حالت برنامه یا حالت مشترک نیز شناخته میشود. برخی از مثالهای این حالت شامل ترجیحات کاربر، اطلاعات ورود به سیستم، اعلانها در یک برنامه شبکه اجتماعی، سبد خرید در یک برنامه تجارت الکترونیک، وضعیت خوانده شده/نشده مقالات در یک برنامه خبری و غیره است.
آموزش مدیریت وضعیت برنامه در فلاتر
سادهترین مثال برای مدیریت حالت برنامه را میتوان با استفاده از بسته provider آموخت. مدیریت حالت با provider آسان برای درک است و نیاز به کدنویسی کمتری دارد. provider یک کتابخانه شخص ثالث است. در اینجا، ما نیاز داریم تا سه مفهوم اصلی برای استفاده از این کتابخانه را درک کنیم:
- ChangeNotifier: ChangeNotifier یک کلاس ساده است که اطلاعرسانی تغییرات را به شنوندگان خود میدهد. درک و پیادهسازی آن آسان است و برای تعداد کمی از شنوندگان بهینهسازی شده است. برای شنوندهها به منظور مشاهده مدل برای تغییرات استفاده میشود. در اینجا، ما تنها از متد notifyListener() برای اطلاعرسانی به شنوندگان استفاده میکنیم.
- ChangeNotifierProvider: ChangeNotifierProvider یک ارائهدهنده برای ChangeNotifier است که میتواند آن را در درخت ویجتها فراهم کند، به طوری که هر ویجت در درخت بتواند به راحتی به آن دسترسی پیدا کند و از تغییرات آگاه شود.
- Consumer: Consumer ویجتی است که میتواند به ChangeNotifier گوش دهد و هر زمان که ChangeNotifier بهروزرسانی شود، بازسازی شود. این به اطمینان از اینکه UI همیشه با دادههای جاری هماهنگ است، کمک میکند.
ChangeNotifier
ChangeNotifier یک کلاس ساده است که به شنوندگان خود اطلاعرسانی تغییرات را میدهد. درک و پیادهسازی آن آسان است و برای تعداد کمی از شنوندگان بهینهسازی شده است. این کلاس برای زمانی استفاده میشود که میخواهیم شنوندگان تغییرات مدل را دنبال کنند. در این روش، تنها از متد notifyListeners() برای اطلاعرسانی به شنوندگان استفاده میکنیم.
به عنوان مثال، بیایید یک مدل بر پایهی ChangeNotifier تعریف کنیم. در این مدل، Counter با استفاده از ChangeNotifier گسترش مییابد که برای اطلاعرسانی به شنوندگان هنگام فراخوانی notifyListeners() به کار میرود. این تنها متدی است که نیاز به پیادهسازی در مدل ChangeNotifier دارد. در این مثال، دو تابع increment و decrement تعریف شدهاند که برای افزایش و کاهش مقدار استفاده میشوند. هر زمان که مدل به گونهای تغییر کند که ممکن است رابط کاربری برنامهی شما تغییر کند، میتوانیم متد notifyListeners() را فراخوانی کنیم.
import 'package:flutter/material.dart'; class Counter with ChangeNotifier { int _counter; Counter(this._counter); getCounter() => _counter; setCounter(int counter) => _counter = counter; void increment() { _counter++; notifyListeners(); } void decrement() { _counter--; notifyListeners(); } }
ChangeNotifierProvider
ChangeNotifierProvider ویجتی است که یک نمونه از ChangeNotifier را در اختیار فرزندان خود قرار میدهد و از بسته provider استفاده میکند. برای درک بهتر مفهوم ChangeNotifierProvider، به تکه کد زیر توجه کنید.
در اینجا، ما یک سازنده تعریف کردهایم که نمونه جدیدی از مدل Counter را ایجاد میکند. ChangeNotifierProvider مدل Counter را تنها زمانی بازسازی میکند که نیاز باشد. همچنین، به طور خودکار متد dispose() را روی مدل Counter فراخوانی میکند، زمانی که دیگر به نمونهی آن نیازی نباشد.
class MyApp extends StatelessWidget { @override Widget build(BuildContext context) { return MaterialApp( theme: ThemeData( primarySwatch: Colors.indigo, ), home: ChangeNotifierProvider( builder: (_) => CounterModel(), child: CounterView(), ), ); } }
وقتی نیاز باشد بیش از یک کلاس را در برنامه فلاتر خود فراهم کنید، میتوانید از MultiProvider استفاده کنید. MultiProvider لیستی از تمام Providerهای مختلفی است که در دامنه خود استفاده میشوند. بدون استفاده از این ویجت، مجبور بودیم Providerها را به صورت تو در تو قرار دهیم، به طوری که هر کدام زیرمجموعه دیگری باشند. میتوانید این مفهوم را از کد زیر درک کنید:
void main() { runApp( MultiProvider( providers: [ ChangeNotifierProvider(builder: (context) => Counter()), Provider(builder: (context) => SomeOtherClass()), ], child: MyApp(), ), ); }
Consumer
Consumer یک نوع ویجت از مجموعه Provider است که کار پیچیدهای انجام نمیدهد. این ویجت تنها ارتباط با Provider را در یک ویجت جدید برقرار کرده و پیادهسازی ساخت ویجت را به یک بیلدر محول میکند. کد زیر به شکل واضحتری این مفهوم را توضیح میدهد:
return Consumer( builder: (context, count, child) { return Text("Total price: ${count.total}"); }, );
در مثال بالا، میبینید که ویجت Consumer تنها به یک تابع بیلدر نیاز دارد، که هر زمان ChangeNotifier تغییر کند فراخوانی میشود. تابع بیلدر شامل سه آرگومان است که عبارتند از context، count، و child. آرگومان اول، context، در هر متد build() وجود دارد. آرگومان دوم نمونهای از ChangeNotifier است، و آرگومان سوم، child، برای بهینهسازی استفاده میشود. بهترین ایده این است که ویجت Consumer را تا حد امکان عمیق در درخت ویجتها قرار دهید.
این رویکرد به دو دلیل مفید است:
- کارایی بهینه: با قرار دادن Consumer در بخشهای عمیقتر درخت ویجتها، فقط آن بخشهایی از UI که واقعاً نیاز به بازسازی دارند در پاسخ به تغییرات مدل بهروز رسانی میشوند. این امر مانع از بازسازیهای غیرضروری میشود که میتواند بر عملکرد برنامه تأثیر بگذارد.
- کاهش وابستگی: با استفاده از Consumer در بخشهای مشخصی از درخت ویجتها که واقعاً به دادههای موجود در ChangeNotifier وابسته هستند، وابستگیهای بین بخشهای مختلف UI کاهش مییابد. این باعث میشود که مدیریت حالت و تغییرات در برنامه سادهتر و قابل مدیریتتر باشد.
به این ترتیب، استفاده از Consumer به صورت استراتژیک میتواند به بهبود ساختار و عملکرد برنامههای فلاتر کمک کند.