كيفية إنشاء صفحة Register و login في تطبيقات Flutter باستخدام Firebase وتخزين البيانات في firestore

كيفية إنشاء صفحة Register و login في تطبيقات Flutter باستخدام Firebase وتخزين البيانات في firestore

كيفية إنشاء صفحة Register و login في تطبيقات Flutter باستخدام Firebase وتخزين البيانات في firestore


استكمالا لدورة التعامل مع الفايربيز باستخدام فلاتر نقدم لكم اول درس فعلي وهو كيفية القيام بعملية تسجيل حساب وتسجيل الدخول داخل التطبيق الخاص بك باستخدام تقنية فلاتر مع الfirebase والطريقة بسيطه جدا وابسط مما تتخيل ولكن عليك فقط التركيز في المقاله وفهم ما يتم ذكرة في هذه المقاله لكي لا يحدث معك مشاكل وتكون قادر على فهم الاليه التي بها الfirebase .


تعرف على كيفية إنشاء تطبيقات لنظامي التشغيل Android و iOS باستخدام Flutter ، إطار عمل برمجة الأجهزة المحمولة متعدد الأنظمة الأساسية الرائد من Google. يوضح لك احمد محمود من جي كودرس كيفية النهوض والركض مع Flutter بسرعة وفعالية في هذه الدورة.


إنشاء Model  يحمل جميع بيانات المستخدمين

عندما تريد ارسال list of data يفضل ان تقوم بعمل Model خاص بالبيانات لكي يسهل عليك عملية نقل البيانات دفعه واحده دون تمرير البيانات واحد تلو الاخر سواء في عملية الاستدعاء او في عملية الارسال لهذا قمنا بعمل model يحمل البيانات التي تخص المستخدم والتي سوف نقوم بإرسالها في firebase .


إنشاء Model  يحمل جميع بيانات المستخدمين

UserModel.dart


class UserModel {
  String? name;
  String? email;
  String? phone;
  String? uId;
  String? imageUrl;

  UserModel({
    this.name,
    this.email,
    this.phone,
    this.uId,
    this.imageUrl,
  });

  UserModel.fromMap(Map<String, dynamic> json) {
    name = json['name'];
    email = json['email'];
    phone = json['phone'];
    uId = json['uId'];
    imageUrl = json['imageUrl'];
  }

  Map<String, dynamic> toMap() => {
    'name': name,
    'email': email,
    'phone': phone,
    'uId': uId,
    'imageUrl': imageUrl,
  };
}


اضافات مكتبات الfirebase

الان قم باضافة المكتبات التاليه في المكان المخصص لها داخل pubspec.yaml لكي تستطيع التعامل مع خواص الفايزبيز .


  #firebase

  firebase_core: ^1.16.0

  firebase_auth: ^3.3.17

  firebase_storage: ^10.2.15

  cloud_firestore: ^3.1.14


شرح كيفية رفع البيانات والاميل وكلمة المرور الخاص بالمستخدم داخل الfirebase

يتم ارفاق الاكواد التاليه بداخل ملف cubit وهي عباره عن استقبال للاميل والرقم السري والاسم والهاتف وبعد ذلك سوف نقوم بإنشاء حساب عن طريق الاميل والرقم السري للمستخدم بعد ان يتم انشاء الحساب نقوم بعمل void اخرى تستقبل الid والاسم والاميل ورقم الهاتف لكي نقوم بتخزينها داخل firestore بعدها يتم استخدام الموديل الذي قمنا به ووضع البيانات التي نريدها وهيا الاميل والid والاسم ورقم الهاتف وبعدها يتم وضع set داخل Location يحمل الmodel وبعد ان يتم الرفع نقوم بعمل void اخرى تخص الحصول على البيانات وهي تقوم بالانتقال الlocation وتحصل على البيانات ويتم تخزينها داخل userModel ليتم تعبئة الmodel بالبيانات , ايضا بعد عملية تسجيل الدخول وهي void منفصله يتم فيها الحصول على البيانات بعد ادخال اسم المستخدم وكلمة المرور بشكل صحيح .


شرح كيفية رفع البيانات والاميل وكلمة المرور الخاص بالمستخدم داخل الfirebase

