
سلام به همگی چند وقتی میشه با فلاتر آشنا شدم. نسبت به تجربه ای که تو اندروید داشتم طراحی UI تو فلاتر خیلی آسونتره واسه همین تصمیم گرفتم یه پروژه با فلاتر درست کنم و تصمیم دارم پروژمو این جا با شما به اشتراک بزارم. تو این مجموعه از پست ها چند تا از مهم ترین موضوعات فلاتر رو باهم بررسی میکنیم و در نهایت یک پروژه کامل رو به پایان میرسونیم. این پست سومین پست از این سری هستش واسه دیدن قسمت قبل به این لینک مراجعه کنید.
تعریف Wikipedia:
جیسان (به انگلیسی: JSON) مخفف JavaScript Object Notation (نشانهگذاری شیء جاوااسکریپت)، یک استاندارد باز متنیِ سبک برای انتقال دادهها است به گونهای که برای انسان نیز خوانا باشد. با وجود ارتباط عمیقی که با جاوااسکریپت دارد، جیسان مستقل از زبان است و مفسرهایش تقریباً برای هر زبانی موجود هستند.
اما اگر بخوام به زبان ساده تربگم Json یه زبان (Notation) که برای ذخیره و با تبادل اطلاعات استفاده میشه. ما معمولا زمانی از Json استفاده میکنیم که بخواهیم اطلاعاتی رو از سمت سرور بگیرم و یا برای سرور ارسال کنیم.
واسه این که کار با داده ها رو آسون کنیم مدل ها رو میسازیم. مدل ها معمولا کلاس هایی هستند که صرفا داده رو نگه میدارن و معمولا منطق دیگه ای درون خودشون ندارن. اگه با Java کار کرده باشید به این کلاس ها Pojo میگن. همچنین اگه با کاتلین کار کرده باشید بهشون Data Class میگن که کار باهاشون خیلی هم آسونه.
اما بریم سراغ ساختن مدل خودمون تو Dart. واسه ساختن مدل تو فلاتر مراحل زیر رو انجام بدین:
بیاید برای شروع فرض بگیریم که کلاس Food ما دوتا خصوصیت بیشتر نداره. یه name که برای نمایش اسم غذا و یه image که ادرس عکس غذا رو درون خودش نگه میداره. پس کد کلاسمون چیزی شبیه به این میشه:
class Food { final String name; final String image;
//constructor Food({ this.name, this.image, });
}
سوال اینجاست که چطور این مدل رو تبدیل کنیم به Json و بر عکس مثلا وقتی دیتا Json از سرور بگیریم چطور تبدیلش کنیم به مدلمون؟
تو این مقاله به دو روش معمول که تو فلاتر (دارت) پیشنهاد شده رو مورد بررسی قرار میدیم:
معمولا از روش اول واسه داده های ساده و از دومی برای داده های پیچیده که مدیریتشون سخته استفاده میشه.
تبدیل دستی تو فلاتر خیلی سادست. فلاتر یه کتاب خونه داخلی از پیش تعریف شده تو پکیج dart:convert داره که کار باهاش خیلی راحته. فرض کنید json نمونه ما به این شکله:
{ "name" : "pizza", "image" : "url" }
کافیه پکیج dart:convert رو اضافه کنید و بعد کد زیر رو اجرا کنید.
Map<String, dynamic> food = jsonDecode(jsonString);
print('pizza name , ${food['name']}! ');
print('image url ${food['email']}.');
همونطور که میبینید متد jsonDecode یه ورودی از نوع String میگیره و خروجی از نوع Map به ما میده. شما به راحتی میتونید با پیمایش تو Map به اطلاعات دست پیدا کنید. اما ما هدفمون اینه که از json یک مدل درس کنیم ولی اینجا یه خروجی مپ داریم.
پس باید یکسری کد ها به مدلمون اضافه کنیم. فایل مدلتون رو به شکل زیر تغییر بدین:
class Food { final String name; final String image;
//constructor Food({ this.name, this.image, }); //named constructor Food.fromJson(Map<String , dynamic> json) : name = json['name'], image= json['image']; }
اگه به تیکه کد بالا نگاه کنید میبینید که ما یه Constructor جدید به کلاس مدلمون اضافه کردیم تو دارت به این شکل از constructor ها Named Constructor میگن اگه اطلاعاتی درموردش ندارید میتونید به این لینک مراجعه کنید.
در واقع کاری که Constructor جدیدمون انجام میده اینه که یه پارامتر Map رو میگیره و مقادیر مدلمون رو مقداردهی میکنه. شاید حدس زده باشید ما با ترکیب Constructor جدیدمون و متد از پیش تعریف شده jsonDecode میتونیم به راحتی json رو به مدل تبدیل کنیم.
Map foodMap = jsonDecode(jsonString); var food= Food.fromJson(foodMap );
print('pizza name , ${food.name}!'); print('image url ${food.email}.');
به این فرایند میگن Decode کردن. در واقع ما یه json داریم اول تبدیلش میکنیم به یه Map بعدش با فرستادن Map به مدل اونو تبدیل به مدل میکنیم.
اما حالت برعکس چی؟ اگه بخوایم یه مدل رو به json تبدیل کنیم چی؟ فلاتر و دارت باز هم یه متد واسه این کار دارن. jsonEncode یه متد که یه پارامتر Map میگیره و اونو به string تبدیل میکنه همون json ماست. واسه استفاده از این متد لازمه بازم کلاس مدلمون رو یکم تغییر بدیم. پس کد کلاستون رو به کد زیر تبدیل کنید:
class Food { final String name; final String image;
//constructor Food({ this.name, this.image, }); //named constructor Food.fromJson(Map<String , dynamic> json) : name = json['name'], image= json['image']; Map<String, dynamic> toJson() => { 'name': name, 'email': email, }; }
واسه استفاده از jsonEncode لازمه به این شکل ازش استفاده کنید.
var food=Food("name","url"); String json = jsonEncode(food.toJson());
ما تو کد بالا یه مدل ساختیم پاسش کردیم به متد jsonEncode و یه خروجی json رو دریافت کردیم. یه نکته باید بگم اونم این که نیاز نیست متد toJson مربوط به مدل مون رو صدا بزنیم. در واقع خود متد jsonEncode این کار رو برامون میکنه. چه عالییی...
پس کدمون به این شکل تغییر میکنه:
var food=Food("name","url"); String json = jsonEncode(food);
تا اینجا encode و decode کردن جیسون رو به صورت دستی یاد گرفتیم. اما یه سوال بزرگ اونم این که اگر مدلمون 20 تا فیلد داشت چی؟ قراره کلی کد بزنیم؟ این جاست که کتابخونه json_serializable به کمکون میاد.
در واقع کاری که که کتابخونه json_serializable انجام میده اینه که کدهایی اضافی و تکراری(boilerplate code) شما رو هر دفعه که کلاس مدلتون تغییر میکنه از نو میسازه ( Generate میکنه) .
واسه استفاده از این کتابخونه نیازه که اول وابستگی های اون رو به فایل pubspec.yaml اضافه کنید.
dependencies: # Your other regular dependencies here json_annotation: ^2.0.0
dev_dependencies: # Your other dev_dependencies here build_runner: ^1.0.0 json_serializable: ^2.0.0
وابستگی های بالا رو مثل کد بالا به فایل pubspec.yaml اضافه کنید. شاید این فایل هم بتونه کمکتون کنه.
مرحله بعد اضافه کردن کتابخونه json_serializable به مدلتونه. کد کلاس مدلتون رو به کد زیر تغییر بدید.
//1 import 'package:json_annotation/json_annotation.dart'; //2 part 'food.g.dart'; //3 @JsonSerializable() class Food { final String name; final String image;
//constructor Food({ this.name, this.image, }); //4 Food.fromJson(Map<String , dynamic> json) => _$FoodFromJson(json); //5 Map<String, dynamic> toJson() => _$FoodToJson(this); }
اضافه کردن این کتابخونه به مدل به 4 بخش تقسیم میشه:
اما اگه کد بالا رو اجرا کنید هنوز هم یه مشکلی هست و حتما به یه ارور مثل ارور زیر بر میخورید.

مشکل اینجاست که ما کدمون رو تغییر دادیم اما generator رو فعال نکردیم. و به ما ارور میده که فایل مورد نظر generate نشده.
واسه فعال کردن generator در فلاتر 2 روش وجود داره:
تو این روش ما generator رو برای یک اجرا میکنیم و اگه تغییراتی رو فایل مدلمون اعمال کنیم باید باز هم اجراش کنیم . برای اجرا generator به این روش کد flutter pub run build_runner build رو در ریشه پروژتون در terminal اجرا کنید. اگه از cmd استفاده میکنید یادتون نره که به آدرس ریشه پروژتون برید.
با این روش هر وقت که ما تغییری رو فایلای مورد نظرمون ایجاد میکنیم کد مرتبط باهاش بلافاصله تولید میشه. واسه استفاده از این روش کد flutter pub run build_runner watch رو در ریشه پروژتون اجرا کنید. واسه اطلاعات بیشتر تو این مورد میتونید برید به این لینک. و اما یه نکته یادتون نره که generator رو وقتی لازم نداشتید متوقف کنید واسه متوقف کردنش CTRL + C رو همزمان فشار بدید.
مثال بالا یه مثال ساده بود اگر میخواید پروژرو ادامه بدید باید بگم که مدل ما یکم پیچیده تر از این کدیه که زدیم. میتونید به عنوان تمرین مدل رو خودتون کامل کنید. لینک مدل کامل شده روی گیت هاب من هست. اگرم حوصله نداشتید میتونید فایلش رو کپی کنید.
پیشنهاد میکنم لینک های بالا رو مطالعه کنید.