Flutter में ऐनिमेशन

1. परिचय

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

Flutter के ऐनिमेशन फ़्रेमवर्क के बारे में खास जानकारी

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

  • इंप्लिसिट ऐनिमेशन, पहले से बने ऐनिमेशन इफ़ेक्ट होते हैं. ये पूरे ऐनिमेशन को अपने-आप चलाते हैं. जब ऐनिमेशन की टारगेट वैल्यू बदलती है, तो यह ऐनिमेशन को मौजूदा वैल्यू से टारगेट वैल्यू तक चलाता है. साथ ही, बीच में हर वैल्यू दिखाता है, ताकि विजेट आसानी से ऐनिमेट हो सके. इंप्लिसिट ऐनिमेशन के उदाहरणों में AnimatedSize, AnimatedScale, और AnimatedPositioned शामिल हैं.
  • अश्लील ऐनिमेशन भी पहले से बने ऐनिमेशन इफ़ेक्ट होते हैं. हालांकि, इनका इस्तेमाल करने के लिए, Animation ऑब्जेक्ट की ज़रूरत होती है. उदाहरण के लिए, SizeTransition, ScaleTransition या PositionedTransition.
  • ऐनिमेशन एक क्लास है, जो चल रहे या बंद किए गए ऐनिमेशन को दिखाती है. यह वैल्यू से बनी होती है, जो उस टारगेट वैल्यू को दिखाती है जिस पर ऐनिमेशन चल रहा है. साथ ही, इसमें स्टेटस भी होता है, जो किसी भी समय स्क्रीन पर ऐनिमेशन की मौजूदा वैल्यू दिखाता है. यह Listenable का सबक्लास है. ऐनिमेशन चलने के दौरान स्टेटस में बदलाव होने पर, यह अपने लिसनर को सूचना देता है.
  • AnimationController, ऐनिमेशन बनाने और उसकी स्थिति को कंट्रोल करने का एक तरीका है. forward(), reset(), stop(), और repeat() जैसे तरीकों का इस्तेमाल करके, ऐनिमेशन को कंट्रोल किया जा सकता है. इसके लिए, आपको ऐनिमेशन के असर को तय करने की ज़रूरत नहीं होती. जैसे, स्केल, साइज़ या पोज़िशन.
  • Tweens का इस्तेमाल, शुरुआत और आखिर की वैल्यू के बीच वैल्यू को इंटरपोलेट करने के लिए किया जाता है. साथ ही, इनका इस्तेमाल किसी भी तरह की वैल्यू के लिए किया जा सकता है, जैसे कि डबल, Offset या Color.
  • कर्व का इस्तेमाल, समय के साथ किसी पैरामीटर में होने वाले बदलाव की दर में बदलाव करने के लिए किया जाता है. जब कोई ऐनिमेशन चलता है, तो आम तौर पर ईज़िंग कर्व लागू किया जाता है, ताकि ऐनिमेशन की शुरुआत या आखिर में बदलाव की दर को तेज़ या धीमा किया जा सके. कर्व, 0.0 से 1.0 के बीच की इनपुट वैल्यू लेते हैं और 0.0 से 1.0 के बीच की आउटपुट वैल्यू दिखाते हैं.

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

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

3026390ad413769c.gif

आपको ये करने का तरीका दिखेगा...

  • ऐसा विजेट बनाएं जिसका साइज़ और रंग ऐनिमेशन के साथ बदलता हो
  • 3D कार्ड फ़्लिप इफ़ेक्ट बनाना
  • ऐनिमेशन पैकेज में पहले से मौजूद बेहतरीन ऐनिमेशन इफ़ेक्ट का इस्तेमाल करना
  • Android के नए वर्शन पर उपलब्ध, पीछे जाने पर झलक दिखाने वाले हाथ के जेस्चर की सुविधा जोड़ें

आपको इनके बारे में जानकारी मिलेगी

इस कोडलैब में आपको ये चीज़ें सीखने को मिलेंगी:

  • ऐनिमेशन वाले इफ़ेक्ट का इस्तेमाल करने का तरीका. इससे, बहुत सारे कोड के बिना बेहतरीन ऐनिमेशन बनाए जा सकते हैं.
  • AnimatedSwitcher या AnimationController जैसे पहले से बने ऐनिमेट किए गए विजेट का इस्तेमाल करके, अपने इफ़ेक्ट कॉन्फ़िगर करने के लिए, ऐनिमेशन वाले इफ़ेक्ट का इस्तेमाल करने का तरीका.
  • 3D इफ़ेक्ट दिखाने वाला अपना विजेट बनाने के लिए, AnimationController का इस्तेमाल करने का तरीका.
  • कम से कम सेटअप के साथ शानदार ऐनिमेशन इफ़ेक्ट दिखाने के लिए, animations पैकेज का इस्तेमाल करने का तरीका.

आपको इन चीज़ों की ज़रूरत होगी

  • Flutter SDK टूल
  • कोई IDE, जैसे कि VSCode या Android Studio / IntelliJ

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

इस लैब को पूरा करने के लिए, आपके पास दो सॉफ़्टवेयर होने चाहिए — Flutter SDK टूल और एडिटर.

इनमें से किसी भी डिवाइस का इस्तेमाल करके, कोडलैब चलाया जा सकता है:

  • आपके कंप्यूटर से कनेक्ट किया गया Android (सातवें चरण में, अनुमानित बैक को लागू करने के लिए सुझाया गया) या iOS डिवाइस, जो डेवलपर मोड पर सेट हो.
  • iOS सिम्युलेटर (इसके लिए, Xcode टूल इंस्टॉल करने की ज़रूरत है).
  • Android एमुलेटर (Android Studio में सेटअप करना ज़रूरी है).
  • ब्राउज़र (डीबग करने के लिए Chrome ज़रूरी है).
  • Windows, Linux या macOS डेस्कटॉप कंप्यूटर. आपको उस प्लैटफ़ॉर्म पर ऐप्लिकेशन बनाना होगा जिस पर आपको उसे डिप्लॉय करना है. इसलिए, अगर आपको Windows डेस्कटॉप ऐप्लिकेशन बनाना है, तो आपको सही बिल्ड चेन ऐक्सेस करने के लिए, Windows पर डेवलप करना होगा. ऑपरेटिंग सिस्टम के हिसाब से कुछ ज़रूरी शर्तें होती हैं. इनके बारे में ज़्यादा जानकारी docs.flutter.dev/desktop पर दी गई है.

अपने इंस्टॉलेशन की पुष्टि करना

Flutter Doctor टूल का इस्तेमाल करके, यह पुष्टि करें कि आपका Flutter SDK टूल सही तरीके से कॉन्फ़िगर किया गया है और आपने ऊपर दिए गए टारगेट प्लैटफ़ॉर्म में से कम से कम एक को इंस्टॉल किया है:

