ניהול משתמשים ב-Flutter - מדריך # 5 - דף ניהול משתמש
במדריך החמישי בסדרת המדריכים על ניהול משתמשים באפליקציית Flutter נוסיף מסך המאפשר למשתמש לערוך את פרטיו, ובנוסף נלמד כיצד לעקוב אחר מצב המשתמש בזמן אמת בכל הנוגע לאישור מייל הרשמה ששירות Firebase Authentication שולח למשתמשים חדשים.
במדריך הראשון למדנו כיצד לרשום משתמש לאפליקציה, בשני לעשות login למשתמש קיים. בשלישי הוספנו את הקוד המאפשר למשתמש לערוך את פרטיו ולהעלות תמונה. ברביעי שליחת תזכורת סיסמה למשתמש.
להורדת הקוד - אפליקציית Flutter לניהול משתמשים על גבי פלטפורמת Firebase
נוסיף מסך home_screen שצפייה בו אפשרית רק למשתמשים רשומים באפליקציה:
lib/screens/home_screen.dart
import 'package:flutter/material.dart';
class HomeScreen extends StatefulWidget {
const HomeScreen({super.key});
@override
State<HomeScreen> createState() => _HomeScreenState();
}
class _HomeScreenState extends State<HomeScreen> {
@override
Widget build(BuildContext context) {
//
}
}
- הקובץ home_screen.dart מכיל את הקלאס HomeScreen.
- הקלאס HomeScreen יוצג למשתמש רק בתנאי שהוא רשום לאפליקציה.
קובץ זה יוצג למשתמש רק בתנאי שהמשתמש רשום לשירות Firebase Auth של האפליקציה בזכות ה- WidgetTree אשר בורר אילו מסכים להציג למשתמש על פי מצבו:
lib/widgets/widget_tree.dart
import 'package:firebase_auth/firebase_auth.dart';
import 'package:flutter/material.dart';
import '../screens/auth_screen.dart';
import '../screens/home_screen.dart';
class WidgetTree extends StatefulWidget {
const WidgetTree({super.key});
@override
State<WidgetTree> createState() => _WidgetTreeState();
}
class _WidgetTreeState extends State<WidgetTree> {
@override
Widget build(BuildContext context) {
return StreamBuilder<User?>(
stream: FirebaseAuth.instance.authStateChanges(),
builder: (context, snapshot) {
if (snapshot.connectionState == ConnectionState.waiting) {
return const Center(
child: CircularProgressIndicator(),
);
} else if (snapshot.hasError) {
return const Center(
child: Text("Error in auth"),
);
} else if (snapshot.hasData) {
return const HomeScreen();
} else {
return const AuthScreen();
}
});
}
}
- ה-WidgetTree מעודכן בזמן אמת במצב המשתמש המנוהל ע"י Firebase Auth באמצעות StreamBuilder.
- רק אם קיים מידע על המשתמש אצל Firebase מוצג בפני המשתמש הקלאס HomeScreen לעיל. אחרת, המשתמש רואה את הקלאס AuthScreen אשר כולל טופס הרשמה/כניסת משתמש.
הבעיה המרכזית במסך שיוצג למשתמש היא כיצד להציג תוכן שונה למשתמש שאישר את המייל שלו לעומת מי שלא. בגלל שלא הצלחתי למצוא דרך פשוטה לברר את סטטוס המשתמש באמצעות מתודות של Firebase Auth נשלח בקשה לבירור סטטוס משתמש לשירות מדי כמה שניות עד שהמשתמש יאשר את המייל שלו או יצא מן המסך.
נוסיף את המשתנים הבאים אל תוך הקלאס:
lib/screens/home_screen.dart
late bool _isEmailVerified = false;
late Timer _timerEmailVerification;
- משתנה פרטי isEmailVerified שברירת המחדל היא false
- משתנה טיימר שתכף נשתמש בו כדי לשלוח את הבקשות מדי כמה זמן.
- המשתנים מוגדרים late כדי שההערכה שלהם לא תעשה בזמן הקומפילציה אלא בזמן הריצה כי הערך שלהם צריך להתעדכן אחרי יסוד האובייקט מן הקלאס.
חשוב לדעת מה מצבו של המשתמש אצל Firebase Auth:
lib/screens/home_screen.dart
User? user = AuthProvider(FirebaseAuth.instance).currentUser;
את המשתנה timerEmailVerification נגדיר בתוך הפונקציה הבאה:
lib/screens/home_screen.dart
void _setEmailVerifiedTimer() async {
_isEmailVerifiedFunc();
_timerEmailVerification =
Timer.periodic(const Duration(seconds: 10), (timer) async {
_isEmailVerifiedFunc();
});
}
הפונקציה setEmailVerifiedTimer רצה בפעם הראשונה שקוראים לה, ואח"כ מפעילה טיימר בכל 10 שניות אשר בודק את מצב המשתמש על ידי קריאה לפונקציה isEmailVerifiedFunc ששולחת בקשה לבירור סטטוס המשתמש אצל שירות Firebase:
lib/screens/home_screen.dart
Future<void> _isEmailVerifiedFunc() async {
await FirebaseAuth.instance.currentUser?.reload();
final user = FirebaseAuth.instance.currentUser;
if (user?.emailVerified ?? false) {
_timerEmailVerification.cancel();
//Navigator.pop(context, true);
setState(() {
_isEmailVerified = true;
});
}
}
- אם המשתמש אישר את המייל הפונקציה משנה את מצב המשתנה isEmailVerified, ומבטלת את הטיימר.
נקרא לפונקציה setEmailVerifiedTimer מתוך initState כדי שתרוץ ראשונה כאשר המסך מוצג:
lib/screens/home_screen.dart
@override
void initState() {
super.initState();
_setEmailVerifiedTimer();
}
כיוון שהטיימר מוגדר ב-initState נפסיק את פעולתו בשלב ה-dispose למניעת memory leak:
lib/screens/home_screen.dart
@override
void dispose() {
_timerEmailVerification.cancel();
super.dispose();
}
הפונקציה signOut תשמש להוציא את המשתמש ממצב של משתמש מזוהה באפליקציה:
lib/screens/home_screen.dart
Future<void> signOut() async {
await Provider.of<AuthProvider>(context, listen: false).signOut();
}
נוסיף את קוד הטמפלייט של המסך:
lib/screens/home_screen.dart
@override
Widget build(BuildContext context) {
final tm = Theme.of(context);
return Scaffold(
appBar: AppBar(
backgroundColor: tm.primaryColor,
centerTitle: true,
title: title("Hello Amigo"),
),
body: Container(
height: double.infinity,
width: double.infinity,
padding: const EdgeInsets.all(20),
child: SingleChildScrollView(
child: Column(
crossAxisAlignment: CrossAxisAlignment.center,
mainAxisAlignment: MainAxisAlignment.center,
children: <Widget>[
userData(user, _isEmailVerified),
const SizedBox(height: 20),
if (_isEmailVerified)
ElevatedButton.icon(
style: ElevatedButton.styleFrom(
backgroundColor: Theme.of(context).primaryColor,
),
icon: const Icon(Icons.app_registration_outlined,
color: Colors.white),
label: const Text(
'Update user profile',
style: TextStyle(
fontSize: 16,
color: Colors.white,
),
),
onPressed: () {
Navigator.push(
context,
MaterialPageRoute(
builder: (context) => const UpdateUserScreen()));
},
),
const SizedBox(height: 20),
TextButton(
child: const Text(
'Sign out',
style: TextStyle(
color: Colors.black,
decoration: TextDecoration.underline,
fontSize: 16,
),
),
onPressed: () => signOut(),
),
],
),
),
),
);
}
- האלמנט כולו מוחזק בתוך ווידג'ט Scaffold בתוכו האלמנטים מסודרים אחד מעל לשני בתוך עמודה Column
- התצוגה שונה בין משתמש שאישר את המייל שלו למי שלא
תצוגת הווידג'ט userData שונה בין משתמש שאישר לכזה שלא:
Widget userData(User? user, bool isEmailVerified) {
String displayName = user?.displayName ?? "Unknown";
String email = user?.email ?? "Unknown";
String imgUrl = user?.photoURL ?? '';
return Column(
crossAxisAlignment: CrossAxisAlignment.start,
mainAxisAlignment: MainAxisAlignment.end,
children: [
if (!isEmailVerified)
alert(
'We sent you a verification email you must click the link to unlock the app',
Colors.red),
const SizedBox(height: 20),
Text.rich(
TextSpan(
text: 'Name:',
style: const TextStyle(
fontWeight: FontWeight.bold, height: 1.4, fontSize: 16),
children: [
TextSpan(
text: " $displayName",
style: const TextStyle(fontWeight: FontWeight.normal),
),
],
),
),
Text.rich(
TextSpan(
text: 'Email:',
style: const TextStyle(
fontWeight: FontWeight.bold, height: 1.4, fontSize: 16),
children: [
TextSpan(
text: " $email",
style: const TextStyle(fontWeight: FontWeight.normal),
),
],
),
),
const SizedBox(height: 20),
profileImage(imgUrl),
]);
}
הווידג'ט userData מציג את תמונת המשתמש באמצעות ווידג'ט נוסף ששמו profileImage:
Widget profileImage(userImageUrl) {
return Center(
child: userImageUrl == ""
? Icon(Icons.person_add, size: 120, color: Constants().primaryColor)
: Container(
alignment: Alignment.center,
width: double.infinity,
height: 200,
decoration: BoxDecoration(
borderRadius: BorderRadius.circular(10),
image: DecorationImage(
image: NetworkImage(userImageUrl),
fit: BoxFit.cover,
),
),
),
);
}
מדריכים נוספים בסדרת ה-Flutter שעשויים לעניין אותך
ניהול משתמשים באפליקציית Flutter - מדריך # 1 - הרשמה
ניהול משתמשים Flutter - מדריך # 2 - כניסת משתמשים
ניהול משתמשים ב-Flutter - מדריך # 3 - עדכון משתמש קיים
ניהול משתמשים ב- Flutter - מדריך # 4 - איפוס סיסמת משתמש
אהבתם? לא אהבתם? דרגו!
0 הצבעות, ממוצע 0 מתוך 5 כוכבים
המדריכים באתר עוסקים בנושאי תכנות ופיתוח אישי. הקוד שמוצג משמש להדגמה ולצרכי לימוד. התוכן והקוד המוצגים באתר נבדקו בקפידה ונמצאו תקינים. אבל ייתכן ששימוש במערכות שונות, דוגמת דפדפן או מערכת הפעלה שונה ולאור השינויים הטכנולוגיים התכופים בעולם שבו אנו חיים יגרום לתוצאות שונות מהמצופה. בכל מקרה, אין בעל האתר נושא באחריות לכל שיבוש או שימוש לא אחראי בתכנים הלימודיים באתר.
למרות האמור לעיל, ומתוך רצון טוב, אם נתקלת בקשיים ביישום הקוד באתר מפאת מה שנראה לך כשגיאה או כחוסר עקביות נא להשאיר תגובה עם פירוט הבעיה באזור התגובות בתחתית המדריכים. זה יכול לעזור למשתמשים אחרים שנתקלו באותה בעיה ואם אני רואה שהבעיה עקרונית אני עשוי לערוך התאמה במדריך או להסיר אותו כדי להימנע מהטעיית הציבור.
שימו לב! הסקריפטים במדריכים מיועדים למטרות לימוד בלבד. כשאתם עובדים על הפרויקטים שלכם אתם צריכים להשתמש בספריות וסביבות פיתוח מוכחות, מהירות ובטוחות.
המשתמש באתר צריך להיות מודע לכך שאם וכאשר הוא מפתח קוד בשביל פרויקט הוא חייב לשים לב ולהשתמש בסביבת הפיתוח המתאימה ביותר, הבטוחה ביותר, היעילה ביותר וכמובן שהוא צריך לבדוק את הקוד בהיבטים של יעילות ואבטחה. מי אמר שלהיות מפתח זו עבודה קלה ?
השימוש שלך באתר מהווה ראייה להסכמתך עם הכללים והתקנות שנוסחו בהסכם תנאי השימוש.