DevBoi

[Flutter] Boiler Flutter Template. 소셜 로그인 (IOS) 본문

[Mobile]/[Flutter]

[Flutter] Boiler Flutter Template. 소셜 로그인 (IOS)

HiSmith 2023. 8. 6. 13:51
반응형

소셜 로그인을 달아 놓을 거다.

카카오 개발자 계정 관련 세팅을 정리한다.

 

 

1. Kakao Developer 세팅 

<참고링크>

https://developers.kakao.com/docs/latest/ko/getting-started/app#platform

 

Kakao Developers

카카오 API를 활용하여 다양한 어플리케이션을 개발해보세요. 카카오 로그인, 메시지 보내기, 친구 API, 인공지능 API 등을 제공합니다.

developers.kakao.com

 

로그인 > 내 애플리케이션>애플리케이션 추가

추가한 애플리케이션 > 플랫폼 설정 하기

 

우선 IOS부터 진행하자

Bundle 아이디 찾는 법 : 해당 프로젝트 Runner.xcworkspace를 열고 아래 사진 처럼 확인

 

저장이 되면 아래와 같이 등록이 된다.

 

사이드 바에 카카오 로그인 활성 상태 변경, 동의항목에서 필요한 정보들 체크

 

info.plist, IOS URL 스키마 값인 네이티브 앱키를 넣어주면 IOS 세팅이 끝난다.

<key>CFBundleURLTypes</key>
      <array>
        <dict>
        <key>CFBundleURLSchemes</key>
        <array>
          <!-- kakao login -->
          <string>kakao네이티브 앱 키</string>
        </array>
        </dict>
      </array>

 

이제 코드단 구현을 해보자

1. 의존성 추가(이중 골라서 필요한것만 하면됨) 나는 sdk_user에 대해서만 사용했다.

dependencies:
  kakao_flutter_sdk: ^1.4.1 # 전체 추가
  kakao_flutter_sdk_user: ^1.4.1 # 카카오 로그인
  kakao_flutter_sdk_talk: ^1.4.1 # 카카오톡 메시지, 카카오톡 소셜(프로필 가져오기, 친구 목록 가져오기)
  kakao_flutter_sdk_story: ^1.4.1 # 카카오스토리
  kakao_flutter_sdk_share: ^1.4.1 # 카카오톡 공유
  kakao_flutter_sdk_navi: ^1.4.1 # 카카오내비
  kakao_flutter_sdk_friend: ^1.4.1 # 카카오톡 소셜(피커: 친구 선택하기)

2.버튼 추가

이건, 카카오 로그인에 대한 처리 해줄 이벤트를 받을 버튼이다.

버튼과 이벤트 함수에 대한 부분은 굳이 작성하지 않겠다(포스팅 글의 목적과 다르니)

3. 로그인 소스 작성

void main() async {
  KakaoSdk.init(nativeAppKey: '네이티브 키');
  runApp(MyApp());
}

Main.dart에 sdk를 추가해준다.

해당 작성을 하게 되면, 초기화에 돌기 때문에 관련 값이 설정 된다.

 

abstract class SocialLogin{
  Future<bool> login();
  Future<bool> logout();
}

소셜 로그인에 대한 추상화 클래스이다.

import 'package:boilerflutterapp/view/screen/social_login.dart';
import 'package:kakao_flutter_sdk_user/kakao_flutter_sdk_user.dart';

class KakaoViewModel{
  final SocialLogin _socialLogin;
  bool isLogined = false;
  User? user;
  KakaoViewModel(this._socialLogin);

  Future login() async{
    isLogined = await _socialLogin.login();
    if(isLogined){
      user = await UserApi.instance.me();
    }
  }
  Future logout() async{
    await _socialLogin.logout();
    isLogined = false;
    user = null;
  }
}

뷰 모델을 만든다. 해당 로그인에 대한 함수와 로그아웃에 대한 함수가 있다.

카카오쪽에서 제공하는 api는 UserAPi관련이다.