cubit.dart


 void userRegister ({
    required String email,
    required String password,
    required String name,
    required String phone,
  }) async {
    emit(RegisterLoadingState());

    await FirebaseAuth.instance
        .createUserWithEmailAndPassword(
        email: email,
        password: password)
        .then((value) {
      print(value.user!.email);
      print(value.user!.uid);
      userCreate(
        uId: value.user!.uid,
        email: email,
        phone: phone,
        name: name,
      );
    }).catchError((error) {
      emit(Error(error.toString()));
    });
  }

  void userCreate({
    required String email,
    required String uId,
    required String name,
    required String phone,
  }) async {

    UserModel userModel = UserModel(
      email: email,
      name: name,
      phone: phone,
      uId: uId,
    );

    await FirebaseFirestore.instance
        .collection('users')
        .doc(uId)
        .set(userModel.toMap())
        .then((value) {
      sl<CacheHelper>().put('uId', uId);
      getUserDate();
      emit(CreateUserSuccessState());
    }).catchError((error) {
      emit(Error(error.toString()));
    });
  }

  void userLogin({
    required String email,
    required String password,
  }) {
    emit(LoginLoadingState());
    FirebaseAuth.instance
        .signInWithEmailAndPassword(
        email:email,
        password: password)
        .then((value) {
      print(value.user!.email);
      print(value.user!.uid);
      uId = value.user!.uid;
      getUserDate();
    }).catchError((error) {
      emit(Error(error.toString()));
    });
  }

  UserModel? userModel;
  void getUserDate() {
    emit(HomeGetUserLoadingState());
    FirebaseFirestore.instance.collection('users').doc(uId).get().then((value) {
      userModel = UserModel.fromMap(value.data()!);
      print(FirebaseAuth.instance.currentUser!.emailVerified);
      emit(HomeGetUserSuccessState());
    }).catchError((error) {
      print(error.toString());
      emit(Error(error));
    });
  }
  


تصميم لصفحة login بإستخدام flutter

في هذه الصفحة عباره عن مكان لوضع الاميل ومكان اخر لوضع كلمة المرور وزر login وعندما يتم ادخال بيانات يظهر الزر , ونقوم باستدعاء الmethode من الcubit ونرسل لها الاميل والرقم السري .


تصميم لصفحة login بإستخدام flutter

login.dart


import 'package:flutter/material.dart';
import 'package:flutter_bloc/flutter_bloc.dart';
import 'package:hexcolor/hexcolor.dart';
import 'package:lottie/lottie.dart';
import 'package:social_firebase/core/constants.dart';
import 'package:social_firebase/core/cubit/cubit.dart';
import 'package:social_firebase/core/cubit/state.dart';
import 'package:social_firebase/core/widget/main_scaffold.dart';
import 'package:social_firebase/features/home/page/homePage.dart';
import 'package:social_firebase/features/register/page/register.dart';
import 'package:social_firebase/features/register/wedget/app_button.dart';
import 'package:social_firebase/features/register/wedget/app_text_form_field.dart';

class LoginPage extends StatefulWidget {
  const LoginPage({Key? key}) : super(key: key);

  @override
  State<LoginPage> createState() => _LoginPageState();
}

class _LoginPageState extends State<LoginPage> {
  GlobalKey<FormState> formKey = GlobalKey<FormState>();
  TextEditingController emailController = TextEditingController();
  TextEditingController passwordController = TextEditingController();
  bool isDisabled = true;
  String btn = 'login';

