Dart के पैटर्न और रिकॉर्ड के बारे में ज़्यादा जानें

1. परिचय

Dart 3 में, भाषा के लिए पैटर्न पेश किए गए हैं. यह व्याकरण की एक नई कैटगरी है. Dart कोड लिखने के इस नए तरीके के अलावा, भाषा में कई अन्य सुधार भी किए गए हैं. इनमें ये शामिल हैं

  • अलग-अलग तरह के डेटा को बंडल करने के लिए रिकॉर्ड,
  • ऐक्सेस कंट्रोल करने के लिए, क्लास मॉडिफ़ायर
  • नए स्विच एक्सप्रेशन और if-case स्टेटमेंट.

इन सुविधाओं से, Dart कोड लिखते समय आपको ज़्यादा विकल्प मिलते हैं. इस कोडलैब में, इन फ़ंक्शन का इस्तेमाल करके अपने कोड को ज़्यादा कॉम्पैक्ट, स्ट्रीमलाइन, और फ़्लेक्सिबल बनाने का तरीका जानें.

इस कोडलैब में यह मानकर चला गया है कि आपको Flutter और Dart के बारे में कुछ जानकारी है. अगर आपको बुनियादी बातें याद नहीं हैं, तो यहां दिए गए संसाधनों की मदद से उन्हें फिर से जान लें:

आपको क्या बनाना है

इस कोडलैब में, एक ऐसा ऐप्लिकेशन बनाया गया है जो Flutter में JSON दस्तावेज़ दिखाता है. यह ऐप्लिकेशन, बाहरी सोर्स से मिलने वाले JSON का सिम्युलेशन करता है. जेएसओएन में दस्तावेज़ का डेटा शामिल होता है. जैसे, बदलाव की तारीख, टाइटल, हेडर, और पैराग्राफ़. डेटा को रिकॉर्ड में व्यवस्थित तरीके से पैक करने के लिए कोड लिखें, ताकि इसे ट्रांसफ़र किया जा सके और जहां भी आपके Flutter विजेट को इसकी ज़रूरत हो वहां इसे अनपैक किया जा सके.

इसके बाद, वैल्यू के पैटर्न से मैच होने पर, सही विजेट बनाने के लिए पैटर्न का इस्तेमाल किया जाता है. आपको यह भी पता चलेगा कि पैटर्न का इस्तेमाल करके, डेटा को लोकल वैरिएबल में कैसे बदला जाता है.

इस कोडलैब में बनाया गया फ़ाइनल ऐप्लिकेशन, एक ऐसा दस्तावेज़ होता है जिसमें टाइटल, पिछली बार बदलाव की तारीख, हेडर, और पैराग्राफ़ होते हैं.

आपको क्या सीखने को मिलेगा

  • ऐसा रिकॉर्ड कैसे बनाएं जिसमें अलग-अलग टाइप की कई वैल्यू सेव की जा सकें.
  • रिकॉर्ड का इस्तेमाल करके, किसी फ़ंक्शन से एक से ज़्यादा वैल्यू कैसे दिखाई जाती हैं.
  • रिकॉर्ड और अन्य ऑब्जेक्ट से डेटा को मैच करने, उसकी पुष्टि करने, और उसे अलग-अलग हिस्सों में बांटने के लिए, पैटर्न का इस्तेमाल कैसे करें.
  • पैटर्न से मैच करने वाली वैल्यू को नए या मौजूदा वैरिएबल से कैसे बाइंड करें.
  • स्विच स्टेटमेंट, स्विच एक्सप्रेशन, और if-case स्टेटमेंट की नई सुविधाओं का इस्तेमाल कैसे करें.
  • पूरी तरह से जांच करने की सुविधा का इस्तेमाल कैसे करें, ताकि यह पक्का किया जा सके कि हर मामले को स्विच स्टेटमेंट या स्विच एक्सप्रेशन में हैंडल किया गया है.

2. अपना एनवायरमेंट सेट अप करने का तरीका

  1. Flutter SDK इंस्टॉल करें.
  2. Visual Studio Code (VS Code) जैसे एडिटर को सेट अप करें.
  3. कम से कम एक टारगेट प्लैटफ़ॉर्म (iOS, Android, डेस्कटॉप या वेब ब्राउज़र) के लिए, प्लैटफ़ॉर्म सेटअप के चरणों को पूरा करें.

