1. परिचय
Material Design, डिजिटल प्रॉडक्ट को बेहतर और खूबसूरत बनाने का एक सिस्टम है. स्टाइल, ब्रैंडिंग, इंटरैक्शन, और मोशन को सिद्धांतों और कॉम्पोनेंट के एक जैसे सेट के तहत लाकर, प्रॉडक्ट टीमें डिज़ाइन की अपनी पूरी क्षमता का इस्तेमाल कर सकती हैं.
| मटीरियल कॉम्पोनेंट (एमडीसी) की मदद से, डेवलपर मटीरियल डिज़ाइन लागू कर सकते हैं. MDC को Google के इंजीनियरों और यूज़र एक्सपीरियंस (यूएक्स) डिज़ाइनर की टीम ने बनाया है. इसमें कई शानदार और काम के यूज़र इंटरफ़ेस (यूआई) कॉम्पोनेंट शामिल हैं. यह Android, iOS, वेब, और Flutter.material.io/develop के लिए उपलब्ध है |
Flutter के लिए Material का मोशन सिस्टम क्या है?
Flutter के लिए Material Motion System, ऐनिमेशन पैकेज में ट्रांज़िशन पैटर्न का एक सेट है. इससे उपयोगकर्ताओं को ऐप्लिकेशन को समझने और उसमें नेविगेट करने में मदद मिल सकती है. इस बारे में, Material Design के दिशा-निर्देशों में बताया गया है.
Material डिज़ाइन के चार मुख्य ट्रांज़िशन पैटर्न यहां दिए गए हैं:
- कंटेनर ट्रांसफ़ॉर्म: यह कंटेनर वाले यूज़र इंटरफ़ेस (यूआई) एलिमेंट के बीच ट्रांज़िशन करता है. साथ ही, एक एलिमेंट को दूसरे में बदलकर, दो अलग-अलग यूज़र इंटरफ़ेस (यूआई) एलिमेंट के बीच दिखने वाला कनेक्शन बनाता है.

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

- फ़ेड थ्रू: यह पैटर्न, उन यूज़र इंटरफ़ेस (यूआई) एलिमेंट के बीच ट्रांज़िशन के लिए इस्तेमाल किया जाता है जिनके बीच बेहतर तालमेल नहीं होता. इसमें, आने वाले एलिमेंट के स्केल के साथ-साथ, क्रम से फ़ेड आउट और फ़ेड इन होने की सुविधा मिलती है.

- फ़ेड: इसका इस्तेमाल उन यूज़र इंटरफ़ेस (यूआई) एलिमेंट के लिए किया जाता है जो स्क्रीन की सीमाओं के अंदर ही दिखते हैं और फिर गायब हो जाते हैं.

