회원가입 버튼 함수

UserController.to.join(_username.text.trim(), _email.text.trim(), _password.text.trim());

UserController

Future<void> join(String username, String email, String password) async {
    String result = await _userRepository.join(username, email, password);
    if (result == "success") {
      print("success");
    } else if (result == "user-not-found") {
      print('No user found for that email.');
    } else if (result == "wrong-password") {
      print('Wrong password provided for that user.');
    } else {
      print(result);
    }
  }

JoinReqDto

class JoinReqDto {
  final String? username;
  final String? email;
  final String? password;

  JoinReqDto(this.username, this.email, this.password);
  Map<String, dynamic> toJson() => {
        "username": username,
        "email": email,
        "password": password,
      };
}

UserRepository

Future<String> join(String username, String email, String password) async {
    JoinReqDto dto = JoinReqDto(username, email, password);
    try {
      UserCredential userCredential = await _userProvider.join(dto);
      UserModel _newUser = UserModel(
        uid: userCredential.user!.uid,
        email: email,
        name: username,
        photoUrl: "photoUrl",
      );
      await _userProvider.joinDetail(_newUser);
      return "success";
    } on FirebaseAuthException catch (e) {
      return e.code;
    }
  }

UserProvider

// firebase auth 등록
Future<UserCredential> join(JoinReqDto dto) async =>
      await auth.createUserWithEmailAndPassword(
          email: dto.email.toString(), password: dto.password.toString());
// firebase storage 등록
Future<void> joinDetail(UserModel user) async =>
      await firebaseFirestore.doc('/users/${user.uid}').set(user.toJson());

Git Commit

https://github.com/mugon-dev/flutter_firebase_blog/commit/d5a51d681816c3759436585345cbf2d4fc39dab4

 

email, password, username 회원가입, 로그인, 로그아웃 구현 및 클라우드 저장 · mugon-dev/flutter_firebase_blo

Permalink This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository. Browse files email, password, username 회원가입, 로그인, 로그아웃 구현 및 클라우드 저장 Loading branch informati

github.com

 

728x90

Main

firebase 초기화 중 Splash()를 먼저 보여주고 AuthController에서 로그인 상태에 따라 페이지 이동

void main() async {
  WidgetsFlutterBinding.ensureInitialized();
  await initialization.then((value) {
    Get.put(AuthController());
  });
  runApp(App());
}

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

  @override
  Widget build(BuildContext context) {
    return GetMaterialApp(
      debugShowCheckedModeBanner: false,
      home: Splash(),
    );
  }
}

class Splash extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return Scaffold(
      body: Center(child: CircularProgressIndicator()),
    );
  }
}

 

AuthController

onReady 함수 내에서 로그인 상태를 확인, 그에 따라 페이지 이동

class AuthController extends GetxController {
  static AuthController get to => Get.find();
  Rxn<User> firebaseUser = Rxn<User>();
  Rxn<UserModel> firestoreUser = Rxn<UserModel>();
  RxBool isLoggedIn = false.obs;

  @override
  void onReady() {
    // TODO: implement onReady
    super.onReady();
    //run every time auth state changes
    ever(firebaseUser, handleAuthChanged);
    firebaseUser.bindStream(user);
  }

  handleAuthChanged(_firebaseUser) async {
    //get user data from firestore
    if (_firebaseUser?.uid != null) {
      firestoreUser.bindStream(streamFirestoreUser());
    }
    if (_firebaseUser == null) {
      Get.offAll(() => LoginPage());
    } else {
      Get.offAll(() => HomePage());
    }
  }

  //Streams the firestore user from the firestore collection
  Stream<UserModel> streamFirestoreUser() {
    print('streamFirestoreUser()');

    return firebaseFirestore
        .doc('/users/${firebaseUser.value!.uid}')
        .snapshots()
        .map((snapshot) => UserModel.fromMap(snapshot.data()!));
  }

