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

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

ניהול משתמשים באפליקציית Flutter - מדריך # 1 - הרשמה

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

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

 

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

 

Flutter firebase auth app - registration screen

סדרת המדריכים כוללת :

  • ניהול משתמשים באמצעות שירות Firebase
  • הרשמה Register עם שם משתמש וסיסמה
  • כניסת משתמש קיים Login
  • תזכורת סיסמה
  • עריכת פרטי משתמש והעלאת תמונה

בהכנת מדריך זה נעזרתי בתיעוד הרשמי של גוגל Get started with Firebase Authentication שמסביר כיצד לנהל את משתמשי האתר באמצעות Flutter ו-Firebase.

 

מתחילים

ניצור אפליקציה חדשה באמצעות הטרמינל, שורת הפקודות:

$ flutter create my_auth_app
  • my_auth_app הוא השם שבחרתי. אתה יכול לבחור כל שם.

נפתח את האפליקציה ב-VSCode, נפתח את הקונסולה של ה-IDE, ובתוכה נריץ את הפקודות להתקנת החבילות:

$ flutter pub add firebase_core
$ flutter pub add firebase_auth
$ flutter pub add provider
  • firebase_core - תאפשר לנו לתקשר עם השירותים של FireBase בהם מסד נתונים, שירות אחסון קבצים ועבודה עם משתמשים
  • firebase_auth בשביל עבודה עם שירות ניהול המשתמשים.
  • Provider - מאפשר לנהל את המידע באפליקציה ממקום אחד.

נבנה את האפליקציה בעזרת הפקודה:

$ flutter run

נייבא את החבילות לתוך הקובץ הראשי של האפליקציה main.dart:

lib/main.dart

import 'package:flutter/material.dart';


import 'package:firebase_auth/firebase_auth.dart';
import 'package:firebase_core/firebase_core.dart';

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

lib/main.dart

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

בשלב זה צריך להתחבר עם Firebase. לקריאת המדריך על עבודה עם Firebase ו-Flutter.

 

הרשמה Sign In עם שם משתמש וסיסמה

בתוך הקונסולה של Firebase נכנסים לפרויקט שלנו >> בוחרים באפשרות Authentication >> לשונית Sign-in method ואז מוסיפים provider (ספק) מסוג Email/Password:

 Firebase איך לפתוח פרוייקט

Pick authentication from the navigation

Choose sign in method in the Firebase authentication service

עושים Enable, ושומרים:

Enable Email/Password authentication

נוסיף קלאס AuthProvider בתוכו יהיו המתודות שישמשו לתקשר את החלק שתפקידו לנהל את המשתמשים באפליקציה עם Firebase. הקלאס יהיה מסוג provider לפיכך הוא ירחיב את ChangeNotifier (כיצד להשתמש ב-Provider באפליקציות Flutter):

lib/providers/auth_provider.dart

import 'package:firebase_auth/firebase_auth.dart';
import 'package:flutter/material.dart';

class AuthProvider extends ChangeNotifier {
 final FirebaseAuth _firebaseAuth;
 AuthProvider(this._firebaseAuth);


    // the class code
}

נוסיף לתוך הקלאס AuthProvider את המתודה createUser ליצירת משתמש חדש. המתודה מקבלת פרמטרים שם משתמש וסיסמה ומחזירה מחרוזת עם המילה "success" במקרה של הצלחת ההרשמה או הודעת שגיאה במקרה של בעיה. המתודה מתקשרת עם FirebaseAuth באמצעות המתודה createUserWithEmailAndPassword:

lib/providers/auth_provider.dart

final _failMsg = "failed for unknown reason";


Future<String> createUser(
     {required String email, required String password}) async {
   try {
     User? user = (await _firebaseAuth.createUserWithEmailAndPassword(
             email: email, password: password))
         .user;


     if (user != null) {
       return "success";
     }


     return _failMsg;
   } on FirebaseAuthException catch (e) {
     //return e.message.toString();
     var msgToUser = 'Something is wrong :-(';
     if (e.code == 'weak-password') {
       msgToUser = 'The password is too weak.';
     } else if (e.code == 'email-already-in-use') {
       msgToUser = 'The account already exists.';
     } else if (e.message.runtimeType == String) {
       msgToUser = e.message!;
     }
     return msgToUser;
   }
}

