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

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

ניהול משתמשים ב-Flutter - מדריך # 5 - דף ניהול משתמש

 

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

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

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

 

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

 

User management with Firebase Authentication service in a Flutter application

נוסיף מסך 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 - איפוס סיסמת משתמש

 

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

 

 

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

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

 

 

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

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

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

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

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

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

 

 

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

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