תחי ישראל - אין לנו ארץ אחרת

תחי ישראל -אין לנו ארץ אחרת

Dart לפיתוח אפליקציות Flutter - ממש על קצה המזלג

מחבר:
בתאריך:

שפת Dart מזכירה Kotlin וגם קצת JavaScript. זו השפה באמצעותה כותבים אפליקציות Flutter שניתן לפרוס בקלות על אנדרואיד, אייפון ויישומי דסקטופ. במדריך זה ריכזתי כמה דברים מאוד בסיסיים שחשוב לדעת כיצד לעשות ב-Dart לפני שמתחילים לפתח אפליקציות Flutter.

ניתן להתאמן על שימוש בשפה בלי להתקין שום דבר על המחשב. כל מה שצריך הוא לגלוש לאתר: https://dartpad.dev, ולהקליד את הפקודות במדריך.

בשפת Dart הפונקציה main() מאתחלת את ביצוע הקוד. לדוגמה:

main () {
   print("Hello World!");
}
  • חשוב לסיים כל שורת קוד בנקודה פסיק ;

התוצאה:

DartPad in action

Hello World!

נתחיל שורה של הערה בשני קווים נטויים.

// main() is responsible for 
// executing all the code
main () {
   print("Hello World!");
}
  • ללא הפונקציה main() הקוד לא יצא אל הפועל. בהתאם, את כל הקוד במדריך צריך לבצע בתוכה אלא אם צויין אחרת.

אנחנו יכולים להגדיר משתנים באמצעות מילת המפתח var:

var greeting = "Hello";
  • כש-Dart יכול להסיק בעצמו את הסוג נעדיף להגדיר את המשתנה באמצעות var אבל לא תמיד זה המצב.

Dart היא שפה שאוהבת לעבוד עם סוגים types לכן אם Dart לא יכול להסיק בעצמו את סוג המשתנה כי הערך יוצב בהמשך נגדיר את סוג המשתנה כדי לעזור לקומפיילר לאתר שגיאות בקוד כמה שיותר מוקדם.

ישנם כמה סוגים בסיסיים של משתנים שחשוב להכיר ב-Dart:

מחרוזות מגדירים כ- String

String greeting;

בוליאנים הם bool:

bool isEarthPerson;

אחר כך אנחנו צריכים להציב משתנים בהתאם לסוג:

greeting = "Hello earth person";
isEarthPerson = true;

אנחנו יכולים להציב ערך בזמן ההגדרה של המשתנה ואז אין צורך לציין את הסוג:

var greeting = "Hello earth person";
var isEarthPerson = true;

נשתמש בסוג משתנה int כדי לציין מספר שלם:

int n;
n = 3;

הסוג double מציין מספר עשרוני:

double d;
d = 3.14;

 

שימוש בתנאים (בוליאנים)

בוליאנים מאפשרים לתוכנה לעשות משהו אחד אם מתקיים התנאי ואחר אם אינו מתקיים. הסוג בוליאני יכול לקבל אחד משני ערכים: true או false.

var greeting = "Hello earth person";
var isEarthPerson = true;
var visitedMars = true;
   
// run code if both variables
//  are true
if (isEarthPerson && visitedMars) {
     print ("Kudos!!");
// run the code if only the first
//  variable is true
} else if (isEarthPerson && !visitedMars) {
     print (greeting);
// default code to run
} else {
     print ("Not earth person. Maybe a Martian.");
}

התוצאה:

Kudos!!
  • נציין את האופרטור and באמצעות &&.
  • את האופרטור or נציין באמצעות 2 צינורות ||.
  • כדי להפוך את הערך של בוליאני מ-true ל- false והפוך נקדים את ה-bang operator ! לשם המשתנה.

   

Ternary operator

אפשר לכתוב תנאים באופן קומפקטי יותר על ידי שימוש ב Ternary operator. לדוגמה, אפשר לכתוב תנאי שמברר האם משתנה הוא מספר זוגי כך:

var myInt= 8;
  
String evenness = '';
  
 if (myInt % 2 == 0) {
    evenness = "$myInt is even";
} else {
    evenness = "$myInt is odd";
}
  
print(evenness);
  • הבעיה היא שתחביר זה הוא ארוך מדי.

את התנאי אפשר לכווץ לשורה אחת:

String evenness = myInt % 2 == 0? "$myInt is even" : "$myInt is odd";
  • מה שבא משמאל לסימן השאלה הוא התנאי.
  • מימין לסימן השאלה נכתוב את חלופות הקוד לביצוע.
  • בין החלופות. מה שמופיע משמאל לנקודתיים הוא הקוד לביצוע במקרה של קיום התנאי. מימין לנקודתיים ישנו הקוד לביצוע במקרה בו התנאי אינו מתקיים.

   

שימוש בלולאות

נשתמש בלולאת for:

main () {
   int start = 1; 
   int end = 5;
   
   for(int i = start ; i <= end; i++ ) { 
      print(i);
   } 
}

התוצאה:

1
2
3
4
5

אפשר גם להשתמש בלולאת while:

var bigNumber = 1000;
  
while (bigNumber > 5) {
  print(bigNumber);
  bigNumber = bigNumber ~/ 10;
}

התוצאה:

1000
100
10
  • האופרטור ~/ הוא floor divide והוא מעגל מספר עשרוני למספר השלם הנמוך הקרוב ביותר.

 

רשימה list

רשימה יכולה להכיל אוסף של משתנים. לדוגמה, רשימה של מחרוזות ששמה cars:

List<String> cars = [];

נוסיף פריטים לרשימה באמצעות הפקודה add():

main () {
    List<String> cars = [];
  
    cars.add("Tesla");
    cars.add("Audi");
    cars.add("BMW");
   
    print(cars);
}

התוצאה:

[Tesla, Audi, BMW]

להסרת פריטים נשתמש בפקודה-remove():

main () {
    List<String> cars = [];
    
    cars.add("Tesla");
    cars.add("Audi");
    cars.add("BMW");
  
    cars.remove("Audi");
  
    print(cars);
}

התוצאה:

[Tesla, BMW]

נעבור בלולאה על כל אחד מהפריטים ברשימה:

main () {
   List cars = ["Tesla", "Audi", "BMW"];
  
   for (int i=0;i<cars.length;i++){
        print(cars[i]);
    }
}

התוצאה:

Tesla
Audi
BMW

 

פונקציות

כשאנחנו מגדירים פונקציה נגדיר את סוג המשתנים שהיא מחזירה. אם פונקציה לא מחזירה דבר היא תהיה void:

main () {
     void greet(){
       print("Hello world!");
     }
  
     greet();
}

התוצאה:

Hello world!

אם פונקציה מחזירה מחרוזת נגדיר כ-String:

main () {
   
   String greet(){
     return "Hello Mishu";
   }
  
   print(greet());
}

התוצאה:

Hello Mishu!

אם פונקציה מקבלת פרמטרים אז חשוב להגדיר את סוג המשתנים שהיא רשאית לקבל:

main () {
   
   String greet(String person){
     return "Hello $person";
   }
  
   print(greet("Mishu"));
}

התוצאה:

Hello Mishu!

אנחנו יכולים להפוך את הפרמטרים של הפונקציה לאופציונליים על ידי כך שנעטוף את הפרמטרים בסוגריים מרובעים ונוסיף ערך ברירת מחדל:

String greetOptional(String bless, [String person = '']){
     return "$bless $person";
}
  
print(greetOptional("Helloo", "Moshe"));

print(greetOptional("Helloo"));

התוצאה:

Helloo Moshe

Helloo 

ישנה דרך חלופית שבה פונקציה יכולה לקבל פרמטר אופציונלי.

כשלא מעבירים ערך לפרמטר הערך שהוא מקבל הוא null באופן אוטומטי. אנחנו יכולים להגדיר את סוג המשתנה כאופציונלי על ידי הוספת סימן שאלה לימין הסוג, והצבת ערך ברירת מחדל:

String greetOptional(String bless, [String? person]){
     if (person == null) {
       return "$bless mishoo";
     }
     return "$bless $person";
}
  • הנושא של טיפול בערכי null הוא חשוב ביותר כשעובדים עם Dart. נא לשים לב ולהתנהג בהתאם!

