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

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

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

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

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

The default app that comes with every flutter installation

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

השימוש ב-Firebase הוא בחינם בגבול הסביר. כדי להשתמש צריך חשבון גוגל ואז לגלוש לאתר Firebase, וללחוץ על Go to console בפאנל העליון:

Go to console in the firebase UI

לחיצה על Add project תפתח wizard שמנחה כיצד להוסיף פרויקט חדש:

Firebase wizard helps you in setting your project

נתתי לפרויקט של Firebase את השם "carsdb":

the name of the new project in the firebase console

בסיום התהליך לוחצים על Continue כדי להתחיל לעבוד עם הפרויקט החדש:

Your firebase project is now ready. Press continue

לחיצה בפאנל השמאלי על All products תפתח את רשימת המוצרים שגוגל מציעים:

Press on the left pane to open the menu with all the services

מתוך המוצרים נבחר ב-Realtime Database:

Pick the realtime database

  • יש אפשרות לעבוד עם Firestore שגם הוא מסד נתונים טוב מאוד. בשביל הפרויקט הזה אני משתמש ב-Realtime שהוא קצת יותר פשוט.

לחיצה על Create Database תיצור את מסד הנתונים:

Press on the button to create the Realtime Database

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

במסך הבא נבחר לעבוד במצב test mode. אומנם המידע לא מאובטח אבל קל יותר לעבוד עם מסד הנתונים בשלבי הפיתוח. לפני שמעלים את האפליקציה לאוויר חובה לחזק את אבטחת מסד הנתונים.

Test mode - everyone can read and write to the database. Use with caution.

נלחץ על Enable כדי ליצור את מסד הנתונים.

אם הכל עובד כשורה נראה מסד נתונים ריק בממשק של Firebase:

The realtime database at work

עם מסד הנתונים נתקשר באמצעות REST API (מה זה REST API?). את הבקשות נשלח לשרת המרוחק בצירוף הפעולה שאנחנו מעוניינים שהשרת יעשה. GET כדי למשוך מידע מהשרת. POST כדי להוסיף מידע. PATCH כדי לעדכן. PUT כדי להחליף את המידע. DELETE ע"מ למחוק.

 

קישור אפליקציית Flutter למסד נתונים Firebase

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

$ flutter create [project_name]

הדרך הפשוטה ביותר שמצאתי להקמת אפליקציה שעובדת עם מסד נתונים Firebase היא לעקוב אחרי ההוראות במדריך הרשמי של גוגל. שם יש הוראות עבור הקמת אפליקציות אנדרואיד ואייפון: Flutter app with Firebase backend - Flutter

מדריך נוסף בו נעזרתי הוא: Flutter app with Firebase backend - Firebase

נתקין את החבילות של DART להם נזדקק כדי לעבוד עם מסד הנתונים:

  • firebase_core
  • firebase_database

נבדוק את התקשורת שלנו עם מסד הנתונים. לשם כך נשתמש באפליקציה של Flutter שבאה עם ההתקנה במסגרתה מעדכנים מונה counter על ידי לחיצה על כפתור הפעלה מרחף floating action button.

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

נייבא את התלויות:

/lib/main.dart

import 'dart:async';
import 'package:firebase_core/firebase_core.dart';
import 'package:flutter/material.dart';
import 'package:firebase_database/firebase_database.dart';

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

/lib/main.dart

void main() async {
 WidgetsFlutterBinding.ensureInitialized();
 await Firebase.initializeApp();
 runApp(const MyApp());
}

נגדיר את המצביע (רפרנס למסד הנתונים):

/lib/main.dart

class _MyHomePageState extends State {
 int _counter = 0;
 
 final DatabaseReference _db = FirebaseDatabase.instance.ref();
  // rest of code
}

נוסיף את שמו של מסד הנתונים:

/lib/main.dart

class _MyHomePageState extends State {
 int _counter = 0;
 
 final DatabaseReference _db = FirebaseDatabase.instance.ref();
  static const counterPath = 'Counter';
 
  // rest of code
}

לחיצה על כפתור הפעולה קוראת לפונקציה _incrementCounter ומעדכנת את המונה:

/lib/main.dart

void _incrementCounter() {
   setState(() {
     _counter++;
    });
 }

נוסיף לפונקציה קוד לעדכון המידע במסד הנתונים באמצעות המתודה set של Firebase:

נוסיף לפונקציה קוד לעדכון המידע במסד הנתונים באמצעות המתודה set של Firebase:

/lib/main.dart

void _incrementCounter() {
   setState(() {
     _counter++;
   });
   final Map data = {
       "counter": _counter,
       "date": DateTime.now().millisecondsSinceEpoch,
     };
     _db.child(counterPath).set(data);
 }

המתודה set מקבלת בתור פרמטר מפה Map (ששמה במקרה שלנו data) המכילה את שני שדות: counter בשביל ערך המונה ו-date עם התאריך עכשיו.

הביטוי:

_db.child(counterPath)

מגדיר את מסד הנתונים שאיתו צריך לעבוד.

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

נוסיף פונקציה לקריאה READ ממסד הנתונים:

/lib/main.dart

void _listenToCounter() {
   _db.child(counterPath).onValue.listen((event) {
     final data = Map.from(event.snapshot.value as dynamic);
     var rec = data.entries.toList();
     setState(() {
       _counter = rec[1].value;
     });
   });
 }

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

_db.child(counterPath).onValue.listen

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

עדכון התצוגה מתאפשר הודות ל-setState שמעדכן את המשתנה _counter ומורה לתצוגה להתעדכן במידע חדש.

