ফ্লাটারে অ্যানিমেশন

1. ভূমিকা

অ্যানিমেশনগুলি আপনার অ্যাপের ব্যবহারকারীর অভিজ্ঞতা উন্নত করার, ব্যবহারকারীর কাছে গুরুত্বপূর্ণ তথ্য যোগাযোগ করার এবং আপনার অ্যাপটিকে আরও সুন্দর এবং ব্যবহার করার জন্য উপভোগ্য করে তোলার একটি দুর্দান্ত উপায়৷

ফ্লটারের অ্যানিমেশন ফ্রেমওয়ার্কের ওভারভিউ

ফ্লটার প্রতিটি ফ্রেমে উইজেট গাছের একটি অংশ পুনঃনির্মাণ করে অ্যানিমেশন প্রভাব প্রদর্শন করে। এটি অ্যানিমেশন তৈরি এবং রচনা করা সহজ করতে পূর্ব-নির্মিত অ্যানিমেশন প্রভাব এবং অন্যান্য API প্রদান করে।

  • অন্তর্নিহিত অ্যানিমেশনগুলি পূর্ব-নির্মিত অ্যানিমেশন প্রভাব যা সম্পূর্ণ অ্যানিমেশন স্বয়ংক্রিয়ভাবে চালায়। যখন অ্যানিমেশনের লক্ষ্য মান পরিবর্তিত হয়, তখন এটি বর্তমান মান থেকে লক্ষ্য মান পর্যন্ত অ্যানিমেশন চালায় এবং এর মধ্যে প্রতিটি মান প্রদর্শন করে যাতে উইজেটটি মসৃণভাবে অ্যানিমেট হয়। অন্তর্নিহিত অ্যানিমেশনের উদাহরণগুলির মধ্যে রয়েছে 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 মতো প্রাক-নির্মিত অ্যানিমেটেড উইজেটগুলি ব্যবহার করে আপনার নিজের প্রভাবগুলি কনফিগার করতে কীভাবে স্পষ্টভাবে অ্যানিমেটেড প্রভাবগুলি ব্যবহার করবেন।
  • আপনার নিজস্ব উইজেট সংজ্ঞায়িত করতে AnimationController কীভাবে ব্যবহার করবেন যা একটি 3D প্রভাব প্রদর্শন করে।
  • ন্যূনতম সেটআপ সহ অভিনব অ্যানিমেশন প্রভাবগুলি প্রদর্শন করতে animations প্যাকেজটি কীভাবে ব্যবহার করবেন।

আপনি কি প্রয়োজন হবে

  • ফ্লাটার SDK
  • একটি IDE, যেমন VSCode বা Android Studio/IntelliJ

2. আপনার ফ্লটার ডেভেলপমেন্ট এনভায়রনমেন্ট সেট আপ করুন

এই ল্যাবটি সম্পূর্ণ করতে আপনার দুটি টুকরো সফ্টওয়্যার প্রয়োজন — ফ্লাটার SDK এবং একটি সম্পাদক

আপনি এই ডিভাইসগুলির যেকোনো একটি ব্যবহার করে কোডল্যাব চালাতে পারেন:

  • একটি ফিজিক্যাল অ্যান্ড্রয়েড ( 7 ধাপে ভবিষ্যদ্বাণী বাস্তবায়নের জন্য প্রস্তাবিত ) বা iOS ডিভাইস আপনার কম্পিউটারের সাথে সংযুক্ত এবং ডেভেলপার মোডে সেট করা আছে।
  • আইওএস সিমুলেটর (এক্সকোড সরঞ্জাম ইনস্টল করা প্রয়োজন)।
  • অ্যান্ড্রয়েড এমুলেটর (অ্যান্ড্রয়েড স্টুডিওতে সেটআপ প্রয়োজন)।
  • একটি ব্রাউজার (ডিবাগিংয়ের জন্য Chrome প্রয়োজন)।
  • একটি উইন্ডোজ , লিনাক্স , বা ম্যাকোস ডেস্কটপ কম্পিউটার। আপনি যে প্ল্যাটফর্মে স্থাপন করার পরিকল্পনা করছেন সেখানে আপনাকে অবশ্যই বিকাশ করতে হবে। সুতরাং, আপনি যদি একটি উইন্ডোজ ডেস্কটপ অ্যাপ বিকাশ করতে চান, তাহলে যথাযথ বিল্ড চেইন অ্যাক্সেস করতে আপনাকে অবশ্যই উইন্ডোজে বিকাশ করতে হবে। অপারেটিং সিস্টেম-নির্দিষ্ট প্রয়োজনীয়তা রয়েছে যা docs.flutter.dev/desktop- এ বিস্তারিতভাবে কভার করা হয়েছে।

