1. מבוא
| רכיבי Material (MDC) עוזרים למפתחים להטמיע את Material Design. MDC נוצרה על ידי צוות של מהנדסים ומעצבי UX ב-Google. היא כוללת עשרות רכיבי ממשק משתמש יפים ופונקציונליים, וזמינה ל-Android, ל-iOS, לאינטרנט ול-Flutter.material.io/develop |
עכשיו אפשר להשתמש ב-Material Flutter כדי להתאים אישית את הסגנון הייחודי של האפליקציות יותר מאי פעם. ההרחבה האחרונה של Material Design מעניקה למעצבים ולמפתחים גמישות רבה יותר בביטוי המותג של המוצר.
ב-codelabs MDC-101 ו-MDC-102 השתמשתם ב-Material Flutter כדי לבנות את הבסיס של אפליקציה בשם Shrine, אפליקציית מסחר אלקטרוני שמוכרת בגדים ומוצרים לבית. האפליקציה הזו מכילה תהליך שימוש שמתחיל במסך כניסה, ואז מעביר את המשתמש למסך בית שבו מוצגים מוצרים.
מה תפַתחו
ב-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-102?
אם השלמתם את MDC-102, הקוד שלכם אמור להיות מוכן לשימוש ב-Codelab הזה. דילוג לשלב: שינוי הצבעים.
מתחילים מאפס?
הורדת אפליקציית ה-codelab למתחילים
אפליקציה לתחילת הדרך נמצאת בספרייה material-components-flutter-codelabs-103-starter_and_102-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 103-starter_and_102-complete
פותחים את הפרויקט ומריצים את האפליקציה
- פותחים את הפרויקט בכלי העריכה הרצוי.
- פועלים לפי ההוראות שבקטע תחילת העבודה: ניסיון נסיעה כדי להפעיל את האפליקציה בעורך שבחרתם.
הצלחת! בדף הכניסה של Shrine אמור להופיע במכשיר שלכם קוד שנוצר ב-codelab הקודם.
Android | iOS |
|
|
לוחצים על 'הבא' כדי לראות את דף המוצר.
Android | iOS |
|
|
4. שינוי הצבעים
נוצרה ערכת צבעים שמייצגת את המותג Shrine, והמעצב רוצה שתטמיעו את ערכת הצבעים הזו באפליקציית Shrine.
כדי להתחיל, נייבא את הצבעים האלה לפרויקט שלנו.
יצירה colors.dart
ליצור קובץ dart חדש ב-lib בשם colors.dart. מייבאים את הערכים material.dart ומוסיפים את הערכים const Color:
import 'package:flutter/material.dart';
const kShrinePink50 = Color(0xFFFEEAE6);
const kShrinePink100 = Color(0xFFFEDBD0);
const kShrinePink300 = Color(0xFFFBB8AC);
const kShrinePink400 = Color(0xFFEAA4A4);
const kShrineBrown900 = Color(0xFF442B2D);
const kShrineErrorRed = Color(0xFFC5032B);
const kShrineSurfaceWhite = Color(0xFFFFFBFA);
const kShrineBackgroundWhite = Colors.white;
לוח צבעים בהתאמה אישית
ערכת הצבעים הזו נוצרה על ידי מעצב עם צבעים בהתאמה אישית (כפי שמוצג בתמונה שלמטה). הוא מכיל צבעים שנבחרו מהמותג של Shrine והוחלו על Material Theme Editor, שהרחיב אותם כדי ליצור פלטה מלאה יותר. (הצבעים האלה לא נלקחו מלוחות הצבעים של Material משנת 2014).
בכלי Material Theme Editor הם מאורגנים בגוונים שמסומנים במספרים, כולל התוויות 50, 100, 200 ועד 900 של כל צבע. הגופן Shrine משתמש רק בגוונים 50, 100 ו-300 מלוח הצבעים הוורוד ובגוון 900 מלוח הצבעים החום.

