একটি স্তব্ধ অ্যানিমেশন ক্রমিক বা ওভারল্যাপিং অ্যানিমেশন নিয়ে গঠিত। অ্যানিমেশনটি সম্পূর্ণরূপে অনুক্রমিক হতে পারে, পরেরটির পরে একটি পরিবর্তন ঘটতে পারে বা এটি আংশিক বা সম্পূর্ণভাবে ওভারল্যাপ হতে পারে। যখন অ্যানিমেশনটি ক্রমিক হয়, তখন প্রতিটি উপাদানের শুরুর সময়ের মধ্যে সামান্য বিলম্বের সাথে উপাদানগুলি ক্রমানুসারে অ্যানিমেট করা হয়। এটি একটি ক্যাসকেডিং বা রিপল ইফেক্ট তৈরি করে, যেখানে অ্যানিমেশনটি একবারে না হয়ে পর্যায়ক্রমে উপাদানগুলির মধ্য দিয়ে যেতে দেখা যায়।
স্ট্যাগার্ড অ্যানিমেশনগুলিকে এক ধরণের মাইক্রো-ইন্টার্যাকশন হিসাবে বিবেচনা করা হয় কারণ তারা সূক্ষ্ম, ইন্টারেক্টিভ প্রতিক্রিয়া প্রদান করে ব্যবহারকারীর অভিজ্ঞতাকে উন্নত করে যা ব্যবহারকারীকে একটি ইন্টারফেসের মাধ্যমে গাইড করে। ফ্লটারে, আপনি অন্তর্নিহিত বা স্পষ্ট অ্যানিমেশন ব্যবহার করে সূক্ষ্ম অ্যানিমেশন তৈরি করে মাইক্রো-ইন্টার্যাকশন তৈরি করতে পারেন।
প্রেক্ষাপটের জন্য, অন্তর্নিহিত অ্যানিমেশনগুলিকে সহজ এবং ব্যবহারে সহজ করার জন্য ডিজাইন করা হয়েছে কারণ অ্যানিমেশনের বিশদগুলিকে বিমূর্ত করা হয় যখন স্পষ্ট অ্যানিমেশনগুলি জটিল অ্যানিমেশনগুলির জন্য আরও উপযুক্ত কারণ তারা অ্যানিমেশন প্রক্রিয়ার সম্পূর্ণ নিয়ন্ত্রণ অফার করে৷ এটি বিশেষত ব্যবহার করা হয় যখন আপনার অ্যানিমেশনের উপর আরও সূক্ষ্ম নিয়ন্ত্রণের প্রয়োজন হয়।
এই প্রবন্ধে, আমরা মাইক্রো-ইন্টার্যাকশনের ধারণা নিয়ে আলোচনা করব, তারপরে একটি মাইক্রো-ইন্টার্যাকশন ব্যবহারের ক্ষেত্রে, আমরা একটি স্তম্ভিত অ্যানিমেশন তৈরি করতে স্পষ্ট অ্যানিমেশন ব্যবহার করব যা একটি কলাম উইজেটে থাকা শিশুদের অ্যানিমেট করে।
দুর্দান্ত পণ্যগুলি এমন পণ্য যা বৈশিষ্ট্য এবং বিশদ উভয় ক্ষেত্রেই ভাল সরবরাহ করে। বৈশিষ্ট্যগুলি আপনার পণ্যগুলিতে লোকেদের নিয়ে আসে, তবে বিবরণগুলি তাদের রাখে৷ এই বিবরণগুলি এমন জিনিস যা আপনার অ্যাপটিকে অন্যদের থেকে আলাদা করে তোলে৷ মাইক্রো ইন্টারঅ্যাকশনের মাধ্যমে, আপনি আপনার ব্যবহারকারীদের আনন্দদায়ক প্রতিক্রিয়া প্রদান করে এই বিবরণগুলি তৈরি করতে পারেন।
মাইক্রো-ইন্টারঅ্যাকশনের ধারণাটি এই ধারণার উপর ভিত্তি করে যে ছোট বিবরণ সামগ্রিক ব্যবহারকারীর অভিজ্ঞতার উপর একটি বড় প্রভাব ফেলতে পারে। মাইক্রো-ইন্টারঅ্যাকশনগুলি প্রয়োজনীয় ফাংশনগুলি পরিবেশন করতে ব্যবহার করা যেতে পারে যেমন প্রতিক্রিয়া যোগাযোগ বা একটি কর্মের ফলাফল। মাইক্রো-মিথস্ক্রিয়াগুলির উদাহরণগুলির মধ্যে রয়েছে:
নীচে, আমরা মাইক্রো-ইন্টার্যাকশনের বাস্তব-জীবনের অ্যাপ্লিকেশন দেখতে পাচ্ছি, এই সূক্ষ্ম অ্যানিমেশনগুলি ফ্লটারে তৈরি করা হয়েছে, ব্যবহারকারীর অভিজ্ঞতা উন্নত করতে। 👇🏽
এই অ্যাপগুলির জন্য ডিজাইনের রেফারেন্স ড্রিবল থেকে নেওয়া হয়েছে
এই উদাহরণে, আমরা একটি স্তম্ভিত অ্যানিমেশন তৈরি করব যা একটি কলাম উইজেটে বাচ্চাদের অ্যানিমেট করে যখন সেই পৃষ্ঠাটি সোয়াইপ করা হয়। এটি স্পষ্ট অ্যানিমেশন পদ্ধতি ব্যবহার করে তৈরি করা হবে কারণ এই ক্ষেত্রে, আমরা কীভাবে অ্যানিমেশন চালাতে চাই তার সম্পূর্ণ নিয়ন্ত্রণ আমাদের থাকতে হবে।
👇🏽 শেষ হলে অ্যানিমেশন কেমন হবে তা এখানে
এই টিউটোরিয়ালের সর্বাধিক ব্যবহার করতে, আপনার নিম্নলিখিতগুলি থাকা উচিত:
একবার, আপনি সমস্ত প্রকল্পের পূর্বশর্তগুলি পরীক্ষা করে দেখেছেন, আসুন ডুব দেওয়া যাক৷
প্রথমত, আমরা প্রধান স্ক্রিন তৈরি করব যাতে এই দুটি পৃষ্ঠা রয়েছে। এই দুটি পৃষ্ঠা একটি পেজভিউ উইজেটে মোড়ানো হবে যা সোয়াইপে কোন পৃষ্ঠাটি প্রদর্শিত হবে তা নিয়ন্ত্রণ করে। এছাড়াও, প্রধান স্ক্রিনের নীচে, আমাদের একটি সূচক রয়েছে যা আমাদেরকে দেখায় যে আমরা বর্তমানে যে পৃষ্ঠাটিতে আছি।
class MainScreen extends StatefulWidget { const MainScreen({super.key}); @override State<MainScreen> createState() => _MainScreenState(); } class _MainScreenState extends State<MainScreen>{ final controller = PageController(keepPage: true); @override Widget build(BuildContext context) { return Scaffold( body: SafeArea( child: PageView( controller: controller, children: [ Page1(pageController: controller,), Page2(pageController: controller,), ], ), ), bottomNavigationBar: Container( alignment: Alignment.topCenter, padding: const EdgeInsets.only(top: 10), height: 30, child: SmoothPageIndicator( controller: controller, count: 2, effect: const JumpingDotEffect( dotHeight: 10, dotWidth: 10, activeDotColor: Colors.grey, dotColor: Colors.black12, ), ), ) ); } }
উপরের কোড স্নিপেটে ব্যবহৃত SmoothPageIndicator
pub.dev এ পাওয়া যাবে। এখানে, SmoothPageIndicator
ব্যবহার করা হয় বর্তমান পৃষ্ঠাটি দৃশ্যে দেখানোর জন্য। আপনি প্যাকেজটি আপনার pubspec.yaml
এ যোগ করতে পারেন 👇🏽
dependencies: flutter: sdk: flutter smooth_page_indicator: ^1.1.0
দুটি পৃষ্ঠায়, আমাদের কাছে কয়েকটি খালি কার্ড উইজেট সহ একটি কলাম উইজেট থাকবে। এই খালি কার্ড উইজেটগুলি কলামটি পূরণ করতে ব্যবহার করা হবে যাতে আমরা স্তব্ধ অ্যানিমেশনটি সম্পূর্ণরূপে দেখতে এবং উপলব্ধি করতে পারি। আমরা খালি কার্ড উইজেটটিকে পুনঃব্যবহারযোগ্য উপাদান হিসাবে তৈরি করব যাতে আমরা এটি ব্যবহার করি এমন প্রতিটি জায়গায় স্ক্র্যাচ থেকে এটি তৈরি করতে না হয়।
class EmptyCard extends StatelessWidget { const EmptyCard({super.key, required this.width, required this.height}); final double width; final double height; @override Widget build(BuildContext context) { return Container( height: height, width: width, decoration: BoxDecoration( borderRadius: const BorderRadius.all(Radius.circular(10)), color: Colors.blue.shade200, boxShadow: const [ BoxShadow( color: Colors.black12, blurRadius: 4, offset: Offset(0,4), ) ] ), ); } }
এই সমস্ত বয়লারপ্লেট কোডের পথের বাইরে, আমরা এখন অ্যানিমেশন তৈরি করা শুরু করব। প্রথমত, আমরা একটি নতুন রাষ্ট্রীয় উইজেট তৈরি করব যাকে আমরা AnimateWidget
বলব। অ্যানিমেশন কীভাবে চলবে তা নিয়ন্ত্রণ করার জন্য এতে বেশ কয়েকটি প্যারামিটার থাকবে।
class AnimateWidget extends StatefulWidget { const AnimateWidget({super.key, required this.duration, required this.position, required this.horizontalOffset, required this.child, required this.controller, }); final Duration duration; final int position; final double? horizontalOffset; final Widget child; final PageController controller; @override State<AnimateWidget> createState() => _AnimateWidgetState(); }
উপরের স্নিপেটে দেখা AnimateWidget
প্যারামগুলির সাথে, আমরা সফলভাবে নিয়ন্ত্রণ করতে পারি:
pageController
, যখন পৃষ্ঠাটি সোয়াইপ করা হয় তখন এটি অ্যানিমেশনকে ট্রিগার করে।
এর পরে, AnimateWiget
, আমরা নিম্নলিখিতগুলি সংজ্ঞায়িত করব:
AnimationController
: অ্যানিমেশন সিকোয়েন্স নিয়ন্ত্রণ করতে ব্যবহৃত হয়।
বর্তমান পৃষ্ঠা: বর্তমান পৃষ্ঠার ডেটা ধরে রাখতে ব্যবহার করা হবে।
টাইমার: কিছু বিলম্বের পরে অ্যানিমেশন ট্রিগার করতে ব্যবহার করা হবে; এটিই স্তব্ধ অ্যানিমেশন ক্যাসকেডিং প্রভাব নিয়ে আসবে।
class _AnimateWidgetState extends State<AnimateWidget> with AutomaticKeepAliveClientMixin, SingleTickerProviderStateMixin{ @override bool get wantKeepAlive => true; late AnimationController animationController; int currentPage = 0; Timer? _timer; @override void initState() { super.initState(); animationController = AnimationController(vsync: this, duration: widget.duration); _timer = Timer(getDelay(), animationController.forward); widget.controller.addListener(() { currentPage = widget.controller.page!.round(); if(currentPage == widget.controller.page){ _timer = Timer(getDelay(), animationController.forward); } }); } @override void dispose() { _timer?.cancel(); animationController.dispose(); super.dispose(); } Duration getDelay(){ var delayInMilliSec = widget.duration.inMilliseconds ~/ 6; int getStaggeredListDuration(){ return widget.position * delayInMilliSec; } return Duration(milliseconds: getStaggeredListDuration()); }
উপরের কোড স্নিপেট ভাঙ্গা, আপনি লক্ষ্য করবেন যে:
AnimationController
ব্যবহার করেছি, এই কারণে, আমরা SingleTickerProviderStateMixin
চালু করেছি।
init state
পদ্ধতিতে, আমরা AnimationController
শুরু করেছি, তারপর animationController.forward
, পৃষ্ঠা এন্ট্রি এবং পৃষ্ঠা সোয়াইপ ব্যবহার করে অ্যানিমেশনটি ট্রিগার করেছি।
dispose
পদ্ধতিতে, আমরা সম্পদ পরিষ্কার করেছি।
AutomaticKeepAliveClientMixin
ব্যবহার করেছি। এটি AnimationController
নিষ্পত্তি রোধ করার জন্য, যখন একটি পৃষ্ঠা সোয়াইপ করা হয় এবং আর দৃশ্যমান হয় না।
getDelay
ফাংশনে, আমরা প্রতিটি উপাদানের জন্য অ্যানিমেশন ট্রিগার হওয়ার আগে বিলম্বের হিসাব করেছি। এটি অর্জন করতে, আমরা সময়কালকে মিলিসেকেন্ডে 6 দ্বারা ভাগ করেছি এবং ফলাফলগুলি আনুমানিক করেছি, তারপর এটিকে অবস্থান দ্বারা গুণ করেছি। এটি উপাদানটির অবস্থান (এই ক্ষেত্রে, সূচক)।
বিল্ড পদ্ধতিতে, আমরা একটি AnimatedBuilder
ফেরত দিই। এই অ্যানিমেটেড বিল্ডারে, আমরা _slideAnimation
নামে একটি উইজেট ফাংশন ফেরত দেব যা একটি Transform.translate
উইজেট প্রদান করে। _slideAnimation
ফাংশনে, আমাদের offsetAnimation
ফাংশন আছে।
offsetAnimation
ফাংশন অ্যানিমেশন বৈশিষ্ট্য প্রদান করে যা Transform.translate
উইজেটে ব্যবহৃত হয়। Transform.translate
উইজেট অ্যানিমেশন থেকে মান ব্যবহার করে চাইল্ড উইজেটকে অ্যানিমেট করে।
@override Widget build(BuildContext context) { super.build(context); return AnimatedBuilder( animation: animationController, builder: (context, child){ return _slideAnimation(animationController); } ); } Widget _slideAnimation(Animation<double> animationController){ Animation<double> offsetAnimation(double offset, Animation<double> animationController) { return Tween<double>(begin: offset, end: 0.0).animate( CurvedAnimation( parent: animationController, curve: const Interval(0.0, 1.0, curve: Curves.ease), ), ); } return Transform.translate( offset: Offset( widget.horizontalOffset == 0.0 ? 0.0 : offsetAnimation(widget.horizontalOffset!, animationController).value, 0.0, ), child: widget.child, ); }
এটি AnimateWidget
ক্লাস 👇🏽 এর জন্য সম্পূর্ণ কোড
class AnimateWidget extends StatefulWidget { const AnimateWidget({super.key, required this.duration, required this.position, required this.horizontalOffset, required this.child, required this.controller, }); final Duration duration; final int position; final double? horizontalOffset; final Widget child; final PageController controller; @override State<AnimateWidget> createState() => _AnimateWidgetState(); } class _AnimateWidgetState extends State<AnimateWidget> with AutomaticKeepAliveClientMixin, SingleTickerProviderStateMixin{ @override bool get wantKeepAlive => true; late AnimationController animationController; int currentPage = 0; Timer? _timer; @override void initState() { super.initState(); animationController = AnimationController(vsync: this, duration: widget.duration); _timer = Timer(getDelay(), animationController.forward); widget.controller.addListener(() { currentPage = widget.controller.page!.round(); if(currentPage == widget.controller.page){ _timer = Timer(getDelay(), animationController.forward); } }); } @override void dispose() { _timer?.cancel(); animationController.dispose(); super.dispose(); } Duration getDelay(){ var delayInMilliSec = widget.duration.inMilliseconds ~/ 6; int getStaggeredListDuration(){ return widget.position * delayInMilliSec; } return Duration(milliseconds: getStaggeredListDuration()); } @override Widget build(BuildContext context) { super.build(context); return AnimatedBuilder( animation: animationController, builder: (context, child){ return _slideAnimation(animationController); } ); } Widget _slideAnimation(Animation<double> animationController){ Animation<double> offsetAnimation(double offset, Animation<double> animationController) { return Tween<double>(begin: offset, end: 0.0).animate( CurvedAnimation( parent: animationController, curve: const Interval(0.0, 1.0, curve: Curves.ease), ), ); } return Transform.translate( offset: Offset( widget.horizontalOffset == 0.0 ? 0.0 : offsetAnimation(widget.horizontalOffset!, animationController).value, 0.0, ), child: widget.child, ); } }
এর পরে, একটি কলাম উইজেটে এই AnimateWidget
ক্লাসটি ব্যবহার করার জন্য, আমরা toStaggeredList
নামক একটি স্ট্যাটিক পদ্ধতি সহ একটি ক্লাস তৈরি করব যা একটি তালিকা প্রদান করে, এই পদ্ধতিতে, আমরা একটি তালিকা children
সহ সমস্ত প্রয়োজনীয় প্যারামিটার পাস করি। children
পরামিতি হল যেখানে আমরা উপাদানগুলির তালিকা পাস করব যা আমরা অ্যানিমেটিং করব।
এর পরে, আমরা শিশুদের ম্যাপ করব, প্রতিটি শিশুকে AnimateWidget
দিয়ে মোড়ানো।
class AnimateList{ static List<Widget>toStaggeredList({ required Duration duration, double? horizontalOffset, required PageController controller, required List<Widget>children, })=> children .asMap() .map((index, widget){ return MapEntry( index, AnimateWidget( duration: duration, position: index, horizontalOffset: horizontalOffset, controller: controller, child: widget, ) ); }) .values .toList(); }
AnimateWidget
এ, তালিকার প্রতিটি শিশুকে সফলভাবে অ্যানিমেট করার জন্য আমরা প্রয়োজনীয় প্যারামিটারগুলি পাস করি। AnimateList.toStaggeredList
পদ্ধতি ব্যবহার করে, আমরা এখন যে দুটি পৃষ্ঠায় কাজ করছি সেখানে এটি বাস্তবায়ন করতে পারি।
class Page1 extends StatelessWidget { const Page1({super.key, required this.pageController}); final PageController pageController; @override Widget build(BuildContext context) { return SingleChildScrollView( padding: const EdgeInsets.all(16), child: Column( children: AnimateList.toStaggeredList( duration: const Duration(milliseconds: 375), controller: pageController, horizontalOffset: MediaQuery.of(context).size.width / 2, children: [ const EmptyCard(width: 250, height: 50,), const Padding( padding: EdgeInsets.only(top: 20), child: EmptyCard(width: 180, height: 80,), ), const Padding( padding: EdgeInsets.only(top: 20), child: EmptyCard(width: 270, height: 50,), ), const Padding( padding: EdgeInsets.symmetric(vertical: 20), child: Row( mainAxisAlignment: MainAxisAlignment.spaceEvenly, children: [ EmptyCard(height: 50, width: 70), EmptyCard(height: 50, width: 70), EmptyCard(height: 50, width: 70), ], ), ), const EmptyCard(width: 250, height: 50,), const Padding( padding: EdgeInsets.only(top: 20), child: EmptyCard(width: 180, height: 80,), ), const Padding( padding: EdgeInsets.only(top: 20), child: EmptyCard(width: 270, height: 50,), ), const Padding( padding: EdgeInsets.symmetric(vertical: 20), child: Row( mainAxisAlignment: MainAxisAlignment.spaceEvenly, children: [ EmptyCard(height: 50, width: 70), EmptyCard(height: 50, width: 70), EmptyCard(height: 50, width: 70), ], ), ), ], ), ), ); } } class Page2 extends StatelessWidget { const Page2({super.key, required this.pageController}); final PageController pageController; @override Widget build(BuildContext context) { return SingleChildScrollView( padding: const EdgeInsets.all(16), child: Column( children: AnimateList.toStaggeredList( duration: const Duration(milliseconds: 375), controller: pageController, horizontalOffset: MediaQuery.of(context).size.width / 2, children: [ const EmptyCard(width: 220, height: 70,), const Padding( padding: EdgeInsets.only(top: 20), child: EmptyCard(width: 300, height: 70,), ), const Padding( padding: EdgeInsets.only(top: 20), child: EmptyCard(width: 200, height: 50,), ), const Padding( padding: EdgeInsets.symmetric(vertical: 20), child: Row( mainAxisAlignment: MainAxisAlignment.spaceEvenly, children: [ EmptyCard(height: 70, width: 70), EmptyCard(height: 70, width: 70), ], ), ), const EmptyCard(width: 220, height: 70,), const Padding( padding: EdgeInsets.only(top: 20), child: EmptyCard(width: 300, height: 70,), ), const Padding( padding: EdgeInsets.symmetric(vertical: 20), child: Row( mainAxisAlignment: MainAxisAlignment.spaceEvenly, children: [ EmptyCard(height: 70, width: 70), EmptyCard(height: 70, width: 70), ], ), ), ], ), ), ); } }
কলাম উইজেটের বাচ্চাদের মধ্যে, আমরা AnimateList.toStaggeredList
পাস করব এবং কলামে প্রদর্শিত উইজেটগুলি সহ প্রয়োজনীয় প্যারামিটারগুলি পাস করব। এটির মাধ্যমে, আমরা সোয়াইপে ট্রিগার করা একটি স্তম্ভিত অ্যানিমেশন সফলভাবে তৈরি করেছি। আপনি এখানে সম্পূর্ণ কোড চেক আউট করতে পারেন.
এটি আমাদের চূড়ান্ত ফলাফল:
আমরা এই টিউটোরিয়ালের শেষে চলে এসেছি। এই মুহুর্তে, আমরা মাইক্রো-ইন্টার্যাকশনের ধারণাটি কভার করেছি এবং ব্যবহারকারীর অভিজ্ঞতার উপর এর প্রভাব রয়েছে। আমরা পৃষ্ঠা সোয়াইপে ট্রিগার করা একটি কলাম উইজেটে আইটেমগুলির স্তম্ভিত অ্যানিমেশন তৈরির প্রক্রিয়ার মধ্য দিয়েও গিয়েছিলাম।
আপনার প্রকল্পের ব্যবহারকারীর অভিজ্ঞতা উন্নত করতে আপনি আপনার অ্যাপে অনেক ধরণের মাইক্রো-ইন্টার্যাকশন যোগ করতে পারেন; আপনি Flutter এ আরও ইন্টারেক্টিভ অ্যানিমেশন তৈরি করে পরীক্ষা করতে পারেন।
আপনি যদি এই নিবন্ধটি সহায়ক বলে মনে করেন তবে আপনি একটি লাইক বা মন্তব্য রেখে এটিকে সমর্থন করতে পারেন। আপনি আরও সম্পর্কিত নিবন্ধের জন্য আমাকে অনুসরণ করতে পারেন.