[Flutter] 플러터로 쇼핑몰 앱 만들기 [2]

     

플러터로 쇼핑몰 앱 만들기 [2]

 

구현 기능 

- 회원가입 / 로그인

- 상품 조회

- 장바구니

 

개발환경

- 윈도우 10, IntelliJ

 

github:

전체 - tkdlek11112/shopingmall_flutter: simple shopingmall by flutter (github.com)

이번 포스팅 완료 브랜치 - tkdlek11112/shopingmall_flutter at 플러터쇼핑몰만들기_2 (github.com)

 

 

들어가기 전에 

지난 포스팅에서 로그인 화면, 회원가입 화면, 하단 바로 이루어진 메인화면 + 4개의 탭을 만들었습니다. 처음 로그인하면 splash화면이 뜨고, 로그인 여부를 체크해서 로그인 화면 or 메인화면으로 보내주고, 로그인 화면에서는 버튼을 눌러 회원가입 화면으로 이동할 수 있습니다. 하지만 아직 아무 기능도 하지 않고 있는 더미 화면들입니다. 

 

이번 포스팅에서는 구글 파이어 베이스에 연동해서 회원가입, 로그인을 만들어보도록 하겠습니다. 사실 저는 백엔드 개발자라서 이런 기능들 그냥 백엔드 서버로 띄우는 게 더 좋긴 한데.. ㅎㅎ 백엔드까지는 하고 싶지 않은 분들도 있기 때문에 일단은 파이어 베이스로 만들고 백엔드 만드는 것은 따로 포스팅을 하겠습니다. (스프링 부트로 만들 예정)

 

 

파이어 베이스 설정

프런트 개발자들이 쉽게 백엔드 기능을 만들 수 있도록 도와주는 갓 구글 형님들의 파이어 베이스입니다. 구글 아이디가 없으신 분들은 없을 것이라 생각되므로 https://console.firebase.google.com/ 에 접속해서 로그인해줍니다. 개발자 가입을 해야 된다면 하셔야 됩니다~!!

 

메인화면에 새 프로젝트 만들기가 있는데 우리의 쇼핑몰 프로젝트를 만들어줍니다. shopingmall-flutter라고 만들겠습니다.

shopingmall-flutter

 

그러면 구글 애널리틱스를 붙일 건지 물어보는데, 붙여보고 싶으시면 붙이시고 아니면 아니 오를 누릅니다. 붙이려면 구글 애널리틱스 계정이 있어야 합니다. 구글 애널리틱스는 우리가 파이어 베이스를 사용하면서 사용자의 행동을 수집할 수 있게 도와주는 툴입니다. 실제 서비스할 거는 아니니 그냥 아니오로 하죵.

 

예전에는 파이어 베이스를 사용하기 위해 앱마다 등록해줘야 했는데, 최근에 플러터가 새로 생긴 것 같습니다. 

플러터가 생굤네

 

플러터 앱에 파이어베이스 추가

 

일단 CLI와 SDK를 설치하면 되는 것 같습니다. CLI부터 설치해볼까요?

 

설치하는 방법이 cli 바이너리를 다운로드하는 방법이 있고 npm으로 설치하는 방법이 있는데 저는 npm이 설치되어있어서 이 방법을 택했습니다.

 

npm install -g firebase-tools

설치 완료 후 터미널에서 firebase login을 치면 cli 사용에 대해 y/n을 묻고 y를 누르면 브라우저가 뜨면서 구글 로그인을 하라고 합니다. 구글 로그인까지 완료하면 cli 설정 완료~!

 

로그인

제대로 로그인이 되었는지 확인하려면 firebase projects:list를 써보면 됩니다. 현재 만들어진 프로젝트 리스트가 나오는데, 쇼핑몰이랑 옛날에 제가 만들었던 StockBoard라는 프로젝트가 나오네요 ㅋㅋ 

프로젝트 리스트

 

자 이제 CLI는 설치했고, SDK를 설치하면 되는데, 이건 플러터를 지금 개발하고 있다면 당연히 설치되어 있을 겁니다 ㅎㅎ 

다음 단계로 고고

 

2단계

이제 명령어를 몇 개 치라고 합니다. 플러터 프로젝트에 뭔가 세팅을 하는 명령어인가 봅니다.

 

위에 dart pub global activate flutterfire_cli를 하고 나서 아래 flutterfie를 하고 나니 실행이 안됩니다. 윈도우에서는 뭔가 환경변수 세팅을 해줘야 되는 것 같네요. 어휴 윈도우...

 

