flutter - קלאסים ווידג'טים
במדריך זה נלמד כיצד להשתמש בקלאסים וווידג'טים של flutter לפיתוח אפליקציות. ממשק האפליקציה כולל מספר כפתורים שלחיצה עליהם גורמת להצגת מידע בהתאם לכפתור עליו לוחץ המשתמש:
להורדת תיקייה מכווצת עם קבצי הפרויקט אותו נפתח במדריך
תחילת עבודה על פרויקט Flutter
ניצור פרויקט flutter חדש על ידי הקלדת בטרמינל של הפקודה ליצירת פרויקט חדש ואת שם הפרויקט. עבור הפרויקט בחרתי את השם "cars_basic_app":
$ flutter create cars_basic _app
נפתח את תיקיית הפרויקט באמצעות vscode, ונחליף את תוכן הקובץ main.dart בקוד בסיסי של אפליקציה:
lib/main.dart
import 'package:flutter/material.dart';
void main() {
runApp(const MyApp());
}
class MyApp extends StatelessWidget {
const MyApp({super.key});
// This widget is the root of your application.
@override
Widget build(BuildContext context) {
return MaterialApp(
title: 'Cars basic app',
);
}
}
- הפונקציה main רצה ראשונה באפליקציית flutter ומריצה את runApp שקוראת לקלאסים אשר מציגים מידע למשתמש הודות להיותם מצוידים במתודה build.
- הפונקציה build מקבלת כפרמטר BuildContext אשר מכיל את המידע אודות מיקומו של הווידג'ט בעץ הווידג'טים widget tree.
- בתוך המתודה build ישנו ווידג'ט MaterialApp שנותן לדף מראה של Material (ספרייה של גוגל שמקנה לאפליקציות אנדרואיד את המראה האופייני). בתוך הווידג'ט נשלב מיד את הווידג'טים הנוספים שיעשו את הממשק למשתמש.
הממשק למשתמש UI של האפליקציה מורכב מווידג'טים. שני סוגים עיקריים הם stateful לעומת stateless. לווידג'טים המיועדים להשתנות במהלך חיי האפליקציה נשתמש בסוג stateful.
ווידג'ט ראשון משלנו custom widget
ניצור קלאס מסוג StatefulWidget ששמו "MyCars" שתפקידו להציג את המידע על הפריט הנבחר ואת רשימת/טור הכפתורים. נתחיל להקליד stful כדי ש-vscode יציע לנו אפשרויות השלמה אוטומטית מתוכם נבחר באפשרות הראשונה Flutter Stateful Widget:
נלחץ על האפשרות הראשונה כדי לקבל שלד של StatefulWidget:
נמלא את שם הווידג'ט כדי שיהיה "MyCars":
class MyCars extends StatefulWidget {
const MyCars({super.key});
@override
State createState() => _MyCarsState();
}
class _MyCarsState extends State {
@override
Widget build(BuildContext context) {
return Container();
}
}
- תפקיד הווידג'טים להציג מידע למשתמש באמצעות המתודה build.
לווידג'ט נקצה קובץ משלו. זו לא חובה להקצות לכל ווידג'ט קובץ נפרד אבל בשביל קוד קריא כדאי לשקול לחלק את האפליקציה למספר קבצים. בפרט אם הווידג'ט מכיל שורות קוד רבות. יתרון חשוב של חלוקה לווידג'טים הוא ש-flutter יכול לעדכן רק חלק מהמסך מה שחוסך במשאבים ומשפר את ביצועי האפליקציה. את הווידג'טים נמקם בתוך תיקייה נפרדת widgets שאותה נוסיף לתוך התיקייה lib. בתוך התיקייה החדשה נשים את הווידג'ט MyCars בתוך קובץ שקראתי לו list_cars_widget.dart:
lib/widgets/list_cars_widget.dart
import 'package:flutter/material.dart';
class MyCars extends StatefulWidget {
const MyCars({super.key});
@override
State createState() => _MyCarsState();
}
class _MyCarsState extends State {
@override
Widget build(BuildContext context) {
return Container();
}
}
נייבא את הקלאס החדש לתוך הקובץ הראשי של האפליקציה main.dart:
lib/main.dart
import 'package:flutter/material.dart';
import './widgets/list_cars_widget.dart';
- מקובל למקם קודם את הספריות של flutter ורק אחר כך את הקלאסים שאנחנו כתבנו.
כדי שהאפליקציה תציג את הווידג'ט החדש נגדיר שדף הבית של האפליקציה יקח את המראה שלו מקלאס הווידג'ט על ידי מיפוי התכונה home של MaterialApp על הפונקציה MyCars:
lib/main.dart
class MyApp extends StatelessWidget {
const MyApp({super.key});
// This widget is the root of your application.
@override
Widget build(BuildContext context) {
return MaterialApp(
title: 'Mוultiple classes app',
home: const MyCars(),
);
}
}
מיד נוסיף מידע על מכוניות אותן תציג האפליקציה. בשביל להציג מידע על מכוניות נשתמש באב טיפוס, class עם שדות הדרושים לתיאור מכונית. הקלאס Car יגדיר את התכונות הדרושות מאובייקט מסוג זה באפליקציה.
הוספת קלאס מודל
ניצור תיקייה חדשה בשם models שתכיל את הקלאסים ונשים אותה בתוך התיקייה libs. לתוך התיקייה נוסיף קובץ car.dart בתוכו נכתוב את הקלאס:
libs/models/car.dart
class Car {
final int id;
final String name;
final double price;
Car({
required this.id,
required this.name,
required this.price,
});
}
- השדות הדרושים למכונית הם מזהה מספרי id, מחרוזת name ומספר עשרוני בשביל המחיר.
בקובץ הווידג'ט נכלול את הקישור לקובץ הקלאס בתור תלות נוספת:
lib/widgets/list_cars_widget.dart
import 'package:flutter/material.dart';
import '../models/car.dart';
נוסיף רשימה של מכוניות בה כל פריט יהיה שייך לסוג Car:
lib/widgets/list_cars_widget.dart
class _MyCarsState extends State {
List cars = [
Car(id: 0, name: "Tesla", price: 320000),
Car(id: 1, name: "BMW", price: 489999),
Car(id: 2, name: "Mercedes", price: 599999),
];
// …
}
בחירת המידע להצגה ומילת המפתח late
נבחר להציג את פרטי המכונית הראשונה כברירת מחדל. נגדיר איזו מכונית להציג באמצעות משתנה selectedCar אותו נגדיר late:
late Car selectedCar;
- המשתנה מוגדר late היות והוא יקבל ערך בזמן שהתוכנית רצה run time להבדיל מזמן הקומפילציה, תהליך הפיכת הקוד שכתב המתכנת לשפת מכונה, compilation time.
- משתנה מסוג late חייב לקבל ערך.
בשביל להציב ערך למשתנה נגדיר בתוך המתודה build ערך ברירת מחדל (0 כדי להציג את הפריט הראשון ברשימה):
lib/widgets/list_cars_widget.dart
class _MyCarsState extends State {
List cars = [
Car(id: 0, name: "Tesla", price: 320000),
Car(id: 1, name: "BMW", price: 489999),
Car(id: 2, name: "Mercedes", price: 599999),
];
late Car selectedCar;
var selectedCarIndex = 0;
@override
Widget build(BuildContext context) {
selectedCar = cars[selectedCarIndex];
return Container();
}
}
ווידג'טים לבניית ממשק המשתמש
Scaffold הוא קלאס של Material שיוצר את מראה הדף הבסיסי. נגדיר שהמתודה-build תחזיר ווידג'ט Scaffold:
lib/widgets/list_cars_widget.dart
class _MyCarsState extends State {
List cars = [
Car(id: 0, name: "Tesla", price: 320000),
Car(id: 1, name: "BMW", price: 489999),
Car(id: 2, name: "Mercedes", price: 599999),
];
late Car selectedCar;
var selectedCarIndex = 0;
@override
Widget build(BuildContext context) {
selectedCar = cars[selectedCarIndex];
return Scaffold();
}
}
נוסיף שני חלקים ל-Scaffold: appBar ו-body.
נוסיף AppBar בשביל הפאנל העליון:
return Scaffold(
appBar: AppBar(
backgroundColor: Color.fromARGB(255, 33, 150, 243),
title: Text("My Cars"),
centerTitle: true,
),
),
נוסיף ריפוד בתוך גוף הווידג'ט Scaffold כדי להרחיק מהשוליים:
return Scaffold(
body: Padding(
padding: const EdgeInsets.fromLTRB(30.0, 40.0, 30.0, 0.0),
),
);
בתוך הווידג'ט Scaffold שמבנה את מתווה הדף layout צריך להיות אלמנט המציג את המכונית הנבחרת, ומתחתיו אחד מתחת לשני כפתורים המאפשרים בחירה. האלמנט הבסיסי ביותר להצגת אלמנטים אחד מתחת לשני במבנה של עמודה הוא Column:
return Scaffold(
body: Padding(
padding: const EdgeInsets.fromLTRB(30.0, 40.0, 30.0, 0.0),
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [],
),
),
);
- הווידג'ט Column הוא אחד החשובים של flutter. נשתמש בו כדי למקם ווידג'טים אחד מעל לשני.
- במידה ונרצה למקם ווידג'טים אחד לצד השני, לרוחב המסך, נשתמש בווידג'ט Row.
- התכונה crossAxisAlignment מגדירה כיצד האלמנטים בתוך הווידג'ט אמורים להתיישר. הגדרתי ערך start לתכונה כדי שהאלמנטים יתיישרו לשמאל.
תיעוד כל קטלוג הווידג'טים של Flutter, ויש רבים, קיים ב-Widget catalog.
הווידג'ט Column כולל רשימת ווידג'טים ילדים children. בתור הילד הראשון, האלמנט הראשון ברשימה, נגדיר שיציג את שם המכונית הנבחרת בתוך הווידג'ט Text:
return Scaffold(
body: Padding(
padding: const EdgeInsets.fromLTRB(30.0, 40.0, 30.0, 0.0),
child: Column(
children: [
Text(
selectedCar.name,
textAlign: TextAlign.center,
style: TextStyle(fontWeight: FontWeight.bold, fontSize: 24, color: Colors.black),
),
],
),
),
);
- אחת התכונות של הווידג'ט Text היא TextAlign שאחראית על יישור השורה. במקרה זה, השורה מתיישרת למרכז.
- התכונה style מאפשרת לעצב את הטקסט. באמצעותה הוספנו עובי fontWeight והגדרנו את גודל הטקסט fontStyle ואת הצבע color.
כדי לראות את כל תכונות הווידג'ט נעמיד את הסמן על שם הווידג'ט כדי לראות רשימה:
ווידג'ט Text ואינטרפולציה
אנחנו יכולים להציג מידע עשיר הכולל כמה משתנים בשילוב טקסט בתוך הווידג'ט Text על ידי שימוש באינטרפולציה. לשם כך, נוסיף לפני שמות המשתנים $, את הביטויים נקיף בסוגריים מסולסלים. לדוגמה, נציג את שם המכונית ואת המחיר בתוך אותה שורת טקסט:
Text(
'${selectedCar.name} ${selectedCar.price} NIS',
textAlign: TextAlign.center,
),
נריץ את האפליקציה על ידי לחיצה על Run בתפריט הראשי של ה-IDE ומתוך התפריט הנפתח נבחר Run without debugging:
כדי להריץ את האימולטור שמציג את האפליקציה:
נוסיף ווידג'ט נוסף לרשימה - כפתור שיציג את שם המכונית הראשונה ברשימה:
lib/widgets/list_cars_widget.dart
return Scaffold(
body: Padding(
padding: const EdgeInsets.fromLTRB(30.0, 40.0, 30.0, 0.0),
child: Column(
children: [
Text(
'${selectedCar.name} ${selectedCar.price} NIS',
textAlign: TextAlign.center,
),
ElevatedButton(
onPressed: () => null,
child: Text(cars[0].name),
),
],
),
),
);
- הכפתור מסוג ElevatedButton מצוייד בתכונה onPressed שקוראת לפונקציה callback בתגובה להקלקה על הכפתור ותכונה child שהצבנו לתוכה Text עם שם המכונית.
כך זה נראה באימולטור:
עבודה עם map למיפוי רשימות על ווידג'טים
אפשר להוסיף כפתורים כמספר הפריטים ברשימה. רק שזו משימה מייגעת בפרט אם הרשימה ארוכה, וזו גם גישה לא גמישה שתקשה עלינו להכניס שינויים במידת הצורך בעתיד. דרך עדיפה היא להשתמש בפונקציה map שהיא מתודה אותה ניתן להפעיל על רשימות Dart שעוברת על כל אחד מפריטי הרשימה בלולאה ומחזירה את המידע הנדרש לצורך בניית הווידג'ט:
lib/widgets/list_cars_widget.dart
cars.map((item) {
return ElevatedButton(
onPressed: () => null,
child: Text(item.name),
);
}).toList()),
נפעיל את המתודה map על רשימת המכוניות כדי ליצור מספר כפתורים התואם את אורך הרשימה:
return Scaffold(
body: Padding(
padding: const EdgeInsets.fromLTRB(30.0, 40.0, 30.0, 0.0),
child: Column(
children: [
Text(
'${selectedCar.name} ${selectedCar.price} NIS',
textAlign: TextAlign.center,
),
Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: cars.map((item) {
return ElevatedButton(
onPressed: () => null,
child: Text(item.name),
);
}).toList(),
),
],
),
),
);
- הוספנו ווידג'ט Column בתוכו פריטי הרשימה cars ממופים לווידג'ט כפתור מסוג ElevatedButton באמצעות המתודה map.
כך נראה מסך האימולטור:
הקצאת קובץ סקריפט נפרד לווידג'ט
הכפתור הוא ווידג'ט פשוט ורזה בשלב זה. בהמשך נוסיף לו תכונות נוספות מה שיקשה על קריאת הקוד. לכן כדאי להקצות לווידג'ט קובץ נפרד.
נפתח קובץ חדש: lib/widgets/btn_widget.dart
בתוכו נוסיף קלאס stateless ווידג'ט ששמו BtnWidget. מתחילים להקליד stls ב-IDE ובוחרים באפשרות Flutter Stateless Widget מהתפריט שנפתח מה שיוצר שלד של Stateless widget:
lib/widgets/btn_widget.dart
import 'package:flutter/material.dart';
class BtnWidget extends StatelessWidget {
const BtnWidget();
@override
Widget build(BuildContext context) {
return Container();
}
}
- בחרתי בווידג'ט stateless (להבדיל מ-stateful) היות ומרגע היווסדו המידע בווידג'ט יישאר קבוע.
נוסיף לווידג'ט כפתור מסוג ElevatedButton:
lib/widgets/btn_widget.dart
import 'package:flutter/material.dart';
class BtnWidget extends StatelessWidget {
const BtnWidget();
@override
Widget build(BuildContext context) {
return ElevatedButton(
onPressed: () => null,
child: Text('Text button'),
);
}
}
כדי שהווידג'ט יוצג בממשק למשתמש נייבא אותו לקובץ list_cars_widget.dart:
lib/widgets/list_cars_widget.dart
import 'package:flutter/material.dart';
import '../models/car.dart';
import './btn_widget.dart';
בנוסף, נשלב את הווידג'ט שזה עתה הוספנו במקום הכפתור הקיים:
lib/widgets/list_cars_widget.dart
Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: cars.map((item) {
return BtnWidget();
}).toList(),
),
התוצאה באימולטור:
העברת מידע בין ווידג'ט הכפתור לווידג'ט ההורה אליו הוא משתייך
כדי שווידג'ט הכפתור יציג את שם המכונית אנחנו צריכים להעביר את פרטי המכונית לווידג'ט. לשם כך, נעביר את פרטי האובייקט מסוג Car לווידג'ט הכפתור, ואח"כ נקלוט את המידע בתוך ווידג'ט הכפתור.
נעביר את המידע לווידג'ט הכפתור על ידי מיפוי באמצעות המתודה map:
lib/widgets/btn_widget.dart
cars.map((item) {
return BtnWidget(item);
}).toList(),
בצד של ווידג'ט הכפתור נוסיף קוד שיקלוט את האובייקט מסוג Car על ידי כך שנערוך 4 שינויים בקוד הווידג'ט:
lib/widgets/btn_widget.dart
import 'package:flutter/material.dart';
// 1. Add Car class as dependency
import '../models/car.dart';
class BtnWidget extends StatelessWidget {
// 2. Add a final car variable.
final Car car;
// 3. Fill the car variable when constructing the widget.
BtnWidget(this.car);
@override
Widget build(BuildContext context) {
// 4. Make the car's name the text on the button
return ElevatedButton(
onPressed: () => null,
child: Text(car.name),
);
}
}
- נוסיף את הסקריפט car.dart בתור תלות.
- נגדיר את המשתנה car בתור final בגלל שהערך משתנה מכפתור לכפתור אבל עבור כל כפתור אינדיבידואלי הערך נשאר קבוע. בפעם הראשונה שהתוכנה רצה, הערך מוצב ואחר כך נשאר קבוע.
- בזמן שנוצר הווידג'ט flutter מעביר לו לתוך הקונסטרקטור את האובייקט המסוים מסוג Car אותו הוא צריך להציג.
- נשלב את שם האובייקט בתור הכיתוב על הכפתור.
כדי לשפר את מראה הכפתורים נוסיף להם רוחב ונרווח סביבם. נעטוף את -ElevatedButton בווידג'ט מסוג Padding. בשביל לעטוף בווידג'ט ב-IDE נקליק עם העכבר על שם הווידג'ט (ElevatedButton) נלחץ על אייקון המנורה שיופיע משמאל ונבחר מהתפריט Wrap with Padding. התוצאה:
lib/widgets/btn_widget.dart
@override
Widget build(BuildContext context) {
return Padding(
padding: const EdgeInsets.all(8.0),
child: ElevatedButton(
onPressed: () => null,
child: Text(car.name),
),
);
}
בשביל שהכפתורים יתפסו את כל הרוחב נעטוף את הווידג'ט כפתור בווידג'ט נוסף, Container שנגדיר לו רוחב:
lib/widgets/btn_widget.dart
@override
Widget build(BuildContext context) {
return Container(
width: double.infinity,
child: Padding(
padding: const EdgeInsets.all(8.0),
child: ElevatedButton(
onPressed: () => null,
child: Text(car.name),
),
),
);
}
- Container הוא אחד הווידג'טים העיקריים של flutter. הוא מאפשר מגוון רחב של אפשרויות עיצוב.
- הגדרנו רוחב double.infinity כדי למלא את מלוא רוחב המסך.
- הווידג'ט כפתור מקבל את מימדיו מהווידג'ט Container שעוטף אותו.
כדי להקנות צבע שונה לכפתור של הפריט הנבחר נעביר את המידע אודות הפריט הנבחר לווידג'ט.
lib/widgets/list_cars_widget.dart
return BtnWidget(item, selectedCarIndex);
בתוך ווידג'ט הכפתור נגדיר משתנה selectedIndex אליו נציב את המידע אודות אינדקס הרשימה הנבחר:
lib/widgets/btn_widget.dart
class BtnWidget extends StatelessWidget {
final Car car;
final int selectedIndex;
BtnWidget(this.car, this.selectedIndex);
//..
בתוך המתודה build נגדיר צבע לכפתור באמצעות:
lib/widgets/btn_widget.dart
@override
Widget build(BuildContext context) {
return Container(
width: double.infinity,
child: Padding(
padding: const EdgeInsets.all(8.0),
child: ElevatedButton(
onPressed: () => null,
child: Text(car.name),
style: ButtonStyle(
backgroundColor: MaterialStateProperty.all(Colors.blue),
),
),
),
);
}
- התכונה של ElevatedButton בה השתמשנו לעיצוב הכפתור היא style המקבלת ערך ButtonStyle עם backgroundColor.
תחביר חלופי לכתיבת תנאי באמצעות ternary operator וצביעת הכפתור הפעיל בצבע שונה
אנחנו רוצים שהצבע ברירת המחדל יהיה אפור ואם הכפתור שייך לפריט הנבחר אז שיהיה כחול. לשם כך, נשתמש בתנאי:
lib/widgets/btn_widget.dart
@override
Widget build(BuildContext context) {
return Container(
width: double.infinity,
child: Padding(
padding: const EdgeInsets.all(8.0),
child: ElevatedButton(
onPressed: () => null,
child: Text(car.name),
style: ButtonStyle(
backgroundColor: car.id == selectedIndex
? MaterialStateProperty.all(Colors.blue)
: MaterialStateProperty.all(Colors.grey),
),
),
),
);
}
- בשביל התנאי השתמשנו ב-ternary operator שהוא תחביר קומפקטי לכתיבת תנאים. במקרה זה, התנאי אומר שאם הכפתור נבחר אז צבע הרקע צריך להיות כחול. אחרת אפור.
באימולטור, הכפתור העליון ביותר שמייצג את האלמנט הראשון ברשימה המוגדר כנבחר כברירת מחדל יקבל עכשיו צבע שונה.
העברת בחירת המשתמש מתוך ווידג'ט הכפתור לווידג'ט אליו הוא משתייך באמצעות העברה של פונקציה בקונסטרקטור
כדי לשנות את המידע שמציגה האפליקציה על פי הכפתור עליו מקליק המשתמש, אנחנו צריכים להעביר את בחירת המשתמש מתוך הווידג'ט כפתור אל הווידג'ט רשימה שנמצא מעליו. המתודה setSelected שנכתוב בתוך הווידג'ט רשימה מאפשר לשנות את הערך:
lib/widgets/list_cars_widget.dart
var selectedCarIndex = 0;
void setSelected(int id) {
setState(() {
selectedCarIndex = id;
});
}
- המתודה setSelected מקבלת בתור פרמטר את ה-id ומציבה אותו בתור ערך של selectedCarIndex, האינדקס הנבחר של הרשימה.
- כדי לשנות את המצב בתוך הקלאס אנחנו משתמשים ב- setState, מתודה של StatefuleWidget שתפקידה לשנות את המידע בתוך הקלאס ואז להפעיל את build כדי שיבנה מחדש את התצוגה.
המשתמש לוחץ על הכפתור, והשאלה היא כיצד להעביר את המידע על בחירת המשתמש מתוך הווידג'ט כפתור אל הרשימה שבתוכה נמצאת המתודה setSelected. הפתרון שלנו יהיה להעביר את המתודה setSelected מההורה לכפתור על ידי כך שנוסיף את המתודה בתור פרמטר שלישי אותו מקבל BtnWidget:
lib/widgets/list_cars_widget.dart
return BtnWidget(item, selectedCarIndex, setSelected);
- אנחנו מעבירים פונקציה בתור פרמטר לפונקציה אחרת.
נגדיר בתוך הווידג'ט כפתור את הפונקציה שמתקבלת דרך הקונסטרקטור:
lib/widgets/btn_widget.dart
class BtnWidget extends StatelessWidget {
final Car car;
final int selectedIndex;
final Function setSelected;
BtnWidget(this.car, this.selectedIndex, this.setSelected);
// …
- סוג המשתנה הוא Function.
בתוך המתודה build נגדיר שלחיצה על הכפתור תעביר למתודה setSelected את ה-id:
lib/widgets/btn_widget.dart
ElevatedButton(
onPressed: () => setSelected(car.id),
child: Text(car.name),
style: ButtonStyle(
//…
),
),
עכשיו המשתמש יכול לשנות את המידע המוצג על ידי לחיצה על הכפתורים באפליקציה.
פורמט המספר ושימוש בחבילה intl של Dart
נשפר את מראה מחיר המכונית בתצוגה על ידי הוספת פסיקים למספר. לשם כך נייבא חבילה intl שמשמשת לפירמוט ותרגום. לדוגמה, הצגת תאריכים בפורמטים שונים (אירופאי לעומת אמריקאי). במקרה זה, נשתמש בחבילה כדי לפרמט את תצוגת המחיר.
החבילה שייכת ל-dart. ניתן למצוא את כל החבילות באתר https://pub.dev.
לצורך התקנת החבילה נפתח את הטרמינל של ה-IDE ונריץ את הפקודה להתקנה כפי שהיא מופיעה בדף החבילה intl :
$ dart pub add intl
דף החבילה כולל מידע חשוב אודות האפליקציה. כולל כיצד להתקין, מה מידת הפופולריות, תיעוד מקרי שימוש נפוצים וכיצד ניתן להשתמש בחבילה. החבילה intl היא פופולרית ביותר בגלל שהיא מאפשרת, בין השאר, לתרגם תכנים, ליישר את הטקסט (ימין לשמאל והפוך) ולפרמט תאריכים. אנחנו נשתמש בה כדי לפרמט מספרים.
אחרי ההתקנה החבילה תופיע כתלות בקובץ pubspec.yaml. בתוך הקובץ מרוכזות הגדרות האפליקציה. כולל, החבילות בהם האפליקציה תלויה לפעולתה.
נייבא את החבילה לתוך הסקריפט הרלוונטי:
lib/widgets/list_cars_widget.dart
import 'package:flutter/material.dart';
import 'package:intl/intl.dart';
import '../models/car.dart';
import './btn_widget.dart';
- קודם החבילות הכלליות, אח"כ החבילות שהותקנו, והכי למטה החבילות שלנו.
נגדיר פורמטר, ונעביר לו את מבנה המספר לו אנו מצפים:
var formatter = NumberFormat('###,###,###');
נשתמש בזה כדי לפרמט את המספר בתצוגה:
${formatter.format(selectedCar.price)
התוצאה:
כיצד לאפשר גלילה במקרה של רשימות ארוכות הגולשות מחוץ למסך
עדיין יש בעיה. אם נוסיף פריטים לרשימה ונשנה את האוריינטציה של המכשיר לאופקית נקבל בעיה שאי אפשר לראות את האלמנטים שבתחתית הרשימה כי אי אפשר לגלול אליהם:
- סימון שחור-צהוב אומר לנו שתוכן חורג מחוץ למסך ואי אפשר לגלול אליו.
כדי לפתור את הבעיה נשתמש בשילוב של שלושה אלמנטים מקוננים: Expanded בתוכו SizedBox ובתוכו ListView שיחליפו את הווידג'ט Column.
lib/widgets/list_cars_widget.dart
Expanded(
child: SizedBox(
height: 200.0,
child: ListView.builder(
itemCount: cars.length,
shrinkWrap: true,
itemBuilder: (BuildContext context, int index) {
return BtnWidget(
cars[index], selectedCarIndex, setSelected);
},
),
),
),
- Expanded הוא ווידג'ט שממלא את מלוא השטח של ההורה שעוטף אותו.
- SizedBox - באמצעותו ניתן לרשימה גובה. הבעיה ש-ListView לא יודע כמה גובה לקחת. לכן, צריך להגביל אותו על ידי ווידג'ט עוטף שיש לו תכונה של גובה.
- ListView הוא ווידג'ט חשוב של flutter שמאפשר להפוך רשימת פריטים לווידג'טים שניתן לרנדר למסך. שימוש ב-builder הופך את רשימת הילדים לניתנת לגלילה. בנוסף, flutter נמנע מרינדור הווידג'טים החורגים מחוץ לאלמנט העוטף מה שמקנה יתרון מבחינת צריכת משאבים של תצוגות ארוכות.
ווידג'ט Row למיקום אלמנטים אחד לצד השני
ראינו שניתן להשתמש בווידג'ט Column כדי למקם ווידג'טים אחד מעל לשני. באופן דומה, נשתמש בווידג'ט Row לצורך מיקום אלמנטים אחד לצד השני בשורה אחת. במקרה שלנו, נשתמש בזה כדי להפריד את החלק שמציג את פרטי המכונית הנבחרת לשני תאים: טקסט ותמונה.
lib/widgets/list_cars_widget.dart
Row(
mainAxisAlignment: MainAxisAlignment.spaceEvenly,
crossAxisAlignment: CrossAxisAlignment.center,
children: [
Expanded(
flex: 1,
child: Image(image: AssetImage('images/myCar.png')),
),
Expanded(
flex: 2,
child: Text(
'${selectedCar.name} ${formatter.format(selectedCar.price)} NIS',
textAlign: TextAlign.center,
),
),
],
),
- בתוך ה-Row נשתמש בווידג'טים Expanded שניתן להם תכונה של flex כדי שהשורה תתפוס את כל הרוחב.
- רוחב השורה מתחלק על פי היחס בין ערכי ה-flex של האלמנטים מסוג Expanded. במקרה זה, התא הראשון תופס שליש שורה והשני שני שלישים בגלל שהיחס הוא 1:2.
שילוב תמונות בתור נכסים
בשביל לשלב תמונות אנחנו יכולים להשתמש בתמונות שנמצאות בתיקיות בתוך האפליקציה. לשם כך, הוספתי תיקייה images בתוך שורש האפליקצייה, מחוץ לתיקייה lib, בתוכה שמתי את התמונה.
כדי להתיר לאפליקציה להשתמש בתמונות בתיקייה נגדיר אותה בקובץ ההגדרות pubspec.yaml בתור סוג של asset:
את התמונה נציג באמצעות ווידג'ט מסוג Image:
lib/widgets/list_cars_widget.dart
Image(image: AssetImage('images/myCar.png'))
אותו נשלב בתוך תא ה-Expanded הראשון של ה-Row:
lib/widgets/list_cars_widget.dart
Row(
mainAxisAlignment: MainAxisAlignment.spaceEvenly,
crossAxisAlignment: CrossAxisAlignment.center,
children: [
Expanded(
flex: 1,
child: Image(image: AssetImage('images/myCar.png')),
),
Expanded(
flex: 2,
child: Text(
'${selectedCar.name} ${formatter.format(selectedCar.price)} NIS',
textAlign: TextAlign.center,
),
),
],
),
להורדת תיקייה מכווצת עם קבצי הפרויקט אותם פיתחנו במדריך
מדריכים נוספים שעשויים לעניין אותך
אהבתם? לא אהבתם? דרגו!
0 הצבעות, ממוצע 0 מתוך 5 כוכבים
המדריכים באתר עוסקים בנושאי תכנות ופיתוח אישי. הקוד שמוצג משמש להדגמה ולצרכי לימוד. התוכן והקוד המוצגים באתר נבדקו בקפידה ונמצאו תקינים. אבל ייתכן ששימוש במערכות שונות, דוגמת דפדפן או מערכת הפעלה שונה ולאור השינויים הטכנולוגיים התכופים בעולם שבו אנו חיים יגרום לתוצאות שונות מהמצופה. בכל מקרה, אין בעל האתר נושא באחריות לכל שיבוש או שימוש לא אחראי בתכנים הלימודיים באתר.
למרות האמור לעיל, ומתוך רצון טוב, אם נתקלת בקשיים ביישום הקוד באתר מפאת מה שנראה לך כשגיאה או כחוסר עקביות נא להשאיר תגובה עם פירוט הבעיה באזור התגובות בתחתית המדריכים. זה יכול לעזור למשתמשים אחרים שנתקלו באותה בעיה ואם אני רואה שהבעיה עקרונית אני עשוי לערוך התאמה במדריך או להסיר אותו כדי להימנע מהטעיית הציבור.
שימו לב! הסקריפטים במדריכים מיועדים למטרות לימוד בלבד. כשאתם עובדים על הפרויקטים שלכם אתם צריכים להשתמש בספריות וסביבות פיתוח מוכחות, מהירות ובטוחות.
המשתמש באתר צריך להיות מודע לכך שאם וכאשר הוא מפתח קוד בשביל פרויקט הוא חייב לשים לב ולהשתמש בסביבת הפיתוח המתאימה ביותר, הבטוחה ביותר, היעילה ביותר וכמובן שהוא צריך לבדוק את הקוד בהיבטים של יעילות ואבטחה. מי אמר שלהיות מפתח זו עבודה קלה ?
השימוש שלך באתר מהווה ראייה להסכמתך עם הכללים והתקנות שנוסחו בהסכם תנאי השימוש.