1. לפני שמתחילים
במעבדת הקוד הזה, אתה לומד כמה מהיסודות של Firebase ליצירת אפליקציות Flutter לנייד עבור אנדרואיד ו-iOS.
דרישות מוקדמות
- היכרות עם Flutter
- ה-SDK של Flutter
- עורך טקסט לבחירתך
מה תלמד
- כיצד לבנות אפליקציית RSVP ו-Chat ספר אורחים באנדרואיד, iOS, האינטרנט ו-macOS עם Flutter.
- כיצד לאמת משתמשים עם אימות Firebase ולסנכרן נתונים עם Firestore.
מה אתה צריך
כל אחד מהמכשירים הבאים:
- מכשיר אנדרואיד או iOS פיזי המחובר למחשב שלך ומוגדר למצב מפתח.
- סימולטור iOS (דורש כלי Xcode ).
- אמולטור אנדרואיד (דורש הגדרה ב- Android Studio ).
אתה צריך גם את הדברים הבאים:
- דפדפן לבחירתך, כגון Google Chrome.
- IDE או עורך טקסט לבחירתך המוגדר עם התוספים Dart ו-Flutter, כגון Android Studio או Visual Studio Code .
- הגרסה
stable
האחרונה של Flutter אוbeta
אם אתה נהנה לחיות על הקצה. - חשבון Google ליצירה וניהול של פרויקט Firebase שלך.
-
Firebase
CLI נכנס לחשבון Google שלך.
2. קבל את הקוד לדוגמה
הורד את הגרסה הראשונית של הפרויקט שלך מ-GitHub:
- משורת הפקודה, שכבו את מאגר GitHub בספריית
flutter-codelabs
:
git clone https://github.com/flutter/codelabs.git flutter-codelabs
ספריית flutter-codelabs
מכילה את הקוד לאוסף של קודlabs. הקוד עבור מעבדת קוד זה נמצא בספריית flutter-codelabs/firebase-get-to-know-flutter
. הספרייה מכילה סדרה של צילומי מצב המראים כיצד הפרויקט שלך אמור להיראות בסוף כל שלב. לדוגמה, אתה בשלב השני.
- מצא את הקבצים התואמים לשלב השני:
cd flutter-codelabs/firebase-get-to-know-flutter/step_02
אם אתה רוצה לדלג קדימה או לראות איך משהו צריך להיראות לאחר שלב, חפש בספרייה הקרויה על שם השלב שבו אתה מעוניין.
ייבא את אפליקציית המתנע
- פתח או ייבא את
flutter-codelabs/firebase-get-to-know-flutter/step_02
ב-IDE המועדף עליך. ספרייה זו מכילה את קוד ההתחלה של מעבדת הקוד, המורכבת מאפליקציית Flutter meetup שעדיין לא פעילה.
אתר את הקבצים שדורשים עבודה
הקוד באפליקציה זו מתפזר על פני מספר ספריות. פיצול זה של פונקציונליות מקל על העבודה מכיוון שהוא מקבץ את הקוד לפי פונקציונליות.
- אתר את הקבצים הבאים:
-
lib/main.dart
: קובץ זה מכיל את נקודת הכניסה הראשית ואת ווידג'ט האפליקציה. -
lib/home_page.dart
: קובץ זה מכיל את הווידג'ט של דף הבית. -
lib/src/widgets.dart
: קובץ זה מכיל קומץ ווידג'טים שיעזרו בסטנדרטיזציה של סגנון האפליקציה. הם יוצרים את המסך של אפליקציית המתנע. -
lib/src/authentication.dart
: קובץ זה מכיל יישום חלקי של אימות עם קבוצה של ווידג'טים ליצירת חוויית כניסה למשתמש עבור אימות מבוסס דוא"ל של Firebase. הווידג'טים האלה עבור זרימת ההרשאה עדיין לא נמצאים בשימוש באפליקציית המתנע, אבל אתה מוסיף אותם בקרוב.
-
אתה מוסיף קבצים נוספים לפי הצורך כדי לבנות את שאר האפליקציה.
עיין בקובץ lib/main.dart
אפליקציה זו מנצלת את חבילת google_fonts
כדי להפוך את Roboto לגופן ברירת המחדל בכל האפליקציה. אתה יכול לחקור את fonts.google.com ולהשתמש בגופנים שאתה מגלה שם בחלקים שונים של האפליקציה.
אתה משתמש בווידג'טים המסייעים מהקובץ lib/src/widgets.dart
בצורה של Header
, Paragraph
ו- IconAndDetail
. ווידג'טים אלה מבטלים קוד משוכפל כדי להפחית את העומס בפריסת העמוד המתוארת בדף HomePage
. זה גם מאפשר מראה ותחושה עקביים.
כך נראית האפליקציה שלך ב-Android, iOS, האינטרנט ו-macOS:
3. צור והגדר פרויקט Firebase
הצגת מידע על אירועים נהדרת עבור האורחים שלך, אבל היא לא מאוד שימושית עבור אף אחד בפני עצמו. אתה צריך להוסיף קצת פונקציונליות דינמית לאפליקציה. כדי לעשות זאת, עליך לחבר את Firebase לאפליקציה שלך. כדי להתחיל עם Firebase, עליך ליצור ולהגדיר פרויקט Firebase.
צור פרויקט Firebase
- היכנס ל- Firebase .
- במסוף, לחץ על הוסף פרויקט או צור פרויקט .
- בשדה שם הפרויקט , הזן Firebase-Flutter-Codelab ולאחר מכן לחץ על המשך .
- לחץ על אפשרויות יצירת הפרויקט. אם תתבקש, קבל את תנאי Firebase, אך דלג על ההגדרה של Google Analytics מכיוון שלא תשתמש בה עבור האפליקציה הזו.
למידע נוסף על פרויקטים של Firebase, ראה הבנת פרויקטי Firebase .
האפליקציה משתמשת במוצרי Firebase הבאים, הזמינים עבור אפליקציות אינטרנט:
- אימות: מאפשר למשתמשים להיכנס לאפליקציה שלך.
- Firestore: שומרת נתונים מובנים בענן ומקבלת התראות מיידיות כאשר הנתונים משתנים.
- כללי אבטחה של Firebase: מאבטח את מסד הנתונים שלך.
חלק מהמוצרים הללו זקוקים לתצורה מיוחדת או שעליך להפעיל אותם במסוף Firebase.
אפשר אימות כניסה בדוא"ל
- בחלונית סקירת הפרויקטים של מסוף Firebase, הרחב את תפריט Build .
- לחץ על אימות > התחל > שיטת כניסה > דואר אלקטרוני/סיסמה > הפעל > שמור .
הפעל את Firestore
אפליקציית האינטרנט משתמשת ב-Firestore כדי לשמור הודעות צ'אט ולקבל הודעות צ'אט חדשות.
הפעל את Firestore:
- בתפריט בנייה , לחץ על Firestore Database > צור מסד נתונים .
- בחר התחל במצב בדיקה ולאחר מכן קרא את כתב הוויתור על כללי האבטחה. מצב בדיקה מבטיח שאתה יכול לכתוב בחופשיות למסד הנתונים במהלך הפיתוח.
- לחץ על הבא ולאחר מכן בחר את המיקום עבור מסד הנתונים שלך. אתה יכול להשתמש בברירת המחדל. לא תוכל לשנות את המיקום מאוחר יותר.
- לחץ על הפעל .
4. הגדר את Firebase
כדי להשתמש ב-Firebase עם Flutter, עליך להשלים את המשימות הבאות כדי להגדיר את פרויקט Flutter לשימוש נכון בספריות FlutterFire
:
- הוסף את התלות
FlutterFire
לפרויקט שלך. - רשום את הפלטפורמה הרצויה בפרויקט Firebase.
- הורד את קובץ התצורה הספציפי לפלטפורמה ולאחר מכן הוסף אותו לקוד.
בספרייה ברמה העליונה של אפליקציית Flutter שלך, קיימות ספריות משנה android
, ios
, macos
ו- web
, המכילות את קובצי התצורה הספציפיים לפלטפורמה עבור iOS ו- Android, בהתאמה.
הגדר תלות
עליך להוסיף את ספריות FlutterFire
עבור שני מוצרי Firebase שבהם אתה משתמש באפליקציה זו: אימות ו-Firestore.
- משורת הפקודה, הוסף את התלות הבאות:
$ flutter pub add firebase_core
חבילת firebase_core
היא הקוד הנפוץ הנדרש עבור כל התוספים של Firebase Flutter.
$ flutter pub add firebase_auth
חבילת firebase_auth
מאפשרת אינטגרציה עם Authentication.
$ flutter pub add cloud_firestore
חבילת cloud_firestore
מאפשרת גישה לאחסון נתונים של Firestore.
$ flutter pub add provider
חבילת firebase_ui_auth
מספקת קבוצה של ווידג'טים וכלי עזר להגברת מהירות המפתחים עם זרימות אימות.
$ flutter pub add firebase_ui_auth
הוספת את החבילות הנדרשות, אך עליך גם להגדיר את פרויקטי iOS, Android, macOS ו-Web runner כך שישתמשו כראוי ב-Firebase. אתה גם משתמש בחבילת provider
המאפשרת הפרדה של ההיגיון העסקי מהלוגיקת התצוגה.
התקן את FlutterFire CLI
ה-CLI של FlutterFire תלוי ב-CLI הבסיסי של Firebase.
- אם עדיין לא עשית זאת, התקן את Firebase CLI במחשב שלך.
- התקן את FlutterFire CLI:
$ dart pub global activate flutterfire_cli
לאחר ההתקנה, פקודת flutterfire
זמינה ברחבי העולם.
הגדר את האפליקציות שלך
ה-CLI שואב מידע מפרויקט Firebase שלך ומאפליקציות פרויקט נבחרות כדי ליצור את כל התצורה עבור פלטפורמה ספציפית.
בשורש האפליקציה שלך, הפעל את פקודת configure
:
$ flutterfire configure
פקודת התצורה מנחה אותך בתהליכים הבאים:
- בחר פרויקט Firebase המבוסס על קובץ
.firebaserc
או ממסוף Firebase. - קבע פלטפורמות לתצורה, כגון אנדרואיד, iOS, macOS ואינטרנט.
- זהה את אפליקציות Firebase שמהן ניתן לחלץ את התצורה. כברירת מחדל, ה-CLI מנסה להתאים אוטומטית לאפליקציות Firebase בהתבסס על תצורת הפרויקט הנוכחית שלך.
- צור קובץ
firebase_options.dart
בפרויקט שלך.
הגדר את macOS
Flutter ב-macOS בונה אפליקציות בארגז חול מלא. מכיוון שהאפליקציה הזו משתלבת עם הרשת כדי לתקשר עם שרתי Firebase, עליך להגדיר את האפליקציה שלך עם הרשאות לקוח רשת.
macos/Runner/DebugProfile.entitlements
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict>
<key>com.apple.security.app-sandbox</key>
<true/>
<key>com.apple.security.cs.allow-jit</key>
<true/>
<key>com.apple.security.network.server</key>
<true/>
<!-- Add the following two lines -->
<key>com.apple.security.network.client</key>
<true/>
</dict>
</plist>
macos/Runner/Release.entitlements
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict>
<key>com.apple.security.app-sandbox</key>
<true/>
<!-- Add the following two lines -->
<key>com.apple.security.network.client</key>
<true/>
</dict>
</plist>
למידע נוסף, ראה תמיכה בשולחן העבודה עבור Flutter .
5. הוסף פונקציונליות RSVP
כעת, לאחר שהוספת את Firebase לאפליקציה, תוכל ליצור כפתור RSVP שרושם אנשים עם אימות . עבור מקורי אנדרואיד, מקורי iOS ואינטרנט, ישנן חבילות FirebaseUI Auth
מובנות מראש, אך עליך לבנות את היכולת הזו עבור Flutter.
הפרויקט שאחזרת קודם לכן כלל קבוצה של ווידג'טים המיישמת את ממשק המשתמש עבור רוב זרימת האימות. אתה מיישם את ההיגיון העסקי כדי לשלב את האימות עם האפליקציה.
הוסף הגיון עסקי עם חבילת Provider
השתמש בחבילת provider
כדי להפוך אובייקט מצב אפליקציה מרכזי לזמין בכל עץ הווידג'טים של Flutter של האפליקציה:
- צור קובץ חדש בשם
app_state.dart
עם התוכן הבא:
lib/app_state.dart
import 'package:firebase_auth/firebase_auth.dart'
hide EmailAuthProvider, PhoneAuthProvider;
import 'package:firebase_core/firebase_core.dart';
import 'package:firebase_ui_auth/firebase_ui_auth.dart';
import 'package:flutter/material.dart';
import 'firebase_options.dart';
class ApplicationState extends ChangeNotifier {
ApplicationState() {
init();
}
bool _loggedIn = false;
bool get loggedIn => _loggedIn;
Future<void> init() async {
await Firebase.initializeApp(
options: DefaultFirebaseOptions.currentPlatform);
FirebaseUIAuth.configureProviders([
EmailAuthProvider(),
]);
FirebaseAuth.instance.userChanges().listen((user) {
if (user != null) {
_loggedIn = true;
} else {
_loggedIn = false;
}
notifyListeners();
});
}
}
הצהרות import
מציגות את Firebase Core ו-Auth, מושכות את חבילת provider
שהופכת את אובייקט מצב האפליקציה לזמין בכל עץ הווידג'טים, וכוללות את ווידג'טים לאימות מחבילת firebase_ui_auth
.
לאובייקט מצב יישום ApplicationState
זה יש אחריות עיקרית אחת לשלב זה, שהוא להתריע בפני עץ הווידג'טים שהיה עדכון למצב מאומת.
אתה משתמש רק בספק כדי להעביר לאפליקציה את מצב התחברות של משתמש. כדי לאפשר למשתמש להיכנס, אתה משתמש בממשקי המשתמש המסופקים על ידי חבילת firebase_ui_auth
, שהיא דרך מצוינת לאתחל במהירות מסכי התחברות באפליקציות שלך.
שלב את זרימת האימות
- שנה את הייבוא בחלק העליון של הקובץ
lib/main.dart
:
lib/main.dart
import 'package:firebase_ui_auth/firebase_ui_auth.dart'; // new
import 'package:flutter/material.dart';
import 'package:go_router/go_router.dart'; // new
import 'package:google_fonts/google_fonts.dart';
import 'package:provider/provider.dart'; // new
import 'app_state.dart'; // new
import 'home_page.dart';
- חבר את מצב האפליקציה לאתחול האפליקציה ולאחר מכן הוסף את זרימת האימות לדף
HomePage
:
lib/main.dart
void main() {
// Modify from here...
WidgetsFlutterBinding.ensureInitialized();
runApp(ChangeNotifierProvider(
create: (context) => ApplicationState(),
builder: ((context, child) => const App()),
));
// ...to here.
}
השינוי בפונקציה main()
הופך את חבילת הספקים אחראית ליצירת אובייקט מצב האפליקציה עם הווידג'ט ChangeNotifierProvider
. אתה משתמש במחלקת provider
הספציפית הזו מכיוון שאובייקט מצב האפליקציה מרחיב את המחלקה ChangeNotifier
, המאפשרת לחבילת provider
לדעת מתי להציג מחדש ווידג'טים תלויים.
- עדכן את האפליקציה שלך כדי לטפל בניווט למסכים שונים ש-FirebaseUI מספק לך, על ידי יצירת תצורת
GoRouter
:
lib/main.dart
// Add GoRouter configuration outside the App class
final _router = GoRouter(
routes: [
GoRoute(
path: '/',
builder: (context, state) => const HomePage(),
routes: [
GoRoute(
path: 'sign-in',
builder: (context, state) {
return SignInScreen(
actions: [
ForgotPasswordAction(((context, email) {
final uri = Uri(
path: '/sign-in/forgot-password',
queryParameters: <String, String?>{
'email': email,
},
);
context.push(uri.toString());
})),
AuthStateChangeAction(((context, state) {
final user = switch (state) {
SignedIn state => state.user,
UserCreated state => state.credential.user,
_ => null
};
if (user == null) {
return;
}
if (state is UserCreated) {
user.updateDisplayName(user.email!.split('@')[0]);
}
if (!user.emailVerified) {
user.sendEmailVerification();
const snackBar = SnackBar(
content: Text(
'Please check your email to verify your email address'));
ScaffoldMessenger.of(context).showSnackBar(snackBar);
}
context.pushReplacement('/');
})),
],
);
},
routes: [
GoRoute(
path: 'forgot-password',
builder: (context, state) {
final arguments = state.uri.queryParameters;
return ForgotPasswordScreen(
email: arguments['email'],
headerMaxExtent: 200,
);
},
),
],
),
GoRoute(
path: 'profile',
builder: (context, state) {
return ProfileScreen(
providers: const [],
actions: [
SignedOutAction((context) {
context.pushReplacement('/');
}),
],
);
},
),
],
),
],
);
// end of GoRouter configuration
// Change MaterialApp to MaterialApp.router and add the routerConfig
class App extends StatelessWidget {
const App({super.key});
@override
Widget build(BuildContext context) {
return MaterialApp.router(
title: 'Firebase Meetup',
theme: ThemeData(
buttonTheme: Theme.of(context).buttonTheme.copyWith(
highlightColor: Colors.deepPurple,
),
primarySwatch: Colors.deepPurple,
textTheme: GoogleFonts.robotoTextTheme(
Theme.of(context).textTheme,
),
visualDensity: VisualDensity.adaptivePlatformDensity,
useMaterial3: true,
),
routerConfig: _router, // new
);
}
}
לכל מסך יש סוג אחר של פעולה המשויכת אליו בהתבסס על המצב החדש של זרימת האימות. לאחר רוב שינויי המצב באימות, תוכל לנתב מחדש למסך מועדף, בין אם זה מסך הבית או מסך אחר, כגון פרופיל.
- בשיטת הבנייה של מחלקת
HomePage
, שלב את מצב האפליקציה עם הווידג'טAuthFunc
:
lib/home_page.dart
import 'package:firebase_auth/firebase_auth.dart' // new
hide EmailAuthProvider, PhoneAuthProvider; // new
import 'package:flutter/material.dart'; // new
import 'package:provider/provider.dart'; // new
import 'app_state.dart'; // new
import 'src/authentication.dart'; // new
import 'src/widgets.dart';
class HomePage extends StatelessWidget {
const HomePage({super.key});
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: const Text('Firebase Meetup'),
),
body: ListView(
children: <Widget>[
Image.asset('assets/codelab.png'),
const SizedBox(height: 8),
const IconAndDetail(Icons.calendar_today, 'October 30'),
const IconAndDetail(Icons.location_city, 'San Francisco'),
// Add from here
Consumer<ApplicationState>(
builder: (context, appState, _) => AuthFunc(
loggedIn: appState.loggedIn,
signOut: () {
FirebaseAuth.instance.signOut();
}),
),
// to here
const Divider(
height: 8,
thickness: 1,
indent: 8,
endIndent: 8,
color: Colors.grey,
),
const Header("What we'll be doing"),
const Paragraph(
'Join us for a day full of Firebase Workshops and Pizza!',
),
],
),
);
}
}
אתה מייצר את הווידג'ט AuthFunc
ועוטף אותו בווידג'ט Consumer
. הווידג'ט לצרכן הוא הדרך הרגילה שבה ניתן להשתמש בחבילת provider
כדי לבנות מחדש חלק מהעץ כאשר מצב האפליקציה משתנה. הווידג'ט AuthFunc
הוא הווידג'טים המשלימים שאתה בודק.
בדוק את זרימת האימות
- באפליקציה, הקש על כפתור RSVP כדי להפעיל את
SignInScreen
.
- הכנס כתובת אימייל. אם אתה כבר רשום, המערכת מבקשת ממך להזין סיסמה. אחרת, המערכת תבקש ממך למלא את טופס ההרשמה.
- הזן סיסמה בת פחות משישה תווים כדי לבדוק את זרימת הטיפול בשגיאות. אם אתה רשום, אתה רואה את הסיסמה עבור במקום זאת.
- הזן סיסמאות שגויות כדי לבדוק את זרימת הטיפול בשגיאות.
- הזן את הסיסמה הנכונה. אתה רואה את חוויית הכניסה, שמציעה למשתמש את האפשרות להתנתק.
6. כתוב הודעות ל-Firestore
זה נהדר לדעת שמשתמשים מגיעים, אבל צריך לתת לאורחים משהו אחר לעשות באפליקציה. מה אם הם יכלו להשאיר הודעות בספר אורחים? הם יכולים לשתף מדוע הם מתרגשים להגיע או את מי הם מקווים לפגוש.
כדי לאחסן את הודעות הצ'אט שמשתמשים כותבים באפליקציה, אתה משתמש ב-Firestore .
מודל נתונים
Firestore הוא מסד נתונים NoSQL, והנתונים המאוחסנים במסד הנתונים מפוצלים לאוספים, מסמכים, שדות ותתי-אוספים. אתה מאחסן כל הודעה בצ'אט כמסמך באוסף guestbook
, שהוא אוסף ברמה העליונה.
הוסף הודעות ל-Firestore
בסעיף זה, אתה מוסיף את הפונקציונליות עבור משתמשים לכתוב הודעות למסד הנתונים. ראשית, אתה מוסיף שדה טופס וכפתור שלח, ולאחר מכן אתה מוסיף את הקוד שמחבר את האלמנטים הללו למסד הנתונים.
- צור קובץ חדש בשם
guest_book.dart
, הוסף ווידג'ט מצביGuestBook
כדי לבנות את רכיבי ממשק המשתמש של שדה הודעה וכפתור שלח:
lib/guest_book.dart
import 'dart:async';
import 'package:flutter/material.dart';
import 'src/widgets.dart';
class GuestBook extends StatefulWidget {
const GuestBook({required this.addMessage, super.key});
final FutureOr<void> Function(String message) addMessage;
@override
State<GuestBook> createState() => _GuestBookState();
}
class _GuestBookState extends State<GuestBook> {
final _formKey = GlobalKey<FormState>(debugLabel: '_GuestBookState');
final _controller = TextEditingController();
@override
Widget build(BuildContext context) {
return Padding(
padding: const EdgeInsets.all(8.0),
child: Form(
key: _formKey,
child: Row(
children: [
Expanded(
child: TextFormField(
controller: _controller,
decoration: const InputDecoration(
hintText: 'Leave a message',
),
validator: (value) {
if (value == null || value.isEmpty) {
return 'Enter your message to continue';
}
return null;
},
),
),
const SizedBox(width: 8),
StyledButton(
onPressed: () async {
if (_formKey.currentState!.validate()) {
await widget.addMessage(_controller.text);
_controller.clear();
}
},
child: Row(
children: const [
Icon(Icons.send),
SizedBox(width: 4),
Text('SEND'),
],
),
),
],
),
),
);
}
}
יש כאן כמה נקודות עניין. ראשית, אתה מייצר טופס כדי שתוכל לאמת שההודעה אכן מכילה תוכן ולהראות למשתמש הודעת שגיאה אם אין כזו. כדי לאמת טופס, אתה ניגש למצב הטופס מאחורי הטופס באמצעות GlobalKey
. למידע נוסף על מפתחות וכיצד להשתמש בהם, ראה מתי להשתמש במקשים .
שים לב גם לאופן שבו הווידג'טים מונחים, יש לך Row
עם TextFormField
ו- StyledButton
, שמכילה Row
. שימו לב גם שה- TextFormField
עטוף ב-widget Expanded
, מה שמאלץ את TextFormField
למלא כל שטח נוסף בשורה. כדי להבין טוב יותר מדוע זה נדרש, ראה הבנת אילוצים .
כעת, כשיש לך ווידג'ט המאפשר למשתמש להזין טקסט כלשהו כדי להוסיף לספר האורחים, עליך להעלות אותו על המסך.
- ערוך את הגוף של
HomePage
כדי להוסיף את שתי השורות הבאות בסוף הילדים שלListView
:
const Header("What we'll be doing"),
const Paragraph(
'Join us for a day full of Firebase Workshops and Pizza!',
),
// Add the following two lines.
const Header('Discussion'),
GuestBook(addMessage: (message) => print(message)),
אמנם זה מספיק כדי להציג את הווידג'ט, אבל זה לא מספיק כדי לעשות משהו מועיל. אתה מעדכן את הקוד הזה בקרוב כדי להפוך אותו לפונקציונלי.
תצוגה מקדימה של האפליקציה
כאשר משתמש לוחץ על שלח , הוא מפעיל את קטע הקוד הבא. זה מוסיף את התוכן של שדה קלט ההודעה לאוסף guestbook
של מסד הנתונים. באופן ספציפי, שיטת addMessageToGuestBook
מוסיפה את תוכן ההודעה למסמך חדש עם מזהה שנוצר באופן אוטומטי באוסף guestbook
.
שים לב ש- FirebaseAuth.instance.currentUser.uid
הוא הפניה למזהה הייחודי שנוצר אוטומטית שהאימות נותן עבור כל המשתמשים המחוברים.
- בקובץ
lib/app_state.dart
, הוסף את שיטתaddMessageToGuestBook
. אתה מחבר את היכולת הזו עם ממשק המשתמש בשלב הבא.
lib/app_state.dart
import 'package:cloud_firestore/cloud_firestore.dart'; // new
import 'package:firebase_auth/firebase_auth.dart'
hide EmailAuthProvider, PhoneAuthProvider;
import 'package:firebase_core/firebase_core.dart';
import 'package:firebase_ui_auth/firebase_ui_auth.dart';
import 'package:flutter/material.dart';
import 'firebase_options.dart';
class ApplicationState extends ChangeNotifier {
// Current content of ApplicationState elided ...
// Add from here...
Future<DocumentReference> addMessageToGuestBook(String message) {
if (!_loggedIn) {
throw Exception('Must be logged in');
}
return FirebaseFirestore.instance
.collection('guestbook')
.add(<String, dynamic>{
'text': message,
'timestamp': DateTime.now().millisecondsSinceEpoch,
'name': FirebaseAuth.instance.currentUser!.displayName,
'userId': FirebaseAuth.instance.currentUser!.uid,
});
}
// ...to here.
}
חבר את ממשק המשתמש ומסד הנתונים
יש לך ממשק משתמש שבו המשתמש יכול להזין את הטקסט שהוא רוצה להוסיף לספר האורחים ויש לך את הקוד להוסיף את הערך ל-Firestore. עכשיו כל מה שאתה צריך לעשות הוא לחבר את השניים.
- בקובץ
lib/home_page.dart
, בצע את השינוי הבא בווידג'טHomePage
:
lib/home_page.dart
import 'package:firebase_auth/firebase_auth.dart'
hide EmailAuthProvider, PhoneAuthProvider;
import 'package:flutter/material.dart';
import 'package:provider/provider.dart';
import 'app_state.dart';
import 'guest_book.dart'; // new
import 'src/authentication.dart';
import 'src/widgets.dart';
class HomePage extends StatelessWidget {
const HomePage({Key? key}) : super(key: key);
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: const Text('Firebase Meetup'),
),
body: ListView(
children: <Widget>[
Image.asset('assets/codelab.png'),
const SizedBox(height: 8),
const IconAndDetail(Icons.calendar_today, 'October 30'),
const IconAndDetail(Icons.location_city, 'San Francisco'),
Consumer<ApplicationState>(
builder: (context, appState, _) => AuthFunc(
loggedIn: appState.loggedIn,
signOut: () {
FirebaseAuth.instance.signOut();
}),
),
const Divider(
height: 8,
thickness: 1,
indent: 8,
endIndent: 8,
color: Colors.grey,
),
const Header("What we'll be doing"),
const Paragraph(
'Join us for a day full of Firebase Workshops and Pizza!',
),
// Modify from here...
Consumer<ApplicationState>(
builder: (context, appState, _) => Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
if (appState.loggedIn) ...[
const Header('Discussion'),
GuestBook(
addMessage: (message) =>
appState.addMessageToGuestBook(message),
),
],
],
),
),
// ...to here.
],
),
);
}
}
החלפת את שתי השורות שהוספת בתחילת שלב זה ביישום המלא. אתה שוב משתמש Consumer<ApplicationState>
כדי להפוך את מצב האפליקציה לזמין לחלק של העץ שאתה מציג. זה מאפשר לך להגיב למישהו שמזין הודעה בממשק המשתמש ולפרסם אותה במסד הנתונים. בסעיף הבא, אתה בודק אם ההודעות שנוספו מתפרסמות במסד הנתונים.
בדיקת שליחת הודעות
- במידת הצורך, היכנס לאפליקציה.
- הזן הודעה, כגון
Hey there!
ולאחר מכן לחץ על שלח .
פעולה זו כותבת את ההודעה למסד הנתונים שלך ב-Firestore. עם זאת, אינך רואה את ההודעה באפליקציית Flutter האמיתית שלך מכיוון שאתה עדיין צריך ליישם אחזור של הנתונים, מה שאתה עושה בשלב הבא. עם זאת, בלוח המחוונים של מסד הנתונים של מסוף Firebase, אתה יכול לראות את ההודעה שהוספת באוסף guestbook
. אם אתה שולח יותר הודעות, אתה מוסיף עוד מסמכים לאוסף guestbook
שלך. לדוגמה, ראה את קטע הקוד הבא:
7. קרא הודעות
זה מקסים שאורחים יכולים לכתוב הודעות למסד הנתונים, אבל הם עדיין לא יכולים לראות אותם באפליקציה. הגיע הזמן לתקן את זה!
סנכרן הודעות
כדי להציג הודעות, עליך להוסיף מאזינים המופעלים כאשר הנתונים משתנים ולאחר מכן ליצור רכיב ממשק משתמש המציג הודעות חדשות. אתה מוסיף קוד למצב האפליקציה שמאזין להודעות חדשות שנוספו מהאפליקציה.
- צור קובץ חדש
guest_book_message.dart
, הוסף את המחלקה הבאה כדי לחשוף תצוגה מובנית של הנתונים שאתה מאחסן ב-Firestore.
lib/guest_book_message.dart
class GuestBookMessage {
GuestBookMessage({required this.name, required this.message});
final String name;
final String message;
}
- בקובץ
lib/app_state.dart
, הוסף את הייבוא הבא:
lib/app_state.dart
import 'dart:async'; // new
import 'package:cloud_firestore/cloud_firestore.dart';
import 'package:firebase_auth/firebase_auth.dart'
hide EmailAuthProvider, PhoneAuthProvider;
import 'package:firebase_core/firebase_core.dart';
import 'package:firebase_ui_auth/firebase_ui_auth.dart';
import 'package:flutter/material.dart';
import 'firebase_options.dart';
import 'guest_book_message.dart'; // new
- בחלק של
ApplicationState
שבו אתה מגדיר מצב ו-getters, הוסף את השורות הבאות:
lib/app_state.dart
bool _loggedIn = false;
bool get loggedIn => _loggedIn;
// Add from here...
StreamSubscription<QuerySnapshot>? _guestBookSubscription;
List<GuestBookMessage> _guestBookMessages = [];
List<GuestBookMessage> get guestBookMessages => _guestBookMessages;
// ...to here.
- במקטע האתחול של
ApplicationState
, הוסף את השורות הבאות כדי להירשם לשאילתה על אוסף המסמכים כאשר משתמש מתחבר וביטול המנוי כאשר הוא מתנתק:
lib/app_state.dart
Future<void> init() async {
await Firebase.initializeApp(
options: DefaultFirebaseOptions.currentPlatform);
FirebaseUIAuth.configureProviders([
EmailAuthProvider(),
]);
FirebaseAuth.instance.userChanges().listen((user) {
if (user != null) {
_loggedIn = true;
_guestBookSubscription = FirebaseFirestore.instance
.collection('guestbook')
.orderBy('timestamp', descending: true)
.snapshots()
.listen((snapshot) {
_guestBookMessages = [];
for (final document in snapshot.docs) {
_guestBookMessages.add(
GuestBookMessage(
name: document.data()['name'] as String,
message: document.data()['text'] as String,
),
);
}
notifyListeners();
});
} else {
_loggedIn = false;
_guestBookMessages = [];
_guestBookSubscription?.cancel();
}
notifyListeners();
});
}
סעיף זה חשוב כי זה המקום שבו אתה בונה שאילתה על אוסף guestbook
, ומטפל בהרשמה ובביטול ההרשמה לאוסף זה. אתה מאזין לזרם, שבו אתה בונה מטמון מקומי של ההודעות באוסף guestbook
וגם מאחסן הפניה למנוי הזה כדי שתוכל לבטל את המנוי ממנו מאוחר יותר. יש הרבה דברים שקורים כאן, אז כדאי לחקור את זה ב-debugger כדי לבדוק מה קורה כדי לקבל מודל מנטלי ברור יותר. למידע נוסף, ראה קבל עדכונים בזמן אמת עם Firestore .
- בקובץ
lib/guest_book.dart
, הוסף את הייבוא הבא:
import 'guest_book_message.dart';
- בווידג'ט
GuestBook
, הוסף רשימה של הודעות כחלק מהתצורה כדי לחבר את המצב המשתנה הזה לממשק המשתמש:
lib/guest_book.dart
class GuestBook extends StatefulWidget {
// Modify the following line:
const GuestBook({
super.key,
required this.addMessage,
required this.messages,
});
final FutureOr<void> Function(String message) addMessage;
final List<GuestBookMessage> messages; // new
@override
_GuestBookState createState() => _GuestBookState();
}
- ב-
_GuestBookState
, שנה את שיטתbuild
באופן הבא כדי לחשוף את התצורה הזו:
lib/guest_book.dart
class _GuestBookState extends State<GuestBook> {
final _formKey = GlobalKey<FormState>(debugLabel: '_GuestBookState');
final _controller = TextEditingController();
@override
// Modify from here...
Widget build(BuildContext context) {
return Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
// ...to here.
Padding(
padding: const EdgeInsets.all(8.0),
child: Form(
key: _formKey,
child: Row(
children: [
Expanded(
child: TextFormField(
controller: _controller,
decoration: const InputDecoration(
hintText: 'Leave a message',
),
validator: (value) {
if (value == null || value.isEmpty) {
return 'Enter your message to continue';
}
return null;
},
),
),
const SizedBox(width: 8),
StyledButton(
onPressed: () async {
if (_formKey.currentState!.validate()) {
await widget.addMessage(_controller.text);
_controller.clear();
}
},
child: Row(
children: const [
Icon(Icons.send),
SizedBox(width: 4),
Text('SEND'),
],
),
),
],
),
),
),
// Modify from here...
const SizedBox(height: 8),
for (var message in widget.messages)
Paragraph('${message.name}: ${message.message}'),
const SizedBox(height: 8),
],
// ...to here.
);
}
}
אתה עוטף את התוכן הקודם של שיטת build()
בווידג'ט Column
ואז אתה מוסיף אוסף עבור הזנב של הילדים של Column
כדי ליצור Paragraph
חדשה עבור כל הודעה ברשימת ההודעות.
- עדכן את גוף
HomePage
כדי לבנות נכוןGuestBook
עם פרמטרmessages
החדש:
lib/home_page.dart
Consumer<ApplicationState>(
builder: (context, appState, _) => Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
if (appState.loggedIn) ...[
const Header('Discussion'),
GuestBook(
addMessage: (message) =>
appState.addMessageToGuestBook(message),
messages: appState.guestBookMessages, // new
),
],
],
),
),
בדוק סנכרון הודעות
Firestore מסנכרן נתונים באופן אוטומטי ומיידי עם לקוחות הרשומים למסד הנתונים.
בדיקת סנכרון הודעות:
- באפליקציה, מצא את ההודעות שיצרת קודם לכן במסד הנתונים.
- כתוב הודעות חדשות. הם מופיעים באופן מיידי.
- פתח את סביבת העבודה שלך במספר חלונות או כרטיסיות. ההודעות מסתנכרנות בזמן אמת על פני החלונות והכרטיסיות.
- אופציונלי: בתפריט מסד הנתונים של מסוף Firebase, מחק, שנה או הוסף הודעות חדשות באופן ידני. כל השינויים מופיעים בממשק המשתמש.
מזל טוב! אתה קורא מסמכי Firestore באפליקציה שלך!
תצוגה מקדימה של האפליקציה
8. הגדר כללי אבטחה בסיסיים
תחילה הגדרת את Firestore לשימוש במצב בדיקה, מה שאומר שמסד הנתונים שלך פתוח לקריאה וכתיבה. עם זאת, עליך להשתמש במצב בדיקה רק בשלבים מוקדמים של הפיתוח. בתור שיטות עבודה מומלצות, עליך להגדיר כללי אבטחה עבור מסד הנתונים שלך בזמן שאתה מפתח את האפליקציה שלך. האבטחה היא חלק בלתי נפרד מהמבנה וההתנהגות של האפליקציה שלך.
כללי האבטחה של Firebase מאפשרים לך לשלוט בגישה למסמכים ואוספים במסד הנתונים שלך. תחביר הכללים הגמישים מאפשר לך ליצור כללים התואמים כל דבר, החל מכל הכתיבה למסד הנתונים כולו ועד לפעולות במסמך ספציפי.
הגדר כללי אבטחה בסיסיים:
- בתפריט פיתוח של מסוף Firebase, לחץ על מסד נתונים > כללים . אתה אמור לראות את כללי האבטחה הבאים כברירת מחדל ואזהרה לגבי שהכללים פומביים:
- זהה את האוספים אליהם האפליקציה כותבת נתונים:
ב- match /databases/{database}/documents
, זהה את האוסף שברצונך לאבטח:
rules_version = '2';
service cloud.firestore {
match /databases/{database}/documents {
match /guestbook/{entry} {
// You'll add rules here in the next step.
}
}
מכיוון שהשתמשת ב-UID של האימות כשדה בכל מסמך של ספר אורחים, אתה יכול לקבל את ה-UID של האימות ולוודא שלכל מי שמנסה לכתוב למסמך יש מזהה אימות תואם.
- הוסף את כללי הקריאה והכתיבה לקבוצת הכללים שלך:
rules_version = '2';
service cloud.firestore {
match /databases/{database}/documents {
match /guestbook/{entry} {
allow read: if request.auth.uid != null;
allow write:
if request.auth.uid == request.resource.data.userId;
}
}
}
כעת, רק משתמשים מחוברים יכולים לקרוא הודעות בספר האורחים, אך רק מחבר הודעה יכול לערוך הודעה.
- הוסף אימות נתונים כדי לוודא שכל השדות הצפויים קיימים במסמך:
rules_version = '2';
service cloud.firestore {
match /databases/{database}/documents {
match /guestbook/{entry} {
allow read: if request.auth.uid != null;
allow write:
if request.auth.uid == request.resource.data.userId
&& "name" in request.resource.data
&& "text" in request.resource.data
&& "timestamp" in request.resource.data;
}
}
}
9. שלב בונוס: תרגל את מה שלמדת
הקלט את סטטוס ה-RSVP של משתתף
כרגע, האפליקציה שלך מאפשרת לאנשים לשוחח בצ'אט רק כשהם מתעניינים באירוע. כמו כן, הדרך היחידה לדעת אם מישהו מגיע היא כאשר הוא אומר זאת בצ'אט.
בשלב זה, אתה מתארגן ומודיע לאנשים כמה אנשים מגיעים. אתה מוסיף כמה יכולות למצב האפליקציה. הראשון הוא היכולת של משתמש מחובר לקבוע אם הוא משתתף. השני הוא מונה של כמה אנשים משתתפים.
- בקובץ
lib/app_state.dart
, הוסף את השורות הבאות לקטע ה-Accessors שלApplicationState
כדי שקוד ממשק המשתמש יוכל לקיים אינטראקציה עם מצב זה:
lib/app_state.dart
int _attendees = 0;
int get attendees => _attendees;
Attending _attending = Attending.unknown;
StreamSubscription<DocumentSnapshot>? _attendingSubscription;
Attending get attending => _attending;
set attending(Attending attending) {
final userDoc = FirebaseFirestore.instance
.collection('attendees')
.doc(FirebaseAuth.instance.currentUser!.uid);
if (attending == Attending.yes) {
userDoc.set(<String, dynamic>{'attending': true});
} else {
userDoc.set(<String, dynamic>{'attending': false});
}
}
- עדכן את שיטת
init()
שלApplicationState
באופן הבא:
lib/app_state.dart
Future<void> init() async {
await Firebase.initializeApp(
options: DefaultFirebaseOptions.currentPlatform);
FirebaseUIAuth.configureProviders([
EmailAuthProvider(),
]);
// Add from here...
FirebaseFirestore.instance
.collection('attendees')
.where('attending', isEqualTo: true)
.snapshots()
.listen((snapshot) {
_attendees = snapshot.docs.length;
notifyListeners();
});
// ...to here.
FirebaseAuth.instance.userChanges().listen((user) {
if (user != null) {
_loggedIn = true;
_emailVerified = user.emailVerified;
_guestBookSubscription = FirebaseFirestore.instance
.collection('guestbook')
.orderBy('timestamp', descending: true)
.snapshots()
.listen((snapshot) {
_guestBookMessages = [];
for (final document in snapshot.docs) {
_guestBookMessages.add(
GuestBookMessage(
name: document.data()['name'] as String,
message: document.data()['text'] as String,
),
);
}
notifyListeners();
});
// Add from here...
_attendingSubscription = FirebaseFirestore.instance
.collection('attendees')
.doc(user.uid)
.snapshots()
.listen((snapshot) {
if (snapshot.data() != null) {
if (snapshot.data()!['attending'] as bool) {
_attending = Attending.yes;
} else {
_attending = Attending.no;
}
} else {
_attending = Attending.unknown;
}
notifyListeners();
});
// ...to here.
} else {
_loggedIn = false;
_emailVerified = false;
_guestBookMessages = [];
_guestBookSubscription?.cancel();
_attendingSubscription?.cancel(); // new
}
notifyListeners();
});
}
קוד זה מוסיף שאילתה רשומה תמיד כדי לקבוע את מספר המשתתפים ושאילתה שנייה הפעילה רק בזמן שמשתמש מחובר כדי לקבוע אם המשתמש משתתף.
- הוסף את הספירה הבאה בראש הקובץ
lib/app_state.dart
.
lib/app_state.dart
enum Attending { yes, no, unknown }
- צור קובץ חדש
yes_no_selection.dart
, הגדר ווידג'ט חדש שפועל כמו לחצני בחירה:
lib/yes_no_selection.dart
import 'package:flutter/material.dart';
import 'app_state.dart';
import 'src/widgets.dart';
class YesNoSelection extends StatelessWidget {
const YesNoSelection(
{super.key, required this.state, required this.onSelection});
final Attending state;
final void Function(Attending selection) onSelection;
@override
Widget build(BuildContext context) {
switch (state) {
case Attending.yes:
return Padding(
padding: const EdgeInsets.all(8.0),
child: Row(
children: [
FilledButton(
onPressed: () => onSelection(Attending.yes),
child: const Text('YES'),
),
const SizedBox(width: 8),
TextButton(
onPressed: () => onSelection(Attending.no),
child: const Text('NO'),
),
],
),
);
case Attending.no:
return Padding(
padding: const EdgeInsets.all(8.0),
child: Row(
children: [
TextButton(
onPressed: () => onSelection(Attending.yes),
child: const Text('YES'),
),
const SizedBox(width: 8),
FilledButton(
onPressed: () => onSelection(Attending.no),
child: const Text('NO'),
),
],
),
);
default:
return Padding(
padding: const EdgeInsets.all(8.0),
child: Row(
children: [
StyledButton(
onPressed: () => onSelection(Attending.yes),
child: const Text('YES'),
),
const SizedBox(width: 8),
StyledButton(
onPressed: () => onSelection(Attending.no),
child: const Text('NO'),
),
],
),
);
}
}
}
זה מתחיל במצב בלתי מוגדר ולא נבחרו כן ולא לא . ברגע שהמשתמש בוחר אם הוא משתתף, אתה מציג את האפשרות הזו מסומנת עם כפתור מלא והאפשרות השנייה נסוגה עם עיבוד שטוח.
- עדכן את שיטת
build()
שלHomePage
כדי לנצל את היתרונות שלYesNoSelection
, לאפשר למשתמש מחובר לקבוע אם הוא משתתף ולהציג את מספר המשתתפים באירוע:
lib/home_page.dart
Consumer<ApplicationState>(
builder: (context, appState, _) => Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
// Add from here...
switch (appState.attendees) {
1 => const Paragraph('1 person going'),
>= 2 => Paragraph('${appState.attendees} people going'),
_ => const Paragraph('No one going'),
},
// ...to here.
if (appState.loggedIn) ...[
// Add from here...
YesNoSelection(
state: appState.attending,
onSelection: (attending) => appState.attending = attending,
),
// ...to here.
const Header('Discussion'),
GuestBook(
addMessage: (message) =>
appState.addMessageToGuestBook(message),
messages: appState.guestBookMessages,
),
],
],
),
),
הוסף כללים
כבר הגדרת כמה כללים, כך שהנתונים שתוסיף באמצעות הכפתורים יידחו. עליך לעדכן את הכללים כדי לאפשר תוספות לאוסף attendees
.
- באוסף
attendees
, קח את ה-UID של האימות שבו השתמשת כשם המסמך וודא שה-uid
של המגיש זהה למסמך שהם כותבים:
rules_version = '2';
service cloud.firestore {
match /databases/{database}/documents {
// ... //
match /attendees/{userId} {
allow read: if true;
allow write: if request.auth.uid == userId;
}
}
}
זה מאפשר לכולם לקרוא את רשימת המשתתפים מכיוון שאין שם נתונים פרטיים, אלא רק היוצר יכול לעדכן אותם.
- הוסף אימות נתונים כדי לוודא שכל השדות הצפויים קיימים במסמך:
rules_version = '2';
service cloud.firestore {
match /databases/{database}/documents {
// ... //
match /attendees/{userId} {
allow read: if true;
allow write: if request.auth.uid == userId
&& "attending" in request.resource.data;
}
}
}
- אופציונלי: באפליקציה, לחץ על הלחצנים כדי לראות את התוצאות בלוח המחוונים של Firestore במסוף Firebase.
תצוגה מקדימה של האפליקציה
10. מזל טוב!
השתמשת ב-Firebase כדי לבנות אפליקציית אינטרנט אינטראקטיבית בזמן אמת!