본문 바로가기

Flutter/11 Quiz

[Flutter] Quiz - level3(Getx login)

이번에는 코딩셰프 도장깨기 레벨2 입니다.

개발환경 : 윈도우11, 안드로이드 스튜디오, flutter 3.0.1

소스코드 위치 - Release 02_done · mike-bskim/dojang_level3 · GitHub

 

Release 02_done · mike-bskim/dojang_level3

 

github.com

 

 

 

미션은 아래와 같습니다.

 

1. lgoin 기능 구현
2. 웰컴 페이지 이동시 인디케이터 추가
3. 사용자 이메일 주소를 윌컴 페이지에서 보이게 추가
4. 보너스 문제, sign up 할때 사용자 이름도 입력받고 그걸 Storage 에 user 컬렉션 아래에 추가할것.

 

 

화면은 아래와 같습니다.

 

 

 

main.dart

 

import 'package:firebase_core/firebase_core.dart';
import 'package:flutter/material.dart';
import 'package:get/get.dart';

import 'controller/auth_controller.dart';
import 'screens/login.dart';

void main() async{
  WidgetsFlutterBinding.ensureInitialized();
  // 인젝션 방식이 다른 스타일임
  await Firebase.initializeApp().then((value) => Get.put(AuthController()));
  runApp(const MyApp());
}

class MyApp extends StatelessWidget {
  const MyApp({Key? key}) : super(key: key);

  @override
  Widget build(BuildContext context) {
    return GetMaterialApp(
      theme: ThemeData(
          primaryColor: Colors.blue
      ),
      home: const LoginPage(),
    );
  }
}

 

 

./controller/auth_controller.dart

 

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

import '../screens/login.dart';
import '../screens/welcome.dart';

class AuthController extends GetxController {
  static AuthController get to => Get.find();

  // 아래 3줄은 Getx login 할때 핵심 기본 코드임
  // Get.find() 대신 접근 가능한 방법
  static AuthController instance = Get.find();
  // 선언가능한 다른 방식, final _user = Rxn<User?>();, Rxn<User?> get user => _user;
  late Rx<User?> _user;
  FirebaseAuth authentication = FirebaseAuth.instance;

  // loading indicator
  RxBool isLoading = false.obs;

  Rx<User?> get user => _user;

  @override
  void onReady() {
    // getx 초기화 이후 호출됨,
    // 네트워크를 통해서 전달되는 다양한 정보를 가지고 기능 구현하기 위해서 초기화를 시켜줄때 좋음,
    super.onReady();
    debugPrint('AuthController >> onReady');
    _user = Rx<User?>(authentication.currentUser);
    // 스트림 처리
    _user.bindStream(authentication.userChanges());
    // ever(listener, callback)
    ever(_user, _moveToPage);
  }

  @override
  void onInit() {
    // getx 인스턴스 생성전에 호출됨, 인스턴스와 관련된 것을 호출시 주의할것,
    debugPrint('AuthController >> onInit');
    once(isLoading, (_) => debugPrint("isLoading 최초 호출($isLoading)"));
    ever(isLoading, (_) => debugPrint("isLoading 매번 호출($isLoading)"));
    super.onInit();
  }

  _moveToPage(User? user) {
    if (user == null) {
      Get.offAll(() => const LoginPage());
    } else {
      // debugPrint(user.toString());
      // debugPrint(user.email.toString());
      Get.offAll(() => const WelcomePage());
    }
    isLoading(false);
  }

  void register({
    required String email,
    required String password,
    required String name,
  }) async {
    try {
      await authentication
          .createUserWithEmailAndPassword(email: email, password: password)
          .then((value) => debugPrint('생성완료:[${value.user}]'));
      // 4. sign up 할때 사용자 이름도 입력받고 그걸 Storage 에 user 컬렉션 아래에 추가할것.
      var doc = FirebaseFirestore.instance.collection('users').doc(user.value!.uid);
      await doc.set({
        'id': doc.id,
        'datetime': DateTime.now().toString(),
        'email': email,
        'name': name,
      });
    } catch (e) {
      isLoading.value = false;
      Get.snackbar(
        "Error message",
        "User message",
        backgroundColor: Colors.red,
        snackPosition: SnackPosition.BOTTOM,
        titleText: const Text(
          "Registration is failed",
          style: TextStyle(color: Colors.white),
        ),
        messageText: Text(
          e.toString(),
          style: const TextStyle(color: Colors.white),
        ),
      );
    }
  }

  // 1. 로그인 구현
  void login({required String email, required String passWord}) async {
    try {
      await FirebaseAuth.instance.signInWithEmailAndPassword(email: email, password: passWord);
    } on FirebaseAuthException catch (e) {
      if (e.code == 'user-not-found' || e.code == 'wrong-password') {
        debugPrint(e.code.toString());
        myDialog(msg: "Wrong email or Wrong password");
      }
      isLoading.value = false;
    }
  }

  void myDialog({required String msg}) {
    Get.defaultDialog(
      title: "Notice",
      middleText: msg,
      backgroundColor: Colors.blue,
      titleStyle: const TextStyle(color: Colors.white),
      middleTextStyle: const TextStyle(color: Colors.white),
    );
  }

  void logout() {
    authentication.signOut();
  }
}

 

 

./screens/login.dart

 

import 'package:flutter/material.dart';
import 'package:google_fonts/google_fonts.dart';
import 'package:get/get.dart';

import '../controller/auth_controller.dart';
import 'signup.dart';

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

  @override
  Widget build(BuildContext context) {
    var emailController = TextEditingController(text: 'aaa@aaa.com'); //(text: "010 5555 5555")
    var passwordController = TextEditingController();

    return Scaffold(
      backgroundColor: Colors.grey[300],
      body: SafeArea(
        child: Center(
          child: SingleChildScrollView(
            child: Column(
              mainAxisAlignment: MainAxisAlignment.center,
              children: [
                const Icon(Icons.phone_android, size: 100),
                const SizedBox(height: 30),
                Text('Hello', style: GoogleFonts.bebasNeue(fontSize: 36.0)),
                const SizedBox(height: 10),
                const Text('Welcome back',
                    style: TextStyle(fontSize: 20, fontWeight: FontWeight.bold)),
                const SizedBox(height: 50),
                Padding(
                  padding: const EdgeInsets.symmetric(horizontal: 25.0),
                  child: Container(
                    decoration: BoxDecoration(
                        color: Colors.grey[200],
                        border: Border.all(color: Colors.white),
                        borderRadius: BorderRadius.circular(12)),
                    child: Padding(
                      padding: const EdgeInsets.only(left: 20.0),
                      child: TextField(
                        controller: emailController,
                        decoration:
                            const InputDecoration(border: InputBorder.none, hintText: 'Email'),
                      ),
                    ),
                  ),
                ),
                const SizedBox(height: 10),
                Padding(
                  padding: const EdgeInsets.symmetric(horizontal: 25.0),
                  child: Container(
                    decoration: BoxDecoration(
                        color: Colors.grey[200],
                        border: Border.all(color: Colors.white),
                        borderRadius: BorderRadius.circular(12)),
                    child: Padding(
                      padding: const EdgeInsets.only(left: 20.0),
                      child: TextField(
                        controller: passwordController,
                        obscureText: true,
                        decoration:
                            const InputDecoration(border: InputBorder.none, hintText: 'Password'),
                      ),
                    ),
                  ),
                ),
                const SizedBox(height: 10),
                GestureDetector(
                  onTap: () async {
                    AuthController.instance.isLoading.value = true;
                    await Future.delayed(const Duration(seconds: 3));
                    // 1. 로그인 구현
                    AuthController.instance.login(
                        email: emailController.text.trim(),
                        passWord: passwordController.text.trim());
                  },
                  child: Padding(
                    padding: const EdgeInsets.symmetric(horizontal: 25.0),
                    child: Container(
                      padding: const EdgeInsets.all(20),
                      decoration:
                          BoxDecoration(color: Colors.red, borderRadius: BorderRadius.circular(12)),
                      child: Obx(
                        () => Center(
                          // 2. 인디케이터 구현
                          child: AuthController.instance.isLoading.value
                              ? const SizedBox(
                                  height: 20,
                                  width: 20,
                                  child: CircularProgressIndicator(color: Colors.white))
                              : const Text('Sign in',
                                  style: TextStyle(
                                      color: Colors.white,
                                      fontSize: 16,
                                      fontWeight: FontWeight.bold)),
                        ),
                      ),
                    ),
                  ),
                ),
                const SizedBox(
                  height: 25,
                ),
                Row(
                  mainAxisAlignment: MainAxisAlignment.center,
                  children: [
                    const Text('Not a member?'),
                    GestureDetector(
                      onTap: () => Get.to(() => const SignupPage()),
                      child: const Text(
                        ' Register Now!',
                        style: TextStyle(color: Colors.blue, fontWeight: FontWeight.bold),
                      ),
                    )
                  ],
                )
              ],
            ),
          ),
        ),
      ),
    );
  }
}

 

 

./screens/signup.dart

 

import 'package:flutter/material.dart';
import 'package:getx_login_test/controller/auth_controller.dart';
import 'package:google_fonts/google_fonts.dart';
import 'package:get/get.dart';

class SignupPage extends StatelessWidget {
  const SignupPage({Key? key}) : super(key: key);

