১. ভূমিকা
ম্যাটেরিয়াল ডিজাইন হলো আকর্ষণীয় ও সুন্দর ডিজিটাল পণ্য তৈরির একটি পদ্ধতি। একগুচ্ছ সুসংহত নীতি ও উপাদানের অধীনে স্টাইল, ব্র্যান্ডিং, ইন্টারঅ্যাকশন এবং মোশনকে একত্রিত করার মাধ্যমে প্রোডাক্ট টিমগুলো তাদের ডিজাইনের সর্বোচ্চ সম্ভাবনাকে কাজে লাগাতে পারে।
| ম্যাটেরিয়াল কম্পোনেন্টস (MDC) ডেভেলপারদের ম্যাটেরিয়াল ডিজাইন বাস্তবায়ন করতে সাহায্য করে। গুগলের একদল ইঞ্জিনিয়ার এবং ইউএক্স ডিজাইনার দ্বারা তৈরি, MDC-তে কয়েক ডজন সুন্দর এবং কার্যকরী UI কম্পোনেন্ট রয়েছে এবং এটি অ্যান্ড্রয়েড, আইওএস, ওয়েব এবং ফ্লাটারের জন্য উপলব্ধ। material.io/develop |
ফ্লাটারের জন্য ম্যাটেরিয়ালের মোশন সিস্টেমটি কী?
ফ্লাটারের ম্যাটেরিয়াল মোশন সিস্টেম হলো অ্যানিমেশন প্যাকেজের অন্তর্গত একগুচ্ছ ট্রানজিশন প্যাটার্ন, যা ম্যাটেরিয়াল ডিজাইন নির্দেশিকায় বর্ণিত অনুযায়ী ব্যবহারকারীদের একটি অ্যাপ বুঝতে ও নেভিগেট করতে সাহায্য করে।
উপাদান রূপান্তরের চারটি প্রধান ধরণ নিম্নরূপ:
- কন্টেইনার ট্রান্সফর্ম: কন্টেইনারযুক্ত UI এলিমেন্টগুলোর মধ্যে ট্রানজিশন; একটি এলিমেন্টকে নির্বিঘ্নে অন্যটিতে রূপান্তরিত করার মাধ্যমে দুটি স্বতন্ত্র UI এলিমেন্টের মধ্যে একটি দৃশ্যমান সংযোগ তৈরি করে।

- শেয়ার্ড অ্যাক্সিস: এমন UI এলিমেন্টগুলোর মধ্যে ট্রানজিশন, যাদের মধ্যে একটি স্থানিক বা নেভিগেশনাল সম্পর্ক রয়েছে; এলিমেন্টগুলোর মধ্যকার সম্পর্ককে আরও দৃঢ় করতে x, y, বা z অ্যাক্সিসে একটি শেয়ার্ড ট্রান্সফরমেশন ব্যবহার করে।

- ফেড থ্রু: এমন UI এলিমেন্টগুলোর মধ্যে ট্রানজিশন বা রূপান্তর, যাদের একে অপরের সাথে কোনো দৃঢ় সম্পর্ক নেই; এটি একটি ক্রমিক ফেড আউট ও ফেড ইন পদ্ধতি ব্যবহার করে এবং আগত এলিমেন্টটিকে স্কেল করে।

- ফেড: স্ক্রিনের সীমানার মধ্যে UI এলিমেন্টগুলোর প্রবেশ বা প্রস্থানের জন্য ব্যবহৃত হয়।

অ্যানিমেশন প্যাকেজটি এই প্যাটার্নগুলির জন্য ট্রানজিশন উইজেট সরবরাহ করে, যা ফ্লাটার অ্যানিমেশন লাইব্রেরি ( flutter/animation.dart ) এবং ফ্লাটার ম্যাটেরিয়াল লাইব্রেরি ( flutter/material.dart ) উভয়ের উপর ভিত্তি করে নির্মিত:
এই কোডল্যাবে আপনারা ফ্লাটার ফ্রেমওয়ার্ক এবং ম্যাটেরিয়াল লাইব্রেরির উপর ভিত্তি করে নির্মিত ম্যাটেরিয়াল ট্রানজিশন ব্যবহার করবেন, অর্থাৎ আপনারা উইজেট নিয়ে কাজ করবেন। :)
আপনি যা তৈরি করবেন
এই কোডল্যাবটি আপনাকে ডার্ট ব্যবহার করে 'Reply' নামক একটি উদাহরণ ফ্লাটার ইমেল অ্যাপে কিছু ট্রানজিশন তৈরি করতে সাহায্য করবে, যা দেখাবে কীভাবে আপনি অ্যানিমেশন প্যাকেজের ট্রানজিশন ব্যবহার করে আপনার অ্যাপের চেহারা ও অনুভূতি নিজের মতো করে সাজিয়ে নিতে পারেন।
Reply অ্যাপটির জন্য স্টার্টার কোড সরবরাহ করা হবে, এবং আপনাকে অ্যাপটিতে নিম্নলিখিত ম্যাটেরিয়াল ট্রানজিশনগুলো অন্তর্ভুক্ত করতে হবে, যা নিচে সম্পূর্ণ কোডল্যাবের GIF-টিতে দেখা যাচ্ছে:
- ইমেল তালিকা থেকে ইমেল বিস্তারিত পৃষ্ঠায় কন্টেইনার ট্রান্সফর্ম ট্রানজিশন
- FAB থেকে ইমেল কম্পোজ পেজে কন্টেইনার ট্রান্সফর্ম ট্রানজিশন
- সার্চ আইকন থেকে সার্চ ভিউ পেজে শেয়ার্ড জেড-অ্যাক্সিস ট্রানজিশন
- মেইলবক্স পৃষ্ঠাগুলির মধ্যে ধীরে ধীরে পরিবর্তন
- রচনা এবং উত্তরের মধ্যে Fade Through রূপান্তর FAB
- অদৃশ্য হয়ে যাওয়া মেইলবক্সের শিরোনামের মধ্যে ধীরে ধীরে পরিবর্তন
- বটম অ্যাপ বারের অ্যাকশনগুলির মধ্যে ফেড থ্রু ট্রানজিশন

