گردش داده و نمایندگی رویداد در فلاتر — از صفر تا صد
در زمان توسعه اپلیکیشنهای فلاتر، باید اطمینان پیدا کنیم که ویجتها تا حد امکان قابلیت استفاده مجدد دارند. بدین ترتیب ویجتها تا حد امکان از هر نوع منطقی آزاد باقی میمانند. در این نوشته به بررسی برخی تکنیکهای گردش داده و نمایندگی رویداد در فلاتر میپردازیم که موجب میشود اپلیکیشنهای فلاتر انعطافپذیری بیشتری یافته و پیوند کمتری با منطق پروژه داشته باشند.
گردش داده
یک سناریوی معمول در اپلیکیشنهای فلاتر، دانلود کردن دادهها از شبکه و سپس نمایش آن روی صفحه است. یکی از رویکردهای ممکن برای اجرای این سناریو، ارسال درخواست شبکه در یک ویجت و سپس دانلود کردن دادهها است.
این رویکرد به صورت زیر پیادهسازی میشود:
1class CustomerList extends StatelessWidget {
2
3 final data = [1,2,3,4,5]; // data from the web service
4
5 Widget build(BuildContext context) {
6 return ListView.builder(
7 itemCount: data.length,
8 itemBuilder: (context, index) {
9 return ListTile(
10 title: Text("Hello World")
11 );
12 }
13 );
14 }
15
16}
17
18class HomePage extends StatelessWidget {
19
20 Widget build(BuildContext context) {
21 return Scaffold(
22 appBar: AppBar(
23 title: Text("Flutter")
24 ),
25 body: CustomerList()
26 );
27 }
28
29}
مشکل رویکرد فوق این است که پیوند زیادی با CustomerList و همچنین با دادههای آرایه دارد. این بدان معنی است که CustomerList نمیتواند دادهها را از والد گرفته و نمایش دهد. بدین ترتیب صرفاً میتواند روی آرایه اعلانشده درون ویجت CustomerList کار کند و امکان استفاده از آن در سناریوهای دیگر وجود ندارد.
یک روش بهتر برای مدیریت این سناریو این است که دادهها از والد به ویجت CustomerList ارسال شوند و سپس نمایش یابند. این رویکرد در کد زیر پیادهسازی شده است:
1class CustomerList extends StatelessWidget {
2
3 final List<int> data;
4
5 CustomerList({this.data});
6
7 Widget build(BuildContext context) {
8 return ListView.builder(
9 itemCount: data.length,
10 itemBuilder: (context, index) {
11 return ListTile(
12 title: Text("Hello World")
13 );
14 }
15 );
16 }
17
18}
19
20class HomePage extends StatelessWidget {
21
22 final data = [1,2,3,4,5];
23
24 Widget build(BuildContext context) {
25 return Scaffold(
26 appBar: AppBar(
27 title: Text("Flutter")
28 ),
29 body: CustomerList(data: data)
30 );
31 }
32
33}
این کد مشابه نسخه قبلی است، اما یک تفاوت مهم دارد. این بار دادهها را به ویجت CustomerList ارسال میکنیم و ویجت CustomerList تنها برای نمایش دادهها استفاده میشود و کاری به جز آن ندارد. این بدان معنی است که CustomerList مسئول واکشی کردن دادهها نیست. تنها مسئولیت این ویجت نمایش دادن دادهها است. CustomerList میتواند به سادگی در صفحههای دیگر استفاده شود و در این حالت میتوان مجموعه متفاوتی از دادههای مشتری را به ویجت ارسال کرد. در زمان توسعه اپلیکیشنهای فلاتر باید اطمینان پیدا کنیم که دادهها از والد به فرزند ارسال میشوند.
نمایندگی
ویجتها صرفاً برای نمایش دادهها استفاده نمیشوند، بلکه میتوانند رویدادهایی نیز ایجاد کنند. زمانی که رویدادها در ویجت فرزند ایجاد میشوند، بهتر است «نمایندگی رویداد» (Event Delegation) را به والد بدهیم تا اکشن مناسب را اجرا کند. کد زیر را که در آن ویجت CustomerList یک اکشن روی رویداد onTap مربوط به ListTile انجام میدهد در نظر بگیرید:
1class CustomerList extends StatelessWidget {
2
3 final List<int> data;
4
5 CustomerList({this.data});
6
7 void _navigateToCustomerDetailsScreen(BuildContext context) {
8
9 Navigator.push(context,MaterialPageRoute(builder: (context) => Text("Customer Details Screen")()));
10
11 }
12
13 Widget build(BuildContext context) {
14 return ListView.builder(
15 itemCount: data.length,
16 itemBuilder: (context, index) {
17 return ListTile(
18 onTap: () {
19 // navigate to the other screen
20 _navigateToCustomerDetailsScreen(context)
21 },
22 title: Text("Hello World")
23 );
24 }
25 );
26 }
27
28}
مشکل رویکرد فوق آن است که ویجت CustomerList اکنون به طور کامل با اکشن _navigateToCustomerDetailsScreen پیوند یافته است. روش بهتر آن است که به والد اجازه دهیم تصمیم بگیرد در زمان تپ شدن ویجت، میخواهد چه کاری انجام دهد. پیادهسازی آن به صورت زیر است:
1class CustomerList extends StatelessWidget {
2
3 final List<int> data;
4 final Function onSelected;
5
6 CustomerList({this.data,this.onSelected});
7
8 Widget build(BuildContext context) {
9 return ListView.builder(
10 itemCount: data.length,
11 itemBuilder: (context, index) {
12 return ListTile(
13 onTap: this.onSelected,
14 title: Text("Hello World")
15 );
16 }
17 );
18 }
19
20}
این رویکرد بسیار بهتر است، زیرا اکنون نمایندگی رویداد به والد داده شده است و بدین ترتیب والد بهتر میتواند در مورد کاری که میخواهد انجام بدهد، تصمیمگیری کند. این وضعیت موجب میشود که CustomerList انعطافپذیری بیشتری پیدا کند، چون اکنون میتواند نیازهای والدهای مختلفی را برآورده سازد.
سازماندهی و نامگذاری
ساختار اپلیکیشن به راهنماهایی که شرکت ارائه میکند، بستگی دارد. ما پس از تحقیق و خواندن کد توسعهدهندگان مختلف مجموعه راهنماییهای کوچک زیر را جمعبندی کردهایم:
- نامگذاری فایلهای دارت به شیوه «حالت ماری» (snake case) باشد. برای نمونه از نام customer_list.dart استفاده کنید.
- Page نشاندهنده صفحههای اپلیکیشن باشد که شامل ویجتها میشود. برای نمونه HomePage ،CustomerListPage و غیره.
- هر نوع از کلاسها در پروژه در پوشه مجزایی سازماندهی شوند، برای نمونه صفحهها، ویجتها، سرویسها و غیره.
سخن پایانی
در این مقاله با گردش دادهها در اپلیکیشن فلاتر آشنا شدیم و پیشنهاد کردیم که ارتباط فرزند به والد با استفاده از الگوی نمایندگی مناسب خواهد بود. امیدواریم از مطالعه این رهنما بهره آموزشی لازم را برده باشید.
اگر این مطلب برای شما مفید بوده است، آموزشهای زیر نیز به شما پیشنهاد میشوند:
- مجموعه آموزشهای برنامهنویسی اندروید
- مجموعه آموزشهای برنامهنویسی
- گوگل فلاتر (Flutter) از صفر تا صد — ساخت اپلیکیشن به کمک ویجت
- فلاتر برای وب — راهنمای مقدماتی
- مفاهیم مقدماتی فلاتر (Flutter) — به زبان ساده
==