আপনার ইনস্টলেশন যাচাই করুন

আপনার ফ্লাটার SDK সঠিকভাবে কনফিগার করা হয়েছে কিনা যাচাই করতে এবং উপরের লক্ষ্য প্ল্যাটফর্মগুলির মধ্যে অন্তত একটি ইনস্টল করা আছে, Flutter Doctor টুলটি ব্যবহার করুন:

$ 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-এ ফ্লাটার/স্যাম্পল রিপোজিটরি থেকে স্টার্ট অ্যাপ ক্লোন করতে git ব্যবহার করুন।

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

বিকল্পভাবে, আপনি একটি .zip ফাইল হিসাবে সোর্স কোড ডাউনলোড করতে পারেন।

অ্যাপটি চালান

অ্যাপটি চালানোর জন্য, flutter run কমান্ডটি ব্যবহার করুন এবং একটি টার্গেট ডিভাইস নির্দিষ্ট করুন, যেমন android , ios বা chrome । সমর্থিত প্ল্যাটফর্মের সম্পূর্ণ তালিকার জন্য, সমর্থিত প্ল্যাটফর্ম পৃষ্ঠাটি দেখুন।

$ flutter run -d android

এছাড়াও আপনি আপনার পছন্দের IDE ব্যবহার করে অ্যাপটি চালাতে এবং ডিবাগ করতে পারেন। আরও তথ্যের জন্য অফিসিয়াল ফ্লটার ডকুমেন্টেশন দেখুন।

কোড ঘুরে দেখুন

স্টার্টার অ্যাপ হল একটি মাল্টিপল-চয়েস কুইজ গেম যা মডেল-ভিউ-ভিউ-মডেল বা MVVM ডিজাইন প্যাটার্ন অনুসরণ করে দুটি স্ক্রীন নিয়ে গঠিত। QuestionBank (মডেল) ক্লাস থেকে ব্যবহারকারীকে একাধিক পছন্দের প্রশ্ন জিজ্ঞাসা করতে QuestionScreen (View) QuizViewModel (View-Model) ক্লাস ব্যবহার করে।

  • home_screen.dart - একটি নতুন গেম বোতাম সহ একটি স্ক্রীন প্রদর্শন করে
  • main.dart - উপাদান 3 ব্যবহার করতে এবং হোম স্ক্রীন দেখানোর জন্য MaterialApp কনফিগার করে
  • model.dart - অ্যাপ জুড়ে ব্যবহৃত মূল ক্লাসগুলিকে সংজ্ঞায়িত করে৷
  • question_screen.dart - কুইজ গেমের জন্য UI প্রদর্শন করে
  • view_model.dart - QuestionScreen দ্বারা প্রদর্শিত কুইজ গেমের জন্য রাজ্য এবং যুক্তি সংরক্ষণ করে

fbb1e1f7b6c91e21.png

অ্যাপটি এখনও কোনো অ্যানিমেটেড প্রভাব সমর্থন করে না, ব্যবহারকারী যখন নতুন গেম বোতাম টিপে তখন Flutter's Navigator ক্লাস দ্বারা প্রদর্শিত ডিফল্ট ভিউ ট্রানজিশন ব্যতীত।

4. অন্তর্নিহিত অ্যানিমেশন প্রভাব ব্যবহার করুন

