شروع با فلاتر
با وجود سیستمعاملهای مختلفی که دستگاههای موبایل و دسکتاپ را اجرا میکنند، توسعه کراس پلتفرمی مدتها هدفی برای توسعه اپلیکیشن بوده است. توانایی نوشتن یک codebase و انتشار در چندین پلتفرم باعث صرفه جویی در زمان و تلاش قابل توجهی برای شرکت و تیم شما می شود.
فلاتر چیست؟
فلاتر یکی از جدیدترین فریم ورک هایی که وارد عرصه کراس پلتفرم شده است، Flutter گوگل است. در حالی که Flutter در ابتدا فقط از پلتفرمهای موبایل اندروید و iOS پشتیبانی میکرد، از آن زمان به بعد گسترش یافته و شامل پشتیبانی از وب، macOS، ویندوز، لینوکس، فوشیا و دستگاه تعبیه شده (embedded device) شده است.
در این آموزش، یک برنامه فلاتر به نام GHFlutter میسازید که API GitHub را برای اعضای تیم در یک سازمان GitHub جستجو میکند و اطلاعات را در یک لیست قابل پیمایش(scrollable) نمایش میدهد:
شما می توانید با استفاده از شبیه ساز iOS، شبیه ساز اندروید، یک مرورگر وب، یک برنامه دسکتاپ native یا همه موارد فوق، برنامه را توسعه دهید!
در اینجا چند کار وجود دارد که در حین ساختن برنامه یاد می گیرید که چگونه با Flutter انجام دهید:
- یک محیط توسعه راه اندازی کنید.
- یک پروژه جدید ایجاد کنید.
- از hot reload استفاده کنید.
- فایل ها و پکیج ها را import کنید.
- ویجت ها را ایجاد و استفاده کنید.
- برقراری network calls
- نمایش item ها در یک لیست
- یک theme برنامه اضافه کنید.
در این بین، کمی دارت را نیز یاد خواهید گرفت!
شروع یادگیری فلاتر
می توانید از macOS، Linux، Windows یا Chrome OS برای توسعه Flutter خود استفاده کنید. در حالی که می توانید از هر ویرایشگری با زنجیره ابزار Flutter استفاده کنید، پلاگین های IDE برای IntelliJ IDEA، Android Studio و Visual Studio Code وجود دارد که چرخه توسعه را آسان تر می کند.
- این آموزش از VS Code استفاده می کند.
مقدمه ای بر فلاتر
اپلیکیشن های فلاتر از زبان برنامه نویسی Dart استفاده می کنند. dart ، feature های مشترکی با زبان های مدرن دیگر مانند کاتلین و سوئیفت دارد . همچنین می توانید Dart را به کد جاوا اسکریپت تبدیل کنید.
به عنوان یک فریمورک کراس پلتفرمی، Flutter بسیار شبیه React Native است. هر دو امکان یک سبک برنامه نویسی واکنشی و اعلامی را فراهم می کنند. با این حال، برخلاف React Native، Flutter نیازی به استفاده از JavaScript bridge ندارد، که در نتیجه زمان راه اندازی برنامه و عملکرد کلی را بهبود می بخشد. دارت با استفاده از کامپایل Ahead-Of-Time (AOT) به این امر دست می یابد.
دارت همچنین می تواند از کامپایل Just-In-Time (JIT) استفاده کند. کامپایل JIT با Flutter با اجازه دادن به hot reload برای refresh کردن UI در حین توسعه بدون نیاز به build جدید، روند توسعه را بهبود می بخشد.
همانطور که در این آموزش خواهید دید، فریمورک Flutter بر اساس ایده widget ها ساخته شده است.
تنظیم محیط توسعه شما
- بسته نصبی را برای سیستم عامل دستگاه توسعه خود دانلود کنید تا آخرین نسخه پایدار Flutter SDK را دریافت کنید.
- بسته نصبی را در محل دلخواه extract کنید.
- ابزار flutter را به مسیر خود اضافه کنید.
- دستور flutter doctor را اجرا کنید، که به شما در مورد هر گونه مشکل در نصب فلاتر هشدار می دهد.
- وابستگی های(dependency) جا افتاده را نصب کنید.
- IDE خود را با extension یا پلاگین Flutter تنظیم کنید.
- تست و ارزیابی یک برنامه
برای اجرای پروژه خود به عنوان یک اپلیکیشن موبایل، باید از یکی از گزینه های زیر استفاده کنید:
- شبیه ساز iOS یا شبیه ساز اندروید را اجرا کنید.
- یک دستگاه iOS یا Android را برای توسعه راه اندازی کنید.
- کد خود را به عنوان یک برنامه وب اجرا کنید.
- در نهایت ، می توانید کد خود را به عنوان یک برنامه دسکتاپ اجرا کنید.
حتی اگر هدف نهایی شما موبایل باشد، استفاده از یک برنامه وب یا دسکتاپ در طول توسعه به شما این مزیت را می دهد که بتوانید اندازه برنامه را تغییر دهید و مشاهده کنید که با اندازه های مختلف صفحه نمایش چگونه به نظر می رسد. اگر رایانه قدیمیتری دارید، نسخه وب یا دسکتاپ نیز سریعتر از شبیهساز Android یا شبیهساز iOS بارگیری میشود.
توجه: برای ساخت و آزمایش بر روی iOS Simulator یا یک دستگاه iOS، باید از macOS با Xcode استفاده کنید. همچنین، حتی اگر قصد دارید از VS Code بهعنوان IDE اصلی خود استفاده کنید، سادهترین راه برای دریافت Android SDK و شبیهساز اندروید، نصب Android Studio است.
ایجاد یک پروژه جدید
در VS Code با افزونه Flutter نصب شده، با انتخاب View ▸ Command Palette ، یا فشار دادن Command-Shift-P در macOS یا Control-Shift-P در لینوکس یا ویندوز، Command palette را باز کنید. Flutter: New Application Project را در پالت وارد کرده و Return را فشار دهید.
پوشه ای را برای ذخیره پروژه انتخاب کنید. سپس، ghflutter را برای نام پروژه وارد کنید، Return را فشار دهید و منتظر بمانید تا Flutter پروژه را در VS Code راه اندازی کند. وقتی پروژه آماده شد، main.dart را در ویرایشگر خود خواهید دید.
در VS Code، پنلی را در سمت چپ می بینید که ساختار پروژه شما را نشان می دهد. فولدرهایی برای اندروید، iOS و web وجود دارد. این پوشه ها حاوی فایل های لازم برای استقرار برنامه شما در آن پلتفرم ها هستند. همچنین یک پوشه lib وجود دارد که حاوی main.dart است و دارای کدی است که برای هر دو سیستم عامل اعمال می شود. شما عمدتاً در پوشه lib در این آموزش کار خواهید کرد.
تست بخش مهمی از توسعه Flutter است. با این حال، در این مقاله از آن صرفه نظر می کنیم. بنابراین پوشه تست را با کلیک راست روی آن و انتخاب Delete از منو حذف کنید.
ویرایش کد (Editing the Code)
سپس کد main.dart را با کد زیر جایگزین کنید:
import 'package:flutter/material.dart';
void main() => runApp(const GHFlutterApp());
class GHFlutterApp extends StatelessWidget {
const GHFlutterApp({Key? key}) : super(key: key);
@override
Widget build(BuildContext context) {
return MaterialApp(
title: 'GHFlutter',
home: Scaffold(
appBar: AppBar(
title: const Text('GHFlutter'),
),
body: const Center(
child: Text('GHFlutter'),
),
),
);
}
}
توجه: اگر از VS Code استفاده می کنید، به یاد داشته باشید که تغییرات خود را ذخیره کنید تا اعمال شوند. File ▸ Save را از منوی VS Code انتخاب کنید یا Command-S را در macOS یا Control-S را در ویندوز یا لینوکس فشار دهید.
اگر از Android Studio استفاده میکنید، نگران ذخیرهسازی هدفمند نباشید .بهطور پیشفرض ذخیرهسازی خودکار انجام میشود.
در کد بالا، main از اپراتور => برای یک تابع single lineجهت اجرای برنامه استفاده می کند. شما یک کلاس برای برنامه به نام GHFlutterApp دارید.
برنامه شما خود یک ویجت stateless است. بیشتر موجودیتهای موجود در یک برنامه Flutter، ویجتها هستند، خواه stateless یا stateful باشند. برای ایجاد ویجت برنامه خود، build را override کنید. درست بعد از کلمه کلیدی return ، ویجت MaterialApp را مشاهده می کنید که به برنامه شما کمک می کند تا با دستورالعمل های Material Design مطابقت داشته باشد.
اجرای برنامه
اکنون، زمان اجرای برنامه ساده خود است. روی پلتفرم انتخاب شده فعلی در پایین سمت راست کلیک کنید تا لیستی از تمام پلتفرم های موجود که می توانند برنامه شما را اجرا کنند دریافت کنید. تصویر اینجا نشان می دهد که مرورگر وب کروم، شبیه ساز ios و شبیه ساز اندروید Pixel در دسترس هستند.
یک پلتفرم – به عنوان مثال، امولاتور تلفن همراه Pixel – را انتخاب کنید و منتظر بمانید تا امولاتور راه اندازی شود.
پس از آماده شدن امولاتور ، با فشار دادن F5، با انتخاب Debug ▸ Start Debugging از منو یا با کلیک کردن روی نماد پخش مثلثی در بالا سمت راست، آن را build بگیرید و اجرا کنید. Debug Console باز می شود و اگر روی اندروید اجرا می کنید، Gradle را در حال انجام build خواهید دید. اگر روی iOS اجرا شود، Xcode را در حال build پروژه خواهید دید.
این برنامه در شبیه ساز اندروید اجرا می شود:
لحظه ای وقت بگذارید و از این که به تازگی اولین برنامه Flutter خود را اجرا کردید، قدردانی کنید.
بنر DEBUG که در گوشه سمت راست بالا می بینید نشان می دهد که برنامه در حالت debug اجرا می شود.
برنامه در حال اجرا را با کلیک کردن بر روی دکمه مربع قرمز رنگ Stop در سمت راست نوار ابزار که در بالای پنجره VS Code قرار دارد، متوقف کنید:
استفاده از Hot Reload
یکی از بهترین جنبه های توسعه Flutter این است که می توانید همزمان با ایجاد تغییرات، برنامه خود را دوباره بارگیری کنید. این ویژگی به شما این امکان را می دهد که با به روز رسانی بخش های مختلف UI، فیدبک فوری دریافت کنید.
Build کنید و دوباره اجرا کنید:
اکنون، بدون توقف برنامه در حال اجرا، (رشته) string ، app bar برنامه را در main.dart به چیز دیگری تغییر دهید:
appBar: AppBar(
title: const Text('GHFlutter App'),
),
قبلاً در قسمت appbar، GHFlutter نوشته شده بود. حالا میگه GHFlutter App.اکنون main.dart را ذخیره کنید. با این کار به طور خودکار hot reload را نیز راه اندازی می کند، اما می توانید روی دکمه Hot Reload نیز کلیک کنید:
تقریباً بلافاصله تغییرات را در برنامه در حال اجرا مشاهده میکنید:
فیچر Hot Reload ممکن است همیشه کار نکند (مستندات رسمی Hot Reload ، توضیحات خوبی در باره علت کار نکردن آن ارائه می دهد) اما به طور کلی، زمانی که در حال ایجاد UI خود هستید، زمان بسیار خوبی برای شما صرفهجویی میکند.
Import کردن فایل
زمانی می رسد به جای اینکه همه کدهای dart خود را در یک فایل main.dart نگه دارید، می خواهید بتوانید کد را از فایل های دیگری که ایجاد می کنید Import کنید. اکنون نمونهای برای Import کردن stringها مشاهده خواهید کرد، که در مواقعی که نیاز به localize کردن string های user-facing خود دارید به شما کمک میکند.
با کلیک بر روی lib و سپس کلیک کردن بر روی دکمه New File یک فایل به نام strings.dart در پوشه lib ایجاد کنید:
خط زیر را به فایل جدید اضافه کنید:
const appTitle = 'GHFlutter';
توجه: اگر با یک زبان برنامه نویسی متفاوت تجربه دارید، ممکن است عادت داشته باشید که string ها را به عنوان static constants (ثابت های استاتیک) در یک کلاس نگه دارید. دارت، constant های top-level را خارج از یک کلاس اجازه می دهد. گروه بندی آنها در یک فایل کافی است.
وارد کردن زیر را به بالای main.dart اضافه کنید:
import 'strings.dart' as strings;
ویجت خود را برای استفاده از strings.dart تغییر دهید. GHFlutterApp به شکل زیر خواهد بود:
class GHFlutterApp extends StatelessWidget {
const GHFlutterApp({Key? key}) : super(key: key);
@override
Widget build(BuildContext context) {
return MaterialApp(
title: strings.appTitle,
home: Scaffold(
appBar: AppBar(
title: const Text(strings.appTitle),
),
body: const Center(
child: Text(strings.appTitle),
),
),
);
}
تغییرات خود را ذخیره کنید و hot reload را انجام دهید. اکنون برنامه به حالت اولیه خود بازگشته است، اما شما از یک string از طریق strings.dart استفاده می کنید.
درک ویجت ها
تقریباً هر المنت در برنامه Flutter شما یک ویجت است. ویجتها به گونهای طراحی شدهاند که immutable یا unchangeable (غیر قابل تغییر) باشند، زیرا استفاده از ویجتهای immutable (تغییر ناپذیر) به سبک بودن UI اپلیکیشن کمک میکند. میتوانید ویجتها را مانند یک نقشه طرح اولیه(blueprint)تصورکنید که نشان میدهند UI چگونه باید باشد.
شما از دو نوع اصلی ویجت استفاده خواهید کرد:
Stateless: ویجت هایی که فقط به اطلاعات پیکربندی خودشان، مانند یک تصویر static در image view ، وابسته هستند.
Stateful: ویجت هایی که نیاز به حفظ اطلاعات dynamic دارند. آنها این کار را با تعامل با یک آبجکت State انجام می دهند.
هر دو ویجت Stateless و Stateful در هر زمانی که فریمورک Flutter به آنها دستور دهد دوباره ترسیم می شوند. تفاوت در این است که ویجت های Stateful پیکربندی خود را به یک آبجکت State واگذار می کنند.
ساخت ویجت ها
برای ساختن ویجت های خود، به انتهای main.dart بروید و شروع به تایپ stful (مخفف “stateful”)کنید. با این کار یک pop-up شبیه به زیر ، ظاهر می شود:
برای انتخاب گزینه اول، Return را فشار دهید.
VS Code به شما کمک می کند تا نام را با استفاده از cursor (مکان نما) های متعدد پر کنید. GHFlutter را بنویسید:
این کد جدیدی است که به تازگی اضافه کرده اید:
class GHFlutter extends StatefulWidget {
const GHFlutter({ Key? key }) : super(key: key);
@override
_GHFlutterState createState() => _GHFlutterState();
}
class _GHFlutterState extends State<GHFlutter> {
@override
Widget build(BuildContext context) {
return Container(
);
}
}
در اینجا چند نکته قابل توجه است:
شما یک subclass ، ویجت Stateful به نام GHFlutter ساخته اید.
خطی که با const شروع می شود سازنده (constructor)کلاس است.
برای ایجاد آبجکت State ویجت stateful، createState را overriding می کنید.
GHFlutterState_ نام کلاس state است. Underscore در GHFlutterState_ به این معنی است که این کلاس، فایل-خصوصی (file-private) است. نمی توان آن را به فایل های دیگر importکرد.
build مکانی اصلی است که در آن ویجت های خود را می سازید. این یکی در حال حاضر یک Container خالی را به طور پیش فرض برمی گرداند. بعداً آن را با چیز دیگری عوض خواهید کرد.
تمام متد build را در GHFlutterState_ با موارد زیر جایگزین کنید:
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: const Text(strings.appTitle),
),
body: const Text(strings.appTitle),
);
}
Scaffold، یک container برای ویجت های Material Design است که به عنوان root سلسله مراتب ویجت عمل می کند. در اینجا، یک AppBar و یک body به Scaffold اضافه کردهاید، و هر کدام حاوی یک ویجت Text هستند. کد زیر را جایگزین GHFlutterApp کنید:
class GHFlutterApp extends StatelessWidget {
const GHFlutterApp({Key? key}) : super(key: key);
@override
Widget build(BuildContext context) {
return MaterialApp(
title: strings.appTitle,
home: const GHFlutter(),
);
}
}
اکنون، GHFlutterApp از GHFlutter جدید شما بهعنوان attribute اصلی خود استفاده میکند، بهجای اینکه خودش یک Scaffold بسازد.نکته: کلمه کلیدی const که گاهی در مقابل ویجت ها و متغیرها می بینید نشان دهنده ثابت زمان کامپایل (compile-time)است. اضافه کردن const همیشه ضروری نیست، اما انجام این کار به Flutter اجازه می دهد تا برخی بهینه سازی ها را انجام دهد.
Hot reload را انجام دهید و ویجت جدید را در عمل خواهید دید:
شما هنوز تغییر زیادی نکرده اید، اما اکنون برای ساخت ویجت جدید آمادگی لازم را پیدا کرده اید.
برقراری تماس های شبکه(Network Calls)
قبلاً، شما strings.dart را به پروژه وارد کرده بودید. به طور مشابه، می توانید package های دیگر را از فریمورک Flutter یا حتی از توسعه دهندگان دیگر import کنید. به عنوان مثال، اکنون از چند پکیج اضافی برای برقراری تماس شبکه HTTP و parseکردن JSON response به آبجکت های دارت استفاده خواهید کرد.
Import کردن پکیج ها
دو Import جدید در بالای main.dart اضافه کنید:
import 'dart:convert';
import 'package:http/http.dart' as http;
متوجه خواهید شد که پکیج http ، در دسترس نیست. دلیلش این است که هنوز آن را به پروژه اضافه نکرده اید.به pubspec.yaml در فولدر اصلی پروژه خود بروید. در بخش dependency ها، درست در زیر cupertino_icons cupertino_icons: ^1.0.2, خط زیر را اضافه کنید:
http: ^0.13.3
توجه: به تورفتگی دقت کنید. از همان تورفتگی دو فاصله ای که پکیج cupertino_icons دارد استفاده کنید.
اکنون، وقتی pubspec.yaml خود را ذخیره می کنید، Flutter extension در VS Code دستور flutter pub get را اجرا می کند. Flutter ، پکیج http ارائه شده را دریافت می کند و آن را در main.dart در دسترس قرار می دهد.
اکنون خطوط آبی را در زیر دو import اخیر خود در main.dart خواهید دید که نشان میدهد در حال حاضر استفاده نشدهاند.
نگران نباشید شما فقط در مدت کوتاهی از آنها استفاده خواهید کرد..
استفاده از کد ناهمزمان (Asynchronous Code)
برنامه های دارت تک نخی (single-threaded) هستند، اما دارت از اجرای کد روی threadهای دیگر پشتیبانی می کند. همچنین از اجرای کدهای ناهمزمان کهthread UI را مسدود نمی کند، پشتیبانی می کند. برای این کار از pattern (الگوی) async/wait استفاده می کند.توجه: بسیاری از مبتدیان به اشتباه تصور می کنند که method های ناهمزمان بر روی thread دیگری اجرا می شوند. در حالی که task (وظایف) I/O مانند تماسهای شبکه که به سیستم واگذار میکنید، روی system thread مختلف اجرا میشوند.
کدهایی که خودتان در متدهای async مینویسید، همگی روی UI thread اجرا میشوند. این برنامه قرار است بعداً اجرا شود، زمانی که UI مشغول نیست. اگر واقعاً میخواهید کدی را روی thread دیگری اجرا کنید، باید چیزی را ایجاد کنید که یک ایزوله دارت جدید (new Dart isolate) نامیده میشود.در مرحله بعد، یک تماس شبکه ناهمزمان (asynchronous network call) برای بازیابی لیستی از اعضای تیم GitHub برقرار می کنید. برای انجام این کار، یک لیست خالی به عنوان property در GHFlutterState_ و همچنین یک property برای نگه داشتن یک text style اضافه کنید:
var _members = <dynamic>[];
final _biggerFont = const TextStyle(fontSize: 18.0);
همانطور که قبلاً آموختید، زیرخط (underscore) در ابتدای نام ها، اعضای کلاس را به صورت فایل خصوصی (file-private) می کند. کلمه کلیدی dynamic به دارت می گوید که لیست می تواند هر چیزی را در خود جای دهد. به طور کلی، استفاده از dynamic ایده آل نیست زیرا از سیستم ایمنی نوع دارت منصرف می شود. با این حال، هنگام برقراری تماس های شبکه، پرداختن به dynamic اجتناب ناپذیر است. برای برقراری تماس HTTP ناهمزمان، loadData_ را به GHFlutterState_ اضافه کنید:
Future<void> _loadData() async {
const dataUrl = 'https://api.github.com/orgs/raywenderlich/members';
final response = await http.get(Uri.parse(dataUrl));
setState(() {
_members = json.decode(response.body) as List;
});
}
});
در اینجا، کلمه کلیدی async را به loadData_ اضافه کرده اید تا به دارت بگویید ناهمزمان است. سرنخ دیگری مبنی بر ناهمزمان بودن آن نوع بازگشت Future است. await را در جلو ()http.get قرار میدهید زیرا این یک تماس ناهمزمان دیگر است که ممکن است کمی طول بکشد.
هنگامی که تماس HTTP کامل شد، یک callback به setState ارسال می کنید که به طور همزمان(synchronously) روی UI thread اجرا می شود. در این مورد، شما JSON response را decode میکنید و آن را به لیست members_ اختصاص میدهید. اگر members_ را بدون تماس با setState تنظیم کنید، فلاتر UI را rebuild نمی کند و کاربران شما متوجه نمی شوند که وضعیت تغییر کرده است.
اضافه کردن یک override initState به _GHFlutterState:
@override
void initState() {
super.initState();
_loadData();
}
این متد زمانی که کلاس state برای اولین بار ایجاد می شود loadData _ را فراخوانی می کند.اکنون که لیستی از اعضا را در Dart ایجاد کرده اید، به راهی برای نمایش آنها در یک لیست در UI نیاز دارید.
استفاده از ListView
Dart یک ListView ارائه می دهد که به شما امکان می دهد داده ها را در یک لیست نشان دهید. ListView مانند یک RecyclerView در Android یا یک UICollectionView در iOS عمل میکند.buildRow _ را به GHFlutterState _ اضافه کنید:
Widget _buildRow(int i) {
return ListTile(
title: Text('${_members[i]['login']}', style: _biggerFont),
);
}
در اینجا، شما یک ListTile را return می کنید که نام login ، parse شده از JSON را برای member در اندیس i نشان می دهد. همچنین از text style که قبلا ایجاد کرده اید استفاده می کند.
خط body در متد build، GHFlutterState _ را با موارد زیر جایگزین کنید:
body: ListView.builder(
padding: const EdgeInsets.all(16.0),
itemCount: _members.length,
itemBuilder: (BuildContext context, int position) {
return _buildRow(position);
}),
padding مقداری فضای خالی در اطراف هر آیتم لیست اضافه می کند – در این مورد 16 پیکسل تنظیم شده است itemCount به ListView می گوید که در مجموع چند ردیف خواهد داشت. در نهایت، برای هر ردیف جدیدی که روی صفحه قابل مشاهده است، ItemBuilder را فراخوانی کنید، که از آن به عنوان فرصتی برای ساخت ListTile سفارشی خود در _buildRow استفاده می کنید.
توجه: Copy و paste گاهی اوقات قالب بندی (formatting) را به هم می زند. با فشار دادن Shift-Option-F در macOS یا Shift-Alt-F در ویندوز، قالب بندی را برطرف کنید. اگر VS Code را برای انجام این کار تنظیم کرده باشید، ممکن است فایل را به صورت خودکار فرمت کنید.در این مرحله، احتمالاً باید به جای hot reload ، یک راه اندازی مجدد(restart) کامل انجام دهید. دکمه Hot Restart برای این منظور مفید خواهد بود . هنوز هم سریعتر از توقف کامل (stopping) برنامه و rebuilding آن است.
پس از راه اندازی مجدد، موارد زیر را مشاهده خواهید کرد:
برقراری تماس شبکه(network call)، parse کردن داده ها و نمایش نتایج در یک لیست به همین راحتی است!
اکنون، زمان آن است که لیست را کمی زیباتر کنیم.
افزودن جدا کننده ها (divider)
برای افزودن جدا کننده ها به لیست، به جای ListView.builder از ListView.separated استفاده می کنید. بدنه Scaffold را با کد زیر جایگزین کنید:
body: ListView.separated(
itemCount: _members.length,
itemBuilder: (BuildContext context, int position) {
return _buildRow(position);
},
separatorBuilder: (context, index) {
return const Divider();
}),
استفاده از ListView.separated به شما یک گزینه separatorBuilder می دهد که به شما امکان می دهد بین tile های لیست یک Divider اضافه کنید. اکنون که Divider دارید، padding را نیز از builder حذف کرده اید.Hot reload را انجام دهید. اکنون، جدا کننده های بین ردیف ها را خواهید دید:
برای افزودن padding به هر ردیف، ListTile را با Padding در داخل buildRow_ قرار دهید. ساده ترین راه برای انجام این کار در کد VS این است که مکان نمای خود را روی ListTile قرار دهید و Command- را در macOS یا Control- را در ویندوز فشار دهید. سپس همانطور که در GIF زیر نشان داده شده است، padding را به 16.0 افزایش دهید.
همچنین، میتوانید buildRow_ را با کد زیر جایگزین کنید تا به همان نتیجه برسید:
Widget _buildRow(int i) {
return Padding(
padding: const EdgeInsets.all(16.0),
child: ListTile(
title: Text('${_members[i]['login']}', style: _biggerFont),
),
);
}
ListTile اکنون یک ویجت فرزند از Padding است. Hot reload برای مشاهده پدیدار شدن padding روی ردیفها است ، اما نه در جداکننده ها (divider).
Parse کردن Custom Types (انواع سفارشی)
در قسمت قبل، تجزیه کننده JSON (JSON parser)، memberها را در JSON response گرفت و آنها را به members_ اختصاص داد. اگرچه شما لیست را dynamic تعریف کردید، اما نوع (type)واقعی که دارت در لیست قرار داد Map بود، یک ساختار داده ای که جفت های کلید-مقدار (key-value) را نگه می دارد. این نوع معادل Map در کاتلین یا Dictionary در سوئیفت است.با این حال، شما همچنین می توانید از انواع سفارشی دیگری نیز استفاده کنید.یک نوع Member جدید در پایین main.dart اضافه کنید:
class Member {
Member(this.login);
final String login;
}
Member یک سازنده دارد که هنگام ایجاد یک شی member ، ویژگی login را تنظیم می کند.تعریف members_ را در GHFlutterState_ به روز کنید تا لیستی از اشیاء Member باشد:
final _members = <Member>[];
شما از final به جای var استفاده کردید زیرا به جای اختصاص مجدد یک لیست جدید به members_ ، اکنون مواردی را به لیست موجود اضافه می کنید.کد زیر را جایگزین setState در loadData_ کنید:
setState(() {
final dataList = json.decode(response.body) as List;
for (final item in dataList) {
final login = item['login'] as String? ?? '';
final member = Member(login);
_members.add(member);
}
});
در اینجا هر map رمزگشایی (decode) شده را به یک Member تبدیل می کند و آن را به لیست member ها اضافه می کند. درtitle خط ListTile را با عبارت زیر جایگزین کنید:
title: Text('${_members[i].login}', style: _biggerFont),
اگر hot reload را امتحان کنید، خطایی خواهید دید، بنابراین hot restart را انجام دهید. همان صفحه قبلی را خواهید دید، با این تفاوت که اکنون از کلاس Member جدید شما استفاده می کند.
دانلود تصاویر با NetworkImage
در GitHub، هر Member یک URL برای آواتار خود دارد. پیشرفت بعدی شما این است که آن آواتار را به کلاس Member اضافه کنید و آواتارها را در برنامه نشان دهید.برای افزودن ویژگی avatarUrl، Member را به روز کنید. مانند دستور زیر:
class Member {
Member(this.login, this.avatarUrl);
final String login;
final String avatarUrl;
}
از آنجایی که avatarUrl اکنون یک پارامتر ضروری است، Flutter در loadData_ از شما ایراد می گیرد. نسخه به روز شده زیر را جایگزین کال بک setState در _loadData کنید:
setState(() {
final dataList = json.decode(response.body) as List;
for (final item in dataList) {
final login = item['login'] as String? ?? '';
final url = item['avatar_url'] as String? ?? '';
final member = Member(login, url);
_members.add(member);
}
});
کد بالا از کلید avatar_url در map تجزیهشده (parse شده) از JSON برای جستجوی مقدار URL استفاده میکند، سپس آن را روی رشته url تنظیم میکند که به Member ارسال میکنید.اکنون که به URL مربوط به آواتار دسترسی دارید، آن را به ListTile خود اضافه کنید. buildRow_ را با عبارت زیر جایگزین کنید:
Widget _buildRow(int i) {
return Padding(
padding: const EdgeInsets.all(16.0),
child: ListTile(
title: Text('${_members[i].login}', style: _biggerFont),
leading: CircleAvatar(
backgroundColor: Colors.green,
backgroundImage: NetworkImage(_members[i].avatarUrl),
),
),
);
}
یک CircleAvatar را به لبه اصلی ListTile شما اضافه می کند. در حالی که منتظر دانلود تصاویر هستید، پس زمینه CircleAvatar سبز می شود.یک hot restart به جای hot reload انجام دهید. آواتارهای member خود را در هر ردیف خواهید دید:
Clean up کردن کد
بیشتر کد شما اکنون در main.dart است. برای اینکه کد کمی تمیزتر شود، کلاسها را در فایلهای خودشان تغییر میدهید.فایل هایی با نام member.dart و ghflutter.dart در پوشه lib ایجاد کنید. Member را به member.dart و GHFlutterState_ و GHFlutter را به ghflutter.dart منتقل کنید.شما به هیچ عبارت import در Member.dart نیاز نخواهید داشت، اما import در ghflutter.dart باید به صورت زیر باشد
import 'dart:convert';
import 'package:flutter/material.dart';
import 'package:http/http.dart' as http;
import 'member.dart';
import 'strings.dart' as strings;
همچنین باید import در main.dart را به روز کنید. کل فایل را با موارد زیر جایگزین کنید:
import 'package:flutter/material.dart';
import 'ghflutter.dart';
import 'strings.dart' as strings;
void main() => runApp(const GHFlutterApp());
class GHFlutterApp extends StatelessWidget {
const GHFlutterApp({Key? key}) : super(key: key);
@override
Widget build(BuildContext context) {
return MaterialApp(
title: strings.appTitle,
// TODO: add theme here
home: const GHFlutter(),
);
}
}
همه چیز را ذخیره کنید و برنامه را دوباره اجرا کنید. تغییری نخواهید دید، اما کد اکنون کمی تمیزتر شده است.
اضافه کردن یک Theme
آخرین پیشرفت شما این است که با افزودن یک ویژگی Theme به MaterialApp که در main.dart ایجاد کرده اید، به راحتی یک Theme به برنامه اضافه کنید.//TODO: add theme here را در اینجا پیدا کنید و آن را با موارد زیر جایگزین کنید:
theme: ThemeData(primaryColor: Colors.green.shade800),
در اینجا، شما از سایه سبز (shade of green)به عنوان مقدار رنگ Material Design برای theme استفاده می کنید.save کرده و hot reload را برای مشاهده theme جدید اعمال کنید:
اسکرین شات های برنامه تاکنون از شبیه ساز اندروید بوده است. همچنین میتوانید برنامه theme نهایی را در iOS Simulator اجرا کنید
و در مرورگر وب Chrome چگونه به نظر می رسد:
به راحتی آن را به عنوان یک برنامه ویندوز، مک یا لینوکس نیز اجرا کنید. این فقط به کمی راه اندازی بیشتر و اضافه کردن پشتیبانی دسکتاپ (desktop support)به برنامه شما نیاز دارد. در macOS، همچنین باید به برنامه اجازه دسترسی به اینترنت را بدهید.این برنامه در حال اجرا در macOS است:
اکنون این همان چیزی است که شما به آن کراس پلتفرم می گویید! :]
دیدگاهتان را بنویسید