  // Sign out
  Future<void> signOut() {
    return auth.signOut();
  }
}

Git commit

https://github.com/mugon-dev/flutter_firebase_blog/commit/d5a51d681816c3759436585345cbf2d4fc39dab4

 

email, password, username 회원가입, 로그인, 로그아웃 구현 및 클라우드 저장 · mugon-dev/flutter_firebase_blo

Permalink This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository. Browse files email, password, username 회원가입, 로그인, 로그아웃 구현 및 클라우드 저장 Loading branch informati

github.com

 

728x90

Installation

dependencies:
  flutter:
    sdk: flutter
  firebase_core: ^1.4.0

Android

Project Setting

Installing Firebase configuration File

android/build.gradle

google - service 버전 확인

buildscript {
  dependencies {
    // ... other dependencies
    classpath 'com.google.gms:google-services:4.3.8'
  }
}

/android/app/build.gradle

apply plugin: 'com.google.gms.google-services'

minSdkVersion 최소 16

호환성 해결

/android/app/build.gradle

android {
    defaultConfig {
        // ...
        minSdkVersion 16
        targetSdkVersion 28
        multiDexEnabled true
    }
}

dependencies {
  implementation 'com.android.support:multidex:1.0.3'
}

Enabling use of Firebase Emulator Suite

android/app/src/debug/AndroidManifest.xml

기존 application android태그안에 usesCleartextTraffic="true" 추가
http 주소 사용가능

<application android:usesCleartextTraffic="true">
  <!-- possibly other elements -->
</application>

Firebase console에서 앱 추가

android 앱에 firebase 추가

앱 등록 - Android 패키지 이름

android/app/build.gradle 의 applicationId

앱 등록 - 디버그 서명 인증서 SHA (Mac version)

터미널
비밀번호 : android

keytool -list -v \
-alias androiddebugkey -keystore ~/.android/debug.keystore

아래의 인증서 지문 복사 , 붙여넣기

구성파일 다운로드

파일은 다운받아 android/app 위치에 저장

FirebaseSDK 추가

android/build.gradle

buildscript {
  repositories {
    // Check that you have the following line (if not, add it):
    google()  // Google's Maven repository
  }
  dependencies {
    ...
    // Add this line
    classpath 'com.google.gms:google-services:4.3.8'
  }
}

allprojects {
  ...
  repositories {
    // Check that you have the following line (if not, add it):
    google()  // Google's Maven repository
    ...
  }
}

android/app/build.gradle

apply plugin: 'com.android.application'
// Add this line
apply plugin: 'com.google.gms.google-services'

dependencies {
  // Import the Firebase BoM
  implementation platform('com.google.firebase:firebase-bom:28.2.0')

  // Add the dependency for the Firebase SDK for Google Analytics
  // When using the BoM, don't specify versions in Firebase dependencies
  implementation 'com.google.firebase:firebase-analytics-ktx'

  // Add the dependencies for any other desired Firebase products
  // https://firebase.google.com/docs/android/setup#available-libraries
}

IOS

Firebase console에서 앱 추가

ios 폴더 우클릭으로 xcode에서 열기

Runner 클릭

bundle identifier 부분이 ios 번들 id

대문자가 들어가면 안되니 - 소문자로 변경

com.example.firebaseSetting → com.example.firebase-setting

구성파일 추가

Runner/Runner 위치에 추가

추가한 파일의 client id를 복사해서

Runner / info 의 URL types의 URL schemes에 추가

Enabling use of Firebase Emulator Suite

ios/Runner/Info.plist

<key>NSAppTransportSecurity</key>
<dict>
    <key>NSAllowsLocalNetworking</key>
    <true/>
</dict>

pop error

ios 폴더 위치에서 pod install

ios version error

ios deployment target version 변경

Git commit

https://github.com/mugon-dev/flutter_firebase_blog/commit/74b44de7d0320a0085f432b160cfe0c7e42865b3

 

