1. परिचय
Flutter, Google का यूज़र इंटरफ़ेस (यूआई) टूलकिट है. इसकी मदद से, एक ही कोड बेस से मोबाइल, वेब, और डेस्कटॉप के लिए ऐप्लिकेशन बनाए जा सकते हैं. इस कोडलैब में, यह Flutter ऐप्लिकेशन बनाया जाएगा:
ऐप्लिकेशन से मिलने वाले नाम आसान होते हैं, जैसे कि "newstay", "lightstream", "mainbrake" या "ग्रेपीन". उपयोगकर्ता अगले नाम के बारे में पूछ सकता है, मौजूदा नाम को पसंदीदा के तौर पर चुन सकता है, और पसंदीदा नामों की सूची को एक अलग पेज पर देख सकता है. ऐप्लिकेशन अलग-अलग स्क्रीन साइज़ के हिसाब से काम करता है.
आप इन चीज़ों के बारे में जानेंगे
- Flutter के काम करने के तरीके की बुनियादी बातें
- Flutter में लेआउट बनाना
- उपयोगकर्ता के इंटरैक्शन (जैसे कि बटन दबाने पर) को ऐप्लिकेशन के व्यवहार से कनेक्ट करना
- अपने Flutter कोड को व्यवस्थित रखना
- ऐप्लिकेशन को रिस्पॉन्सिव बनाना (अलग-अलग स्क्रीन के लिए)
- एक जैसा रंग-रूप हासिल करना और CANNOT TRANSLATE
आप एक बुनियादी मचान से शुरुआत करेंगे, ताकि आप दिलचस्प हिस्सों पर सीधे जा सकें.
फ़िलिप कोडलैब आपको पूरे कोडलैब के बारे में बता रहा है!
लैब शुरू करने के लिए, 'आगे बढ़ें' पर क्लिक करें.
2. Flutter एनवायरमेंट को सेट अप करें
संपादक
इस कोडलैब को आसान बनाने के लिए, हम मानते हैं कि अपने डेवलपमेंट एनवायरमेंट के तौर पर विज़ुअल स्टूडियो कोड (वीएस कोड) का इस्तेमाल किया जाएगा. यह मुफ़्त है और सभी बड़े प्लैटफ़ॉर्म पर काम करता है.
बेशक, अपनी पसंद के किसी भी एडिटर का इस्तेमाल किया जा सकता है: Android Studio, अन्य IntelliJ IDE, Emacs, Vim या Notepad++. वे सभी Flutter के साथ काम करते हैं.
हम इस कोडलैब के लिए वीएस कोड का इस्तेमाल करने का सुझाव देते हैं, क्योंकि निर्देशों में डिफ़ॉल्ट रूप से वीएस कोड के हिसाब से बने शॉर्टकट होते हैं. "यहां क्लिक करें" जैसी बातें कहना आसान है या "इस बटन को दबाएं" "X करने के लिए अपने एडिटर में उचित कार्रवाई करें" जैसा कुछ न करें.
डेवलपमेंट टारगेट चुनें
Flutter कई प्लैटफ़ॉर्म पर उपलब्ध टूलकिट है. आपका ऐप्लिकेशन इनमें से किसी भी ऑपरेटिंग सिस्टम पर चल सकता है:
- iOS
- Android
- Windows
- macOS
- Linux
- वेब
हालांकि, एक ऐसा ऑपरेटिंग सिस्टम चुनना आम बात है जिसके लिए आपको खास तौर पर डेवलप करना है. यह आपका "डेवलपमेंट टारगेट"—यह वह ऑपरेटिंग सिस्टम है जिस पर आपका ऐप्लिकेशन डेवलपमेंट के दौरान चलता है.
उदाहरण के लिए, मान लें कि Flutter ऐप्लिकेशन बनाने के लिए Windows लैपटॉप का इस्तेमाल किया जा रहा है. अगर Android को अपने डेवलपमेंट टारगेट के तौर पर चुना जाता है, तो आम तौर पर यूएसबी केबल के ज़रिए अपने Windows लैपटॉप से एक Android डिवाइस जोड़ा जाता है. इसके बाद, उस Android डिवाइस का इस्तेमाल करके ऐप्लिकेशन डेवलप किया जा रहा है. लेकिन आप Windows को डेवलपमेंट टारगेट के रूप में भी चुन सकते हैं, जिसका मतलब है कि आपका ऐप्लिकेशन-इन-डेवलपमेंट आपके संपादक के साथ Windows ऐप्लिकेशन के रूप में चलता है.
अपने डेवलपमेंट टारगेट के तौर पर वेब को चुनना दिलचस्प हो सकता है. इस विकल्प की एक समस्या यह है कि Flutter को डेवलपमेंट से जुड़ी सबसे अहम सुविधाओं में से एक का इस्तेमाल नहीं किया जा सकता. स्टेटफ़ुल हॉट रीलोड. Flutter, वेब ऐप्लिकेशन को हॉट-रीलोड नहीं कर सकता.
अभी चुनें. याद रखें: आप अपने ऐप्लिकेशन को बाद में किसी भी समय अन्य ऑपरेटिंग सिस्टम पर चला सकते हैं. बस ध्यान रखें कि डेवलपमेंट टारगेट के बारे में साफ़ तौर पर जानकारी देने से, अगला कदम और आसान हो जाता है.
Flutter इंस्टॉल करें
Flutter SDK टूल को इंस्टॉल करने के तरीके से जुड़े सबसे अप-टू-डेट निर्देश हमेशा docs.flutter.dev पर उपलब्ध होते हैं.
Flutter वेबसाइट पर दिए गए निर्देशों में न सिर्फ़ SDK टूल को इंस्टॉल करने का तरीका बताया गया है, बल्कि डेवलपमेंट टारगेट से जुड़े टूल और एडिटर प्लगिन भी दिए गए हैं. याद रखें कि इस कोडलैब के लिए, आपको सिर्फ़ ये चीज़ें इंस्टॉल करनी होंगी:
- Flutter SDK टूल
- Flutter प्लगिन के साथ विज़ुअल स्टूडियो कोड
- आपके चुने गए डेवलपमेंट टारगेट के लिए ज़रूरी सॉफ़्टवेयर. उदाहरण के लिए: Windows को टारगेट करने के लिए Visual Studio या macOS को टारगेट करने के लिए Xcode
अगले सेक्शन में, आपको अपना पहला Flutter प्रोजेक्ट बनाना होगा.
अगर आपको अब तक समस्याएं आ रही हैं, तो इनमें से कुछ सवाल और जवाब (StackOverflow से) आपको समस्या हल करने में मददगार हो सकते हैं.
अक्सर पूछे जाने वाले सवाल
- मुझे Flutter SDK टूल का पाथ कैसे मिलेगा?
- Flutter निर्देश न मिलने पर, मैं क्या करूं?
- "स्टार्टअप लॉक को रिलीज़ करने के लिए, एक और फ़्लटर कमांड का इंतज़ार किया जा रहा है" गड़बड़ी को कैसे ठीक करूं कोई समस्या है?
- मैं Flutter को कैसे बताऊं कि मेरा Android SDK इंस्टॉलेशन कहां है?
flutter doctor --android-licenses
को चलाते समय, मैं Java की गड़बड़ी से कैसे निपटूं?- मैं Android पर
sdkmanager
टूल न मिलने की समस्या का कैसे हल करूं? - मैं "
cmdline-tools
कॉम्पोनेंट मौजूद नहीं है" समस्या से जुड़ी समस्या को कैसे हल करूं में गड़बड़ी है? - मैं Apple Silicon (M1) पर CocoaPods कैसे चलाऊं?
- मैं बनाम कोड में सेव करने पर, अपने-आप फ़ॉर्मैट होने की सुविधा को कैसे बंद करूं?
3. प्रोजेक्ट बनाना
अपना पहला Flutter प्रोजेक्ट बनाएं
विज़ुअल स्टूडियो कोड लॉन्च करें और F1
या Ctrl+Shift+P
या Shift+Cmd+P
के साथ कमांड पैलेट खोलें. "फ़्लटर न्यू" टाइप करना शुरू करें. Flutter: New Project कमांड चुनें.
इसके बाद, ऐप्लिकेशन चुनें. इसके बाद, एक फ़ोल्डर चुनें, जिसमें आपको अपना प्रोजेक्ट बनाना है. यह आपकी होम डायरेक्ट्री या C:\src\
जैसा कुछ हो सकता है.
आखिर में, अपने प्रोजेक्ट को नाम दें. namer_app
या my_awesome_namer
जैसा कुछ.
Flutter अब आपका प्रोजेक्ट फ़ोल्डर बनाता है और VS Code खोलता है.
अब ऐप्लिकेशन के बेसिक स्कैफ़ोल्ड से, तीन फ़ाइलों का कॉन्टेंट ओवरराइट हो जाएगा.
कॉपी करें और शुरुआती ऐप्लिकेशन चिपकाएं
बनाम कोड के बाएं पैनल में पक्का करें कि Explorer चुना गया है और pubspec.yaml
फ़ाइल खोलें.
इस फ़ाइल की सामग्री को इनसे बदलें:
pubspec.yaml
name: namer_app
description: A new Flutter project.
publish_to: 'none' # Remove this line if you wish to publish to pub.dev
version: 0.0.1+1
environment:
sdk: ^3.1.1
dependencies:
flutter:
sdk: flutter
english_words: ^4.0.0
provider: ^6.0.0
dev_dependencies:
flutter_test:
sdk: flutter
flutter_lints: ^2.0.0
flutter:
uses-material-design: true
pubspec.yaml
फ़ाइल आपके ऐप्लिकेशन के बारे में बुनियादी जानकारी के बारे में बताती है. जैसे, ऐप्लिकेशन का मौजूदा वर्शन, उसकी डिपेंडेंसी, और वे एसेट जिनसे ऐप्लिकेशन शिप किया जाएगा.
इसके बाद, प्रोजेक्ट में एक अन्य कॉन्फ़िगरेशन फ़ाइल खोलें, analysis_options.yaml
.
इसकी सामग्री को निम्न से बदलें:
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 में आपका पहला हमला है. इसलिए, आपने ऐनालाइज़र से कहा है कि वे इस काम को आसान बना दें. इसे बाद में कभी भी ट्यून किया जा सकता है. जब आप कोई असली प्रोडक्शन ऐप्लिकेशन पब्लिश करने के करीब आते हैं, तो आप यह ज़रूर चाहेंगे कि आप ऐनालाइज़र को इससे ज़्यादा सख्त बनाना चाहें.
आखिर में, main.dart
फ़ाइल को lib/
डायरेक्ट्री में खोलें.
इस फ़ाइल की सामग्री को इनसे बदलें:
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(
useMaterial3: true,
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
खोलें और यह पक्का करें कि आपने अपना टारगेट डिवाइस चुना हो. बनाम कोड के नीचे दाएं कोने में, आपको मौजूदा टारगेट डिवाइस को दिखाने वाला बटन मिलेगा. इसे बदलने के लिए क्लिक करें.
जब lib/main.dart
खुला हो, तब "चलाएं" ढूंढें वीएस कोड की विंडो के ऊपरी दाएं कोने में बटन पर क्लिक करें और उस पर क्लिक करें.
करीब एक मिनट के बाद, आपका ऐप्लिकेशन डीबग मोड में लॉन्च होता है. इस बारे में अभी ज़्यादा जानकारी नहीं दिख रही है:
पहला हॉट रीलोड
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 का मशहूर स्टेटफ़ुल हॉट रीलोड है. जब आप सोर्स फ़ाइल में बदलावों को सेव करते हैं, तब हॉट रीलोड ट्रिगर होता है.
अक्सर पूछे जाने वाले सवाल
- अगर VSCode में हॉट रीलोड की सुविधा काम नहीं करती है, तो क्या होगा?
- क्या मुझे ‘r' दबाना है VSCode में हॉट रीलोड के लिए क्या करना चाहिए?
- क्या वेब पर हॉट रीलोड की सुविधा काम करती है?
- मैं "डीबग" को कैसे निकालूं बैनर?
बटन जोड़ना
इसके बाद, 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'),
),
],
),
);
// ...
बदलाव को सेव करने के बाद, ऐप्लिकेशन फिर से अपडेट हो जाता है: एक बटन दिखता है और जब इस बटन पर क्लिक किया जाता है, तो बनाम कोड में मौजूद डीबग कंसोल में एक बटन दबाया गया! मैसेज दिखता है.
Flutter क्रैश कोर्स 5 मिनट में पूरा होगा
डीबग कंसोल देखने में ही बहुत मज़ा आ सकता है, आप चाहते हैं कि बटन कोई काम कर सके. हालांकि, इससे पहले, 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(
useMaterial3: true,
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
में दिया गया कोड देखें) का इस्तेमाल करके, पूरे ऐप्लिकेशन के लिए राज्य बनाया और उपलब्ध कराया जाता है. इससे ऐप्लिकेशन का कोई भी विजेट, अपने राज्य को होल्ड कर सकता है.
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
वह विजेट है जिसमें बदलाव किया जा चुका है. नीचे दिए गए नंबर वाली हर लाइन, ऊपर दिए गए कोड में लाइन नंबर टिप्पणी पर मैप होती है:
- हर विजेट एक
build()
तरीका तय करता है. विजेट की स्थितियां बदलने पर, हर बार अपने-आप कॉल होता है, ताकि विजेट हमेशा अप-टू-डेट रहे. MyHomePage
watch
तरीके का इस्तेमाल करके, ऐप्लिकेशन की मौजूदा स्थिति में होने वाले बदलावों को ट्रैक करता है.build
के हर तरीके के लिए, एक विजेट या (आम तौर पर) विजेट का एक नेस्ट किया हुआ पेड़ दिखना चाहिए. इस मामले में, टॉप-लेवल का विजेटScaffold
है. इस कोडलैब मेंScaffold
का इस्तेमाल नहीं किया जा सकता. हालांकि, यह एक मददगार विजेट है और यह असल दुनिया के ज़्यादातर Flutter ऐप्लिकेशन में मौजूद है.Column
, Flutter में मौजूद सबसे बेसिक लेआउट विजेट में से एक है. यह जितने चाहें उतने बच्चों का इस्तेमाल करता है और उन्हें ऊपर से नीचे की ओर एक कॉलम में रखता है. डिफ़ॉल्ट रूप से, कॉलम में विज़ुअल तौर पर, चाइल्ड खाते सबसे ऊपर दिखते हैं. आपको जल्द ही इसे बदल देना चाहिए, ताकि कॉलम बीच में हो जाए.- आपने पहले चरण में इस
Text
विजेट को बदल दिया है. - यह दूसरा
Text
विजेटappState
को लेता है और उस क्लास के अकेले सदस्यcurrent
(जो किWordPair
है) को ऐक्सेस करता है.WordPair
कई मददगार गैटर उपलब्ध कराता है, जैसे किasPascalCase
याasSnakeCase
. यहां हमasLowerCase
का इस्तेमाल करते हैं. हालांकि, अगर आपको कोई विकल्प पसंद है, तो इसे अब बदलें. - ध्यान दें कि 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. ऐप्लिकेशन को शानदार बनाएं
ऐप्लिकेशन इस समय ऐसा दिखता है.
बहुत बढ़िया नहीं. ऐप्लिकेशन का बीच वाला हिस्सा—रैंडम तरीके से जनरेट किए गए शब्दों का जोड़ा—ज़्यादा साफ़ तौर पर दिखना चाहिए. आखिरकार, यही मुख्य वजह है कि हमारे उपयोगकर्ता इस ऐप्लिकेशन का इस्तेमाल कर रहे हैं! इसके अलावा, ऐप्लिकेशन का कॉन्टेंट थोड़ा अजीब है और पूरे ऐप्लिकेशन में बोरिंग ब्लैक ऐंड सफ़ेद.
यह सेक्शन, ऐप्लिकेशन के डिज़ाइन पर काम करके इन समस्याओं को हल करता है. इस सेक्शन का आखिरी लक्ष्य कुछ ऐसा है:
विजेट निकालना
वर्तमान शब्द युग्म को दिखाने के लिए ज़िम्मेदार पंक्ति अब ऐसी दिखती है: 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 मेन्यू को कॉल करें. बनाम कोड में, इन दो में से किसी एक तरीके से ऐसा किया जा सकता है:
- कोड के उस हिस्से पर राइट क्लिक करें जिसे आपको रीफ़ैक्टर करना है (इस मामले में
Text
) और ड्रॉप-डाउन मेन्यू से रिफ़ैक्टर... चुनें,
या
- अपने कर्सर को उस पीस कोड पर ले जाएं जिसे रीफ़ैक्टर करना है (इस मामले में,
Text
) औरCtrl+.
(Win/Linux) याCmd+.
(Mac) दबाएं.
रिफ़ैक्टर मेन्यू में, एक्सट्रैक्ट विजेट चुनें. कोई नाम असाइन करें, जैसे कि 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 मेन्यू को कॉल करें. हालांकि, इस बार आपको बैज को एक्सट्रैक्ट करने की ज़रूरत नहीं है.
इसके बजाय, Wrap with पैडिंग को चुनें. इससे Text
विजेट की जगह Padding
नाम का एक नया पैरंट विजेट बन जाएगा. सेव करने के बाद, आप देखेंगे कि रैंडम शब्द के लिए पहले से ही ज़्यादा जगह खाली है.
8.0
की डिफ़ॉल्ट वैल्यू से पैडिंग (जगह) बढ़ाएं. उदाहरण के लिए, स्पेसर पैडिंग के लिए 20
जैसा कुछ इस्तेमाल करें.
अब एक लेवल ऊपर जाएं. अपना कर्सर Padding
विजेट पर रखें, रीफ़ैक्टर मेन्यू को ऊपर खोलें, और विजेट के साथ रैप करें... को चुनें.
इससे आपको पैरंट विजेट तय करने की सुविधा मिलती है. "कार्ड" टाइप करें और Enter दबाएं.
इससे Card
विजेट के साथ, Padding
विजेट और Text
भी रैप होता है.
थीम और स्टाइल
कार्ड को ज़्यादा खास बनाने के लिए, उसे ज़्यादा बेहतर रंग से पेंट करें. कलर स्कीम को एक जैसा बनाए रखना अच्छी बात है. इसलिए, कलर चुनने के लिए ऐप्लिकेशन की 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
ऐप्लिकेशन का सबसे खास रंग है.
कार्ड को अब ऐप्लिकेशन के मुख्य रंग से पेंट कर दिया गया है:
इस रंग और पूरे ऐप्लिकेशन की कलर स्कीम को बदला जा सकता है. इसके लिए, MyApp
तक स्क्रोल करें और वहां ColorScheme
के लिए सीड का रंग बदलें.
ध्यान दें कि रंग आसानी से कैसे ऐनिमेट होता है. इसे इंप्लिसिट ऐनिमेशन कहा जाता है. कई Flutter विजेट, वैल्यू के बीच आसानी से इंटरपोलेट करते हैं, ताकि यूज़र इंटरफ़ेस (यूआई) सिर्फ़ "जंप" न हो राज्यों के बीच होगा.
कार्ड के नीचे दिखने वाले, ऊपर उठे हुए बटन का भी रंग बदलता है. यह हार्ड-कोडिंग वैल्यू के बजाय, पूरे ऐप्लिकेशन में 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
प्रॉपर्टी एक बड़ी स्टाइल है, जिसका इस्तेमाल टेक्स्ट दिखाने के लिए किया जाता है. यहां डिसप्ले शब्द का इस्तेमाल टाइपोग्राफ़िक के तौर पर किया गया है, जैसे कि डिसप्ले टाइपफ़ेस में.displayMedium
के दस्तावेज़ में बताया गया है कि "डिसप्ले स्टाइल, छोटे और ज़रूरी टेक्स्ट के लिए रिज़र्व होती हैं"—यह सुविधा असल में हमारे इस्तेमाल के उदाहरण है.- थीम की
displayMedium
प्रॉपर्टीnull
हो सकती है. Dart, जिस प्रोग्रामिंग भाषा में इस ऐप्लिकेशन को लिखा जा रहा है वह शून्य से सुरक्षित है. इसलिए, यह आपको ऑब्जेक्ट के उन तरीकों को कॉल नहीं करने देगा जो संभावित तौर परnull
हो सकते हैं. हालांकि, इस मामले में आप!
ऑपरेटर ("बैंग ऑपरेटर") का इस्तेमाल करके यह पक्का कर सकते हैं कि Dart को जानकारी है कि आप क्या कर रहे हैं. (इस मामले मेंdisplayMedium
निश्चित तौर पर शून्य नहीं है. हालांकि, हमें यह पता चला है कि यह कोडलैब के दायरे से बाहर है.) displayMedium
कोcopyWith()
को कॉल करने से, आपके तय किए गए बदलावों के साथ टेक्स्ट स्टाइल की कॉपी दिखेगी. इस मामले में, सिर्फ़ टेक्स्ट का रंग बदला जा रहा है.- नया रंग पाने के लिए, आपको एक बार फिर से ऐप्लिकेशन की थीम का ऐक्सेस मिलेगा. कलर स्कीम की
onPrimary
प्रॉपर्टी ऐसे रंग के बारे में बताती है जो ऐप्लिकेशन के मुख्य रंग पर इस्तेमाल करने के लिए सही होता है.
ऐप्लिकेशन अब कुछ ऐसा दिखना चाहिए:
अगर आपको ऐसा लगता है, तो कार्ड की जानकारी में और बदलाव करें. यहां कुछ आइडिया दिए गए हैं:
copyWith()
से आपको टेक्स्ट के रंग के साथ-साथ, टेक्स्ट की स्टाइल में भी बहुत कुछ बदलाव करने की सुविधा मिलती है. बदली जा सकने वाली प्रॉपर्टी की पूरी सूची पाने के लिए, अपने कर्सर कोcopyWith()
के ब्रैकेट में कहीं भी रखें औरCtrl+Shift+Space
(Win/Linux) याCmd+Shift+Space
(Mac) दबाएं.- इसी तरह,
Card
विजेट के बारे में ज़्यादा जानकारी दी जा सकती है. उदाहरण के लिए,elevation
पैरामीटर की वैल्यू बढ़ाकर, कार्ड के शैडो को बड़ा किया जा सकता है. - रंगों के साथ एक्सपेरिमेंट करके देखें.
theme.colorScheme.primary
के अलावा,.secondary
,.surface
, और कई अन्य कार्यक्रम हैं. इन सभी रंगों केonPrimary
जैसे हैं.
सुलभता को बेहतर बनाना
Flutter का इस्तेमाल करके, ऐप्लिकेशन को डिफ़ॉल्ट रूप से ऐक्सेस किया जा सकता है. उदाहरण के लिए, हर Flutter ऐप्लिकेशन, स्क्रीन रीडर जैसे TalkBack और VoiceOver जैसे टेक्स्ट और इंटरैक्टिव एलिमेंट को सही तरीके से दिखाता है.
हालांकि, कभी-कभी कुछ काम करना पड़ता है. ऐसे ऐप्लिकेशन में, स्क्रीन रीडर को जनरेट किए गए कुछ वर्ड पेयर को बोलने में समस्याएं हो सकती हैं. हालांकि, लोगों को 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
के मुख्य (वर्टिकल) ऐक्सिस पर सेंटर करता है.
चाइल्ड एंट्री कॉलम के क्रॉस ऐक्सिस पर पहले से ही फ़ोकस में होती हैं (दूसरे शब्दों में, वे पहले से ही हॉरिज़ॉन्टल तौर पर सेंटर में होती हैं). हालांकि, Column
खुद Scaffold
के अंदर नहीं है. हम विजेट की जांच करने वाले टूल का इस्तेमाल करके, इसकी पुष्टि कर सकते हैं.
विजेट इंस्पेक्टर खुद इस कोडलैब के दायरे से बाहर है. हालांकि, यह देखा जा सकता है कि Column
को हाइलाइट करने पर, यह ऐप्लिकेशन की पूरी चौड़ाई का इस्तेमाल नहीं करता. यह सिर्फ़ उतना ही हॉरिज़ॉन्टल स्पेस लेता है जितना इसके बच्चों की ज़रूरत है.
सिर्फ़ कॉलम को बीच में रखा जा सकता है. अपना कर्सर Column
पर रखें, रिफ़ैक्टर मेन्यू (Ctrl+.
या Cmd+.
के साथ) को कॉल करें और सेंटर के साथ रैप करें को चुनें.
ऐप्लिकेशन अब कुछ ऐसा दिखना चाहिए:
अगर आप चाहें, तो इसमें थोड़ा और बदलाव कर सकते हैं.
BigCard
के ऊपर दिया गयाText
विजेट हटाया जा सकता है. यह तर्क दिया जा सकता है कि जानकारी देने वाले टेक्स्ट ("एक रैंडम AWESOME आइडिया:") की अब ज़रूरत नहीं है, क्योंकि इसके बिना भी यूज़र इंटरफ़ेस (यूआई) समझ में आता है. और इस तरह से यह ज़्यादा साफ़ होता है.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'),
),
],
),
),
);
}
}
// ...
ऐप्लिकेशन कुछ ऐसा दिखता है:
अगले सेक्शन में, जनरेट किए गए शब्दों को पसंदीदा (या ‘पसंद’) करने की सुविधा जोड़ी जाएगी.
6. फ़ंक्शन जोड़ें
यह ऐप्लिकेशन काम करता है और कभी-कभी इसमें दिलचस्प वर्ड पेयर भी मिलते हैं. हालांकि, जब उपयोगकर्ता आगे बढ़ें पर क्लिक करता है, तो शब्दों का हर जोड़ा हमेशा के लिए हट जाता है. "याद रखने" का एक तरीका होना बेहतर होगा सबसे अच्छे सुझाव: जैसे कि 'पसंद करें' बटन.
कारोबार का लॉजिक जोड़ें
स्क्रोल करके 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
के अलावा कुछ और जोड़ने की कोशिश करते हैं, तो वह आपके ऐप्लिकेशन को चलाना भी नहीं छोड़ता. इसके बदले में,favorites
सूची का इस्तेमाल करके यह पता लगाया जा सकता है कि इसमें कभी भी कोई अनचाही चीज़ नहीं छिप सकती, जैसे किnull
.
- आपने एक नई विधि भी जोड़ी है,
toggleFavorite()
, जो या तो पसंदीदा की सूची से वर्तमान शब्द युग्म को निकाल देती है (अगर वह पहले से वहां मौजूद है) या उसे जोड़ देती है (अगर वह अभी तक वहां नहीं है). दोनों ही मामलों में, कोड बाद मेंnotifyListeners();
को कॉल करता है.
बटन जोड़ें
"कारोबार के नियम" सेटिंग के साथ अब यूज़र इंटरफ़ेस पर फिर से काम किया जा सकता है. 'पसंद करें' बटन को क्रम से लगाना ‘आगे बढ़ें’ बटन पर क्लिक करें बटन के लिए Row
की ज़रूरत है. Row
विजेट, Column
के बराबर हॉरिज़ॉन्टल है, जिसे आपने पहले देखा था.
सबसे पहले, मौजूदा बटन को Row
में रैप करें. MyHomePage
के build()
वाले तरीके पर जाएं, अपना कर्सर ElevatedButton
पर रखें, Ctrl+.
या Cmd+.
के साथ रीफ़ैक्टर मेन्यू को कॉल करें, और लाइन के साथ रैप करें को चुनें.
सेव करते समय, आप देखेंगे कि 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'),
),
],
),
],
),
),
);
}
}
// ...
यूज़र इंटरफ़ेस (यूआई), वहीं पर मौजूद है जहां वह पहले था.
इसके बाद, पसंद करें बटन जोड़ें और उसे toggleFavorite()
से कनेक्ट करें. चुनौती पूरी करने के लिए, पहले नीचे दिए गए कोड ब्लॉक को देखे बिना, खुद ही इसे करने की कोशिश करें.
अगर आप इसे ठीक उसी तरह न करें जैसा कि नीचे बताया गया है, तो कोई समस्या नहीं है. अगर आपको कोई बड़ी चुनौती नहीं चाहिए, तो दिल वाले आइकॉन की चिंता न करें.
असफल होने में भी कोई बुराई नहीं है. आखिरकार, Flutter के साथ आपका यह पहला घंटा है.
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
लागू करनी होगी.
इस चरण को पूरा करने के लिए, 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'),
),
],
),
],
),
);
}
}
// ...
सेव करने के बाद, आपको दिखेगा कि यूज़र इंटरफ़ेस (यूआई) का विज़ुअल साइड तैयार है—लेकिन यह काम नहीं करता. नेविगेशन रेल में ♥︎ (दिल का आइकॉन) क्लिक करने से कुछ नहीं होता.
बदलावों की जांच करें.
- सबसे पहले, ध्यान दें कि
MyHomePage
का पूरा कॉन्टेंट एक नए विजेट,GeneratorPage
में एक्सट्रैक्ट किया गया है. पुरानेMyHomePage
विजेट का सिर्फ़Scaffold
ही एक हिस्सा है, जो निकाला नहीं जा सका. - नए
MyHomePage
में एकRow
है, जिसमें दो बच्चे हैं. पहला विजेटSafeArea
है और दूसराExpanded
विजेट है. SafeArea
यह पक्का करता है कि उसका बच्चा किसी हार्डवेयर नॉच या स्टेटस बार से छिप न गया हो. इस ऐप्लिकेशन में बैज,NavigationRail
के चारों ओर रैप हो जाता है, ताकि मोबाइल स्टेटस बार की वजह से नेविगेशन बटन छिप न जाएं.- नेविगेशन रेल में
extended: false
लाइन कोtrue
में बदला जा सकता है. यह आइकॉन के बगल में मौजूद लेबल दिखाता है. आगे के चरण में, आपको ऐप्लिकेशन में ज़रूरत के मुताबिक हॉरिज़ॉन्टल स्पेस होने पर, इस प्रोसेस को अपने-आप करने का तरीका बताया जाएगा. - नेविगेशन रेल में दो मंज़िलें (होम और पसंदीदा) हैं, जिनके आइकॉन और लेबल अलग-अलग हैं. यह मौजूदा
selectedIndex
के बारे में भी जानकारी देता है. शून्य का चुना गया इंडेक्स, पहले डेस्टिनेशन को चुनता है. एक इंडेक्स में से किसी एक इंडेक्स को चुना जाता है. दूसरा डेस्टिनेशन चुना जाता है और यह क्रम इसी तरह जारी रहता है. फ़िलहाल, इसे शून्य पर हार्ड कोड किया गया है. - नेविगेशन रेल से यह भी तय होता है कि जब उपयोगकर्ता
onDestinationSelected
के साथ किसी डेस्टिनेशन को चुनता है, तो क्या होता है. फ़िलहाल, ऐप्लिकेशन सिर्फ़print()
के साथ अनुरोध की गई इंडेक्स वैल्यू दिखाता है. Row
का दूसरा चाइल्ड विजेट,Expanded
विजेट है. बड़े किए गए विजेट, लाइनों और कॉलम में बहुत काम के होते हैं. इनकी मदद से, कुछ ऐसे लेआउट बनाए जा सकते हैं जिनमें कुछ बच्चे अपनी ज़रूरत के हिसाब से ही जगह लेते हैं (इस मामले मेंSafeArea
). वहीं, दूसरे विजेट, खाली जगह (इस मामले मेंExpanded
) का ज़्यादा से ज़्यादा इस्तेमाल कर सकते हैं.Expanded
विजेट के बारे में सोचने का एक तरीका यह है कि वे "लालची" होते हैं. अगर आपको इस विजेट की भूमिका को बेहतर तरीके से समझना है, तोSafeArea
विजेट को किसी अन्यExpanded
के साथ रैप करके देखें. इससे बनने वाला लेआउट कुछ ऐसा दिखता है:
Expanded
के दो विजेट, जो उपलब्ध हॉरिज़ॉन्टल स्पेस को उनके बीच बांटते हैं. हालांकि, नेविगेशन रेल को बाईं ओर थोड़ा स्लाइस करने की ही ज़रूरत पड़ती थी.Expanded
विजेट में, रंगीनContainer
और कंटेनर के अंदरGeneratorPage
मौजूद है.
स्टेटलेस बनाम स्टेटफ़ुल विजेट
अब तक, MyAppState
में आपके राज्य की सभी ज़रूरतें पूरी की गई हैं. इसलिए, आपने अब तक जो विजेट लिखे हैं वे स्टेटलेस हैं. उनमें अपनी कोई म्यूटेबल स्थिति नहीं होती. कोई भी विजेट खुद नहीं बदल सकता—उसे MyAppState
से गुज़रना होगा.
यह बदलने वाला है.
आपके पास नेविगेशन रेल के selectedIndex
की वैल्यू को होल्ड करने का कोई तरीका होना चाहिए. आपको onDestinationSelected
कॉलबैक में जाकर भी इस वैल्यू को बदलना है.
आप selectedIndex
को MyAppState
की दूसरी प्रॉपर्टी के तौर पर जोड़ सकते हैं. और यह काम भी करेगा. हालांकि, यह कल्पना भी की जा सकती है कि अगर हर विजेट की वैल्यू इसमें सेव होती है, तो ऐप्लिकेशन की मौजूदा स्थिति और बेहतर हो जाएगी.
कुछ राज्य सिर्फ़ एक विजेट के लिए काम के होते हैं. इसलिए, यह उस विजेट के साथ ही रहना चाहिए.
StatefulWidget
डालें. यह एक तरह का विजेट है, जिसमें State
शामिल है. सबसे पहले, MyHomePage
को स्टेटफ़ुल विजेट में बदलें.
अपना कर्सर MyHomePage
की पहली लाइन (जो class MyHomePage...
से शुरू होता है) पर रखें. इसके बाद, Ctrl+.
या Cmd+.
का इस्तेमाल करके रीफ़ैक्टर मेन्यू को कॉल करें. इसके बाद, StatefulWidget में बदलें चुनें.
IDE आपके लिए एक नई क्लास बनाता है, _MyHomePageState
. यह क्लास State
को बढ़ाती है और इसलिए अपने खुद के मान प्रबंधित कर सकती है. (यह खुद बदल सकती है.) यह भी ध्यान दें कि पुराने, स्टेटलेस विजेट की build
विधि (विजेट में रहने के बजाय) _MyHomePageState
पर स्थानांतरित हो गई है. इसे हर हाल में बदला गया है—build
तरीके में कुछ भी नहीं बदला है. यह बस अब कहीं और रहती है.
सेटस्टेट
नए स्टेटफ़ुल विजेट के लिए, सिर्फ़ एक वैरिएबल ट्रैक करना ज़रूरी है: 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(),
),
),
],
),
);
}
}
// ...
बदलावों की जांच करें:
- आपको एक नया वैरिएबल,
selectedIndex
, और0
में शुरू करना है. - इस नए वैरिएबल का इस्तेमाल अब तक मौजूद हार्ड कोड किए गए
0
के बजाय,NavigationRail
डेफ़िनिशन में किया जा सकता है. - जब
onDestinationSelected
कॉलबैक को कॉल किया जाता है, तो कंसोल में सिर्फ़ नई वैल्यू को प्रिंट करने के बजाय, इसेsetState()
कॉल मेंselectedIndex
को असाइन किया जाता है. यह कॉल, पहले इस्तेमाल किए गएnotifyListeners()
तरीके जैसा ही है. इससे यह पक्का होता है कि यूज़र इंटरफ़ेस (यूआई) अपडेट हो गया है.
नेविगेशन रेल अब उपयोगकर्ता के इंटरैक्शन का जवाब देती है. हालांकि, दाईं ओर के बड़े किए गए हिस्से में कोई बदलाव नहीं होता है. ऐसा इसलिए होता है, क्योंकि कोड यह तय करने के लिए 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');
}
// ...
कोड के इस हिस्से की जांच करें:
- यह कोड,
Widget
तरह के एक नए वैरिएबलpage
के बारे में बताता है. - इसके बाद, स्विच स्टेटमेंट,
selectedIndex
की मौजूदा वैल्यू के हिसाब सेpage
को एक स्क्रीन असाइन करता है. - फ़िलहाल, कोई
FavoritesPage
उपलब्ध नहीं है. इसलिए,Placeholder
का इस्तेमाल करें; एक आसान विजेट, जो यूज़र इंटरफ़ेस (यूआई) के उस हिस्से को 'पूरा नहीं हुआ' के तौर पर मार्क करते हुए, क्रॉस किए गए रेक्टैंगल की तरह दिखता है.
- फ़ेल-तेज़ सिद्धांत को लागू करने पर, स्विच स्टेटमेंट यह पक्का करता है कि अगर
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
का इस्तेमाल करके, यह उन्हें अपने-आप लेबल दिखाएगा.
Flutter ऐसे कई विजेट उपलब्ध कराता है जो आपके ऐप्लिकेशन को अपने-आप रिस्पॉन्सिव बनाने में मदद करते हैं. उदाहरण के लिए, Wrap
, Row
या Column
से मिलता-जुलता विजेट है, जो बच्चों को अपने-आप अगली "लाइन" पर ले जाता है वर्टिकल या हॉरिज़ॉन्टल स्पेस न होने पर, इसे "रन" कहते हैं. FittedBox
एक विजेट है, जो आपकी पसंद के हिसाब से, उपलब्ध जगह में अपने-आप फ़िट हो जाता है.
हालांकि, ज़रूरत के मुताबिक जगह होने पर NavigationRail
, लेबल अपने-आप नहीं दिखाता, क्योंकि उसे हर कॉन्टेक्स्ट में जगह के बारे में जानकारी नहीं मिलती. यह आप पर निर्भर करता है कि डेवलपर कौन है.
मान लें कि आपने लेबल सिर्फ़ तब दिखाने का फ़ैसला किया है, जब MyHomePage
की चौड़ाई कम से कम 600 पिक्सल हो.
इस मामले में, LayoutBuilder
का विजेट इस्तेमाल किया जाएगा. आपके पास कितना स्थान उपलब्ध है, इसके आधार पर यह आपको अपना विजेट ट्री बदलने की सुविधा देता है.
एक बार फिर से, VS कोड में Flutter के Refactor मेन्यू का इस्तेमाल करके ज़रूरी बदलाव करें. हालांकि, इस बार समस्या थोड़ी और मुश्किल है:
_MyHomePageState
कीbuild
विधि के अंदर, अपना कर्सरScaffold
पर रखें.Ctrl+.
(Windows/Linux) याCmd+.
(Mac) का इस्तेमाल करके, Refactor मेन्यू को कॉल करें.- Wrap with Builder चुनें और Enter दबाएं.
- जोड़े गए नए
Builder
के नाम कोLayoutBuilder
में बदलें. - कॉलबैक पैरामीटर सूची को
(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
में स्ट्रिंग की सूची शामिल है, तो आपको ऐसा कोड मिल सकता है:
दूसरी ओर, अगर आप फ़ंक्शनल प्रोग्रामिंग से ज़्यादा परिचित हैं, तो Dart आपको messages.map((m) => Text(m)).toList()
की तरह कोड भी लिखने की सुविधा देता है. साथ ही, आपके पास किसी भी समय विजेट की सूची बनाने और उसे build
तरीके में जोड़ने का विकल्प है.
पसंदीदा पेज को खुद जोड़ने का यह फ़ायदा है कि ज़्यादा जानकारी के लिए, खुद फ़ैसले लिए जा सकते हैं. इसका नुकसान यह है कि आपको ऐसी समस्या का सामना करना पड़ सकता है जिसे आप खुद हल न कर पाएं. याद रखें: किसी काम में असफल होना ठीक है. यह सीखने की सबसे अहम चीज़ों में से एक है. किसी को भी यह उम्मीद नहीं होगी कि आप पहले घंटे में Flutter के डेवलपमेंट को बेहतर बना पाएंगे और न ही आपको ऐसा करना चाहिए.
इसके बाद, पसंदीदा पेज को लागू करने का सिर्फ़ एक तरीका होता है. इसे लागू करने का तरीका, (उम्मीद है कि) आपको कोड के साथ काम करने के लिए प्रेरित करेगा—यूज़र इंटरफ़ेस में सुधार करेगा और उसे अपने हिसाब से बनाएगा.
यह रही 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
विजेट के साथ काम न करने वाला एक मचान बनाया और उसे एक रिस्पॉन्सिव और मज़ेदार छोटे ऐप्लिकेशन में बदल दिया.
हमने इन विषयों के बारे में बताया
- Flutter के काम करने के तरीके की बुनियादी बातें
- Flutter में लेआउट बनाना
- उपयोगकर्ता के इंटरैक्शन (जैसे कि बटन दबाने पर) को ऐप्लिकेशन के व्यवहार से कनेक्ट करना
- अपने Flutter कोड को व्यवस्थित रखना
- अपने ऐप्लिकेशन को रिस्पॉन्सिव बनाएं
- एक जैसा रंग-रूप हासिल करना और CANNOT TRANSLATE
आगे क्या?
- इस लैब के दौरान लिखे गए ऐप्लिकेशन के साथ ज़्यादा प्रयोग करें.
- एक ही ऐप्लिकेशन के इस बेहतर वर्शन का कोड देखें और जानें कि आप ऐनिमेशन वाली सूचियां, ग्रेडिएंट, क्रॉस-फ़ेड वगैरह कैसे जोड़ सकते हैं.
- flutter.dev/learn पर जाकर, अपने सीखने के सफ़र को फ़ॉलो करें.