מיד נוסיף את הקלאס כ-provider לאפליקציה אבל קודם נוסיף 2 מסכים, אחד שיוצג למשתמש שאינו רשום AuthScreen ושני שיוצג למי שרשום HomeScreen:

lib/screens/auth_screen.dart

import 'package:flutter/material.dart';


class AuthScreen extends StatefulWidget {
 const AuthScreen({super.key});


 @override
 State<AuthScreen> createState() => _AuthScreenState();
}


class _AuthScreenState extends State<AuthScreen> {
 @override
 Widget build(BuildContext context) {
   return Container(
     child: const Text("Auth screen"),
   );
 }
}
  • בתוך קלאס stateful מוצג טקסט ממלא מקום.

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) {
   return Container(
     child: const Text("home screen"),
   );
 }
}
  • בתוך קלאס stateful מוצג טקסט ממלא מקום.

נוסיף ווידג'ט WidgetTree שתפקידו לברור איזה מסך להציג למשתמש. במקרה של טעינה הוא מציג אנימציית טעינה. במקרה שהמשתמש אינו רשום יוצג לו טופס הרישום AuthScreen. אם המשתמש רשום יוצג לו HomeScreen.

lib/widgets/widget_tree.dart

import 'package:flutter/material.dart';


import 'package:firebase_auth/firebase_auth.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();
         }
       });
 }
}

חזרה לקובץ הראשי של האפליקציה. נוסיף את קלאס ה-provider כ-ListenableProvider:

lib/main.dart

class MyApp extends StatelessWidget {
 const MyApp({super.key});


 // This widget is the root of your application.
 @override
 Widget build(BuildContext context) {
   return MultiProvider(
     providers: [
       ListenableProvider<AuthProvider>(
         create: (_) => AuthProvider(FirebaseAuth.instance),
       ),
     ],
     child: MaterialApp(
       title: 'Auth app',
       debugShowCheckedModeBanner: false,
       theme: ThemeData(
         primaryColor: Colors.blue,
         scaffoldBackgroundColor: Colors.white,
       ),
       home: const WidgetTree(),
     ),
   );
 }
}

האפליקציה מציגה את הווידג'ט WidgetTree כיוון שהוא נתיב route הבית של האפליקציה.

הווידג'ט מפנה למסך AuthScreen כפי שאפשר לראות:

Flutter + Firebase Auth app first look

  • כך זה נראה כי אין עיצוב. גם בזה נטפל.

נשתמש במסך AuthScreen לרישום משתמשים חדשים, כניסת משתמשים קיימים ושליחת מייל תזכורת כיוון ששלושת המסכים דורשים פחות או יותר את אותם האלמנטים. ניצור את המסך בתוך הקובץ auth_screen.dart:

lib/screens/auth_screen.dart

import 'package:flutter/material.dart';
import 'package:firebase_auth/firebase_auth.dart';
import 'package:provider/provider.dart';


import '../providers/auth_provider.dart';


enum ScreenMode { login, register, reset }


class AuthScreen extends StatefulWidget {
 const AuthScreen({super.key});


 @override
 State<AuthScreen> createState() => _AuthScreenState();
}


class _AuthScreenState extends State<AuthScreen> {
    Widget build(BuildContext context) {
        return Scaffold(
            body: Container(
            
             ),
        );
    }
}

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

lib/screens/auth_screen.dart

enum ScreenMode { login, register, reset }

בתוך הקלאס AuthScreenState נוסיף מתודה שתאפשר את שינוי מצב המסך :

lib/screens/auth_screen.dart

ScreenMode _screenMode = ScreenMode.login;


void _changeScreenMode(ScreenMode mode) {
   setState(() {
     _screenMode = mode;
   });
}

בפעם הראשונה בה משתמשים נכנסים למסך יופיע מצב login כברירת מחדל. נגדיר את זה במתודה initState:

lib/screens/auth_screen.dart

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


   _changeScreenMode(ScreenMode.login);
}

נגדיר פונקציה appBar פרטית שתשנה את ה-AppBar של ה-Scaffold בהתאם למצב המסך screenMode:

lib/screens/auth_screen.dart