3. प्रोजेक्ट बनाना

पैटर्न, रिकॉर्ड, और अन्य नई सुविधाओं के बारे में जानने से पहले, एक Flutter प्रोजेक्ट बनाएं. इसमें आपको अपना पूरा कोड लिखना होगा.

Flutter प्रोजेक्ट बनाना

  1. patterns_codelab नाम का नया प्रोजेक्ट बनाने के लिए, flutter create कमांड का इस्तेमाल करें. --empty फ़्लैग की वजह से, lib/main.dart फ़ाइल में स्टैंडर्ड काउंटर ऐप्लिकेशन नहीं बनाया जा सकता. हालांकि, आपको इसे हटाना ही होगा.
flutter create --empty patterns_codelab
  1. इसके बाद, VS Code का इस्तेमाल करके patterns_codelab डायरेक्ट्री खोलें.
code patterns_codelab

VS Code में बनाया गया प्रोजेक्ट दिखाया गया है

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 फ़ाइल बनाती है.

  1. ऐप्लिकेशन के लिए शुरुआती पॉइंट बनाने के लिए, 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(),
      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 विजेट का इस्तेमाल करके पेज का विज़ुअल लेआउट दिखाता है.
  1. यह पक्का करने के लिए कि सब कुछ ठीक से काम कर रहा है, अपनी होस्ट मशीन पर ऐप्लिकेशन चलाएँ. इसके लिए, चलाएँ और डीबग करें पर क्लिक करें:

&#39;चलाएं और डीबग करें&#39; बटन

  1. डिफ़ॉल्ट रूप से, Flutter उस टारगेट प्लैटफ़ॉर्म को चुनता है जो उपलब्ध होता है. टारगेट प्लैटफ़ॉर्म बदलने के लिए, स्टेटस बार पर मौजूद मौजूदा प्लैटफ़ॉर्म चुनें:

VS Code में टारगेट प्लैटफ़ॉर्म चुनने वाला टूल

आपको एक खाली फ़्रेम दिखेगा. इसमें DocumentScreen विजेट में तय किए गए title और body एलिमेंट दिखेंगे:

इस चरण में बनाया गया ऐप्लिकेशन.

5. रिकॉर्ड बनाना और उन्हें वापस लाना

इस चरण में, रिकॉर्ड का इस्तेमाल करके फ़ंक्शन कॉल से कई वैल्यू वापस लाई जाती हैं. इसके बाद, DocumentScreen विजेट में उस फ़ंक्शन को कॉल करें, ताकि वैल्यू ऐक्सेस की जा सकें और उन्हें यूज़र इंटरफ़ेस (यूआई) में दिखाया जा सके.

रिकॉर्ड बनाना और उसे वापस करना

  • data.dart में, Document क्लास में metadata नाम का एक नया 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 है.

रिकॉर्ड फ़ील्ड ऐक्सेस करना

  1. DocumentScreen विजेट में, build तरीके में metadata getter तरीके को कॉल करें, ताकि आपको अपना रिकॉर्ड मिल सके और आप उसकी वैल्यू ऐक्सेस कर सकें:

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: [                                        // And the following line.
          Center(child: Text('Last modified ${metadataRecord.modified}')),
        ],
      ),
    );
  }
}

metadata getter मेथड, एक रिकॉर्ड दिखाता है. इसे लोकल वैरिएबल metadataRecord को असाइन किया जाता है. रिकॉर्ड, एक फ़ंक्शन कॉल से कई वैल्यू वापस पाने और उन्हें किसी वैरिएबल को असाइन करने का आसान तरीका है.

उस रिकॉर्ड में मौजूद अलग-अलग फ़ील्ड को ऐक्सेस करने के लिए, रिकॉर्ड के बिल्ट-इन गेटर सिंटैक्स का इस्तेमाल किया जा सकता है.

  • पोज़ीशनल फ़ील्ड (नाम के बिना फ़ील्ड, जैसे कि title) पाने के लिए, रिकॉर्ड पर getter का इस्तेमाल करें. इससे सिर्फ़ बिना नाम वाले फ़ील्ड दिखते हैं.
  • modified जैसे नाम वाले फ़ील्ड में पोज़ीशनल गेटर नहीं होता. इसलिए, इसके नाम का इस्तेमाल सीधे तौर पर किया जा सकता है. जैसे, metadataRecord.modified.

किसी पोज़ीशनल फ़ील्ड के लिए गेटर का नाम तय करने के लिए, $1 से शुरू करें और नाम वाले फ़ील्ड छोड़ें. उदाहरण के लिए:

var record = (named: 'v', 'y', named2: 'x', 'z');
print(record.$1);                               // prints y
print(record.$2);                               // prints z
  1. ऐप्लिकेशन में JSON वैल्यू देखने के लिए, हॉट रिलोड करें. VS Code Dart प्लगिन, फ़ाइल सेव करने पर हर बार हॉट-रीलोड करता है.

ऐप्लिकेशन का स्क्रीनशॉट, जिसमें टाइटल और बदलाव की तारीख दिख रही है.

आपको दिखेगा कि हर फ़ील्ड ने अपना टाइप बनाए रखा है.

  • Text() तरीके में, स्ट्रिंग को पहले आर्ग्युमेंट के तौर पर लिया जाता है.
  • modified फ़ील्ड, तारीख और समय की जानकारी देने वाला फ़ील्ड है. इसे स्ट्रिंग इंटरपोलेशन का इस्तेमाल करके String में बदला जाता है.

अलग-अलग तरह के डेटा को टाइप-सेफ़ तरीके से वापस लाने का दूसरा तरीका, क्लास को तय करना है. हालांकि, इसमें ज़्यादा जानकारी देनी होती है.

6. पैटर्न के साथ मैच करना और डिस्ट्रक्चर करना

रिकॉर्ड, अलग-अलग तरह का डेटा आसानी से इकट्ठा कर सकते हैं और उसे आसानी से शेयर कर सकते हैं. अब पैटर्न का इस्तेमाल करके, अपने कोड को बेहतर बनाएं.

पैटर्न, एक ऐसा स्ट्रक्चर होता है जिसमें एक या उससे ज़्यादा वैल्यू हो सकती हैं. यह एक ब्लूप्रिंट की तरह होता है. पैटर्न की तुलना असल वैल्यू से की जाती है, ताकि यह पता लगाया जा सके कि वे मेल खाती हैं या नहीं.

कुछ पैटर्न मैच होने पर, मैच की गई वैल्यू को डीस्ट्रक्चर करते हैं. इसके लिए, वे वैल्यू से डेटा निकालते हैं. डीस्ट्रक्चरिंग की मदद से, किसी ऑब्जेक्ट से वैल्यू को अनपैक किया जा सकता है, ताकि उन्हें लोकल वैरिएबल को असाइन किया जा सके या उन पर आगे की मैचिंग की जा सके.

किसी रिकॉर्ड को लोकल वैरिएबल में डिस्ट्रक्चर करना

  1. 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 (title, modified: modified) = document.metadata;   // Modify

    return Scaffold(
      appBar: AppBar(title: Text(title)),                    // Modify from here...
      body: Column(children: [Center(child: Text('Last modified $modified'))]),
    );                                                       // To here.
  }
}

रिकॉर्ड पैटर्न (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 लिखें.

  1. पिछले चरण में मिले नतीजे को देखने के लिए, हॉट रिलोड करें. दोनों कोड का व्यवहार एक जैसा है. आपने सिर्फ़ अपने कोड को ज़्यादा संक्षिप्त बना दिया है.

7. डेटा एक्सट्रैक्ट करने के लिए पैटर्न का इस्तेमाल करना

कुछ मामलों में, पैटर्न सिर्फ़ मैच और डिस्ट्रक्चर नहीं करते, बल्कि पैटर्न के मैच होने या न होने के आधार पर, यह फ़ैसला भी ले सकते हैं कि कोड क्या करता है. इन्हें गलत साबित किए जा सकने वाले पैटर्न कहा जाता है.

आपने पिछले चरण में जिस वैरिएबल डिक्लेरेशन पैटर्न का इस्तेमाल किया है वह एक irrefutable पैटर्न है: वैल्यू को पैटर्न से मेल खाना चाहिए. ऐसा न होने पर, यह एक गड़बड़ी होगी और डिस्ट्रक्चरिंग नहीं होगी. किसी वैरिएबल के एलान या असाइनमेंट के बारे में सोचें. अगर वैरिएबल एक ही टाइप के नहीं हैं, तो किसी वैरिएबल को वैल्यू असाइन नहीं की जा सकती.

दूसरी ओर, कंट्रोल फ़्लो के संदर्भों में, खंडन किए जा सकने वाले पैटर्न का इस्तेमाल किया जाता है:

  • वे उम्मीद करते हैं कि तुलना की गई कुछ वैल्यू मेल नहीं खाएंगी.
  • इनका इस्तेमाल, कंट्रोल फ़्लो पर असर डालने के लिए किया जाता है. यह इस बात पर निर्भर करता है कि वैल्यू मैच होती है या नहीं.
  • अगर ये मेल नहीं खाते हैं, तो गड़बड़ी की वजह से एक्ज़ीक्यूशन में रुकावट नहीं आती. ये सिर्फ़ अगले स्टेटमेंट पर चले जाते हैं.
  • ये ऐसे वैरिएबल को डिस्ट्रक्चर और बाइंड कर सकते हैं जो मैच होने पर सिर्फ़ इस्तेमाल किए जा सकते हैं

बिना पैटर्न वाली 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 case {                                         // Modify from here...
      '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 में मौजूद डेटा से मेल खाता हो. यह मैच, उन सभी जांचों को पूरा करता है जिन्हें आपने metadata के पहले वर्शन में, आने वाले JSON की पुष्टि करने के लिए लिखा था. यह कोड इनकी पुष्टि करता है:

  • _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() फ़ंक्शन, getBlocks() ऑब्जेक्ट की सूची दिखाता है. इसका इस्तेमाल बाद में यूज़र इंटरफ़ेस (यूआई) बनाने के लिए किया जाता है.Block जाने-पहचाने if-case स्टेटमेंट का इस्तेमाल करके, blocks मेटाडेटा की वैल्यू की पुष्टि की जाती है. साथ ही, इसे blocksJson नाम के नए List में कास्ट किया जाता है. पैटर्न के बिना, कास्ट करने के लिए आपको toList() मेथड की ज़रूरत होगी.

सूची लिटरल में Block ऑब्जेक्ट से नई सूची भरने के लिए, collection for शामिल है.

इस सेक्शन में, पैटर्न से जुड़ी ऐसी कोई सुविधा नहीं बताई गई है जिसे आपने इस कोडलैब में पहले से आज़माया न हो. अगले चरण में, आपको अपने यूज़र इंटरफ़ेस (यूआई) में सूची के आइटम रेंडर करने के लिए तैयार रहना होगा.

9. दस्तावेज़ दिखाने के लिए पैटर्न का इस्तेमाल करना

अब आपने if-case स्टेटमेंट और refutable पैटर्न का इस्तेमाल करके, 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),
    );
  }
}

build तरीके में मौजूद स्विच स्टेटमेंट, block ऑब्जेक्ट के type फ़ील्ड को चालू करता है.

  1. पहले केस स्टेटमेंट में, कॉन्स्टेंट स्ट्रिंग पैटर्न का इस्तेमाल किया गया है. अगर block.type, कॉन्स्टेंट वैल्यू h1 के बराबर है, तो पैटर्न मैच होता है.
  2. दूसरे केस स्टेटमेंट में, लॉजिकल-ओआर पैटर्न का इस्तेमाल किया गया है. इसमें दो कॉन्स्टेंट स्ट्रिंग पैटर्न को सबपैटर्न के तौर पर इस्तेमाल किया गया है. अगर block.type, सबपैटर्न p या checkbox में से किसी एक से मेल खाता है, तो पैटर्न मैच हो जाता है.
  1. आखिरी केस वाइल्डकार्ड पैटर्न, _ है. स्विच केस में वाइल्डकार्ड, बाकी सभी चीज़ों से मेल खाते हैं. ये default क्लॉज़ की तरह ही काम करते हैं. स्विच स्टेटमेंट में अब भी इनका इस्तेमाल किया जा सकता है. हालांकि, ये थोड़े ज़्यादा शब्दों में लिखे जाते हैं.

वाइल्डकार्ड पैटर्न का इस्तेमाल उन सभी जगहों पर किया जा सकता है जहां पैटर्न इस्तेमाल करने की अनुमति है. उदाहरण के लिए, वैरिएबल डिक्लेरेशन पैटर्न में: var (title, _) = document.metadata;

इस संदर्भ में, वाइल्डकार्ड किसी भी वैरिएबल को बाइंड नहीं करता है. यह दूसरे फ़ील्ड को खारिज कर देता है.

अगले सेक्शन में, Block ऑब्जेक्ट दिखाने के बाद, स्विच की अन्य सुविधाओं के बारे में जानें.

दस्तावेज़ का कॉन्टेंट दिखाना

DocumentScreen विजेट के build तरीके में getBlocks() को कॉल करके, Block ऑब्जेक्ट की सूची वाला एक लोकल वैरिएबल बनाएं.

  1. 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 विजेट बनाती है.

  1. ऐप्लिकेशन चलाएं. इसके बाद, आपको स्क्रीन पर ये ब्लॉक दिखने चाहिए:

यह ऐप्लिकेशन, JSON डेटा के &#39;ब्लॉक&#39; सेक्शन से कॉन्टेंट दिखाता है.

10. स्विच एक्सप्रेशन का इस्तेमाल करना

पैटर्न की मदद से, switch और case में कई सुविधाएं जोड़ी जा सकती हैं. इन्हें ज़्यादा जगहों पर इस्तेमाल करने के लिए, Dart में switch एक्सप्रेशन होते हैं. केस की सीरीज़, वैरिएबल असाइनमेंट या रिटर्न स्टेटमेंट को सीधे तौर पर वैल्यू दे सकती है.

स्विच स्टेटमेंट को स्विच एक्सप्रेशन में बदलें

Dart analyzer, आपके कोड में बदलाव करने के लिए सुझाव देता है.

  1. अपने कर्सर को पिछले सेक्शन के स्विच स्टेटमेंट पर ले जाएं.
  2. उपलब्ध असिस्ट देखने के लिए, लाइटबल्ब पर क्लिक करें.
  3. Convert to switch expression असिस्ट को चुनें.

VS Code में &#39;convert to switch expression&#39; सुविधा उपलब्ध है.

इस कोड का नया वर्शन ऐसा दिखता है:

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',
  };
}

यह तरीका, एक स्विच एक्सप्रेशन दिखाता है. यह difference वैल्यू पर स्विच करता है, जो एक Duration ऑब्जेक्ट है. यह 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 एक्सप्रेशन में किया जा सकता है.
  • ये सिर्फ़ मिलान होने के बाद किसी पैटर्न में शर्त जोड़ते हैं.
  • अगर गार्ड क्लॉज़ की वैल्यू 'गलत' होती है, तो पूरे पैटर्न को खारिज कर दिया जाता है. इसके बाद, अगले केस को लागू किया जाता है.

तारीख को नए फ़ॉर्मैट में बदलकर यूज़र इंटरफ़ेस (यूआई) में जोड़ना

  1. आखिर में, 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]);
              },
            ),
          ),
        ],
      ),
    );
  }
}
  1. अपने ऐप्लिकेशन में बदलाव देखने के लिए, हॉट रिलोड करें:

यह ऐप्लिकेशन, formatDate() फ़ंक्शन का इस्तेमाल करके &#39;बदलाव किए जाने की आखिरी तारीख: दो हफ़्ते पहले&#39; स्ट्रिंग दिखाता है.

12. क्लास को पूरी तरह से स्विच करने के लिए सील करना

ध्यान दें कि आपने आखिरी स्विच के आखिर में वाइल्डकार्ड या डिफ़ॉल्ट केस का इस्तेमाल नहीं किया है. हालांकि, यह हमेशा अच्छा होता है कि उन वैल्यू के लिए भी केस शामिल किए जाएं जो फ़ॉल थ्रू हो सकती हैं. हालांकि, इस तरह के सामान्य उदाहरण में ऐसा करना ज़रूरी नहीं है, क्योंकि आपको पता है कि आपने जो केस तय किए हैं वे सभी संभावित वैल्यू inDays के लिए हैं.

जब स्विच के हर केस को हैंडल किया जाता है, तो उसे एग्ज़ॉस्टिव स्विच कहा जाता है. उदाहरण के लिए, अगर bool टाइप में true और false के मामले शामिल हैं, तो इसे चालू करने में ज़्यादा समय लगता है. अगर हर इनम वैल्यू के लिए केस मौजूद हैं, तो enum टाइप को चालू करने पर, सभी केस की जांच की जाती है. ऐसा इसलिए, क्योंकि इनम, कॉन्स्टेंट वैल्यू की तय संख्या को दिखाता है.

Dart 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);
}

इनमें से हर क्लास, ओरिजनल 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 कीवर्ड एक क्लास मॉडिफ़ायर है. इसका मतलब है कि इस क्लास को सिर्फ़ एक ही लाइब्रेरी में एक्सटेंड या लागू किया जा सकता है. विश्लेषक को इस क्लास के सबटाइप के बारे में पता होता है. इसलिए, अगर कोई स्विच इनमें से किसी एक को कवर नहीं करता है और पूरी तरह से काम नहीं करता है, तो वह गड़बड़ी की सूचना देता है.

विजेट दिखाने के लिए, स्विच एक्सप्रेशन का इस्तेमाल करना

  1. 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 के पहले वर्शन में, आपने Block ऑब्जेक्ट के किसी फ़ील्ड को चालू किया था, ताकि TextStyle को वापस लाया जा सके. अब, Block ऑब्जेक्ट के इंस्टेंस को स्विच किया जाता है और उसकी सबक्लास को दिखाने वाले ऑब्जेक्ट पैटर्न से मैच किया जाता है. इस प्रोसेस में, ऑब्जेक्ट की प्रॉपर्टी निकाली जाती हैं.

Dart analyzer यह जांच कर सकता है कि स्विच एक्सप्रेशन में हर सबक्लास को हैंडल किया गया है या नहीं. ऐसा इसलिए, क्योंकि आपने Block सील की गई क्लास बनाई है.

यह भी ध्यान दें कि यहां स्विच एक्सप्रेशन का इस्तेमाल करने से, नतीजे को सीधे child एलिमेंट में पास किया जा सकता है. पहले, इसके लिए अलग से रिटर्न स्टेटमेंट की ज़रूरत होती थी.

  1. पहली बार रेंडर किए गए चेकबॉक्स JSON डेटा को देखने के लिए, हॉट रिलोड करें:

&#39;Learn Dart 3&#39; चेकबॉक्स दिखाने वाला ऐप्लिकेशन

13. बधाई हो

आपने पैटर्न, रिकॉर्ड, बेहतर स्विच और केस, और सील की गई क्लास का एक्सपेरिमेंट कर लिया है. आपने काफ़ी जानकारी दी है, लेकिन इन सुविधाओं के बारे में सिर्फ़ बुनियादी जानकारी दी है. पैटर्न के बारे में ज़्यादा जानने के लिए, सुविधा की खास बातें देखें.

अलग-अलग तरह के पैटर्न, अलग-अलग कॉन्टेक्स्ट में उनके दिखने की संभावना, और सबपैटर्न की संभावित नेस्टिंग की वजह से, व्यवहार में बदलाव की संभावनाएं अनंत होती हैं. हालांकि, इन्हें आसानी से देखा जा सकता है.

पैटर्न का इस्तेमाल करके, Flutter में कॉन्टेंट को कई तरह से दिखाया जा सकता है. पैटर्न का इस्तेमाल करके, डेटा को सुरक्षित तरीके से निकाला जा सकता है. इससे, कुछ ही लाइनों के कोड में अपना यूज़र इंटरफ़ेस (यूआई) बनाया जा सकता है.

आगे क्या करना है?

  • Dart के दस्तावेज़ के भाषा सेक्शन में, पैटर्न, रिकॉर्ड, बेहतर स्विच और केस, और क्लास मॉडिफ़ायर के बारे में दस्तावेज़ देखें.

रेफ़रंस दस्तावेज़

पूरा सैंपल कोड और उसे इस्तेमाल करने का तरीका जानने के लिए, flutter/codelabs रिपॉज़िटरी देखें.

हर नई सुविधा के बारे में ज़्यादा जानकारी पाने के लिए, डिज़ाइन से जुड़े ओरिजनल दस्तावेज़ देखें: