طراحی یک custom widget در فلاتر

بسم الله الرّحمٰن الرّحیماگر تازه با فلاتر آشنا شدین و هنوز برنامه درست و حسابی باهاش ننوشتین، این نوشته می‌تونه کمک خوبی بهتون بکنه. با این متن شما اصول اولیه طراحی یک صفحه در فلاتر + ساختن یه ویجت دلخواه + قراردادن تعدادی از اون ویجت توی یه صفحه رو یاد می‌گیرید. برای مثال ما ویجت زیر رو انتخاب کردیم که بهتون آموزش بدیم.قبل از این که مستقیما وارد طراحی بشیم، اجازه بدین توضیحاتی داشته‌باشیم برای افرادی که هیچ گونه آشنایی با فلاتر ندارن. اگر قبلا برنامه‌نویسی اندروید کار کرده باشین، احتمالا متن بیشتر به دردتون خواهد خورد.نصب و راه‌اندازیبا توجه به این که برای نصب و راه‌اندازی فلاتر تعداد زیادی متن و فیلم و عکس و ... به تمام زبان‌های دنیا وجود داره، و با یه سرچ ساده، میشه به تمام اون‌ها دسترسی داشت، ما از این قضیه می‌گذریم و به صورت پیش‌فرض در نظر می‌گیریم که شما فلاتر رو نصب کردین و آماده‌این برای کد زدن. اگه این مرحله رو نرفتین پیشنهاد می‌کنیم که این لینک رو مشاهده کنین: https://flutter.dev/docs ویجت چیست؟برنامه‌نویسی در فلاتر به زبان دارت هست. بر خلاف بسیاری از محیط‌های طراحی مثل اندروید که زبان منطق با زبان طراحی متفاوت هست، در فلاتر، زبان هر دو، دارت هست. بنابرین ما در طراحی فلاتر با آبجکت‌های زبان دارت روبرو هستیم. تمامی آبجکت‌های طراحی، از آبجکت Widget ارث‌بری دارن. بنابرین هر تکه طراحی یک صفحه در فلاتر، یک ویجت هست.انواع ویجتحالا اینجا چند تا از مهم‌ترین ویجت‌ها رو با هم بررسی می‌کنیم.Row, Column, Container, Expanded, Flexible, Stackبرای قسمت‌بندی و طراحی صفحه،Text, Image, Iconبرای نمایش محتوا در صفحه،RaisedButtonبرای ورودی گرفتن از کاربر و در نهایت می‌رسیم به دو ویجت مهم StateFullWidget و StateLessWidget.ویجت‌های قسمت‌بندی و محتوا (انتزاعی)۱. سطر یا Row: برای نمایش سطری چند ویجت. برای دوستانی که اندروید کار کردن، معادل Linear Layout با Orientation:horizontal هست.Row( children: [ ... ] ),۲. ستون یا Column: نمایش ستونی چند ویجت. معادل Linear Layout با Orientation:vertical در برنامه نویسی اندروید است.Column( children:[ ... ] ),۳. ظرف یا Container: خبر بد این هست که ویجت‌ها در فلاتر، بر خلاف طراحی در اندروید، قابلیت داشتن margin, padding, width, height ندارن. برای انجام این کار باید اون‌ها رو در یک Container قرار بدین. دقت کنین که کانتینر، بر خلاف Row و Column فقط و فقط یه ویجت درش قرار می‌گیره.Container( child: ... ),برای درک این ۳ ویجت به عکس زیر توجه کنین:۴. منبسط شده یا Expanded: ویجتی برای پر کردن فضای خالی باقی‌مانده در ویجت پدر هست. Expanded هم مثل Container فقط یه بچه داره. (یه ویجت در اون قرار می‌گیره) این ویجت معادل گذاشتن مقدار match_parent به ازای عرض و طول، در اندروید هست.Expanded( child: ... ),برای درک ویجت Expanded به عکس زیر توجه کنین. این عکس یک Column است که فرزند یک Expanded شده است و حداکثر مساحتی که می‌تواند اشغال می‌کند.۵. منعطف یا Flexible: این ویجت برای مدیریت اندازه دو ویجت در یک سطح هست. مثلا اگر شما می‌خواهید یک ویجت ۱٫۳ و یک ویجت دیگه ۲٫۳ صفحه رو بگیره، اون‌ها رو در Flexible قرار میدین و مقدار flex اولی رو ۱ و دومی رو ۲ می‌گذرین. دقیقا مثل weight در اندروید.Flexible( flex: 1, child: ... ), Flexible( flex: 2, child: ... ),برای درک Flexible عکس زیر را ببینین. این عکس دو ویجت Flexible است که در یک Row که فرزند یک ویجت Expanded است، قرار گرفته‌اند.۶. پشته یا Stack که برای روی هم گذاشتن دو ویجت استفاده میشه.Stack( children: [ ... ] ),عکس زیر استکی شامل سه فرزند Container است.ویجت‌های نمایش محتوا۱. متن یا Text: برای نمایش متن.Text('salam'),۲. عکس یا Image: برای نمایش عکس.Image(),۳. آیکون یا Icon: برای نمایش آیکون. خبر خوب اینه که خود فلاتر یه لیست کامل و جامع از آیکون‌ها داره که می‌تونین به شکل زیر ازش استفاده کنین.Icon(Icons.home),ویجت‌های ورودی گرفتندکمه یا RaisedButton: البته RaisedButton یکی از انواع دکمه هست ولی رایج‌ترین هست.RaisedButton( child: Text('click'), onPressed: () {} )برای مشاهده نمونه Text و Image و Icon و RaisedButton ‌می‌توانید عکس زیر را مشاهده کنید:دو ویجت خیلی مهم: StateFull و StateLess:این دو ویجت تعیین کننده این هستن که آیا وضعیت طراحی ما قابل تغییر هست یا نه. مثلا طراحی یه Text هیچ‌وقت تغییری نمی‌کنه یا یه آیکون. هیچ چیزی درون اون‌ها قابل تغییر نیست. همچنین یه صفحه که فقط یک متن داره، هیچ وقت تغییری نمی‌کنه. این ویجت‌ها باید StateLess باشن.اما این ویجت چی؟همون جوری که می‌بینین، این ویجت یه قلب داره که اگه کاربر کلیک کنه، باید فعال بشه و وضعیتش تغییر کنه. بنابرین ویجت کلی ما دو تا وضعیت خواهد داشت. یکی با قلب غیرفعال و یکی با قلب فعال. پس لازمه که StateFull باشه. البته چون هر ویجت Statefull هر بار با تغییر وضعیت، به طور کامل لود میشه، خیلی بهتره که کوچکترین واحد ممکن رو Statefull بکنین. مثلا در این جا، یک ویجت Statefull بسازین که فقط یه آیکون قلب داشته‌باشه.شیوه ساختن ویجت StateLess به این شکل:class MyStatelessWidget extends StatelessWidget{ @override Widget build(BuildContext context){ return Text(); } } و ویجت Statefull:class MyStatefullWidget extends StatefullWidget{ @override _MyStatefullWidgetState createState() => MyStatefullWidgetState(); } class MyStatefullWidgetState extends State<MyStatefullWidget> { @override Widget build(BuildContext context){ return Container(); } }که می‌توانیم با تابع setState وضعیت یک ویجت را تغییر بدهیم.طراحی ویجت مورد نظرابتدا سعی کنین ساختار کلی ویجت مورد نظر رو به دست بیارین. مثلا برای ویجت ما:همان‌طور که می‌بینید و در عکس با رنگ قرمز نشان داده شده‌است، ویجت کلی ما یک Column است که فرزندان آن به ترتیب یک ImageView، یک Row که با رنگ سبز نشان داده شده‌است و خود شامل فرزندان Text, Spacer, Icon است، یک Text و در نهایت یک Row که با رنگ سبز نشان داده شده‌است و شامل دو فرزند RaisedButton است، می‌باشد.Column:----Image----Row:------------Text------------Spacer------------Icon----Text----Row:------------RaisedButton------------RaisedButtonاگر فقط همین کلاس‌ها را پیاده‌سازی کنید، با صفحه‌ای مواجه خواهید شد که از نظر مکان متن‌ها و عکس و دکمه‌ها و آیکون، دلخواه ما نیست و همه از سمت چپ به هم چسبیده‌اند. برای حل این قضیه باید از Containerها، Paddingها و attributeهایی مثل mainAxisAlignment در Rowها و Columnها استفاده کرد. مثلا اگر mainAxisAlignment در یک سطر، Center باشد، ویجت‌های فرزند، در وسط آن سطر قرار می‌گیرند. یا اگر SpaceBetween باشد، ویجت‌ها حداکثر فاصله را از هم می‌گیرند.فقط round کردن اطراف ویجت مانده‌است که با استفاده از ویجتی مثل ClipRRect صورت می‌گیرد. همچنین برای فعال / غیرفعال کردن ویجت آیکون قلب، آن را در یک StatefullWidget تعریف می‌کنیم و دو استیت فعال و غیر فعال برای آن تعریف می‌کنیم.این کد را در گیت‌هاب می‌تونید ببینید: https://github.com/mmir98/FlutterCustomCardWidget/blob/master/CustomCardWidget/lib/CustomCardWidget.dart در این ویجت، ما ۵ ورودی برای ویجت داریم:class CustomCard extends StatelessWidget { final String title; final String description; final String imgPath; final double imageHeight; final double imageWidth; ...حال Constructor کلاس CustomCard ما به این صورت خواهد بود:‍‍‍CustomCard(this.title, this.description, this.imgpath, {this.imageHeight, this.imageWidth});که نشان {} در اطراف imageHeight و imageWidth به معنی اختیاری بودن وارد کردن آن‌ها هنگام ساخت ویجت می‌باشد.حال برای به کار بردن لیستی از این ویجت، کافیست داده‌ای مانند زیر داشته‌باشیم:List<Map<String, Object>> data = [ { &quottitle&quot: &quotCard title 1&quot, &quotdescription&quot: &quotSome text as description for card 1 Some text as description for card 1 Some text as description for card 1 Some text as &quot, &quotimg&quot: &quotimages/card1_img.jpg&quot }, { &quottitle&quot: &quotCard title 2&quot, &quotdescription&quot: &quotSome text as description for card 2&quot, &quotimg&quot: &quotimages/card2_img.jpg&quot }, { &quottitle&quot: &quotCard title 3&quot, &quotdescription&quot: &quotSome text as description for card 3&quot, &quotimg&quot: &quotimages/card3_img.jpg&quot } ]; در این‌جا به جای این که به صورت دستی داده‌ها را به شکل زیر وارد کلاس ویجت کنیم،CustomCard( title: data[1]['title'], description: data[1]['description'], imgPath: data[1]['img'], ), CustomCard( title: data[2]['title'], description: data[2]['description'], imgPath: data[2]['img'], ), CustomCard( title: data[3]['title'], description: data[3]['description'], imgPath: data[3]['img'], ), میتوانیم داده‌ها را به عنوان لیستی از ویجت‌های دلخواهی که طراحی کرده‌ایم وارد کنیم:children: <Widget>[ ...data.map((e) => CustomCard( title: e['title'], description: e['description'], imgPath: e['img'], // imageWidth: 500, //Define a specific width and height for the image in the customCard. //imageHeight: 180, )).toList() ], که در این حالت، ۳ ویجت از ویجت customCard که قبلا طراحی کردیم، خواهیم داشت.نویسندگان:محمدجواد میرتیموریمهدی فرزادیمحسن کثیری