ऐनिमेशन पैकेज, इन पैटर्न के लिए ट्रांज़िशन विजेट उपलब्ध कराता है. ये विजेट, Flutter ऐनिमेशन लाइब्रेरी (flutter/animation.dart) और Flutter मटीरियल लाइब्रेरी (flutter/material.dart), दोनों के आधार पर बनाए गए हैं:
इस कोडलैब में, फ़्लटर फ़्रेमवर्क और मटीरियल लाइब्रेरी के आधार पर बनाए गए मटीरियल ट्रांज़िशन का इस्तेमाल किया जाएगा. इसका मतलब है कि आपको विजेट का इस्तेमाल करना होगा. :)
आपको क्या बनाने को मिलेगा
इस कोडलैब में, Dart का इस्तेमाल करके Reply नाम के एक उदाहरण फ़्लटर ईमेल ऐप्लिकेशन में कुछ ट्रांज़िशन बनाने का तरीका बताया गया है. इससे यह दिखाया जा सकेगा कि ऐनिमेशन पैकेज से ट्रांज़िशन का इस्तेमाल करके, अपने ऐप्लिकेशन के लुक और फ़ील को कैसे पसंद के मुताबिक बनाया जा सकता है.
Reply ऐप्लिकेशन के लिए स्टार्टर कोड उपलब्ध कराया जाएगा. आपको ऐप्लिकेशन में ये Material ट्रांज़िशन शामिल करने होंगे. इन्हें यहां दिए गए पूरे कोडलैब के GIF में देखा जा सकता है:
- ईमेल की सूची से ईमेल की ज़्यादा जानकारी वाले पेज पर कंटेनर ट्रांसफ़ॉर्म ट्रांज़िशन
- एफ़एबी से ईमेल लिखने वाले पेज पर कंटेनर ट्रांसफ़ॉर्म ट्रांज़िशन
- शेयर्ड ज़ेड-ऐक्सिस ट्रांज़िशन, खोज आइकॉन से खोज व्यू पेज पर
- मेलबॉक्स के पेजों के बीच फ़ेड थ्रू ट्रांज़िशन
- कंपोज़ और जवाब देने वाले फ़्लोटिंग ऐक्शन बटन के बीच फ़ेड थ्रू ट्रांज़िशन
- फ़ेड थ्रू ट्रांज़िशन, जिसमें डिसअपीयरिंग मेलबॉक्स का टाइटल गायब हो रहा है
- बॉटम ऐप्लिकेशन बार में मौजूद कार्रवाइयों के बीच फ़ेड थ्रू ट्रांज़िशन

आपको इन चीज़ों की ज़रूरत होगी
- Flutter डेवलपमेंट और Dart की बुनियादी जानकारी
- कोड एडिटर
- Android/iOS एम्युलेटर या डिवाइस
- सैंपल कोड (अगला चरण देखें)
Flutter ऐप्लिकेशन बनाने के अपने अनुभव को आप क्या रेटिंग देंगे?
आपको इस कोडलैब से क्या सीखना है?
2. Flutter डेवलपमेंट एनवायरमेंट सेट अप करना
इस लैब को पूरा करने के लिए, आपको दो सॉफ़्टवेयर की ज़रूरत होगी— Flutter SDK और एडिटर.
इनमें से किसी भी डिवाइस पर कोडलैब चलाया जा सकता है:
- आपके पास Android या iOS डिवाइस होना चाहिए. यह डिवाइस आपके कंप्यूटर से कनेक्ट होना चाहिए और डेवलपर मोड पर सेट होना चाहिए.
- iOS सिम्युलेटर (इसके लिए, Xcode टूल इंस्टॉल करना ज़रूरी है).
- Android Emulator (इसे Android Studio में सेट अप करना ज़रूरी है).
- कोई ब्राउज़र (डीबग करने के लिए Chrome ज़रूरी है).
- Windows, Linux या macOS के डेस्कटॉप ऐप्लिकेशन के तौर पर. आपको उसी प्लैटफ़ॉर्म पर डेवलपमेंट करना होगा जहां आपको ऐप्लिकेशन डिप्लॉय करना है. इसलिए, अगर आपको Windows डेस्कटॉप ऐप्लिकेशन डेवलप करना है, तो आपको Windows पर ही डेवलप करना होगा, ताकि सही बिल्ड चेन को ऐक्सेस किया जा सके. ऑपरेटिंग सिस्टम के हिसाब से कुछ ज़रूरी शर्तें होती हैं. इनके बारे में docs.flutter.dev/desktop पर पूरी जानकारी दी गई है.
3. कोडलैब का स्टार्टर ऐप्लिकेशन डाउनलोड करें
पहला विकल्प: GitHub से स्टार्टर कोडलैब ऐप्लिकेशन को क्लोन करना
GitHub से इस कोडलब का क्लोन बनाने के लिए, ये कमांड चलाएं:
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 डायरेक्ट्री में मौजूद है.
प्रोजेक्ट की डिपेंडेंसी की पुष्टि करना
प्रोजेक्ट, ऐनिमेशन पैकेज पर निर्भर करता है. pubspec.yaml में, ध्यान दें कि dependencies सेक्शन में यह जानकारी शामिल होती है:
animations: ^2.0.0
प्रोजेक्ट खोलें और ऐप्लिकेशन चलाएं
- अपनी पसंद के एडिटर में प्रोजेक्ट खोलें.
- अपने चुने गए एडिटर के लिए, शुरू करें: बिना शुल्क आज़माएं में दिए गए "ऐप्लिकेशन चलाएं" निर्देशों का पालन करें.
हो गया! Reply के होम पेज का स्टार्टर कोड, आपके डिवाइस/एम्युलेटर पर चलना चाहिए. आपको इनबॉक्स में ईमेल की सूची दिखेगी.

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

ज़रूरी नहीं: गहरे रंग वाला मोड
अगर Reply की ब्राइट थीम से आपकी आंखों को परेशानी हो रही है, तो यहां दी गई जानकारी पढ़ें. ऐप्लिकेशन में एक सेटिंग शामिल है. इसकी मदद से, ऐप्लिकेशन की थीम को गहरे रंग वाले मोड में बदला जा सकता है, ताकि आपकी आंखों को आराम मिले. सबसे नीचे मौजूद ड्रॉअर खुला होने पर, सेटिंग आइकॉन पर टैप करके इस सेटिंग को ऐक्सेस किया जा सकता है.

4. सैंपल ऐप्लिकेशन कोड के बारे में जानकारी
आइए, कोड देखते हैं. हमने एक ऐसा ऐप्लिकेशन उपलब्ध कराया है जो ऐप्लिकेशन में अलग-अलग स्क्रीन के बीच ट्रांज़िशन के लिए, ऐनिमेशन पैकेज का इस्तेमाल करता है.
- होम पेज: इससे चुना गया मेलबॉक्स दिखता है
- InboxPage: यह ईमेल की सूची दिखाता है
- MailPreviewCard: यह ईमेल की झलक दिखाता है
- MailViewPage: यह एक पूरा ईमेल दिखाता है
- ComposePage: इसकी मदद से नया ईमेल लिखा जा सकता है
- SearchPage: इससे सर्च व्यू दिखता है
router.dart
सबसे पहले, ऐप्लिकेशन के रूट नेविगेशन को सेट अप करने का तरीका समझने के लिए, router.dart डायरेक्ट्री में lib खोलें:
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
हमने अपने ऐप्लिकेशन की स्थिति में ReplySearchPath का रूट सेट किया है. इसके लिए, हमने home.dart में _BottomAppBarActionItems के अंदर यह कोड लिखा है:
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 में home.dart के अंदर यह काम करते हैं:
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, इनर नेविगेटर की स्थिति को ट्रैक करता है.
home.dart
आखिर में, नेविगेशन राउटिंग के इस्तेमाल का उदाहरण देखने के लिए, 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();
},
),
);
},
इस इमेज में दिखाया गया है कि कस्टम ट्रांज़िशन के बिना, ईमेल लिखने वाले पेज पर कैसे जाया जा सकता है. इस कोडलैब के दौरान, आपको Reply के कोड के बारे में जानकारी मिलेगी. इससे, ऐप्लिकेशन में अलग-अलग नेविगेशन कार्रवाइयों के साथ काम करने वाले, मटेरियल ट्रांज़िशन सेट अप किए जा सकते हैं.
अब आपको स्टार्टर कोड के बारे में पता चल गया है. इसलिए, अब हम पहला ट्रांज़िशन लागू करते हैं.
5. ईमेल की सूची से ईमेल की ज़्यादा जानकारी वाले पेज पर कंटेनर ट्रांसफ़ॉर्म ट्रांज़िशन जोड़ें
शुरू करने के लिए, ईमेल पर क्लिक करने पर ट्रांज़िशन जोड़ा जाएगा. नेविगेशन में बदलाव के लिए, कंटेनर ट्रांसफ़ॉर्म वाला पैटर्न सबसे सही है. ऐसा इसलिए, क्योंकि इसे उन यूज़र इंटरफ़ेस (यूआई) एलिमेंट के बीच ट्रांज़िशन के लिए डिज़ाइन किया गया है जिनमें कंटेनर शामिल होता है. इस पैटर्न से दो यूज़र इंटरफ़ेस (यूआई) एलिमेंट के बीच, दिखने वाला कनेक्शन बनाने में मदद मिलती है.
कोई भी कोड जोड़ने से पहले, Reply ऐप्लिकेशन चलाकर देखें. इसके बाद, किसी ईमेल पर क्लिक करें. इसमें जंप-कट होना चाहिए. इसका मतलब है कि स्क्रीन को बिना किसी ट्रांज़िशन के बदल दिया जाता है:
इससे पहले