firebase android, ios setting · mugon-dev/flutter_firebase_blog@74b44de

Permalink This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository. Browse files firebase android, ios setting Loading branch information Showing 13 changed files with 244 additions and 113 deletions.

github.com

 

728x90

Factory 패턴

팩토리 메서드 패턴(Factory method pattern)은 객체지향 디자인 패턴이다. Factory method는 부모(상위) 클래스에 알려지지 않은 구체 클래스를 생성하는 패턴이며. 자식(하위) 클래스가 어떤 객체를 생성할지를 결정하도록 하는 패턴이기도 하다.

장점

싱글톤 패턴 : static을 사용한 효과로 객체를 생성하지 않고도 패턴을 사용가능.

예제

피자 가게에서 피자를 주문하고 그 가격을 출력해주는 예제

객체 생성 로직을 어디서 하는가 ?

Factory 패턴을 사용하지 않은 예제

void main(){
	var userSelectedPizza = PizzaType.HamMushroom;
	Pizza pizza;
	switch (userSelectedPizza){
		case PizzaType.HamMushroom;
			pizza = HamAndMushroomPizza();
			break;
		case PizzaType.Deluxe;
			pizza = DeluxePizza();
			break;
		case PizzaType.Seafood;
			pizza = SeafoodPizza();
			break;
	}
	print(pizza.getPrice());
}
// 피자 종류
enum PizzaType { HamMushroom, Deluxe, Seafood }

abstract class Pizza{
	double getPrice();
}

class HamAndMushroomPizza implements Pizza{
	double price = 10.5;
	@override
	double getPrice(){
		return price;
	}
}

class DeluxePizza implements Pizza{
	double price = 5.5;
	@override
	double getPrice(){
		return price;
	}
}

class SeafoodPizza implements Pizza{
	double price = 7.8;
	@override
	double getPrice(){
		return price;
	}
}

Factory 패턴을 사용한 예제

void main(){
	var userSelectedPizza = PizzaType.HamMushroom;
	print(Pizza.pizzaFactory(userSelectedPizza).getPrice());
}
// 피자 종류
enum PizzaType { HamMushroom, Deluxe, Seafood }

abstract class Pizza{
	double getPrice();
	// 팩토리 패턴
	static pizzaFactory(PizzaType type){
		switch (userSelectedPizza){
		case PizzaType.HamMushroom;
			return HamAndMushroomPizza();
			break;
		case PizzaType.Deluxe;
			return DeluxePizza();
			break;
		case PizzaType.Seafood;
			return SeafoodPizza();
			break;
		}
	}
}

class HamAndMushroomPizza implements Pizza{
	double price = 10.5;
	@override
	double getPrice(){
		return price;
	}
}

class DeluxePizza implements Pizza{
	double price = 5.5;
	@override
	double getPrice(){
		return price;
	}
}

class SeafoodPizza implements Pizza{
	double price = 7.8;
	@override
	double getPrice(){
		return price;
	}
}

로직 변경

DeluxePizza 객체 생성할때 orderNumber가 필요하다고 수정

class DeluxePizza implements Pizza{
	double price = 5.5;
	String orderNumber;
	DeluxePizza(this.orderNumber);
	@override
	double getPrice(){
		return price;
	}
}

Factory 패턴을 사용하지 않은 예제

void main(){
	var userSelectedPizza = PizzaType.HamMushroom;
	Pizza pizza;
	var orderNumber = '1234';
	switch (userSelectedPizza){
		case PizzaType.HamMushroom;
			pizza = HamAndMushroomPizza();
			break;
		case PizzaType.Deluxe;
			pizza = DeluxePizza(orderNumber);
			break;
		case PizzaType.Seafood;
			pizza = SeafoodPizza();
			break;
	}
	print(pizza.getPrice());
}

Factory 패턴을 사용한 예제

void main(){
	var userSelectedPizza = PizzaType.HamMushroom;
	String orderNumber = '1234';
	print(Pizza.pizzaFactory(userSelectedPizza,orderNumber).getPrice());
}
// 피자 종류
enum PizzaType { HamMushroom, Deluxe, Seafood }

abstract class Pizza{
	double getPrice();
	// 팩토리 패턴
	static pizzaFactory(PizzaType type, String orderNumber){
		switch (userSelectedPizza){
		case PizzaType.HamMushroom;
			return HamAndMushroomPizza();
			break;
		case PizzaType.Deluxe;
			return DeluxePizza(orderNumber);
			break;
		case PizzaType.Seafood;
			return SeafoodPizza();
			break;
		}
	}
}

Factory 패턴을 사용한 예제에서 Map 타입으로 데이터 넘겨주기

void main(){
	var userSelectedPizza = PizzaType.HamMushroom;
	String orderNumber = '1234';
	Map<String, dynamic> json = {
		'type': PizzaType.HamMushroom,
		'orderNumber' : '1234',
	}
	print(Pizza.pizzaFactory(json).getPrice());
}
// 피자 종류
enum PizzaType { HamMushroom, Deluxe, Seafood }

abstract class Pizza{
	double getPrice();
	// 팩토리 패턴
	static pizzaFactory(Map<String, dynamic> json){
		switch (json['type'] as PizzaType){
		case PizzaType.HamMushroom;
			return HamAndMushroomPizza();
			break;
		case PizzaType.Deluxe;
			return DeluxePizza(json['orderNumber'] as String);
			break;
		case PizzaType.Seafood;
			return SeafoodPizza();
			break;
		}
	}
}

Factory 패턴을 사용한 예제에서 Map 타입으로 데이터 넘겨줄때 Factory 타입으로 리턴

void main(){
	var userSelectedPizza = PizzaType.HamMushroom;
	String orderNumber = '1234';
	Map<String, dynamic> json = {
		'type': PizzaType.HamMushroom,
		'orderNumber' : '1234',
	}
	print(Pizza.fromJson(json).getPrice());
}
// 피자 종류
enum PizzaType { HamMushroom, Deluxe, Seafood }

abstract class Pizza{
	double getPrice();
	// 팩토리 패턴
	Factory Pizza.fromJson(Map<String, dynamic> json){
		switch (json['type'] as PizzaType){
		case PizzaType.HamMushroom;
			return HamAndMushroomPizza();
			break;
		case PizzaType.Deluxe;
			return DeluxePizza(json['orderNumber'] as String);
			break;
		case PizzaType.Seafood;
			return SeafoodPizza();
			break;
		default:
			return SeafoodPizza();
		}
	}
}

참고

https://www.youtube.com/watch?v=ZikfiBnFMk8 

 

728x90

'Study > Flutter' 카테고리의 다른 글

[Flutter] Provider pattern (bloc -> provider )  (0) 2021.06.16
[Flutter] Stateful widget  (0) 2021.06.14
[UI초급] Container, materialApp, Scaffold  (0) 2021.06.07

SingleGetTickerProviderMixin 를 mixin한 GetxController를 상속받은 컨트롤러 만들기

animation을 사용하기위해 SingleGetTickerProviderMixin를 가져옴

class TextAnimation extends GetxController with SingleGetTickerProviderMixin {
  Map<String, String?>? post;

  AnimationController? animationController;
  Animation<Offset>? animationOffset;

  @override
  void onInit() {
    super.onInit();
    animationController = AnimationController(
      vsync: this,
      duration: const Duration(milliseconds: 1000),
    );
    final curve =
        CurvedAnimation(parent: animationController!, curve: Curves.bounceIn);
    animationOffset = Tween<Offset>(
            begin: const Offset(0.0, -1.0), end: const Offset(0.0, 0.0))
        .animate(curve);
    animationController!.repeat();
    _loadData();
  }