$ flutter doctor

Doctor summary (to see all details, run flutter doctor -v):
[✓] Flutter (Channel stable, 3.24.2, on macOS 14.6.1 23G93 darwin-arm64, locale
    en)
[✓] Android toolchain - develop for Android devices
[✓] Xcode - develop for iOS and macOS
[✓] Chrome - develop for the web
[✓] Android Studio
[✓] IntelliJ IDEA Ultimate Edition
[✓] VS Code
[✓] Connected device (4 available)
[✓] Network resources

• No issues found!

3. स्टार्टर ऐप्लिकेशन चलाना

स्टार्टर ऐप्लिकेशन डाउनलोड करना

GitHub पर flutter/samples डेटा स्टोर करने की जगह से, स्टार्ट ऐप्लिकेशन को क्लोन करने के लिए git का इस्तेमाल करें.

$ git clone https://github.com/flutter/codelabs.git
$ cd codelabs/animations/step_01/

इसके अलावा, सोर्स कोड को .zip फ़ाइल के तौर पर डाउनलोड किया जा सकता है.

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

ऐप्लिकेशन चलाने के लिए, flutter run कमांड का इस्तेमाल करें और टारगेट किए गए डिवाइस की जानकारी दें, जैसे कि android, ios या chrome. जिन प्लैटफ़ॉर्म पर यह सुविधा काम करती है उनकी पूरी सूची देखने के लिए, इस्तेमाल किए जा सकने वाले प्लैटफ़ॉर्म पेज पर जाएं.

$ flutter run -d android

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

कोड के बारे में जानकारी

स्टार्टर ऐप्लिकेशन, एक मल्टी-चॉइस क्विज़ गेम है. इसमें मॉडल-व्यू-व्यू-मॉडल या MVVM डिज़ाइन पैटर्न के हिसाब से दो स्क्रीन हैं. QuestionScreen (व्यू), QuizViewModel (व्यू-मॉडल) क्लास का इस्तेमाल करके, उपयोगकर्ता से QuestionBank (मॉडल) क्लास के कई विकल्प वाले सवाल पूछता है.

  • home_screen.dart - नया गेम बटन वाली स्क्रीन दिखाता है
  • main.dart - Material 3 का इस्तेमाल करने और होम स्क्रीन दिखाने के लिए, MaterialApp को कॉन्फ़िगर करता है
  • model.dart - पूरे ऐप्लिकेशन में इस्तेमाल की जाने वाली मुख्य क्लास के बारे में बताता है
  • question_screen.dart - क्विज़ गेम का यूज़र इंटरफ़ेस दिखाता है
  • view_model.dart - क्विज़ गेम की स्थिति और लॉजिक को सेव करता है. इसे QuestionScreen दिखाता है

fbb1e1f7b6c91e21.png

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

4. ऐनिमेशन के इम्प्लीसिट इफ़ेक्ट का इस्तेमाल करना

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

206dd8d9c1fae95.gif

ऐनिमेशन वाला स्कोरबोर्ड विजेट बनाना

इस कोड की मदद से, lib/scoreboard.dart नाम की नई फ़ाइल बनाएं:

lib/scoreboard.dart

import 'package:flutter/material.dart';

class Scoreboard extends StatelessWidget {
  final int score;
  final int totalQuestions;

  const Scoreboard({
    super.key,
    required this.score,
    required this.totalQuestions,
  });

  @override
  Widget build(BuildContext context) {
    return Padding(
      padding: const EdgeInsets.all(8.0),
      child: Row(
        mainAxisAlignment: MainAxisAlignment.center,
        children: [
          for (var i = 0; i < totalQuestions; i++)
            Icon(
              Icons.star,
              size: 50,
              color:
                  score < i + 1 ? Colors.grey.shade400 : Colors.yellow.shade700,
            )
        ],
      ),
    );
  }
}

इसके बाद, StatusBar विजेट के चाइल्ड में Scoreboard विजेट जोड़ें. साथ ही, उन Text विजेट की जगह पर Scoreboard विजेट जोड़ें जो पहले स्कोर और सवालों की कुल संख्या दिखाते थे. आपका एडिटर, फ़ाइल में सबसे ऊपर ज़रूरी import "scoreboard.dart" अपने-आप जोड़ देगा.

lib/question_screen.dart

class StatusBar extends StatelessWidget {
  final QuizViewModel viewModel;

  const StatusBar({required this.viewModel, super.key});

  @override
  Widget build(BuildContext context) {
    return Card(
      elevation: 4,
      child: Padding(
        padding: EdgeInsets.all(8.0),
        child: Row(
          mainAxisAlignment: MainAxisAlignment.spaceAround,
          children: [
            Scoreboard(                                        // NEW
              score: viewModel.score,                          // NEW
              totalQuestions: viewModel.totalQuestions,        // NEW
            ),
          ],
        ),
      ),
    );
  }
}

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

ऐनिमेशन के किसी इफ़ेक्ट का इस्तेमाल करना

AnimatedStar नाम का एक नया विजेट बनाएं. यह विजेट, AnimatedScale विजेट का इस्तेमाल करके, स्टार के चालू होने पर scale की वैल्यू को 0.5 से 1.0 में बदलता है:

lib/scoreboard.dart

​​import 'package:flutter/material.dart';

class Scoreboard extends StatelessWidget {
  final int score;
  final int totalQuestions;

  const Scoreboard({
    super.key,
    required this.score,
    required this.totalQuestions,
  });

  @override
  Widget build(BuildContext context) {
    return Padding(
      padding: const EdgeInsets.all(8.0),
      child: Row(
        mainAxisAlignment: MainAxisAlignment.center,
        children: [
          for (var i = 0; i < totalQuestions; i++)
            AnimatedStar(                                      // NEW
              isActive: score > i,                             // NEW
            )                                                  // NEW
        ],
      ),
    );
  }
}

class AnimatedStar extends StatelessWidget {                   // Add from here...
  final bool isActive;
  final Duration _duration = const Duration(milliseconds: 1000);
  final Color _deactivatedColor = Colors.grey.shade400;
  final Color _activatedColor = Colors.yellow.shade700;

  AnimatedStar({super.key, required this.isActive});

  @override
  Widget build(BuildContext context) {
    return AnimatedScale(
      scale: isActive ? 1.0 : 0.5,
      duration: _duration,
      child: Icon(
        Icons.star,
        size: 50,
        color: isActive ? _activatedColor : _deactivatedColor,
      ),
    );
  }
}                                                              // To here.

अब, जब कोई उपयोगकर्ता किसी सवाल का सही जवाब देता है, तो AnimatedStar विजेट, अपने-आप होने वाले ऐनिमेशन का इस्तेमाल करके अपना साइज़ अपडेट करता है. यहां Icon के color में ऐनिमेशन नहीं है, सिर्फ़ scale में है. यह ऐनिमेशन, AnimatedScale विजेट से किया जाता है.

84aec4776e70b870.gif

दो वैल्यू के बीच इंटरपोलेट करने के लिए, ट्वीन का इस्तेमाल करना

ध्यान दें कि isActive फ़ील्ड के 'सही' में बदलने के तुरंत बाद, AnimatedStar विजेट का रंग बदल जाता है.

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

AnimatedIcon को भी आज़माया जा सकता है. इससे आइकॉन के आकार के बीच ट्रांज़िशन इफ़ेक्ट लागू होते हैं. हालांकि, AnimatedIcons क्लास में स्टार आइकॉन डिफ़ॉल्ट रूप से लागू नहीं होता.

इसके बजाय, हम ImplicitlyAnimatedWidget के किसी दूसरे सबक्लास का इस्तेमाल करेंगे, जिसे TweenAnimationBuilder कहा जाता है. यह पैरामीटर के तौर पर Tween लेता है. ट्वीन एक क्लास है, जो दो वैल्यू (begin और end) लेती है और उनके बीच की वैल्यू का हिसाब लगाती है, ताकि ऐनिमेशन उन्हें दिखा सके. इस उदाहरण में, हम ColorTween का इस्तेमाल करेंगे. यह Tween<Color> इंटरफ़ेस के मुताबिक है, जो ऐनिमेशन इफ़ेक्ट बनाने के लिए ज़रूरी है.

Icon विजेट चुनें और अपने आईडीई में "बिल्डर के साथ रैप करें" क्विक ऐक्शन का इस्तेमाल करें. इसके बाद, नाम को TweenAnimationBuilder में बदलें. इसके बाद, कुल समय और ColorTween डालें.

lib/scoreboard.dart

class AnimatedStar extends StatelessWidget {
  final bool isActive;
  final Duration _duration = const Duration(milliseconds: 1000);
  final Color _deactivatedColor = Colors.grey.shade400;
  final Color _activatedColor = Colors.yellow.shade700;

  AnimatedStar({super.key, required this.isActive});

  @override
  Widget build(BuildContext context) {
    return AnimatedScale(
      scale: isActive ? 1.0 : 0.5,
      duration: _duration,
      child: TweenAnimationBuilder(                            // Add from here...
        duration: _duration,
        tween: ColorTween(
          begin: _deactivatedColor,
          end: isActive ? _activatedColor : _deactivatedColor,
        ),
        builder: (context, value, child) {                     // To here.
          return Icon(
            Icons.star,
            size: 50,
            color: value,                                      // Modify from here...
          );
        },                                                     // To here.
      ),
    );
  }
}

अब नया ऐनिमेशन देखने के लिए, ऐप्लिकेशन को हॉट-रीलोड करें.

8b0911f4af299a60.gif

ध्यान दें कि ColorTween पैरामीटर की वैल्यू के आधार पर, ColorTween की end वैल्यू बदल जाती है.isActive ऐसा इसलिए होता है, क्योंकि जब भी Tween.end की वैल्यू बदलती है, तो TweenAnimationBuilder अपना ऐनिमेशन फिर से चलाता है. ऐसा होने पर, नया ऐनिमेशन मौजूदा ऐनिमेशन वैल्यू से लेकर नई आखिरी वैल्यू तक चलता है. इससे, किसी भी समय (ऐनिमेशन चलने के दौरान भी) रंग बदला जा सकता है. साथ ही, बीच की सही वैल्यू के साथ ऐनिमेशन का बेहतर असर दिखाया जा सकता है.

कर्व लागू करना

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

Curve, ईज़िंग फ़ंक्शन लागू करता है. इससे, समय के साथ किसी पैरामीटर में होने वाले बदलाव की दर तय होती है. Flutter, Curves क्लास में पहले से बने ईज़िंग कर्व का कलेक्शन उपलब्ध कराता है. जैसे, easeIn या easeOut.

5dabe68d1210b8a1.gif

3a9e7490c594279a.gif

इन डायग्राम (Curves एपीआई दस्तावेज़ वाले पेज पर उपलब्ध) से पता चलता है कि कर्व कैसे काम करते हैं. कर्व, 0.0 से 1.0 के बीच की इनपुट वैल्यू (x ऐक्सिस पर दिखती है) को 0.0 से 1.0 के बीच की आउटपुट वैल्यू (y ऐक्सिस पर दिखती है) में बदल देते हैं. इन डायग्राम में यह भी दिखाया गया है कि आसानी से बदलने वाले कर्व का इस्तेमाल करने पर, अलग-अलग ऐनिमेशन इफ़ेक्ट कैसा दिखते हैं.

AnimatedStar में _curve नाम का नया फ़ील्ड बनाएं और उसे AnimatedScale और TweenAnimationBuilder विजेट के पैरामीटर के तौर पर पास करें.

lib/scoreboard.dart

class AnimatedStar extends StatelessWidget {
  final bool isActive;
  final Duration _duration = const Duration(milliseconds: 1000);
  final Color _deactivatedColor = Colors.grey.shade400;
  final Color _activatedColor = Colors.yellow.shade700;
  final Curve _curve = Curves.elasticOut;                       // NEW

  AnimatedStar({super.key, required this.isActive});

  @override
  Widget build(BuildContext context) {
    return AnimatedScale(
      scale: isActive ? 1.0 : 0.5,
      curve: _curve,                                           // NEW
      duration: _duration,
      child: TweenAnimationBuilder(
        curve: _curve,                                         // NEW
        duration: _duration,
        tween: ColorTween(
          begin: _deactivatedColor,
          end: isActive ? _activatedColor : _deactivatedColor,
        ),
        builder: (context, value, child) {
          return Icon(
            Icons.star,
            size: 50,
            color: value,
          );
        },
      ),
    );
  }
}

इस उदाहरण में, elasticOut कर्व से स्प्रिंग का बढ़ा-चढ़ाकर दिखाया गया इफ़ेक्ट मिलता है. यह इफ़ेक्ट स्प्रिंग की गति से शुरू होता है और आखिर में बैलेंस हो जाता है.

8f84142bff312373.gif

AnimatedSize और TweenAnimationBuilder पर लागू किया गया यह कर्व देखने के लिए, ऐप्लिकेशन को हॉट रीलोड करें.

206dd8d9c1fae95.gif

धीमे ऐनिमेशन चालू करने के लिए DevTools का इस्तेमाल करना

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

DevTools खोलने के लिए, पक्का करें कि ऐप्लिकेशन डीबग मोड में चल रहा हो. इसके बाद, विजेट इंस्पेक्टर खोलें. इसके लिए, VSCode में डीबग टूलबार में जाकर विजेट इंस्पेक्टर को चुनें या IntelliJ / Android Studio में डीबग टूल विंडो में जाकर, Flutter DevTools खोलें बटन को चुनें.

3ce33dc01d096b14.png

363ae0fbcd0c2395.png

विजेट की जांच करने वाला टूल खुलने के बाद, टूलबार में मौजूद ऐनिमेशन धीमा करें बटन पर क्लिक करें.

adea0a16d01127ad.png

5. अश्लील ऐनिमेशन इफ़ेक्ट का इस्तेमाल करना

इम्प्लीसिट ऐनिमेशन की तरह, एक्सप्लिशिट ऐनिमेशन भी पहले से बने ऐनिमेशन इफ़ेक्ट होते हैं. हालांकि, टारगेट वैल्यू के बजाय, ये पैरामीटर के तौर पर Animation ऑब्जेक्ट लेते हैं. इस वजह से, ये ऐसे मामलों में काम के होते हैं जहां ऐनिमेशन पहले से ही नेविगेशन ट्रांज़िशन, AnimatedSwitcher या AnimationController से तय होता है.

अश्लील ऐनिमेशन इफ़ेक्ट का इस्तेमाल करना

साफ़ तौर पर दिखने वाले ऐनिमेशन इफ़ेक्ट का इस्तेमाल शुरू करने के लिए, Card विजेट को AnimatedSwitcher में रैप करें.

lib/question_screen.dart

class QuestionCard extends StatelessWidget {
  final String? question;

  const QuestionCard({
    required this.question,
    super.key,
  });

  @override
  Widget build(BuildContext context) {
    return AnimatedSwitcher(                                 // NEW
      duration: const Duration(milliseconds: 300),           // NEW
      child: Card(
        key: ValueKey(question),
        elevation: 4,
        child: Padding(
          padding: const EdgeInsets.all(16.0),
          child: Text(
            question ?? '',
            style: Theme.of(context).textTheme.displaySmall,
          ),
        ),
      ),                                                     // NEW
    );
  }
}

AnimatedSwitcher डिफ़ॉल्ट रूप से क्रॉस-फ़ेड इफ़ेक्ट का इस्तेमाल करता है. हालांकि, transitionBuilder पैरामीटर का इस्तेमाल करके, इसे बदला जा सकता है. ट्रांज़िशन बिल्डर, AnimatedSwitcher और Animation ऑब्जेक्ट को पास किया गया चाइल्ड विजेट उपलब्ध कराता है. साफ़ तौर पर दिखाने वाले ऐनिमेशन का इस्तेमाल करने का यह एक शानदार मौका है.

इस कोडलैब में, हम साफ़ तौर पर दिखने वाले पहले ऐनिमेशन के तौर पर SlideTransition का इस्तेमाल करेंगे. इसमें एक Animation<Offset> होता है, जो शुरू और खत्म होने के ऑफ़सेट की जानकारी देता है. इन ऑफ़सेट के बीच, इनकमिंग और आउटगोइंग विजेट मूव करेंगे.

ट्वीन में एक हेल्पर फ़ंक्शन, animate() होता है. यह किसी भी Animation को, ट्वीन के साथ लागू किए गए किसी दूसरे Animation में बदल देता है. इसका मतलब है कि Tween<Offset> का इस्तेमाल, AnimatedSwitcher से मिले Animation<double> को Animation<Offset> में बदलने के लिए किया जा सकता है, ताकि उसे SlideTransition विजेट को दिया जा सके.

lib/question_screen.dart

class QuestionCard extends StatelessWidget {
  final String? question;

  const QuestionCard({
    required this.question,
    super.key,
  });

  @override
  Widget build(BuildContext context) {
    return AnimatedSwitcher(
      transitionBuilder: (child, animation) {               // Add from here...
        final curveAnimation =
            CurveTween(curve: Curves.easeInCubic).animate(animation);
        final offsetAnimation =
            Tween<Offset>(begin: Offset(-0.1, 0.0), end: Offset.zero)
                .animate(curveAnimation);
        return SlideTransition(position: offsetAnimation, child: child);
      },                                                    // To here.
      duration: const Duration(milliseconds: 300),
      child: Card(
        key: ValueKey(question),
        elevation: 4,
        child: Padding(
          padding: const EdgeInsets.all(16.0),
          child: Text(
            question ?? '',
            style: Theme.of(context).textTheme.displaySmall,
          ),
        ),
      ),
    );
  }
}

ध्यान दें कि यह Animation पर Curve लागू करने के लिए Tween.animate का इस्तेमाल करता है. इसके बाद, इसे 0.0 से 1.0 तक की रेंज वाले Tween<double> से, x-ऐक्सिस पर -0.1 से 0.0 तक ट्रांज़िशन करने वाले Tween<Offset> में बदलता है.

इसके अलावा, ऐनिमेशन क्लास में drive() फ़ंक्शन होता है, जो किसी भी Tween (या Animatable) को लेकर उसे नए Animation में बदल देता है. इससे ट्वीन को "चेन" किया जा सकता है, जिससे नतीजा देने वाला कोड ज़्यादा छोटा हो जाता है:

lib/question_screen.dart

transitionBuilder: (child, animation) {
  var offsetAnimation = animation
      .drive(CurveTween(curve: Curves.easeInCubic))
      .drive(Tween<Offset>(begin: Offset(-0.1, 0.0), end: Offset.zero));
  return SlideTransition(position: offsetAnimation, child: child);
},

साफ़ तौर पर सेक्शुअल ऐक्शन दिखाने वाले ऐनिमेशन का एक और फ़ायदा यह है कि उन्हें आसानी से एक साथ कंपोज किया जा सकता है. FadeTransition नाम का एक और साफ़ तौर पर दिखने वाला ऐनिमेशन जोड़ें. यह ऐनिमेशन, SlideTransition विजेट को रैप करके, उसी घुमावदार ऐनिमेशन का इस्तेमाल करता है.

lib/question_screen.dart

