1. مقدمه
دارت 3 الگوهایی را به زبان معرفی می کند، مقوله جدیدی از دستور زبان. فراتر از این روش جدید برای نوشتن کد دارت، چندین پیشرفت زبان دیگر نیز وجود دارد، از جمله
- سوابق برای دسته بندی داده ها از انواع مختلف،
- اصلاح کننده های کلاس برای کنترل دسترسی، و
- عبارات سوئیچ جدید و عبارات if-case .
این ویژگی ها انتخاب های شما را هنگام نوشتن کد دارت گسترش می دهند. در این لبه کد، یاد می گیرید که چگونه از آنها برای فشرده تر، ساده تر و انعطاف پذیرتر کردن کد خود استفاده کنید.
این کد لبه فرض می کند که شما با فلاتر و دارت آشنایی دارید. اگر احساس میکنید کمی زنگ زدهاید، اصول اولیه را با منابع زیر در نظر بگیرید:
چیزی که خواهی ساخت
این Codelab برنامه ای ایجاد می کند که یک سند JSON را در Flutter نمایش می دهد. این برنامه JSON را شبیه سازی می کند که از یک منبع خارجی می آید. JSON حاوی داده های سند مانند تاریخ اصلاح، عنوان، سرصفحه ها و پاراگراف ها است. شما کدی را می نویسید تا داده ها را به طور منظم در پرونده ها بسته بندی کنید تا بتوان آنها را در هر جایی که ویجت های فلاتر شما به آن نیاز دارند منتقل و باز کرد.
سپس از الگوها برای ساخت ویجت مناسب زمانی که مقدار با آن الگو مطابقت دارد استفاده می کنید. همچنین نحوه استفاده از الگوها برای تخریب داده ها به متغیرهای محلی را مشاهده می کنید.
چیزی که یاد خواهید گرفت
- نحوه ایجاد رکوردی که چندین مقدار را با انواع مختلف ذخیره می کند.
- نحوه برگرداندن چندین مقدار از یک تابع با استفاده از یک رکورد
- نحوه استفاده از الگوها برای تطبیق، اعتبارسنجی و تخریب داده ها از سوابق و اشیاء دیگر.
- نحوه اتصال مقادیر مطابق الگو به متغیرهای جدید یا موجود.
- نحوه استفاده از قابلیت های جدید دستور سوئیچ، عبارات سوئیچ و دستورات if-case.
- چگونه می توان از بررسی جامع بودن استفاده کرد تا اطمینان حاصل شود که هر مورد در یک عبارت سوئیچ یا عبارت سوئیچ رسیدگی می شود.
2. محیط خود را تنظیم کنید
- Flutter SDK را نصب کنید.
- یک ویرایشگر مانند Visual Studio Code (VS Code) راه اندازی کنید .
- مراحل تنظیم پلتفرم را برای حداقل یک پلتفرم هدف (iOS، Android، Desktop یا یک مرورگر وب) طی کنید.
3. پروژه را ایجاد کنید
قبل از غواصی در الگوها، رکوردها و سایر ویژگی های جدید، لحظه ای را صرف ایجاد یک پروژه Flutter ساده کنید که تمام کدهای خود را برای آن بنویسید.
یک پروژه فلاتر ایجاد کنید
- از دستور
flutter create
برای ایجاد یک پروژه جدید به نامpatterns_codelab
استفاده کنید. پرچم--empty
از ایجاد برنامه پیشخوان استاندارد در فایلlib/main.dart
جلوگیری می کند که به هر حال باید آن را حذف کنید.
flutter create --empty patterns_codelab
- سپس، دایرکتوری
patterns_codelab
با استفاده از VS Code باز کنید.
code patterns_codelab
حداقل نسخه SDK را تنظیم کنید
- محدودیت نسخه SDK را برای پروژه خود تنظیم کنید تا به Dart 3 یا بالاتر بستگی داشته باشد.
pubspec.yaml
environment:
sdk: ^3.0.0
4. پروژه را راه اندازی کنید
در این مرحله شما دو فایل Dart را ایجاد یا به روز می کنید:
- فایل
main.dart
که حاوی ویجت های برنامه است و - فایل
data.dart
که داده های برنامه را ارائه می کند.
در مراحل بعدی به اصلاح هر دوی این فایل ها ادامه خواهید داد.
داده های برنامه را تعریف کنید
- یک فایل جدید به
lib/data.dart
ایجاد کنید و کد زیر را به آن اضافه کنید:
lib/data.dart
import 'dart:convert';
class Document {
final Map<String, Object?> _json;
Document() : _json = jsonDecode(documentJson);
}
const documentJson = '''
{
"metadata": {
"title": "My Document",
"modified": "2023-05-10"
},
"blocks": [
{
"type": "h1",
"text": "Chapter 1"
},
{
"type": "p",
"text": "Lorem ipsum dolor sit amet, consectetur adipiscing elit."
},
{
"type": "checkbox",
"checked": false,
"text": "Learn Dart 3"
}
]
}
''';
برنامهای را تصور کنید که دادهها را از یک منبع خارجی دریافت میکند، مانند یک جریان ورودی/خروجی یا درخواست HTTP. در این لبه کد، شما با تمسخر دادههای JSON ورودی با یک رشته چند خطی در متغیر documentJson
، آن مورد استفاده واقعیتر را ساده میکنید.
داده های JSON در کلاس Document
تعریف شده است. بعداً در این لبه کد، توابعی را اضافه می کنید که داده ها را از JSON تجزیه شده برمی گرداند. این کلاس فیلد _json
را در سازنده خود تعریف و مقداردهی اولیه می کند.
برنامه را اجرا کنید
دستور flutter create
فایل lib/main.dart
را به عنوان بخشی از ساختار فایل Flutter پیش فرض ایجاد می کند.
- برای ایجاد نقطه شروع برای برنامه، محتوای
main.dart
را با کد زیر جایگزین کنید:
lib/main.dart
import 'package:flutter/material.dart';
import 'data.dart';
void main() {
runApp(const DocumentApp());
}
class DocumentApp extends StatelessWidget {
const DocumentApp({super.key});
@override
Widget build(BuildContext context) {
return MaterialApp(
theme: ThemeData(useMaterial3: true),
home: DocumentScreen(
document: Document(),
),
);
}
}
class DocumentScreen extends StatelessWidget {
final Document document;
const DocumentScreen({
required this.document,
super.key,
});
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: const Text('Title goes here'),
),
body: const Column(
children: [
Center(
child: Text('Body goes here'),
),
],
),
);
}
}
شما دو ویجت زیر را به برنامه اضافه کردید:
-
DocumentApp
آخرین نسخه طراحی متریال را برای قالب بندی رابط کاربری راه اندازی می کند. -
DocumentScreen
طرح بصری صفحه را با استفاده از ویجتScaffold
ارائه می دهد.
- برای اطمینان از اینکه همه چیز به خوبی اجرا می شود، برنامه را در دستگاه میزبان خود با کلیک روی Run and Debug اجرا کنید:
- بهطور پیشفرض، Flutter هر پلتفرم هدفی را که در دسترس باشد انتخاب میکند. برای تغییر پلتفرم هدف، پلتفرم فعلی را در نوار وضعیت انتخاب کنید:
شما باید یک قاب خالی با title
و عناصر body
تعریف شده در ویجت DocumentScreen
ببینید:
5. ایجاد و بازگرداندن سوابق
در این مرحله از رکوردها برای برگرداندن چندین مقدار از یک فراخوانی تابع استفاده می کنید. سپس، آن تابع را در ویجت DocumentScreen
فراخوانی می کنید تا به مقادیر دسترسی داشته باشید و آنها را در رابط کاربری منعکس کنید.
یک رکورد ایجاد و برگردانید
- در
data.dart
، یک متد getter جدید به کلاس Document به نامmetadata
اضافه کنید که یک رکورد را برمیگرداند:
lib/data.dart
import 'dart:convert';
class Document {
final Map<String, Object?> _json;
Document() : _json = jsonDecode(documentJson);
(String, {DateTime modified}) get metadata { // Add from here...
const title = 'My Document';
final now = DateTime.now();
return (title, modified: now);
} // to here.
}
نوع برگشتی برای این تابع یک رکورد با دو فیلد است، یکی با نوع String
و دیگری با نوع DateTime
.
دستور return با قرار دادن دو مقدار داخل پرانتز، یک رکورد جدید می سازد (title, modified: now)
.
فیلد اول موقعیتی و بدون نام است و فیلد دوم modified
نام دارد.
دسترسی به فیلدهای رکورد
- در ویجت
DocumentScreen
، متد فرادادهmetadata
را در متدbuild
فراخوانی کنید تا بتوانید رکورد خود را دریافت کرده و به مقادیر آن دسترسی داشته باشید:
lib/main.dart
class DocumentScreen extends StatelessWidget {
final Document document;
const DocumentScreen({
required this.document,
super.key,
});
@override
Widget build(BuildContext context) {
final metadataRecord = document.metadata; // Add this line.
return Scaffold(
appBar: AppBar(
title: Text(metadataRecord.$1), // Modify this line,
),
body: Column(
children: [
Center(
child: Text(
'Last modified ${metadataRecord.modified}', // And this one.
),
),
],
),
);
}
}
متد فراداده metadata
رکوردی را برمیگرداند که به متغیر محلی metadataRecord
اختصاص داده میشود. رکوردها روشی سبک و آسان برای برگرداندن چندین مقدار از یک فراخوانی یک تابع و تخصیص آنها به یک متغیر است.
برای دسترسی به فیلدهای جداگانه تشکیل شده در آن رکورد، می توانید از نحو گیرنده داخلی رکوردها استفاده کنید.
- برای به دست آوردن یک فیلد موقعیتی (یک فیلد بدون نام، مانند
title
)، از دریافت کننده$<num>
در رکورد استفاده کنید. این فقط فیلدهای بدون نام را برمی گرداند. - فیلدهای نامگذاری شده مانند
modified
دریافت کننده موقعیتی ندارند، بنابراین می توانید مستقیماً از نام آن مانندmetadataRecord.modified
استفاده کنید.
برای تعیین نام یک گیرنده برای یک فیلد موقعیتی، از $1
شروع کنید و از فیلدهای نامگذاری شده رد شوید. به عنوان مثال:
var record = (named: 'v', 'y', named2: 'x', 'z');
print(record.$1); // prints y
print(record.$2); // prints z
- برای مشاهده مقادیر JSON نمایش داده شده در برنامه، دوباره بارگیری کنید. پلاگین VS Code Dart هر بار که فایلی را ذخیره می کنید، دوباره بارگیری می شود.
می بینید که هر فیلد در واقع نوع خود را حفظ کرده است.
- متد
Text()
یک رشته را به عنوان اولین آرگومان خود می گیرد. - فیلد
modified
DateTime است و با استفاده از درون یابی رشته ای بهString
تبدیل می شود.
روش دیگر برای بازگرداندن انواع مختلف داده، تعریف کلاس است که پرمخاطب تر است.
6. مطابقت و تخریب با الگوها
رکوردها می توانند به طور موثر انواع مختلف داده ها را جمع آوری کرده و به راحتی آن ها را منتقل کنند. اکنون، کد خود را با استفاده از الگوها بهبود دهید.
یک الگو ساختاری را نشان می دهد که یک یا چند مقدار می تواند داشته باشد، مانند یک نقشه. الگوها با مقادیر واقعی مقایسه می شوند تا مشخص شود که آیا مطابقت دارند یا خیر.
برخی از الگوها، هنگامی که با هم مطابقت دارند، با بیرون کشیدن داده ها از آن، مقدار منطبق را تخریب می کنند . Destructuring به شما امکان می دهد مقادیر را از یک شی باز کنید تا آنها را به متغیرهای محلی اختصاص دهید یا تطبیق بیشتری روی آنها انجام دهید.
یک رکورد را به متغیرهای محلی تخریب کنید
- متد
build
DocumentScreen
را برایmetadata
فراداده و استفاده از آن برای مقداردهی اولیه اعلان متغیر الگوی تغییر دهید:
lib/main.dart
class DocumentScreen extends StatelessWidget {
final Document document;
const DocumentScreen({
required this.document,
super.key,
});
@override
Widget build(BuildContext context) {
final (title, modified: modified) = document.metadata; // Modify
return Scaffold(
appBar: AppBar(
title: Text(title), // Modify
),
body: Column(
children: [
Center(
child: Text(
'Last modified $modified', // Modify
),
),
],
),
);
}
}
الگوی رکورد (title, modified: modified)
شامل دو الگوی متغیر است که با فیلدهای رکورد برگردانده شده توسط metadata
مطابقت دارند.
- عبارت با الگوی فرعی مطابقت دارد زیرا نتیجه یک رکورد با دو فیلد است که یکی از آنها
modified
نام دارد. - از آنجایی که آنها مطابقت دارند، الگوی اعلان متغیر، عبارت را تخریب می کند، به مقادیر آن دسترسی پیدا می کند و آنها را به متغیرهای محلی جدید با همان نوع و نام،
String title
وDateTime modified
دهد.
زمانی که نام یک فیلد و متغیری که آن را پر می کند یکسان است، مختصر وجود دارد. روش build
DocumentScreen
را به صورت زیر Refactor کنید.
lib/main.dart
class DocumentScreen extends StatelessWidget {
final Document document;
const DocumentScreen({
required this.document,
super.key,
});
@override
Widget build(BuildContext context) {
final (title, :modified) = document.metadata; // Modify
return Scaffold(
appBar: AppBar(
title: Text(title),
),
body: Column(
children: [
Center(
child: Text(
'Last modified $modified',
),
),
],
),
);
}
}
نحو الگوی متغیر :modified
مخفف modified: modified
است. اگر یک متغیر محلی جدید با نام دیگری میخواهید، میتوانید به جای آن modified: localModified
را بنویسید.
- Hot Reload برای مشاهده نتیجه مشابه در مرحله قبل. رفتار دقیقاً مشابه است. شما فقط کد خود را مختصرتر کردید.
7. از الگوها برای استخراج داده ها استفاده کنید
در زمینههای خاص، الگوها نه تنها با هم مطابقت دارند و ساختارشان را تخریب میکنند، بلکه میتوانند بر اساس مطابقت یا عدم تطابق الگو در مورد اینکه کد چه کاری انجام میدهد ، تصمیم بگیرند. به این الگوهای ابطال پذیر می گویند.
الگوی اعلان متغیری که در مرحله آخر استفاده کردید یک الگوی انکارناپذیر است: مقدار باید با الگو مطابقت داشته باشد وگرنه یک خطا است و تخریب صورت نمیگیرد. به هر اعلان یا تخصیص متغیری فکر کنید. شما نمی توانید مقداری را به یک متغیر اختصاص دهید اگر از نوع یکسان نباشد.
از سوی دیگر، الگوهای ابطال پذیر در زمینه های جریان کنترل استفاده می شود:
- آنها انتظار دارند که برخی از ارزش هایی که با آنها مقایسه می کنند مطابقت نداشته باشند.
- آنها برای تأثیرگذاری بر جریان کنترل ، بر اساس مطابقت یا عدم تطابق مقدار، قرار دارند.
- اگر مطابقت نداشتند ، اجرا را با خطا قطع نمیکنند ، فقط به دستور بعدی میروند.
- آنها می توانند متغیرهایی را که فقط زمانی قابل استفاده هستند که مطابقت داشته باشند را تخریب و متصل کنند
مقادیر JSON را بدون الگو بخوانید
در این بخش، دادهها را بدون تطابق الگو میخوانید تا ببینید الگوها چگونه میتوانند به شما در کار با دادههای JSON کمک کنند.
- نسخه قبلی
metadata
با نسخه ای که مقادیر را از نقشه_json
می خواند جایگزین کنید. این نسخه ازmetadata
را در کلاسDocument
کپی و جایگذاری کنید:
lib/data.dart
class Document {
final Map<String, Object?> _json;
Document() : _json = jsonDecode(documentJson);
(String, {DateTime modified}) get metadata {
if (_json.containsKey('metadata')) { // Modify from here...
final metadataJson = _json['metadata'];
if (metadataJson is Map) {
final title = metadataJson['title'] as String;
final localModified =
DateTime.parse(metadataJson['modified'] as String);
return (title, modified: localModified);
}
}
throw const FormatException('Unexpected JSON'); // to here.
}
}
این کد تأیید می کند که داده ها بدون استفاده از الگوها به درستی ساختار یافته اند. در مرحله بعد، از تطبیق الگو برای انجام همان اعتبارسنجی با استفاده از کد کمتر استفاده می کنید. قبل از انجام هر کار دیگری سه بررسی را انجام می دهد:
- JSON شامل ساختار داده ای است که شما انتظار دارید:
if (_json.containsKey('metadata'))
- داده ها دارای نوع مورد انتظار شما هستند:
if (metadataJson is Map)
- این که داده ها تهی نیستند ، که به طور ضمنی در بررسی قبلی تأیید شده است.
مقادیر JSON را با استفاده از الگوی نقشه بخوانید
با یک الگوی قابل رد، می توانید با استفاده از الگوی نقشه بررسی کنید که JSON دارای ساختار مورد انتظار است.
- نسخه قبلی
metadata
را با این کد جایگزین کنید:
lib/data.dart
class Document {
final Map<String, Object?> _json;
Document() : _json = jsonDecode(documentJson);
(String, {DateTime modified}) get metadata {
if (_json // Modify from here...
case {
'metadata': {
'title': String title,
'modified': String localModified,
}
}) {
return (title, modified: DateTime.parse(localModified));
} else {
throw const FormatException('Unexpected JSON');
} // to here.
}
}
در اینجا، نوع جدیدی از if-statement (که در Dart 3 معرفی شده است)، if-case را مشاهده می کنید. بدنه case فقط در صورتی اجرا می شود که الگوی case با داده های _json
مطابقت داشته باشد. این تطابق همان بررسیهایی را انجام میدهد که در نسخه اول metadata
برای تأیید اعتبار JSON ورودی نوشتید. این کد موارد زیر را تایید می کند:
-
_json
یک نوع Map است. -
_json
حاوی یک کلیدmetadata
است. -
_json
null نیست. -
_json['metadata']
نیز یک نوع نقشه است. -
_json['metadata']
حاویtitle
کلید وmodified
. -
title
وlocalModified
رشته هایی هستند و پوچ نیستند.
اگر مقدار مطابقت نداشته باشد، الگو رد میشود (از ادامه اجرا خودداری میکند) و به عبارت else
میرود. اگر تطابق موفقیت آمیز باشد، الگو مقادیر title
و modified
را از نقشه تخریب می کند و آنها را به متغیرهای محلی جدید متصل می کند.
برای لیست کامل الگوها، جدول را در بخش الگوهای مشخصات ویژگی ببینید.
8. برنامه را برای الگوهای بیشتر آماده کنید
تا اینجای کار، به بخش metadata
داده های JSON پرداخته اید. در این مرحله، منطق کسبوکار خود را کمی بیشتر اصلاح میکنید تا دادههای موجود در فهرست blocks
را مدیریت کرده و آنها را در برنامه خود ارائه دهید.
{
"metadata": {
// ...
},
"blocks": [
{
"type": "h1",
"text": "Chapter 1"
},
// ...
]
}
کلاسی ایجاد کنید که داده ها را ذخیره کند
- یک کلاس جدید،
Block
، بهdata.dart
اضافه کنید، که برای خواندن و ذخیره دادههای یکی از بلوکهای موجود در دادههای JSON استفاده میشود.
lib/data.dart
class Block {
final String type;
final String text;
Block(this.type, this.text);
factory Block.fromJson(Map<String, dynamic> json) {
if (json case {'type': final type, 'text': final text}) {
return Block(type, text);
} else {
throw const FormatException('Unexpected JSON format');
}
}
}
سازنده کارخانه fromJson()
از همان if-case با الگوی نقشه استفاده می کند که قبلاً دیده اید.
توجه داشته باشید که json
با الگوی نقشه مطابقت دارد، حتی اگر یکی از کلیدهای checked
در الگو در نظر گرفته نشده باشد. الگوهای نقشه هر ورودی را در شیء نقشه که به صراحت در الگو لحاظ نشده است را نادیده می گیرند.
لیستی از اشیاء Block را برگردانید
- سپس یک تابع جدید به
getBlocks()
را به کلاسDocument
اضافه کنید.getBlocks()
JSON را به نمونه هایی از کلاسBlock
تجزیه می کند و لیستی از بلوک ها را برای رندر در رابط کاربری شما برمی گرداند:
lib/data.dart
class Document {
final Map<String, Object?> _json;
Document() : _json = jsonDecode(documentJson);
(String, {DateTime modified}) get metadata {
if (_json
case {
'metadata': {
'title': String title,
'modified': String localModified,
}
}) {
return (title, modified: DateTime.parse(localModified));
} else {
throw const FormatException('Unexpected JSON');
}
}
List<Block> getBlocks() { // Add from here...
if (_json case {'blocks': List blocksJson}) {
return [for (final blockJson in blocksJson) Block.fromJson(blockJson)];
} else {
throw const FormatException('Unexpected JSON format');
}
} // to here.
}
تابع getBlocks()
لیستی از اشیاء Block
را برمیگرداند که بعداً از آنها برای ساختن رابط کاربری استفاده میکنید. یک دستور if-case آشنا، اعتبارسنجی را انجام میدهد و مقدار ابرداده blocks
را به List
جدیدی به نام blocksJson
میفرستد (بدون الگو، برای ارسال به روش toList()
نیاز دارید).
لیست تحت اللفظی شامل مجموعه ای برای به منظور پر کردن لیست جدید با اشیاء Block
است.
این بخش هیچ ویژگی مرتبط با الگو را که قبلاً در این Codelab امتحان نکرده اید، معرفی نمی کند. در مرحله بعد، شما آماده می شوید تا موارد لیست را در UI خود رندر کنید.
9. از الگوها برای نمایش سند استفاده کنید
اکنون با استفاده از عبارت if-case و الگوهای ابطالپذیر، دادههای JSON خود را با موفقیت تخریب و دوباره ترکیب میکنید. اما if-case تنها یکی از پیشرفتها برای کنترل ساختارهای جریان است که با الگوها همراه است. اکنون، دانش خود را در مورد الگوهای ابطال پذیر برای تغییر عبارات به کار می گیرید.
آنچه را که با استفاده از الگوها با دستورهای سوئیچ ارائه می شود، کنترل کنید
- در
main.dart
، یک ویجت جدید به نامBlockWidget
ایجاد کنید که استایل هر بلوک را بر اساس فیلدtype
آن تعیین می کند.
lib/main.dart
class BlockWidget extends StatelessWidget {
final Block block;
const BlockWidget({
required this.block,
super.key,
});
@override
Widget build(BuildContext context) {
TextStyle? textStyle;
switch (block.type) {
case 'h1':
textStyle = Theme.of(context).textTheme.displayMedium;
case 'p' || 'checkbox':
textStyle = Theme.of(context).textTheme.bodyMedium;
case _:
textStyle = Theme.of(context).textTheme.bodySmall;
}
return Container(
margin: const EdgeInsets.all(8),
child: Text(
block.text,
style: textStyle,
),
);
}
}
دستور switch در متد build
فیلد type
شیء block
را روشن می کند.
- عبارت case اول از یک الگوی رشته ثابت استفاده می کند. اگر
block.type
برابر با مقدار ثابتh1
باشد، الگو مطابقت دارد. - عبارت case دوم از یک الگوی منطقی یا با دو الگوی رشته ثابت به عنوان الگوهای فرعی استفاده می کند. اگر
block.type
با یکی از زیرالگوهایp
یاcheckbox
مطابقت داشته باشد، الگو مطابقت دارد.
- مورد نهایی یک الگوی عام است ,
_
. حروف عام در موارد سوئیچ با سایر موارد مطابقت دارند. آنها مانند عباراتdefault
عمل می کنند، که هنوز در دستورات سوئیچ مجاز هستند (آنها فقط کمی پرمخاطب تر هستند).
الگوهای عام را می توان هر جا که یک الگو مجاز است استفاده کرد - به عنوان مثال، در یک الگوی اعلان متغیر: var (title, _) = document.metadata;
در این زمینه، عام هیچ متغیری را متصل نمی کند. فیلد دوم را کنار می گذارد.
در بخش بعدی، با ویژگی های سوئیچ بیشتر پس از نمایش اشیاء Block
آشنا می شوید.
نمایش محتوای سند
با فراخوانی getBlocks()
در متد build
ویجت DocumentScreen
یک متغیر محلی ایجاد کنید که حاوی لیستی از اشیاء Block
باشد.
- متد
build
موجود درDocumentationScreen
را با این نسخه جایگزین کنید:
lib/main.dart
class DocumentScreen extends StatelessWidget {
final Document document;
const DocumentScreen({
required this.document,
super.key,
});
@override
Widget build(BuildContext context) {
final (title, :modified) = document.metadata;
final blocks = document.getBlocks(); // Add this line
return Scaffold(
appBar: AppBar(
title: Text(title),
),
body: Column(
children: [
Text('Last modified: $modified'), // Modify from here
Expanded(
child: ListView.builder(
itemCount: blocks.length,
itemBuilder: (context, index) {
return BlockWidget(block: blocks[index]);
},
),
), // to here.
],
),
);
}
}
خط BlockWidget(block: blocks[index])
یک ویجت BlockWidget
را برای هر آیتم در لیست بلوک های برگشتی از متد getBlocks()
می سازد.
- برنامه را اجرا کنید و سپس بلوک های ظاهر شده روی صفحه را ببینید:
10. از عبارات سوئیچ استفاده کنید
الگوها قابلیت های زیادی به switch
و case
اضافه می کنند. برای اینکه آنها را در مکانهای بیشتری قابل استفاده کند، Dart دارای عبارات سوئیچ است. مجموعه ای از موارد می توانند یک مقدار را مستقیماً به یک انتساب یا عبارت بازگشتی متغیر ارائه دهند.
عبارت switch را به عبارت switch تبدیل کنید
تحلیلگر دارت به شما کمک می کند تا در کد خود تغییراتی ایجاد کنید.
- مکان نما خود را به دستور switch از بخش قبل ببرید.
- برای مشاهده کمک های موجود روی لامپ کلیک کنید.
- کمک عبارت Convert to switch را انتخاب کنید.
نسخه جدید این کد به شکل زیر است:
lib/main.dart
class BlockWidget extends StatelessWidget {
final Block block;
const BlockWidget({
required this.block,
super.key,
});
@override
Widget build(BuildContext context) {
TextStyle? textStyle; // Modify from here
textStyle = switch (block.type) {
'h1' => Theme.of(context).textTheme.displayMedium,
'p' || 'checkbox' => Theme.of(context).textTheme.bodyMedium,
_ => Theme.of(context).textTheme.bodySmall
}; // to here.
return Container(
margin: const EdgeInsets.all(8),
child: Text(
block.text,
style: textStyle,
),
);
}
}
یک عبارت switch شبیه به دستور switch به نظر می رسد، اما کلمه کلیدی case
را حذف می کند و از =>
برای جدا کردن الگو از بدنه case استفاده می کند. برخلاف دستورهای سوئیچ، عبارات سوئیچ یک مقدار را برمیگردانند و میتوانند در هر جایی که بتوان از یک عبارت استفاده کرد استفاده کرد.
11. از الگوهای شی استفاده کنید
دارت یک زبان شی گرا است، بنابراین الگوها برای همه اشیا اعمال می شوند. در این مرحله، یک الگوی شی را روشن میکنید و ویژگیهای شی را تخریب میکنید تا منطق رندر تاریخ رابط کاربری خود را افزایش دهید.
استخراج خواص از الگوهای شی
در این بخش، نحوه نمایش آخرین تاریخ اصلاح شده را با استفاده از الگوها بهبود می دهید.
- متد
formatDate
بهmain.dart
اضافه کنید:
lib/main.dart
String formatDate(DateTime dateTime) {
final today = DateTime.now();
final difference = dateTime.difference(today);
return switch (difference) {
Duration(inDays: 0) => 'today',
Duration(inDays: 1) => 'tomorrow',
Duration(inDays: -1) => 'yesterday',
Duration(inDays: final days, isNegative: true) => '${days.abs()} days ago',
Duration(inDays: final days) => '$days days from now',
};
}
این روش یک عبارت سوئیچ را برمیگرداند که difference
مقدار را روشن میکند، یک شی Duration
. این بازه زمانی بین today
و مقدار modified
از داده های JSON را نشان می دهد.
هر مورد از عبارت سوئیچ از یک الگوی شی استفاده میکند که با فراخوانی گیرندههای خصوصیات شی inDays
و isNegative
مطابقت دارد. نحو به نظر می رسد که ممکن است در حال ساخت یک شی Duration باشد، اما در واقع به فیلدهایی در شی difference
دسترسی دارد.
سه حالت اول از زیرالگوهای ثابت 0
، 1
و -1
برای مطابقت با ویژگی شی inDays
و برگرداندن رشته مربوطه استفاده می کنند.
دو مورد آخر به مدتهایی فراتر از امروز، دیروز و فردا رسیدگی میکنند:
- اگر ویژگی
isNegative
با الگوی ثابت بولیtrue
مطابقت داشته باشد، به این معنی که تاریخ اصلاح در گذشته بوده است، چند روز پیش نمایش داده می شود. - اگر این مورد تفاوت را پیدا نکرد، مدت زمان باید تعداد روز مثبت باشد (نیازی به تأیید صریح با
isNegative: false
نیست)، بنابراین تاریخ اصلاح در آینده است و روزهای بعد را نشان میدهد.
منطق قالب بندی را برای هفته ها اضافه کنید
- دو مورد جدید را به عملکرد قالببندی خود اضافه کنید تا مدت زمان بیش از ۷ روز را شناسایی کنید تا رابط کاربری بتواند آنها را به صورت هفتهها نمایش دهد:
lib/main.dart
String formatDate(DateTime dateTime) {
final today = DateTime.now();
final difference = dateTime.difference(today);
return switch (difference) {
Duration(inDays: 0) => 'today',
Duration(inDays: 1) => 'tomorrow',
Duration(inDays: -1) => 'yesterday',
Duration(inDays: final days) when days > 7 => '${days ~/ 7} weeks from now', // Add from here
Duration(inDays: final days) when days < -7 =>
'${days.abs() ~/ 7} weeks ago', // to here.
Duration(inDays: final days, isNegative: true) => '${days.abs()} days ago',
Duration(inDays: final days) => '$days days from now',
};
}
این کد بندهای محافظ را معرفی می کند:
- یک بند محافظ از کلمه کلیدی
when
بعد از الگوی مورد استفاده می کند. - آنها را می توان در موارد if-case، عبارات سوئیچ و عبارات سوئیچ استفاده کرد.
- آنها فقط یک شرط را پس از تطبیق الگو به آن اضافه می کنند.
- اگر بند محافظ نادرست ارزیابی شود، کل الگو رد می شود و اجرا به حالت بعدی ادامه می یابد.
تاریخ تازه فرمت شده را به UI اضافه کنید
- در نهایت، روش
build
را درDocumentScreen
به روز کنید تا از تابعformatDate
استفاده کنید:
lib/main.dart
class DocumentScreen extends StatelessWidget {
final Document document;
const DocumentScreen({
required this.document,
super.key,
});
@override
Widget build(BuildContext context) {
final (title, :modified) = document.metadata;
final formattedModifiedDate = formatDate(modified); // Add this line
final blocks = document.getBlocks();
return Scaffold(
appBar: AppBar(
title: Text(title),
),
body: Column(
children: [
Text('Last modified: $formattedModifiedDate'), // Modify this line
Expanded(
child: ListView.builder(
itemCount: blocks.length,
itemBuilder: (context, index) {
return BlockWidget(block: blocks[index]);
},
),
),
],
),
);
}
}
- برای مشاهده تغییرات در برنامه خود، بارگیری مجدد داغ:
12. یک کلاس را برای تعویض کامل مهر و موم کنید
توجه داشته باشید که از علامت عام یا حروف پیشفرض در انتهای آخرین سوئیچ استفاده نکردهاید. اگرچه این تمرین خوب است که همیشه یک مورد برای مقادیری که ممکن است از بین بروند درج شود، اما در یک مثال ساده مانند این اشکالی ندارد، زیرا میدانید مواردی که حساب برای همه مقادیر ممکن inDays
میتوانند بهطور بالقوه در نظر بگیرند، تعریف کردهاید.
هنگامی که هر مورد در یک سوئیچ رسیدگی می شود، به آن سوئیچ جامع می گویند. برای مثال، روشن کردن یک نوع bool
زمانی جامع است که موارد true
و false
داشته باشد. روشن کردن نوع enum
زمانی که مواردی برای هر یک از مقادیر enum نیز وجود داشته باشد جامع است، زیرا enum ها تعداد ثابتی از مقادیر ثابت را نشان می دهند.
دارت 3 بررسی جامعیت را به اشیا و سلسله مراتب کلاسها با اصلاح کننده کلاس جدید sealed
گسترش داد. کلاس Block
خود را به عنوان یک ابر کلاس مهر و موم شده بازسازی کنید.
زیر کلاس ها را ایجاد کنید
- در
data.dart
، سه کلاس جدید ایجاد کنید -HeaderBlock
،ParagraphBlock
، وCheckboxBlock
- کهBlock
گسترش میدهند:
lib/data.dart
class HeaderBlock extends Block {
final String text;
HeaderBlock(this.text);
}
class ParagraphBlock extends Block {
final String text;
ParagraphBlock(this.text);
}
class CheckboxBlock extends Block {
final String text;
final bool isChecked;
CheckboxBlock(this.text, this.isChecked);
}
هر یک از این کلاسها با مقادیر type
متفاوتی از JSON اصلی مطابقت دارد: 'h1'
، 'p'
و 'checkbox'
.
ابر کلاس را مهر و موم کنید
- کلاس
Block
را به عنوانsealed
علامت گذاری کنید. سپس، if-case را به عنوان یک عبارت سوئیچ که زیرکلاس مربوط بهtype
مشخص شده در JSON را برمی گرداند، تغییر شکل دهید:
lib/data.dart
sealed class Block {
Block();
factory Block.fromJson(Map<String, Object?> json) {
return switch (json) {
{'type': 'h1', 'text': String text} => HeaderBlock(text),
{'type': 'p', 'text': String text} => ParagraphBlock(text),
{'type': 'checkbox', 'text': String text, 'checked': bool checked} =>
CheckboxBlock(text, checked),
_ => throw const FormatException('Unexpected JSON format'),
};
}
}
کلمه کلیدی sealed
یک اصلاح کننده کلاس است که به این معنی است که شما فقط می توانید این کلاس را در همان کتابخانه گسترش دهید یا پیاده سازی کنید . از آنجایی که تحلیلگر انواع فرعی این کلاس را می شناسد، اگر سوئیچ نتواند یکی از آنها را پوشش دهد و جامع نباشد، خطا را گزارش می کند.
برای نمایش ویجت ها از عبارت switch استفاده کنید
- کلاس BlockWidget را در
main.dart
با یک عبارت سوئیچ که از الگوهای شی برای هر مورد استفاده می کند، به روز کنید:
lib/main.dart
class BlockWidget extends StatelessWidget {
final Block block;
const BlockWidget({
required this.block,
super.key,
});
@override
Widget build(BuildContext context) {
return Container(
margin: const EdgeInsets.all(8),
child: switch (block) {
HeaderBlock(:final text) => Text(
text,
style: Theme.of(context).textTheme.displayMedium,
),
ParagraphBlock(:final text) => Text(text),
CheckboxBlock(:final text, :final isChecked) => Row(
children: [
Checkbox(value: isChecked, onChanged: (_) {}),
Text(text),
],
),
},
);
}
}
در اولین نسخه BlockWidget
، فیلدی از یک شی Block
را برای برگرداندن یک TextStyle
روشن کردید. اکنون، نمونهای از خود شی Block
را تغییر میدهید و با الگوهای شی که نشاندهنده زیر کلاسهای آن هستند، مطابقت میدهید، و ویژگیهای شی را در فرآیند استخراج میکنید.
تحلیلگر دارت میتواند بررسی کند که هر زیر کلاس در عبارت سوئیچ مدیریت میشود زیرا شما Block
یک کلاس مهر و موم شده ساختهاید.
همچنین توجه داشته باشید که استفاده از عبارت switch در اینجا به شما امکان می دهد نتیجه را مستقیماً به عنصر child
منتقل کنید، برخلاف دستور بازگشت جداگانه ای که قبلاً لازم بود.
- برای مشاهده چک باکس دادههای JSON که برای اولین بار ارائه شده است، دوباره بارگیری کنید:
13. تبریک می گویم
شما با موفقیت الگوها، رکوردها، سوئیچ و کیس پیشرفته و کلاس های مهر و موم شده را آزمایش کردید. شما اطلاعات زیادی را پوشش دادید - اما به سختی سطح این ویژگی ها را خراشیدید. برای اطلاعات بیشتر در مورد الگوها، مشخصات ویژگی را ببینید.
انواع الگوهای مختلف، زمینههای متفاوتی که میتوانند در آن ظاهر شوند، و تودرتوی بالقوه زیرالگوها، احتمالات در رفتار را به ظاهر بیپایان میسازد. اما دیدن آنها آسان است.
شما می توانید انواع روش ها را برای نمایش محتوا در Flutter با استفاده از الگوها تصور کنید. با استفاده از الگوها، می توانید با خیال راحت داده ها را استخراج کنید تا رابط کاربری خود را در چند خط کد بسازید.
بعدش چی؟
- مستندات مربوط به الگوها، رکوردها، سوئیچ ها و موارد پیشرفته و اصلاح کننده های کلاس را در بخش زبان اسناد دارت بررسی کنید.
اسناد مرجع
نمونه کد کامل را گام به گام در مخزن flutter/codelabs
مشاهده کنید.
برای مشخصات دقیق هر ویژگی جدید، اسناد طراحی اصلی را بررسی کنید: