आपका पहला Flutter ऐप्लिकेशन

1. परिचय

Flutter, Google का यूज़र इंटरफ़ेस (यूआई) टूलकिट है. इसकी मदद से, मोबाइल, वेब, और डेस्कटॉप के लिए एक ही कोडबेस से ऐप्लिकेशन बनाए जा सकते हैं. इस कोडलैब में, आपको यह Flutter ऐप्लिकेशन बनाना होगा:

यह ऐप्लिकेशन, सुनने में अच्छे लगने वाले नाम जनरेट करता है. जैसे, "newstay", "lightstream", "mainbrake" या "graypine". उपयोगकर्ता, अगले नाम के बारे में पूछ सकता है, मौजूदा नाम को पसंदीदा के तौर पर मार्क कर सकता है, और पसंदीदा नामों की सूची को किसी दूसरे पेज पर देख सकता है. ऐप्लिकेशन, अलग-अलग स्क्रीन साइज़ के हिसाब से काम करता है.

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

  • Flutter के काम करने के तरीके के बारे में बुनियादी जानकारी
  • Flutter में लेआउट बनाना
  • उपयोगकर्ता के इंटरैक्शन (जैसे, बटन दबाना) को ऐप्लिकेशन के व्यवहार से कनेक्ट करना
  • अपने Flutter कोड को व्यवस्थित रखना
  • अपने ऐप्लिकेशन को अलग-अलग स्क्रीन के हिसाब से रिस्पॉन्सिव बनाना
  • अपने ऐप्लिकेशन का रंग-रूप और स्टाइल एक जैसा बनाए रखना

हम बुनियादी स्ट्रक्चर से शुरुआत करेंगे, ताकि आप सीधे दिलचस्प हिस्सों पर जा सकें.

e9c6b402cd8003fd.png

यहां फ़िलिप, आपको पूरे कोडलैब के बारे में बता रहे हैं!

लैब शुरू करने के लिए, 'आगे बढ़ें' पर क्लिक करें.

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

संपादक

इस कोडलैब को जितना हो सके उतना आसान बनाने के लिए, हम यह मानकर चल रहे हैं कि आप Visual Studio Code (VS Code) का इस्तेमाल डेवलपमेंट एनवायरमेंट के तौर पर करेंगे. यह सुविधा मुफ़्त है और सभी मुख्य प्लैटफ़ॉर्म पर काम करती है.

ज़रूरी नहीं कि आप Android Studio का ही इस्तेमाल करें. आप अपनी पसंद के किसी भी एडिटर का इस्तेमाल कर सकते हैं. जैसे, Android Studio, IntelliJ IDEs, Emacs, Vim या Notepad++. ये सभी एडिटर, Flutter के साथ काम करते हैं.

हमारा सुझाव है कि इस कोडलैब के लिए VS Code का इस्तेमाल करें. ऐसा इसलिए, क्योंकि निर्देश डिफ़ॉल्ट रूप से VS Code के खास शॉर्टकट के लिए होते हैं. "X करने के लिए, अपने एडिटर में ज़रूरी कार्रवाई करें" जैसे निर्देश देने के बजाय, "यहां क्लिक करें" या "यह बटन दबाएं" जैसे निर्देश देना ज़्यादा आसान होता है.

228c71510a8e868.png

कोई डेवलपमेंट टारगेट चुनना

Flutter एक मल्टी-प्लैटफ़ॉर्म टूलकिट है. आपका ऐप्लिकेशन इनमें से किसी भी ऑपरेटिंग सिस्टम पर काम कर सकता है:

  • iOS
  • Android
  • Windows
  • macOS
  • Linux
  • वेब

हालांकि, आम तौर पर एक ही ऑपरेटिंग सिस्टम चुना जाता है, जिसके लिए मुख्य तौर पर डेवलपमेंट किया जाता है. इसे "डेवलपमेंट टारगेट" कहा जाता है. यह वह ऑपरेटिंग सिस्टम होता है जिस पर आपका ऐप्लिकेशन डेवलपमेंट के दौरान चलता है.

16695777c07f18e5.png

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

वेब को डेवलपमेंट टारगेट के तौर पर चुनने का विकल्प आकर्षक लग सकता है. इस विकल्प को चुनने का नुकसान यह है कि आपको Flutter की सबसे काम की डेवलपमेंट सुविधाओं में से एक, स्टेटफ़ुल हॉट रिलोड की सुविधा नहीं मिलती. Flutter, वेब ऐप्लिकेशन को हॉट-रीलोड नहीं कर सकता.

अभी अपनी पसंद का विकल्प चुनें. याद रखें: आपके पास बाद में, अपने ऐप्लिकेशन को अन्य ऑपरेटिंग सिस्टम पर चलाने का विकल्प हमेशा होता है. हालांकि, डेवलपमेंट का टारगेट तय होने से, अगला चरण आसानी से पूरा किया जा सकता है.

Flutter इंस्टॉल करना

Flutter SDK टूल को इंस्टॉल करने के बारे में सबसे नए निर्देश, हमेशा docs.flutter.dev पर उपलब्ध होते हैं.

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

  1. Flutter SDK
  2. Flutter प्लगिन के साथ Visual Studio Code
  3. आपके चुने गए डेवलपमेंट टारगेट के लिए ज़रूरी सॉफ़्टवेयर. उदाहरण के लिए: Windows को टारगेट करने के लिए Visual Studio या macOS को टारगेट करने के लिए Xcode

अगले सेक्शन में, अपना पहला Flutter प्रोजेक्ट बनाया जा सकता है.

अगर आपको अब तक समस्याएं आ रही हैं, तो समस्या हल करने के लिए, आपको StackOverflow पर मौजूद इन सवालों और जवाबों से मदद मिल सकती है.

अक्सर पूछे जाने वाले सवाल

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

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

Visual Studio Code लॉन्च करें और कमांड पैलेट खोलें (F1 या Ctrl+Shift+P या Shift+Cmd+P की मदद से). "flutter new" टाइप करना शुरू करें. Flutter: New Project कमांड चुनें.

इसके बाद, ऐप्लिकेशन चुनें. इसके बाद, वह फ़ोल्डर चुनें जिसमें आपको अपना प्रोजेक्ट बनाना है. यह आपकी होम डायरेक्ट्री या C:\src\ जैसा कुछ हो सकता है.

आखिर में, अपने प्रोजेक्ट का नाम डालें. जैसे, namer_app या my_awesome_namer.

260a7d97f9678005.png

अब Flutter, आपका प्रोजेक्ट फ़ोल्डर बनाता है और VS Code उसे खोलता है.

अब ऐप्लिकेशन के बेसिक स्ट्रक्चर के साथ, तीन फ़ाइलों के कॉन्टेंट को बदला जाएगा.

ऐप्लिकेशन को कॉपी करके चिपकाना

VS Code के बाएं पैनल में, पक्का करें कि Explorer चुना गया हो. इसके बाद, pubspec.yaml फ़ाइल खोलें.

e2a5bab0be07f4f7.png

इस फ़ाइल के कॉन्टेंट की जगह यह कॉन्टेंट डालें:

pubspec.yaml

name: namer_app
description: "A new Flutter project."
publish_to: "none"
version: 0.1.0

environment:
  sdk: ^3.9.0

dependencies:
  flutter:
    sdk: flutter
  english_words: ^4.0.0
  provider: ^6.1.5

dev_dependencies:
  flutter_test:
    sdk: flutter
  flutter_lints: ^6.0.0

flutter:
  uses-material-design: true

pubspec.yaml फ़ाइल में आपके ऐप्लिकेशन के बारे में बुनियादी जानकारी होती है. जैसे, उसका मौजूदा वर्शन, उसकी डिपेंडेंसी, और वे ऐसेट जिनके साथ उसे शिप किया जाएगा.

इसके बाद, प्रोजेक्ट में मौजूद दूसरी कॉन्फ़िगरेशन फ़ाइल analysis_options.yaml खोलें.

a781f218093be8e0.png

इसके कॉन्टेंट को इससे बदलें:

analysis_options.yaml

include: package:flutter_lints/flutter.yaml

linter:
  rules:
    avoid_print: false
    prefer_const_constructors_in_immutables: false
    prefer_const_constructors: false
    prefer_const_literals_to_create_immutables: false
    prefer_final_fields: false
    unnecessary_breaks: true
    use_key_in_widget_constructors: false

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

आखिर में, lib/ डायरेक्ट्री में मौजूद main.dart फ़ाइल खोलें.

e54c671c9bb4d23d.png

इस फ़ाइल के कॉन्टेंट की जगह यह कॉन्टेंट डालें:

lib/main.dart

import 'package:english_words/english_words.dart';
import 'package:flutter/material.dart';
import 'package:provider/provider.dart';

void main() {
  runApp(MyApp());
}

class MyApp extends StatelessWidget {
  const MyApp({super.key});

  @override
  Widget build(BuildContext context) {
    return ChangeNotifierProvider(
      create: (context) => MyAppState(),
      child: MaterialApp(
        title: 'Namer App',
        theme: ThemeData(
          colorScheme: ColorScheme.fromSeed(seedColor: Colors.deepOrange),
        ),
        home: MyHomePage(),
      ),
    );
  }
}

class MyAppState extends ChangeNotifier {
  var current = WordPair.random();
}

class MyHomePage extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    var appState = context.watch<MyAppState>();

    return Scaffold(
      body: Column(
        children: [Text('A random idea:'), Text(appState.current.asLowerCase)],
      ),
    );
  }
}

अब तक, ऐप्लिकेशन में कोड की ये 50 लाइनें हैं.

अगले सेक्शन में, ऐप्लिकेशन को डीबग मोड में चलाएं और डेवलपमेंट शुरू करें.

4. बटन जोड़ना

इस चरण में, अगला बटन जोड़ा जाता है, ताकि शब्दों की नई जोड़ी जनरेट की जा सके.

ऐप्लिकेशन लॉन्च करना

सबसे पहले, lib/main.dart खोलें और पक्का करें कि आपने टारगेट डिवाइस चुना हो. VS Code के सबसे नीचे दाएं कोने में, आपको एक बटन दिखेगा. इस बटन पर, मौजूदा टारगेट डिवाइस दिखता है. इसे बदलने के लिए क्लिक करें.

lib/main.dart खुला होने पर, VS Code की विंडो के सबसे ऊपर दाएं कोने में मौजूद "चलाएं" b0a5d0200af5985d.png बटन ढूंढें और उस पर क्लिक करें.

एक मिनट बाद, आपका ऐप्लिकेशन डीबग मोड में लॉन्च हो जाएगा. फ़िलहाल, यह बहुत ज़्यादा नहीं है:

f96e7dfb0937d7f4.png

पहली बार हॉट रीलोड

lib/main.dart के सबसे नीचे, पहले Text ऑब्जेक्ट में स्ट्रिंग जोड़ें. इसके बाद, फ़ाइल को Ctrl+S या Cmd+S के साथ सेव करें. उदाहरण के लिए:

lib/main.dart

// ...

    return Scaffold(
      body: Column(
        children: [
          Text('A random AWESOME idea:'),  // ← Example change.
          Text(appState.current.asLowerCase),
        ],
      ),
    );

// ...

ध्यान दें कि ऐप्लिकेशन तुरंत बदल जाता है, लेकिन रैंडम शब्द वही रहता है. यह Flutter की मशहूर स्टेटफ़ुल हॉट रिलोड सुविधा है. सोर्स फ़ाइल में बदलाव सेव करने पर, हॉट रिलोड ट्रिगर होता है.

अक्सर पूछे जाने वाले सवाल

बटन जोड़ना

इसके बाद, Column के सबसे नीचे, दूसरे Text इंस्टेंस के ठीक नीचे एक बटन जोड़ें.

lib/main.dart

// ...

    return Scaffold(
      body: Column(
        children: [
          Text('A random AWESOME idea:'),
          Text(appState.current.asLowerCase),

          // ↓ Add this.
          ElevatedButton(
            onPressed: () {
              print('button pressed!');
            },
            child: Text('Next'),
          ),

        ],
      ),
    );

// ...

बदलाव सेव करने पर, ऐप्लिकेशन फिर से अपडेट होता है: एक बटन दिखता है. इस पर क्लिक करने पर, VS Code में मौजूद Debug Console में button pressed! मैसेज दिखता है.

पांच मिनट में Flutter का क्रैश कोर्स

डीबग कंसोल को देखना जितना मज़ेदार है, उतना ही ज़रूरी है कि बटन कुछ ज़्यादा काम का हो. हालांकि, इससे पहले lib/main.dart में दिए गए कोड को ध्यान से देखें, ताकि आपको यह समझ आ सके कि यह कैसे काम करता है.

lib/main.dart

// ...

void main() {
  runApp(MyApp());
}

// ...

फ़ाइल में सबसे ऊपर, आपको main() फ़ंक्शन दिखेगा. मौजूदा फ़ॉर्म में, यह सिर्फ़ Flutter को MyApp में तय किए गए ऐप्लिकेशन को चलाने के लिए कहता है.

lib/main.dart

// ...

class MyApp extends StatelessWidget {
  const MyApp({super.key});

  @override
  Widget build(BuildContext context) {
    return ChangeNotifierProvider(
      create: (context) => MyAppState(),
      child: MaterialApp(
        title: 'Namer App',
        theme: ThemeData(
          colorScheme: ColorScheme.fromSeed(seedColor: Colors.deepOrange),
        ),
        home: MyHomePage(),
      ),
    );
  }
}

// ...

MyApp क्लास एक्सटेंड होती है StatelessWidget. विजेट ऐसे एलिमेंट होते हैं जिनकी मदद से हर Flutter ऐप्लिकेशन बनाया जाता है. जैसा कि आप देख सकते हैं, ऐप्लिकेशन भी एक विजेट है.

MyApp में मौजूद कोड, पूरे ऐप्लिकेशन को सेट अप करता है. यह ऐप्लिकेशन-वाइड स्टेट बनाता है (इसके बारे में बाद में ज़्यादा जानकारी दी गई है), ऐप्लिकेशन का नाम रखता है, विज़ुअल थीम तय करता है, और "होम" विजेट सेट करता है. यह आपके ऐप्लिकेशन का शुरुआती पॉइंट होता है.

lib/main.dart

// ...

class MyAppState extends ChangeNotifier {
  var current = WordPair.random();
}

// ...

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

  • MyAppState से पता चलता है कि ऐप्लिकेशन को काम करने के लिए किस तरह के डेटा की ज़रूरत है. फ़िलहाल, इसमें सिर्फ़ एक वैरिएबल है, जिसमें मौजूदा रैंडम शब्द का जोड़ा है. इसमें बाद में और जानकारी जोड़ी जा सकती है.
  • स्टेट क्लास, ChangeNotifier को बढ़ाती है. इसका मतलब है कि यह अपने बदलावों के बारे में दूसरों को सूचना दे सकती है. उदाहरण के लिए, अगर मौजूदा शब्द युग्म बदलता है, तो ऐप्लिकेशन में मौजूद कुछ विजेट को इसकी जानकारी होनी चाहिए.
  • ChangeNotifierProvider का इस्तेमाल करके, पूरे ऐप्लिकेशन के लिए स्टेट बनाई जाती है और उसे उपलब्ध कराया जाता है (ऊपर MyApp में कोड देखें). इससे ऐप्लिकेशन में मौजूद कोई भी विजेट, स्टेट को ऐक्सेस कर सकता है.

d9b6ecac5494a6ff.png

lib/main.dart

// ...

class MyHomePage extends StatelessWidget {
  @override
  Widget build(BuildContext context) {           //  1
    var appState = context.watch<MyAppState>();  //  2

    return Scaffold(                             //  3
      body: Column(                              //  4
        children: [
          Text('A random AWESOME idea:'),        //  5
          Text(appState.current.asLowerCase),    //  6
          ElevatedButton(
            onPressed: () {
              print('button pressed!');
            },
            child: Text('Next'),
          ),
        ],                                       //  7
      ),
    );
  }
}

// ...

आखिर में, MyHomePage है. यह वह विजेट है जिसमें आपने पहले ही बदलाव कर दिया है. यहां दी गई हर नंबर वाली लाइन, ऊपर दिए गए कोड में मौजूद लाइन-नंबर वाले कमेंट से मैप होती है:

  1. हर विजेट, build() तरीके को तय करता है. विजेट की स्थितियों में बदलाव होने पर, यह तरीका अपने-आप कॉल होता है, ताकि विजेट हमेशा अप-टू-डेट रहे.
  2. MyHomePage, watch तरीके का इस्तेमाल करके, ऐप्लिकेशन की मौजूदा स्थिति में हुए बदलावों को ट्रैक करता है.
  3. हर build तरीके को एक विजेट या (ज़्यादातर) विजेट का नेस्ट किया गया ट्री दिखाना चाहिए. इस मामले में, टॉप-लेवल विजेट Scaffold है. इस कोडलैब में, आपको Scaffold के साथ काम नहीं करना है. हालांकि, यह एक काम का विजेट है और यह असल दुनिया के ज़्यादातर Flutter ऐप्लिकेशन में मौजूद होता है.
  4. Column, Flutter में सबसे बुनियादी लेआउट विजेट में से एक है. यह किसी भी संख्या में बच्चों को लेता है और उन्हें ऊपर से नीचे तक एक कॉलम में रखता है. डिफ़ॉल्ट रूप से, कॉलम अपने चाइल्ड विजेट को सबसे ऊपर दिखाता है. जल्द ही, इसे इस तरह से बदलें कि कॉलम बीच में आ जाए.
  5. आपने पहले चरण में इस Text विजेट में बदलाव किया था.
  6. यह दूसरा Text विजेट, appState लेता है और उस क्लास के सिर्फ़ एक सदस्य, current (जो कि WordPair है) को ऐक्सेस करता है. WordPair कई मददगार गेटर उपलब्ध कराता है, जैसे कि asPascalCase या asSnakeCase. यहां हमने asLowerCase का इस्तेमाल किया है. हालांकि, अगर आपको कोई दूसरा विकल्प पसंद है, तो अब इसे बदला जा सकता है.
  7. ध्यान दें कि Flutter कोड में, ट्रेलिंग कॉमा का ज़्यादा इस्तेमाल किया जाता है. इस कॉमा की ज़रूरत नहीं है, क्योंकि children इस Column पैरामीटर लिस्ट का आखिरी (और सिर्फ़) सदस्य है. हालांकि, आखिर में कॉमा का इस्तेमाल करना आम तौर पर एक अच्छा तरीका है: इससे ज़्यादा सदस्यों को आसानी से जोड़ा जा सकता है. साथ ही, यह Dart के ऑटो-फ़ॉर्मेटर के लिए एक हिंट के तौर पर भी काम करता है, ताकि वह वहां एक नई लाइन डाल सके. ज़्यादा जानकारी के लिए, कोड फ़ॉर्मैट करना लेख पढ़ें.

इसके बाद, बटन को स्थिति से कनेक्ट करें.

आपका पहला व्यवहार

स्क्रोल करके MyAppState पर जाएं और getNext का तरीका जोड़ें.

lib/main.dart

// ...

class MyAppState extends ChangeNotifier {
  var current = WordPair.random();

  //  Add this.
  void getNext() {
    current = WordPair.random();
    notifyListeners();
  }
}

// ...

नए getNext() तरीके से, current को नए रैंडम WordPair के साथ फिर से असाइन किया जाता है. यह notifyListeners() को भी कॉल करता है. यह ChangeNotifier) का एक तरीका है, जिससे यह पक्का किया जाता है कि MyAppState देखने वाले हर व्यक्ति को सूचना मिले.

अब सिर्फ़ बटन के कॉलबैक से getNext वाले तरीके को कॉल करना बाकी है.

lib/main.dart

// ...

    ElevatedButton(
      onPressed: () {
        appState.getNext();  // ← This instead of print().
      },
      child: Text('Next'),
    ),

// ...

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

अगले सेक्शन में, यूज़र इंटरफ़ेस को और बेहतर बनाया जाएगा.

5. ऐप्लिकेशन को ज़्यादा आकर्षक बनाना

फ़िलहाल, ऐप्लिकेशन इस तरह दिखता है.

3dd8a9d8653bdc56.png

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

इस सेक्शन में, ऐप्लिकेशन के डिज़ाइन पर काम करके इन समस्याओं को हल किया जाता है. इस सेक्शन का मुख्य मकसद कुछ ऐसा है:

2bbee054d81a3127.png

विजेट को एक्सट्रैक्ट करना

मौजूदा शब्द के जोड़े को दिखाने वाली लाइन अब ऐसी दिखती है: Text(appState.current.asLowerCase). इसे ज़्यादा मुश्किल बनाने के लिए, इस लाइन को अलग विजेट में एक्सट्रैक्ट करना बेहतर होता है. Flutter में, यूज़र इंटरफ़ेस (यूआई) के अलग-अलग लॉजिकल हिस्सों के लिए अलग-अलग विजेट का इस्तेमाल करना, जटिलता को मैनेज करने का एक अहम तरीका है.

Flutter, विजेट निकालने के लिए रिफ़ैक्टरिंग हेल्पर उपलब्ध कराता है. हालांकि, इसका इस्तेमाल करने से पहले पक्का करें कि निकाली जा रही लाइन सिर्फ़ ज़रूरी चीज़ों को ऐक्सेस करती हो. फ़िलहाल, लाइन appState को ऐक्सेस करती है. हालांकि, इसे सिर्फ़ यह जानने की ज़रूरत है कि मौजूदा शब्द का जोड़ा क्या है.

इस वजह से, MyHomePage विजेट को इस तरह से फिर से लिखें:

lib/main.dart

// ...

class MyHomePage extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    var appState = context.watch<MyAppState>();
    var pair = appState.current;                 //  Add this.

    return Scaffold(
      body: Column(
        children: [
          Text('A random AWESOME idea:'),
          Text(pair.asLowerCase),                //  Change to this.
          ElevatedButton(
            onPressed: () {
              appState.getNext();
            },
            child: Text('Next'),
          ),
        ],
      ),
    );
  }
}

// ...

बढ़िया। Text विजेट अब पूरे appState को नहीं दिखाता.

अब, Refactor मेन्यू खोलें. VS Code में, ऐसा इनमें से किसी एक तरीके से किया जा सकता है:

  1. आपको जिस कोड को फिर से फ़ैक्टर करना है उस पर राइट क्लिक करें (इस मामले में Text) और ड्रॉप-डाउन मेन्यू से फिर से फ़ैक्टर करें... चुनें,

या

  1. अपने कर्सर को उस कोड पर ले जाएं जिसे आपको रीफ़ैक्टर करना है (इस मामले में Text), और Ctrl+. (Win/Linux) या Cmd+. (Mac) दबाएं.

Refactor मेन्यू में जाकर, Extract Widget को चुनें. कोई नाम असाइन करें, जैसे कि BigCard. इसके बाद, Enter पर क्लिक करें.

इससे मौजूदा फ़ाइल के आखिर में, BigCard नाम की एक नई क्लास अपने-आप बन जाती है. क्लास कुछ इस तरह दिखती है:

lib/main.dart

// ...

class BigCard extends StatelessWidget {
  const BigCard({super.key, required this.pair});

  final WordPair pair;

  @override
  Widget build(BuildContext context) {
    return Text(pair.asLowerCase);
  }
}

// ...

ध्यान दें कि रीफ़ैक्टरिंग के दौरान भी ऐप्लिकेशन काम करता रहता है.

कार्ड जोड़ें

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

BigCard क्लास और उसमें मौजूद build() तरीका ढूंढें. पहले की तरह, Text विजेट पर Refactor मेन्यू खोलें. हालांकि, इस बार आपको विजेट को नहीं निकालना है.

इसके बजाय, पैडिंग के साथ रैप करें को चुनें. इससे Text विजेट के चारों ओर एक नया पैरंट विजेट बन जाता है, जिसे Padding कहा जाता है. सेव करने के बाद, आपको दिखेगा कि रैंडम शब्द के बीच में पहले से ज़्यादा जगह है.

पैडिंग को 8.0 की डिफ़ॉल्ट वैल्यू से बढ़ाएं. उदाहरण के लिए, ज़्यादा पैडिंग के लिए 20 का इस्तेमाल करें.

इसके बाद, एक लेवल ऊपर जाएं. अपने कर्सर को Padding विजेट पर रखें. इसके बाद, Refactor मेन्यू को ऊपर की ओर खींचें और Wrap with widget... को चुनें.

इससे पैरंट विजेट के बारे में जानकारी दी जा सकती है. "कार्ड" टाइप करें और Enter दबाएं.

यह Padding विजेट और इसलिए Text को Card विजेट के साथ रैप करता है.

lib/main.dart

// ...

class BigCard extends StatelessWidget {
  const BigCard({super.key, required this.pair});

  final WordPair pair;

  @override
  Widget build(BuildContext context) {
    return Card(
      child: Padding(
        padding: const EdgeInsets.all(20),
        child: Text(pair.asLowerCase),
      ),
    );
  }
}

// ...

अब ऐप्लिकेशन कुछ ऐसा दिखेगा:

6031adbc0a11e16b.png

थीम और स्टाइल

कार्ड को ज़्यादा आकर्षक बनाने के लिए, उसे गहरे रंग से पेंट करें. हमेशा एक जैसी कलर स्कीम का इस्तेमाल करना बेहतर होता है. इसलिए, रंग चुनने के लिए ऐप्लिकेशन के Theme का इस्तेमाल करें.

BigCard के build() तरीके में ये बदलाव करें.

lib/main.dart

// ...

  @override
  Widget build(BuildContext context) {
    final theme = Theme.of(context);       //  Add this.

    return Card(
      color: theme.colorScheme.primary,    //  And also this.
      child: Padding(
        padding: const EdgeInsets.all(20),
        child: Text(pair.asLowerCase),
      ),
    );
  }

// ...

इन दो नई लाइनों से कई काम किए जा सकते हैं:

  • सबसे पहले, कोड Theme.of(context) की मदद से ऐप्लिकेशन की मौजूदा थीम का अनुरोध करता है.
  • इसके बाद, कोड कार्ड के रंग को थीम की colorScheme प्रॉपर्टी के रंग के जैसा सेट करता है. कलर स्कीम में कई रंग शामिल हैं. इनमें से primary सबसे प्रमुख रंग है. यह ऐप्लिकेशन का मुख्य रंग है.

अब कार्ड को ऐप्लिकेशन के मुख्य रंग से पेंट किया गया है:

a136f7682c204ea1.png

इस रंग को बदला जा सकता है. साथ ही, पूरे ऐप्लिकेशन की कलर स्कीम को भी बदला जा सकता है. इसके लिए, MyApp तक ऊपर की ओर स्क्रोल करें. इसके बाद, ColorScheme के लिए सीड कलर बदलें.

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

कार्ड के नीचे मौजूद एलिवेटेड बटन का रंग भी बदल जाता है. ऐप्लिकेशन के लिए Theme का इस्तेमाल करने से, वैल्यू को हार्ड-कोड करने के मुकाबले ज़्यादा फ़ायदा मिलता है.

TextTheme

कार्ड में अब भी समस्या है: टेक्स्ट बहुत छोटा है और उसका रंग ऐसा है कि उसे पढ़ना मुश्किल है. इस समस्या को ठीक करने के लिए, BigCard के build() तरीके में ये बदलाव करें.

lib/main.dart

// ...

  @override
  Widget build(BuildContext context) {
    final theme = Theme.of(context);
    //  Add this.
    final style = theme.textTheme.displayMedium!.copyWith(
      color: theme.colorScheme.onPrimary,
    );

    return Card(
      color: theme.colorScheme.primary,
      child: Padding(
        padding: const EdgeInsets.all(20),
        //  Change this line.
        child: Text(pair.asLowerCase, style: style),
      ),
    );
  }

// ...

इस बदलाव की वजह:

  • theme.textTheme, का इस्तेमाल करके, ऐप्लिकेशन की फ़ॉन्ट थीम को ऐक्सेस किया जा सकता है. इस क्लास में bodyMedium (मीडियम साइज़ के स्टैंडर्ड टेक्स्ट के लिए), caption (इमेज के कैप्शन के लिए) या headlineLarge (बड़े हेडलाइन के लिए) जैसे सदस्य शामिल हैं.
  • displayMedium प्रॉपर्टी, डिसप्ले टेक्स्ट के लिए एक बड़ी स्टाइल है. यहां display शब्द का इस्तेमाल टाइपोग्राफ़िक सेंस में किया गया है. जैसे, display typeface. displayMedium के दस्तावेज़ में बताया गया है कि "डिसप्ले स्टाइल, छोटे और ज़रूरी टेक्स्ट के लिए रिज़र्व होती हैं"—यह हमारे इस्तेमाल के उदाहरण से मेल खाता है.
  • थीम की displayMedium प्रॉपर्टी, सिद्धांत के तौर पर null हो सकती है. Dart, प्रोग्रामिंग की एक ऐसी भाषा है जिसमें नल वैल्यू को सुरक्षित तरीके से हैंडल किया जाता है. इसलिए, यह आपको उन ऑब्जेक्ट के तरीकों को कॉल करने की अनुमति नहीं देगा जो संभावित रूप से null हैं. हालाँकि, इस मामले में ! ऑपरेटर ("बैंग ऑपरेटर") का इस्तेमाल करके, Dart को यह बताया जा सकता है कि आपको पता है कि क्या करना है. (इस मामले में, displayMedium की वैल्यू null नहीं है. इसकी वजह इस कोडलैब के दायरे से बाहर है.)
  • displayMedium पर copyWith() को कॉल करने पर, टेक्स्ट स्टाइल की कॉपी मिलती है. इसमें आपके तय किए गए बदलाव शामिल होते हैं. इस मामले में, सिर्फ़ टेक्स्ट का रंग बदला जा रहा है.
  • नया रंग पाने के लिए, ऐप्लिकेशन की थीम को फिर से ऐक्सेस करें. कलर स्कीम की onPrimary प्रॉपर्टी, एक ऐसा रंग तय करती है जो ऐप्लिकेशन के मुख्य रंग के ऊपर इस्तेमाल करने के लिए सही हो.

अब ऐप्लिकेशन कुछ ऐसा दिखना चाहिए:

2405e9342d28c193.png

अगर आपको लगता है, तो कार्ड में और बदलाव करें. यहां कुछ आइडिया दिए गए हैं:

  • copyWith() की मदद से, टेक्स्ट के स्टाइल में रंग के अलावा और भी कई बदलाव किए जा सकते हैं. बदली जा सकने वाली प्रॉपर्टी की पूरी सूची पाने के लिए, अपने कर्सर को copyWith() के ब्रैकेट में कहीं भी रखें और Ctrl+Shift+Space (Win/Linux) या Cmd+Shift+Space (Mac) दबाएं.
  • इसी तरह, Card विजेट के बारे में ज़्यादा जानकारी बदली जा सकती है. उदाहरण के लिए, elevation पैरामीटर की वैल्यू बढ़ाकर, कार्ड की शैडो को बड़ा किया जा सकता है.
  • अलग-अलग रंगों का इस्तेमाल करके देखें. theme.colorScheme.primary के अलावा, .secondary, .surface, और कई अन्य ऐप्लिकेशन भी उपलब्ध हैं. इन सभी रंगों के onPrimary वर्शन उपलब्ध हैं.

सुलभता को बेहतर बनाना

Flutter, ऐप्लिकेशन को डिफ़ॉल्ट रूप से ऐक्सेस करने की सुविधा देता है. उदाहरण के लिए, हर Flutter ऐप्लिकेशन, TalkBack और VoiceOver जैसे स्क्रीन रीडर को ऐप्लिकेशन में मौजूद सभी टेक्स्ट और इंटरैक्टिव एलिमेंट सही तरीके से दिखाता है.

d1fad7944fb890ea.png

हालांकि, कभी-कभी कुछ काम करना ज़रूरी होता है. इस ऐप्लिकेशन के मामले में, स्क्रीन रीडर को जनरेट किए गए कुछ शब्दों के जोड़े का उच्चारण करने में समस्याएं आ सकती हैं. इंसानों को cheaphead में मौजूद दो शब्दों की पहचान करने में कोई समस्या नहीं होती. हालांकि, स्क्रीन रीडर इस शब्द के बीच में मौजूद ph को f के तौर पर पढ़ सकता है.

इसका एक समाधान यह है कि pair.asLowerCase को "${pair.first} ${pair.second}" से बदल दिया जाए. बाद वाला फ़ंक्शन, स्ट्रिंग इंटरपोलेशन का इस्तेमाल करके pair में मौजूद दो शब्दों से एक स्ट्रिंग (जैसे कि "cheap head") बनाता है. एक शब्द के बजाय दो अलग-अलग शब्दों का इस्तेमाल करने से, यह पक्का किया जा सकता है कि स्क्रीन रीडर उन्हें सही तरीके से पहचानें. साथ ही, इससे दृष्टिबाधित लोगों को बेहतर अनुभव मिलता है.

हालांकि, आपको pair.asLowerCase का विज़ुअल डिज़ाइन पसंद आ सकता है. टेक्स्ट विजेट के विज़ुअल कॉन्टेंट को ऐसे सिमैंटिक कॉन्टेंट से बदलने के लिए, Text की semanticsLabel प्रॉपर्टी का इस्तेमाल करें जो स्क्रीन रीडर के लिए ज़्यादा सही हो:

lib/main.dart

// ...

  @override
  Widget build(BuildContext context) {
    final theme = Theme.of(context);
    final style = theme.textTheme.displayMedium!.copyWith(
      color: theme.colorScheme.onPrimary,
    );

    return Card(
      color: theme.colorScheme.primary,
      child: Padding(
        padding: const EdgeInsets.all(20),

        //  Make the following change.
        child: Text(
          pair.asLowerCase,
          style: style,
          semanticsLabel: "${pair.first} ${pair.second}",
        ),
      ),
    );
  }

// ...

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

यूज़र इंटरफ़ेस (यूआई) को बीच में अलाइन करना

अब जब रैंडम शब्द के जोड़े को विज़ुअल फ़्लेयर के साथ दिखाया गया है, तो इसे ऐप्लिकेशन की विंडो/स्क्रीन के बीच में रखने का समय आ गया है.

सबसे पहले, यह याद रखें कि BigCard, Column का हिस्सा है. डिफ़ॉल्ट रूप से, कॉलम में बच्चों को सबसे ऊपर रखा जाता है. हालांकि, हम इस सेटिंग को बदल सकते हैं. MyHomePage के build() तरीके पर जाएं और यह बदलाव करें:

lib/main.dart

// ...

class MyHomePage extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    var appState = context.watch<MyAppState>();
    var pair = appState.current;

    return Scaffold(
      body: Column(
        mainAxisAlignment: MainAxisAlignment.center,  //  Add this.
        children: [
          Text('A random AWESOME idea:'),
          BigCard(pair: pair),
          ElevatedButton(
            onPressed: () {
              appState.getNext();
            },
            child: Text('Next'),
          ),
        ],
      ),
    );
  }
}

// ...

इससे, Column के अंदर मौजूद चाइल्ड विजेट, इसकी मुख्य (वर्टिकल) ऐक्सिस के बीच में आ जाते हैं.

b555d4c7f5000edf.png

बच्चों को कॉलम के क्रॉस ऐक्सिस के हिसाब से पहले से ही बीच में रखा गया है. दूसरे शब्दों में कहें, तो उन्हें पहले से ही हॉरिज़ॉन्टल तौर पर बीच में रखा गया है. हालांकि, Column खुद Scaffold के बीच में नहीं है. विजेट इंस्पेक्टर का इस्तेमाल करके, इसकी पुष्टि की जा सकती है.

विजेट इंस्पेक्टर, इस कोडलैब के दायरे से बाहर है. हालांकि, यहां देखा जा सकता है कि जब Column को हाइलाइट किया जाता है, तो यह ऐप्लिकेशन की पूरी चौड़ाई नहीं लेता है. यह सिर्फ़ उतना हॉरिज़ॉन्टल स्पेस लेता है जितना इसके चाइल्ड को चाहिए.

सिर्फ़ कॉलम को बीच में अलाइन किया जा सकता है. अपने कर्सर को Column पर रखें. इसके बाद, Ctrl+. या Cmd+. की मदद से Refactor मेन्यू को कॉल करें और Wrap with Center को चुनें.

अब ऐप्लिकेशन कुछ ऐसा दिखना चाहिए:

455688d93c30d154.png

अगर आपको लगता है कि इसमें कुछ और बदलाव किए जा सकते हैं, तो ऐसा करें.

  • ऊपर मौजूद Text विजेट को BigCard से हटाया जा सकता है. यह तर्क दिया जा सकता है कि जानकारी देने वाले टेक्स्ट ("एक शानदार आइडिया:") की अब ज़रूरत नहीं है, क्योंकि यूज़र इंटरफ़ेस (यूआई) इसके बिना भी समझ में आता है. इससे कोड ज़्यादा साफ़ दिखता है.
  • BigCard और ElevatedButton के बीच, SizedBox(height: 10) विजेट भी जोड़ा जा सकता है. इस तरह, दोनों विजेट के बीच थोड़ी ज़्यादा जगह होती है. SizedBox विजेट सिर्फ़ जगह लेता है और खुद से कुछ भी रेंडर नहीं करता. आम तौर पर, इसका इस्तेमाल विज़ुअल "गैप" बनाने के लिए किया जाता है.

बदलाव करने के बाद, MyHomePage में यह कोड शामिल होगा:

lib/main.dart

// ...

class MyHomePage extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    var appState = context.watch<MyAppState>();
    var pair = appState.current;

    return Scaffold(
      body: Center(
        child: Column(
          mainAxisAlignment: MainAxisAlignment.center,
          children: [
            BigCard(pair: pair),
            SizedBox(height: 10),
            ElevatedButton(
              onPressed: () {
                appState.getNext();
              },
              child: Text('Next'),
            ),
          ],
        ),
      ),
    );
  }
}

// ...

ऐप्लिकेशन ऐसा दिखता है:

3d53d2b071e2f372.png

अगले सेक्शन में, जनरेट किए गए शब्दों को पसंदीदा बनाने (या ‘पसंद करें') की सुविधा जोड़ी जाएगी.

6. सुविधा जोड़ना

यह ऐप्लिकेशन काम करता है. साथ ही, कभी-कभी शब्दों के दिलचस्प जोड़े भी उपलब्ध कराता है. हालांकि, जब भी उपयोगकर्ता अगला पर क्लिक करता है, तो शब्दों का हर जोड़ा हमेशा के लिए गायब हो जाता है. बेहतरीन सुझावों को "याद रखने" का कोई तरीका होना चाहिए. जैसे, 'पसंद करें' बटन.

e6b01a8c90df8ffa.png

कारोबारी नियम जोड़ना

MyAppState तक स्क्रोल करें और यह कोड जोड़ें:

lib/main.dart

// ...

class MyAppState extends ChangeNotifier {
  var current = WordPair.random();

  void getNext() {
    current = WordPair.random();
    notifyListeners();
  }

  //  Add the code below.
  var favorites = <WordPair>[];

  void toggleFavorite() {
    if (favorites.contains(current)) {
      favorites.remove(current);
    } else {
      favorites.add(current);
    }
    notifyListeners();
  }
}

// ...

बदलावों की जांच करें:

  • आपने MyAppState में favorites नाम की एक नई प्रॉपर्टी जोड़ी है. इस प्रॉपर्टी को खाली सूची के साथ शुरू किया गया है: [].
  • आपने यह भी बताया है कि सूची में सिर्फ़ शब्दों के जोड़े शामिल किए जा सकते हैं: <WordPair>[]. इसके लिए, जेनेरिक का इस्तेमाल किया गया है. इससे आपका ऐप्लिकेशन ज़्यादा मज़बूत बनता है. अगर आपने अपने ऐप्लिकेशन में WordPair के अलावा कोई और वैल्यू जोड़ने की कोशिश की, तो Dart उसे चलाने से भी मना कर देगा. इसके बाद, favorites सूची का इस्तेमाल किया जा सकता है. आपको पता होगा कि इसमें कभी भी कोई अनचाही चीज़ (जैसे कि null) नहीं छिपी होगी.
  • आपने toggleFavorite() नाम का एक नया तरीका भी जोड़ा है. इससे, शब्दों के मौजूदा जोड़े को पसंदीदा शब्दों की सूची से हटाया जा सकता है. ऐसा तब होता है, जब वह जोड़ा पहले से ही सूची में मौजूद हो. इसके अलावा, अगर वह जोड़ा सूची में मौजूद नहीं है, तो उसे जोड़ा जा सकता है. दोनों ही मामलों में, कोड इसके बाद notifyListeners(); को कॉल करता है.

बटन जोड़ना

"बिज़नेस लॉजिक" को अलग करने के बाद, अब यूज़र इंटरफ़ेस पर फिर से काम करने का समय है. ‘अगला' बटन के बाईं ओर ‘पसंद करें' बटन रखने के लिए, Row की ज़रूरत होती है. Row विजेट, Column के हॉरिज़ॉन्टल वर्शन जैसा होता है. इसे आपने पहले देखा था.

सबसे पहले, मौजूदा बटन को Row में रैप करें. MyHomePage के build() तरीके पर जाएं. इसके बाद, अपने कर्सर को ElevatedButton पर रखें. Ctrl+. या Cmd+. की मदद से, रीफ़ैक्टर करें मेन्यू को कॉल करें. इसके बाद, Wrap with Row को चुनें.

सेव करने पर, आपको दिखेगा कि Row, Column की तरह काम करता है. डिफ़ॉल्ट रूप से, यह अपने बच्चों को बाईं ओर रखता है. (Column ने अपने बच्चों को सबसे ऊपर रखा है.) इस समस्या को ठीक करने के लिए, पहले की तरह ही तरीका इस्तेमाल किया जा सकता है. हालांकि, इस बार mainAxisAlignment का इस्तेमाल करना होगा. हालांकि, शिक्षा देने के मकसद से mainAxisSize का इस्तेमाल करें. इससे Row को यह निर्देश मिलता है कि वह उपलब्ध पूरे हॉरिज़ॉन्टल स्पेस का इस्तेमाल न करे.

यह बदलाव करें:

lib/main.dart

// ...

class MyHomePage extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    var appState = context.watch<MyAppState>();
    var pair = appState.current;

    return Scaffold(
      body: Center(
        child: Column(
          mainAxisAlignment: MainAxisAlignment.center,
          children: [
            BigCard(pair: pair),
            SizedBox(height: 10),
            Row(
              mainAxisSize: MainAxisSize.min,   //  Add this.
              children: [
                ElevatedButton(
                  onPressed: () {
                    appState.getNext();
                  },
                  child: Text('Next'),
                ),
              ],
            ),
          ],
        ),
      ),
    );
  }
}

// ...

यूज़र इंटरफ़ेस (यूआई) पहले जैसा हो जाएगा.

3d53d2b071e2f372.png

इसके बाद, पसंद करें बटन जोड़ें और इसे toggleFavorite() से कनेक्ट करें. चुनौती के तौर पर, पहले इसे खुद से करने की कोशिश करें. इसके लिए, नीचे दिए गए कोड ब्लॉक को न देखें.

e6b01a8c90df8ffa.png

अगर आपने इसे नीचे दिए गए तरीके से नहीं किया है, तो भी कोई दिक्कत नहीं है. असल में, अगर आपको कोई बड़ी चुनौती नहीं चाहिए, तो दिल वाले आइकॉन के बारे में चिंता न करें.

अगर आप इस कोड को चलाने में सफल नहीं होते हैं, तो कोई बात नहीं. यह Flutter के साथ आपका पहला घंटा है.

252f7c4a212c94d2.png

MyHomePage में दूसरा बटन जोड़ने का एक तरीका यहां बताया गया है. इस बार, आइकॉन वाला बटन बनाने के लिए ElevatedButton.icon() कंस्ट्रक्टर का इस्तेमाल करें. इसके बाद, build तरीके के सबसे ऊपर, सही आइकॉन चुनें. यह इस बात पर निर्भर करता है कि मौजूदा शब्द का जोड़ा पहले से ही पसंदीदा सूची में है या नहीं. साथ ही, ध्यान दें कि दो बटन के बीच थोड़ी जगह छोड़ने के लिए, SizedBox का फिर से इस्तेमाल किया गया है.

lib/main.dart

// ...

class MyHomePage extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    var appState = context.watch<MyAppState>();
    var pair = appState.current;

    //  Add this.
    IconData icon;
    if (appState.favorites.contains(pair)) {
      icon = Icons.favorite;
    } else {
      icon = Icons.favorite_border;
    }

    return Scaffold(
      body: Center(
        child: Column(
          mainAxisAlignment: MainAxisAlignment.center,
          children: [
            BigCard(pair: pair),
            SizedBox(height: 10),
            Row(
              mainAxisSize: MainAxisSize.min,
              children: [

                //  And this.
                ElevatedButton.icon(
                  onPressed: () {
                    appState.toggleFavorite();
                  },
                  icon: Icon(icon),
                  label: Text('Like'),
                ),
                SizedBox(width: 10),

                ElevatedButton(
                  onPressed: () {
                    appState.getNext();
                  },
                  child: Text('Next'),
                ),
              ],
            ),
          ],
        ),
      ),
    );
  }
}

// ...

ऐप्लिकेशन ऐसा दिखना चाहिए:

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

7. नेविगेशन रेल जोड़ना

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

f62c54f5401a187.png

इस चरण को जल्द से जल्द पूरा करने के लिए, MyHomePage को दो अलग-अलग विजेट में बांटें.

MyHomePage में मौजूद सभी कोड को चुनें, उसे मिटाएं, और उसकी जगह यह कोड डालें:

lib/main.dart

// ...

class MyHomePage extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return Scaffold(
      body: Row(
        children: [
          SafeArea(
            child: NavigationRail(
              extended: false,
              destinations: [
                NavigationRailDestination(
                  icon: Icon(Icons.home),
                  label: Text('Home'),
                ),
                NavigationRailDestination(
                  icon: Icon(Icons.favorite),
                  label: Text('Favorites'),
                ),
              ],
              selectedIndex: 0,
              onDestinationSelected: (value) {
                print('selected: $value');
              },
            ),
          ),
          Expanded(
            child: Container(
              color: Theme.of(context).colorScheme.primaryContainer,
              child: GeneratorPage(),
            ),
          ),
        ],
      ),
    );
  }
}

class GeneratorPage extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    var appState = context.watch<MyAppState>();
    var pair = appState.current;

    IconData icon;
    if (appState.favorites.contains(pair)) {
      icon = Icons.favorite;
    } else {
      icon = Icons.favorite_border;
    }

    return Center(
      child: Column(
        mainAxisAlignment: MainAxisAlignment.center,
        children: [
          BigCard(pair: pair),
          SizedBox(height: 10),
          Row(
            mainAxisSize: MainAxisSize.min,
            children: [
              ElevatedButton.icon(
                onPressed: () {
                  appState.toggleFavorite();
                },
                icon: Icon(icon),
                label: Text('Like'),
              ),
              SizedBox(width: 10),
              ElevatedButton(
                onPressed: () {
                  appState.getNext();
                },
                child: Text('Next'),
              ),
            ],
          ),
        ],
      ),
    );
  }
}

// ...

सेव करने के बाद, आपको दिखेगा कि यूज़र इंटरफ़ेस (यूआई) का विज़ुअल हिस्सा तैयार है. हालांकि, यह काम नहीं करता. नेविगेशन रेल में मौजूद ♥︎ (दिल) पर क्लिक करने से कुछ नहीं होता.

388bc25fe198c54a.png

बदलावों की जांच करें.

  • सबसे पहले, ध्यान दें कि MyHomePage का पूरा कॉन्टेंट, GeneratorPage नाम के नए विजेट में एक्सट्रैक्ट किया गया है. MyHomePage विजेट का सिर्फ़ Scaffold हिस्सा एक्सट्रैक्ट नहीं किया गया है.
  • नई MyHomePage में दो बच्चों के साथ एक Row शामिल है. पहला विजेट SafeArea है और दूसरा Expanded विजेट है.
  • SafeArea यह पक्का करता है कि उसके चाइल्ड विजेट को हार्डवेयर नॉच या स्टेटस बार से न ढका जाए. इस ऐप्लिकेशन में, विजेट NavigationRail के चारों ओर रैप होता है, ताकि नेविगेशन बटन को मोबाइल के स्टेटस बार से छिपाने से रोका जा सके. उदाहरण के लिए.
  • NavigationRail में जाकर, extended: false लाइन को true में बदला जा सकता है. इससे आइकॉन के बगल में मौजूद लेबल दिखते हैं. आने वाले समय में, आपको यह पता चलेगा कि जब ऐप्लिकेशन में काफ़ी हॉरिज़ॉन्टल स्पेस हो, तो इसे अपने-आप कैसे किया जा सकता है.
  • नेविगेशन रेल में दो डेस्टिनेशन (होम और पसंदीदा) हैं. इनके आइकॉन और लेबल भी दिए गए हैं. यह मौजूदा selectedIndex के बारे में भी बताता है. चुने गए इंडेक्स के तौर पर शून्य चुनने पर, पहला डेस्टिनेशन चुना जाता है. चुने गए इंडेक्स के तौर पर एक चुनने पर, दूसरा डेस्टिनेशन चुना जाता है. इसी तरह, आगे भी डेस्टिनेशन चुने जाते हैं. फ़िलहाल, इसे शून्य पर सेट किया गया है.
  • नेविगेशन रेल से यह भी तय होता है कि जब उपयोगकर्ता, onDestinationSelected की मदद से किसी डेस्टिनेशन को चुनता है, तो क्या होता है. फ़िलहाल, ऐप्लिकेशन सिर्फ़ print() के साथ अनुरोध की गई इंडेक्स वैल्यू को आउटपुट करता है.
  • Row का दूसरा चाइल्ड Expanded विजेट है. बढ़े हुए विजेट, लाइनों और कॉलम में बहुत काम आते हैं. इनकी मदद से, ऐसे लेआउट बनाए जा सकते हैं जहां कुछ चाइल्ड विजेट सिर्फ़ उतना स्पेस लेते हैं जितना उन्हें चाहिए (इस मामले में SafeArea) और अन्य विजेट को बाकी स्पेस का ज़्यादा से ज़्यादा हिस्सा लेना चाहिए (इस मामले में Expanded). Expanded विजेट को इस तरह से भी समझा जा सकता है कि वे "लालची" होते हैं. अगर आपको इस विजेट की भूमिका के बारे में बेहतर तरीके से जानना है, तो SafeArea विजेट को किसी दूसरे Expanded के साथ रैप करके देखें. इससे मिलने वाला लेआउट कुछ ऐसा दिखता है:

6bbda6c1835a1ae.png

  • दो Expanded विजेट, उपलब्ध हॉरिज़ॉन्टल स्पेस को आपस में बांट लेते हैं. भले ही, नेविगेशन रेल को सिर्फ़ बाईं ओर थोड़ी जगह की ज़रूरत हो.
  • Expanded विजेट के अंदर, रंगीन Container है. साथ ही, कंटेनर के अंदर GeneratorPage है.

स्टेटलेस बनाम स्टेटफ़ुल विजेट

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

यह अब बदलने वाला है.

आपको नेविगेशन रेल के selectedIndex की वैल्यू को सेव करने के लिए किसी तरीके की ज़रूरत होगी. आपको इस वैल्यू को onDestinationSelected कॉलबैक से भी बदलने की सुविधा चाहिए.

MyAppState की एक और प्रॉपर्टी के तौर पर, selectedIndex को जोड़ा जा सकता है. और यह काम करेगा. हालांकि, अगर हर विजेट अपनी वैल्यू को इसमें सेव करता है, तो ऐप्लिकेशन की स्थिति तेज़ी से बदलती है.

e52d9c0937cc0823.jpeg

कुछ स्टेट सिर्फ़ एक विजेट से जुड़ी होती हैं, इसलिए उन्हें उसी विजेट के साथ रहना चाहिए.

StatefulWidget डालें. यह एक तरह का विजेट है, जिसमें State होता है. सबसे पहले, MyHomePage को स्टेटफ़ुल विजेट में बदलें.

अपने कर्सर को MyHomePage की पहली लाइन पर रखें. यह वह लाइन है जो class MyHomePage... से शुरू होती है. इसके बाद, Ctrl+. या Cmd+. का इस्तेमाल करके, फिर से व्यवस्थित करें मेन्यू खोलें. इसके बाद, Convert to StatefulWidget को चुनें.

आईडीई आपके लिए एक नई क्लास, _MyHomePageState बनाता है. यह क्लास State से मिलती है. इसलिए, यह अपनी वैल्यू मैनेज कर सकती है. (यह अपने-आप बदल सकता है.) यह भी ध्यान दें कि पुराने, स्टेटलेस विजेट का build तरीका, अब _MyHomePageState में चला गया है. यह विजेट में नहीं है. इसे हूबहू ट्रांसफ़र किया गया है. build तरीके में कोई बदलाव नहीं किया गया है. अब यह किसी दूसरी जगह पर मौजूद है.

setState

नए स्टेटफ़ुल विजेट को सिर्फ़ एक वैरिएबल को ट्रैक करने की ज़रूरत होती है: selectedIndex. _MyHomePageState में ये तीन बदलाव करें:

lib/main.dart

// ...

class _MyHomePageState extends State<MyHomePage> {

  var selectedIndex = 0;     //  Add this property.

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      body: Row(
        children: [
          SafeArea(
            child: NavigationRail(
              extended: false,
              destinations: [
                NavigationRailDestination(
                  icon: Icon(Icons.home),
                  label: Text('Home'),
                ),
                NavigationRailDestination(
                  icon: Icon(Icons.favorite),
                  label: Text('Favorites'),
                ),
              ],
              selectedIndex: selectedIndex,    //  Change to this.
              onDestinationSelected: (value) {

                //  Replace print with this.
                setState(() {
                  selectedIndex = value;
                });

              },
            ),
          ),
          Expanded(
            child: Container(
              color: Theme.of(context).colorScheme.primaryContainer,
              child: GeneratorPage(),
            ),
          ),
        ],
      ),
    );
  }
}

// ...

बदलावों की जांच करें:

  1. आपने एक नया वैरिएबल, selectedIndex बनाया है और उसे 0 पर सेट किया है.
  2. अब तक इस्तेमाल किए जा रहे हार्ड-कोड किए गए 0 के बजाय, NavigationRail की डेफ़िनिशन में इस नए वैरिएबल का इस्तेमाल करें.
  3. जब onDestinationSelected कॉलबैक को कॉल किया जाता है, तो नई वैल्यू को सिर्फ़ कंसोल पर प्रिंट करने के बजाय, उसे setState() कॉल के अंदर selectedIndex को असाइन किया जाता है. यह कॉल, पहले इस्तेमाल किए गए notifyListeners() तरीके जैसा ही है. इससे यह पक्का होता है कि यूज़र इंटरफ़ेस (यूआई) अपडेट हो गया है.

नेविगेशन रेल अब उपयोगकर्ता के इंटरैक्शन पर प्रतिक्रिया देती है. हालांकि, दाईं ओर बड़ा किया गया हिस्सा पहले जैसा ही रहता है. ऐसा इसलिए है, क्योंकि कोड में selectedIndex का इस्तेमाल यह तय करने के लिए नहीं किया जा रहा है कि कौनसी स्क्रीन दिखेगी.

selectedIndex का इस्तेमाल करना

_MyHomePageState के build तरीके के सबसे ऊपर, return Scaffold से ठीक पहले यह कोड डालें:

lib/main.dart

// ...

Widget page;
switch (selectedIndex) {
  case 0:
    page = GeneratorPage();
    break;
  case 1:
    page = Placeholder();
    break;
  default:
    throw UnimplementedError('no widget for $selectedIndex');
}

// ...

इस कोड की जांच करें:

  1. इस कोड में, page टाइप का एक नया वैरिएबल page बनाया गया है.Widget
  2. इसके बाद, स्विच स्टेटमेंट page में मौजूद मौजूदा वैल्यू के हिसाब से, page को स्क्रीन असाइन करता है.selectedIndex
  3. फ़िलहाल, FavoritesPage उपलब्ध नहीं है. इसलिए, Placeholder का इस्तेमाल करें. यह एक काम का विजेट है. इसे जहां भी रखा जाता है वहां एक आयत बनाता है और उसे क्रॉस कर देता है. इससे यूज़र इंटरफ़ेस (यूआई) के उस हिस्से को अधूरा माना जाता है.

5685cf886047f6ec.png

  1. fail-fast सिद्धांत लागू करने पर, स्विच स्टेटमेंट यह भी पक्का करता है कि अगर selectedIndex न तो 0 है और न ही 1, तो गड़बड़ी दिखे. इससे आने वाले समय में बग को रोकने में मदद मिलती है. अगर आपने कभी नेविगेशन रेल में कोई नया डेस्टिनेशन जोड़ा है और इस कोड को अपडेट करना भूल गए हैं, तो प्रोग्राम डेवलपमेंट के दौरान क्रैश हो जाता है. इससे आपको यह अनुमान लगाने की ज़रूरत नहीं पड़ती कि चीज़ें क्यों काम नहीं कर रही हैं. साथ ही, आपको प्रोडक्शन में गड़बड़ी वाला कोड पब्लिश करने की अनुमति भी नहीं मिलती.

अब page में वह विजेट मौजूद है जिसे आपको दाईं ओर दिखाना है. इससे आपको शायद यह अंदाज़ा लग गया होगा कि और क्या बदलाव करना है.

यहां _MyHomePageState में एक बदलाव करने के बाद का वर्शन दिया गया है:

lib/main.dart

// ...

class _MyHomePageState extends State<MyHomePage> {
  var selectedIndex = 0;

  @override
  Widget build(BuildContext context) {
    Widget page;
    switch (selectedIndex) {
      case 0:
        page = GeneratorPage();
        break;
      case 1:
        page = Placeholder();
        break;
      default:
        throw UnimplementedError('no widget for $selectedIndex');
    }

    return Scaffold(
      body: Row(
        children: [
          SafeArea(
            child: NavigationRail(
              extended: false,
              destinations: [
                NavigationRailDestination(
                  icon: Icon(Icons.home),
                  label: Text('Home'),
                ),
                NavigationRailDestination(
                  icon: Icon(Icons.favorite),
                  label: Text('Favorites'),
                ),
              ],
              selectedIndex: selectedIndex,
              onDestinationSelected: (value) {
                setState(() {
                  selectedIndex = value;
                });
              },
            ),
          ),
          Expanded(
            child: Container(
              color: Theme.of(context).colorScheme.primaryContainer,
              child: page,  //  Here.
            ),
          ),
        ],
      ),
    );
  }
}