আপনার যা যা লাগবে
- ফ্লাটার ডেভেলপমেন্ট এবং ডার্ট সম্পর্কে প্রাথমিক জ্ঞান
- একটি কোড সম্পাদক
- একটি অ্যান্ড্রয়েড/আইওএস এমুলেটর বা ডিভাইস
- নমুনা কোড (পরবর্তী ধাপ দেখুন)
ফ্লাটার অ্যাপ তৈরিতে আপনার অভিজ্ঞতার স্তরকে আপনি কীভাবে মূল্যায়ন করবেন?
এই কোডল্যাব থেকে আপনি কী শিখতে চান?
২. আপনার ফ্লাটার ডেভেলপমেন্ট পরিবেশ সেট আপ করুন।
এই ল্যাবটি সম্পন্ন করার জন্য আপনার দুটি সফটওয়্যার প্রয়োজন— ফ্লাটার এসডিকে এবং একটি এডিটর ।
আপনি এই ডিভাইসগুলোর যেকোনো একটি ব্যবহার করে কোডল্যাবটি চালাতে পারেন:
- আপনার কম্পিউটারের সাথে সংযুক্ত এবং ডেভেলপার মোডে সেট করা একটি ফিজিক্যাল অ্যান্ড্রয়েড বা আইওএস ডিভাইস।
- iOS সিমুলেটর (এর জন্য Xcode টুলস ইনস্টল করা প্রয়োজন)।
- অ্যান্ড্রয়েড এমুলেটর (অ্যান্ড্রয়েড স্টুডিওতে সেটআপ করা প্রয়োজন)।
- একটি ব্রাউজার (ডিবাগিংয়ের জন্য ক্রোম আবশ্যক)।
- একটি Windows , Linux , বা macOS ডেস্কটপ অ্যাপ্লিকেশন হিসেবে, আপনাকে অবশ্যই সেই প্ল্যাটফর্মে ডেভেলপ করতে হবে যেখানে আপনি এটি ডেপ্লয় করার পরিকল্পনা করছেন। সুতরাং, আপনি যদি একটি Windows ডেস্কটপ অ্যাপ ডেভেলপ করতে চান, তবে উপযুক্ত বিল্ড চেইন অ্যাক্সেস করার জন্য আপনাকে অবশ্যই Windows-এই ডেভেলপ করতে হবে। অপারেটিং সিস্টেম-ভিত্তিক কিছু নির্দিষ্ট আবশ্যকতা রয়েছে, যা docs.flutter.dev/desktop- এ বিস্তারিতভাবে আলোচনা করা হয়েছে।
৩. কোডল্যাব স্টার্টার অ্যাপটি ডাউনলোড করুন।
বিকল্প ১: গিটহাব থেকে স্টার্টার কোডল্যাব অ্যাপটি ক্লোন করুন
গিটহাব থেকে এই কোডল্যাবটি ক্লোন করতে, নিম্নলিখিত কমান্ডগুলো চালান:
git clone https://github.com/material-components/material-components-flutter-motion-codelab.git cd material-components-flutter-motion-codelab
বিকল্প ২: স্টার্টার কোডল্যাব অ্যাপের জিপ ফাইলটি ডাউনলোড করুন।
স্টার্টার অ্যাপটি material-components-flutter-motion-codelab-starter ডিরেক্টরিতে রয়েছে।
প্রকল্পের নির্ভরতা যাচাই করুন
প্রজেক্টটি 'animations' প্যাকেজের উপর নির্ভরশীল। pubspec.yaml ফাইলে, ' dependencies সেকশনে নিম্নলিখিত বিষয়গুলো অন্তর্ভুক্ত রয়েছে তা লক্ষ্য করুন:
animations: ^2.0.0
প্রজেক্টটি খুলুন এবং অ্যাপটি চালান।
- আপনার পছন্দের এডিটরে প্রজেক্টটি খুলুন।
- আপনার নির্বাচিত এডিটরের জন্য " Get Started: Test drive "-এ থাকা "Run the app" নির্দেশাবলী অনুসরণ করুন।
সফল! Reply-এর হোমপেজের স্টার্টার কোডটি আপনার ডিভাইস/এমুলেটরে চালু হওয়া উচিত। আপনি ইনবক্সটি দেখতে পাবেন, যেখানে ইমেইলের একটি তালিকা থাকবে।

ঐচ্ছিক: ডিভাইসের অ্যানিমেশনগুলির গতি কমান
যেহেতু এই কোডল্যাবে দ্রুত অথচ নিখুঁত ট্রানজিশন রয়েছে, তাই এটি প্রয়োগ করার সময় ট্রানজিশনের কিছু সূক্ষ্ম বিবরণ পর্যবেক্ষণ করার জন্য ডিভাইসের অ্যানিমেশনগুলো ধীর করে নেওয়া সহায়ক হতে পারে। এটি অ্যাপের ভেতরের একটি সেটিংয়ের মাধ্যমে করা যায়, যা নিচের ড্রয়ারটি খোলা থাকা অবস্থায় সেটিংস আইকনে ট্যাপ করে অ্যাক্সেস করা যায়। চিন্তা করবেন না, ডিভাইসের অ্যানিমেশন ধীর করার এই পদ্ধতিটি রিপ্লাই অ্যাপের বাইরের ডিভাইসের অ্যানিমেশনগুলোকে প্রভাবিত করবে না।

ঐচ্ছিক: ডার্ক মোড
Reply-এর উজ্জ্বল থিম যদি আপনার চোখে কষ্ট দেয়, তবে আর খোঁজার দরকার নেই। অ্যাপটির ভেতরেই একটি সেটিং রয়েছে, যা ব্যবহার করে আপনি অ্যাপের থিমটি ডার্ক মোডে পরিবর্তন করতে পারবেন, যা আপনার চোখের জন্য আরও আরামদায়ক হবে। নিচের ড্রয়ারটি খোলা থাকা অবস্থায় সেটিংস আইকনে ট্যাপ করে এই সেটিংটি ব্যবহার করা যাবে।

৪. নমুনা অ্যাপ কোডটির সাথে পরিচিত হন।
চলুন কোডটি দেখি। আমরা এমন একটি অ্যাপ দিয়েছি যা অ্যাপ্লিকেশনের বিভিন্ন স্ক্রিনের মধ্যে পরিবর্তন আনার জন্য অ্যানিমেশন প্যাকেজ ব্যবহার করে।
- হোমপেজ: নির্বাচিত মেইলবক্স প্রদর্শন করে
- ইনবক্সপেজ : ইমেইলের তালিকা প্রদর্শন করে
- MailPreviewCard : ইমেইলের একটি প্রিভিউ প্রদর্শন করে।
- MailViewPage: একটি একক, সম্পূর্ণ ইমেল প্রদর্শন করে
- ComposePage: এর মাধ্যমে একটি নতুন ইমেল রচনা করা যায়।
- SearchPage: একটি অনুসন্ধান দৃশ্য প্রদর্শন করে
রাউটার.ডার্ট
প্রথমে, অ্যাপটির রুট নেভিগেশন কীভাবে সেটআপ করা হয়েছে তা বোঝার জন্য, lib ডিরেক্টরিতে থাকা router.dart খুলুন:
class ReplyRouterDelegate extends RouterDelegate<ReplyRoutePath>
with ChangeNotifier, PopNavigatorRouterDelegateMixin<ReplyRoutePath> {
ReplyRouterDelegate({required this.replyState})
: navigatorKey = GlobalObjectKey<NavigatorState>(replyState) {
replyState.addListener(() {
notifyListeners();
});
}
@override
final GlobalKey<NavigatorState> navigatorKey;
RouterProvider replyState;
@override
void dispose() {
replyState.removeListener(notifyListeners);
super.dispose();
}
@override
ReplyRoutePath get currentConfiguration => replyState.routePath!;
@override
Widget build(BuildContext context) {
return MultiProvider(
providers: [
ChangeNotifierProvider<RouterProvider>.value(value: replyState),
],
child: Selector<RouterProvider, ReplyRoutePath?>(
selector: (context, routerProvider) => routerProvider.routePath,
builder: (context, routePath, child) {
return Navigator(
key: navigatorKey,
onPopPage: _handlePopPage,
pages: [
// TODO: Add Shared Z-Axis transition from search icon to search view page (Motion)
const CustomTransitionPage(
transitionKey: ValueKey('Home'),
screen: HomePage(),
),
if (routePath is ReplySearchPath)
const CustomTransitionPage(
transitionKey: ValueKey('Search'),
screen: SearchPage(),
),
],
);
},
),
);
}
bool _handlePopPage(Route<dynamic> route, dynamic result) {
// _handlePopPage should not be called on the home page because the
// PopNavigatorRouterDelegateMixin will bubble up the pop to the
// SystemNavigator if there is only one route in the navigator.
assert(route.willHandlePopInternally ||
replyState.routePath is ReplySearchPath);
final bool didPop = route.didPop(result);
if (didPop) replyState.routePath = const ReplyHomePath();
return didPop;
}
@override
Future<void> setNewRoutePath(ReplyRoutePath configuration) {
replyState.routePath = configuration;
return SynchronousFuture<void>(null);
}
}
এটি আমাদের রুট নেভিগেটর, এবং এটি আমাদের অ্যাপের সেই স্ক্রিনগুলো পরিচালনা করে যেগুলো পুরো ক্যানভাস জুড়ে থাকে, যেমন HomePage এবং SearchPage । আমরা ‘ ReplySearchPath এ রুট সেট করেছি কিনা তা পরীক্ষা করার জন্য এটি আমাদের অ্যাপের স্টেট পর্যবেক্ষণ করে। যদি আমরা তা করে থাকি, তবে এটি স্ট্যাকের শীর্ষে SearchPage রেখে আমাদের নেভিগেটরটি পুনরায় তৈরি করে। লক্ষ্য করুন যে, আমাদের স্ক্রিনগুলো একটি CustomTransitionPage মধ্যে মোড়ানো আছে, যেখানে কোনো ট্রানজিশন সংজ্ঞায়িত করা নেই। এটি আপনাকে কোনো কাস্টম ট্রানজিশন ছাড়াই স্ক্রিনগুলোর মধ্যে নেভিগেট করার একটি উপায় দেখায়।
হোম.ডার্ট
home.dart এর _BottomAppBarActionItems এর ভিতরে নিম্নলিখিত কাজটি করে আমরা আমাদের অ্যাপের স্টেটে ReplySearchPath এ রাউট সেট করি:
Align(
alignment: AlignmentDirectional.bottomEnd,
child: IconButton(
icon: const Icon(Icons.search),
color: ReplyColors.white50,
onPressed: () {
Provider.of<RouterProvider>(
context,
listen: false,
).routePath = const ReplySearchPath();
},
),
);
আমাদের onPressed প্যারামিটারে, আমরা RouterProvider অ্যাক্সেস করি এবং এর routePath ReplySearchPath এ সেট করি। আমাদের RouterProvider আমাদের রুট নেভিগেটরের অবস্থার হিসাব রাখে।
mail_view_router.dart
এখন, চলুন দেখি আমাদের অ্যাপের অভ্যন্তরীণ নেভিগেশন কীভাবে সেট আপ করা হয়েছে, lib ডিরেক্টরিতে থাকা mail_view_router.dart খুলুন। আপনি উপরেরটির মতো একটি নেভিগেটর দেখতে পাবেন:
class MailViewRouterDelegate extends RouterDelegate<void>
with ChangeNotifier, PopNavigatorRouterDelegateMixin {
MailViewRouterDelegate({required this.drawerController});
final AnimationController drawerController;
@override
Widget build(BuildContext context) {
bool _handlePopPage(Route<dynamic> route, dynamic result) {
return false;
}
return Selector<EmailStore, String>(
selector: (context, emailStore) => emailStore.currentlySelectedInbox,
builder: (context, currentlySelectedInbox, child) {
return Navigator(
key: navigatorKey,
onPopPage: _handlePopPage,
pages: [
// TODO: Add Fade through transition between mailbox pages (Motion)
CustomTransitionPage(
transitionKey: ValueKey(currentlySelectedInbox),
screen: InboxPage(
destination: currentlySelectedInbox,
),
)
],
);
},
);
}
...
}
এটি আমাদের অভ্যন্তরীণ নেভিগেটর। এটি আমাদের অ্যাপের সেইসব অভ্যন্তরীণ স্ক্রিন পরিচালনা করে যেগুলো শুধু ক্যানভাসের মূল অংশ ব্যবহার করে, যেমন InboxPage । আমাদের অ্যাপের স্টেটে বর্তমান মেইলবক্স কোনটি, তার উপর নির্ভর করে InboxPage ইমেইলের একটি তালিকা প্রদর্শন করে। যখনই আমাদের অ্যাপের স্টেটের ' currentlySelectedInbox ' প্রপার্টিতে কোনো পরিবর্তন আসে, তখনই স্ট্যাকের শীর্ষে সঠিক InboxPage দিয়ে নেভিগেটরটি পুনরায় তৈরি করা হয়।
হোম.ডার্ট
home.dart এর _HomePageState এর ভিতরে নিম্নলিখিত কাজটি করে আমরা আমাদের অ্যাপের স্টেটে বর্তমান মেইলবক্স সেট করি:
void _onDestinationSelected(String destination) {
var emailStore = Provider.of<EmailStore>(
context,
listen: false,
);
if (emailStore.onMailView) {
emailStore.currentlySelectedEmailId = -1;
}
if (emailStore.currentlySelectedInbox != destination) {
emailStore.currentlySelectedInbox = destination;
}
setState(() {});
}
আমাদের _onDestinationSelected ফাংশনে, আমরা EmailStore অ্যাক্সেস করি এবং এর currentlySelectedInbox কে নির্বাচিত গন্তব্যে সেট করি। আমাদের EmailStore আমাদের অভ্যন্তরীণ নেভিগেটরগুলোর অবস্থা ট্র্যাক করে।
হোম.ডার্ট
সবশেষে, নেভিগেশন রাউটিং-এর একটি উদাহরণ দেখতে, lib ডিরেক্টরিতে থাকা home.dart খুলুন। InkWell উইজেটের onTap প্রপার্টির ভিতরে _ReplyFabState ক্লাসটি খুঁজুন, যা দেখতে এইরকম হবে:
onTap: () {
Provider.of<EmailStore>(
context,
listen: false,
).onCompose = true;
Navigator.of(context).push(
PageRouteBuilder(
pageBuilder: (
BuildContext context,
Animation<double> animation,
Animation<double> secondaryAnimation,
) {
return const ComposePage();
},
),
);
},
এখানে দেখানো হয়েছে, কীভাবে কোনো কাস্টম ট্রানজিশন ছাড়াই ইমেইল কম্পোজ পেজে যাওয়া যায়। এই কোডল্যাবে, আপনি রিপ্লাই-এর কোডের গভীরে গিয়ে ম্যাটেরিয়াল ট্রানজিশন সেট আপ করবেন, যা অ্যাপ জুড়ে বিভিন্ন নেভিগেশন অ্যাকশনের সাথে একযোগে কাজ করে।
এখন যেহেতু আপনি স্টার্টার কোডের সাথে পরিচিত হয়েছেন, চলুন আমাদের প্রথম ট্রানজিশনটি বাস্তবায়ন করি।
৫. ইমেল তালিকা থেকে ইমেল বিস্তারিত পৃষ্ঠায় কন্টেইনার ট্রান্সফর্ম ট্রানজিশন যোগ করুন।
শুরুতে, আপনি একটি ইমেইলে ক্লিক করার সময় একটি ট্রানজিশন যোগ করবেন। এই নেভিগেশন পরিবর্তনের জন্য কন্টেইনার ট্রান্সফর্ম প্যাটার্নটি বেশ উপযুক্ত, কারণ এটি কন্টেইনারযুক্ত UI এলিমেন্টগুলোর মধ্যে ট্রানজিশনের জন্য ডিজাইন করা হয়েছে। এই প্যাটার্নটি দুটি UI এলিমেন্টের মধ্যে একটি দৃশ্যমান সংযোগ তৈরি করে।
কোনো কোড যোগ করার আগে, রিপ্লাই অ্যাপটি চালিয়ে একটি ইমেইলে ক্লিক করে দেখুন। এতে একটি সাধারণ জাম্প-কাট হওয়া উচিত, যার অর্থ হলো কোনো ট্রানজিশন ছাড়াই স্ক্রিনটি প্রতিস্থাপিত হয়ে যাবে।
আগে

নিচের কোড স্নিপেটে দেখানো অনুযায়ী mail_card_preview.dart ফাইলের শীর্ষে animations প্যাকেজটি ইম্পোর্ট করে শুরু করুন:
মেইল_কার্ড_প্রিভিউ.ডার্ট
import 'package:animations/animations.dart';
এখন যেহেতু আপনি অ্যানিমেশন প্যাকেজটি ইম্পোর্ট করেছেন, আমরা আপনার অ্যাপে সুন্দর ট্রানজিশন যোগ করা শুরু করতে পারি। চলুন, প্রথমে একটি StatelessWidget ক্লাস তৈরি করি, যেখানে আমাদের OpenContainer উইজেটটি থাকবে।
mail_card_preview.dart ফাইলে, MailPreviewCard এর ক্লাস ডেফিনিশনের পরে নিম্নলিখিত কোড স্নিপেটটি যোগ করুন:
মেইল_কার্ড_প্রিভিউ.ডার্ট
// TODO: Add Container Transform transition from email list to email detail page (Motion)
class _OpenContainerWrapper extends StatelessWidget {
const _OpenContainerWrapper({
required this.id,
required this.email,
required this.closedChild,
});
final int id;
final Email email;
final Widget closedChild;
@override
Widget build(BuildContext context) {
final theme = Theme.of(context);
return OpenContainer(
openBuilder: (context, closedContainer) {
return MailViewPage(id: id, email: email);
},
openColor: theme.cardColor,
closedShape: const RoundedRectangleBorder(
borderRadius: BorderRadius.all(Radius.circular(0)),
),
closedElevation: 0,
closedColor: theme.cardColor,
closedBuilder: (context, openContainer) {
return InkWell(
onTap: () {
Provider.of<EmailStore>(
context,
listen: false,
).currentlySelectedEmailId = id;
openContainer();
},
child: closedChild,
);
},
);
}
}
এবার চলুন আমাদের নতুন র্যাপারটি ব্যবহার করা যাক। MailPreviewCard ক্লাস ডেফিনিশনের ভিতরে আমরা আমাদের build() ফাংশনের Material উইজেটটিকে আমাদের নতুন _OpenContainerWrapper দিয়ে র্যাপ করব:
মেইল_কার্ড_প্রিভিউ.ডার্ট
// TODO: Add Container Transform transition from email list to email detail page (Motion)
return _OpenContainerWrapper(
id: id,
email: email,
closedChild: Material(
...
আমাদের _OpenContainerWrapper একটি InkWell উইজেট আছে এবং OpenContainer এর কালার প্রোপার্টিগুলো এর ভেতরের কন্টেইনারের রঙ নির্ধারণ করে। তাই, আমরা Material এবং Inkwell উইজেটগুলো সরিয়ে ফেলতে পারি। এর ফলে কোডটি দেখতে নিম্নরূপ হবে:
মেইল_কার্ড_প্রিভিউ.ডার্ট
// TODO: Add Container Transform transition from email list to email detail page (Motion)
return _OpenContainerWrapper(
id: id,
email: email,
closedChild: Dismissible(
key: ObjectKey(email),
dismissThresholds: const {
DismissDirection.startToEnd: 0.8,
DismissDirection.endToStart: 0.4,
},
onDismissed: (direction) {
switch (direction) {
case DismissDirection.endToStart:
if (onStarredInbox) {
onStar();
}
break;
case DismissDirection.startToEnd:
onDelete();
break;
default:
}
},
background: _DismissibleContainer(
icon: 'twotone_delete',
backgroundColor: colorScheme.primary,
iconColor: ReplyColors.blue50,
alignment: Alignment.centerLeft,
padding: const EdgeInsetsDirectional.only(start: 20),
),
confirmDismiss: (direction) async {
if (direction == DismissDirection.endToStart) {
if (onStarredInbox) {
return true;
}
onStar();
return false;
} else {
return true;
}
},
secondaryBackground: _DismissibleContainer(
icon: 'twotone_star',
backgroundColor: currentEmailStarred
? colorScheme.secondary
: theme.scaffoldBackgroundColor,
iconColor: currentEmailStarred
? colorScheme.onSecondary
: colorScheme.onBackground,
alignment: Alignment.centerRight,
padding: const EdgeInsetsDirectional.only(end: 20),
),
child: mailPreview,
),
);
এই পর্যায়ে, আপনার একটি সম্পূর্ণ কার্যকর কন্টেইনার ট্রান্সফর্ম থাকা উচিত। কোনো ইমেইলে ক্লিক করলে লিস্ট আইটেমটি প্রসারিত হয়ে একটি ডিটেইলস স্ক্রিন তৈরি করে এবং ইমেইলের তালিকাটি সংকুচিত হয়ে যায়। ব্যাক বাটন চাপলে ইমেইলের ডিটেইলস স্ক্রিনটি আবার সংকুচিত হয়ে লিস্ট আইটেমে পরিণত হয় এবং ইমেইলের তালিকাটি বড় হতে থাকে।
পরে

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

এই ট্রানজিশনটি কনফিগার করার পদ্ধতিটি আগের ধাপের মতোই হবে, যেহেতু আমরা একই উইজেট ক্লাস, অর্থাৎ OpenContainer ব্যবহার করছি।
home.dart ফাইলের একদম উপরে, package:animations/animations.dart ইম্পোর্ট করুন এবং _ReplyFabState build() মেথডটি পরিবর্তন করুন। এবার রিটার্ন করা Material উইজেটটিকে একটি OpenContainer উইজেট দিয়ে র্যাপ করুন:
হোম.ডার্ট
// TODO: Add Container Transform from FAB to compose email page (Motion)
return OpenContainer(
openBuilder: (context, closedContainer) {
return const ComposePage();
},
openColor: theme.cardColor,
onClosed: (success) {
Provider.of<EmailStore>(
context,
listen: false,
).onCompose = false;
},
closedShape: circleFabBorder,
closedColor: theme.colorScheme.secondary,
closedElevation: 6,
closedBuilder: (context, openContainer) {
return Material(
color: theme.colorScheme.secondary,
...
আমাদের পূর্ববর্তী OpenContainer উইজেটটি কনফিগার করতে ব্যবহৃত প্যারামিটারগুলোর পাশাপাশি, এখন onClosed ও সেট করা হচ্ছে। onClosed হলো একটি ClosedCallback , যা তখন কল করা হয় যখন OpenContainer রাউটটি পপ করা হয় বা ক্লোজড অবস্থায় ফিরে আসে। সেই ট্রানজ্যাকশনের রিটার্ন ভ্যালুটি এই ফাংশনে একটি আর্গুমেন্ট হিসেবে পাস করা হয়। আমরা এই Callback ব্যবহার করি আমাদের অ্যাপের প্রোভাইডারকে জানাতে যে আমরা ComposePage রাউটটি ছেড়ে এসেছি, যাতে এটি সমস্ত লিসেনারকে অবহিত করতে পারে।
আমাদের আগের ধাপের মতোই, আমরা আমাদের উইজেট থেকে Material উইজেটটি সরিয়ে দেব, কারণ OpenContainer উইজেটটি closedColor এর মাধ্যমে closedBuilder থেকে ফেরত আসা উইজেটের রঙ নিয়ন্ত্রণ করে। এছাড়াও, আমরা আমাদের InkWell উইজেটের onTap এর ভেতর থেকে Navigator.push() কলটি সরিয়ে দেব এবং এর পরিবর্তে OpenContainer উইজেটের closedBuilder থেকে পাওয়া openContainer() Callback ব্যবহার করব, কারণ এখন OpenContainer উইজেটটি তার নিজের রাউটিং নিজেই সামলাচ্ছে।
ফলস্বরূপ কোডটি নিম্নরূপ:
হোম.ডার্ট
// TODO: Add Container Transform from FAB to compose email page (Motion)
return OpenContainer(
openBuilder: (context, closedContainer) {
return const ComposePage();
},
openColor: theme.cardColor,
onClosed: (success) {
Provider.of<EmailStore>(
context,
listen: false,
).onCompose = false;
},
closedShape: circleFabBorder,
closedColor: theme.colorScheme.secondary,
closedElevation: 6,
closedBuilder: (context, openContainer) {
return Tooltip(
message: tooltip,
child: InkWell(
customBorder: circleFabBorder,
onTap: () {
Provider.of<EmailStore>(
context,
listen: false,
).onCompose = true;
openContainer();
},
child: SizedBox(
height: _mobileFabDimension,
width: _mobileFabDimension,
child: Center(
child: fabSwitcher,
),
),
),
);
},
);
এখন কিছু পুরোনো কোড পরিপাটি করার পালা। যেহেতু আমাদের OpenContainer উইজেটটি এখন onClosed ClosedCallback এর মাধ্যমে আমাদের অ্যাপের প্রোভাইডারকে জানিয়ে দেয় যে আমরা আর ComposePage এ নেই, তাই আমরা mail_view_router.dart থেকে আমাদের আগের ইমপ্লিমেন্টেশনটি সরিয়ে ফেলতে পারি:
mail_view_router.dart
// TODO: Add Container Transform from FAB to compose email page (Motion)
emailStore.onCompose = false; /// delete this line
return SynchronousFuture<bool>(true);
এই ধাপের জন্য এটুকুই! FAB থেকে কম্পোজ স্ক্রিনে আপনার একটি ট্রানজিশন থাকা উচিত যা দেখতে নিচের ছবির মতো হবে:
পরে

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

শুরু করার জন্য, চলুন আমাদের router.dart ফাইলে যাই। আমাদের ReplySearchPath ক্লাস ডেফিনিশনের পরে নিম্নলিখিত কোডটুকু যোগ করুন:
রাউটার.ডার্ট
// TODO: Add Shared Z-Axis transition from search icon to search view page (Motion)
class SharedAxisTransitionPageWrapper extends Page {
const SharedAxisTransitionPageWrapper(
{required this.screen, required this.transitionKey})
: super(key: transitionKey);
final Widget screen;
final ValueKey transitionKey;
@override
Route createRoute(BuildContext context) {
return PageRouteBuilder(
settings: this,
transitionsBuilder: (context, animation, secondaryAnimation, child) {
return SharedAxisTransition(
fillColor: Theme.of(context).cardColor,
animation: animation,
secondaryAnimation: secondaryAnimation,
transitionType: SharedAxisTransitionType.scaled,
child: child,
);
},
pageBuilder: (context, animation, secondaryAnimation) {
return screen;
});
}
}
এখন, আমরা যে ট্রানজিশনটি চাই তা সম্পন্ন করতে আমাদের নতুন SharedAxisTransitionPageWrapper ব্যবহার করব। আমাদের ReplyRouterDelegate ক্লাস ডেফিনিশনের ভিতরে, pages প্রপার্টির অধীনে, CustomTransitionPage এর পরিবর্তে আমাদের সার্চ স্ক্রিনটিকে একটি SharedAxisTransitionPageWrapper দিয়ে র্যাপ করব:
রাউটার.ডার্ট
return Navigator(
key: navigatorKey,
onPopPage: _handlePopPage,
pages: [
// TODO: Add Shared Z-Axis transition from search icon to search view page (Motion)
const CustomTransitionPage(
transitionKey: ValueKey('Home'),
screen: HomePage(),
),
if (routePath is ReplySearchPath)
const SharedAxisTransitionPageWrapper(
transitionKey: ValueKey('Search'),
screen: SearchPage(),
),
],
);
এখন অ্যাপটি পুনরায় চালানোর চেষ্টা করুন।

সবকিছু বেশ ভালো দেখাচ্ছে! আপনি যখন নিচের অ্যাপ বারে থাকা সার্চ আইকনে ক্লিক করেন, তখন একটি শেয়ার্ড অ্যাক্সিস ট্রানজিশনের মাধ্যমে সার্চ পেজটি ধীরে ধীরে দৃশ্যমান হয়। তবে, লক্ষ্য করুন, সার্চ পেজটি হোম পেজের উপর দিয়ে ধীরে ধীরে দৃশ্যমান হওয়ার সময় হোম পেজটি বাইরের দিকে সরে না গিয়ে স্থির থাকে। এছাড়াও, ব্যাক বাটন চাপলে হোম পেজটি ধীরে ধীরে দৃশ্যমান না হয়ে স্থির থাকে, যখন সার্চ পেজটি বাইরের দিকে সরে যায়। সুতরাং, আমাদের কাজ এখনও শেষ হয়নি।
CustomTransitionPage এর পরিবর্তে আমাদের SharedAxisTransitionWrapper দিয়ে HomePage র্যাপ করে চলুন উভয় সমস্যার সমাধান করি:
রাউটার.ডার্ট
return Navigator(
key: navigatorKey,
onPopPage: _handlePopPage,
pages: [
// TODO: Add Shared Z-Axis transition from search icon to search view page (Motion)
const SharedAxisTransitionPageWrapper(
transitionKey: ValueKey('home'),
screen: HomePage(),
),
if (routePath is ReplySearchPath)
const SharedAxisTransitionPageWrapper(
transitionKey: ValueKey('search'),
screen: SearchPage(),
),
],
);
ব্যাস! এবার অ্যাপটি আবার চালু করে সার্চ আইকনে ট্যাপ করুন। হোম এবং সার্চ ভিউ স্ক্রিন দুটি একই সাথে Z-অক্ষ বরাবর গভীরতায় ফেড ও স্কেল হতে থাকবে, যা স্ক্রিন দুটির মধ্যে একটি নির্বিঘ্ন প্রভাব তৈরি করবে।
পরে

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

শুরু করার জন্য, চলুন আমাদের mail_view_router.dart ফাইলটিতে যাই। আমাদের MailViewRouterDelegate ক্লাস ডেফিনিশনের পরে নিম্নলিখিত কোড স্নিপেটটি যোগ করুন:
mail_view_router.dart
// TODO: Add Fade through transition between mailbox pages (Motion)
class FadeThroughTransitionPageWrapper extends Page {
const FadeThroughTransitionPageWrapper({
required this.mailbox,
required this.transitionKey,
}) : super(key: transitionKey);
final Widget mailbox;
final ValueKey transitionKey;
@override
Route createRoute(BuildContext context) {
return PageRouteBuilder(
settings: this,
transitionsBuilder: (context, animation, secondaryAnimation, child) {
return FadeThroughTransition(
fillColor: Theme.of(context).scaffoldBackgroundColor,
animation: animation,
secondaryAnimation: secondaryAnimation,
child: child,
);
},
pageBuilder: (context, animation, secondaryAnimation) {
return mailbox;
});
}
}
আমাদের আগের ধাপের মতোই, আমরা যে ট্রানজিশনটি চাই তা সম্পন্ন করতে আমাদের নতুন FadeThroughTransitionPageWrapper ব্যবহার করব। আমাদের MailViewRouterDelegate ক্লাস ডেফিনিশনের ভিতরে, pages প্রপার্টির অধীনে, আমাদের মেইলবক্স স্ক্রিনকে CustomTransitionPage দিয়ে র্যাপ করার পরিবর্তে, FadeThroughTransitionPageWrapper ব্যবহার করুন:
mail_view_router.dart
return Navigator(
key: navigatorKey,
onPopPage: _handlePopPage,
pages: [
// TODO: Add Fade through transition between mailbox pages (Motion)
FadeThroughTransitionPageWrapper(
mailbox: InboxPage(destination: currentlySelectedInbox),
transitionKey: ValueKey(currentlySelectedInbox),
),
],
);
অ্যাপটি আবার চালান। যখন আপনি নিচের নেভিগেশন ড্রয়ারটি খুলবেন এবং মেইলবক্স পরিবর্তন করবেন, তখন ইমেইলের বর্তমান তালিকাটি ধীরে ধীরে অদৃশ্য হয়ে যাবে এবং নতুন তালিকাটি ধীরে ধীরে দৃশ্যমান হবে। চমৎকার!
পরে

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

এই কোডল্যাবের বাকি অংশে আমরা home.dart নিয়ে কাজ করব, তাই animations প্যাকেজটি ইম্পোর্ট করার বিষয়ে চিন্তা করবেন না, কারণ আমরা ধাপ ২-এ home.dart এর জন্য তা ইতিমধ্যেই করে ফেলেছি।
পরবর্তী কয়েকটি ট্রানজিশন কনফিগার করার পদ্ধতি প্রায় একই রকম হবে, কারণ সেগুলোতে _FadeThroughTransitionSwitcher নামক একটি পুনঃব্যবহারযোগ্য ক্লাস ব্যবহার করা হবে।
home.dart এর _ReplyFabState নিচে নিচের কোডটি যোগ করুন:
হোম.ডার্ট
// TODO: Add Fade through transition between compose and reply FAB (Motion)
class _FadeThroughTransitionSwitcher extends StatelessWidget {
const _FadeThroughTransitionSwitcher({
required this.fillColor,
required this.child,
});
final Widget child;
final Color fillColor;
@override
Widget build(BuildContext context) {
return PageTransitionSwitcher(
transitionBuilder: (child, animation, secondaryAnimation) {
return FadeThroughTransition(
fillColor: fillColor,
child: child,
animation: animation,
secondaryAnimation: secondaryAnimation,
);
},
child: child,
);
}
}
এখন, আমাদের _ReplyFabState এ fabSwitcher উইজেটটি খুঁজুন। fabSwitcher ইমেল ভিউতে আছে কি না, তার উপর ভিত্তি করে একটি ভিন্ন আইকন রিটার্ন করে। চলুন, এটিকে আমাদের _FadeThroughTransitionSwitcher দিয়ে র্যাপ করি:
হোম.ডার্ট
// TODO: Add Fade through transition between compose and reply FAB (Motion)
static final fabKey = UniqueKey();
static const double _mobileFabDimension = 56;
@override
Widget build(BuildContext context) {
final theme = Theme.of(context);
final circleFabBorder = const CircleBorder();
return Selector<EmailStore, bool>(
selector: (context, emailStore) => emailStore.onMailView,
builder: (context, onMailView, child) {
// TODO: Add Fade through transition between compose and reply FAB (Motion)
final fabSwitcher = _FadeThroughTransitionSwitcher(
fillColor: Colors.transparent,
child: onMailView
? Icon(
Icons.reply_all,
key: fabKey,
color: Colors.black,
)
: const Icon(
Icons.create,
color: Colors.black,
),
);
...
আমরা আমাদের _FadeThroughTransitionSwitcher একটি স্বচ্ছ fillColor দিই, ফলে ট্রানজিশনের সময় এলিমেন্টগুলোর মধ্যে কোনো ব্যাকগ্রাউন্ড থাকে না। এছাড়াও আমরা একটি UniqueKey তৈরি করে সেটিকে আইকনগুলোর একটিতে অ্যাসাইন করি।
এখন, এই ধাপে, আপনার একটি সম্পূর্ণ অ্যানিমেটেড প্রাসঙ্গিক FAB থাকা উচিত। ইমেল ভিউতে গেলে পুরানো FAB আইকনটি ধীরে ধীরে মিলিয়ে যায় এবং আকার ছোট হয়ে যায়, আর নতুনটি ধীরে ধীরে মিলিয়ে যায় এবং আকার বড় হয়ে আসে।
পরে

১০. অদৃশ্য হতে থাকা মেইলবক্সের শিরোনামের মাঝে ফেড-থ্রু ট্রানজিশন যোগ করুন।
এই ধাপে, আমরা একটি ফেড-থ্রু ট্রানজিশন যোগ করব, যাতে ইমেল ভিউতে থাকাকালীন মেইলবক্সের শিরোনামটি দৃশ্যমান এবং অদৃশ্য অবস্থার মধ্যে ধীরে ধীরে পরিবর্তিত হয়। যেহেতু আমরা কোনো স্থানিক বা শ্রেণিবিন্যাসগত সম্পর্কের ওপর জোর দিতে চাই না, তাই আমরা মেইলবক্সের শিরোনাম ধারণকারী Text উইজেট এবং একটি খালি SizedBox মধ্যে একটি সাধারণ "অদলবদল" করার জন্য ফেড-থ্রু ব্যবহার করব।
অতিরিক্ত কোনো কোড যোগ করার আগে, অ্যাপটি রান করে একটি ইমেইলে ট্যাপ করুন এবং ইমেইল ভিউটি খুলুন। মেইলবক্সের শিরোনামটি কোনো ট্রানজিশন ছাড়াই অদৃশ্য হয়ে যাবে।
আগে

এই কোডল্যাবের বাকি অংশটি দ্রুত শেষ হয়ে যাবে, কারণ আমরা আমাদের শেষ ধাপে _FadeThroughTransitionSwitcher মধ্যে বেশিরভাগ কাজ ইতিমধ্যেই করে ফেলেছি।
এখন, আমাদের ট্রানজিশন যোগ করার জন্য home.dart এ থাকা _AnimatedBottomAppBar ক্লাসে যাওয়া যাক। আমরা আমাদের আগের ধাপ থেকে _FadeThroughTransitionSwitcher পুনরায় ব্যবহার করব এবং আমাদের onMailView কন্ডিশনালটিকে এর মধ্যে রাখব, যা হয় একটি খালি SizedBox রিটার্ন করবে, অথবা এমন একটি মেইলবক্স টাইটেল রিটার্ন করবে যা বটম ড্রয়ারের সাথে সিঙ্কে ফেড হবে:
হোম.ডার্ট
...
const _ReplyLogo(),
const SizedBox(width: 10),
// TODO: Add Fade through transition between disappearing mailbox title (Motion)
_FadeThroughTransitionSwitcher(
fillColor: Colors.transparent,
child: onMailView
? const SizedBox(width: 48)
: FadeTransition(
opacity: fadeOut,
child: Selector<EmailStore, String>(
selector: (context, emailStore) =>
emailStore.currentlySelectedInbox,
builder: (
context,
currentlySelectedInbox,
child,
) {
return Text(
currentlySelectedInbox,
style: Theme.of(context)
.textTheme
.bodyMedium!
.copyWith(
color: ReplyColors.white50,
),
);
},
),
),
),
ব্যাস, এই ধাপটি শেষ!
অ্যাপটি আবার চালান। যখন আপনি একটি ইমেল খুলবেন এবং ইমেল ভিউতে যাবেন, তখন নিচের অ্যাপ বারে থাকা মেইলবক্সের শিরোনামটি ধীরে ধীরে মিলিয়ে গিয়ে বড় হয়ে যাবে। চমৎকার!
পরে

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

গত ধাপের মতোই, আমরা আবারও আমাদের _FadeThroughTransitionSwitcher ব্যবহার করব। কাঙ্ক্ষিত ট্রানজিশনটি পেতে, আমাদের _BottomAppBarActionItems ক্লাস ডেফিনিশনে যান এবং আমাদের build() ফাংশনের return উইজেটটিকে একটি _FadeThroughTransitionSwitcher দিয়ে র্যাপ করুন:
হোম.ডার্ট
// TODO: Add Fade through transition between bottom app bar actions (Motion)
return _FadeThroughTransitionSwitcher(
fillColor: Colors.transparent,
child: drawerVisible
? Align(
key: UniqueKey(),
alignment: AlignmentDirectional.bottomEnd,
child: IconButton(
icon: const Icon(Icons.settings),
color: ReplyColors.white50,
onPressed: () async {
drawerController.reverse();
showModalBottomSheet(
context: context,
shape: RoundedRectangleBorder(
borderRadius: modalBorder,
),
builder: (context) => const SettingsBottomSheet(),
);
},
),
)
: onMailView
...
এবার চেষ্টা করে দেখা যাক! যখন আপনি একটি ইমেল খুলবেন এবং ইমেল ভিউতে যাবেন, তখন পুরানো বটম অ্যাপ বারের অ্যাকশনগুলো ফেড ও স্কেল আউট হয়ে যাবে এবং নতুন অ্যাকশনগুলো ফেড ও স্কেল ইন হয়ে যাবে। খুব ভালো!
পরে

১২. অভিনন্দন!
১০০ লাইনেরও কম ডার্ট কোড ব্যবহার করে, অ্যানিমেশন প্যাকেজটি আপনাকে একটি বিদ্যমান অ্যাপে সুন্দর ট্রানজিশন তৈরি করতে সাহায্য করেছে যা ম্যাটেরিয়াল ডিজাইন নির্দেশিকা মেনে চলে এবং সমস্ত ডিভাইসে দেখতে ও আচরণে সামঞ্জস্যপূর্ণ থাকে।

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