  @override
  Widget build(BuildContext context) {
    var nameController = TextEditingController();
    var emailController = TextEditingController();
    var passwordController = TextEditingController();

    return Scaffold(
      backgroundColor: Colors.grey[300],
      body: SafeArea(
        child: Center(
          child: SingleChildScrollView(
            child: Column(
              mainAxisAlignment: MainAxisAlignment.center,
              children: [
                const Icon(Icons.card_travel_outlined, color: Colors.deepPurple, size: 100),
                const SizedBox(height: 30),
                Text('Sign Up', style: GoogleFonts.bebasNeue(fontSize: 36.0)),
                const SizedBox(height: 10),
                Text('Thank you for join us', style: GoogleFonts.bebasNeue(fontSize: 28)),
                const SizedBox(height: 50),
                Padding(
                  padding: const EdgeInsets.symmetric(horizontal: 25.0),
                  child: Container(
                    decoration: BoxDecoration(
                        color: Colors.grey[200],
                        border: Border.all(color: Colors.white),
                        borderRadius: BorderRadius.circular(12)),
                    child: Padding(
                      padding: const EdgeInsets.only(left: 20.0),
                      child: TextField(
                          controller: nameController,
                          decoration:
                              const InputDecoration(border: InputBorder.none, hintText: 'Name')),
                    ),
                  ),
                ),
                const SizedBox(height: 10),
                Padding(
                  padding: const EdgeInsets.symmetric(horizontal: 25.0),
                  child: Container(
                    decoration: BoxDecoration(
                        color: Colors.grey[200],
                        border: Border.all(color: Colors.white),
                        borderRadius: BorderRadius.circular(12)),
                    child: Padding(
                      padding: const EdgeInsets.only(left: 20.0),
                      child: TextField(
                          controller: emailController,
                          decoration:
                              const InputDecoration(border: InputBorder.none, hintText: 'Email')),
                    ),
                  ),
                ),
                const SizedBox(height: 10),
                Padding(
                  padding: const EdgeInsets.symmetric(horizontal: 25.0),
                  child: Container(
                    decoration: BoxDecoration(
                        color: Colors.grey[200],
                        border: Border.all(color: Colors.white),
                        borderRadius: BorderRadius.circular(12)),
                    child: Padding(
                      padding: const EdgeInsets.only(left: 20.0),
                      child: TextField(
                          controller: passwordController,
                          obscureText: true,
                          decoration: const InputDecoration(
                              border: InputBorder.none, hintText: 'Password')),
                    ),
                  ),
                ),
                const SizedBox(
                  height: 10,
                ),
                GestureDetector(
                  onTap: () async {
                    AuthController.instance.isLoading.value = true;
                    await Future.delayed(const Duration(seconds: 3));
                    AuthController.instance.register(
                        email: emailController.text.trim(),
                        password: passwordController.text.trim(),
                        name: nameController.text.trim());
                  },
                  child: Padding(
                    padding: const EdgeInsets.symmetric(horizontal: 25.0),
                    child: Container(
                      padding: const EdgeInsets.all(20),
                      decoration:
                          BoxDecoration(color: Colors.red, borderRadius: BorderRadius.circular(12)),
                      child: Obx(
                        () => Center(
                          // 2. 인디케이터 구현
                          child: AuthController.instance.isLoading.value
                              ? const SizedBox(
                                  height: 20,
                                  width: 20,
                                  child: CircularProgressIndicator(color: Colors.white))
                              : const Text('Sign up',
                                  style: TextStyle(
                                      color: Colors.white,
                                      fontSize: 16,
                                      fontWeight: FontWeight.bold)),
                        ),
                      ),
                    ),
                  ),
                ),
                const SizedBox(
                  height: 25,
                ),
                Row(
                  mainAxisAlignment: MainAxisAlignment.center,
                  children: [
                    const Text('Already registered?'),
                    GestureDetector(
                      onTap: () => Get.back(),
                      child: const Text(
                        ' Go back Login page!',
                        style: TextStyle(color: Colors.blue, fontWeight: FontWeight.bold),
                      ),
                    )
                  ],
                )
              ],
            ),
          ),
        ),
      ),
    );
  }
}

 

 

./screens/welcom.dart

 

import 'package:flutter/material.dart';

import '../controller/auth_controller.dart';

class WelcomePage extends StatelessWidget {
  const WelcomePage({Key? key}) : super(key: key);

  @override
  Widget build(BuildContext context) {
    TextStyle textStyle = const TextStyle(fontSize: 20);
    SizedBox sizedBox = const SizedBox(height: 8);
    return Scaffold(
      body: SafeArea(
        child: Padding(
          padding: const EdgeInsets.all(16.0),
          child: Column(
            crossAxisAlignment: CrossAxisAlignment.start,
            children: [
              Text('Welcome', style: textStyle),
              sizedBox,
              // 3. 이메일 주소 화면에 표시
              Text(AuthController.instance.user.value!.email.toString(), style: textStyle),
              sizedBox,
              Text(AuthController.instance.user.value!.uid.toString(), style: textStyle),
              sizedBox,
              IconButton(
                onPressed: () {
                  AuthController.instance.logout();
                },
                icon: const Icon(Icons.login_outlined),
              ),
            ],
          ),
        ),
      ),
    );
  }
}

 

 

 

 

 

 

[참고자료] 코딩셰프 -

https://www.youtube.com/watch?v=Hlb_nqrIUfA&list=PLQt_pzi-LLfo1L1NEQDAk3HNRkWZ5VcCJ&index=5 

 

'Flutter > 11 Quiz' 카테고리의 다른 글

[Flutter] Quiz - level2(GridView)  (0) 2022.07.12