// ...

अब ऐप्लिकेशन, हमारे GeneratorPage और प्लेसहोल्डर के बीच स्विच करता है. यह प्लेसहोल्डर जल्द ही पसंदीदा पेज बन जाएगा.

जवाबदेही

इसके बाद, नेविगेशन रेल को रिस्पॉन्सिव बनाएं. इसका मतलब है कि जब लेबल दिखाने के लिए काफ़ी जगह हो, तो उन्हें अपने-आप दिखने दें. इसके लिए, extended: true का इस्तेमाल करें.

a8873894c32e0d0b.png

Flutter कई ऐसे विजेट उपलब्ध कराता है जिनकी मदद से, आपके ऐप्लिकेशन अपने-आप रिस्पॉन्सिव बन जाते हैं. उदाहरण के लिए, Wrap, Row या Column जैसा ही एक विजेट है. इसमें वर्टिकल या हॉरिज़ॉन्टल स्पेस कम होने पर, चाइल्ड विजेट अपने-आप अगली "लाइन" (इसे "रन" कहा जाता है) में रैप हो जाते हैं. FittedBox एक ऐसा विजेट है जो आपकी तय की गई सेटिंग के हिसाब से, उपलब्ध जगह में अपने-आप फ़िट हो जाता है.

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

मान लें कि आपको लेबल सिर्फ़ तब दिखाने हैं, जब MyHomePage की चौड़ाई कम से कम 600 पिक्सल हो.

इस मामले में, इस्तेमाल किया जाने वाला विजेट LayoutBuilder है. इससे, उपलब्ध जगह के हिसाब से विजेट ट्री को बदला जा सकता है.

ज़रूरी बदलाव करने के लिए, VS Code में Flutter के Refactor मेन्यू का फिर से इस्तेमाल करें. हालांकि, इस बार यह थोड़ा ज़्यादा पेचीदा है:

  1. _MyHomePageState के build तरीके में, अपने कर्सर को Scaffold पर रखें.
  2. Ctrl+. (Windows/Linux) या Cmd+. (Mac) दबाकर, Refactor मेन्यू खोलें.
  3. Wrap with Builder को चुनें और Enter दबाएं.
  4. हाल ही में जोड़े गए Builder का नाम बदलकर LayoutBuilder करें.
  5. कॉल बैक पैरामीटर की सूची को (context) से बदलकर (context, constraints) करें.

LayoutBuilder का builder कॉलबैक, हर बार कंस्ट्रेंट बदलने पर कॉल किया जाता है. ऐसा तब होता है, जब:

  • जब उपयोगकर्ता ऐप्लिकेशन की विंडो का साइज़ बदलता है
  • जब उपयोगकर्ता अपने फ़ोन को पोर्ट्रेट मोड से लैंडस्केप मोड में घुमाता है या वापस पोर्ट्रेट मोड में लाता है
  • MyHomePage के बगल में मौजूद कुछ विजेट का साइज़ बढ़ जाता है. इससे MyHomePage के लिए उपलब्ध जगह कम हो जाती है

अब आपका कोड यह तय कर सकता है कि मौजूदा constraints से क्वेरी करके लेबल दिखाना है या नहीं. _MyHomePageState के build तरीके में, एक लाइन का यह बदलाव करें:

lib/main.dart

// ...

class _MyHomePageState extends State<MyHomePage> {
  var selectedIndex = 0;

  @override
  Widget build(BuildContext context) {
    Widget page;
    switch (selectedIndex) {
      case 0:
        page = GeneratorPage();
        break;
      case 1:
        page = Placeholder();
        break;
      default:
        throw UnimplementedError('no widget for $selectedIndex');
    }

    return LayoutBuilder(builder: (context, constraints) {
      return Scaffold(
        body: Row(
          children: [
            SafeArea(
              child: NavigationRail(
                extended: constraints.maxWidth >= 600,  //  Here.
                destinations: [
                  NavigationRailDestination(
                    icon: Icon(Icons.home),
                    label: Text('Home'),
                  ),
                  NavigationRailDestination(
                    icon: Icon(Icons.favorite),
                    label: Text('Favorites'),
                  ),
                ],
                selectedIndex: selectedIndex,
                onDestinationSelected: (value) {
                  setState(() {
                    selectedIndex = value;
                  });
                },
              ),
            ),
            Expanded(
              child: Container(
                color: Theme.of(context).colorScheme.primaryContainer,
                child: page,
              ),
            ),
          ],
        ),
      );
    });
  }
}


// ...

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

अब सिर्फ़ इतना काम बाकी है कि उस Placeholder को पसंदीदा स्क्रीन से बदल दिया जाए. इसके बारे में अगले सेक्शन में बताया गया है.

8. नया पेज जोड़ें

क्या आपको Placeholder विजेट याद है? हमने इसका इस्तेमाल पसंदीदा पेज के बजाय किया था.

अब इसे ठीक करने का समय आ गया है.

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

यहां कुछ सुझाव दिए गए हैं:

  • अगर आपको स्क्रोल करने वाला Column चाहिए, तो ListView विजेट का इस्तेमाल करें.
  • ध्यान रखें कि context.watch<MyAppState>() का इस्तेमाल करके, किसी भी विजेट से MyAppState इंस्टेंस को ऐक्सेस किया जा सकता है.
  • अगर आपको कोई नया विजेट आज़माना है, तो ListTile में title (आम तौर पर टेक्स्ट के लिए), leading (आइकॉन या अवतार के लिए), और onTap (इंटरैक्शन के लिए) जैसी प्रॉपर्टी होती हैं. हालांकि, आपको पहले से पता है कि किन विजेट से इस तरह के इफ़ेक्ट मिलते हैं.
  • Dart, कलेक्शन लिटरल के अंदर for लूप का इस्तेमाल करने की अनुमति देता है. उदाहरण के लिए, अगर messages में स्ट्रिंग की सूची शामिल है, तो आपके पास इस तरह का कोड हो सकता है:

f0444bba08f205aa.png

दूसरी ओर, अगर आपको फ़ंक्शनल प्रोग्रामिंग के बारे में ज़्यादा जानकारी है, तो Dart में messages.map((m) => Text(m)).toList() की तरह कोड भी लिखा जा सकता है. इसके अलावा, विजेट की सूची बनाकर, build तरीके का इस्तेमाल करके उसमें विजेट जोड़े जा सकते हैं.

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

252f7c4a212c94d2.png

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

यहां नई FavoritesPage क्लास दी गई है:

lib/main.dart

// ...

class FavoritesPage extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    var appState = context.watch<MyAppState>();

    if (appState.favorites.isEmpty) {
      return Center(
        child: Text('No favorites yet.'),
      );
    }

    return ListView(
      children: [
        Padding(
          padding: const EdgeInsets.all(20),
          child: Text('You have '
              '${appState.favorites.length} favorites:'),
        ),
        for (var pair in appState.favorites)
          ListTile(
            leading: Icon(Icons.favorite),
            title: Text(pair.asLowerCase),
          ),
      ],
    );
  }
}

यहां बताया गया है कि विजेट क्या करता है:

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

अब आपको सिर्फ़ Placeholder विजेट को FavoritesPage से बदलना है. और यह लीजिए!

आपको इस ऐप्लिकेशन का फ़ाइनल कोड, GitHub पर मौजूद कोडलैब रेपो में मिल जाएगा.

9. अगले चरण

बधाई हो!

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

d6e3d5f736411f13.png

हमने क्या-क्या कवर किया है

  • Flutter के काम करने के तरीके के बारे में बुनियादी जानकारी
  • Flutter में लेआउट बनाना
  • उपयोगकर्ता के इंटरैक्शन (जैसे, बटन दबाना) को ऐप्लिकेशन के व्यवहार से कनेक्ट करना
  • अपने Flutter कोड को व्यवस्थित रखना
  • अपने ऐप्लिकेशन को रिस्पॉन्सिव बनाना
  • अपने ऐप्लिकेशन का रंग-रूप और स्टाइल एक जैसा बनाए रखना

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

  • इस लैब के दौरान लिखे गए ऐप्लिकेशन के साथ ज़्यादा एक्सपेरिमेंट करें.
  • उसी ऐप्लिकेशन के इस ऐडवांस वर्शन का कोड देखें. इससे आपको यह पता चलेगा कि ऐनिमेटेड सूचियां, ग्रेडिएंट, क्रॉस-फ़ेड वगैरह कैसे जोड़े जा सकते हैं.
  • flutter.dev/learn पर जाकर, अपनी लर्निंग जर्नी को फ़ॉलो करें.