इस कोडलैब (कोड बनाना सीखने के लिए ट्यूटोरियल) के बारे में जानकारी
1. परिचय
Dart 3 में भाषा में पैटर्न जोड़े गए हैं. यह व्याकरण की एक नई कैटगरी है. Dart कोड लिखने के इस नए तरीके के अलावा, भाषा में कई अन्य सुधार भी किए गए हैं. इनमें ये शामिल हैं
- अलग-अलग तरह के डेटा को बंडल करने के लिए रिकॉर्ड,
- ऐक्सेस कंट्रोल करने के लिए क्लास में बदलाव करने वाले टूल, और
- नए switch एक्सप्रेशन और if-case स्टेटमेंट.
इन सुविधाओं की मदद से, Dart कोड लिखते समय आपके पास ज़्यादा विकल्प होते हैं. इस कोडलैब में, आपको इनका इस्तेमाल करने का तरीका पता चलेगा. इससे, आपके कोड को ज़्यादा कॉम्पैक्ट, आसान, और सुविधाजनक बनाया जा सकता है.
इस कोडलैब में यह माना गया है कि आपको Flutter और Dart के बारे में कुछ जानकारी है. अगर आपको लगता है कि आपने कुछ समय से वीडियो एडिटिंग नहीं की है, तो इन संसाधनों की मदद से बुनियादी बातों को दोबारा याद करें:
आपको क्या बनाना है
इस कोडलैब में, ऐसा ऐप्लिकेशन बनाया गया है जो Flutter में JSON दस्तावेज़ दिखाता है. ऐप्लिकेशन, किसी बाहरी सोर्स से आने वाले JSON को सिम्युलेट करता है. जेएसओएन में दस्तावेज़ का डेटा शामिल होता है. जैसे, बदलाव करने की तारीख, टाइटल, हेडर, और पैराग्राफ़. डेटा को रिकॉर्ड में व्यवस्थित तरीके से पैक करने के लिए कोड लिखा जाता है, ताकि आपके Flutter विजेट को जहां भी ज़रूरत हो वहां डेटा ट्रांसफ़र किया जा सके और उसे अनपैक किया जा सके.
इसके बाद, जब वैल्यू उस पैटर्न से मेल खाती है, तो सही विजेट बनाने के लिए पैटर्न का इस्तेमाल किया जाता है. साथ ही, डेटा को लोकल वैरिएबल में डिस्ट्रक्चर करने के लिए पैटर्न का इस्तेमाल करने का तरीका भी देखा जा सकता है.
आपको क्या सीखने को मिलेगा
- ऐसा रिकॉर्ड बनाने का तरीका जो अलग-अलग तरह की कई वैल्यू को सेव करता है.
- किसी रिकॉर्ड का इस्तेमाल करके, फ़ंक्शन से कई वैल्यू पाने का तरीका.
- रिकॉर्ड और अन्य ऑब्जेक्ट के डेटा को मैच करने, उसकी पुष्टि करने, और उसे अलग-अलग हिस्सों में बांटने के लिए, पैटर्न का इस्तेमाल करने का तरीका.
- पैटर्न से मैच होने वाली वैल्यू को नए या मौजूदा वैरिएबल से बांधने का तरीका.
- switch स्टेटमेंट की नई सुविधाओं, switch एक्सप्रेशन, और if-case स्टेटमेंट का इस्तेमाल करने का तरीका.
- पूरी तरह से जांच करने की सुविधा का फ़ायदा कैसे लें, ताकि यह पक्का किया जा सके कि हर मामले को स्विच स्टेटमेंट या स्विच एक्सप्रेशन में मैनेज किया गया है.
2. अपना एनवायरमेंट सेट अप करने का तरीका
- Flutter SDK इंस्टॉल करें.
- कोई एडिटर सेट अप करें, जैसे कि Visual Studio Code (VS Code).
- टारगेट किए गए कम से कम एक प्लैटफ़ॉर्म (iOS, Android, डेस्कटॉप या वेब ब्राउज़र) के लिए, प्लैटफ़ॉर्म सेटअप करने का तरीका अपनाएं.
3. प्रोजेक्ट बनाना
पैटर्न, रिकॉर्ड, और अन्य नई सुविधाओं के बारे में जानने से पहले, एक आसान Flutter प्रोजेक्ट बनाएं. इसके लिए, अपना पूरा कोड खुद लिखें.
Flutter प्रोजेक्ट बनाना
patterns_codelab
नाम का नया प्रोजेक्ट बनाने के लिए,flutter create
कमांड का इस्तेमाल करें.--empty
फ़्लैग,lib/main.dart
फ़ाइल में स्टैंडर्ड काउंटर ऐप्लिकेशन बनाने से रोकता है. हालांकि, आपको इसे हटाना ही होगा.
flutter create --empty patterns_codelab
- इसके बाद, VS Code का इस्तेमाल करके
patterns_codelab
डायरेक्ट्री खोलें.
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"
}
]
}
''';
किसी ऐसे प्रोग्राम की कल्पना करें जो किसी बाहरी सोर्स से डेटा पा रहा हो. जैसे, I/O स्ट्रीम या एचटीटीपी अनुरोध. इस कोडलैब में, documentJson
वैरिएबल में एक से ज़्यादा लाइन वाली स्ट्रिंग का इस्तेमाल करके, आने वाले JSON डेटा को मॉक करके, ज़्यादा असली इस्तेमाल के उदाहरण को आसान बनाया गया है.
JSON डेटा को Document
क्लास में दिखाया जाता है. इस कोडलैब में आगे, ऐसे फ़ंक्शन जोड़े जाते हैं जो पार्स किए गए JSON से डेटा दिखाते हैं. यह क्लास, अपने कंस्ट्रक्टर में _json
फ़ील्ड को तय और शुरू करती है.
ऐप्लिकेशन चलाना
flutter create
कमांड, डिफ़ॉल्ट Flutter फ़ाइल स्ट्रक्चर के हिस्से के तौर पर lib/main.dart
फ़ाइल बनाता है.
- ऐप्लिकेशन के लिए शुरुआती पॉइंट बनाने के लिए,
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
, यूज़र इंटरफ़ेस (यूआई) को थीम देने के लिए, Material Design का नया वर्शन सेट अप करता है.DocumentScreen
,Scaffold
विजेट का इस्तेमाल करके पेज का विज़ुअल लेआउट दिखाता है.
- यह पक्का करने के लिए कि सब कुछ ठीक से काम कर रहा है, चालू करें और डीबग करें पर क्लिक करके, ऐप्लिकेशन को अपनी होस्ट मशीन पर चलाएं:
- डिफ़ॉल्ट रूप से, Flutter वह टारगेट प्लैटफ़ॉर्म चुनता है जो उपलब्ध होता है. टारगेट प्लैटफ़ॉर्म बदलने के लिए, स्टेटस बार पर मौजूदा प्लैटफ़ॉर्म चुनें:
आपको DocumentScreen
विजेट में तय किए गए title
और body
एलिमेंट के साथ एक खाली फ़्रेम दिखेगा:
5. रिकॉर्ड बनाना और दिखाना
इस चरण में, फ़ंक्शन कॉल से कई वैल्यू दिखाने के लिए रिकॉर्ड का इस्तेमाल किया जाता है. इसके बाद, वैल्यू ऐक्सेस करने और उन्हें यूज़र इंटरफ़ेस (यूआई) में दिखाने के लिए, DocumentScreen
विजेट में उस फ़ंक्शन को कॉल किया जाता है.
रिकॉर्ड बनाना और उसे दिखाना
data.dart
में,metadata
नाम की Document क्लास में एक नया getter तरीका जोड़ें, जो रिकॉर्ड दिखाता है:
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
विजेट में,build
तरीके मेंmetadata
गटर तरीके को कॉल करें, ताकि आप अपना रिकॉर्ड पा सकें और उसकी वैल्यू ऐक्सेस कर सकें:
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
फ़ील्ड, टाइमस्टैंप है. इसे स्ट्रिंग इंटरपोलेशन का इस्तेमाल करके,String
में बदला जाता है.
अलग-अलग तरह का डेटा दिखाने के लिए, टाइप-सेफ़ तरीके से एक क्लास तय की जा सकती है. हालांकि, यह तरीका ज़्यादा शब्दों वाला होता है.
6. पैटर्न के साथ मैच करना और उन्हें अलग-अलग हिस्सों में बांटना
रिकॉर्ड, अलग-अलग तरह का डेटा आसानी से इकट्ठा कर सकते हैं और उसे आसानी से शेयर कर सकते हैं. अब पैटर्न का इस्तेमाल करके, अपने कोड को बेहतर बनाएं.
पैटर्न, ब्लूप्रिंट की तरह एक स्ट्रक्चर को दिखाता है, जिसमें एक या उससे ज़्यादा वैल्यू हो सकती हैं. पैटर्न की तुलना असल वैल्यू से की जाती है, ताकि यह पता लगाया जा सके कि वे मैच करते हैं या नहीं.
कुछ पैटर्न, मैच होने पर उससे डेटा खींचकर, मैच हुई वैल्यू को डिस्ट्रक्चर कर देते हैं. डेस्ट्रक्चर करने की सुविधा की मदद से, किसी ऑब्जेक्ट की वैल्यू को अनपैक करके, उन्हें लोकल वैरिएबल को असाइन किया जा सकता है या उन पर आगे की मैचिंग की जा सकती है.
किसी रिकॉर्ड को लोकल वैरिएबल में अलग-अलग करना
metadata
को कॉल करने के लिए,DocumentScreen
केbuild
तरीके को फिर से तैयार करें और इसका इस्तेमाल पैटर्न वैरिएबल के एलान को शुरू करने के लिए करें:
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
जैसे एक ही टाइप और नाम वाले नए लोकल वैरिएबल से जोड़ा जाता है. ऐसा इसलिए किया जाता है, क्योंकि ये वैल्यू मैच करती हैं.
जब किसी फ़ील्ड का नाम और उसे पॉप्युलेट करने वाला वैरिएबल एक ही होता है, तो इसके लिए एक शॉर्टहैंड होता है. DocumentScreen
के build
तरीके को इस तरह फिर से बनाएं.
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
लिखें.
- पिछले चरण में जैसा नतीजा मिला था वैसा ही नतीजा देखने के लिए, हॉट रीलोड करें. कोड का काम पहले जैसा ही है. आपने सिर्फ़ कोड को ज़्यादा छोटा बनाया है.
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 कहा जाता है. केस बॉडी सिर्फ़ तब लागू होती है, जब केस पैटर्न _json
में मौजूद डेटा से मेल खाता हो. यह मैच, इनकमिंग JSON की पुष्टि करने के लिए, metadata
के पहले वर्शन में लिखी गई वही जांच करता है. यह कोड इन चीज़ों की पुष्टि करता है:
_json
, मैप का टाइप है._json
मेंmetadata
कुंजी शामिल है._json
शून्य नहीं है._json['metadata']
भी मैप का एक टाइप है._json['metadata']
में कुंजियांtitle
औरmodified
शामिल हैं.title
औरlocalModified
स्ट्रिंग हैं और ये null नहीं हैं.
अगर वैल्यू मेल नहीं खाती है, तो पैटर्न अस्वीकार कर देता है (कार्रवाई जारी रखने से इनकार करता है) और else
क्लॉज़ पर चला जाता है. मैच होने पर, पैटर्न मैप से title
और modified
की वैल्यू को अलग कर देता है और उन्हें नए लोकल वैरिएबल से बांध देता है.
पैटर्न की पूरी सूची के लिए, सुविधा के स्पेसिफ़िकेशन में पैटर्न सेक्शन में टेबल देखें.
8. ऐप्लिकेशन को ज़्यादा पैटर्न के लिए तैयार करना
अब तक, आपने JSON डेटा के metadata
हिस्से को ठीक किया है. इस चरण में, blocks
सूची में मौजूद डेटा को मैनेज करने और उसे अपने ऐप्लिकेशन में रेंडर करने के लिए, अपने कारोबार के लॉजिक को थोड़ा और बेहतर बनाया जाता है.
{
"metadata": {
// ...
},
"blocks": [
{
"type": "h1",
"text": "Chapter 1"
},
// ...
]
}
डेटा स्टोर करने वाली क्लास बनाना
data.dart
में एक नई क्लास,Block
जोड़ें. इसका इस्तेमाल, 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
नाम की अतिरिक्त जानकारी मौजूद है, जो पैटर्न में नहीं है. ऐसा इसलिए होता है, क्योंकि "मैप पैटर्न" कहे जाने वाले इस तरह के पैटर्न का इस्तेमाल करने पर, वे सिर्फ़ उन चीज़ों पर ध्यान देते हैं जिन्हें आपने पैटर्न में तय किया है. साथ ही, डेटा में मौजूद अन्य चीज़ों को अनदेखा कर देते हैं.
ब्लॉक ऑब्जेक्ट की सूची दिखाता है
- इसके बाद,
Document
क्लास में नया फ़ंक्शनgetBlocks()
जोड़ें.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
मेटाडेटा की वैल्यू को blocksJson
नाम के नए List
में कास्ट करता है. पैटर्न के बिना, आपको कास्ट करने के लिए toList()
मेथड की ज़रूरत होगी.
नई सूची को Block
ऑब्जेक्ट से भरने के लिए, सूची के लिटरल में collection for शामिल होता है.
इस सेक्शन में, पैटर्न से जुड़ी ऐसी कोई सुविधा नहीं दी गई है जिसे आपने इस कोडलैब में पहले से आज़माया न हो. अगले चरण में, आपको अपने यूज़र इंटरफ़ेस (यूआई) में सूची के आइटम रेंडर करने की तैयारी करनी होगी.
9. दस्तावेज़ दिखाने के लिए पैटर्न का इस्तेमाल करना
अब आपके पास if-case स्टेटमेंट और ऐसे पैटर्न का इस्तेमाल करके, अपने JSON डेटा को अलग-अलग हिस्सों में बांटने और फिर से जोड़ने का विकल्प है जिन पर भरोसा नहीं किया जा सकता. हालांकि, पैटर्न के साथ मिलने वाले फ़्लो स्ट्रक्चर को कंट्रोल करने के लिए, अगर-स्थिति सिर्फ़ एक बेहतर सुविधा है. अब, झूठे साबित होने वाले पैटर्न के बारे में अपनी जानकारी को स्विच स्टेटमेंट पर लागू करें.
स्विच स्टेटमेंट के साथ पैटर्न का इस्तेमाल करके, यह कंट्रोल करना कि क्या रेंडर किया जाए
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,
),
);
}
}
build
तरीके में मौजूद स्विच स्टेटमेंट, block
ऑब्जेक्ट के type
फ़ील्ड को चालू करता है.
- पहले केस स्टेटमेंट में, कंसटेंट स्ट्रिंग पैटर्न का इस्तेमाल किया गया है. अगर
block.type
, कॉन्स्टेंट वैल्यूh1
के बराबर है, तो पैटर्न मैच होता है. - दूसरा केस स्टेटमेंट, लॉजिकल-या पैटर्न का इस्तेमाल करता है. इसमें दो कॉन्स्टेंट स्ट्रिंग पैटर्न, सबपैटर्न के तौर पर इस्तेमाल किए जाते हैं. अगर
block.type
,p
याcheckbox
में से किसी भी सबपैटर्न से मेल खाता है, तो पैटर्न मैच करता है.
- आखिरी केस, वाइल्डकार्ड पैटर्न,
_
है. स्विच केस में वाइल्डकार्ड, बाकी सभी चीज़ों से मेल खाते हैं. येdefault
क्लॉज़ की तरह ही काम करते हैं. इन्हें अब भी स्विच स्टेटमेंट में इस्तेमाल किया जा सकता है. हालांकि, ये थोड़े ज़्यादा शब्दों वाले होते हैं.
वाइल्डकार्ड पैटर्न का इस्तेमाल, पैटर्न की अनुमति वाले किसी भी हिस्से में किया जा सकता है. उदाहरण के लिए, वैरिएबल के एलान वाले पैटर्न में: var (title, _) = document.metadata;
इस संदर्भ में, वाइल्डकार्ड किसी भी वैरिएबल को बांधता नहीं है. यह दूसरे फ़ील्ड को खारिज कर देता है.
अगले सेक्शन में, Block
ऑब्जेक्ट दिखाने के बाद, स्विच की अन्य सुविधाओं के बारे में बताया गया है.
दस्तावेज़ का कॉन्टेंट दिखाना
DocumentScreen
विजेट के build
तरीके में getBlocks()
को कॉल करके, Block
ऑब्जेक्ट की सूची वाला एक लोकल वैरिएबल बनाएं.
DocumentationScreen
में मौजूदाbuild
तरीके को इस वर्शन से बदलें:
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])
लाइन, getBlocks()
तरीके से लौटाए गए ब्लॉक की सूची में मौजूद हर आइटम के लिए एक BlockWidget
विजेट बनाती है.
- ऐप्लिकेशन चलाएं. इसके बाद, आपको स्क्रीन पर ब्लॉक दिखेंगे:
10. स्विच एक्सप्रेशन का इस्तेमाल करना
पैटर्न की मदद से, switch
और case
में कई सुविधाएं जोड़ी जा सकती हैं. Dart में स्विच एक्सप्रेशन हैं, ताकि फ़ंक्शन को ज़्यादा जगहों पर इस्तेमाल किया जा सके. केस की सीरीज़, सीधे तौर पर वैरिएबल असाइनमेंट या रिटर्न स्टेटमेंट में वैल्यू दे सकती है.
स्विच स्टेटमेंट को स्विच एक्सप्रेशन में बदलना
Dart analyzer, कोड में बदलाव करने में आपकी मदद करने के लिए सहायता करता है.
- अपने कर्सर को पिछले सेक्शन के स्विच स्टेटमेंट पर ले जाएं.
- उपलब्ध सहायता देखने के लिए, लाइट बल्ब पर क्लिक करें.
- स्विच एक्सप्रेशन में बदलें सहायता चुनें.
इस कोड का नया वर्शन ऐसा दिखता है:
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,
),
);
}
}
स्विच एक्सप्रेशन, स्विच स्टेटमेंट जैसा दिखता है. हालांकि, इसमें case
कीवर्ड का इस्तेमाल नहीं किया जाता. साथ ही, पैटर्न को केस बॉडी से अलग करने के लिए, =>
का इस्तेमाल किया जाता है. स्विच स्टेटमेंट के उलट, स्विच एक्सप्रेशन एक वैल्यू दिखाते हैं. साथ ही, इनका इस्तेमाल एक्सप्रेशन के तौर पर कहीं भी किया जा सकता है.
11. ऑब्जेक्ट पैटर्न का इस्तेमाल करना
Dart एक ऑब्जेक्ट-ओरिएंटेड भाषा है, इसलिए पैटर्न सभी ऑब्जेक्ट पर लागू होते हैं. इस चरण में, अपने यूज़र इंटरफ़ेस (यूआई) की तारीख रेंडर करने के लॉजिक को बेहतर बनाने के लिए, ऑब्जेक्ट पैटर्न को चालू किया जाता है और ऑब्जेक्ट प्रॉपर्टी को डिस्ट्रक्चर किया जाता है.
ऑब्जेक्ट पैटर्न से प्रॉपर्टी निकालना
इस सेक्शन में, पैटर्न का इस्तेमाल करके, बदलाव करने की आखिरी तारीख को दिखाने के तरीके को बेहतर बनाया जा सकता है.
main.dart
मेंformatDate
तरीका जोड़ें:
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',
};
}
यह तरीका एक स्विच एक्सप्रेशन दिखाता है, जो Duration
ऑब्जेक्ट की वैल्यू difference
को चालू करता है. यह JSON डेटा में today
और modified
वैल्यू के बीच के समय को दिखाता है.
स्विच एक्सप्रेशन के हर मामले में, ऑब्जेक्ट के पैटर्न का इस्तेमाल किया जा रहा है. यह पैटर्न, ऑब्जेक्ट की प्रॉपर्टी inDays
और isNegative
पर मौजूद गेट्टर को कॉल करके मैच करता है. सिंटैक्स से ऐसा लगता है कि यह Duration ऑब्जेक्ट बना रहा है, लेकिन असल में यह difference
ऑब्जेक्ट के फ़ील्ड ऐक्सेस कर रहा है.
पहले तीन उदाहरणों में, ऑब्जेक्ट प्रॉपर्टी inDays
से मैच करने और उससे जुड़ी स्ट्रिंग दिखाने के लिए, कॉन्स्टेंट सबपैटर्न 0
, 1
, और -1
का इस्तेमाल किया जाता है.
आखिरी दो मामले, आज, कल, और कल के बाद की अवधि को मैनेज करते हैं:
- अगर
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-cases, switch स्टेटमेंट, और switch एक्सप्रेशन में किया जा सकता है.
- ये किसी पैटर्न में शर्त सिर्फ़ मैच होने के बाद जोड़ते हैं.
- अगर गार्ड क्लॉज़ की वैल्यू गलत होती है, तो पूरे पैटर्न को अस्वीकार कर दिया जाता है और अगले केस पर कार्रवाई की जाती है.
नए फ़ॉर्मैट में बदली गई तारीख को यूज़र इंटरफ़ेस (यूआई) में जोड़ना
- आखिर में,
formatDate
फ़ंक्शन का इस्तेमाल करने के लिए,DocumentScreen
मेंbuild
का तरीका अपडेट करें:
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
टाइप को चालू करना तब मुश्किल होता है, जब एनम की हर वैल्यू के लिए केस भी हों. इसकी वजह यह है कि एनम, कॉन्स्टेंट वैल्यू की तय संख्या दिखाते हैं.
Dart 3 में, नए क्लास मॉडिफ़ायर sealed
की मदद से, ऑब्जेक्ट और क्लास के लेआउट की पूरी जांच की सुविधा जोड़ी गई है. अपनी Block
क्लास को सील की गई सुपरक्लास के तौर पर फिर से तैयार करें.
सबक्लास बनाना
data.dart
में,Block
को एक्सटेंड करने वाली तीन नई क्लास बनाएं—HeaderBlock
,ParagraphBlock
, औरCheckboxBlock
:
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);
}
इनमें से हर क्लास, ओरिजनल JSON की अलग-अलग type
वैल्यू से जुड़ी होती है: 'h1'
, 'p'
, और 'checkbox'
.
सुपर क्लास को सील करना
Block
क्लास कोsealed
के तौर पर मार्क करें. इसके बाद, if-case को स्विच एक्सप्रेशन के तौर पर फिर से लिखें. यह एक्सप्रेशन, JSON में बताए गएtype
से जुड़ा सबक्लास दिखाता है:
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
कीवर्ड एक क्लास मॉडिफ़ायर है. इसका मतलब है कि इस क्लास को सिर्फ़ उसी लाइब्रेरी में एक्सटेंड या लागू किया जा सकता है. विश्लेषक को इस क्लास के सब-टाइप की जानकारी होती है. इसलिए, अगर कोई स्विच उनमें से किसी एक को कवर नहीं करता है और पूरी जानकारी नहीं देता है, तो वह गड़बड़ी की रिपोर्ट करता है.
विजेट दिखाने के लिए, स्विच एक्सप्रेशन का इस्तेमाल करना
main.dart
मेंBlockWidget
क्लास को, स्विच एक्सप्रेशन के साथ अपडेट करें. यह एक्सप्रेशन हर मामले के लिए ऑब्जेक्ट पैटर्न का इस्तेमाल करता है:
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
के पहले वर्शन में, आपने TextStyle
दिखाने के लिए, Block
ऑब्जेक्ट के फ़ील्ड को चालू किया था. अब, Block
ऑब्जेक्ट के इंस्टेंस को स्विच करें और उसकी सबक्लास दिखाने वाले ऑब्जेक्ट पैटर्न से मैच करें. इस प्रोसेस में, ऑब्जेक्ट की प्रॉपर्टी निकाली जाती हैं.
Dart analyzer यह जांच सकता है कि हर सबक्लास को स्विच एक्सप्रेशन में मैनेज किया गया है या नहीं, क्योंकि आपने Block
को सील की गई क्लास बनाया है.
यह भी ध्यान दें कि यहां स्विच एक्सप्रेशन का इस्तेमाल करके, नतीजे को सीधे child
एलिमेंट में पास किया जा सकता है. ऐसा करने के लिए, पहले अलग से return स्टेटमेंट की ज़रूरत होती थी.
- चेकबॉक्स का JSON डेटा पहली बार रेंडर होने के बाद देखने के लिए, हॉट रीलोड करें:
13. बधाई हो
आपने पैटर्न, रिकॉर्ड, बेहतर स्विच और केस, और सील की गई क्लास के साथ प्रयोग किया है. आपने बहुत सारी जानकारी दी है, लेकिन इन सुविधाओं के बारे में ज़्यादा नहीं बताया है. पैटर्न के बारे में ज़्यादा जानकारी के लिए, सुविधा की खास बातें देखें.
अलग-अलग तरह के पैटर्न, अलग-अलग संदर्भों में दिखने की संभावना, और सबपैटर्न के नेस्ट होने की वजह से, उपयोगकर्ता के व्यवहार की संभावनाएं अनगिनत लगती हैं. हालांकि, इन्हें देखना आसान है.
पैटर्न का इस्तेमाल करके, Flutter में कॉन्टेंट दिखाने के सभी तरीके आज़माए जा सकते हैं. पैटर्न का इस्तेमाल करके, डेटा को सुरक्षित तरीके से निकाला जा सकता है, ताकि कुछ लाइनों के कोड में अपना यूज़र इंटरफ़ेस (यूआई) बनाया जा सके.
आगे क्या करना है?
- Dart के दस्तावेज़ के भाषा सेक्शन में, पैटर्न, रिकॉर्ड, बेहतर स्विच और केस, और क्लास मॉडिफ़ायर के बारे में दस्तावेज़ देखें.
रेफ़रंस दस्तावेज़
flutter/codelabs
रिपॉज़िटरी में, सिलसिलेवार तरीके से पूरा सैंपल कोड देखें.
हर नई सुविधा के बारे में ज़्यादा जानकारी के लिए, डिज़ाइन के मूल दस्तावेज़ देखें: