1. מבוא
| רכיבי Material (MDC) עוזרים למפתחים להטמיע את Material Design. MDC נוצרה על ידי צוות של מהנדסים ומעצבי UX ב-Google. היא כוללת עשרות רכיבי ממשק משתמש יפים ופונקציונליים, וזמינה ל-Android, ל-iOS, לאינטרנט ול-Flutter.material.io/develop |
ב-codelab MDC-101, השתמשתם בשני רכיבי Material כדי ליצור דף כניסה: שדות טקסט ולחצנים עם אפקט גלי דיו. עכשיו נרחיב את הבסיס הזה על ידי הוספת ניווט, מבנה ונתונים.
מה תפַתחו
ב-codelab הזה תיצרו מסך בית לאפליקציה בשם Shrine, אפליקציית מסחר אלקטרוני שמוכרת בגדים ומוצרים לבית. היא תכלול:
- סרגל אפליקציה עליון
- רשימת מוצרים בפריסה של רשת
Android | iOS |
|
|
רכיבים ותת-מערכות של Material Flutter במעבדת התכנות הזו
- סרגל האפליקציה העליון
- רשתות
- כרטיסים
איך היית מדרג את רמת הניסיון שלך בפיתוח ב-Flutter?
2. הגדרת סביבת הפיתוח של Flutter
כדי להשלים את שיעור ה-Lab הזה, תצטרכו שני סוגי תוכנה: Flutter SDK ועורך.
אפשר להריץ את ה-codelab באמצעות כל אחד מהמכשירים הבאים:
- מכשיר פיזי עם Android או iOS שמחובר למחשב ומוגדר למצב פיתוח.
- סימולטור iOS (נדרשת התקנה של כלי Xcode).
- אמולטור Android (נדרשת הגדרה ב-Android Studio).
- דפדפן (חובה להשתמש ב-Chrome לצורך ניפוי באגים).
- כאפליקציה למחשב Windows, Linux או macOS. אתם צריכים לפתח בפלטפורמה שבה אתם מתכננים לבצע פריסה. לכן, אם רוצים לפתח אפליקציה למחשב שולחני עם Windows, צריך לפתח ב-Windows כדי לגשת לשרשרת הבנייה המתאימה. יש דרישות ספציפיות למערכות הפעלה שמוסברות בפירוט במאמר docs.flutter.dev/desktop.
3. הורדת אפליקציה לתחילת הדרך של ה-Codelab
המשך מ-MDC-101?
אם השלמתם את MDC-101, הקוד שלכם אמור להיות מוכן ל-codelab הזה. דילוג לשלב: הוספת סרגל אפליקציות עליון.
מתחילים מאפס?
הורדת האפליקציה של ה-Codelab למתחילים
אפליקציה לתחילת הדרך נמצאת בספרייה material-components-flutter-codelabs-102-starter_and_101-complete/mdc_100_series.
...או לשכפל אותו מ-GitHub
כדי לשכפל את ה-codelab הזה מ-GitHub, מריצים את הפקודות הבאות:
git clone https://github.com/material-components/material-components-flutter-codelabs.git cd material-components-flutter-codelabs/mdc_100_series git checkout 102-starter_and_101-complete
פותחים את הפרויקט ומריצים את האפליקציה
- פותחים את הפרויקט בכלי העריכה הרצוי.
- פועלים לפי ההוראות שבקטע תחילת העבודה: ניסיון נסיעה כדי להפעיל את האפליקציה בעורך שבחרתם.
הצלחת! בדף הכניסה של Shrine מתוך ה-codelab MDC-101 אמור להופיע במכשיר.
Android | iOS |
|
|
עכשיו, אחרי שמסך הכניסה נראה טוב, נמלא את האפליקציה במוצרים.
4. הוספת סרגל אפליקציות עליון
אם תלחצו עכשיו על הלחצן 'הבא', תוכלו לראות את מסך הבית עם הכיתוב 'הצלחתם!'. נהדר! אבל עכשיו למשתמש אין פעולות לבצע, או מושג לגבי המיקום שלו באפליקציה. כדי לעזור לו, הגיע הזמן להוסיף ניווט.
ב-Material Design יש דפוסי ניווט שמבטיחים רמת שימושיות גבוהה. אחד מהרכיבים הבולטים ביותר הוא סרגל האפליקציות העליון.
כדי לספק ניווט ולתת למשתמשים גישה מהירה לפעולות אחרות, נוסיף סרגל אפליקציה עליון.
הוספת ווידג'ט של סרגל אפליקציות
ב-home.dart, מוסיפים AppBar ל-Scaffold ומסירים את const שמודגש:
return const Scaffold(
// TODO: Add app bar (102)
appBar: AppBar(
// TODO: Add buttons and title (102)
),
הוספת AppBar לשדה appBar: של Scaffold נותנת לנו פריסה מושלמת בחינם, שבה AppBar נמצא בחלק העליון של הדף והגוף מתחתיו.
הוספת ווידג'ט טקסט
ב-home.dart, מוסיפים כותרת ל-AppBar:
// TODO: Add app bar (102)
appBar: AppBar(
// TODO: Add buttons and title (102)
title: const Text('SHRINE'),
// TODO: Add trailing buttons (102)
שומרים את הפרויקט.
Android | iOS |
|
|
בסרגלי אפליקציות רבים יש לחצן ליד הכותרת. נוסיף סמל תפריט לאפליקציה.
הוספת לחצן סמל מוביל
בזמן שאתם עדיין ב-home.dart, מגדירים IconButton לשדה leading: של AppBar. (כדאי להוסיף אותו לפני השדה title: כדי לחקות את הסדר מההתחלה לסוף):
// TODO: Add buttons and title (102)
leading: IconButton(
icon: const Icon(
Icons.menu,
semanticLabel: 'menu',
),
onPressed: () {
print('Menu button');
},
),
שומרים את הפרויקט.
Android | iOS |
|
|
סמל התפריט (שנקרא גם 'המבורגר') מופיע בדיוק במקום שבו מצפים לראות אותו.
אפשר גם להוסיף לחצנים בצד השמאלי של הכותרת. ב-Flutter, הן נקראות 'פעולות'.
הוספת פעולות
יש מקום לעוד שני לחצני סמל.
מוסיפים אותם למופע AppBar אחרי הכותרת:
// TODO: Add trailing buttons (102)
actions: <Widget>[
IconButton(
icon: const Icon(
Icons.search,
semanticLabel: 'search',
),
onPressed: () {
print('Search button');
},
),
IconButton(
icon: const Icon(
Icons.tune,
semanticLabel: 'filter',
),
onPressed: () {
print('Filter button');
},
),
],
שומרים את הפרויקט. מסך הבית אמור להיראות כך:
Android | iOS |
|
|
עכשיו לאפליקציה יש לחצן מוביל, כותרת ושתי פעולות בצד שמאל. בסרגל האפליקציות מוצג גם גובה באמצעות צל עדין שמראה שהוא נמצא בשכבה שונה מהתוכן.
5. הוספת כרטיס לרשת
עכשיו, כשיש לאפליקציה שלנו מבנה מסוים, נארגן את התוכן ונציב אותו בכרטיסים.
הוספת תצוגת רשת
נתחיל בהוספת כרטיס אחד מתחת לסרגל האפליקציות העליון. לווידג'ט Card לבדו אין מספיק מידע כדי להציג את עצמו במקום שבו נוכל לראות אותו, ולכן נרצה להוסיף אותו לווידג'ט GridView.
מחליפים את Center בגוף של Scaffold ב-GridView:
// TODO: Add a grid view (102)
body: GridView.count(
crossAxisCount: 2,
padding: const EdgeInsets.all(16.0),
childAspectRatio: 8.0 / 9.0,
// TODO: Build a grid of cards (102)
children: <Widget>[Card()],
),
בואו נפרק את הקוד הזה. ה-GridView מפעיל את הבונה count() כי מספר הפריטים שהוא מציג הוא ספיר ולא אינסופי. אבל צריך להוסיף לו מידע נוסף כדי להגדיר את הפריסה שלו.
הערך crossAxisCount: מציין כמה פריטים יש לרוחב. אנחנו רוצים 2 עמודות.
השדה padding: מספק מקום בכל 4 הצדדים של תצוגת הרשת. כמובן שאי אפשר לראות את הריווח בצדדים השמאלי או התחתון כי עדיין אין רכיבי צאצא של GridView לידם.
בשדה childAspectRatio: מציינים את גודל הפריטים על סמך יחס גובה-רוחב (רוחב חלקי גובה).
כברירת מחדל, בתצוגת רשת כל המשבצות הן באותו גודל.
יש לנו כרטיס אחד אבל הוא ריק. נוסיף ווידג'טים של ילדים לכרטיס שלנו.
פריסת התוכן
בכרטיסים צריכים להיות אזורים לתמונה, לכותרת ולטקסט משני.
מעדכנים את הילדים של GridView:
// TODO: Build a grid of cards (102)
children: <Widget>[
Card(
clipBehavior: Clip.antiAlias,
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: <Widget>[
AspectRatio(
aspectRatio: 18.0 / 11.0,
child: Image.asset('assets/diamond.png'),
),
Padding(
padding: const EdgeInsets.fromLTRB(16.0, 12.0, 16.0, 8.0),
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: <Widget>[
Text('Title'),
const SizedBox(height: 8.0),
Text('Secondary Text'),
],
),
),
],
),
)
],
הקוד הזה מוסיף ווידג'ט Column שמשמש לסידור הווידג'טים המשניים בצורה אנכית.
הערך crossAxisAlignment: field מציין CrossAxisAlignment.start, כלומר 'יישור הטקסט לקצה המוביל'.
הווידג'ט AspectRatio קובע את הצורה של התמונה, לא משנה איזה סוג תמונה מסופק.
המאפיין Padding (ריווח פנימי) מזיז את הטקסט קצת פנימה מהצד.
שני הווידג'טים Text מוערמים אנכית עם 8 נקודות של רווח ריק ביניהם (SizedBox). אנחנו יוצרים עוד עמודה כדי להכניס אותם לתוך הריווח הפנימי.
שומרים את הפרויקט.
Android | iOS |
|
|
בתצוגה המקדימה הזו אפשר לראות שהכרטיס מוסט מהקצה, עם פינות מעוגלות וצל (שמבטא את הגובה של הכרטיס). הצורה כולה נקראת 'מאגר' ב-Material. (לא להתבלבל עם מחלקת הווידג'ט בפועל שנקראת Container).
בדרך כלל הכרטיסים מוצגים באוסף עם כרטיסים אחרים. בואו נסדר אותם כקולקציה ברשת.
6. איך יוצרים אוסף כרטיסים
אם יש כמה כרטיסים במסך, הם מקובצים יחד לאוסף אחד או יותר. הקלפים באוסף נמצאים באותו מישור, כלומר הם נמצאים באותו גובה (אלא אם מרימים או גוררים את הקלפים, אבל לא נעשה את זה כאן).
הכפלת הכרטיס לאוסף
כרגע הכרטיס שלנו מוצג בשורה בשדה children: של GridView. זה הרבה קוד מוטמע שיכול להיות קשה לקריאה. בואו נחלץ אותו לפונקציה שיכולה ליצור כמה כרטיסים ריקים שרוצים, ומחזירה רשימה של כרטיסים.
יוצרים פונקציה פרטית חדשה מעל הפונקציה build() (חשוב לזכור שפונקציות שמתחילות בקו תחתון הן פונקציות API פרטיות):
// TODO: Make a collection of cards (102)
List<Card> _buildGridCards(int count) {
List<Card> cards = List.generate(
count,
(int index) {
return Card(
clipBehavior: Clip.antiAlias,
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: <Widget>[
AspectRatio(
aspectRatio: 18.0 / 11.0,
child: Image.asset('assets/diamond.png'),
),
Padding(
padding: const EdgeInsets.fromLTRB(16.0, 12.0, 16.0, 8.0),
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: const <Widget>[
Text('Title'),
SizedBox(height: 8.0),
Text('Secondary Text'),
],
),
),
],
),
);
},
);
return cards;
}
מקצים את הכרטיסים שנוצרו לשדה children של GridView. חשוב להחליף את כל מה שכלול ב-GridView בקוד החדש הזה:
// TODO: Add a grid view (102)
body: GridView.count(
crossAxisCount: 2,
padding: const EdgeInsets.all(16.0),
childAspectRatio: 8.0 / 9.0,
children: _buildGridCards(10) // Replace
),
שומרים את הפרויקט.
Android | iOS |
|
|
הכרטיסים מוצגים, אבל עדיין לא מופיע בהם כלום. עכשיו הגיע הזמן להוסיף נתוני מוצרים.
הוספת נתוני מוצרים
באפליקציה יש מוצרים עם תמונות, שמות ומחירים. נוסיף את זה לווידג'טים שכבר יש לנו בכרטיס
לאחר מכן, ב-home.dart, מייבאים חבילה חדשה וכמה קבצים שסיפקנו למודל נתונים:
import 'package:flutter/material.dart';
import 'package:intl/intl.dart';
import 'model/product.dart';
import 'model/products_repository.dart';
לבסוף, משנים את _buildGridCards() כדי לאחזר את פרטי המוצר ולהשתמש בנתונים האלה בכרטיסים:
// TODO: Make a collection of cards (102)
// Replace this entire method
List<Card> _buildGridCards(BuildContext context) {
List<Product> products = ProductsRepository.loadProducts(Category.all);
if (products.isEmpty) {
return const <Card>[];
}
final ThemeData theme = Theme.of(context);
final NumberFormat formatter = NumberFormat.simpleCurrency(
locale: Localizations.localeOf(context).toString());
return products.map((product) {
return Card(
clipBehavior: Clip.antiAlias,
// TODO: Adjust card heights (103)
child: Column(
// TODO: Center items on the card (103)
crossAxisAlignment: CrossAxisAlignment.start,
children: <Widget>[
AspectRatio(
aspectRatio: 18 / 11,
child: Image.asset(
product.assetName,
package: product.assetPackage,
// TODO: Adjust the box size (102)
),
),
Expanded(
child: Padding(
padding: const EdgeInsets.fromLTRB(16.0, 12.0, 16.0, 8.0),
child: Column(
// TODO: Align labels to the bottom and center (103)
crossAxisAlignment: CrossAxisAlignment.start,
// TODO: Change innermost Column (103)
children: <Widget>[
// TODO: Handle overflowing labels (103)
Text(
product.name,
style: theme.textTheme.titleLarge,
maxLines: 1,
),
const SizedBox(height: 8.0),
Text(
formatter.format(product.price),
style: theme.textTheme.titleSmall,
),
],
),
),
),
],
),
);
}).toList();
}
הערה: הקוד לא יקומפל ולא יפעל עדיין. יש לנו עוד שינוי אחד.
בנוסף, צריך לשנות את הפונקציה build() כדי להעביר את BuildContext אל _buildGridCards() לפני שמנסים לבצע קומפילציה:
// TODO: Add a grid view (102)
body: GridView.count(
crossAxisCount: 2,
padding: const EdgeInsets.all(16.0),
childAspectRatio: 8.0 / 9.0,
children: _buildGridCards(context) // Changed code
),
מבצעים הפעלה מחדש של האפליקציה.
Android | iOS |
|
|
יכול להיות שתשימו לב שלא הוספנו רווח אנכי בין הכרטיסים. הסיבה לכך היא שברירת המחדל היא 4 נקודות שוליים בחלק העליון והתחתון של התמונות.
שומרים את הפרויקט.
נתוני המוצרים מוצגים, אבל יש רווחים מיותרים מסביב לתמונות. התמונות מצוירות עם BoxFit של .scaleDown כברירת מחדל (במקרה הזה). נשנה את זה ל-.fitWidth כדי שהתמונות יוגדלו קצת ויוסרו הרווחים המיותרים.
מוסיפים לשדה fit: של התמונה את הערך BoxFit.fitWidth:
// TODO: Adjust the box size (102)
fit: BoxFit.fitWidth,
Android | iOS |
|
|
המוצרים שלנו מוצגים עכשיו באפליקציה בצורה מושלמת!
7. מעולה!
האפליקציה שלנו כוללת תהליך בסיסי שמעביר את המשתמש ממסך הכניסה למסך הבית, שבו אפשר לראות את המוצרים. הוספנו סרגל אפליקציות עליון (עם כותרת ושלושה לחצנים) וכרטיסים (להצגת התוכן של האפליקציה) בכמה שורות קוד בלבד. מסך הבית שלנו פשוט ופונקציונלי, עם מבנה בסיסי ותוכן שניתן לפעולה.
השלבים הבאים
עם סרגל האפליקציה העליון, הכרטיס, שדה הטקסט והלחצן, השתמשנו עכשיו בארבעה רכיבי ליבה מספריית Material Flutter! כדי לקבל מידע נוסף, אפשר לעיין בקטלוג הווידג'טים של רכיבי Material.
האפליקציה שלנו פועלת באופן מלא, אבל עדיין לא מבטאת מותג או נקודת מבט מסוימים. ב- MDC-103: Material Design Theming with Color, Shape, Elevation and Type, נתאים אישית את הסגנון של הרכיבים האלה כדי להביע מותג מודרני ותוסס.
