근데 환경변수를 세팅해도 못 찾길래 경로 가봤더니 flutterfire.bat으로 되어있네요 ㅡㅡ 극혐이네 윈도우 ㅋㅋ

 

 

그래서 flutterfire.bat 하니까 됩니다 ㅋㅋㅋ 

이제 알아서 세팅이 되나봅니다.

 

예전에는 요게 OS별로 각각 세팅을 해줬어야 했는데 이제는 한 번에 되나 봅니다. 어떤 OS 선택만 해주면 일괄로 등록을 해주네요. 프로젝트 폴더를 보시면 firebase_option.dart 파일이 생성되어 있는 것을 확인할 수 있습니다. 열어보시면 각 OS별 키가 자동으로 등록돼있는 것을 확인할 수 있습니다. 개꿀~

 

파이어 베이스를 등록했으면 이제 로그인을 위해 Authoentication기능을 추가해야 합니다. 

 

Authentication

10줄 미만의 코드로 된 엔드 투 엔드 사용자 ID 설루션이라고 합니다. 얼마나 쉬운지 사용해봅시다.

 

Authentication을 누르고 사용하기를 누르면 아래처럼 어떻게 사용자가 로그인하는지 선택할 수 있습니다. 생각보다 종류가 많군요?

 

고를 수 있다.

저희는 이메일/비밀번호만 누릅니다.

이메일/비밀번호만 사용 설정

다른 추가적인 SNS 로그인을 붙이려면 각 사이트에서 개발자 앱 등록을 해야 사용할 수 있습니다. 나중에 한번 해보세요~ ㅎㅎ

 

이메일/비밀번호 사용 설정을 눌렀다면 끝입니다.?! 아주 쉽죠? 

지난 시간에 firebase 관련 패키지를 등록하면서 firebase_auth라는 패키지를 등록한 게 기억나실 겁니다. 요걸 사용해서 회원가입과 로그인을 만들 예정입니다.

 

회원가입 만들기

 

이제 모델 폴더를 만들고 auth모델과 register모델을 만듭니다. auth는 파이어 베이스와 인터페이스(통신)에 사용되고, register는 화면에 있는 데이터를 다룰 때 사용됩니다. 

 

// models/model_register.dart
import 'package:flutter/material.dart';

class RegisterModel extends ChangeNotifier {
  String email = "";
  String password = "";
  String passwordConfirm = "";

  void setEmail(String email) {
    this.email = email;
    notifyListeners();
  }

  void setPassword(String password) {
    this.password = password;
    notifyListeners();
  }

  void setPasswordConfirm(String passwordConfirm) {
    this.passwordConfirm = passwordConfirm;
    notifyListeners();
  }
}

RegisterModel은 간단합니다. 우리가 이전 포스팅에서 이메일, 비밀번호, 비밀번호 확인 칸을 만들어놨기 때문에 요 값들이 변경될 때마다 set 메서드를 통해 Provider를 이용해 상태변화값을 알립니다. notifyListeners를 이용해 상태 변경 사항을 브로트 캐스팅하는 용도지요. Provider에 대해 잘 모르셔도 나중에 사용해보시면 알게 되실 겁니다 ㅎㅎ

 

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

enum AuthStatus {
  registerSuccess,
  registerFail,
  loginSuccess,
  loginFail
}

class FirebaseAuthProvider with ChangeNotifier {
  FirebaseAuth authClient;
  User? user;

  FirebaseAuthProvider({auth}) : authClient = auth ?? FirebaseAuth.instance;

  Future<AuthStatus> registerWithEmail(String email, String password) async {
    try {
      UserCredential credential = await authClient.createUserWithEmailAndPassword(email: email, password: password);
      return AuthStatus.registerSuccess;
    } catch (e) {
      return AuthStatus.registerFail;
    }
  }
}

 

model_auth.dart는 조금 복잡합니다. 복잡한데 대부분 firebase에서 제공하는 메서드들이라서 그냥 사용하면 되는 내용입니다. 

 

먼저 enum으로 상태를 정의해주었습니다. 요건 그냥 임의로 정의한 거라 여러분 마음대로 이름 지으셔도 됩니다. FirebaseAuth authClient라는 것이 실제로 파이어 베이스에 연결하는 client입니다. 요 client를 이용해 회원가입을 시켜줄 예정인데, 실제로 동작하는 메소드는 createUserWithEmailAndPassword입니다. 생겨먹은 게 뭔가 스프링 부트에 JPA와 비슷하게 생겼네요. 사람들이 생각하는 게 다 비슷비슷한가 봅니다.

 