सबसे पहले, mail_card_preview.dart के सबसे ऊपर, ऐनिमेशन पैकेज के लिए इंपोर्ट जोड़ें. इसे यहां दिए गए स्निपेट में दिखाया गया है:
mail_card_preview.dart
import 'package:animations/animations.dart';
अब आपके पास ऐनिमेशन पैकेज इंपोर्ट करने का विकल्प है. इसलिए, हम आपके ऐप्लिकेशन में बेहतरीन ट्रांज़िशन जोड़ सकते हैं. चलिए, StatelessWidget क्लास बनाकर शुरुआत करते हैं. इसमें हमारा OpenContainer विजेट शामिल होगा.
mail_card_preview.dart में, MailPreviewCard की क्लास डेफ़िनिशन के बाद यह कोड स्निपेट जोड़ें:
mail_card_preview.dart
// 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 से रैप करेंगे:
mail_card_preview.dart
// 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 विजेट हटा सकते हैं. इसके बाद, कोड ऐसा दिखता है:
mail_card_preview.dart
// 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,
),
);
इस चरण में, आपके पास पूरी तरह से काम करने वाला कंटेनर ट्रांसफ़ॉर्म होना चाहिए. किसी ईमेल पर क्लिक करने से, सूची में मौजूद आइटम, ज़्यादा जानकारी वाली स्क्रीन में बड़ा हो जाता है. वहीं, ईमेल की सूची छोटी हो जाती है. 'वापस जाएं' बटन दबाने पर, ईमेल की जानकारी वाली स्क्रीन छोटी हो जाती है और ईमेल की सूची में वापस आ जाती है.
इसके बाद

6. एफ़एबी से ईमेल लिखने वाले पेज पर कंटेनर ट्रांसफ़ॉर्म ट्रांज़िशन जोड़ा गया
कंटेनर ट्रांसफ़ॉर्म का इस्तेमाल जारी रखते हैं. साथ ही, फ़्लोटिंग ऐक्शन बटन से ComposePage तक ट्रांज़िशन जोड़ते हैं. इससे उपयोगकर्ता के लिए, नया ईमेल लिखने के लिए FAB को बड़ा किया जा सकेगा. सबसे पहले, ऐप्लिकेशन को फिर से चलाएं और FAB पर क्लिक करें. इससे आपको पता चलेगा कि ईमेल लिखने की स्क्रीन लॉन्च करते समय कोई ट्रांज़िशन नहीं होता है.
इससे पहले

