Flutter मोबाइल विकास · पाठ

ब्रांड टोकनों के लिए कस्टम ThemeExtension

ThemeExtension क्लास लिखकर विशेष डिज़ाइन टोकन परिभाषित कीजिए और उनका उपयोग कीजिए।

पाठ 3, कुल 4 में से13 चरण

ब्रांड टोकनों के लिए कस्टम ThemeExtension, CoddyKit पर Flutter मोबाइल विकास का एक निःशुल्क पाठ है। यह 4 में से 3वाँ पाठ है। इस अध्ययन पथ के 3 तक कोई भी पाठ पूरा पढ़ना निःशुल्क है — इसके बाद CoddyKit PRO हर पाठ अनलॉक करता है, साथ ही अंतर्निर्मित कोड संपादक और चौबीसों घंटे एआई शिक्षक के साथ व्यावहारिक अभ्यास भी उपलब्ध कराता है। यह Flutter मोबाइल विकास सीखने के मार्ग का हिस्सा है और आपकी प्रगति वेब तथा CoddyKit ऐप पर सिंक होती रहती है। Flutter मोबाइल विकास पाठ्यक्रम में कुल 4 पाठ शामिल हैं।

ब्रांड टोकन के लिए एक स्थान क्यों आवश्यक है

Material 3 आपको समृद्ध ColorScheme देता है, लेकिन वास्तविक उत्पादों में ऐसे अतिरिक्त डिज़ाइन निर्णय होते हैं जो किसी अंतर्निहित स्लॉट से मेल नहीं खाते: ब्रांड ग्रेडिएंट, सफलता का रंग, प्रचार का उभार देने वाला रंग और कार्ड का कस्टम कोना।

इन्हें वैश्विक स्थिरांकों के रूप में पहले से तय करने पर समस्या आती है, जैसे ही आप हल्के और गहरे मोड का समर्थन करते हैं, क्योंकि एक स्थिरांक सक्रिय ThemeData के साथ बदल नहीं सकता।

  • आप चाहते हैं कि ये मान थीम के अंदर रहें।
  • आप चाहते हैं कि थीम के ऐनिमेशन के दौरान ये सहज रूप से मध्यवर्ती मान लें।
  • आप चाहते हैं कि इन्हें बाकी सभी चीज़ों की तरह Theme.of(context) की सरल शैली में पढ़ा जा सके।

Flutter का समाधान है ThemeExtension।

ThemeExtension क्या है

ThemeExtension<T> एक अमूर्त क्लास है, जिसे उपवर्गित करके आप अपने टाइप किए गए मानों के समूह को ThemeData से जोड़ते हैं।

जेनेरिक पैरामीटर T आपकी अपनी क्लास होती है। इसी से Flutter आपकी एक्सटेंशन को स्ट्रिंग कुंजी के बजाय उसके सटीक प्रकार से संग्रहीत और बाद में प्राप्त कर पाता है।

  • यह प्रकार-सुरक्षित है: Map से कास्ट करने की आवश्यकता नहीं।
  • यह थीम-सचेत है: यह ThemeData के अंदर रहती है, इसलिए हल्के और गहरे मोड में अलग-अलग इंस्टेंस रखे जा सकते हैं।
  • यह ऐनिमेशन का समर्थन करती है: थीम बदलने पर Flutter दो इंस्टेंस को मिलाने के लिए lerp को कॉल करता है।

आपको दो मेथड लागू करने होंगे: copyWith और lerp।

एक्सटेंशन क्लास घोषित करना

सबसे पहले अपनी क्लास को टाइप आर्ग्युमेंट के रूप में देते हुए ThemeExtension का उपवर्ग बनाएँ। हर फ़ील्ड को final रखें, ताकि इंस्टेंस अपरिवर्तनीय रहें।

यहाँ हम एक छोटा ब्रांड पैलेट बनाते हैं: प्रचार का उभार देने वाला रंग, सफलता का रंग और ब्रांड ग्रेडिएंट।

import 'package:flutter/material.dart';

class BrandColors extends ThemeExtension<BrandColors> {
  const BrandColors({
    required this.accent,
    required this.success,
    required this.brandGradient,
  });

  final Color accent;
  final Color success;
  final Gradient brandGradient;

  @override
  ThemeExtension<BrandColors> copyWith({
    Color? accent,
    Color? success,
    Gradient? brandGradient,
  }) {
    return BrandColors(
      accent: accent ?? this.accent,
      success: success ?? this.success,
      brandGradient: brandGradient ?? this.brandGradient,
    );
  }
}