AppBar _appBar() {
   var txt = 'Login Page';
   if (_screenMode == ScreenMode.register) {
     txt = "Register";
   } else if (_screenMode == ScreenMode.reset) {
     txt = "Reset Password";
   }
   return AppBar(
     backgroundColor: Theme.of(context).primaryColor,
     title: Text(txt),
   );
}

נקרא לפונקציה appBar מתוך ה-Scaffold:

lib/screens/auth_screen.dart

Widget build(BuildContext context) {
  return Scaffold(
    appBar: _appBar(),
    body: Container(
      height: double.infinity,
       width: double.infinity,
       padding: const EdgeInsets.all(20),
       child: Column(children: [Text('Placeholder')]),
    ),
  );
}

Flutter + Firebase Auth app second look

נכין את השטח להוספת טופס זיהוי המשתמש.

ראשית ניצור מפתח ייחודי שיאפשר ל-Flutter לעשות את הוולידציה בשבילנו:

lib/screens/auth_screen.dart

final _formKey = GlobalKey<FormState>();

נוסיף קונטרולרים בשביל שדות הטופס, אימייל וסיסמה:

lib/screens/auth_screen.dart

// form fields controllers
final _emailController = TextEditingController();
final _passwordController = TextEditingController();

למניעת memory leakage נדאג לשחרר את הקונטרולרים רגע לפני שהווידג'ט מסיים את חייו:

lib/screens/auth_screen.dart

@override
void dispose() {
   // to prevent memory leakage
   // dispose controllers
   _emailController.dispose();
   _passwordController.dispose();


   super.dispose();
}

נגדיר שדות focus בהם נעזר כדי לאפשר למשתמש לעבור בין השדות בלחיצה על המקלדת:

lib/screens/auth_screen.dart

// focus nodes
late FocusNode _emailFocusNode;
late FocusNode _passwordFocusNode;

נגדיר את שדות הפוקוס מיד עם תחילת הפעולה של הווידג'ט בתוך המתודה initState:

lib/screens/auth_screen.dart

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




   _changeScreenMode(ScreenMode.login);


   // focus nodes
   _emailFocusNode = FocusNode();
   _passwordFocusNode = FocusNode();
}

נפטר משדות הפוקוס רגע לפני שהווידג'ט יוצא מפעולה בתוך המתודה dispose:

lib/screens/auth_screen.dart

@override
void dispose() {
   // to prevent memory leakage
   // dispose controllers
   _emailController.dispose();
   _passwordController.dispose();


   // focus nodes
   _emailFocusNode.dispose();
   _passwordFocusNode.dispose();


   super.dispose();
}

נוסיף מתודה setAlert שתחליף את ההודעות המוצגות למשתמש:

lib/screens/auth_screen.dart

var _showProgress = false;


var _alertText = 'Enter email and password';
var _alertColor = Colors.blue;


void _setAlert({txt = 'Enter email and password', color = Colors.blue}) {
   setState(() {
     _alertColor = color;
     _alertText = txt;
   });
}

נוסיף משתנה בוליאני showProgress. אם ערכו true תוצג טעינה:

lib/screens/auth_screen.dart

var _showProgress = false;

נוסיף לתוך ה-Container ווידג'ט מסוג טופס Form:

lib/screens/auth_screen.dart

Widget build(BuildContext context) {
        return Scaffold(
            appBar: _appBar(),
            body: Container(
                height: double.infinity,
                width: double.infinity,
                padding: const EdgeInsets.all(20),
                child: Form(
              key: _formKey,
              child: SingleChildScrollView(
                child: Column(
                  crossAxisAlignment: CrossAxisAlignment.center,
                        mainAxisAlignment: MainAxisAlignment.center,
                        children: <Widget>[
                    // form fields
                        ],
                ),
              ),
                ),
            ),
        );
}

נוסיף פונקציה register שתתקשר את המידע שמזינים המשתמשים לטופס עם ה-provider:

lib/screens/auth_screen.dart

Future<void> _register() async {
   setState(() {
     _showProgress = true;
   });


   try {
     final res = await Provider.of<AuthProvider>(context, listen: false)
         .createUser(
             email: _emailController.text.trim(),
             password: _passwordController.text.trim());
     if (res != 'success') {
       throw res;
     }
   } catch (errMsg) {
     setState(() {
       _setAlert(txt: errMsg.toString(), color: Colors.red);
     });
   } finally {
     if (mounted) {
       setState(() {
         _showProgress = false;
       });
     }
   }
}

קודם שנוסיף את האלמנטים אשר בטופס בתור children לתוך ה-Column נגדיר מספר ווידג'טים ומתודות שבסופו של דבר יהוו את האלמנטים.

ניצור קובץ חדש שיאכלס את הווידג'טים. נקרא לו utils:

lib/widgets/utils.dart

import 'package:flutter/material.dart';

בתוך הקובץ נגדיר מראה אחיד לשדות הטופס באמצעות:

lib/widgets/utils.dart

InputDecoration textInputDecoration = InputDecoration(
  labelStyle: const TextStyle(color: Colors.black, fontWeight: FontWeight.w300),
  focusedBorder: OutlineInputBorder(
    borderSide: BorderSide(
      color: Colors.grey.shade700,
      width: 2,
    ),
  ),
  enabledBorder: OutlineInputBorder(
    borderSide: BorderSide(
      color: Colors.grey.shade700,
      width: 2,
    ),
  ),
  errorBorder: const OutlineInputBorder(
    borderSide: BorderSide(
      color: Colors.red,
      width: 2,
    ),
  ),
);

גם כן בתוך הקובץ utils נגדיר ווידג'ט EmailFormFieldWidget בשביל שדה טופס מסוג Email:

lib/widgets/utils.dart

class EmailFormFieldWidget extends StatelessWidget {
 const EmailFormFieldWidget(
     {super.key,
     required this.controller,
     required this.action,
     required this.focus,
     this.focusNext});


 final TextEditingController controller;
 final TextInputAction action;
 final FocusNode focus;
 final FocusNode? focusNext;


 @override
 Widget build(BuildContext context) {
   return TextFormField(
     controller: controller,
     textInputAction: action,
     focusNode: focus,
     onFieldSubmitted: (_) {
       if (focusNext == null) return;
       FocusScope.of(context).requestFocus(focusNext);
     },
     decoration: textInputDecoration.copyWith(
       labelText: "Email",
       prefixIcon: Icon(
         Icons.email,
         color: Theme.of(context).primaryColor,
       ),
     ),
     keyboardType: TextInputType.emailAddress,
     validator: (String? value) {
       return value != null &&
               RegExp(r'^(([^<>()[].,;:s@"]+(.[^<>()[].,;:s@"]+)*)|(".+"))@(([[0-9]{1,3}.[0-9]{1,3}.[0-9]{1,3}.[0-9]{1,3}])|(([a-zA-Z-0-9]+.)+[a-zA-Z]{2,}))$')
                   .hasMatch(value)
           ? null
           : "Email address is wrong or malformed.";
     },
   );
 }
}

נגדיר ווידג'ט PasswordFormFieldWidget בשביל שדה מסוג Password:

lib/widgets/utils.dart

class PasswordFormFieldWidget extends StatelessWidget {
 const PasswordFormFieldWidget({
   super.key,
   required this.controller,
   required this.focus,
 });


 final TextEditingController controller;
 final FocusNode focus;


 @override
 Widget build(BuildContext context) {
   return TextFormField(
     controller: controller,
     focusNode: focus,
     textInputAction: TextInputAction.done,
     decoration: textInputDecoration.copyWith(
       labelText: "Password",
       prefixIcon: Icon(
         Icons.lock,
         color: Theme.of(context).primaryColor,
       ),
     ),
     obscureText: true,
     validator: (String? value) {
       return (value != null && value.length >= 6)
           ? null
           : 'At least 6 characters';
     },
   );
 }
}

נגדיר ווידג'ט שיציג את הטעינה:

lib/widgets/utils.dart

Widget getCircularProgressIndicator(show) {
 if (show) return const Center(child: CircularProgressIndicator());
 return const SizedBox.shrink();
}

נייבא את utils לתוך הקובץ auth_screen:

lib/screens/auth_screen.dart

import 'package:flutter/material.dart';


import '../widgets/utils.dart';

כפתור השליחה, submit button, צריך להשתנות בהתאם לסוג הטופס. נקצה לכפתור ווידג'ט נפרד:

lib/screens/auth_screen.dart

