یک پدر برنامهنویس و عاشق خانواده (تماس: @skmohammadi)
ویجت FutureBuilder و بارگیری مجدد future در فلاتر

How to Moderate future reloading with flutter FutureBuilder widget
اگر با فلاتر(Flutter) آشنایی داشته باشید، حتما میدونید که ویجت FutureBuilder نمونهای عالی از قابلیت ترکیبپذیری (Composability) فلاتر هست که میتونه عملیات یا پروسهای از نوع Future رو در بر بگیره و امکان مدیریت حالتهای مختلف Loading، Result و Error رو به آسانی مهیا میکنه.
در این مدت یک ماههای که با فلاتر آشنا شدم و چالشهای مقدماتی رو پشت سرگذاشتم به راهکارها و تکنیکهای خوبی در استفاده از ویجتها و تعامل بین اونها رسیدم که قصد دارم به طور خلاصه برخی از اونها رو به اشتراک بذارم.
جلوگیری از بارگیری ناخواسته future در FutureBuilder
هنگامی که شما یک state در ویجت پدر رو تغییر میدید یا از یک route به route دیگه منتقل میشید و یا هر کاری که منجر به بازسازی Widgets Tree بشه، اتفاقی که میاوفته اینه که متد build از ویجت بالادستی یا جاری دوباره اجرا میشه و اگر داخل متد build عملیاتی مثل دریافت دیتا از بیرون یا اجرای یک عملیات زمانبر قرار گرفته باشه، این میتونه خیلی پرهزینه و زائد باشه.
ابتدا نگاهی به ساختار فراخوانی FutureBuilder بندازیم:
123456789101112131415@override Widget build(BuildContext context) { return FutureBuilder( future: _CustomOperation(), builder: (BuildContext context, AsyncSnapshot snapshot) { if (snapshot.hasError) { return Text('Error'); } else if (snapshot.connectionState == ConnectionState.done) { return Text('Date'); } else { return Text('loading'); } }, ); }
در قطعه کد بالا پارامتر future به صورت یک متد(تابع) از نوع Future فراخوانی شده. ویجت FutureBuilder مادامی که مقدار future تغییر کنه، شروع به گرفتن دیتای جدید میکنه و برای جلوگیری از این کار باید تابع مورد نظرمون به صورت ثابت تعریف بشه و موقع rebuild شدن ویجت، اون تابع باز تعریف نشه.
برای این منظور کافیه به شکل زیر عمل کنیم:
123456789101112131415161718192021222324252627class _ChildWidgetState extends State<ChildWidget> { Future _future; @override void initState() { super.initState(); _future = _CustomOperation(); } ... @override Widget build(BuildContext context) { return FutureBuilder( future: _future, builder: (BuildContext context, AsyncSnapshot snapshot) { if (snapshot.hasError) { return Text('Error'); } else if (snapshot.connectionState == ConnectionState.done) { return Text('Date'); } else { return Text('loading'); } }, ); } }
همان طور که مشخصه متغیر جدیدی به نام future_ تعریف کردیم و داخل متد initState مقداردهی کردیمش. راجع به متد initState همین قدر لازمه بدونید که موقع ساخته شدن اولیه ویجت فراخوانی میشه فقط یکبار. به همین خاطر متغیر future_ هم تنها یکبار مقدار دهی میشه. در ادامه متغیر future_ رو به جای CustomOperation_ به FutureBuilder پاس دادیم.
این تغییرات از اجرای ناخوسته عملیات future ما جلوگیری میکنه.
اجرای بارگیری مجدد future از داخل FutureBuilder
تنها کار لازم تغییر یا بازتعریف پارامتر future از FutureBuilder هست و به سادگی میتونیم متد runFuture_ رو به ChildWidget اضافه میکنیم و کارش اینه که مقدار future_ رو بازنشانی کنه :)
123void _runFuture() { _future = _CustomOperation(); }
و به شکل زیر این متد رو استفاده میکنیم:
12345678910111213141516171819202122232425@override Widget build(BuildContext context) { return FutureBuilder( future: _future, builder: (BuildContext context, AsyncSnapshot snapshot) { if (snapshot.hasError) { return Text('Error'); } else if (snapshot.connectionState == ConnectionState.done) { return Column( children: <Widget>[ RaisedButton( onPressed: () { _runFuture(); }, child: Text('Re-Run Future'), ), Text('Date'), ], ); } else { return Text('loading'); } }, ); }
اجرای بارگیری مجدد future از بیرون FutureBuilder
حال فرض کنید بخواهیم داخل ParentWidget بر اساس یک سری تغییرات (مثل تغییر یک state)، ویجت فرزند رو مجبور به اجرای مجدد future کنیم تا دیتا دوباره دریافت بشه.
ابتدا لازمه در ParentWidget یک GlobalKey برای دسترسی به ChildWidget تعریف کنیم:
1GlobalKey<_ChildWidgetState> _keyChild = GlobalKey();
حالا باید این key تعریف شده رو به ویجت ChildWidget پاس بدیم. با این کار قادر هستیم با استفاده متد currentState از کلید ایجاد شده، متد runFuture_ رو اجرا کنیم.
12345678910111213141516171819202122class ParentWidget extends StatefulWidget { @override _ParentWidgetState createState() => _ParentWidgetState(); } class _ParentWidgetState extends State<ParentWidget> { GlobalKey<_ChildWidgetState> _keyChild = GlobalKey(); @override Widget build(BuildContext context) { return Column( children: <Widget>[ RaisedButton(onPressed: () { _keyChild.currentState._runFuture(); }), ChildWidget( key: _keyChild, ) ], ); } }
فکر میکنم برای تازهکارهایی مثل خودم خیلی مشکلگشا باشه :)
اگر نظر، پیشنهاد و سوالی داشتید مطرح کنید، انشالله جواب میدم :D
مطلبی دیگر از این انتشارات
میانبرهای سریع در فلاتر
مطلبی دیگر از این انتشارات
ویجتها در فلاتر: چرخهی حیات
مطلبی دیگر از این انتشارات
مدیریت State و روش های آن در فلاتر