로그인하면 user에 값을 넣어주고, 로그아웃 하면 값을 비워준다.

buildButton2() {
    return loading
        ? Center(child: CircularProgressIndicator())
        : CustomButton(
      label: "kakao login",
      onPressed: () async{
        await viewModel.login();
        setState(() {

        });
      }
    ).fadeInList(4, false);
  }
  profileImage(){
    return Image.network(viewModel.user?.kakaoAccount?.profile?.profileImageUrl??'');
  }

 

 

버튼에서 해당 관련 로직을 쓰는 영역이다.

profileImage는 뷰모델에서 유저의 프로필 이미지를 보여주고

카카오 로그인을 누르게 되면, 뷰모델의 로그인 메소드가 돈다.

 

 

로그인 화면 관련 전체 코드는 아래와 같다.

import 'package:boilerflutterapp/main.dart';
import 'package:boilerflutterapp/util/animations.dart';
import 'package:boilerflutterapp/util/const.dart';
import 'package:boilerflutterapp/util/enum.dart';
import 'package:boilerflutterapp/util/extensions.dart';
import 'package:boilerflutterapp/util/router.dart';
import 'package:boilerflutterapp/util/validations.dart';
import 'package:boilerflutterapp/view/screen/kakao_login.dart';
import 'package:boilerflutterapp/view/screen/main_screen.dart';
import 'package:boilerflutterapp/view/widget/custom_button.dart';
import 'package:boilerflutterapp/view/widget/custom_text_field.dart';
import 'package:flutter/material.dart';
import 'package:lottie/lottie.dart';

import 'kakao_view_model.dart';

class Login extends StatefulWidget {
  @override
  _LoginState createState() => _LoginState();
}

class _LoginState extends State<Login> {
  bool loading = false;
  bool validate = false;
  GlobalKey<FormState> formKey = GlobalKey<FormState>();
  GlobalKey<ScaffoldState> _scaffoldKey = GlobalKey<ScaffoldState>();
  String email = '', password = '', name = '';
  FocusNode nameFN = FocusNode();
  FocusNode emailFN = FocusNode();
  FocusNode passFN = FocusNode();
  FormMode formMode = FormMode.LOGIN;
  final viewModel = KakaoViewModel(KakaoLogin());


  login() async {
    FormState form = formKey.currentState!;
    form.save();
    Navigate.pushPageReplacement(context, MainScreen());
    // if (!form.validate()) {
    //   validate = true;
    //   setState(() {});
    //   showInSnackBar('Please fix the errors in red before submitting.');
    // } else {
    //   Navigate.pushPageReplacement(context, MainScreen());
    // }
  }

  void showInSnackBar(String value) {
    // _scaffoldKey.currentState?.removeCurrentSnackBar();
    // _scaffoldKey.currentState?.showSnackBar(SnackBar(content: Text(value)));
  }

  @override
  Widget build(BuildContext context) {
    final screenWidth = MediaQuery.of(context).size.width;
    return Scaffold(
      key: _scaffoldKey,
      body: Container(
        child: Row(
          children: [
            buildLottieContainer(),
            Expanded(
              child: AnimatedContainer(
                duration: Duration(milliseconds: 500),
                child: Center(
                  child: Padding(
                    padding:
                        EdgeInsets.symmetric(horizontal: screenWidth * 0.1),
                    child: buildFormContainer(),
                  ),
                ),
              ),
            ),
          ],
        ),
      ),
    );
  }

  buildLottieContainer() {
    final screenWidth = MediaQuery.of(context).size.width;
    return AnimatedContainer(
      width: screenWidth < 700 ? 0 : screenWidth * 0.5,
      duration: Duration(milliseconds: 500),
      color: Theme.of(context).colorScheme.secondary.withOpacity(0.3),
      child: Center(
        child: Lottie.asset(
          AppAnimations.chatAnimation,
          height: 400,
          fit: BoxFit.cover,
        ),
      ),
    );
  }