copyWith लागू करना

copyWith कुछ फ़ील्ड बदले हुए एक नया इंस्टेंस लौटाता है। पैटर्न हमेशा एक जैसा होता है: हर पैरामीटर nullable होता है और कॉल करने वाला null दे तो आप this.field पर वापस आते हैं।

  • यह क्लास को अपरिवर्तनीय रखता है — आप कभी बदलाव नहीं करते, बल्कि बदलावों के साथ उसकी प्रतिलिपि बनाते हैं।
  • इसी से उपयोगकर्ता पूरे ऑब्जेक्ट को फिर से बनाए बिना किसी एक टोकन को समायोजित कर पाते हैं।

ध्यान दें कि रिटर्न प्रकार ThemeExtension<BrandColors> है, जो अमूर्त सिग्नेचर से मेल खाता है, भले ही आप ठोस BrandColors बना रहे हों।

सुचारु बदलावों के लिए lerp लागू करना

lerp (रैखिक इंटरपोलेशन) this को 0.0 और 1.0 के बीच के t गुणक के अनुसार दूसरे इंस्टेंस की ओर मिलाता है। थीम ऐनिमेशन के दौरान Flutter इसे कॉल करता है, इसलिए आपके कस्टम टोकन भी अंतर्निहित टोकनों की तरह सहजता से बदलते हैं।

हर प्रकार द्वारा दिए गए स्थिर सहायक का उपयोग करें: Color.lerp और Gradient.lerp। यदि other किसी अलग एक्सटेंशन प्रकार का हो, तो this लौटाकर उससे सुरक्षित रूप से निपटें।

@override
ThemeExtension<BrandColors> lerp(
  covariant ThemeExtension<BrandColors>? other,
  double t,
) {
  if (other is! BrandColors) {
    return this;
  }
  return BrandColors(
    accent: Color.lerp(accent, other.accent, t)!,
    success: Color.lerp(success, other.success, t)!,
    brandGradient: Gradient.lerp(brandGradient, other.brandGradient, t)!,
  );
}

हल्के और गहरे इंस्टेंस परिभाषित करना

चूँकि एक्सटेंशन ThemeData के अंदर रहती है, इसलिए हल्के मोड के लिए एक और गहरे मोड के लिए दूसरा इंस्टेंस बनाएँ। आसान संदर्भ के लिए इन्हें क्लास पर static const के रूप में रखें (या जब कोई मान const से नहीं बनाया जा सकता हो, तब static final के रूप में)।

वैश्विक स्थिरांकों की तुलना में यही मुख्य लाभ है: सक्रिय थीम के आधार पर उसी टोकन नाम से अलग-अलग मान मिलते हैं।

class BrandColors extends ThemeExtension<BrandColors> {
  // ...constructor, fields, copyWith, lerp as before...

  static const light = BrandColors(
    accent: Color(0xFFFF6D00),
    success: Color(0xFF2E7D32),
    brandGradient: LinearGradient(
      colors: [Color(0xFFFF6D00), Color(0xFFFFAB40)],
    ),
  );

  static const dark = BrandColors(
    accent: Color(0xFFFFAB40),
    success: Color(0xFF66BB6A),
    brandGradient: LinearGradient(
      colors: [Color(0xFFFFAB40), Color(0xFFFFD180)],
    ),
  );
}

ThemeData पर एक्सटेंशन पंजीकृत करना

ThemeData के extensions पैरामीटर के ज़रिए इंस्टेंस जोड़ें। इसमें एक्सटेंशन का Iterable लिया जाता है; Flutter उन्हें रनटाइम प्रकार के आधार पर अनुक्रमित करता है।

अपने हल्के ThemeData को हल्का इंस्टेंस और गहरे ThemeData को गहरा इंस्टेंस दें, ताकि प्लेटफ़ॉर्म की चमक के साथ वे अपने-आप बदलें।

MaterialApp(
  theme: ThemeData(
    colorScheme: ColorScheme.fromSeed(seedColor: Colors.deepPurple),
    extensions: const [BrandColors.light],
  ),
  darkTheme: ThemeData(
    colorScheme: ColorScheme.fromSeed(
      seedColor: Colors.deepPurple,
      brightness: Brightness.dark,
    ),
    extensions: const [BrandColors.dark],
  ),
  home: const HomePage(),
);

विजेट में एक्सटेंशन का उपयोग करना

