הקמת אפליקציית Flutter פשוטה על גבי מסד נתונים Firebase
במדריך הזה נבנה אפליקציה פשוטה אותה נבסס על ה-Hello World של עולם ה-Flutter במסגרתה לחיצה על כפתור פעולה מרחף מעדכנת את הערך המוצג בתוך מונה במרכז המסך. באפליקציה הפשוטה אותה נפתח במדריך לחיצה על עדכון המונה תעדכן גם את מסד הנתונים.
את Flutter נחבר למסד נתונים Firebase, כי הם שייכים לאותה אקוסיסטם של גוגל ולכן האינטגרציה ביניהם פשוטה וחלקה.
השימוש ב-Firebase הוא בחינם בגבול הסביר. כדי להשתמש צריך חשבון גוגל ואז לגלוש לאתר Firebase, וללחוץ על Go to console בפאנל העליון:
לחיצה על Add project תפתח wizard שמנחה כיצד להוסיף פרויקט חדש:
נתתי לפרויקט של Firebase את השם "carsdb":
בסיום התהליך לוחצים על Continue כדי להתחיל לעבוד עם הפרויקט החדש:
לחיצה בפאנל השמאלי על All products תפתח את רשימת המוצרים שגוגל מציעים:
מתוך המוצרים נבחר ב-Realtime Database:
- יש אפשרות לעבוד עם Firestore שגם הוא מסד נתונים טוב מאוד. בשביל הפרויקט הזה אני משתמש ב-Realtime שהוא קצת יותר פשוט.
לחיצה על Create Database תיצור את מסד הנתונים:
במסך הראשון נבחר את המיקום הגיאוגרפי של שרת מסד הנתונים (בחרתי את המיקום הקרוב ביותר שיכולתי למצוא במערב אירופה).
במסך הבא נבחר לעבוד במצב test mode. אומנם המידע לא מאובטח אבל קל יותר לעבוד עם מסד הנתונים בשלבי הפיתוח. לפני שמעלים את האפליקציה לאוויר חובה לחזק את אבטחת מסד הנתונים.
נלחץ על Enable כדי ליצור את מסד הנתונים.
אם הכל עובד כשורה נראה מסד נתונים ריק בממשק של Firebase:
עם מסד הנתונים נתקשר באמצעות 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 ומורה לתצוגה להתעדכן במידע חדש.
אם הכל עובד כשורה נראה את המידע המעודכן במסד הנתונים:
כשעובדים עם 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.
נראה מפחיד. כל מה שההודעה אומרת הוא שהגרסה המינימלית של כלי הפיתוח SDK של Flutter היא נמוכה מכדי לעבוד עם Firebase.
מה עושים?
הפתרון פשוט. משנים את ערך ה-minSdkVersion בתוך קובץ ה-gradle של האפליקציה ל-21 או יותר. נפתח את הקובץ, ונאתר בתוכו את השורה:
minSdkVersion flutter.minSdkVersion
/android/app/build.gradle
ונשנה לגרסה מינימלית 21 או למעלה מכך:
/android/app/build.gradle
- ההמלצה היא על גרסה 19 אבל זה לא תמיד עובד. בנוסף, גרסה 21 ומעלה רצה על 98% מהמכשירים.
איך רואים שהאפליקציה עובדת כמו שצריך
מטרת האפליקציה היא לעדכן את מסד הנתונים של Firebase ואחרי שנלחץ על תוספת למונה נוכל לראות את הרישום בקונסולה של מסד הנתונים:
אולי גם זה יעניין אותך
Dart לפיתוח אפליקציות Flutter - ממש על קצה המזלג
שימוש ב-provider באפליקציית Flutter
אהבתם? לא אהבתם? דרגו!
0 הצבעות, ממוצע 0 מתוך 5 כוכבים
המדריכים באתר עוסקים בנושאי תכנות ופיתוח אישי. הקוד שמוצג משמש להדגמה ולצרכי לימוד. התוכן והקוד המוצגים באתר נבדקו בקפידה ונמצאו תקינים. אבל ייתכן ששימוש במערכות שונות, דוגמת דפדפן או מערכת הפעלה שונה ולאור השינויים הטכנולוגיים התכופים בעולם שבו אנו חיים יגרום לתוצאות שונות מהמצופה. בכל מקרה, אין בעל האתר נושא באחריות לכל שיבוש או שימוש לא אחראי בתכנים הלימודיים באתר.
למרות האמור לעיל, ומתוך רצון טוב, אם נתקלת בקשיים ביישום הקוד באתר מפאת מה שנראה לך כשגיאה או כחוסר עקביות נא להשאיר תגובה עם פירוט הבעיה באזור התגובות בתחתית המדריכים. זה יכול לעזור למשתמשים אחרים שנתקלו באותה בעיה ואם אני רואה שהבעיה עקרונית אני עשוי לערוך התאמה במדריך או להסיר אותו כדי להימנע מהטעיית הציבור.
שימו לב! הסקריפטים במדריכים מיועדים למטרות לימוד בלבד. כשאתם עובדים על הפרויקטים שלכם אתם צריכים להשתמש בספריות וסביבות פיתוח מוכחות, מהירות ובטוחות.
המשתמש באתר צריך להיות מודע לכך שאם וכאשר הוא מפתח קוד בשביל פרויקט הוא חייב לשים לב ולהשתמש בסביבת הפיתוח המתאימה ביותר, הבטוחה ביותר, היעילה ביותר וכמובן שהוא צריך לבדוק את הקוד בהיבטים של יעילות ואבטחה. מי אמר שלהיות מפתח זו עבודה קלה ?
השימוש שלך באתר מהווה ראייה להסכמתך עם הכללים והתקנות שנוסחו בהסכם תנאי השימוש.