  buildFormContainer() {
    return Column(
      mainAxisSize: MainAxisSize.min,
      mainAxisAlignment: MainAxisAlignment.center,
      crossAxisAlignment: CrossAxisAlignment.center,

      children: <Widget>[
        profileImage(),
        Text(
          'Boiler App',
          style: TextStyle(
            fontSize: 40.0,
            fontWeight: FontWeight.bold,
          ),
        ).fadeInList(0, false),
        SizedBox(height: 70.0),
        Form(
          autovalidateMode: AutovalidateMode.onUserInteraction,
          key: formKey,
          child: buildForm(),
        ),
        Visibility(
          visible: formMode == FormMode.LOGIN,
          child: Column(
            children: [
              SizedBox(height: 10.0),
              Align(
                alignment: Alignment.centerRight,
                child: TextButton(
                  onPressed: () {
                    formMode = FormMode.FORGOT_PASSWORD;
                    setState(() {});
                  },
                  child: Text('Forgot Password?'),
                ),
              ),
            ],
          ),
        ).fadeInList(3, false),
        SizedBox(height: 20.0),
        buildButton(),
        buildButton2(),
        Visibility(
          visible: formMode == FormMode.LOGIN,
          child: Row(
            mainAxisAlignment: MainAxisAlignment.center,
            children: [
              Text('Don\'t have an account?'),
              TextButton(
                onPressed: () {
                  formMode = FormMode.REGISTER;
                  setState(() {});
                },
                child: Text('Register'),
              ),
            ],
          ),
        ).fadeInList(5, false),
        Visibility(
          visible: formMode != FormMode.LOGIN,
          child: Row(
            mainAxisAlignment: MainAxisAlignment.center,
            children: [
              Text('Already have an account?'),
              TextButton(
                onPressed: () {
                  formMode = FormMode.LOGIN;
                  setState(() {});
                },
                child: Text('Login'),
              ),
            ],
          ),
        ),
      ],
    );
  }

  buildForm() {
    return Column(
      mainAxisSize: MainAxisSize.min,
      children: <Widget>[
        Visibility(
          visible: formMode == FormMode.REGISTER,
          child: Column(
            children: [
              CustomTextField(
                enabled: !loading,
                hintText: "Name",
                textInputAction: TextInputAction.next,
                validateFunction: Validations.validateName,
                onSaved: (String? val) {
                  name = val ?? '';
                },
                focusNode: nameFN,
                nextFocusNode: emailFN,
              ),
              SizedBox(height: 20.0),
            ],
          ),
        ),
        CustomTextField(
          enabled: !loading,
          hintText: "Email",
          textInputAction: TextInputAction.next,
          validateFunction: Validations.validateEmail,
          onSaved: (String? val) {
            email = val ?? '';
          },
          focusNode: emailFN,
          nextFocusNode: passFN,
        ).fadeInList(1, false),
        Visibility(
          visible: formMode != FormMode.FORGOT_PASSWORD,
          child: Column(
            children: [
              SizedBox(height: 20.0),
              CustomTextField(
                enabled: !loading,
                hintText: "Password",
                textInputAction: TextInputAction.done,
                validateFunction: Validations.validatePassword,
                submitAction: login,
                obscureText: true,
                onSaved: (String? val) {
                  password = val ?? '';
                },
                focusNode: passFN,
              ),
            ],
          ),
        ).fadeInList(2, false),
      ],
    );
  }

  buildButton() {
    return loading
        ? Center(child: CircularProgressIndicator())
        : CustomButton(
            label: "Submit",
            onPressed: () => login(),
          ).fadeInList(4, false);
  }
  buildButton2() {
    return loading
        ? Center(child: CircularProgressIndicator())
        : CustomButton(
      label: "kakao login",
      onPressed: () async{
        await viewModel.login();
        setState(() {

        });
      }
    ).fadeInList(4, false);
  }
  profileImage(){
    return Image.network(viewModel.user?.kakaoAccount?.profile?.profileImageUrl??'');
  }
}

 

다음 턴에는, 좀더 화면을 정리하고 소스를 분리하는 내용을 포스팅해보자

반응형