अपनी एक्सटेंशन को Theme.of(context).extension<BrandColors>() से पढ़ें। जेनेरिक प्रकार आर्ग्युमेंट खोज कुंजी का काम करता है और nullable BrandColors? लौटाता है।

इंस्टेंस मिलने के बाद हर टोकन एक साधारण, टाइप किए गए फ़ील्ड की तरह होता है — कास्ट की आवश्यकता नहीं और पूरा ऑटोकम्प्लीट उपलब्ध रहता है।

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

  @override
  Widget build(BuildContext context) {
    final brand = Theme.of(context).extension<BrandColors>()!;
    return Container(
      decoration: BoxDecoration(
        gradient: brand.brandGradient,
        borderRadius: BorderRadius.circular(16),
      ),
      padding: const EdgeInsets.all(16),
      child: Text(
        'Limited offer',
        style: TextStyle(color: brand.accent),
      ),
    );
  }
}

उपयोग के लिए साफ़-सुथरा एक्सटेंशन

हर जगह Theme.of(context).extension<BrandColors>()! लिखना अनावश्यक रूप से लंबा है। एक सामान्य तरीका है कि छोटा-सा BuildContext एक्सटेंशन जोड़कर खोज को एक गेटर के पीछे छिपा दिया जाए।

  • यह non-null assertion को एक ही स्थान पर केंद्रीकृत करता है।
  • कॉल करने की जगह साफ़-सुथरी होकर context.brand.accent बन जाती है।
extension BrandThemeX on BuildContext {
  BrandColors get brand =>
      Theme.of(this).extension<BrandColors>()!;
}

// Usage inside any build method:
// final color = context.brand.success;

lerp का शुद्ध-Dart मानसिक मॉडल

Flutter के बिना भी आप lerp को समझ सकते हैं। इंटरपोलेशन हर चैनल पर a + (b - a) * t लागू करना है। नीचे दिया गया स्निपेट दो पूर्णांकों को उसी तरह मिलाता है, जैसे Color.lerp हर ARGB चैनल को मिलाता है।

इसे चलाने पर दिखता है कि t = 0 से आरंभिक मान, t = 1 से अंतिम मान और t = 0.5 से मध्य मान मिलता है — यही वह व्यवहार है जिस पर आपकी थीम का ऐनिमेशन निर्भर करता है।

int lerpInt(int a, int b, double t) {
  return (a + (b - a) * t).round();
}

void main() {
  const start = 0; // think: red channel of color A
  const end = 200; // red channel of color B
  for (final t in [0.0, 0.25, 0.5, 0.75, 1.0]) {
    print('t=$t -> ${lerpInt(start, end, t)}');
  }
}

कई एक्सटेंशन और सामान्य समस्याएँ

आप कई एक्सटेंशन को साथ-साथ पंजीकृत कर सकते हैं — जैसे BrandColors और रेडियस व स्पेसिंग के लिए अलग BrandShapes। Flutter हर एक्सटेंशन की अपनी प्रकार-कुंजी रखता है, इसलिए उनमें टकराव नहीं होता।

  • पंजीकृत करना भूल जाना: यदि आपने ThemeData.extensions में extension<BrandColors>() नहीं जोड़ा है, तो यह null लौटाता है; इसके बाद ! त्रुटि उत्पन्न करता है।
  • lerp छोड़ देना: यदि lerp केवल this लौटाता है, तो थीम के बदलाव सहज रूप से घुलने के बजाय अचानक हो जाते हैं।
  • गलत प्रकार आर्ग्युमेंट: extension<T>() में दिया गया प्रकार पंजीकृत क्लास से बिल्कुल मेल खाना चाहिए।
ThemeData(
  extensions: const [
    BrandColors.light,
    BrandShapes.standard,
  ],
);

// Look each one up independently:
// final brand = Theme.of(context).extension<BrandColors>()!;
// final shapes = Theme.of(context).extension<BrandShapes>()!;

त्वरित जाँच

आपने अपने हल्के और गहरे ThemeData में BrandColors जोड़ा और अब उनके बीच ऐनिमेशन कर रहे हैं। ब्रांड का accent रंग सहज रूप से घुलने के बजाय अचानक बदल जाता है। इसकी सबसे संभावित वजह कौन-सी गलती है?

पुनरावलोकन