  @override
  Widget build(BuildContext context) {
    return BlocConsumer<MainBloc, MainState>(
      listener: (context, state) {
        if (state is LoginSuccessState) {
          navigateAndFinish(context, const HomeScreen());
        }
      },
      builder: (context, state) {
        return MainScaffold(
          scaffold: Scaffold(
            appBar: AppBar(
              title: Text(
                "Login Page",
                style: Theme.of(context).textTheme.headline6,
              ),
              centerTitle: true,
            ),
            body: Padding(
              padding: const EdgeInsets.all(15.0),
              child: SingleChildScrollView(
                child: Column(
                  children: [
                    Lottie.asset('assets/images/login.json',
                        width: 200, height: 200),
                    AppTextFormField(
                      hint: 'email',
                      icon: const Icon(Icons.email_rounded),
                      label: 'email',
                      callbackHandle: (controller) {
                        emailController = controller;
                      },
                      type: TextInputType.text,
                      textInputAction: TextInputAction.next,
                      onChanged: (value) {
                        enableLoginButton();
                      },
                    ),
                    space10Vertical,
                    AppTextFormField(
                      hint: 'password',
                      isPassword: true,
                      label: 'password',
                      callbackHandle: (controller) {
                        passwordController = controller;
                      },
                      type: TextInputType.text,
                      textInputAction: TextInputAction.next,
                      onChanged: (value) {
                        enableLoginButton();
                      },
                    ),
                    space30Vertical,
                    AppButton(
                      onPress: !isDisabled
                          ? () {
                              MainBloc.get(context).userLogin(
                                  email: emailController.text,
                                  password: passwordController.text);
                            }
                          : null,
                      label: btn,
                    ),
                    space10Vertical,
                    AppButton(
                      onPress: () {
                        navigateTo(context, const RegisterPage());
                      },
                      color: cyanClr,
                      label: 'Register',
                    )
                  ],
                ),
              ),
            ),
          ),
        );
      },
    );
  }

  void enableLoginButton() {
    if (emailController.text.isNotEmpty && passwordController.text.isNotEmpty) {
      isDisabled = false;
      setState(() {});
    } else {
      isDisabled = true;
      setState(() {});
    }
  }
}


تصميم لصفحة register بواسطة flutter

هذا التصميم عباره عن مجموعة من الخصائص وهي مربع لكتابة اسم المستخدم واخر لكلمة المرور واعادة كلمة المرور و رقم الهاتف والاميل ومكان اخر لاختيار الصورة عندما يتم النقر عليه يتم فتح الاستوديو وبعدها تستطيع اختيار صورة وبعدها يصبح لدينا صورة والمسار الخاص بها وهذا ما سيتم رفعه على السيرفر , وبعد ادخال البيانات تم النقر على الزر وبعدها تنتظر الى ان يتم رفع البيانات على السيرفر .


تصميم لصفحة register بواسطة flutter

register.dart


import 'package:flutter/material.dart';
import 'package:flutter_bloc/flutter_bloc.dart';
import 'package:hexcolor/hexcolor.dart';
import 'package:lottie/lottie.dart';
import 'package:social_firebase/core/constants.dart';
import 'package:social_firebase/core/cubit/cubit.dart';
import 'package:social_firebase/core/cubit/state.dart';
import 'package:social_firebase/core/di/injection.dart';
import 'package:social_firebase/core/network/local/cache_helper.dart';
import 'package:social_firebase/core/widget/main_scaffold.dart';
import 'package:social_firebase/features/home/page/homePage.dart';
import 'package:social_firebase/features/register/splash_screen/splash_screen.dart';
import 'package:social_firebase/features/register/wedget/app_button.dart';
import 'package:social_firebase/features/register/wedget/app_text_form_field.dart';

class RegisterPage extends StatefulWidget {
  const RegisterPage({Key? key}) : super(key: key);

  @override
  State<RegisterPage> createState() => _RegisterPageState();
}

class _RegisterPageState extends State<RegisterPage> {
  GlobalKey<FormState> formKey = GlobalKey<FormState>();
  TextEditingController emailController = TextEditingController();
  TextEditingController passwordController = TextEditingController();
  TextEditingController usernameController = TextEditingController();
  TextEditingController phoneController = TextEditingController();
  TextEditingController confirmPasswordController = TextEditingController();

  bool isDisabled = true;
  String btn = 'create account';