Widget _submitButton() {
   var txt = 'Login';
   if (_screenMode == ScreenMode.register) {
     txt = "Register";
   } else if (_screenMode == ScreenMode.reset) {
     txt = "Reset Password";
   }
   return ElevatedButton.icon(
     onPressed: _submit,
     icon: const Icon(Icons.app_registration),
     label: Text(txt, style: const TextStyle(fontSize: 20)),
     style: elevatedButtonStyle.style,
   );
}

נגדיר את מראה הכפתור בתוך utils:

lib/widgets/utils.dart

ElevatedButtonThemeData elevatedButtonStyle = ElevatedButtonThemeData(
 style: ElevatedButton.styleFrom(
   //side: const BorderSide(color: Colors.black, width: 2.5),
   backgroundColor: Colors.blue,
   minimumSize: const Size.fromHeight(50),
 ),
);

כפתור השליחה submitButton קורא לפונקציה submit שעושה ולידציה לטופס ואחר כך קוראת לאחת הפונקציות המשמשות להרשמה, כניסת משתמש או איפוס סיסמה. בשלב זה, הפונקציה תקרא לפונקציה register:

lib/screens/auth_screen.dart

void _submit() {
   _formKey.currentState!.save();
   if (!_formKey.currentState!.validate()) {
     return;
   }
   if (_screenMode == ScreenMode.login) {
     //_signIn();
   } else if (_screenMode == ScreenMode.register) {
     _register();
   } else if (_screenMode == ScreenMode.reset) {
     //_resetPassword();
   }
}

נוסיף לטופס את הווידג'טים שהכנו מראש בתור ה- children של ה-Column:

lib/screens/auth_screen.dart

Column(
  crossAxisAlignment: CrossAxisAlignment.center,
  mainAxisAlignment: MainAxisAlignment.center,
  children: <Widget>[
    if (_showProgress) getCircularProgressIndicator(_showProgress),
    const SizedBox(height: 16),
    EmailFormFieldWidget(
      controller: _emailController,
      focus: _emailFocusNode,
      focusNext: _screenMode != ScreenMode.reset
                ? _passwordFocusNode
                : null,
       action: _screenMode != ScreenMode.reset
              ? TextInputAction.done
              : TextInputAction.next),
    if (_screenMode != ScreenMode.reset) const SizedBox(height: 10),
    if (_screenMode != ScreenMode.reset)
      PasswordFormFieldWidget(
        controller: _passwordController,
        focus: _passwordFocusNode,
      ),
    const SizedBox(height: 20),
    _submitButton(),
    _switchFormLinks()
      ],
),
  • לחיצה על submit תפעיל את המתודה _register שתשלח את הפרטים אותם מילא המשתמש בטופס לטיפול המודל.

מה שחסר לטופס הוא הווידג'ט switchFormLinks אשר יחליף את תוכן הקישורים בתחתית הטופס על פי סוג הטופס בו בחר המשתמש (הרשמה, כניסת משתמש או איפוס סיסמה):

lib/screens/auth_screen.dart

Widget _switchFormLinks() {
   return Row(
     children: [
       if (_screenMode == ScreenMode.login || _screenMode == ScreenMode.reset)
         Flexible(
           flex: 1,
           child: TextButton(
             onPressed: () {
               _changeScreenMode(ScreenMode.register);
               _setAlert(txt: 'Enter email and password', color: Colors.blue);
             },
             child: const Text('Register'),
           ),
         ),
       if (_screenMode == ScreenMode.register ||
           _screenMode == ScreenMode.reset)
         Flexible(
           flex: 1,
           child: TextButton(
             onPressed: () {
               _changeScreenMode(ScreenMode.login);
               _setAlert(txt: 'Enter email and password', color: Colors.blue);
             },
             child: const Text('Login'),
           ),
         ),
       if (_screenMode == ScreenMode.login ||
           _screenMode == ScreenMode.register)
         Flexible(
           flex: 1,
           child: TextButton(
             onPressed: () {
               _changeScreenMode(ScreenMode.reset);
               _setAlert(
                   txt: 'Enter the email you have registered with',
                   color: Colors.blue);
             },
             child: const Text('Reset password'),
           ),
         ),
     ],
   );
}

 

רישום המשתמש במסד הנתונים

חשוב לרשום את המשתמש במסד נתונים כדי שנוכל לשמור עליו מידע שהוא מעבר לשדות הסטנדרטיים שמספק שירות Firebase Auth.

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