हम इस ट्रांज़िशन को उसी तरह से कॉन्फ़िगर करेंगे जैसा हमने पिछले चरण में किया था, क्योंकि हम एक ही विजेट क्लास, OpenContainer का इस्तेमाल कर रहे हैं.
home.dart में, फ़ाइल के सबसे ऊपर मौजूद package:animations/animations.dart को इंपोर्ट करें. इसके बाद, _ReplyFabState build() तरीके में बदलाव करें. आइए, दिखाए गए Material विजेट को OpenContainer विजेट में रैप करें:
home.dart
// 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 विजेट को हटा देंगे. ऐसा इसलिए, क्योंकि Material विजेट, closedColor के साथ closedBuilder से मिले विजेट के रंग को मैनेज करता है.OpenContainer हम InkWell विजेट के onTap के अंदर मौजूद Navigator.push() कॉल को भी हटा देंगे. साथ ही, इसे OpenContainer विजेट के closedBuilder से मिले openContainer() Callback से बदल देंगे. ऐसा इसलिए किया जाएगा, क्योंकि अब OpenContainer विजेट, अपनी राउटिंग को खुद मैनेज कर रहा है.
इससे मिलने वाला कोड यह है:
home.dart
// 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);
इस चरण में बस इतना ही! आपके पास, फ़्लोटिंग ऐक्शन बटन से कंपोज़ स्क्रीन पर ट्रांज़िशन करने का विकल्प होना चाहिए. यह विकल्प कुछ ऐसा दिखना चाहिए:
इसके बाद

7. खोज वाले आइकॉन से खोज के व्यू पेज पर, शेयर्ड ज़ेड-ऐक्सिस ट्रांज़िशन जोड़ना
इस चरण में, हम खोज आइकॉन से फ़ुल स्क्रीन सर्च व्यू में ट्रांज़िशन जोड़ेंगे. नेविगेशन में हुए इस बदलाव में कोई परसिस्टेंट कंटेनर शामिल नहीं है. इसलिए, हम शेयर किए गए Z-ऐक्सिस ट्रांज़िशन का इस्तेमाल कर सकते हैं. इससे दोनों स्क्रीन के बीच की दूरी को बेहतर तरीके से दिखाया जा सकता है. साथ ही, ऐप्लिकेशन के क्रम में एक लेवल ऊपर जाने का संकेत दिया जा सकता है.
कोई और कोड जोड़ने से पहले, ऐप्लिकेशन को चलाकर देखें. साथ ही, स्क्रीन के सबसे नीचे दाएं कोने में मौजूद, 'खोजें' आइकॉन पर टैप करें. इससे, ट्रांज़िशन के बिना खोज के नतीजे दिखाने वाली स्क्रीन खुलनी चाहिए.
इससे पहले

शुरू करने के लिए, आइए हम अपनी router.dart फ़ाइल पर जाएं. हमारी ReplySearchPath क्लास की परिभाषा के बाद, यह स्निपेट जोड़ें:
router.dart
// 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 से रैप करें:
router.dart
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(),
),
],
);
अब ऐप्लिकेशन को फिर से चलाकर देखें.

अब सब ठीक लग रहा है! नीचे मौजूद ऐप्लिकेशन बार में खोज आइकॉन पर क्लिक करने पर, शेयर किए गए ऐक्सिस ट्रांज़िशन की मदद से, खोज पेज को बड़ा करके दिखाया जाता है. हालांकि, ध्यान दें कि होम पेज का साइज़ नहीं बढ़ता है. इसके बजाय, यह स्थिर रहता है, क्योंकि खोज पेज का साइज़ बढ़ता है. इसके अलावा, 'वापस जाएं' बटन दबाने पर, होम पेज नहीं दिखता. इसके बजाय, यह स्थिर रहता है, जबकि खोज पेज नहीं दिखता. इसलिए, अभी हमें और भी काम करना है.
आइए, दोनों समस्याओं को ठीक करने के लिए, HomePage को CustomTransitionPage के बजाय SharedAxisTransitionWrapper से रैप करें:
router.dart
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-ऐक्सिस पर एक साथ फ़ेड और स्केल होनी चाहिए. इससे दोनों स्क्रीन के बीच एक जैसा इफ़ेक्ट दिखेगा.
इसके बाद

8. मेलबॉक्स के पेजों के बीच फ़ेड थ्रू ट्रांज़िशन जोड़ना
इस चरण में, हम अलग-अलग मेलबॉक्स के बीच ट्रांज़िशन जोड़ेंगे. हमें किसी भी तरह के स्पेशल या हैरारिकल संबंध पर ज़ोर नहीं देना है. इसलिए, हम ईमेल की सूचियों के बीच आसानी से "स्वैप" करने के लिए, फ़ेड थ्रू का इस्तेमाल करेंगे.
कोई भी अतिरिक्त कोड जोड़ने से पहले, ऐप्लिकेशन चलाकर देखें. इसके लिए, बॉटम ऐप्लिकेशन बार में मौजूद जवाब देने वाले लोगो पर टैप करें और मेलबॉक्स स्विच करें. ईमेल की सूची में बदलाव तुरंत होना चाहिए.
इससे पहले

शुरू करने के लिए, आइए हम अपनी 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),
),
],
);
ऐप्लिकेशन को फिर से चलाएं. जब बॉटम नेविगेशन ड्रॉअर खोला जाता है और मेलबॉक्स बदले जाते हैं, तो ईमेल की मौजूदा सूची धुंधली हो जानी चाहिए और छोटी हो जानी चाहिए. वहीं, नई सूची धुंधली हो जानी चाहिए और बड़ी हो जानी चाहिए. बढ़िया!
इसके बाद

9. कंपोज़ और जवाब देने वाले फ़्लोटिंग ऐक्शन बटन के बीच फ़ेड थ्रू ट्रांज़िशन जोड़ें
इस चरण में, हम अलग-अलग FAB आइकॉन के बीच ट्रांज़िशन जोड़ेंगे. हमें दूरी या क्रम के हिसाब से किसी संबंध पर ज़ोर नहीं देना है. इसलिए, हम फ़ैब में मौजूद आइकॉन के बीच "स्वैप" करने के लिए, फ़ेड थ्रू का इस्तेमाल करेंगे.
कोई भी अतिरिक्त कोड जोड़ने से पहले, ऐप्लिकेशन चलाएं. इसके बाद, किसी ईमेल पर टैप करें और ईमेल व्यू खोलें. FAB आइकॉन, ट्रांज़िशन के बिना बदलना चाहिए.
इससे पहले

हम इस कोडलैब के बाकी हिस्से में home.dart पर काम करेंगे. इसलिए, ऐनिमेशन पैकेज के लिए इंपोर्ट जोड़ने की चिंता न करें, क्योंकि हमने चरण 2 में home.dart के लिए पहले ही ऐसा कर दिया है.
अगले कुछ ट्रांज़िशन को कॉन्फ़िगर करने का तरीका काफ़ी हद तक एक जैसा होगा, क्योंकि उन सभी में रीयूज़ की जा सकने वाली क्लास, _FadeThroughTransitionSwitcher का इस्तेमाल किया जाएगा.
home.dart में, _ReplyFabState के नीचे यह स्निपेट जोड़ें:
home.dart
// 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 के साथ रैप करें:
home.dart
// 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 आइकॉन धुंधला हो जाता है और छोटा हो जाता है. वहीं, नया आइकॉन धुंधला हो जाता है और बड़ा हो जाता है.
इसके बाद

10. गायब होने वाले मेलबॉक्स के टाइटल के बीच फ़ेड थ्रू ट्रांज़िशन जोड़ें
इस चरण में, हम फ़ेड थ्रू ट्रांज़िशन जोड़ेंगे, ताकि ईमेल व्यू में मेलबॉक्स के टाइटल को दिखने और न दिखने की स्थिति के बीच फ़ेड थ्रू किया जा सके. हमें किसी स्पैटियल या हैरारिकल संबंध पर ज़ोर नहीं देना है. इसलिए, हम फ़ेड थ्रू का इस्तेमाल करेंगे, ताकि मेलबॉक्स के टाइटल वाले Text विजेट और खाली SizedBox के बीच आसानी से "स्वैप" किया जा सके.
कोई भी अतिरिक्त कोड जोड़ने से पहले, ऐप्लिकेशन चलाएं. इसके बाद, किसी ईमेल पर टैप करें और ईमेल व्यू खोलें. मेलबॉक्स का टाइटल, बिना किसी ट्रांज़िशन के गायब हो जाना चाहिए.
इससे पहले