return AnimatedSwitcher(
  transitionBuilder: (child, animation) {
    final curveAnimation =
        CurveTween(curve: Curves.easeInCubic).animate(animation);
    final offsetAnimation =
        Tween<Offset>(begin: Offset(-0.1, 0.0), end: Offset.zero)
            .animate(curveAnimation);
    final fadeInAnimation = curveAnimation;                            // NEW
    return FadeTransition(                                             // NEW
      opacity: fadeInAnimation,                                        // NEW
      child: SlideTransition(position: offsetAnimation, child: child), // NEW
    );                                                                 // NEW
  },

layoutBuilder को पसंद के मुताबिक बनाना

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

d77de181bdde58f7.gif

इसे ठीक करने के लिए, AnimatedSwitcher में layoutBuilder पैरामीटर भी है. इसका इस्तेमाल लेआउट तय करने के लिए किया जा सकता है. लेआउट बिल्डर को कॉन्फ़िगर करके, कार्ड को स्क्रीन पर सबसे ऊपर अलाइन करने के लिए, इस फ़ंक्शन का इस्तेमाल करें:

lib/question_screen.dart

@override
Widget build(BuildContext context) {
  return AnimatedSwitcher(
    layoutBuilder: (currentChild, previousChildren) {
      return Stack(
        alignment: Alignment.topCenter,
        children: <Widget>[
          ...previousChildren,
          if (currentChild != null) currentChild,
        ],
      );
    },

यह कोड, AnimatedSwitcher क्लास के defaultLayoutBuilder का बदला हुआ वर्शन है. हालांकि, इसमें Alignment.center के बजाय Alignment.topCenter का इस्तेमाल किया गया है.

खास जानकारी

  • साफ़ तौर पर दिखने वाले ऐनिमेशन, ऐनिमेशन इफ़ेक्ट होते हैं. ये इफ़ेक्ट, ऐनिमेशन ऑब्जेक्ट का इस्तेमाल करते हैं. वहीं, ImplicitlyAnimatedWidgets, टारगेट वैल्यू और अवधि का इस्तेमाल करते हैं
  • ऐनिमेशन क्लास, चल रहे ऐनिमेशन को दिखाती है, लेकिन किसी खास इफ़ेक्ट के बारे में नहीं बताती.
  • किसी ऐनिमेशन में CurveTween का इस्तेमाल करके, ट्रिन और कर्व लागू करने के लिए, Tween().animate या Animation.drive() का इस्तेमाल करें.
  • AnimatedSwitcher के layoutBuilder पैरामीटर का इस्तेमाल करके, यह तय करें कि इसके चाइल्ड एलिमेंट किस तरह दिखें.

6. ऐनिमेशन की स्थिति कंट्रोल करना

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

AnimationController का इस्तेमाल करके ऐनिमेशन चलाना

AnimationController का इस्तेमाल करके ऐनिमेशन बनाने के लिए, आपको यह तरीका अपनाना होगा:

  1. StatefulWidget बनाना
  2. अपने AnimationController को टिकर देने के लिए, अपनी State क्लास में SingleTickerProviderStateMixin मिक्सिन का इस्तेमाल करें
  3. initState लाइफ़साइकल मेथड में AnimationController को शुरू करें. इसके लिए, vsync (TickerProvider) पैरामीटर को मौजूदा State ऑब्जेक्ट दें.
  4. पक्का करें कि जब भी AnimationController अपने लिसनर को सूचना दे, तब आपका विजेट फिर से बन जाए. इसके लिए, AnimatedBuilder का इस्तेमाल करें या listen() और setState को मैन्युअल तरीके से कॉल करें.

flip_effect.dart नाम की एक नई फ़ाइल बनाएं और नीचे दिया गया कोड कॉपी करके चिपकाएं:

lib/flip_effect.dart

import 'dart:math' as math;

import 'package:flutter/widgets.dart';

class CardFlipEffect extends StatefulWidget {
  final Widget child;
  final Duration duration;

  const CardFlipEffect({
    super.key,
    required this.child,
    required this.duration,
  });

  @override
  State<CardFlipEffect> createState() => _CardFlipEffectState();
}

class _CardFlipEffectState extends State<CardFlipEffect>
    with SingleTickerProviderStateMixin {
  late final AnimationController _animationController;
  Widget? _previousChild;

  @override
  void initState() {
    super.initState();

    _animationController =
        AnimationController(vsync: this, duration: widget.duration);

    _animationController.addListener(() {
      if (_animationController.value == 1) {
        _animationController.reset();
      }
    });
  }

  @override
  void didUpdateWidget(covariant CardFlipEffect oldWidget) {
    super.didUpdateWidget(oldWidget);

    if (widget.child.key != oldWidget.child.key) {
      _handleChildChanged(widget.child, oldWidget.child);
    }
  }

  void _handleChildChanged(Widget newChild, Widget previousChild) {
    _previousChild = previousChild;
    _animationController.forward();
  }

  @override
  Widget build(BuildContext context) {
    return AnimatedBuilder(
      animation: _animationController,
      builder: (context, child) {
        return Transform(
          alignment: Alignment.center,
          transform: Matrix4.identity()
            ..rotateX(_animationController.value * math.pi),
          child: _animationController.isAnimating
              ? _animationController.value < 0.5
                  ? _previousChild
                  : Transform.flip(flipY: true, child: child)
              : child,
        );
      },
      child: widget.child,
    );
  }
}

यह क्लास, AnimationController को सेट अप करती है. साथ ही, जब भी फ़्रेमवर्क didUpdateWidget को कॉल करता है, तब ऐनिमेशन को फिर से चलाती है. इससे यह पता चलता है कि विजेट का कॉन्फ़िगरेशन बदल गया है और हो सकता है कि कोई नया चाइल्ड विजेट हो.

AnimatedBuilder यह पक्का करता है कि जब भी AnimationController अपने लिसनर को सूचना दे, तब विजेट ट्री को फिर से बनाया जाए. साथ ही, कार्ड को फ़्लिप करने के लिए, 3D रोटेशन इफ़ेक्ट लागू करने के लिए Transform विजेट का इस्तेमाल किया जाता है.

इस विजेट का इस्तेमाल करने के लिए, हर जवाब कार्ड को CardFlipEffect विजेट में रैप करें. कार्ड विजेट के लिए key देना न भूलें:

lib/question_screen.dart

@override
Widget build(BuildContext context) {
  return GridView.count(
    shrinkWrap: true,
    crossAxisCount: 2,
    childAspectRatio: 5 / 2,
    children: List.generate(answers.length, (index) {
      var color = Theme.of(context).colorScheme.primaryContainer;
      if (correctAnswer == index) {
        color = Theme.of(context).colorScheme.tertiaryContainer;
      }
      return CardFlipEffect(                                    // NEW
        duration: const Duration(milliseconds: 300),            // NEW
        child: Card.filled(                                     // NEW
          key: ValueKey(answers[index]),                        // NEW
          color: color,
          elevation: 2,
          margin: EdgeInsets.all(8),
          clipBehavior: Clip.hardEdge,
          child: InkWell(
            onTap: () => onTapped(index),
            child: Padding(
              padding: EdgeInsets.all(16.0),
              child: Center(
                child: Text(
                  answers.length > index ? answers[index] : '',
                  style: Theme.of(context).textTheme.titleMedium,
                  overflow: TextOverflow.clip,
                ),
              ),
            ),
          ),
        ),                                                      // NEW
      );
    }),
  );
}

अब CardFlipEffect विजेट का इस्तेमाल करके, जवाब वाले कार्ड को पलटते हुए देखने के लिए, ऐप्लिकेशन को हॉट-रीलोड करें.

5455def725b866f6.gif

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

TweenSequence का इस्तेमाल करके देरी जोड़ना

इस सेक्शन में, आपको CardFlipEffect विजेट में देरी जोड़नी होगी, ताकि हर कार्ड एक-एक करके फ़्लिप हो सके. शुरू करने के लिए, delayAmount नाम का नया फ़ील्ड जोड़ें.

lib/flip_effect.dart

class CardFlipEffect extends StatefulWidget {
  final Widget child;
  final Duration duration;
  final double delayAmount;                      // NEW

  const CardFlipEffect({
    super.key,
    required this.child,
    required this.duration,
    required this.delayAmount,                   // NEW
  });

  @override
  State<CardFlipEffect> createState() => _CardFlipEffectState();
}

इसके बाद, AnswerCards बिल्ड करने के तरीके में delayAmount जोड़ें.

lib/question_screen.dart

@override
Widget build(BuildContext context) {
  return GridView.count(
    shrinkWrap: true,
    crossAxisCount: 2,
    childAspectRatio: 5 / 2,
    children: List.generate(answers.length, (index) {
      var color = Theme.of(context).colorScheme.primaryContainer;
      if (correctAnswer == index) {
        color = Theme.of(context).colorScheme.tertiaryContainer;
      }
      return CardFlipEffect(
        delayAmount: index.toDouble() / 2,                     // NEW
        duration: const Duration(milliseconds: 300),
        child: Card.filled(
          key: ValueKey(answers[index]),

इसके बाद, _CardFlipEffectState में एक नया ऐनिमेशन बनाएं, जो TweenSequence का इस्तेमाल करके देरी लागू करता है. ध्यान दें कि यह Future.delayed जैसी dart:async लाइब्रेरी की किसी भी सुविधा का इस्तेमाल नहीं करता. ऐसा इसलिए होता है, क्योंकि देरी ऐनिमेशन का हिस्सा होती है. यह ऐसा नहीं है जिसे विजेट, AnimationController का इस्तेमाल करते समय साफ़ तौर पर कंट्रोल करता हो. इससे, DevTools में स्लो ऐनिमेशन चालू करते समय ऐनिमेशन इफ़ेक्ट को डीबग करना आसान हो जाता है. ऐसा इसलिए, क्योंकि यह एक ही TickerProvider का इस्तेमाल करता है.

TweenSequence का इस्तेमाल करने के लिए, दो TweenSequenceItem बनाएं. इनमें से एक में ConstantTween शामिल करें, जो ऐनिमेशन को किसी तय समय के लिए 0 पर रखता है. साथ ही, एक सामान्य Tween बनाएं, जो 0.0 से 1.0 तक जाता है.

lib/flip_effect.dart

class _CardFlipEffectState extends State<CardFlipEffect>
    with SingleTickerProviderStateMixin {
  late final AnimationController _animationController;
  Widget? _previousChild;
  late final Animation<double> _animationWithDelay; // NEW

  @override
  void initState() {
    super.initState();

    _animationController = AnimationController(
        vsync: this, duration: widget.duration * (widget.delayAmount + 1));

    _animationController.addListener(() {
      if (_animationController.value == 1) {
        _animationController.reset();
      }
    });

    _animationWithDelay = TweenSequence<double>([   // NEW
      if (widget.delayAmount > 0)                   // NEW
        TweenSequenceItem(                          // NEW
          tween: ConstantTween<double>(0.0),        // NEW
          weight: widget.delayAmount,               // NEW
        ),                                          // NEW
      TweenSequenceItem(                            // NEW
        tween: Tween(begin: 0.0, end: 1.0),         // NEW
        weight: 1.0,                                // NEW
      ),                                            // NEW
    ]).animate(_animationController);               // NEW
  }

आखिर में, बिल्ड करने के तरीके में AnimationController के ऐनिमेशन को, देर से चलने वाले नए ऐनिमेशन से बदलें.

lib/flip_effect.dart

@override
Widget build(BuildContext context) {
  return AnimatedBuilder(
    animation: _animationWithDelay,                            // Modify this line
    builder: (context, child) {
      return Transform(
        alignment: Alignment.center,
        transform: Matrix4.identity()
          ..rotateX(_animationWithDelay.value * math.pi),      // And this line
        child: _animationController.isAnimating
            ? _animationWithDelay.value < 0.5                  // And this one.
                ? _previousChild
                : Transform.flip(flipY: true, child: child)
            : child,
      );
    },
    child: widget.child,
  );
}

अब ऐप्लिकेशन को हॉट रीलोड करें और देखें कि कार्ड एक-एक करके कैसे फ़्लिप होते हैं. Transform विजेट से मिलने वाले 3D इफ़ेक्ट के पर्सपेक्टिव को बदलने की कोशिश करें.

28b5291de9b3f55f.gif

7. कस्टम नेविगेशन ट्रांज़िशन का इस्तेमाल करना

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

नेविगेशन ट्रांज़िशन को ऐनिमेट करना

PageRouteBuilder क्लास एक Route है. इसकी मदद से, ट्रांज़िशन ऐनिमेशन को पसंद के मुताबिक बनाया जा सकता है. इससे आपको इसके transitionBuilder कॉलबैक को बदलने की अनुमति मिलती है. यह दो ऐनिमेशन ऑब्जेक्ट उपलब्ध कराता है, जो नेविगेटर से चलाए जाने वाले इनकमिंग और आउटगोइंग ऐनिमेशन को दिखाता है.

ट्रांज़िशन ऐनिमेशन को पसंद के मुताबिक बनाने के लिए, MaterialPageRoute को PageRouteBuilder से बदलें. साथ ही, जब उपयोगकर्ता HomeScreen से QuestionScreen पर जाता है, तो ट्रांज़िशन ऐनिमेशन को पसंद के मुताबिक बनाने के लिए, HomeScreen को QuestionScreen से बदलें. FadeTransition (ऐनिमेशन वाला विजेट) का इस्तेमाल करके, नई स्क्रीन को पिछली स्क्रीन के ऊपर फ़ेड इन करें.

lib/home_screen.dart

ElevatedButton(
  onPressed: () {
    // Show the question screen to start the game
    Navigator.push(
      context,
      PageRouteBuilder(                                         // NEW
        pageBuilder: (context, animation, secondaryAnimation) { // NEW
          return QuestionScreen();                              // NEW
        },                                                      // NEW
        transitionsBuilder:                                     // NEW
            (context, animation, secondaryAnimation, child) {   // NEW
          return FadeTransition(                                // NEW
            opacity: animation,                                 // NEW
            child: child,                                       // NEW
          );                                                    // NEW
        },                                                      // NEW
      ),                                                        // NEW
    );
  },
  child: Text('New Game'),
),

ऐनिमेशन पैकेज में, पहले से बने शानदार ऐनिमेशन इफ़ेक्ट होते हैं. जैसे, FadeThroughTransition. ऐनिमेशन पैकेज इंपोर्ट करें और FadeTransition को FadeThroughTransition विजेट से बदलें:

lib/home_screen.dart

import 'package;animations/animations.dart';

ElevatedButton(
  onPressed: () {
    // Show the question screen to start the game
    Navigator.push(
      context,
      PageRouteBuilder(
        pageBuilder: (context, animation, secondaryAnimation) {
          return const QuestionScreen();
        },
        transitionsBuilder:
            (context, animation, secondaryAnimation, child) {
          return FadeThroughTransition(                          // NEW
            animation: animation,                                // NEW
            secondaryAnimation: secondaryAnimation,              // NEW
            child: child,                                        // NEW
          );                                                     // NEW
        },
      ),
    );
  },
  child: Text('New Game'),
),

प्रिडिक्टिव बैक ऐनिमेशन को पसंद के मुताबिक बनाना

1c0558ffa3b76439.gif

अनुमानित तरीके से वापस जाने की सुविधा, Android की एक नई सुविधा है. इसकी मदद से, उपयोगकर्ता मौजूदा रूट या ऐप्लिकेशन पर नेविगेट करने से पहले, यह देख सकता है कि उसके पीछे क्या है. स्क्रीन पर वापस खींचने के दौरान, उपयोगकर्ता की उंगली की जगह के हिसाब से झलक दिखाने वाला ऐनिमेशन चलता है.

Flutter, सिस्टम के लेवल पर इस सुविधा को चालू करके, सिस्टम के अनुमानित बैक के साथ काम करता है. ऐसा तब होता है, जब Flutter के नेविगेशन स्टैक पर पॉप करने के लिए कोई रूट न हो. दूसरे शब्दों में, जब बैक बटन दबाने पर ऐप्लिकेशन बंद हो जाए. इस ऐनिमेशन को सिस्टम मैनेज करता है, न कि Flutter.

Flutter ऐप्लिकेशन में एक से दूसरे रूट पर जाने के दौरान, Flutter, अनुमानित तरीके से वापस जाने की सुविधा भी देता है. PredictiveBackPageTransitionsBuilder नाम का एक खास PageTransitionsBuilder, सिस्टम के अनुमानित तरीके से वापस जाने के जेस्चर को सुनता है और जेस्चर की प्रोग्रेस के साथ पेज ट्रांज़िशन को चलाता है.

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

अपने ऐप्लिकेशन के ThemeData कॉन्फ़िगरेशन में, PageTransitionsTheme को कॉन्फ़िगर करें, ताकि Android पर PredictiveBack का इस्तेमाल किया जा सके. साथ ही, अन्य प्लैटफ़ॉर्म पर ऐनिमेशन पैकेज से फ़ेड-थ्रू ट्रांज़िशन इफ़ेक्ट का इस्तेमाल किया जा सके:

lib/main.dart

import 'package:animations/animations.dart';                                 // NEW
import 'package:flutter/material.dart';

import 'home_screen.dart';

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

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

  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      debugShowCheckedModeBanner: false,
      theme: ThemeData(
        colorScheme: ColorScheme.fromSeed(seedColor: Colors.blue),
        useMaterial3: true,
        pageTransitionsTheme: PageTransitionsTheme(
          builders: {
            TargetPlatform.android: PredictiveBackPageTransitionsBuilder(),  // NEW
            TargetPlatform.iOS: FadeThroughPageTransitionsBuilder(),         // NEW
            TargetPlatform.macOS: FadeThroughPageTransitionsBuilder(),       // NEW
            TargetPlatform.windows: FadeThroughPageTransitionsBuilder(),     // NEW
            TargetPlatform.linux: FadeThroughPageTransitionsBuilder(),       // NEW
          },
        ),
      ),
      home: HomeScreen(),
    );
  }
}

अब Navigator.push() कॉल को MaterialPageRoute में बदला जा सकता है.

lib/home_screen.dart

ElevatedButton(
  onPressed: () {
    // Show the question screen to start the game
    Navigator.push(
      context,
      MaterialPageRoute(builder: (context) {       // NEW
        return const QuestionScreen();             // NEW
      }),                                          // NEW
    );
  },
  child: Text('New Game'),
),

मौजूदा सवाल बदलने के लिए, FadeThroughTransition का इस्तेमाल करना

AnimatedSwitcher विजेट, अपने बिल्डर कॉलबैक में सिर्फ़ एक ऐनिमेशन उपलब्ध कराता है. इस समस्या को हल करने के लिए, animations पैकेज में PageTransitionSwitcher उपलब्ध कराया गया है.

lib/question_screen.dart

class QuestionCard extends StatelessWidget {
  final String? question;

  const QuestionCard({
    required this.question,
    super.key,
  });

  @override
  Widget build(BuildContext context) {
    return PageTransitionSwitcher(                                          // NEW
      layoutBuilder: (entries) {                                            // NEW
        return Stack(                                                       // NEW
          alignment: Alignment.topCenter,                                   // NEW
          children: entries,                                                // NEW
        );                                                                  // NEW
      },                                                                    // NEW
      transitionBuilder: (child, animation, secondaryAnimation) {           // NEW
        return FadeThroughTransition(                                       // NEW
          animation: animation,                                             // NEW
          secondaryAnimation: secondaryAnimation,                           // NEW
          child: child,                                                     // NEW
        );                                                                  // NEW
      },                                                                    // NEW
      duration: const Duration(milliseconds: 300),
      child: Card(
        key: ValueKey(question),
        elevation: 4,
        child: Padding(
          padding: const EdgeInsets.all(16.0),
          child: Text(
            question ?? '',
            style: Theme.of(context).textTheme.displaySmall,
          ),
        ),
      ),
    );
  }
}

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

77358e5776eb104c.png

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

closedBuilder से मिला विजेट शुरू में दिखता है. कंटेनर पर टैप करने या openContainer कॉलबैक को कॉल करने पर, यह openBuilder से मिले विजेट में बड़ा हो जाता है.

openContainer कॉलबैक को व्यू-मॉडल से कनेक्ट करने के लिए, QuestionCard विजेट में नया पास जोड़ें और "गेम खत्म" स्क्रीन दिखाने के लिए इस्तेमाल किया जाने वाला कॉलबैक सेव करें:

lib/question_screen.dart

class QuestionScreen extends StatefulWidget {
  const QuestionScreen({super.key});

  @override
  State<QuestionScreen> createState() => _QuestionScreenState();
}

class _QuestionScreenState extends State<QuestionScreen> {
  late final QuizViewModel viewModel =
      QuizViewModel(onGameOver: _handleGameOver);
  VoidCallback? _showGameOverScreen;                                    // NEW

  @override
  Widget build(BuildContext context) {
    return ListenableBuilder(
      listenable: viewModel,
      builder: (context, child) {
        return Scaffold(
          appBar: AppBar(
            actions: [
              TextButton(
                onPressed:
                    viewModel.hasNextQuestion && viewModel.didAnswerQuestion
                        ? () {
                            viewModel.getNextQuestion();
                          }
                        : null,
                child: const Text('Next'),
              )
            ],
          ),
          body: Center(
            child: Column(
              children: [
                QuestionCard(                                           // NEW
                  onChangeOpenContainer: _handleChangeOpenContainer,    // NEW
                  question: viewModel.currentQuestion?.question,        // NEW
                  viewModel: viewModel,                                 // NEW
                ),                                                      // NEW
                Spacer(),
                AnswerCards(
                  onTapped: (index) {
                    viewModel.checkAnswer(index);
                  },
                  answers: viewModel.currentQuestion?.possibleAnswers ?? [],
                  correctAnswer: viewModel.didAnswerQuestion
                      ? viewModel.currentQuestion?.correctAnswer
                      : null,
                ),
                StatusBar(viewModel: viewModel),
              ],
            ),
          ),
        );
      },
    );
  }

  void _handleChangeOpenContainer(VoidCallback openContainer) {        // NEW
    _showGameOverScreen = openContainer;                               // NEW
  }                                                                    // NEW

  void _handleGameOver() {                                             // NEW
    if (_showGameOverScreen != null) {                                 // NEW
      _showGameOverScreen!();                                          // NEW
    }                                                                  // NEW
  }                                                                    // NEW
}

नया विजेट, GameOverScreen जोड़ें:

lib/question_screen.dart

class GameOverScreen extends StatelessWidget {
  final QuizViewModel viewModel;
  const GameOverScreen({required this.viewModel, super.key});

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(
        automaticallyImplyLeading: false,
      ),
      body: Center(
        child: Column(
          mainAxisAlignment: MainAxisAlignment.center,
          children: [
            Scoreboard(
              score: viewModel.score,
              totalQuestions: viewModel.totalQuestions,
            ),
            Text(
              'You Win!',
              style: Theme.of(context).textTheme.displayLarge,
            ),
            Text(
              'Score: ${viewModel.score} / ${viewModel.totalQuestions}',
              style: Theme.of(context).textTheme.displaySmall,
            ),
            ElevatedButton(
              child: Text('OK'),
              onPressed: () {
                Navigator.popUntil(context, (route) => route.isFirst);
              },
            ),
          ],
        ),
      ),
    );
  }
}

QuestionCard विजेट में, कार्ड को ऐनिमेशन पैकेज से OpenContainer विजेट से बदलें. साथ ही, viewModel और Open Container कॉलबैक के लिए दो नए फ़ील्ड जोड़ें:

lib/question_screen.dart

class QuestionCard extends StatelessWidget {
  final String? question;

  const QuestionCard({
    required this.onChangeOpenContainer,
    required this.question,
    required this.viewModel,
    super.key,
  });

  final ValueChanged<VoidCallback> onChangeOpenContainer;
  final QuizViewModel viewModel;

  static const _backgroundColor = Color(0xfff2f3fa);

  @override
  Widget build(BuildContext context) {
    return PageTransitionSwitcher(
      duration: const Duration(milliseconds: 200),
      transitionBuilder: (child, animation, secondaryAnimation) {
        return FadeThroughTransition(
          animation: animation,
          secondaryAnimation: secondaryAnimation,
          child: child,
        );
      },
      child: OpenContainer(                                         // NEW
        key: ValueKey(question),                                    // NEW
        tappable: false,                                            // NEW
        closedColor: _backgroundColor,                              // NEW
        closedShape: const RoundedRectangleBorder(                  // NEW
          borderRadius: BorderRadius.all(Radius.circular(12.0)),    // NEW
        ),                                                          // NEW
        closedElevation: 4,                                         // NEW
        closedBuilder: (context, openContainer) {                   // NEW
          onChangeOpenContainer(openContainer);                     // NEW
          return ColoredBox(                                        // NEW
            color: _backgroundColor,                                // NEW
            child: Padding(                                         // NEW
              padding: const EdgeInsets.all(16.0),                  // NEW
              child: Text(
                question ?? '',
                style: Theme.of(context).textTheme.displaySmall,
              ),
            ),
          );
        },
        openBuilder: (context, closeContainer) {                    // NEW
          return GameOverScreen(viewModel: viewModel);              // NEW
        },                                                          // NEW
      ),
    );
  }
}

4120f9395857d218.gif

8. बधाई हो

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

  • ImplicitlyAnimatedWidget का इस्तेमाल करने का तरीका
  • ExplicitlyAnimatedWidget का इस्तेमाल करने का तरीका
  • ऐनिमेशन में कर्व और ट्वीन लागू करने का तरीका
  • पहले से बने ट्रांज़िशन विजेट, जैसे कि AnimatedSwitcher या PageRouteBuilder का इस्तेमाल करने का तरीका
  • animations पैकेज में पहले से मौजूद बेहतरीन ऐनिमेशन इफ़ेक्ट का इस्तेमाल करने का तरीका. जैसे, FadeThroughTransition और OpenContainer
  • डिफ़ॉल्ट ट्रांज़िशन ऐनिमेशन को पसंद के मुताबिक बनाने का तरीका. इसमें, Android पर प्रिडिक्टिव बैक की सुविधा जोड़ने का तरीका भी शामिल है.

3026390ad413769c.gif

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

इनमें से कुछ कोडलैब देखें:

इसके अलावा, ऐनिमेशन सैंपल ऐप्लिकेशन डाउनलोड करें. इसमें ऐनिमेशन की अलग-अलग तकनीकें दिखाई गई हैं

इसके बारे में और पढ़ें

ऐनिमेशन से जुड़े ज़्यादा संसाधन पाने के लिए, flutter.dev पर जाएं:

इसके अलावा, Medium पर ये लेख पढ़ें:

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