  @override
  Widget build(BuildContext context) {
    return BlocListener<MainBloc, MainState>(
      listener: (context, state) {
        if(state is CreateUserSuccessState) {
          // sl<CacheHelper>().put('uId', uId);
          navigateAndFinish(context, const SplashScreen());
          return;
        }
        if (state is Error) {
          print(state.error);
          SnackBar snackBar =
          SnackBar(
            content: Text(state.error),action:
          SnackBarAction(
            label: 'ok',
            onPressed: (){},
          ),);
          ScaffoldMessenger.of(context).showSnackBar(snackBar);
          return;
        }
      },
      child: MainScaffold(
        scaffold: Scaffold(
          appBar: AppBar(
            title: Text("Register Page" ,style: Theme.of(context).textTheme.headline6,),
            centerTitle: true,
          ),
          body: Padding(
            padding: const EdgeInsets.all(15.0),
            child: SingleChildScrollView(
              physics: const BouncingScrollPhysics(),
              child: Column(
                children: [
                  Lottie.asset('assets/images/register.json',
                  width: 200,height: 200),
                  AppTextFormField(
                    hint: 'username',
                    icon: const Icon(Icons.person_add_alt_1_rounded),
                    label: 'username',
                    callbackHandle: (controller) {
                      usernameController = controller;
                    },
                    type: TextInputType.text,
                    textInputAction: TextInputAction.next,
                    onChanged: (value) {
                      enableLoginButton();
                    },
                  ),
                  space10Vertical,
                  AppTextFormField(
                    hint: 'email',
                    icon: const Icon(Icons.email_rounded),
                    label: 'email',
                    callbackHandle: (controller) {
                      emailController = controller;
                    },
                    type: TextInputType.text,
                    textInputAction: TextInputAction.next,
                    onChanged: (value) {
                      enableLoginButton();
                    },
                  ),
                  space10Vertical,
                  AppTextFormField(
                    hint: 'phone',
                    icon: const Icon(Icons.phone_android_rounded),
                    label: 'phone',
                    callbackHandle: (controller) {
                      phoneController = controller;
                    },
                    type: TextInputType.number,
                    textInputAction: TextInputAction.next,
                    onChanged: (value) {
                      enableLoginButton();
                    },
                  ),
                  space10Vertical,
                  AppTextFormField(
                    hint: 'password',
                    isPassword: true,
                    label: 'password',
                    callbackHandle: (controller) {
                      passwordController = controller;
                    },
                    type: TextInputType.text,
                    textInputAction: TextInputAction.next,
                    onChanged: (value) {
                      enableLoginButton();
                    },
                  ),
                  space10Vertical,
                  AppTextFormField(
                    hint: 'confirm password',
                    isPassword: true,
                    label: 'confirm password',
                    callbackHandle: (controller) {
                      confirmPasswordController = controller;
                    },
                    type: TextInputType.text,
                    textInputAction: TextInputAction.next,
                    onChanged: (value) {
                      enableLoginButton();
                    },
                  ),
                  space10Vertical,
                  AppButton(
                    onPress: !isDisabled ? () {
                      if (passwordController.text != confirmPasswordController.text) {
                          SnackBar snackBar =
                          SnackBar(
                            content: const Text("password and Confirm Password don't match'"),action:
                            SnackBarAction(
                              label: 'ok',
                              onPressed: (){},
                            ),);
                          ScaffoldMessenger.of(context).showSnackBar(snackBar);
                          return;
                      }
                      if (passwordController.text.length < 6) {
                        SnackBar snackBarPass =
                        SnackBar(
                          content: const Text("password should be at least 6 char"),action:
                        SnackBarAction(
                          label: 'ok',
                          onPressed: (){},
                        ),);
                        ScaffoldMessenger.of(context).showSnackBar(snackBarPass);
                        return;
                      }
                      MainBloc.get(context).userRegister
                        (email: emailController.text,
                          password: passwordController.text,
                          name: usernameController.text,
                          phone: phoneController.text);

                    } : null,
                    label: btn,

                  )
                ],
              ),
            ),
          ),
        ),
      ),
    );
  }

  void enableLoginButton() {
    if (emailController.text.isNotEmpty &&
        passwordController.text.isNotEmpty &&
        confirmPasswordController.text.isNotEmpty &&
        phoneController.text.isNotEmpty &&
        usernameController.text.isNotEmpty) {
      isDisabled = false;
      setState(() {});
    } else {
      isDisabled = true;
      setState(() {});
    }
  }

}

The widgets used in the project ---------------------

AppButton.dart

import 'dart:ui';

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

import '../../../core/constants.dart';

class AppButton extends StatelessWidget {
  final String label;
  final VoidCallback? onPress;
  final double width;
  double height;
  Color? color;
  Color? textColor;

  AppButton({
    Key? key,
    required this.label,
    required this.onPress,
    this.width = double.infinity,
    this.height = 50.0,
    this.color,
    this.textColor,
  }) : super(key: key);

