1. परिचय
Material Components (एमडीसी), डेवलपर को Material Design लागू करने में मदद करते हैं. Google के इंजीनियरों और UX डिज़ाइनर की टीम ने बनाया है. एमडीसी में दर्जनों खूबसूरत और काम करने वाले यूज़र इंटरफ़ेस (यूआई) कॉम्पोनेंट हैं. यह Android, iOS, वेब, और Flutter.material.io/प्रॉडक्ट के लिए उपलब्ध है |
कोडलैब MDC-103 में, आपने अपने ऐप्लिकेशन को स्टाइल देने के लिए, Material Components (MDC) के रंग, ऊंचाई, टाइपोग्राफ़ी, और आकार को पसंद के मुताबिक बनाया था.
Material Design सिस्टम में मौजूद कॉम्पोनेंट, पहले से तय किए गए टास्क का एक सेट करता है. साथ ही, इसमें बटन जैसी कुछ खास विशेषताएं होती हैं. हालांकि, बटन सिर्फ़ उपयोगकर्ता के लिए कोई कार्रवाई करने का तरीका नहीं है. यह आकार, साइज़, और रंग का विज़ुअल एक्सप्रेशन भी है. इससे उपयोगकर्ता को पता चलता है कि यह इंटरैक्टिव है और टच या क्लिक करने पर कुछ होगा.
मटीरियल डिज़ाइन दिशा-निर्देश, डिज़ाइनर के नज़रिये से कॉम्पोनेंट के बारे में बताते हैं. इनसे, अलग-अलग प्लैटफ़ॉर्म पर उपलब्ध कई तरह के बेसिक फ़ंक्शन के बारे में पता चलता है. साथ ही, हर कॉम्पोनेंट को बनाने वाले एनाटॉमिक एलिमेंट के बारे में भी पता चलता है. उदाहरण के लिए, किसी बैकड्रॉप में बैक लेयर और उसका कॉन्टेंट, फ़्रंट लेयर और उसका कॉन्टेंट, मोशन के नियम, और डिसप्ले के विकल्प होते हैं. इनमें से हर कॉम्पोनेंट को हर ऐप्लिकेशन की ज़रूरतों, इस्तेमाल के उदाहरणों, और कॉन्टेंट के हिसाब से बनाया जा सकता है.
आपको क्या बनाना है
इस कोडलैब में, आपको Shrine ऐप्लिकेशन के यूज़र इंटरफ़ेस (यूआई) को दो लेवल के प्रज़ेंटेशन में बदलना होगा. इसे "बैकड्रॉप" कहा जाता है. बैकड्रॉप में एक मेन्यू होता है. इसमें चुनी जा सकने वाली कैटगरी की सूची होती है. इन कैटगरी का इस्तेमाल, असिमेट्रिक ग्रिड में दिखाए गए प्रॉडक्ट को फ़िल्टर करने के लिए किया जाता है. इस कोडलैब में, आपको इनका इस्तेमाल करना होगा:
- आकार
- मोशन
- Flutter विजेट (जिन्हें आपने पिछले कोडलैब में इस्तेमाल किया है)
Android | iOS |
इस कोडलैब में मौजूद मटीरियल Flutter कॉम्पोनेंट और सबसिस्टम
- आकार
Flutter डेवलपमेंट के साथ अपने अनुभव के आधार पर आप क्या रेटिंग देंगे?
2. Flutter डेवलपमेंट एनवायरमेंट को सेट अप करें
इस लैब को पूरा करने के लिए, आपके पास दो सॉफ़्टवेयर होने चाहिए— Flutter SDK टूल और एडिटर.
इनमें से किसी भी डिवाइस का इस्तेमाल करके, कोडलैब चलाया जा सकता है:
- आपके कंप्यूटर से कनेक्ट किया गया Android या iOS डिवाइस, जो डेवलपर मोड पर सेट हो.
- iOS सिम्युलेटर (Xcode टूल इंस्टॉल करना ज़रूरी है).
- Android एमुलेटर (Android Studio में सेटअप करना ज़रूरी है).
- ब्राउज़र (डीबग करने के लिए Chrome ज़रूरी है).
- Windows, Linux या macOS डेस्कटॉप ऐप्लिकेशन के तौर पर. आपको उस प्लैटफ़ॉर्म पर गेम बनाना होगा जहां आपको इसे डिप्लॉय करना है. इसलिए, अगर आपको Windows डेस्कटॉप ऐप्लिकेशन बनाना है, तो आपको सही बिल्ड चेन ऐक्सेस करने के लिए, Windows पर डेवलप करना होगा. ऑपरेटिंग सिस्टम के हिसाब से कुछ ज़रूरी शर्तें होती हैं. इनके बारे में ज़्यादा जानकारी docs.flutter.dev/desktop पर दी गई है.
3. कोडलैब स्टार्टर ऐप्लिकेशन डाउनलोड करें
क्या आपने MDC-103 से जारी रखा है?
अगर आपने MDC-103 पूरा कर लिया है, तो आपका कोड इस कोडलैब के लिए तैयार होना चाहिए. सीधे चरण पर जाएं: बैकग्राउंड मेन्यू जोड़ें.
शुरुआत से बनाना है?
स्टार्टर ऐप्लिकेशन, material-components-flutter-codelabs-104-starter_and_103-complete/mdc_100_series
डायरेक्ट्री में मौजूद है.
...या GitHub से इसका क्लोन बनाएं
GitHub से इस कोडलैब को क्लोन करने के लिए, ये कमांड चलाएं:
git clone https://github.com/material-components/material-components-flutter-codelabs.git cd material-components-flutter-codelabs/mdc_100_series git checkout 104-starter_and_103-complete
प्रोजेक्ट खोलें और ऐप्लिकेशन चलाएं
- अपनी पसंद के एडिटर में प्रोजेक्ट खोलें.
- अपने चुने हुए एडिटर के लिए, शुरू करें: टेस्ट ड्राइव में "ऐप्लिकेशन चलाएं" के निर्देशों का पालन करें.
हो गया! आपको अपने डिवाइस पर, पिछले कोडलैब से Shrine का लॉगिन पेज दिखेगा.
Android | iOS |
4. बैकग्राउंड मेन्यू जोड़ना
बैकग्राउंड, अन्य सभी कॉन्टेंट और कॉम्पोनेंट के पीछे दिखता है. यह दो लेयर से बना होता है: बैक लेयर (जिसमें कार्रवाइयां और फ़िल्टर दिखते हैं) और फ़्रंट लेयर (जिसमें कॉन्टेंट दिखता है). आप इंटरैक्टिव जानकारी और कार्रवाइयां, जैसे नेविगेशन या कॉन्टेंट के फ़िल्टर दिखाने के लिए बैकग्राउंड का इस्तेमाल कर सकते हैं.
होम स्क्रीन पर ऐप्लिकेशन बार हटाना
HomePage विजेट हमारे फ़्रंट लेयर का कॉन्टेंट होगा. फ़िलहाल, इसमें ऐप्लिकेशन बार मौजूद है. हम ऐप्लिकेशन बार को बैक लेयर पर ले जाएंगे और होम पेज में सिर्फ़ AsymmetricView शामिल होगा.
home.dart
में, build()
फ़ंक्शन को बदलकर सिर्फ़ AsymmetricView दिखाने के लिए:
// TODO: Return an AsymmetricView (104)
return AsymmetricView(products: ProductsRepository.loadProducts(Category.all));
बैकग्राउंड विजेट जोड़ें
बैकड्रॉप नाम का एक विजेट बनाएं, जिसमें frontLayer
और backLayer
शामिल हों.
backLayer
में एक मेन्यू शामिल होता है. इसकी मदद से, सूची (currentCategory
) को फ़िल्टर करने के लिए कोई कैटगरी चुनी जा सकती है. हम मेन्यू में चुने गए विकल्प को बनाए रखने के लिए, Backdrop को स्टेटफ़ुल विजेट बनाएंगे.
backdrop.dart
नाम वाली नई फ़ाइल को /lib
में जोड़ें:
import 'package:flutter/material.dart';
import 'model/product.dart';
// TODO: Add velocity constant (104)
class Backdrop extends StatefulWidget {
final Category currentCategory;
final Widget frontLayer;
final Widget backLayer;
final Widget frontTitle;
final Widget backTitle;
const Backdrop({
required this.currentCategory,
required this.frontLayer,
required this.backLayer,
required this.frontTitle,
required this.backTitle,
Key? key,
}) : super(key: key);
@override
_BackdropState createState() => _BackdropState();
}
// TODO: Add _FrontLayer class (104)
// TODO: Add _BackdropTitle class (104)
// TODO: Add _BackdropState class (104)
ध्यान दें कि हमने कुछ प्रॉपर्टी को required
के तौर पर मार्क किया है. यह कंस्ट्रक्टर में मौजूद उन प्रॉपर्टी के लिए सबसे सही तरीका है जिनकी कोई डिफ़ॉल्ट वैल्यू नहीं है और जिन्हें null
नहीं किया जा सकता. इसलिए, इसे नहीं भूलना चाहिए.
Backdrop क्लास की परिभाषा में जाकर, _BackdropState क्लास जोड़ें:
// TODO: Add _BackdropState class (104)
class _BackdropState extends State<Backdrop>
with SingleTickerProviderStateMixin {
final GlobalKey _backdropKey = GlobalKey(debugLabel: 'Backdrop');
// TODO: Add AnimationController widget (104)
// TODO: Add BuildContext and BoxConstraints parameters to _buildStack (104)
Widget _buildStack() {
return Stack(
key: _backdropKey,
children: <Widget>[
// TODO: Wrap backLayer in an ExcludeSemantics widget (104)
widget.backLayer,
widget.frontLayer,
],
);
}
@override
Widget build(BuildContext context) {
var appBar = AppBar(
elevation: 0.0,
titleSpacing: 0.0,
// TODO: Replace leading menu icon with IconButton (104)
// TODO: Remove leading property (104)
// TODO: Create title with _BackdropTitle parameter (104)
leading: Icon(Icons.menu),
title: Text('SHRINE'),
actions: <Widget>[
// TODO: Add shortcut to login screen from trailing icons (104)
IconButton(
icon: Icon(
Icons.search,
semanticLabel: 'search',
),
onPressed: () {
// TODO: Add open login (104)
},
),
IconButton(
icon: Icon(
Icons.tune,
semanticLabel: 'filter',
),
onPressed: () {
// TODO: Add open login (104)
},
),
],
);
return Scaffold(
appBar: appBar,
// TODO: Return a LayoutBuilder widget (104)
body: _buildStack(),
);
}
}
build()
फ़ंक्शन, होमपेज की तरह ही ऐप्लिकेशन बार के साथ स्कैफ़ोल्ड दिखाता है. हालांकि, स्कैफ़ोल्ड का शरीर एक स्टैक है. स्टैक के चाइल्ड ओवरलैप हो सकते हैं. हर बच्चे का साइज़ और जगह, स्टैक के पैरंट के हिसाब से तय होती है.
अब Squarespace ऐप्लिकेशन में कोई Backdrop इंस्टेंस जोड़ें.
app.dart
में, backdrop.dart
और model/product.dart
इंपोर्ट करें:
import 'backdrop.dart'; // New code
import 'colors.dart';
import 'home.dart';
import 'login.dart';
import 'model/product.dart'; // New code
import 'supplemental/cut_corners_border.dart';
app.dart,
में, /
रूट में बदलाव करें. इसके लिए, Backdrop
दिखाएं, जिसका frontLayer
HomePage
हो:
// TODO: Change to a Backdrop with a HomePage frontLayer (104)
'/': (BuildContext context) => Backdrop(
// TODO: Make currentCategory field take _currentCategory (104)
currentCategory: Category.all,
// TODO: Pass _currentCategory for frontLayer (104)
frontLayer: HomePage(),
// TODO: Change backLayer field value to CategoryMenuPage (104)
backLayer: Container(color: kShrinePink100),
frontTitle: Text('SHRINE'),
backTitle: Text('MENU'),
),
अपना प्रोजेक्ट सेव करें. आपको हमारा होम पेज और ऐप्लिकेशन बार दिखेगा:
Android | iOS |
backlayer होम पेज के पीछे एक नई लेयर में गुलाबी क्षेत्र दिखाता है.
Flutter Inspector का इस्तेमाल करके, यह पुष्टि की जा सकती है कि स्टैक में HomePage के पीछे सचमुच कोई कंटेनर है या नहीं. यह कुछ ऐसा दिखना चाहिए:
अब आप लेयर के डिज़ाइन और कॉन्टेंट, दोनों में बदलाव कर सकते हैं.
5. आकार जोड़ें
इस तरीके में, ऊपर बाएं कोने में कट जोड़ने के लिए, सामने वाली लेयर को स्टाइल दें.
मटीरियल डिज़ाइन में, इस तरह के कस्टमाइज़ेशन को आकार कहा जाता है. मेटल की सतहों का आकार अलग-अलग हो सकता है. आकार से सरफ़ेस पर जोर और स्टाइल पैदा होती है उसे ब्रैंडिंग के बारे में बताने के लिए इस्तेमाल किया जा सकता है. सामान्य आयताकार आकारों को घुमावदार या कोण वाले कोनों और किनारों, और फिर कितने भी किनारों की मदद से अपनी पसंद के मुताबिक बनाया जा सकता है. वे सममित या अनियमित हो सकते हैं.
फ़्रंट लेयर में कोई आकार जोड़ना
कोण वाले श्राइन लोगो को देखकर ऐप्लिकेशन को आकार देने की कहानी बताई गई है. आकार की कहानी, ऐप्लिकेशन में लागू की जाने वाली आकृतियों का सामान्य इस्तेमाल है. उदाहरण के लिए, लोगो का आकार, लॉगिन पेज पर मौजूद उन एलिमेंट में प्रतिध्वनित होता है जिन पर आकार लागू होता है. इस स्टेप में, ऊपर की ओर बाएं कोने में कोण वाले कट से सामने की लेयर को स्टाइल करें.
backdrop.dart
में, एक नई क्लास _Frontlayer जोड़ें:
// TODO: Add _FrontLayer class (104)
class _FrontLayer extends StatelessWidget {
// TODO: Add on-tap callback (104)
const _FrontLayer({
Key? key,
required this.child,
}) : super(key: key);
final Widget child;
@override
Widget build(BuildContext context) {
return Material(
elevation: 16.0,
shape: const BeveledRectangleBorder(
borderRadius: BorderRadius.only(topLeft: Radius.circular(46.0)),
),
child: Column(
crossAxisAlignment: CrossAxisAlignment.stretch,
children: <Widget>[
// TODO: Add a GestureDetector (104)
Expanded(
child: child,
),
],
),
);
}
}
इसके बाद, _BackdropState के _buildStack()
फ़ंक्शन में, फ़्रंट लेयर को _FrontLayer में रैप करें:
Widget _buildStack() {
// TODO: Create a RelativeRectTween Animation (104)
return Stack(
key: _backdropKey,
children: <Widget>[
// TODO: Wrap backLayer in an ExcludeSemantics widget (104)
widget.backLayer,
// TODO: Add a PositionedTransition (104)
// TODO: Wrap front layer in _FrontLayer (104)
_FrontLayer(child: widget.frontLayer),
],
);
}
फिर से लोड करें.
Android | iOS |
हमने Shrine के मुख्य प्लैटफ़ॉर्म को कस्टम आकार दिया है. हालांकि, हम चाहते हैं कि इसे ऐप्लिकेशन बार के साथ विज़ुअल तौर पर कनेक्ट किया जाए.
ऐप्लिकेशन बार का रंग बदलना
app.dart
में, _buildShrineTheme()
फ़ंक्शन को इस तरह बदलें:
ThemeData _buildShrineTheme() {
final ThemeData base = ThemeData.light(useMaterial3: true);
return base.copyWith(
colorScheme: base.colorScheme.copyWith(
primary: kShrinePink100,
onPrimary: kShrineBrown900,
secondary: kShrineBrown900,
error: kShrineErrorRed,
),
textTheme: _buildShrineTextTheme(base.textTheme),
textSelectionTheme: const TextSelectionThemeData(
selectionColor: kShrinePink100,
),
appBarTheme: const AppBarTheme(
foregroundColor: kShrineBrown900,
backgroundColor: kShrinePink100,
),
inputDecorationTheme: const InputDecorationTheme(
border: CutCornersBorder(),
focusedBorder: CutCornersBorder(
borderSide: BorderSide(
width: 2.0,
color: kShrineBrown900,
),
),
floatingLabelStyle: TextStyle(
color: kShrineBrown900,
),
),
);
}
हॉट रीस्टार्ट. अब आपको नया रंगीन ऐप्लिकेशन बार दिखेगा.
Android | iOS |
इस बदलाव की वजह से, उपयोगकर्ताओं को सामने की सफ़ेद लेयर के पीछे कुछ दिख सकता है. चलिए, मोशन जोड़ते हैं, ताकि उपयोगकर्ता बैकग्राउंड की बैक लेयर देख सकें.
6. मोशन जोड़ें
ऐप्लिकेशन को दिलचस्प बनाने के लिए, मोशन का इस्तेमाल किया जा सकता है. यह बड़ा और ड्रामा वाला हो सकता है, छोटा और कम हो सकता है या इन दोनों के बीच में हो सकता है. हालांकि, याद रखें कि जिस तरह का मोशन इफ़ेक्ट इस्तेमाल किया जा रहा है वह स्थिति के हिसाब से सही होना चाहिए. बार-बार होने वाली सामान्य कार्रवाइयों पर लागू होने वाला मोशन छोटा और सूक्ष्म होना चाहिए, ताकि कार्रवाइयों से उपयोगकर्ता का ध्यान न भटके या नियमित तौर पर बहुत ज़्यादा समय न लगे. हालांकि, कुछ मामलों में ऐनिमेशन का इस्तेमाल करना ज़रूरी हो सकता है. जैसे, जब कोई उपयोगकर्ता पहली बार आपका ऐप्लिकेशन खोलता है. ऐनिमेशन की मदद से, उपयोगकर्ता को आपके ऐप्लिकेशन को इस्तेमाल करने का तरीका बताया जा सकता है.
मेन्यू बटन पर 'दिखाएं' मोशन जोड़ना
backdrop.dart
के सबसे ऊपर, किसी भी क्लास या फ़ंक्शन के दायरे से बाहर, एक कॉन्स्टेंट जोड़ें. इससे हमें अपने ऐनिमेशन के वेग को दिखाने में मदद मिलेगी:
// TODO: Add velocity constant (104)
const double _kFlingVelocity = 2.0;
_BackdropState में AnimationController विजेट जोड़ें, उसे initState()
फ़ंक्शन में इंस्टैंशिएट करें, और स्टेटस के dispose()
फ़ंक्शन में उसे डिस्पोज करें:
// TODO: Add AnimationController widget (104)
late AnimationController _controller;
@override
void initState() {
super.initState();
_controller = AnimationController(
duration: const Duration(milliseconds: 300),
value: 1.0,
vsync: this,
);
}
// TODO: Add override for didUpdateWidget (104)
@override
void dispose() {
_controller.dispose();
super.dispose();
}
// TODO: Add functions to get and change front layer visibility (104)
ऐनिमेशन कंट्रोलर, ऐनिमेशन को कोऑर्डिनेट करता है और आपको ऐनिमेशन को चलाने, रिवर्स करने, और बंद करने के लिए एपीआई देता है. अब हमें ऐसे फ़ंक्शन चाहिए जिनसे यह आगे-पीछे हो सके.
ऐसे फ़ंक्शन जोड़ें जो सामने की लेयर को दिखाने या छिपाने का फ़ैसला लेते हैं:
// TODO: Add functions to get and change front layer visibility (104)
bool get _frontLayerVisible {
final AnimationStatus status = _controller.status;
return status == AnimationStatus.completed ||
status == AnimationStatus.forward;
}
void _toggleBackdropLayerVisibility() {
_controller.fling(
velocity: _frontLayerVisible ? -_kFlingVelocity : _kFlingVelocity);
}
बैकलेयर को किसी ExternalSemantics विजेट में रैप करें. जब बैक लेयर नहीं दिखेगी, तब यह विजेट, बैक लेयर के मेन्यू आइटम को सेमेटिक्स ट्री से हटा देगा.
return Stack(
key: _backdropKey,
children: <Widget>[
// TODO: Wrap backLayer in an ExcludeSemantics widget (104)
ExcludeSemantics(
child: widget.backLayer,
excluding: _frontLayerVisible,
),
...
_buildStack() फ़ंक्शन को बदलकर, BuildContext और BoxConstraints को शामिल करें. साथ ही, PositionedTransition भी शामिल करें, जो RelativeRectTween ऐनिमेशन का इस्तेमाल करता है:
// TODO: Add BuildContext and BoxConstraints parameters to _buildStack (104)
Widget _buildStack(BuildContext context, BoxConstraints constraints) {
const double layerTitleHeight = 48.0;
final Size layerSize = constraints.biggest;
final double layerTop = layerSize.height - layerTitleHeight;
// TODO: Create a RelativeRectTween Animation (104)
Animation<RelativeRect> layerAnimation = RelativeRectTween(
begin: RelativeRect.fromLTRB(
0.0, layerTop, 0.0, layerTop - layerSize.height),
end: const RelativeRect.fromLTRB(0.0, 0.0, 0.0, 0.0),
).animate(_controller.view);
return Stack(
key: _backdropKey,
children: <Widget>[
// TODO: Wrap backLayer in an ExcludeSemantics widget (104)
ExcludeSemantics(
child: widget.backLayer,
excluding: _frontLayerVisible,
),
// TODO: Add a PositionedTransition (104)
PositionedTransition(
rect: layerAnimation,
child: _FrontLayer(
// TODO: Implement onTap property on _BackdropState (104)
child: widget.frontLayer,
),
),
],
);
}
आखिर में, Scaffold के बॉडी के लिए _buildStack फ़ंक्शन को कॉल करने के बजाय, LayoutBuilder विजेट दिखाएं. यह विजेट, _buildStack को अपने बिल्डर के तौर पर इस्तेमाल करता है:
return Scaffold(
appBar: appBar,
// TODO: Return a LayoutBuilder widget (104)
body: LayoutBuilder(builder: _buildStack),
);
हमने LayoutBuilder के इस्तेमाल से लेआउट टाइम तक, फ़्रंट/बैक लेयर स्टैक बनाने में देरी कर दी है, ताकि हम बैकड्रॉप की असल ऊंचाई शामिल कर सकें. LayoutBuilder एक खास विजेट है, उसका बिल्डर कॉलबैक साइज़ कंस्ट्रेंट देता है.
build()
फ़ंक्शन में, ऐप्लिकेशन बार के सबसे आगे मौजूद मेन्यू आइकॉन को आइकॉनबटन में बदलें. बटन पर टैप करने पर, सामने की लेयर के दिखने की सेटिंग को टॉगल करने के लिए, इसका इस्तेमाल करें.
// TODO: Replace leading menu icon with IconButton (104)
leading: IconButton(
icon: const Icon(Icons.menu),
onPressed: _toggleBackdropLayerVisibility,
),
रीलोड करने के बाद, सिम्युलेटर में मेन्यू बटन पर टैप करें.
Android | iOS |
सामने की लेयर, ऐनिमेशन के साथ नीचे की ओर स्लाइड करती है. हालांकि, नीचे की ओर देखने पर, आपको लाल रंग में गड़बड़ी का एक मैसेज और ओवरफ़्लो गड़बड़ी का एक मैसेज दिखेगा. ऐसा इसलिए होता है, क्योंकि इस ऐनिमेशन की वजह से AsymmetricView छोटा हो जाता है. इससे कॉलम के लिए जगह कम हो जाती है. आखिर में, कॉलम दिए गए स्पेस में अपने-आप नहीं दिख पाते और गड़बड़ी का मैसेज दिखता है. अगर कॉलम को ListViews से बदल दिया जाता है, तो कॉलम का साइज़ ऐनिमेशन के दौरान नहीं बदलना चाहिए.
प्रॉडक्ट कॉलम को ListView में रैप करें
supplemental/product_columns.dart
में, OneProductCardColumn
के कॉलम को ListView से बदलें:
class OneProductCardColumn extends StatelessWidget {
const OneProductCardColumn({required this.product, Key? key}) : super(key: key);
final Product product;
@override
Widget build(BuildContext context) {
// TODO: Replace Column with a ListView (104)
return ListView(
physics: const ClampingScrollPhysics(),
reverse: true,
children: <Widget>[
ConstrainedBox(
constraints: const BoxConstraints(
maxWidth: 550,
),
child: ProductCard(
product: product,
),
),
const SizedBox(
height: 40.0,
),
],
);
}
}
कॉलम में MainAxisAlignment.end
शामिल है. सबसे नीचे से लेआउट शुरू करने के लिए, reverse: true
का निशान लगाएं. बदलाव की भरपाई करने के लिए बच्चों के क्रम को उलट दिया गया है.
फिर से लोड करें और मेन्यू बटन पर टैप करें.
Android | iOS |
OneProductCardColumn पर, स्क्रीन के किनारे दिखने वाली स्लेटी रंग की चेतावनी हट गई है! अब दूसरे को ठीक करते हैं.
supplemental/product_columns.dart
में, imageAspectRatio
का हिसाब लगाने का तरीका बदलें. साथ ही, TwoProductCardColumn
में मौजूद कॉलम को ListView से बदलें:
// TODO: Change imageAspectRatio calculation (104)
double imageAspectRatio = heightOfImages >= 0.0
? constraints.biggest.width / heightOfImages
: 49.0 / 33.0;
// TODO: Replace Column with a ListView (104)
return ListView(
physics: const ClampingScrollPhysics(),
children: <Widget>[
Padding(
padding: const EdgeInsetsDirectional.only(start: 28.0),
child: top != null
? ProductCard(
imageAspectRatio: imageAspectRatio,
product: top!,
)
: SizedBox(
height: heightOfCards,
),
),
const SizedBox(height: spacerHeight),
Padding(
padding: const EdgeInsetsDirectional.only(end: 28.0),
child: ProductCard(
imageAspectRatio: imageAspectRatio,
product: bottom,
),
),
],
);
हमने imageAspectRatio
में कुछ सुरक्षा सुविधाएं भी जोड़ी हैं.
फिर से लोड करें. इसके बाद, मेन्यू बटन पर टैप करें.
Android | iOS |
और ज़्यादा ओवरफ़्लो नहीं.
7. बैक लेयर पर मेन्यू जोड़ना
मेन्यू, टैप किए जा सकने वाले टेक्स्ट आइटम की सूची होती है. इससे टेक्स्ट आइटम टच किए जाने पर, सुनने वालों को सूचना मिलती है. इस चरण में, आपको कैटगरी के हिसाब से फ़िल्टर करने का मेन्यू जोड़ना होगा.
मेन्यू जोड़ना
फ़्रंट लेयर में मेन्यू और बैक लेयर में इंटरैक्टिव बटन जोड़ें.
lib/category_menu_page.dart
नाम की एक नई फ़ाइल बनाएं:
import 'package:flutter/material.dart';
import 'colors.dart';
import 'model/product.dart';
class CategoryMenuPage extends StatelessWidget {
final Category currentCategory;
final ValueChanged<Category> onCategoryTap;
final List<Category> _categories = Category.values;
const CategoryMenuPage({
Key? key,
required this.currentCategory,
required this.onCategoryTap,
}) : super(key: key);
Widget _buildCategory(Category category, BuildContext context) {
final categoryString =
category.toString().replaceAll('Category.', '').toUpperCase();
final ThemeData theme = Theme.of(context);
return GestureDetector(
onTap: () => onCategoryTap(category),
child: category == currentCategory
? Column(
children: <Widget>[
const SizedBox(height: 16.0),
Text(
categoryString,
style: theme.textTheme.bodyLarge,
textAlign: TextAlign.center,
),
const SizedBox(height: 14.0),
Container(
width: 70.0,
height: 2.0,
color: kShrinePink400,
),
],
)
: Padding(
padding: const EdgeInsets.symmetric(vertical: 16.0),
child: Text(
categoryString,
style: theme.textTheme.bodyLarge!.copyWith(
color: kShrineBrown900.withAlpha(153)
),
textAlign: TextAlign.center,
),
),
);
}
@override
Widget build(BuildContext context) {
return Center(
child: Container(
padding: const EdgeInsets.only(top: 40.0),
color: kShrinePink100,
child: ListView(
children: _categories
.map((Category c) => _buildCategory(c, context))
.toList()),
),
);
}
}
यह एक जेस्चर डिटेक्टर है, जो एक कॉलम को रैप करता है. इस कॉलम के चाइल्ड एलिमेंट, कैटगरी के नाम होते हैं. चुनी गई कैटगरी को अंडरलाइन किया जाता है.
app.dart
में, ShinApp विजेट को स्टेटलेस से स्टेटफ़ुल में बदलें.
- हाइलाइट
ShrineApp.
- आपके IDE के आधार पर, कोड से जुड़ी कार्रवाइयां दिखाएं:
- Android Studio: ⌥Enter (macOS) या alt + enter दबाएं
- VS Code: ⌘. (macOS) या Ctrl+ दबाएं.
- "StatefulWidget में बदलें" चुनें.
- schemaAppState क्लास को निजी (_ShineAppState) में बदलें. ShrineAppState पर दायां क्लिक करें और
- Android Studio: रीफ़ैक्टर > नाम बदलें को चुनें
- VS Code: 'सिंबल का नाम बदलें' चुनें
- क्लास को निजी बनाने के लिए, _ShineAppState डालें.
app.dart
में, चुनी गई कैटगरी के लिए _ShineAppState में एक वैरिएबल जोड़ें. साथ ही, टैप करने पर कॉलबैक जोड़ें:
class _ShrineAppState extends State<ShrineApp> {
Category _currentCategory = Category.all;
void _onCategoryTap(Category category) {
setState(() {
_currentCategory = category;
});
}
इसके बाद, बैक लेयर को CategoryMenuPage में बदलें.
app.dart
में, कैटगरी मेन्यूपेज इंपोर्ट करें:
import 'backdrop.dart';
import 'category_menu_page.dart';
import 'colors.dart';
import 'home.dart';
import 'login.dart';
import 'model/product.dart';
import 'supplemental/cut_corners_border.dart';
इंस्टेंस वैरिएबल लेने के लिए, build()
फ़ंक्शन में backlayer फ़ील्ड को कैटगरीमेन्यूपेज और वर्तमान श्रेणी फ़ील्ड में बदलें.
'/': (BuildContext context) => Backdrop(
// TODO: Make currentCategory field take _currentCategory (104)
currentCategory: _currentCategory,
// TODO: Pass _currentCategory for frontLayer (104)
frontLayer: HomePage(),
// TODO: Change backLayer field value to CategoryMenuPage (104)
backLayer: CategoryMenuPage(
currentCategory: _currentCategory,
onCategoryTap: _onCategoryTap,
),
frontTitle: const Text('SHRINE'),
backTitle: const Text('MENU'),
),
मेन्यू बटन को फिर से लोड करें और उस पर टैप करें.
Android | iOS |
मेन्यू के किसी विकल्प पर टैप करने पर, अभी कुछ नहीं होता. चलिए, इसे ठीक करते हैं।
home.dart
में, कैटगरी के लिए एक वैरिएबल जोड़ें और उसे AsymmetricView में पास करें.
import 'package:flutter/material.dart';
import 'model/product.dart';
import 'model/products_repository.dart';
import 'supplemental/asymmetric_view.dart';
class HomePage extends StatelessWidget {
// TODO: Add a variable for Category (104)
final Category category;
const HomePage({this.category = Category.all, Key? key}) : super(key: key);
@override
Widget build(BuildContext context) {
// TODO: Pass Category variable to AsymmetricView (104)
return AsymmetricView(
products: ProductsRepository.loadProducts(category),
);
}
}
app.dart
में, frontLayer
के लिए _currentCategory
पास करें:.
// TODO: Pass _currentCategory for frontLayer (104)
frontLayer: HomePage(category: _currentCategory),
फिर से लोड करें. सिम्युलेटर में मेन्यू बटन पर टैप करें और कोई कैटगरी चुनें.
Android | iOS |
उन्हें फ़िल्टर किया गया है!
मेन्यू को चुनने के बाद, सामने की लेयर बंद करें
backdrop.dart
में, _BackdropState में didUpdateWidget()
फ़ंक्शन के लिए बदलाव जोड़ें. यह फ़ंक्शन तब कॉल किया जाता है, जब विजेट का कॉन्फ़िगरेशन बदलता है:
// TODO: Add override for didUpdateWidget() (104)
@override
void didUpdateWidget(Backdrop old) {
super.didUpdateWidget(old);
if (widget.currentCategory != old.currentCategory) {
_toggleBackdropLayerVisibility();
} else if (!_frontLayerVisible) {
_controller.fling(velocity: _kFlingVelocity);
}
}
हॉट रीलोड ट्रिगर करने के लिए अपना प्रोजेक्ट सेव करें. मेन्यू आइकॉन पर टैप करें और कोई कैटगरी चुनें. इसके बाद, मेन्यू अपने-आप बंद हो जाएगा और आपको चुने गए आइटम की कैटगरी दिखेगी. अब आपको यह सुविधा फ़्रंट लेयर में भी जोड़नी होगी.
सामने वाली लेयर को टॉगल करें
backdrop.dart
में, बैकग्राउंड लेयर में टैप करने पर कॉलबैक जोड़ें:
class _FrontLayer extends StatelessWidget {
// TODO: Add on-tap callback (104)
const _FrontLayer({
Key? key,
this.onTap, // New code
required this.child,
}) : super(key: key);
final VoidCallback? onTap; // New code
final Widget child;
इसके बाद, _FrontLayer के चाइल्ड: कॉलम के चाइल्ड में GestureDetector जोड़ें:.
child: Column(
crossAxisAlignment: CrossAxisAlignment.stretch,
children: <Widget>[
// TODO: Add a GestureDetector (104)
GestureDetector(
behavior: HitTestBehavior.opaque,
onTap: onTap,
child: Container(
height: 40.0,
alignment: AlignmentDirectional.centerStart,
),
),
Expanded(
child: child,
),
],
),
इसके बाद, _buildStack()
फ़ंक्शन में _BackdropState पर नई onTap
प्रॉपर्टी लागू करें:
PositionedTransition(
rect: layerAnimation,
child: _FrontLayer(
// TODO: Implement onTap property on _BackdropState (104)
onTap: _toggleBackdropLayerVisibility,
child: widget.frontLayer,
),
),
फिर से लोड करें और सामने वाली लेयर के सबसे ऊपर टैप करें. हर बार सामने की लेयर के ऊपर टैप करने पर, लेयर खुलनी और बंद होनी चाहिए.
8. ब्रैंड का आइकॉन जोड़ना
ब्रैंडेड आइकॉनोग्राफ़ी में जाने-पहचाने आइकॉन भी शामिल हैं. आइए, वीडियो के टीज़र के तौर पर दिखने वाले आइकॉन को पसंद के मुताबिक बनाएं और उसे अपने टाइटल के साथ मर्ज करें, ताकि आपके वीडियो का लुक यूनीक और ब्रैंड के हिसाब से हो.
मेन्यू बटन का आइकॉन बदलना
Android | iOS |
backdrop.dart
में, एक नई क्लास _BackdropTitle बनाएं.
// TODO: Add _BackdropTitle class (104)
class _BackdropTitle extends AnimatedWidget {
final void Function() onPress;
final Widget frontTitle;
final Widget backTitle;
const _BackdropTitle({
Key? key,
required Animation<double> listenable,
required this.onPress,
required this.frontTitle,
required this.backTitle,
}) : _listenable = listenable,
super(key: key, listenable: listenable);
final Animation<double> _listenable;
@override
Widget build(BuildContext context) {
final Animation<double> animation = _listenable;
return DefaultTextStyle(
style: Theme.of(context).textTheme.titleLarge!,
softWrap: false,
overflow: TextOverflow.ellipsis,
child: Row(children: <Widget>[
// branded icon
SizedBox(
width: 72.0,
child: IconButton(
padding: const EdgeInsets.only(right: 8.0),
onPressed: this.onPress,
icon: Stack(children: <Widget>[
Opacity(
opacity: animation.value,
child: const ImageIcon(AssetImage('assets/slanted_menu.png')),
),
FractionalTranslation(
translation: Tween<Offset>(
begin: Offset.zero,
end: const Offset(1.0, 0.0),
).evaluate(animation),
child: const ImageIcon(AssetImage('assets/diamond.png')),
)]),
),
),
// Here, we do a custom cross fade between backTitle and frontTitle.
// This makes a smooth animation between the two texts.
Stack(
children: <Widget>[
Opacity(
opacity: CurvedAnimation(
parent: ReverseAnimation(animation),
curve: const Interval(0.5, 1.0),
).value,
child: FractionalTranslation(
translation: Tween<Offset>(
begin: Offset.zero,
end: const Offset(0.5, 0.0),
).evaluate(animation),
child: backTitle,
),
),
Opacity(
opacity: CurvedAnimation(
parent: animation,
curve: const Interval(0.5, 1.0),
).value,
child: FractionalTranslation(
translation: Tween<Offset>(
begin: const Offset(-0.25, 0.0),
end: Offset.zero,
).evaluate(animation),
child: frontTitle,
),
),
],
)
]),
);
}
}
_BackdropTitle
एक कस्टम विजेट है, जो AppBar
विजेट के title
पैरामीटर के लिए सादे Text
विजेट की जगह लेगा. इसमें ऐनिमेशन वाला मेन्यू आइकॉन है. साथ ही, इसमें आगे और पीछे वाले टाइटल के बीच ऐनिमेशन वाले ट्रांज़िशन हैं. ऐनिमेशन वाले मेन्यू आइकॉन में नई एसेट का इस्तेमाल किया जाएगा. नए slanted_menu.png
का रेफ़रंस, pubspec.yaml
में जोड़ना होगा.
assets:
- assets/diamond.png
# TODO: Add slanted menu asset (104)
- assets/slanted_menu.png
- packages/shrine_images/0-0.jpg
AppBar
बिल्डर से leading
प्रॉपर्टी हटाएं. leading
के मूल विजेट की जगह पर पसंद के मुताबिक बनाए गए ब्रैंड वाले आइकॉन को दिखाने के लिए, उसे हटाना ज़रूरी है. ब्रैंड वाले आइकॉन के लिए ऐनिमेशन listenable
और onPress
हैंडलर _BackdropTitle
को पास किए जाते हैं. frontTitle
और backTitle
को भी पास किया जाता है, ताकि उन्हें बैकड्रॉप टाइटल में रेंडर किया जा सके. AppBar
का title
पैरामीटर ऐसा दिखना चाहिए:
// TODO: Create title with _BackdropTitle parameter (104)
title: _BackdropTitle(
listenable: _controller.view,
onPress: _toggleBackdropLayerVisibility,
frontTitle: widget.frontTitle,
backTitle: widget.backTitle,
),
ब्रैंड वाला आइकॉन, _BackdropTitle.
में बनाया जाता है. इसमें Stack
ऐनिमेशन वाले आइकॉन होते हैं: एक स्लैंटेड मेन्यू और डायमंड, जिसे IconButton
में रैप किया जाता है, ताकि इसे दबाया जा सके. इसके बाद, IconButton
को SizedBox
में लपेटा जाता है, ताकि आइकॉन के हॉरिज़ॉन्टल मोशन के लिए जगह बनाई जा सके.
Flutter के "सब कुछ एक विजेट है" आर्किटेक्चर की मदद से, डिफ़ॉल्ट AppBar
के लेआउट में बदलाव किया जा सकता है. इसके लिए, आपको पूरी तरह से नया कस्टम AppBar
विजेट बनाने की ज़रूरत नहीं है. title
पैरामीटर, मूल रूप से एक Text
विजेट है. इसे ज़्यादा जटिल _BackdropTitle
से बदला जा सकता है. _BackdropTitle
में कस्टम आइकॉन भी शामिल होता है. इसलिए, यह leading
प्रॉपर्टी की जगह ले लेता है, जिसे अब हटाया जा सकता है. विजेट को आसानी से बदलने के लिए, किसी भी दूसरे पैरामीटर में बदलाव करने की ज़रूरत नहीं होती. जैसे, ऐक्शन आइकॉन, जो अपने-आप काम करते रहते हैं.
लॉगिन स्क्रीन पर, फिर से शॉर्टकट जोड़ें
backdrop.dart,
ऐप्लिकेशन बार में मौजूद आखिरी दो आइकॉन से, लॉगिन स्क्रीन पर वापस जाने का शॉर्टकट जोड़ें: आइकॉन के सेमेंटिक लेबल बदलें, ताकि उनका नया मकसद दिखे.
// TODO: Add shortcut to login screen from trailing icons (104)
IconButton(
icon: const Icon(
Icons.search,
semanticLabel: 'login', // New code
),
onPressed: () {
// TODO: Add open login (104)
Navigator.push(
context,
MaterialPageRoute(
builder: (BuildContext context) => LoginPage()),
);
},
),
IconButton(
icon: const Icon(
Icons.tune,
semanticLabel: 'login', // New code
),
onPressed: () {
// TODO: Add open login (104)
Navigator.push(
context,
MaterialPageRoute(
builder: (BuildContext context) => LoginPage()),
);
},
),
पेज को फिर से लोड करने पर, आपको गड़बड़ी का मैसेज दिखेगा. गड़बड़ी को ठीक करने के लिए, login.dart
इंपोर्ट करें:
import 'login.dart';
ऐप्लिकेशन को फिर से लोड करें. इसके बाद, लॉगिन स्क्रीन पर वापस जाने के लिए, 'खोजें' या 'ट्यून करें' बटन पर टैप करें.
9. बधाई हो!
इन चार कोडलैब में, आपको Material Components का इस्तेमाल करके, यूनीक और बेहतरीन उपयोगकर्ता अनुभव बनाने का तरीका पता चला है. इससे, ब्रैंड की खासियत और स्टाइल को दिखाया जा सकता है.
अगले चरण
यह कोडलैब, MDC-104, कोडलैब के इस क्रम को पूरा करता है. मटीरियल कॉम्पोनेंट विजेट के कैटलॉग में जाकर, Material Flutter में और भी कॉम्पोनेंट एक्सप्लोर किए जा सकते हैं.
नए लक्ष्य के लिए, ब्रैंड वाले आइकॉन को ऐसे AnimatedIcon से बदलने की कोशिश करें जो बैकग्राउंड के दिखने पर दो आइकॉन के बीच ऐनिमेट होता है.
अपनी पसंद के हिसाब से, Flutter कोडलैब आज़माए जा सकते हैं. हमारे पास Material के लिए एक और कोडलैब है, जिसमें आपकी दिलचस्पी हो सकती है: Flutter के लिए Material Motion की मदद से खूबसूरत ट्रांज़िशन बनाना.