۵ راهکار برای بهبود عملکرد و performance فلاتر
در این مقاله به راهکارها و نکاتی اشاره خواهد شد که با رعایت آنها اپلیکیشن توسعه داده شده با فلاتر عملکردی سریع تر و بهینه تری خواهد داشت. به این نکته توجه داشته باشید رعایت این موارد نیازمند شرایطی خاص خود هست که در هر بخش توضیح داده شده.
قبل از شروع نکات لازم به ذکر است که آشنایی کامل با زبان دارت و خواص آن در فرآیند توسعه شما با فریمورک فلاتر تاثیر زیادی در بهینه کدنویسی شما دارد پس از نکته غافل نشوید. همچنین به تاریخ انتشار و آخرین آپدیت مقاله دقت داشته باشید چرا که ممکن است برخی از این موارد در ورژن های بعدی فلاتر تغییر کنند.
1- استفاده از for یاwhile بجای forEach و map
اگرچه کار با forEach و یا map کردن داده ها بسیار سطح بالاتر و راحت بنظر میرسد اما در کار با داده های حجیم استفاده از for و یا while سرعت بیشتری دارند. در فرآیند های معمول و داده های کم حجم بهینه ترین روش همان استفاده از forEach و map خواهد بود چراکه کد های خوانایی بیشتری خواهند داشت اما در حجم داده های زیاد این مورد براحتی مشهود و قابل تست است.
کافیست که با استفاده از متد stopwatch() زمان اجرا در حالت های مختلف اندازهگیری کنید:
void main() {
final stopwatch = Stopwatch()..start();
getDataFunction();
stopwatch.stop();
print('Function Execution Time : ${stopwatch.elapsed}');
}
در نمودار زیر میتوانید زمان اجرای برنامه با استفاده از متد های مختلف را ببینید:
بطور خلاصه دلایل وجود این اختلاف در زمان اجرا بدلیل طبیعت functional programming متدهای map و forEach دارای توابع objective داخلی برای کنترل تکرار بوده که نیازمند منابع پردازش بیشتر نسبت به for و while هستند که در متد های for وwhile بطور مستقیم اتفاق میافتد. یا در فرآیند mapping به ازای هر element یک لیست یا collection بطور معمول ایجاد میشود که نیازمند تخصیص memory در سیستم است.
2- آیکون ها و یا تصاویر اپلیکیشن را Precache کنید.
در مواردی که از ثابت بودن و عدم تغییر تصاویر و یا ایکون ها اطمینان دارید، آنها را با استفاده از متد های زیر precache کنید.
برای تصاویر بدون نیاز به اضافه کردن پکیج:
precacheImage(AssetImage(imagePath), context);
و برای ایکون های svg با استفاده از پکیج flutter_svg:
precachePicture(ExactAssetPicture(SvgPicture.svgStringDecoderBuilder, iconPath),context,);
۳- lazy loading
بیشتر توسعه دهندگان فلاتر برای افزایش سرعت load لیست ها و یا grid در داخل اپلیکیشن بجای map از متد builder استفاده میکنند درصورتی که اگر حجم داده ها از حدی بیشتر شود builder هم جوابگو نیست و نرم افزار با افت فریم یا لگ مواجه میشود؛ در این مواردgridview یا list را با استفاده همان builder پیاده سازی کنید اما این بار تنها داده هایی را که کاربر میبیند را render کنید و با هر بار اسکرول کاربر به انتهای صفحه در صورت وجود دادهی بیشتر برای نمایش، مابقی لیست با grid را بارگذاری کنید. مانند مثال زیر:
ایجاد یک تابع برای شبیه سازی دریافت داده از سمت سرور
class _ItemFetcher {
final _count = 103;
final _itemsPerPage = 5;
int _currentPage = 0;
// این تابع برای شبیه سازی گرفتن داده ها از سرور ساخته شده
Future<List<WordPair>> fetch() async {
final list = <WordPair>[];
final n = min(_itemsPerPage, count - currentPage * _itemsPerPage);
await Future.delayed(Duration(seconds: 1), () {
for (int i = 0; i < n; i++) {
list.add(WordPair.random());
}
});
_currentPage++;
return list;
}
}
ساخت اسکرین لیست و بارگذاری دادههای بعدی
class ListScreen extends StatefulWidget {
@override
ListScreenState createState() => ListScreenState();
}
class _ListScreenState extends State<ListScreen> {
final _pairList = <WordPair>[];
final itemFetcher = ItemFetcher();
bool _isLoading = true;
bool _hasMore = true;
@override
void initState() {
super.initState();
_isLoading = true;
_hasMore = true;
_loadMore();
}
// تابع اضافه کردن داده های بیشتر به لیست درصورت وجود
void _loadMore() {
_isLoading = true;
_itemFetcher.fetch().then((List<WordPair> fetchedList) {
if (fetchedList.isEmpty) {
setState(() {
_isLoading = false;
_hasMore = false;
});
} else {
setState(() {
_isLoading = false;
_pairList.addAll(fetchedList);
});
}
});
}
// ویجیت لیست داده ها
@override
Widget build(BuildContext context) {
return ListView.builder(
// نیاز به نمایش اسپینر در صورت وجود داده ی بیشتر
itemCount: hasMore ? pairList.length + 1 : _pairList.length,
itemBuilder: (BuildContext context, int index) {
// بررسی داده های داخل لیست
if (index >= _pairList.length) {
// جلوگیری
if (!_isLoading) {
// بار گزاری داده های بیشتر
_loadMore();
}
// ویجیت اسپینر
return Center(
child: SizedBox(
child: CircularProgressIndicator(),
height: 24,
width: 24,
),
);
}
// ویجیت لیست
return ListTile(
leading: Text(index.toString()),
title: Text(_pairList[index].asPascalCase),
);
},
);
}
}
4- پرهیز از rebuild کردن ویجت ها:
ویجت های stateful هر بار با تغییر هر state مجدد ساخته میشوند. در نتیجه سعی کرده ویجت هایی که در صورت تغییر state وضعیت آنها تغییر نمیکنند را از آنها ایزوله کنید مخصوصا در استفاده از state management های مختلف. اگر چه ممکن است در استفاده از برخی state management ها از ویجت stateful استفاده نکنید اما باز همچنان بازسازی مجدد در صورت تغییر sate اتفاق میافتد.
۵ – ویجت های قابل مشاهده
تنها ویجت هایی که کاربر آنها را میبیند را render کنید. در برخی موارد ممکن است با توجه به طراحی محصول دو ویجت متفاوت را در یک صفحه داشته باشید که باتوجه به شرایط مختلف هر بار یکی از آنها را نمایش دهید. در چنین حالتی دو ویجت را از ویجت مادر کاملا جدا کنید و با توجه به شرط موجود ویجتی را که باید نمایش داده شود را به ویجت مادر برگردانید. پیاده سازی این روش بسته به معماری های مختلف مورد استفاده در فلاتر متفاوت است اما این نکته را به یاد داشته باشید که هر ویجتی که داخل ویجت مادر باشد حتی درصورت عدم نمایش به کاربر بصورت کامل render میشود پس بهتر است که در همان مرحلهی اول در ساختار اصلی قرار نگیرد.
ممنون که تا اینجای مقاله همراه بودید. لطفا مباحث مورد نیاز خودتون رو در بخش نظرات وارد کنید تا در مقالات بعدی به اونها بپردازیم.
کارآفرین حوزهی پزشکی، توسعه دهنده موبایل و طراح محصول - همبنیان گذار و مدیر عامل استارت آپ طب یاد - همبنیان گذار و مدیر محصول مرکز مراقبت پزشکی، پرستاری در منزل سوژین - عضو انجمن صنفی رایانهای حضور در پروژه های متعدد داخلی و خارجی بعنوان توسعه دهنده موبایل و طراح محصول - حضور در پروژه های متعدد علوم پزشکی بعنوان توسعه دهنده موبایل