עד עכשיו העברנו לפונקציה פרמטרים מסוג positional בהם חיוני להקפיד על סדר ההזנה. כדי להעביר פרמטרים מסוג named parameters נשתמש בסוגריים מסולסלים:

String greetNamed({required String person, String bless ='hello'}){
     return "$bless $person";
}
  
print(greetNamed(person: 'moshe'));

print(greetNamed(bless: 'Hola', person: 'amigo'));

פרמטרים מסוג named parameters:

  • אינם דורשים הקפדה על סדר ההזנה (בניגוד ל-positional(.
  • כשמגדירים אותם צריך להגדיר ערך ברירת מחדל או להפוך להכרחיים באמצעות ה-key word required.

  

פונקציות חץ arrow functions

תחביר קומפקטי יותר לכתיבת פונקציות הוא באמצעות שימוש בפונקציות חץ שעושות פעולה ומחזירות ערך והכול בשורה אחת של קוד.

במקום:

String greetNamed({required String person}) {
  return "Hello, $person";
}

אנחנו יכולים לכתוב:

String greetNamed({required String person}) => "Hello, $person";

נריץ את הפונקציה:

print(greetNamed(person: 'moshe'));

התוצאה:

Hello, moshe
  • במקום 3 שורות אנו משתמשים בתחביר קומפקטי של שורה 1.
  • אחרי הסוגריים שמקבלים את הפרמטרים אנחנו כותבים fat arrow ואחריו מגיע גוף הפונקציה.
  • לא משתמשים ב-return

  

פונקציות לעבודה עם רשימות

נגדיר רשימה:

List<String> cars = [
  "Audi",
  "Tesla",
  "BMW",
  "Volvo",
  "Mercedes",
];

נשתמש בפונקציה contains() כדי לברר האם פריט קיים ברשימה. לדוגמה:

cars.contains("Tesla");

נשלב את זה ב-Ternary expression:

var car = "Tesla";
print(cars.contains(car) ? "We have a $car" : "No such car");

התוצאה:

We have a Tesla

  

נשתמש ב-join() כדי להפוך מערך למחרוזת. לדוגמה נהפוך את הרשימה cars למחוזת ונחבר בין הפריטים במחרוזת שתיווצר באמצעות פסיקים:

String strCars = cars.join(",");
print(strCars);

התוצאה:

Audi,Tesla,BMW,Volvo,Mercedes

הפונקציה split() עושה הפוך. לוקחת מחרוזת ומפצלת לרשימה (מערך). לדוגמה:

strCars.split(",")

התוצאה:

[Audi, Tesla, BMW, Volvo, Mercedes]
  • מערך

  

הפונקציה forEach() מקבלת מערך, ומבצעת פעולה על כל פריט.

בדוגמה הבאה נעבור עם לולאת forEach על כל פריט ברשימת המכוניות ונדפיס במידה ואורך המחרוזת הוא 5:

cars.forEach((item){
  if (item.length == 5) {
    print(item);
  }
});

התוצאה:

Tesla
Volvo

 

שימוש בפונקציה where יאפשר לנו לסנן רשימה כדי לקבל את הפריטים שמעניינים אותנו:

cars.where((item){
  return (item.length == 5);
});
  • כל איטרציה מחזירה בוליאני: true, false.
  • מאוד חשוב להשתמש ב-return אחרת כל איטרציה תחזיר null, ונקבל שגיאה.

הפונקציה where() מחזירה Iterable אותו נהפוך לרשימה באמצעות toList():

List<String> myCars = cars.where((item){
  return (item.length == 5);
}).toList();
  
print(myCars);

התוצאה:

[Tesla, Volvo]

אפשר לכתוב יותר בקצרה באמצעות פונקציית חץ:

List<String> myCars = cars.where((item) => item.length == 5).toList();

  

אם אנחנו מעוניינים בפריט הראשון ברשימה שעונה על תנאי נשתמש בפונקציה firstWhere():

String myCar = cars.firstWhere((item){
  return (item.length == 5);
});
  
print(myCar);

התוצאה:

Tesla

  

כדי לבצע פעולה על כל אחד מפריטי הרשימה ולהחזיר רשימה חדשה נשתמש בפונקציה map().

לדוגמה:

List<String> myCars = cars.map((item){
  return "$item has a sunroof";
}).toList();
  
print(myCars);

התוצאה היא רשימה חדשה עם פריטים שעברו מודיפיקציה:

[Audi has a sunroof, Tesla has a sunroof, BMW has a sunroof, Volvo has a sunroof, Mercedes has a sunroof]

כשעובדים עם הפונקציה map() חשוב לשים לב לשני דברים:

  • מאוד חשוב להשתמש ב-return אחרת כל איטרציה תחזיר null.
  • הפונקציה מחזירה Iterable אותו נהפוך לרשימה באמצעות toList().

  

אפשר לקרוא לפונקציה בכל איטרציה:

String withSunroof(String name) {
  return "$name has a sunroof";
}
  
List<String> myCars = cars.map((item){
  return withSunroof(item);
}).toList();

את אותה תוצאה נשיג בתחביר מקוצר באמצעות פונקציית חץ:

List<String> myCars = cars.map((item) => withSunroof(item)).toList();

  

עבודה עם מחלקות ותכנות מונחה עצמים

מחלקות, קלאסים, הם כלי קיבול הכורך יחדיו קוד לטיפול בנושא מסוים. לדוגמה, למכונית יכול להיות שם דגם וגג שמש אז נגדיר מחלקה בשם Car שתכיל את המשתנים name ו-sunRoof:

class Car {
  String name;
  bool hasSunroof;
  
  Car(
    this.name,
    this.hasSunroof
  );
}
  • את ערכי המשתנים נעביר לקלאס בזמן שניצור ממנו אובייקט דרך הקונסטרקטור ששמו כשם הקלאס.

אנחנו יכולים לייצר קלאס שהקונסטרקטור שלו מקבל פרמטרים שיש להם שם:

class Car {
  String name;
  bool hasSunroof;
  
  // Named parameters in the constructor
  // The parameters have to be required or need to have a default value
  Car({required this.name, this.hasSunroof = false});
}

נקים מהקלאס אובייקטים באופן הבא:

final car1 = Car(name: 'Tesla');
final car2 = Car(name: 'BMW', hasSunroof: true);

כדי להפוך תכונה לפרטית נוסיף קו תחתון לפני שמה, לדוגמה: bool _hasSunroof;:

class Car {
  String name;
  bool _hasSunroof;
  
  // ...
}

כדי לקבל יותר אפשרויות בהגדרת ובשליפת הנתונים נשתמש בפונקציות setter ו-getter.

class Car {
  String name;
  bool _hasSunroof;
  
  Car(
    this.name,
    this._hasSunroof
  );

  bool get hasSunroof {
     return _hasSunroof;
  }

  set hasSunroof(bool val) {
     _hasSunroof = val;
  }
}

את הקלאס נקפיד להגדיר מחוץ לפונקציה main אבל ביצוע הקוד יהיה בתוך main:

main () {
   var myCar1 = Car("Tesla", false);
   
   // get the property
   print(myCar1.hasSunroof);
  
   // set the property
   myCar1.hasSunroof = true;

   print(myCar1.hasSunroof);
}
  • את המשתנה myCar1 הגדרתי var במקום Car מפני ש- Dart יכול להבחין בעצמו שהמשתנה ניזון מפונקציה שמחזירה אובייקט מסוג מסוים. יכולתי גם לציין את סוג המשתנה Car זו לא שגיאה אבל פחות נכון מבחינה תכנותית.

התוצאה:

false
true

 

רשימות של אובייקטים

כשעובדים עם Flutter משתמשים הרבה ברשימות של אובייקטים.

נגדיר מחלקה Car שיש לה 2 תכונות - name ו-country:

class Car {
  String name;
  String country;
  
  Car({required this.name, required this.country});
}

ניצור רשימת מכוניות שיישמו את הקלאס Car:

List<Car> cars = [
    Car(name: 'Jaguar', country: 'UK'),
    Car(name: 'BMW', country: 'Germany'),
    Car(name: 'Mercedes', country: 'Germany'),
    Car(name: 'Sabra', country: 'Israel'),
  ];

נשתמש במתודה where() כדי להישאר עם מודלים תוצרת גרמניה.

Iterable<Car> germanCars = cars.where((item) => item.country == "Germany");
  • קיבלנו Iterable.

נהפוך את ה-Iterable לרשימה, ואז נעבור עליו באמצעות לולאת forEach() ונדפיס כל פריט ברשימה:

germanCars.toList().forEach((item) => print(item.name));

התוצאה:

BMW
Mercedes

 

אם כבר אנחנו עוסקים ברשימות אז ננצל את זה כדי ללמוד כיצד לסדר רשימות לפי הסדר באמצעות המתודה sort().

נעביר למתודה בתור פרמטר פונקציה משווה compareTo().

לדוגמה, נסדר את המכוניות לפי סדר א"ב של השמות:

// sort cars according to their name
cars.sort((a, b) => a.name.compareTo(b.name));

נעזר בלולאת forEach() כדי להדפיס את פרטי הרשימה המסודרת:

// print the sorted cars
cars.toList().forEach((item) => print(item.name));

התוצאה:

BMW
Jaguar
Mercedes
Sabra

כדי לקבל את הרשימה בסדר הפוך נשתמש במילת המפתח sorted:

// sort in reverse order
cars.reversed.toList().forEach((item) => print(item.name));

התוצאה:

Sabra
Mercedes
Jaguar
BMW

 

למדריכים נוספים בסדרת ה-Flutter

flutter - קלאסים ווידג'טים

שימוש ב-provider באפליקציית Flutter

הקמת אפליקציית Flutter פשוטה על גבי מסד נתונים Firebase

 

לכל המדריכים בסדרת ה-Flutter

 

אהבתם? לא אהבתם? דרגו!

0 הצבעות, ממוצע 0 מתוך 5 כוכבים

 

 

המדריכים באתר עוסקים בנושאי תכנות ופיתוח אישי. הקוד שמוצג משמש להדגמה ולצרכי לימוד. התוכן והקוד המוצגים באתר נבדקו בקפידה ונמצאו תקינים. אבל ייתכן ששימוש במערכות שונות, דוגמת דפדפן או מערכת הפעלה שונה ולאור השינויים הטכנולוגיים התכופים בעולם שבו אנו חיים יגרום לתוצאות שונות מהמצופה. בכל מקרה, אין בעל האתר נושא באחריות לכל שיבוש או שימוש לא אחראי בתכנים הלימודיים באתר.

למרות האמור לעיל, ומתוך רצון טוב, אם נתקלת בקשיים ביישום הקוד באתר מפאת מה שנראה לך כשגיאה או כחוסר עקביות נא להשאיר תגובה עם פירוט הבעיה באזור התגובות בתחתית המדריכים. זה יכול לעזור למשתמשים אחרים שנתקלו באותה בעיה ואם אני רואה שהבעיה עקרונית אני עשוי לערוך התאמה במדריך או להסיר אותו כדי להימנע מהטעיית הציבור.

שימו לב! הסקריפטים במדריכים מיועדים למטרות לימוד בלבד. כשאתם עובדים על הפרויקטים שלכם אתם צריכים להשתמש בספריות וסביבות פיתוח מוכחות, מהירות ובטוחות.

המשתמש באתר צריך להיות מודע לכך שאם וכאשר הוא מפתח קוד בשביל פרויקט הוא חייב לשים לב ולהשתמש בסביבת הפיתוח המתאימה ביותר, הבטוחה ביותר, היעילה ביותר וכמובן שהוא צריך לבדוק את הקוד בהיבטים של יעילות ואבטחה. מי אמר שלהיות מפתח זו עבודה קלה ?

השימוש שלך באתר מהווה ראייה להסכמתך עם הכללים והתקנות שנוסחו בהסכם תנאי השימוש.

הוסף תגובה חדשה

 

 

ענה על השאלה הפשוטה הבאה כתנאי להוספת תגובה:

איך אומרים בעברית אינטרנט?