כל פרמטר צבעוני בווידג'ט ממופה לצבע מתוך הסכמות האלה. לדוגמה, הצבע של העיטורים של שדה טקסט בזמן שמתקבלות בו נתונים צריך להיות הצבע הראשי של העיצוב. אם הצבע הזה לא נגיש (קשה לראות אותו על הרקע), צריך להשתמש בצבע אחר.
עכשיו, אחרי שבחרנו את הצבעים שבהם נשתמש, אפשר להחיל אותם על ממשק המשתמש. כדי לעשות את זה, נגדיר את הערכים של הווידג'ט ThemeData שאנחנו מחילים על מופע MaterialApp בחלק העליון של היררכיית הווידג'טים.
התאמה אישית של ThemeData.light()
Flutter כולל כמה ערכות נושא מובנות. אחד מהם הוא העיצוב הבהיר. במקום ליצור ווידג'ט ThemeData מאפס, נעתיק את העיצוב הבהיר ונשנה את הערכים כדי להתאים אותם לאפליקציה שלנו.
בואו נייבא את colors.dart אל app.dart.
import 'colors.dart';
אחר כך מוסיפים את הקוד הבא ל-app.dart מחוץ להיקף של המחלקה ShrineApp:
// TODO: Build a Shrine Theme (103)
final ThemeData _kShrineTheme = _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,
),
// TODO: Add the text themes (103)
// TODO: Decorate the inputs (103)
);
}
עכשיו מגדירים את theme: בסוף הפונקציה build() של ShrineApp (בווידג'ט MaterialApp) כנושא החדש:
// TODO: Customize the theme (103)
theme: _kShrineTheme, // New code
שומרים את הפרויקט. מסך הכניסה שלכם אמור להיראות כך:
Android | iOS |
|
|
5. שינוי סגנונות של טיפוגרפיה ותוויות
בנוסף לשינויים בצבעים, המעצב גם נתן לנו טיפוגרפיה ספציפית לשימוש. המאפיין ThemeData של Flutter כולל 3 ערכות נושא של טקסט. כל ערכת נושא של טקסט היא אוסף של סגנונות טקסט, כמו 'כותרת' ו'כותרת משנה'. נשתמש בכמה סגנונות לאפליקציה שלנו ונשנה חלק מהערכים.
התאמה אישית של עיצוב הטקסט
כדי לייבא גופנים לפרויקט, צריך להוסיף אותם לקובץ pubspec.yaml.
בקובץ pubspec.yaml, מוסיפים את השורה הבאה מיד אחרי התג flutter::
# TODO: Insert Fonts (103)
fonts:
- family: Rubik
fonts:
- asset: fonts/Rubik-Regular.ttf
- asset: fonts/Rubik-Medium.ttf
weight: 500
עכשיו יש לכם גישה לגופן Rubik ואפשר להשתמש בו.
פתרון בעיות בקובץ pubspec
אם מעתיקים ומדביקים את ההצהרה שלמעלה, יכול להיות שיופיעו שגיאות בהרצת הפקודה pub get. אם מופיעות שגיאות, מתחילים בהסרת הרווח הלבן המוביל והחלפתו ברווחים באמצעות הזחה של 2 רווחים. (שני רווחים לפני
fonts:
, ארבעה רווחים לפני
family: Rubik
, וכן הלאה.)
אם מופיעה ההודעה Mapping values are not allowed here (מיפוי ערכים אסור כאן), צריך לבדוק את ההזחה של השורה שבה הבעיה מתרחשת ושל השורות שמעליה.
ב-login.dart, משנים את הערכים הבאים בתוך Column():
Column(
children: <Widget>[
Image.asset('assets/diamond.png'),
const SizedBox(height: 16.0),
Text(
'SHRINE',
style: Theme.of(context).textTheme.headlineSmall,
),
],
)
ב-app.dart, מוסיפים את הטקסט הבא אחרי _buildShrineTheme():
// TODO: Build a Shrine Text Theme (103)
TextTheme _buildShrineTextTheme(TextTheme base) {
return base
.copyWith(
headlineSmall: base.headlineSmall!.copyWith(
fontWeight: FontWeight.w500,
),
titleLarge: base.titleLarge!.copyWith(
fontSize: 18.0,
),
bodySmall: base.bodySmall!.copyWith(
fontWeight: FontWeight.w400,
fontSize: 14.0,
),
bodyLarge: base.bodyLarge!.copyWith(
fontWeight: FontWeight.w500,
fontSize: 16.0,
),
)
.apply(
fontFamily: 'Rubik',
displayColor: kShrineBrown900,
bodyColor: kShrineBrown900,
);
}
הפעולה הזו מקבלת TextTheme ומשנה את המראה של הכותרות, הכותרות הראשיות והכתוביות.
החלת המשתנה fontFamily באופן הזה גורמת לכך שהשינויים יחולו רק על ערכי סולם הטיפוגרפיה שצוינו במשתנה copyWith() (כותרת, שם, כיתוב).
עבור חלק מהגופנים, אנחנו מגדירים fontWeight מותאם אישית, במרווחים של 100: w500 (המשקל 500) מתאים לבינוני ו-w400 מתאים לרגיל.
שימוש בטקסטים חדשים
מוסיפים את הנושאים הבאים ל-_buildShrineTheme אחרי השגיאה:
// TODO: Add the text themes (103)
textTheme: _buildShrineTextTheme(base.textTheme),
textSelectionTheme: const TextSelectionThemeData(
selectionColor: kShrinePink100,
),
שומרים את הפרויקט. הפעם, צריך גם להפעיל מחדש את האפליקציה (פעולה שנקראת Hot Restart), כי שינינו את הגופנים.
Android | iOS |
|
|
הטקסט במסך הכניסה ובמסך הבית נראה שונה – חלק מהטקסט מוצג באמצעות הגופן Rubik, וחלק אחר מוצג בחום, במקום בשחור או בלבן. גם הסמלים מוצגים בחום.
הקטנת הטקסט
התוויות גדולות מדי.
ב-home.dart, משנים את children: של העמודה הפנימית ביותר:
// TODO: Change innermost Column (103)
children: <Widget>[
// TODO: Handle overflowing labels (103)
Text(
product.name,
style: theme.textTheme.button,
softWrap: false,
overflow: TextOverflow.ellipsis,
maxLines: 1,
),
const SizedBox(height: 4.0),
Text(
formatter.format(product.price),
style: theme.textTheme.bodySmall,
),
// End new code
],
מרכזים את הטקסט ומשחררים אותו
אנחנו רוצים למרכז את התוויות וליישר את הטקסט לתחתית של כל כרטיס, במקום לתחתית של כל תמונה.
מעבירים את התוויות לסוף (לתחתית) של הציר הראשי ומשנים אותן כך שיהיו ממורכזות::
// TODO: Align labels to the bottom and center (103)
mainAxisAlignment: MainAxisAlignment.end,
crossAxisAlignment: CrossAxisAlignment.center,
שומרים את הפרויקט.
Android | iOS |
|
|
זה נראה הרבה יותר טוב.
הוספת עיצוב לשדות הטקסט
אפשר גם להגדיר עיצוב לשדות טקסט באמצעות InputDecorationTheme.
ב-app.dart, ב-method _buildShrineTheme(), מציינים ערך inputDecorationTheme::
// TODO: Decorate the inputs (103)
inputDecorationTheme: const InputDecorationTheme(
border: OutlineInputBorder(),
),
כרגע, שדות הטקסט מעוטרים ב-filled. אני רוצה להסיר את זה. הסרת filled וציון inputDecorationTheme יגרמו לשדות הטקסט לקבל את סגנון המתאר.
במאפיין login.dart, מסירים את הערכים filled: true:
// Remove filled: true values (103)
TextField(
controller: _usernameController,
decoration: const InputDecoration(
// Removed filled: true
labelText: 'Username',
),
),
const SizedBox(height: 12.0),
TextField(
controller: _passwordController,
decoration: const InputDecoration(
// Removed filled: true
labelText: 'Password',
),
obscureText: true,
),
הפעלה מחדש מתוך הזיכרון (Hot restart). מסך הכניסה אמור להיראות כך כשהשדה 'שם משתמש' פעיל (כשמקלידים בו):
Android | iOS |
|
|
מקלידים בשדה טקסט – הגבולות והתוויות הצפות מוצגים בצבע הראשי. אבל קשה לראות את זה. היא לא נגישה לאנשים שמתקשים להבחין בין פיקסלים שאין ביניהם ניגודיות צבע מספיק גבוהה. (למידע נוסף, אפשר לעיין במאמר בנושא צבעים ונגישות בהנחיות של Material).
ב-app.dart, מציינים focusedBorder: בקטע inputDecorationTheme: :
// TODO: Decorate the inputs (103)
inputDecorationTheme: const InputDecorationTheme(
border: OutlineInputBorder(),
focusedBorder: OutlineInputBorder(
borderSide: BorderSide(
width: 2.0,
color: kShrineBrown900,
),
),
),
לאחר מכן, מציינים floatingLabelStyle: בקטע inputDecorationTheme: :
// TODO: Decorate the inputs (103)
inputDecorationTheme: const InputDecorationTheme(
border: OutlineInputBorder(),
focusedBorder: OutlineInputBorder(
borderSide: BorderSide(
width: 2.0,
color: kShrineBrown900,
),
),
floatingLabelStyle: TextStyle(
color: kShrineBrown900,
),
),
לבסוף, נגדיר שהכפתור 'ביטול' ישתמש בצבע המשני ולא בצבע הראשי כדי להגדיל את הניגודיות.
TextButton(
child: const Text('CANCEL'),
onPressed: () {
_usernameController.clear();
_passwordController.clear();
},
style: TextButton.styleFrom(
primary: Theme.of(context).colorScheme.secondary,
),
),
שומרים את הפרויקט.
Android | iOS |
|
|
6. שינוי הגובה
עכשיו, אחרי שעיצבתם את הדף עם צבעים וטיפוגרפיה ספציפיים שמתאימים ל-Shrine, נשנה את הגובה.
שינוי הגובה של הלחצן 'הבא'
גובה ברירת המחדל של ElevatedButton הוא 2. בואו נגדיל אותו.
ב-login.dart, מוסיפים ערך style: ללחצן NEXT ElevatedButton:
ElevatedButton(
child: const Text('NEXT'),
onPressed: () {
Navigator.pop(context);
},
style: ElevatedButton.styleFrom(
foregroundColor: kShrineBrown900,
backgroundColor: kShrinePink100,
elevation: 8.0,
),
),
שומרים את הפרויקט.
Android | iOS |
|
|
שינוי הגובה של הכרטיס
בשלב הזה, הכרטיסים מונחים על משטח לבן לצד הניווט באתר.
ב-home.dart, מוסיפים ערך elevation: לכרטיסים:
// TODO: Adjust card heights (103)
elevation: 0.0,
שומרים את הפרויקט.
Android | iOS |
|
|
הסרתם את הצל מתחת לכרטיסים.
7. הוספת צורה
מקדש עם סגנון גאומטרי מגניב, שבו האלמנטים מוגדרים בצורה מתומנת או מלבנית. בואו נטמיע את הסגנון של הצורה בכרטיסים במסך הבית, בשדות הטקסט ובכפתורים במסך ההתחברות.
שינוי הצורות של שדות הטקסט במסך ההתחברות
ב-app.dart, מייבאים את הקובץ הבא:
import 'supplemental/cut_corners_border.dart';
עדיין ב-app.dart, משנים את עיצוב שדה הטקסט כך שיוצג עם גבול פינות חתוכות:
// TODO: Decorate the inputs (103)
inputDecorationTheme: const InputDecorationTheme(
border: CutCornersBorder(),
focusedBorder: CutCornersBorder(
borderSide: BorderSide(
width: 2.0,
color: kShrineBrown900,
),
),
floatingLabelStyle: TextStyle(
color: kShrineBrown900,
),
),
שינוי צורות הלחצנים במסך הכניסה
ב-login.dart, מוסיפים גבול מלבני משופע ללחצן ביטול:
TextButton(
child: const Text('CANCEL'),
onPressed: () {
_usernameController.clear();
_passwordController.clear();
},
style: TextButton.styleFrom(
foregroundColor: kShrineBrown900,
shape: const BeveledRectangleBorder(
borderRadius: BorderRadius.all(Radius.circular(7.0)),
),
),
),
ל-TextButton אין צורה גלויה, אז למה להוסיף צורה של גבול? לכן, אנימציית האדוות קשורה לאותה צורה כשנוגעים בה.
עכשיו מוסיפים את אותה צורה ללחצן NEXT (הבא):
ElevatedButton(
child: const Text('NEXT'),
onPressed: () {
Navigator.pop(context);
},
style: ElevatedButton.styleFrom(
foregroundColor: kShrineBrown900,
backgroundColor: kShrinePink100,
elevation: 8.0,
shape: const BeveledRectangleBorder(
borderRadius: BorderRadius.all(Radius.circular(7.0)),
),
),
),
כדי לשנות את הצורה של כל הלחצנים, אפשר גם להשתמש ב-elevatedButtonTheme או ב-textButtonTheme ב-app.dart. זה נשאר כאתגר ללומד!
הפעלה מחדש מתוך הזיכרון (Hot restart).
Android | iOS |
|
|
8. איך משנים את הפריסה
עכשיו נשנה את הפריסה כדי להציג את הכרטיסים ביחסי גובה-רוחב ובגדלים שונים, כך שכל כרטיס ייראה שונה מהאחרים.
החלפת GridView ב-AsymmetricView
כבר כתבנו את הקבצים עבור פריסה אסימטרית.
ב-home.dart, מוסיפים את הייבוא הבא:
import 'supplemental/asymmetric_view.dart';
מחיקת _buildGridCards והחלפת body:
body: AsymmetricView(
products: ProductsRepository.loadProducts(Category.all),
),
שומרים את הפרויקט.
Android | iOS |
|
|
עכשיו המוצרים נגללים אופקית בדוגמה שדומה לאריגה.
9. אפשר לנסות נושא אחר (אופציונלי)
הצבע הוא דרך יעילה להביע את המותג שלכם, ושינוי קטן בצבע יכול להשפיע מאוד על חוויית המשתמש. כדי לבדוק את זה, נראה איך Shrine נראית אם ערכת הצבעים של המותג הייתה קצת שונה.
שינוי צבעים
ב-colors.dart, מוסיפים את הצבע הבא:
const kShrinePurple = Color(0xFF5D1049);
ב-app.dart, משנים את הפונקציה _buildShrineTheme() לפונקציה הבאה:
ThemeData _buildShrineTheme() {
final ThemeData base = ThemeData.light();
return base.copyWith(
colorScheme: base.colorScheme.copyWith(
primary: kShrinePurple,
secondary: kShrinePurple,
error: kShrineErrorRed,
),
scaffoldBackgroundColor: kShrineSurfaceWhite,
textSelectionTheme: const TextSelectionThemeData(
selectionColor: kShrinePurple,
),
appBarTheme: const AppBarTheme(
foregroundColor: kShrineBrown900,
backgroundColor: kShrinePink100,
),
inputDecorationTheme: const InputDecorationTheme(
border: CutCornersBorder(),
focusedBorder: CutCornersBorder(
borderSide: BorderSide(
width: 2.0,
color: kShrinePurple,
),
),
floatingLabelStyle: TextStyle(
color: kShrinePurple,
),
),
);
}
הפעלה מחדש מתוך הזיכרון (Hot restart). העיצוב החדש אמור להופיע.
Android | iOS |
|
|
Android | iOS |
|
|
התוצאה שונה מאוד! נחזיר את app.dart's _buildShrineTheme למצב שהיה לפני השלב הזה. אפשר גם להוריד את הקוד לתחילת הדרך של 104.
10. מעולה!
עד עכשיו יצרתם אפליקציה שדומה למפרט העיצוב של המעצב.
השלבים הבאים
השתמשתם עכשיו ב-Material Flutter הבאים: theme, typography, elevation ו-shape. אפשר לעיין ברכיבים ובמערכות משנה נוספים בספריית Material Flutter.
כדי להבין איך יצרנו את רשת הפריסה האסימטרית עם הגלילה האופקית, כדאי לעיין בקבצים שבספרייה supplemental.
מה קורה אם עיצוב האפליקציה המתוכנן מכיל אלמנטים שאין להם רכיבים בספרייה? במאמר MDC-104: Material Advanced Components אנחנו מסבירים איך ליצור רכיבים בהתאמה אישית באמצעות ספריית Material Flutter כדי להשיג את המראה הרצוי.


