$ flutter pub add cloud_firestore

ב-provider נוסיף את מצביע על הקולקציה "users" במסד הנתונים:

lib/providers/auth_provider.dart

class AuthProvider extends ChangeNotifier {
  //  reference to the users collection
  final CollectionReference _usersCollection =
      FirebaseFirestore.instance.collection("users");


  // the rest of the provider class
}

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

lib/models/app_user_model.dart

class AppUser {
  String? key;
  AppUserData appUserData;

  AppUser({this.key, required this.appUserData});
}

class AppUserData {
  String userId = '';
  String name = '';
  String email = '';
  List<String> cars = [];
  late DateTime date;

  AppUserData(
      {required this.userId,
      required this.name,
      required this.email,
      required this.cars,
      required this.date});

  AppUserData.fromJson(Map<String, dynamic> json) {
    userId = json["uid"];
    name = json["name"];
    email = json["email"];
    cars = (json["cars"] as List).map((item) => item as String).toList();
    date = DateTime.fromMillisecondsSinceEpoch(json["updated_at"]);
  }
}
  • המתודה fromJson תשמש בהמשך למפות את המידע שנמשוך ממסד הנתונים לסוג מידע AppUser אותו הגדרנו באמצעות הקלאס.

חזרה ל-provider.

נוסיף למתודה createUser תנאי שאם המערכת הצליחה לפתוח משתמש ע"ג שירות Firebase Auth נפתח לו רשומה חדשה בקולקציה של המשתמשים במסד הנתונים:

User? user = (await _firebaseAuth.createUserWithEmailAndPassword(
              email: email, password: password))
          .user;

if (user != null) {
        // update the user data
        bool res = await updateUserData(user.uid, email);
}

כך תראה עכשיו המתודה createUser בעקבות השינויים שעשינו בקוד:

lib/providers/auth_provider.dart

Future<String> createUser(
      {required String email, required String password}) async {
    try {
      User? user = (await _firebaseAuth.createUserWithEmailAndPassword(
              email: email, password: password))
          .user;

      if (user != null) {
        // update the user data
        bool res = await updateUserData(user.uid, email);

        if (res == false) {
          return _failMsg;
        }

        final currentUser = FirebaseAuth.instance.currentUser;

        if (currentUser != null && !currentUser.emailVerified) {
          await user.sendEmailVerification();
        }

        return "success";
      }

      return _failMsg;
    } on FirebaseAuthException catch (e) {
      //return e.message.toString();
      var msgToUser = 'Something is wrong :-(';
      if (e.code == 'weak-password') {
        msgToUser = 'The password is too weak.';
      } else if (e.code == 'email-already-in-use') {
        msgToUser = 'The account already exists.';
      } else if (e.message.runtimeType == String) {
        msgToUser = e.message!;
      }
      return msgToUser;
    }
}
  • הרשמה ל-Firebase Auth באמצעות שם משתמש וסיסמה.
  • פתיחת רשומה חדשה במסד הנתונים עם פרטי המשתמש.
  • שליחת מייל אישור הרשמה למשתמש.
  • אם הקוד הצליח אז להחזיר את המחרוזת "success".
  • אם התהליך נכשל אז להחזיר הודעות שגיאה.

נוסיף את המתודה שמעדכנת בפועל את מסד הנתונים לתוך ה-provider:

lib/providers/auth_provider.dart

// update the userdata in the FireStore database
  Future<bool> updateUserData(String userId, String email,
      {String profilePic = "",
      String userName = "",
      List<String> carsKeys = const []}) async {
    try {
      final Map<String, dynamic> data = {
        "uid": userId,
        "email": email,
        "name": userName,
        "profile_pic": profilePic,
        "cars": carsKeys,
        "updated_at": DateTime.now().millisecondsSinceEpoch,
      };

      await _usersCollection.doc(userId).set(data);
      return true;
    } catch (e) {
      return false;
    }
}

 

מדריכים נוספים בסדרת מדריכי ה- Flutter שעשויים לעניין אותך

CRUD באפליקציית Flutter - מדריך ראשון : כתיבה למסד נתונים

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

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

 

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

 

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

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

 

 

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

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

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

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

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

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

 

 

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

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