이제 main.dart에 가서 파이어 베이스 Provider를 추가합니다.

 

// main.dart
import 'package:firebase_core/firebase_core.dart';
import 'package:flutter/material.dart';
import 'package:provider/provider.dart';
import 'package:shopingmall_flutter/firebase_options.dart';
import 'package:shopingmall_flutter/models/model_auth.dart';
import 'screens/screen_splash.dart';
import 'screens/screen_index.dart';
import 'screens/screen_login.dart';
import 'screens/screen_register.dart';

void main() async {
  WidgetsFlutterBinding.ensureInitialized();
  await Firebase.initializeApp(
      options: DefaultFirebaseOptions.currentPlatform
  );
  runApp(MyApp());
}

class MyApp extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return MultiProvider(
      providers: [
        ChangeNotifierProvider(create: (_) => FirebaseAuthProvider()),
      ],
      child: MaterialApp(
        title: 'Flutter Shopping mall',
        routes: {
          '/index': (context) => IndexScreen(),
          '/login': (context) => LoginScreen(),
          '/splash': (context) => SplashScreen(),
          '/register': (context) => RegisterScreen(),
        },
        initialRoute: '/splash',
      ),
    );
  }
}

 

main에서 Firebase.initialzeApp을 통해 초기화를 해주고, MyApp에서는 MultiProvider를 설정해서 FirebaseAuthProvider를 넣어줬습니다.  WidgetsFlutterBinding은 파이어 베이스를 사용하기 위해서 필요한 코드입니다. 

 

main.dart에 Provider에 Auth만 추가하고 아까 생성한 model_register는 추가하지 않았는데, 요놈은 앱 전체에서 사용하는 게 아니라서 따로 RegisterScreen에만 추가하겠습니다.

 

// screens/screen_regist.dart
import 'package:flutter/material.dart';
import 'package:provider/provider.dart';
import 'package:shopingmall_flutter/models/model_auth.dart';
import 'package:shopingmall_flutter/models/model_register.dart';

class RegisterScreen extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return ChangeNotifierProvider(
      create: (_) => RegisterModel(),
      child: Scaffold(
        appBar: AppBar(),
        body: Column(
          children: [
            EmailInput(),
            PasswordInput(),
            PasswordConfirmInput(),
            RegistButton()
          ],
        ),
      ),
    );
  }
}

class EmailInput extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    final register = Provider.of<RegisterModel>(context, listen: false);
    return Container(
      padding: EdgeInsets.all(5),
      child: TextField(
        onChanged: (email) {
          register.setEmail(email);
        },
        keyboardType: TextInputType.emailAddress,
        decoration: InputDecoration(
          labelText: 'email',
          helperText: '',
        ),
      ),
    );
  }
}

class PasswordInput extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    final register = Provider.of<RegisterModel>(context);
    return Container(
      padding: EdgeInsets.all(5),
      child: TextField(
        onChanged: (password) {
          register.setPassword(password);
        },
        obscureText: true,
        decoration: InputDecoration(
          labelText: 'password',
          helperText: '',
          errorText: register.password != register.passwordConfirm ? 'Password incorrect' : null,
        ),
      ),
    );
  }
}

class PasswordConfirmInput extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    final register = Provider.of<RegisterModel>(context, listen: false);
    return Container(
      padding: EdgeInsets.all(5),
      child: TextField(
        onChanged: (password) {
          register.setPasswordConfirm(password);
        },
        obscureText: true,
        decoration: InputDecoration(
          labelText: 'password confirm',
          helperText: '',
        ),
      ),
    );
  }
}

class RegistButton extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    final authClient =
        Provider.of<FirebaseAuthProvider>(context, listen: false);
    final register = Provider.of<RegisterModel>(context, listen: false);
    return Container(
      width: MediaQuery.of(context).size.width * 0.7,
      height: MediaQuery.of(context).size.height * 0.05,
      child: ElevatedButton(
        style: ElevatedButton.styleFrom(
          shape: RoundedRectangleBorder(
            borderRadius: BorderRadius.circular(30.0),
          ),
        ),
        onPressed: () async {
          await authClient
              .registerWithEmail(register.email, register.password)
              .then((registerStatus) {
            if (registerStatus == AuthStatus.registerSuccess) {
              ScaffoldMessenger.of(context)
                ..hideCurrentSnackBar()
                ..showSnackBar(
                  SnackBar(content: Text('Regist Success')),
                );
              Navigator.pop(context);
            } else {
              ScaffoldMessenger.of(context)
                ..hideCurrentSnackBar()
                ..showSnackBar(
                  SnackBar(content: Text('Regist Fail')),
                );
            }
          });
        },
        child: Text('Regist'),
      ),
    );
  }
}

 

