Flutter में ऐनिमेशन

Flutter में ऐनिमेशन

इस कोडलैब (कोड बनाना सीखने के लिए ट्यूटोरियल) के बारे में जानकारी

subjectपिछली बार जून 3, 2025 को अपडेट किया गया
account_circleJohn Ryan, Justin McCandless ने लिखा

1. परिचय

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

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(isActive: score > i),                 // Edit this line.
       
],
     
),
   
);
 
}
}

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 इंटरफ़ेस के मुताबिक है, जो हमारे ऐनिमेशन इफ़ेक्ट को बनाने के लिए ज़रूरी है.

Icon विजेट चुनें और अपने IDE में "बिल्डर के साथ रैप करें" क्विक ऐक्शन का इस्तेमाल करें. इसके बाद, नाम को 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);     // And modify this line.
        },
      ),
    );
  }
}

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

8b0911f4af299a60.gif

ध्यान दें कि isActive पैरामीटर की वैल्यू के आधार पर, ColorTween की end वैल्यू बदल जाती है. ऐसा इसलिए होता है, क्योंकि जब भी 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 का इस्तेमाल, AnimatedSwitcher से मिले Animation को Animation में बदलने के लिए किया जा सकता है, ताकि उसे 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 से, x-ऐक्सिस पर -0.1 से 0.0 तक ट्रांज़िशन करने वाली Tween में बदलता है.

इसके अलावा, ऐनिमेशन क्लास में 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 का इस्तेमाल किया गया है.

खास जानकारी

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

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

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

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

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

  1. StatefulWidget बनाएं
  2. अपनी AnimationController को Ticker देने के लिए, 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 अपने लिसनर को सूचना दे, तब विजेट ट्री फिर से बनाया जाए. साथ ही, Transform विजेट का इस्तेमाल, कार्ड को पलटने की नकल करने के लिए, 3D रोटेशन इफ़ेक्ट लागू करने के लिए किया जाता है.

इस विजेट का इस्तेमाल करने के लिए, हर जवाब कार्ड को CardFlipEffect विजेट में रैप करें. पक्का करें कि आपने Card विजेट के लिए 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 क्लास को सीधे तौर पर एक्सटेंशन करना अक्सर एक अच्छा आइडिया होता है. माफ़ करें, इस क्लास को अपने State में पिछले विजेट को सेव करना होगा. इसलिए, इसे 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 में एक नया Animation बनाएं, जो 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>([              // Add from here...
      if (widget.delayAmount > 0)
        TweenSequenceItem(
          tween: ConstantTween<double>(0.0),
          weight: widget.delayAmount,
        ),
      TweenSequenceItem(tween: Tween(begin: 0.0, end: 1.0), weight: 1.0),
    ]).animate(_animationController);                          // To here.
  }

आखिर में, build तरीके में 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(                                         // Add from here...
        pageBuilder: (context, animation, secondaryAnimation) {
          return const QuestionScreen();
        },
        transitionsBuilder:
            (context, animation, secondaryAnimation, child) {
              return FadeTransition(
                opacity: animation,
                child: child,
              );
            },
      ),                                                        // To here.
    );
  },
  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(                     // Add from here...
               
animation: animation,
               
secondaryAnimation: secondaryAnimation,
               
child: child,
             
);                                                // To here.
           
},
     
),
   
);
 
},
 
child: Text('New Game'),
),

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

1c0558ffa3b76439.gif

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

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

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

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

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

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),
       
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(                                        // Add from here...
        builder: (context) {
          return const QuestionScreen();
        },
      ),                                                        // To here.
    );
  },
  child: Text('New Game'),
),

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

AnimatedSwitcher विजेट, अपने बिल्डर कॉलबैक में सिर्फ़ एक Animation उपलब्ध कराता है. इस समस्या को हल करने के लिए, 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(                              // Add from here...
      layoutBuilder: (entries) {
        return Stack(alignment: Alignment.topCenter, children: entries);
      },
      transitionBuilder: (child, animation, secondaryAnimation) {
        return FadeThroughTransition(
          animation: animation,
          secondaryAnimation: secondaryAnimation,
          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,
          ),
        ),
      ),
    );
  }
}

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

77358e5776eb104c.png

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

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

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

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 विजेट में, Card को animations पैकेज के OpenContainer विजेट से बदलें. साथ ही, viewModel और ओपन कंटेनर कॉलबैक के लिए दो नए फ़ील्ड जोड़ें:

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 को इस्तेमाल करने का तरीका
  • ऐनिमेशन में Curves और Tweens को लागू करने का तरीका
  • पहले से बने ट्रांज़िशन विजेट, जैसे कि AnimatedSwitcher या PageRouteBuilder का इस्तेमाल करने का तरीका
  • animations पैकेज में पहले से मौजूद FadeThroughTransition और OpenContainer जैसे बेहतरीन ऐनिमेशन इफ़ेक्ट इस्तेमाल करने का तरीका
  • Android पर प्रिडिक्टिव बैक की सुविधा जोड़ने के साथ-साथ, डिफ़ॉल्ट ट्रांज़िशन ऐनिमेशन को पसंद के मुताबिक बनाने का तरीका.

3026390ad413769c.gif

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

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

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

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

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

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

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