  void _loadData() {
    post = Get.parameters;
  }
}

실제 사용

아래와 같이 statefull에서 사용하던 방법처럼 animation 부분에 controller에서 만든 animation을 가져오면 됨

class PostDetailView extends GetView<TextAnimation> {
  const PostDetailView();

  @override
  Widget build(BuildContext context) {
    Get.put(TextAnimation());
    return Scaffold(
      appBar: AppBar(
        title: Text(controller.post!['title']!),
      ),
      body: Column(
        children: [
          Hero(
              tag: controller.post!['uid']!,
              child: Image.asset(controller.post!['thumbnail']!)),
          Column(
            children: [
            // 아래와 같이 controller에서 애니메이션을 가져오면 됨
              SlideTransition(
                position: controller.animationOffset!,
                child: Text(
                  controller.post!['title']!,
                ),
              ),
              Text(
                controller.post!['description']!,
              ),
            ],
          ),
        ],
      ),
    );
  }
}
728x90

2021.07.01 - [Usage/Flutter] - [Flutter] Json 데이터를 GetX를 활용하여 list view, detail view 만들기

 

[Flutter] Json 데이터를 GetX를 활용하여 list view, detail view 만들기

Json 데이터 예시 [ { "uid" : 1, "thumbnail": "assets/images/1.jpg", "title" : "꼬북좌 이미지1", "description" : "남심 '저격'브레이브걸스 유정 근황" }, { "uid" : 2, "thumbnail": "assets/images/2.jpg"..

mugon-devlog.tistory.com

Hero 애니메이션 적용

Hero 애니메이션을 적용하기 위해서는 적용받는 대상의 부모와 자식 위젯의 태그를 동일하게 만들어줘야함

아래는 디테일 페이지로 이동하기위해 클릭하는 위젯

아래 위젯의 Image를 클릭하면 디테일 페이지로 이동하는데 이때의 이미지와 디테일 페이지의 이미지의 태그를 동일하게 만들어줘야함

class PostWidget extends StatelessWidget {
  final String uid;
  final String thumbnail;
  final String title;
  final String description;
  final PostClickFunction callBack;

  const PostWidget(
      {required this.uid,
      required this.thumbnail,
      required this.title,
      required this.description,
      required this.callBack});

  @override
  Widget build(BuildContext context) {
    return GestureDetector(
      onTap: callBack,
      child: Container(
        child: Column(
          crossAxisAlignment: CrossAxisAlignment.stretch,
          children: [
            // tag가 연결되는 이미지의 tag와 같아야함
            // 리스트의 이미지의 태그와 디테일 페이지의 이미지의 태그가 동일해야함
            Hero(tag: uid, child: Image.asset(thumbnail)),
            Padding(
              padding: const EdgeInsets.only(
                  top: 10, bottom: 20, left: 10, right: 10),
              child: Column(
                crossAxisAlignment: CrossAxisAlignment.stretch,
                children: [
                  Text(
                    title,
                    style: TextStyle(fontSize: 15, fontWeight: FontWeight.bold),
                  ),
                  Text(
                    description,
                    style: TextStyle(
                      fontSize: 12,
                    ),
                  )
                ],
              ),
            ),
          ],
        ),
      ),
    );
  }
}

아래는 디테일 페이지

class PostDetailView extends GetView<TextAnimation> {
  const PostDetailView();

  @override
  Widget build(BuildContext context) {
    Get.put(TextAnimation());
    return Scaffold(
      appBar: AppBar(
        title: Text(controller.post!['title']!),
      ),
      body: Column(
        children: [
          Hero(
              tag: controller.post!['uid']!,
              child: Image.asset(controller.post!['thumbnail']!)),
          Column(
            children: [
              Text(
                controller.post!['title']!,
              ),
              Text(
                controller.post!['description']!,
              ),
            ],
          ),
        ],
      ),
    );
  }
}
728x90

+ Recent posts