바뀐 부분은 각각 위젯들에 Provider를 추가한 부분입니다. 또한 passwordConfirm위젯에는 에러 메시지도 추가했습니다. 비밀번호가 일치하지 않으면 에러가 나옵니다. 하지만 버튼을 눌렀을 때 체크를 안 하기 때문에 넘어가지긴 합니다 ㅎㅎ

 

registButton에서는 실제로 authClient를 이용해 파이어 베이스에 저장하는 함수를 실행합니다. 

 

이제 실제로 실행해서 회원가입을 해봅시다. 이메일 주소를 입력하고 패스워드를 6자리 이상 입력합니다. 정상적으로 입력이 되고 파이어 베이스에도 회원 데이터가 생기는 것을 확인할 수 있습니다.

 

오홍?

 

하지만 password와 passwordconfirm이 달라도 등록이 되는 걸 볼 수 있는데요 ㅋㅋㅋ 요 문제를 해결하기 위해서 passwrod가 다르면 regist버튼을 diable 하게 만듭니다. 조금만 고치면 됩니다.

 

class RegistButton extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    final authClient =
        Provider.of<FirebaseAuthProvider>(context, listen: false);
    final register = Provider.of<RegisterModel>(context);
    return Container(
      width: MediaQuery.of(context).size.width * 0.7,
      height: MediaQuery.of(context).size.height * 0.05,
      child: ElevatedButton(
        style: ElevatedButton.styleFrom(
          shape: RoundedRectangleBorder(
            borderRadius: BorderRadius.circular(30.0),
          ),
        ),
        onPressed: (register.password != register.passwordConfirm) ? null : () async {
          await authClient
              .registerWithEmail(register.email, register.password)
              .then((registerStatus) {
            if (registerStatus == AuthStatus.registerSuccess) {
              ScaffoldMessenger.of(context)
                ..hideCurrentSnackBar()
                ..showSnackBar(
                  SnackBar(content: Text('Regist Success')),
                );
              Navigator.pop(context);
            } else {
              ScaffoldMessenger.of(context)
                ..hideCurrentSnackBar()
                ..showSnackBar(
                  SnackBar(content: Text('Regist Fail')),
                );
            }
          });
        },
        child: Text('Regist'),
      ),
    );
  }
}

 

버튼 위젯에 onPressed 부분에서 조건 하나를 추가했습니다. password와 passwordConfirm의 값이 같지 않으면 null, 같으면 이전과 같이 등록을 하게 됩니다. 이 부분만 수정한다고 바로 적용되지는 않습니다. 왜냐하면 regiter provider를 listen 하고 있지 않기 때문입니다. 원래는 listen: false였는데 요걸 지워줍니다. 그럼 이제 password값이 변할 때마다 버튼이 비활성화됐다가 활성화됐다가 합니다.

 

버튼이 변한다

 

버튼 위젯에서 onPressed가 null일 경우 자동으로 버튼색이 disableColor로 바뀌게 됩니다. 

 

 

이제 로그인을 만들어봅시다. 

 

회원가입과 똑같이 모델을 먼저 만듭니다. model_login.dart를 만들겠습니다.

 

// model/model_login.dart
import 'package:flutter/material.dart';

class LoginModel extends ChangeNotifier {
  String email = "";
  String password = "";

  void setEmail(String email) {
    this.email = email;
    notifyListeners();
  }

  void setPassword(String password) {
    this.password = password;
    notifyListeners();
  }
}

 

LoginModel은 로그인 화면에서 사용하는 모델입니다. 화면에 email과 password 입력 창밖에 없기 때문에 두 개만 만들어줍니다. 

 

그리고, model_auth에 로그인 관련 기능을 추가합니다.

 

// models/model_auth.dart
import 'package:firebase_auth/firebase_auth.dart';
import 'package:flutter/material.dart';
import 'package:shared_preferences/shared_preferences.dart';

enum AuthStatus {
  registerSuccess,
  registerFail,
  loginSuccess,
  loginFail
}

class FirebaseAuthProvider with ChangeNotifier {
  FirebaseAuth authClient;
  User? user;