অন্তর্নিহিত অ্যানিমেশনগুলি অনেক পরিস্থিতিতে একটি দুর্দান্ত পছন্দ, কারণ তাদের কোনও বিশেষ কনফিগারেশনের প্রয়োজন হয় না। এই বিভাগে, আপনি StatusBar উইজেট আপডেট করবেন যাতে এটি একটি অ্যানিমেটেড স্কোরবোর্ড প্রদর্শন করে। সাধারণ অন্তর্নিহিত অ্যানিমেশন প্রভাবগুলি খুঁজে পেতে, ImplicitlyAnimatedWidget API ডকুমেন্টেশন ব্রাউজ করুন।

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 উইজেটগুলি প্রতিস্থাপন করুন যা পূর্বে স্কোর এবং মোট প্রশ্ন সংখ্যা দেখিয়েছিল। আপনার সম্পাদককে স্বয়ংক্রিয়ভাবে ফাইলের শীর্ষে প্রয়োজনীয় 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 নামে একটি নতুন উইজেট তৈরি করুন যা স্টার সক্রিয় হয়ে গেলে scale পরিমাণ 0.5 থেকে 1.0 পরিবর্তন করতে একটি AnimatedScale উইজেট ব্যবহার করে:

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

দুটি মানের মধ্যে ইন্টারপোলেট করতে একটি টুইন ব্যবহার করুন

লক্ষ্য করুন যে AnimatedStar উইজেটের রঙটি isActive ক্ষেত্রটি সত্যে পরিবর্তিত হওয়ার সাথে সাথে পরিবর্তিত হয়।

একটি অ্যানিমেটেড রঙের প্রভাব অর্জনের জন্য, আপনি একটি AnimatedContainer উইজেট ব্যবহার করার চেষ্টা করতে পারেন (যেটি ImplicitlyAnimatedWidget এর আরেকটি সাবক্লাস), কারণ এটি স্বয়ংক্রিয়ভাবে রঙ সহ এর সমস্ত বৈশিষ্ট্য অ্যানিমেট করতে পারে। দুর্ভাগ্যবশত, আমাদের উইজেটকে একটি আইকন প্রদর্শন করতে হবে, একটি ধারক নয়।

আপনি AnimatedIcon চেষ্টা করতে পারেন, যা আইকনগুলির আকারগুলির মধ্যে রূপান্তর প্রভাব প্রয়োগ করে৷ কিন্তু AnimatedIcons ক্লাসে একটি তারকা আইকনের ডিফল্ট বাস্তবায়ন নেই।

পরিবর্তে, আমরা TweenAnimationBuilder নামক ImplicitlyAnimatedWidget এর আরেকটি সাবক্লাস ব্যবহার করব, যা প্যারামিটার হিসাবে একটি Tween নেয়। একটি tween হল এমন একটি শ্রেণী যা দুটি মান ( begin এবং end ) নেয় এবং মধ্যবর্তী মানগুলি গণনা করে, যাতে একটি অ্যানিমেশন তাদের প্রদর্শন করতে পারে। এই উদাহরণে, আমরা একটি ColorTween ব্যবহার করব, যা আমাদের অ্যানিমেশন প্রভাব তৈরি করতে প্রয়োজনীয় Tween<Color> ইন্টারফেসকে সন্তুষ্ট করে।

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,                                      // Modify from here...
          );
        },                                                     // To here.
      ),
    );
  }
}

এখন, নতুন অ্যানিমেশন দেখতে অ্যাপটি হট-রিলোড করুন।

8b0911f4af299a60.gif

লক্ষ্য করুন যে আমাদের ColorTween এর end মান isActive প্যারামিটারের মানের উপর ভিত্তি করে পরিবর্তিত হয়। এর কারণ হল TweenAnimationBuilder যখনই Tween.end মান পরিবর্তন হয় তখনই তার অ্যানিমেশন পুনরায় চালায়। যখন এটি ঘটে, তখন নতুন অ্যানিমেশন বর্তমান অ্যানিমেশন মান থেকে নতুন শেষ মান পর্যন্ত চলে, যা আপনাকে যে কোনো সময় রঙ পরিবর্তন করতে দেয় (এমনকি অ্যানিমেশন চলমান থাকাকালীন) এবং সঠিক মধ্যবর্তী মানগুলির সাথে একটি মসৃণ অ্যানিমেশন প্রভাব প্রদর্শন করতে দেয়। .

একটি বক্ররেখা প্রয়োগ করুন

এই উভয় অ্যানিমেশন প্রভাব একটি ধ্রুবক হারে চলে, কিন্তু অ্যানিমেশনগুলি প্রায়শই দৃশ্যত আকর্ষণীয় এবং তথ্যপূর্ণ হয় যখন তারা গতি বাড়ায় বা ধীর করে।

একটি Curve একটি সহজ ফাংশন প্রয়োগ করে, যা সময়ের সাথে একটি প্যারামিটারের পরিবর্তনের হারকে সংজ্ঞায়িত করে। Curves ক্লাসে পূর্ব-নির্মিত ইজিং কার্ভের সংগ্রহ সহ ফ্লাটার জাহাজ, যেমন easeIn বা easeOut

5dabe68d1210b8a1.gif

3a9e7490c594279a.gif

এই চিত্রগুলি ( Curves API ডকুমেন্টেশন পৃষ্ঠায় উপলব্ধ) বক্ররেখাগুলি কীভাবে কাজ করে তার একটি সূত্র দেয়। বক্ররেখা 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 স্টুডিওতে ডিবাগ টুল উইন্ডোতে Open Flutter DevTools বোতামটি নির্বাচন করে উইজেট ইন্সপেক্টর খুলুন।

3ce33dc01d096b14.png

363ae0fbcd0c2395.png

একবার উইজেট ইন্সপেক্টর খোলা হলে, টুলবারে স্লো অ্যানিমেশন বোতামে ক্লিক করুন।

adea0a16d01127ad.png

5. স্পষ্ট অ্যানিমেশন প্রভাব ব্যবহার করুন

অন্তর্নিহিত অ্যানিমেশনের মতো, স্পষ্ট অ্যানিমেশনগুলি পূর্ব-নির্মিত অ্যানিমেশন প্রভাব, তবে একটি লক্ষ্য মান নেওয়ার পরিবর্তে, তারা একটি প্যারামিটার হিসাবে একটি Animation অবজেক্ট নেয়। এটি তাদের এমন পরিস্থিতিতে উপযোগী করে তোলে যেখানে অ্যানিমেশনটি ইতিমধ্যেই একটি নেভিগেশন ট্রানজিশন, AnimatedSwitcher বা AnimationController দ্বারা সংজ্ঞায়িত করা হয়েছে, উদাহরণস্বরূপ।

একটি স্পষ্ট অ্যানিমেশন প্রভাব ব্যবহার করুন

একটি স্পষ্ট অ্যানিমেশন প্রভাবের সাথে শুরু করতে, একটি AnimatedSwitcher দিয়ে Card উইজেটটি মুড়ে দিন।

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> যা শুরু এবং শেষ অফসেটকে সংজ্ঞায়িত করে যে ইনকামিং এবং আউটগোয়িং উইজেটগুলির মধ্যে চলে যাবে।

Tweens এর একটি হেল্পার ফাংশন আছে, 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 ব্যবহার করে, এবং তারপর এটিকে একটি Tween<double> থেকে রূপান্তর করতে যা 0.0 থেকে 1.0 পর্যন্ত, একটি T ween<Offset> যা x-এ -0.1 থেকে 0.0 পর্যন্ত রূপান্তরিত হয়। -অক্ষ

বিকল্পভাবে, অ্যানিমেশন ক্লাসে একটি 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
  },

লেআউট বিল্ডার কাস্টমাইজ করুন

আপনি অ্যানিমেশন সুইচারের সাথে একটি ছোট সমস্যা লক্ষ্য করতে পারেন। যখন একটি প্রশ্নকার্ড একটি নতুন প্রশ্নে স্যুইচ করে, তখন এটি অ্যানিমেশন চলাকালীন উপলব্ধ স্থানের মাঝখানে রেখে দেয়, কিন্তু অ্যানিমেশন বন্ধ হয়ে গেলে, উইজেটটি স্ক্রিনের শীর্ষে চলে যায়। এটি একটি জ্যাঙ্কি অ্যানিমেশন সৃষ্টি করে কারণ অ্যানিমেশন চলাকালীন প্রশ্ন কার্ডের চূড়ান্ত অবস্থানের সাথে মেলে না।

d77de181bdde58f7.gif

এটি ঠিক করতে, অ্যানিমেটেড সুইচারের একটি লেআউটবিল্ডার প্যারামিটারও রয়েছে, যা লেআউটটি সংজ্ঞায়িত করতে ব্যবহার করা যেতে পারে। কার্ডটিকে পর্দার শীর্ষে সারিবদ্ধ করতে লেআউট নির্মাতা কনফিগার করতে এই ফাংশনটি ব্যবহার করুন:

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 ক্লাস থেকে ডিফল্টLayoutBuilder- এর একটি পরিবর্তিত সংস্করণ, কিন্তু Alignment.center এর পরিবর্তে Alignment.topCenter ব্যবহার করে।

সারাংশ

  • স্পষ্ট অ্যানিমেশনগুলি হল অ্যানিমেশন প্রভাব যা একটি অ্যানিমেশন অবজেক্ট নেয় (ইমপ্লিসিটলি অ্যানিমেটেড উইজেটগুলির বিপরীতে, যা একটি লক্ষ্য মান এবং সময়কাল নেয়)
  • অ্যানিমেশন ক্লাস একটি চলমান অ্যানিমেশন প্রতিনিধিত্ব করে, কিন্তু একটি নির্দিষ্ট প্রভাব সংজ্ঞায়িত করে না।
  • একটি অ্যানিমেশনে Tweens এবং Curves (CurveTween ব্যবহার করে) প্রয়োগ করতে Tween().animate বা Animation.drive() ব্যবহার করুন।
  • অ্যানিমেটেড সুইচারের লেআউটবিল্ডার প্যারামিটার ব্যবহার করুন যাতে এটি তার বাচ্চাদের কীভাবে সাজায়।

6. একটি অ্যানিমেশনের অবস্থা নিয়ন্ত্রণ করুন

এখন পর্যন্ত, প্রতিটি অ্যানিমেশন ফ্রেমওয়ার্ক দ্বারা স্বয়ংক্রিয়ভাবে চালানো হয়েছে। অন্তর্নিহিত অ্যানিমেশনগুলি স্বয়ংক্রিয়ভাবে চলে, এবং স্পষ্ট অ্যানিমেশন প্রভাবগুলির সঠিকভাবে কাজ করার জন্য একটি অ্যানিমেশন প্রয়োজন৷ এই বিভাগে, আপনি শিখবেন কিভাবে একটি অ্যানিমেশন কন্ট্রোলার ব্যবহার করে আপনার নিজস্ব অ্যানিমেশন অবজেক্ট তৈরি করতে হয় এবং টুইন্সকে একসাথে একত্রিত করতে একটি TweenSequence ব্যবহার করতে হয়।

একটি অ্যানিমেশন কন্ট্রোলার ব্যবহার করে একটি অ্যানিমেশন চালান

একটি অ্যানিমেশন কন্ট্রোলার ব্যবহার করে একটি অ্যানিমেশন তৈরি করতে, আপনাকে এই পদক্ষেপগুলি অনুসরণ করতে হবে:

  1. একটি স্টেটফুল উইজেট তৈরি করুন
  2. আপনার অ্যানিমেশন কন্ট্রোলারে টিকার প্রদান করতে আপনার স্টেট ক্লাসে SingleTickerProviderStateMixin mixin ব্যবহার করুন
  3. vsync (TickerProvider) প্যারামিটারে বর্তমান স্টেট অবজেক্ট প্রদান করে initState জীবনচক্র পদ্ধতিতে অ্যানিমেশন কন্ট্রোলার শুরু করুন।
  4. অ্যানিমেটেডবিল্ডার ব্যবহার করে অথবা ম্যানুয়ালি 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,
    );
  }
}

এই শ্রেণীটি একটি অ্যানিমেশন কন্ট্রোলার সেট আপ করে এবং যখনই ফ্রেমওয়ার্ক didUpdateWidget-কে কল করে যে উইজেট কনফিগারেশন পরিবর্তিত হয়েছে, এবং একটি নতুন চাইল্ড উইজেট থাকতে পারে তখন অ্যানিমেশনটি পুনরায় চালায়।

অ্যানিমেটেডবিল্ডার নিশ্চিত করে যে যখনই অ্যানিমেশন কন্ট্রোলার তার শ্রোতাদের অবহিত করে তখনই উইজেট ট্রিটি পুনরায় তৈরি করা হয় এবং ট্রান্সফর্ম উইজেটটি একটি 3D ঘূর্ণন প্রভাব প্রয়োগ করতে ব্যবহৃত হয় যাতে একটি কার্ড ফ্লিপ করা হয়।

এই উইজেটটি ব্যবহার করতে, প্রতিটি উত্তর কার্ড একটি 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

আপনি লক্ষ্য করতে পারেন যে এই ক্লাসটি দেখতে অনেকটা স্পষ্ট অ্যানিমেশন প্রভাবের মতো। আসলে, আপনার নিজস্ব সংস্করণ বাস্তবায়নের জন্য সরাসরি অ্যানিমেটেড উইজেট ক্লাস প্রসারিত করা প্রায়শই একটি ভাল ধারণা। দুর্ভাগ্যবশত, যেহেতু এই ক্লাসটিকে তার রাজ্যে আগের উইজেট সংরক্ষণ করতে হবে, তাই এটি একটি StatefulWidget ব্যবহার করতে হবে। আপনার নিজের স্পষ্ট অ্যানিমেশন প্রভাব তৈরি করার বিষয়ে আরও জানতে, অ্যানিমেটেড উইজেটের জন্য API ডকুমেন্টেশন দেখুন।

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 ব্যবহার করে বিলম্ব প্রযোজ্য। লক্ষ্য করুন যে এটি dart:async লাইব্রেরি থেকে কোনো ইউটিলিটি ব্যবহার করে না , যেমন Future.delayed । এর কারণ হল বিলম্ব অ্যানিমেশনের অংশ এবং উইজেট যখন অ্যানিমেশন কন্ট্রোলার ব্যবহার করে তখন তা স্পষ্টভাবে নিয়ন্ত্রণ করে এমন কিছু নয়৷ এটি DevTools-এ ধীরগতির অ্যানিমেশন সক্রিয় করার সময় অ্যানিমেশন প্রভাবকে ডিবাগ করা সহজ করে, কারণ এটি একই টিকারপ্রোভাইডার ব্যবহার করে।

একটি TweenSequence ব্যবহার করতে, দুটি TweenSequenceItem s তৈরি করুন, একটিতে একটি 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
  }

অবশেষে, বিল্ড পদ্ধতিতে অ্যানিমেশন কন্ট্রোলারের অ্যানিমেশনটিকে নতুন বিলম্বিত অ্যানিমেশন দিয়ে প্রতিস্থাপন করুন।

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 এ নেভিগেট করেন তখন ট্রানজিশন অ্যানিমেশন কাস্টমাইজ করতে। একটি ফেড ট্রানজিশন (একটি স্পষ্টভাবে অ্যানিমেটেড উইজেট) ব্যবহার করুন যাতে নতুন স্ক্রীনটি আগের স্ক্রিনের উপরে ফেইড হয়ে যায়।

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 প্রতিস্থাপন করুন:

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

প্রেডিকটিভ ব্যাক হল একটি নতুন অ্যান্ড্রয়েড বৈশিষ্ট্য যা ব্যবহারকারীকে নেভিগেট করার আগে বর্তমান রুট বা অ্যাপের পিছনে কী আছে তা দেখতে দেয়। পিক অ্যানিমেশন ব্যবহারকারীর আঙুলের অবস্থান দ্বারা চালিত হয় যখন তারা স্ক্রীন জুড়ে ফিরে আসে।

ফ্লটার সিস্টেম লেভেলে ফিচারটি সক্ষম করে সিস্টেম প্রেডিকটিভ ব্যাককে সমর্থন করে যখন ফ্লটার এর নেভিগেশন স্ট্যাকে পপ করার কোন রুট থাকে না বা অন্য কথায়, যখন ব্যাক অ্যাপ থেকে বেরিয়ে যাবে। এই অ্যানিমেশনটি সিস্টেম দ্বারা পরিচালিত হয় এবং ফ্লাটার নিজেই নয়।

Flutter একটি Flutter অ্যাপের মধ্যে রুটের মধ্যে নেভিগেট করার সময় ভবিষ্যদ্বাণীমূলক ব্যাক সমর্থন করে। PredictiveBackPageTransitionsBuilder নামক একটি বিশেষ PageTransitionsBuilder সিস্টেম ভবিষ্যদ্বাণীমূলক ব্যাক অঙ্গভঙ্গি শোনে এবং অঙ্গভঙ্গির অগ্রগতির সাথে তার পৃষ্ঠার রূপান্তর চালায়।

ভবিষ্যদ্বাণীমূলক ব্যাক শুধুমাত্র অ্যান্ড্রয়েড ইউ এবং তার উপরে সমর্থিত, তবে ফ্লাটার আকর্ষণীয়ভাবে মূল ব্যাক জেসচার আচরণ এবং 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),
        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 ব্যবহার করুন

অ্যানিমেটেড সুইচার উইজেট তার নির্মাতা কলব্যাকে শুধুমাত্র একটি অ্যানিমেশন প্রদান করে। এটি মোকাবেলার জন্য, 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 উইজেটে একটি নতুন পাস the 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);
              },
            ),
          ],
        ),
      ),
    );
  }
}

প্রশ্নকার্ড উইজেটে, ভিউমডেল এবং খোলা কন্টেইনার কলব্যাকের জন্য দুটি নতুন ক্ষেত্র যোগ করে অ্যানিমেশন প্যাকেজ থেকে একটি OpenContainer উইজেট দিয়ে কার্ডটি প্রতিস্থাপন করুন:

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-এর অ্যানিমেশন সিস্টেমের মূল উপাদানগুলি সম্পর্কে শিখেছেন৷ বিশেষ করে, আপনি শিখেছেন:

  • কিভাবে একটি ইমপ্লিসিটলি অ্যানিমেটেড উইজেট ব্যবহার করবেন
  • কিভাবে একটি স্পষ্টভাবে অ্যানিমেটেড উইজেট ব্যবহার করবেন
  • একটি অ্যানিমেশনে কার্ভ এবং টুইন্স কীভাবে প্রয়োগ করবেন
  • অ্যানিমেটেড সুইচার বা PageRouteBuilder এর মতো প্রাক-নির্মিত ট্রানজিশন উইজেটগুলি কীভাবে ব্যবহার করবেন
  • animations প্যাকেজ থেকে অভিনব প্রাক-নির্মিত অ্যানিমেশন প্রভাবগুলি কীভাবে ব্যবহার করবেন, যেমন FadeThroughTransition এবং OpenContainer
  • অ্যান্ড্রয়েডে ভবিষ্যদ্বাণীমূলক ব্যাক সমর্থন যোগ করা সহ ডিফল্ট ট্রানজিশন অ্যানিমেশন কীভাবে কাস্টমাইজ করবেন।

3026390ad413769c.gif

এরপর কি?

এই কোডল্যাবগুলির কয়েকটি দেখুন:

অথবা অ্যানিমেশন নমুনা অ্যাপ ডাউনলোড করুন, যা বিভিন্ন অ্যানিমেশন কৌশল প্রদর্শন করে

আরও পড়া

আপনি flutter.dev এ আরও অ্যানিমেশন সংস্থান খুঁজে পেতে পারেন:

অথবা মিডিয়ামে এই নিবন্ধগুলি দেখুন:

রেফারেন্স ডক্স