इस कोडलैब का बाकी हिस्सा जल्दी पूरा हो जाएगा, क्योंकि हमने पिछले चरण में _FadeThroughTransitionSwitcher का ज़्यादातर काम पहले ही कर लिया है.
अब ट्रांज़िशन जोड़ने के लिए, home.dart में मौजूद _AnimatedBottomAppBar क्लास पर जाएं. हम पिछले चरण में इस्तेमाल किए गए _FadeThroughTransitionSwitcher का फिर से इस्तेमाल करेंगे. साथ ही, हम onMailView को इस तरह से रैप करेंगे कि यह या तो खाली SizedBox दिखाता है या मेलबॉक्स का ऐसा टाइटल दिखाता है जो बॉटम ड्रॉअर के साथ सिंक होकर फ़ेड इन होता है:
home.dart
...
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,
),
);
},
),
),
),
बस, हमने यह चरण पूरा कर लिया है!
ऐप्लिकेशन को फिर से चलाएं. जब कोई ईमेल खोला जाता है और आपको ईमेल व्यू पर ले जाया जाता है, तो सबसे नीचे मौजूद ऐप्लिकेशन बार में मौजूद मेलबॉक्स का टाइटल धुंधला हो जाना चाहिए और छोटा हो जाना चाहिए. बहुत बढ़िया!
इसके बाद

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

पिछले चरण की तरह, हम _FadeThroughTransitionSwitcher का इस्तेमाल फिर से करेंगे. पसंद के मुताबिक ट्रांज़िशन पाने के लिए, हमारी _BottomAppBarActionItems क्लास डेफ़िनिशन पर जाएं. इसके बाद, build() फ़ंक्शन के रिटर्न विजेट को _FadeThroughTransitionSwitcher से रैप करें:
home.dart
// 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
...
अब इसे आज़माएं! जब कोई ईमेल खोला जाता है और ईमेल व्यू पर ले जाया जाता है, तो बॉटम ऐप्लिकेशन बार में मौजूद पुरानी कार्रवाइयों को धुंधला हो जाना चाहिए और उनका साइज़ कम हो जाना चाहिए. वहीं, नई कार्रवाइयों को धुंधला हो जाना चाहिए और उनका साइज़ बढ़ जाना चाहिए. बहुत खूब!
इसके बाद

12. बधाई हो!
ऐनिमेशन पैकेज की मदद से, 100 से कम लाइनों वाले डार्ट कोड का इस्तेमाल करके, मौजूदा ऐप्लिकेशन में शानदार ट्रांज़िशन बनाए जा सकते हैं. यह ऐप्लिकेशन, Material Design के दिशा-निर्देशों का पालन करता है. साथ ही, यह सभी डिवाइसों पर एक जैसा दिखता है और एक जैसा काम करता है.

अगले चरण
मटीरियल मोशन सिस्टम के बारे में ज़्यादा जानने के लिए, दिशा-निर्देश और डेवलपर दस्तावेज़ ज़रूर देखें. साथ ही, अपने ऐप्लिकेशन में कुछ मटीरियल ट्रांज़िशन जोड़कर देखें!
Material मोशन आज़माने के लिए धन्यवाद. हमें उम्मीद है कि आपको यह कोडलैब पसंद आया होगा!
मैंने इस कोडलैब को कम समय और कम मेहनत में पूरा कर लिया
मुझे आने वाले समय में, Material मोशन सिस्टम का इस्तेमाल जारी रखना है
Flutter Gallery देखें
| Material Flutter लाइब्रेरी और Flutter फ़्रेमवर्क से मिले विजेट इस्तेमाल करने के तरीके के बारे में ज़्यादा डेमो देखने के लिए, Flutter Gallery पर जाएं. |