अब Flutter में कस्टम थीम बनाने की पूरी कार्यप्रणाली आपके पास है:

  • अपने ब्रांड टोकनों के लिए final फ़ील्ड वाली ThemeExtension<T> का उपवर्ग बनाएँ।
  • copyWith लागू करें (nullable पैरामीटर और this.field पर फ़ॉलबैक) तथा lerp लागू करें (Color.lerp, Gradient.lerp का उपयोग करें और प्रकार की जाँच करें)।
  • अलग-अलग light और dark इंस्टेंस बनाएँ और उन्हें ThemeData.extensions के ज़रिए पंजीकृत करें।
  • Theme.of(context).extension<BrandColors>() से उपयोग करें, बेहतर हो तो इसे context.brand गेटर में लपेट दें।

लाभ यह है कि आपको ब्रांड-विशिष्ट डिज़ाइन टोकन मिलते हैं जो प्रकार-सुरक्षित, मोड-सचेत और सहज रूप से ऐनिमेट होते हैं — ठीक Material 3 के अंतर्निहित ColorScheme की तरह।

शुरुआत निःशुल्क

एआई शिक्षक के साथ Dart सीखें — निःशुल्क

अपने ब्राउज़र में वास्तविक कोड लिखें और चलाएँ, चौबीसों घंटे एआई शिक्षक से तुरंत सहायता पाएँ, और वेब या ऐप पर वहीं से शुरू करें जहाँ आपने छोड़ा था।

पाठ्यक्रम
22
पाठ
88

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

क्या “ब्रांड टोकनों के लिए कस्टम ThemeExtension” पाठ निःशुल्क है?

हाँ — Flutter मोबाइल विकास अध्ययन पथ के 3 तक कोई भी पाठ, जिसमें “ब्रांड टोकनों के लिए कस्टम ThemeExtension” भी शामिल है, यहाँ वेब पर पूरा पढ़ना निःशुल्क है। इसके बाद CoddyKit PRO हर पाठ अनलॉक करता है, साथ ही अंतर्निर्मित कोड संपादक और चौबीसों घंटे एआई शिक्षक के साथ इंटरैक्टिव अभ्यास भी उपलब्ध कराता है। Flutter मोबाइल विकास पाठ्यक्रम में कुल 4 पाठ शामिल हैं।

“ब्रांड टोकनों के लिए कस्टम ThemeExtension” में मैं क्या सीखूँगा?

ThemeExtension क्लास लिखकर विशेष डिज़ाइन टोकन परिभाषित कीजिए और उनका उपयोग कीजिए। आप ब्राउज़र में सीधे चलाए जाने वाले व्यावहारिक कोड के साथ Flutter मोबाइल विकास का अभ्यास करते हैं, और पाठ पूरा करते समय 24/7 एआई ट्यूटर आपके प्रश्नों के उत्तर देता है।

क्या Flutter मोबाइल विकास शुरू करने के लिए मुझे किसी अनुभव की आवश्यकता है?

पहले के अनुभव की आवश्यकता नहीं है। CoddyKit पर Flutter मोबाइल विकास शुरुआती से लेकर उन्नत शिक्षार्थियों तक सभी के लिए व्यवस्थित किया गया है, इसलिए आप यहीं से या शुरुआत से सीखना शुरू कर सकते हैं और अपनी गति से आगे बढ़ सकते हैं। यह 4 में से 3वाँ पाठ है।

“ब्रांड टोकनों के लिए कस्टम ThemeExtension” पाठ पूरा करने में कितना समय लगता है?

CoddyKit का अधिकांश पाठ लगभग 5–10 मिनट में पूरा हो जाता है। हर पाठ छोटा और संवादात्मक है, इसलिए आप लगातार प्रगति करते हैं और वेब या ऐप पर वहीं से सीखना जारी रख सकते हैं जहाँ आपने छोड़ा था।

क्या मैं इस Flutter मोबाइल विकास पाठ में कोड लिख और चला सकता हूँ?

हाँ। हर Flutter मोबाइल विकास पाठ में एक अंतर्निर्मित कोड संपादक शामिल है, जिससे आप सीधे अपने ब्राउज़र में वास्तविक कोड लिख और चला सकते हैं और तुरंत एआई प्रतिक्रिया पा सकते हैं—स्थानीय सेटअप की आवश्यकता नहीं है।

इस पाठ्यक्रम के सभी पाठ

  1. Material 3 रंग योजनाएँ और बीज रंग
  2. गतिशील रंग और अनुकूलनीय हल्की/गहरी थीम
  3. ब्रांड टोकनों के लिए कस्टम ThemeExtension
  4. उत्तरदायी टाइपोग्राफ़ी और घटक थीमिंग
← Flutter मोबाइल विकास पर वापस जाएँ