אם הכל עובד כשורה נראה את המידע המעודכן במסד הנתונים:

The realtime database at work with the data that we have set

כשעובדים עם Flutter הנושא של NULL safety הוא עקרוני וחשוב כי אנחנו תמיד צריכים לחשוב היכן אנו עלולים לקבל null ולהתגונן מראש. מקום בו אנו עלולים לקבל null הוא כשאנו קוראים ממסד הנתונים. בהתאם, לפני שנקבל את המידע ממסד הנתונים נוודא שאינו null, ואם זה יהיה המצב אז נציב ערך ברירת מחדל שיחליף את ה-null:

void _listenToCounter() {
    _counterStream = _db.child(counterPath).onValue.listen((event) {
      if (event.snapshot.value == null) {
        _counter = 0;
      } else {
        final data = Map.from(event.snapshot.value as dynamic);
        var rec = data.entries.toList();
        _counter = rec[1].value;
      }
      setState(() {});
    });
}

נקרא לפונקציה המאזינה מתוך initState כדי שתתחיל לעבוד מיד בזמן בו הווידג'ט מתחיל את חייו:

/lib/main.dart

@override
void initState() {
   _listenToCounter();
   super.initState();
}

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

נגדיר StreamSubscription שיקבל את ערך המאזין:

late StreamSubscription _counterStream;

הערך של המאזין למסד הנתונים יהיה הערך של ה-StreamSubscription:

/lib/main.dart

void _listenToCounter() {
   _counterStream = _db.child(counterPath).onValue.listen((event) {
     final data = Map.from(event.snapshot.value as dynamic);
     var rec = data.entries.toList();
     setState(() {
       _counter = rec[1].value;
     });
   });
 }

נפסיק את פעולת ה-StreamSubscription בשלב ה-dispose של מחזור חיי הווידג'ט:

/lib/main.dart

@override
 void dispose() {
   _counterStream.cancel();
   super.dispose();
 }

הכול ביחד:

/lib/main.dart

import 'dart:async';
import 'package:firebase_core/firebase_core.dart';
import 'package:flutter/material.dart';
import 'package:firebase_database/firebase_database.dart';
 
void main() async {
 WidgetsFlutterBinding.ensureInitialized();
 await Firebase.initializeApp();
 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: 'Flutter Demo',
     theme: ThemeData(
       primarySwatch: Colors.blue,
     ),
     home: const MyHomePage(title: 'Flutter Demo Home Page'),
   );
 }
}
 
class MyHomePage extends StatefulWidget {
 const MyHomePage({super.key, required this.title});
 
 final String title;
 
 @override
 State createState() => _MyHomePageState();
}
 
class _MyHomePageState extends State {
 int _counter = 0;
 
 final DatabaseReference _db = FirebaseDatabase.instance.ref();
 
 static const counterPath = 'Counter';
 
 late StreamSubscription _counterStream;
 
 void _incrementCounter() {
   setState(() {
     _counter++;
     final Map data = {
       "counter": _counter,
       "date": DateTime.now().millisecondsSinceEpoch,
     };
     _db.child(counterPath).set(data);
   });
 }
 
 void _listenToCounter() {
   _counterStream = _db.child(counterPath).onValue.listen((event) {
     final data = Map.from(event.snapshot.value as dynamic);
     var rec = data.entries.toList();
     setState(() {
       _counter = rec[1].value;
     });
   });
 }
 
 @override
 void initState() {
   _listenToCounter();
   super.initState();
 }
 
 @override
 Widget build(BuildContext context) {
   return Scaffold(
     appBar: AppBar(
       title: Text("Test DB"),
     ),
     body: Center(
       child: Column(
         mainAxisAlignment: MainAxisAlignment.center,
         children: [
           const Text(
             'You have pushed the button this many times:',
           ),
           Text(
             '$_counter',
             style: Theme.of(context).textTheme.headline4,
           ),
         ],
       ),
     ),:
     floatingActionButton: FloatingActionButton(
       onPressed: _incrementCounter,
       tooltip: 'Increment',
       child: const Icon(Icons.add),
     ), // This trailing comma makes auto-formatting nicer for build methods.
   );
 }
 
 @override
 void dispose() {
   _counterStream.cancel();
   super.dispose();
 }
}

/android/app/build.gradle

בפעם הראשונה בה הרצתי את האפליקציה נתקלתי בהודעת שגיאה:

FAILURE: Build failed with an exception.

Rebuild flutter fire failed error message

נראה מפחיד. כל מה שההודעה אומרת הוא שהגרסה המינימלית של כלי הפיתוח SDK של Flutter היא נמוכה מכדי לעבוד עם Firebase.

מה עושים?

הפתרון פשוט. משנים את ערך ה-minSdkVersion בתוך קובץ ה-gradle של האפליקציה ל-21 או יותר. נפתח את הקובץ, ונאתר בתוכו את השורה:

minSdkVersion flutter.minSdkVersion

/android/app/build.gradle

The sdk version that causes the error

ונשנה לגרסה מינימלית 21 או למעלה מכך:

/android/app/build.gradle

The sdk version fix

  • ההמלצה היא על גרסה 19 אבל זה לא תמיד עובד. בנוסף, גרסה 21 ומעלה רצה על 98% מהמכשירים.

 

איך רואים שהאפליקציה עובדת כמו שצריך

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

Firebase realtime database state after update

  

אולי גם זה יעניין אותך

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

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

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

 

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

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

 

 

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

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

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

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

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

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

 

 

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

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