  @override
  Widget build(BuildContext context) {
    return Container(
      width: width,
      height: height,
      decoration: BoxDecoration(
        borderRadius: BorderRadius.circular(
          10.0,
        ),
      ),
      clipBehavior: Clip.antiAliasWithSaveLayer,
      child: MaterialButton(
        enableFeedback: true,
        elevation: 50.0,
        hoverElevation: 150.0,
        color: color ?? Theme.of(context).primaryColor,
        onPressed: onPress,
        disabledColor: HexColor(disableButton),
        child: Text(
          label,
          style: Theme.of(context).textTheme.subtitle2!.copyWith(
            color: textColor == null ? HexColor(surface) : textColor,
          ),
        ),
      ),
    );
  }
}

AppTextFormField.dart

import 'package:flutter/material.dart';
import 'package:flutter_bloc/flutter_bloc.dart';
import 'package:hexcolor/hexcolor.dart';
import 'package:social_firebase/core/constants.dart';
import 'package:social_firebase/core/cubit/cubit.dart';
import 'package:social_firebase/core/cubit/state.dart';

class AppTextFormField extends StatefulWidget {
  final String label;
  final Widget? icon;
  final String hint;
  final bool isPassword;
  final Function callbackHandle;
  final ValueChanged<String>? onChanged;
  final TextInputType type;
  final TextInputAction? textInputAction;
  final bool? readOnly;
  final bool? enabled;
  final ValueChanged<String>? onSubmit;


  const AppTextFormField({
    Key? key,
    this.label = '',
    this.icon,
    this.readOnly = false,
    this.enabled = true,
    this.textInputAction,
    required this.hint,
    this.type = TextInputType.text,
    this.isPassword = false,
    this.onChanged,
    this.onSubmit,
    required this.callbackHandle,
  }) : super(key: key);

  @override
  State<AppTextFormField> createState() => _AppTextFormFieldState();
}

class _AppTextFormFieldState extends State<AppTextFormField> {
  final TextEditingController textEditingController = TextEditingController();
  bool isShown = true;

  @override
  Widget build(BuildContext context) {
    widget.callbackHandle(textEditingController);

    return BlocBuilder<MainBloc, MainState>(
      builder: (BuildContext context, state) {
        return Container(
          decoration: BoxDecoration(
            borderRadius: BorderRadius.circular(8.0),
            color: HexColor(greyWhite),
          ),
          child: TextFormField(
            style: Theme.of(context).textTheme.subtitle2!.copyWith(
              color: secondaryVariant,
            ),
            keyboardType: widget.type,
            enabled:widget.enabled!,
            readOnly: widget.readOnly!,
            controller: textEditingController,
            obscureText: widget.isPassword ? isShown : false,
            onChanged: widget.onChanged,
            onFieldSubmitted: widget.onSubmit,
            textInputAction: widget.textInputAction,
            decoration: InputDecoration(
              border: OutlineInputBorder(
                borderSide: BorderSide(
                  color: MainBloc.get(context).isDark
                      ? HexColor(grey)
                      : secondaryVariant,
                ),
                borderRadius: BorderRadius.circular(8.0),
              ),
              enabledBorder: const OutlineInputBorder(
                borderSide: BorderSide.none,
              ),
              disabledBorder: const OutlineInputBorder(
                borderSide: BorderSide.none,
              ),
              errorBorder: const OutlineInputBorder(
                borderSide: BorderSide.none,
              ),
              focusedErrorBorder: const OutlineInputBorder(
                borderSide: BorderSide.none,
              ),
              hintStyle: Theme.of(context).textTheme.subtitle2!.copyWith(
                color: HexColor(textFormFiledColor),
              ),
              hintText: widget.hint,
              contentPadding: const EdgeInsetsDirectional.only(
                start: 15.0,
              ),
              suffixIcon: widget.isPassword
                  ? IconButton(
                padding: const EdgeInsets.all(15.0),
                onPressed: () {
                  setState(() {
                    isShown = !isShown;
                  });
                },
                icon: isShown
                      ? const Icon(Icons.visibility_off)
                      : const Icon(Icons.visibility),

              )
                  : widget.icon == null
                  ? null
                  : Padding(
                padding: const EdgeInsetsDirectional.all(12.0),
                child: widget.icon,
              ),
            ),
          ),
        );
      },
    );
  }
}

فيديو الشرح



مزيد من المقالات

تعليقات