  FirebaseAuthProvider({auth}) : authClient = auth ?? FirebaseAuth.instance;

  Future<AuthStatus> registerWithEmail(String email, String password) async {
    try {
      UserCredential credential = await authClient.createUserWithEmailAndPassword(email: email, password: password);
      return AuthStatus.registerSuccess;
    } catch (e) {
      return AuthStatus.registerFail;
    }
  }

  Future<AuthStatus> loginWithEmail(String email, String password) async {
    try {
      await authClient.signInWithEmailAndPassword(email: email, password: password).then(
          (credential) async {
            user = credential.user;
            SharedPreferences prefs = await SharedPreferences.getInstance();
            prefs.setBool('isLogin', true);
            prefs.setString('email', email);
            prefs.setString('password', password);
          }
      );
      return AuthStatus.loginSuccess;
    } catch (e) {
      return AuthStatus.loginFail;
    }
  }
}

 

이전에 추가했던 registerWithEmail과 비슷하게 loginWithEmail을 만들어줍니다. 안에는 파이어 베이스의 로그인 메소드인 signInWithEmailAndPassword를 실행합니다. 요걸 실행하면서 SharedPreferences에 로그인했다는 정보를 기록합니다. 이렇게 하면 로그인한 상태에서는 앱을 실행하자마자 바로 index화면으로 가게 될 겁니다.

 

그럼 이제 로그인 화면을 수정해볼까요?

 

// screens/screen_login.dart
import 'package:flutter/material.dart';
import 'package:provider/provider.dart';
import 'package:shopingmall_flutter/models/model_auth.dart';
import 'package:shopingmall_flutter/models/model_login.dart';

class LoginScreen extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return ChangeNotifierProvider(
        create: (_) => LoginModel(),
        child: Scaffold(
          appBar: AppBar(),
          body: Column(
            children: [
              EmailInput(),
              PasswordInput(),
              LoginButton(),
              Padding(
                padding: EdgeInsets.all(10),
                child: Divider(
                  thickness: 1,
                ),
              ),
              RegisterButton(),
            ],
          ),
        ));
  }
}

class EmailInput extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    final login = Provider.of<LoginModel>(context, listen: false);
    return Container(
      padding: EdgeInsets.all(10),
      child: TextField(
        onChanged: (email) {
          login.setEmail(email);
        },
        keyboardType: TextInputType.emailAddress,
        decoration: InputDecoration(
          labelText: 'email',
          helperText: '',
        ),
      ),
    );
  }
}

class PasswordInput extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    final login = Provider.of<LoginModel>(context, listen: false);
    return Container(
      padding: EdgeInsets.all(10),
      child: TextField(
        onChanged: (password) {
          login.setPassword(password);
        },
        obscureText: true,
        decoration: InputDecoration(
          labelText: 'password',
          helperText: '',
        ),
      ),
    );
  }
}

class LoginButton extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    final authClient =
        Provider.of<FirebaseAuthProvider>(context, listen: false);
    final login = Provider.of<LoginModel>(context, listen: false);

    return Container(
      width: MediaQuery.of(context).size.width * 0.7,
      height: MediaQuery.of(context).size.height * 0.05,
      child: ElevatedButton(
        style: ElevatedButton.styleFrom(
          shape: RoundedRectangleBorder(
            borderRadius: BorderRadius.circular(30.0),
          ),
        ),
        onPressed: () async {
          await authClient
              .loginWithEmail(login.email, login.password)
              .then((loginStatus) {
            if (loginStatus == AuthStatus.loginSuccess) {
              ScaffoldMessenger.of(context)
                ..hideCurrentSnackBar()
                ..showSnackBar(SnackBar(
                    content:
                        Text('welcome! ' + authClient.user!.email! + ' ')));
              Navigator.pushReplacementNamed(context, '/index');
            } else {
              ScaffoldMessenger.of(context)
                ..hideCurrentSnackBar()
                ..showSnackBar(SnackBar(content: Text('login fail')));
            }
          });
        },
        child: Text('Login'),
      ),
    );
  }
}

class RegisterButton extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return TextButton(
        onPressed: () {
          Navigator.of(context).pushNamed('/register');
        },
        child: Text(
          'Regist by email',
        ));
  }
}

 

회원가입 화면을 수정했던 것과 마찬가지로, build를 우선 ChangeNotifierProvider로 감싸고, LoginModel을 추가합니다. 그리고 각각 Input 위젯 안에서 Provider를 선언해주고 setEmail과 setPassword를 이용해 입력값을 받아줍니다. 마지막으로 login버튼을 누르면 authClient에 만든 loginWithEmail을 호출하면서 파이어 베이스에 저장된 회원정보를 이용해 로그인이 됩니다. 아까 말한 것처럼 로그인하면 sharedPreference에 값을 저장하는 것을 잊지 맙시다. 보통 토큰을 저장하는데 저희는 토큰이 없으니 email과 password를 다 저장합니다. 

 

한번 로그인해볼까요?

 

로그인

잘 되는군요. 이제 앱을 껐다가 다시 켜보세요. 로그인 화면으로 가지 않고 바로 메인화면이 나올 겁니다. splash화면에서 로그인을 체크하기 때문에 로그인을 한번 하면 이제 계속해서 메인화면으로 바로 넘어가집니다. 

 

마지막으로 로그아웃을 만들어봅시다!!

 

로그아웃 만들기

로그아웃에서는 무슨 일을 해야 할까요? 일단 파이어 베이스에서 로그아웃을 해야 합니다. 그리고 내부에 저장되어있는 데이터를 지워야 합니다. SharedPreference에 우리가 로그인한 정보를 기록했으니 이걸 지워야 합니다. 그래야 앱을 재시작했을 때 로그인 화면으로 갈 수 있죠!

 

model_auth.dart에 로그아웃 기능을 만들어봅시다.

 

// models/model_auth.dart
import 'package:firebase_auth/firebase_auth.dart';
import 'package:flutter/material.dart';
import 'package:shared_preferences/shared_preferences.dart';

enum AuthStatus { registerSuccess, registerFail, loginSuccess, loginFail }

class FirebaseAuthProvider with ChangeNotifier {
  FirebaseAuth authClient;
  User? user;

  FirebaseAuthProvider({auth}) : authClient = auth ?? FirebaseAuth.instance;

  Future<AuthStatus> registerWithEmail(String email, String password) async {
    ...
  }

  Future<AuthStatus> loginWithEmail(String email, String password) async {
    ...
  }
  
  Future<void> logout() async {
    SharedPreferences prefs = await SharedPreferences.getInstance();
    prefs.setBool('isLogin', false);
    prefs.setString('email', '');
    prefs.setString('password', '');
    user = null;
    await authClient.signOut();
  }
}

 

logout을 만들었습니다. 다른 것들은 AuthStatus를 응답으로 받지만, logout은 void입니다. 연결을 끊기만 합니다 ㅎㅎ

 

안에 코드들은 앞서 말한 것처럼 내부 데이터를 지우고, 파이어 베이스에 signout을 하는 것입니다. 요롷게 signout을 해야 해당 계정으로 요청을 보내도 응답하지 않게 됩니다.

 

이제 화면을 만들어봅시다. 로그아웃 화면은 따로 없기 때문에 profile화면에 추가해보도록 하겠습니다.

 

// tabs/tab_profile.dart
import 'package:flutter/cupertino.dart';
import 'package:flutter/material.dart';
import 'package:provider/provider.dart';
import 'package:shopingmall_flutter/models/model_auth.dart';

class TabProfile extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return Center(
      child: Column(
        mainAxisAlignment: MainAxisAlignment.center,
        children: [
          Text("Cart Profile"),
          LoginOutButton(),
        ],
      ),
    );
  }
}

class LoginOutButton extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    final authClient =
        Provider.of<FirebaseAuthProvider>(context, listen: false);
    return TextButton(
        onPressed: () async {
          await authClient.logout();
          ScaffoldMessenger.of(context)
            ..hideCurrentSnackBar()
            ..showSnackBar(SnackBar(content: Text('logout!')));
          Navigator.of(context).pushReplacementNamed('/login');
        },
        child: Text('logout'));
  }
}

 

로그아웃은 성공 실패가 없습니다 ㅋㅋ 무조건 성공입니다. 따라서 authClient.logout을 실행하고 바로 login화면으로 돌아갑니다. 

 

로그아웃!

 

 

마무리

이번 포스팅에서는 파이어 베이스를 설정하고 회원가입, 로그인, 로그아웃을 만들어봤습니다. 백엔드를 따로 구성하지 않아도 회원가입과 로그인을 만들 수 있다니, 파이어 베이스 개꿀~~ 

 

다음 포스팅부터는 쇼핑몰에 핵심인 상품에 대한 화면을 만들어보도록 하겠습니다. 수고하셨습니다~ :)

 

 

 

 

 

 

 

반응형

댓글

Designed by JB FACTORY