List 항목 구현

Obx 위젯으로 상태 변화 감지

Listview를 RefeshIndicator로 감싸고 GlobalKey를 넣어줌으로 화면을 아래로 드래그할때 새로고침 구현

class HomePage extends StatelessWidget {
  var scaffoldKey = GlobalKey<ScaffoldState>();
  var refreshKey = GlobalKey<RefreshIndicatorState>();
  PostController postController = Get.put(PostController());
  @override
  Widget build(BuildContext context) {
    postController.findAll();
    return Scaffold(
      key: scaffoldKey,
      appBar: AppBar(),
      drawer: _navigation(context),
      body: Obx(
        () => RefreshIndicator(
          key: refreshKey,
          onRefresh: () async {
            await postController.findAll();
          },
          child: ListView.separated(
              itemBuilder: (context, index) {
                return ListTile(
                  onTap: () {
                    postController.findAll();
                  },
                  title: Text("${postController.posts[index].title} : ${postController.posts[index].id}"),
                  leading: Text("$index"),
                );
              },
              separatorBuilder: (context, index) {
                return Divider();
              },
              itemCount: postController.posts.length),
        ),
      ),
    );
  }
}

Controller

전체 게시글을 Rx 타입으로 받아 관찰

class PostController extends GetxController {
  static PostController get to => Get.find();
  final _postRepository = PostRepository();
  final posts = <PostResDto>[].obs;
  final post = Post().obs;

  Future<void> findAll() async {
    List<PostResDto> posts = await _postRepository.findAll();
    this.posts.value = posts;
  }
}

PostResDto

게시글을 받아올때 문서 id 추가를 위해 dto 생성

import 'package:flutter_firebase_blog/domain/user/user.dart';

class PostResDto {
  final String? id;
  final String? title;
  final String? content;
  final UserModel? user;
  final DateTime? created;
  final DateTime? updated;

  PostResDto({
    this.id,
    this.title,
    this.content,
    this.user,
    this.created,
    this.updated,
  });

  PostResDto.fromJson(Map<String, dynamic> json)
      : id = json['id'],
        title = json['title'],
        content = json['content'],
        user = UserModel.fromMap(json['user']),
        created = json['created'].toDate(),
        updated = json['updated'].toDate();

  Map<String, Object?> toJson() {
    return {
      'id': id,
      'title': title,
      'content': content,
      'user': user!.toJson(),
      'created': created,
      'updated': updated,
    };
  }
}

Provider

post collection의 전체 데이터 요청

class PostProvider {
  final postRef = firebaseFirestore.collection("post").withConverter<Post>(
        fromFirestore: (snapshot, options) => Post.fromJson(snapshot.data()!),
        toFirestore: (post, options) => post.toJson(),
      );

  Future<List<QueryDocumentSnapshot<Post>>> findAll() async =>
      await postRef.get().then((snapshot) => snapshot.docs);
}

Repository

요청 결과값을 controller에 전달

class PostRepository {
  final _postProvider = PostProvider();

  Future<List<PostResDto>> findAll() async {
    List<QueryDocumentSnapshot<Post>> posts = await _postProvider.findAll();
    List<PostResDto> result = posts
        .map((e) => PostResDto(
            id: e.reference.id,
            title: e.data().title,
            content: e.data().content,
            user: e.data().user,
            created: e.data().created,
            updated: e.data().updated))
        .toList();
    return result;
  }
 }

Git commit

https://github.com/mugon-dev/flutter_firebase_blog/commit/82d8d63c0f17a86be21c327033d9c0cae86d6682

 

home page 전체 게시글 불러오기 구현 및 새로고침 구현 · mugon-dev/flutter_firebase_blog@82d8d63

Permalink This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository. Browse files home page 전체 게시글 불러오기 구현 및 새로고침 구현 Loading branch information Showing 4 changed fil

github.com

문서 id 추가 수정

https://github.com/mugon-dev/flutter_firebase_blog/commit/99878565c8ead21b37753ebb91ebfd32d5966d3b

 

게시글 리스트 가져올때 문서 id 추가 · mugon-dev/flutter_firebase_blog@9987856

Permalink This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository. Browse files 게시글 리스트 가져올때 문서 id 추가 Loading branch information Showing 5 changed files with 56 additions

github.com

 

728x90

글쓰기 버튼 클릭 함수

추후 controller.insert 결과에 따른 분기 추가

CustomElevatedButton(
  text: "글쓰기",
  funPageRoute: () {
    controller.insert(
    title: _title.text,
    content: _content.text,
    user: AuthController.to.firestoreUser()!);
  Get.to(() => HomePage());
}),

Post model

import 'package:flutter_firebase_blog/domain/user/user.dart';

class Post {
  final String? title;
  final String? content;
  final UserModel? user;
  final DateTime? created;
  final DateTime? updated;

  Post({
    this.title,
    this.content,
    this.user,
    this.created,
    this.updated,
  });

  Post.fromJson(Map<String, dynamic> json)
      : title = json['title'],
        content = json['content'],
        user = UserModel.fromMap(json['user']),
        created = json['created'].toDate(),
        updated = json['updated'].toDate();

  Map<String, Object?> toJson() {
    return {
      'title': title,
      'content': content,
      'user': user!.toJson(),
      'created': created,
      'updated': updated,
    };
  }
}

Post Controller 

추후 insert 성공, 실패 분기 추가

  Future<void> insert({
    required String title,
    required String content,
    required UserModel user,
  }) async {
    await _postRepository.insert(title: title, content: content, user: user);
  }

Post Provider

collection의 ref를 미리 가져와 add를 통해 insert

class PostProvider {
  final postRef = firebaseFirestore.collection("post").withConverter<Post>(
        fromFirestore: (snapshot, options) => Post.fromJson(snapshot.data()!),
        toFirestore: (post, options) => post.toJson(),
      );

  Future<void> insert(Post post) async => await postRef.add(post);
}

Post Repository

  Future<void> insert({
    required String title,
    required String content,
    required UserModel user,
  }) async {
    Post post = Post(
      title: title,
      content: content,
      user: user,
      created: DateTime.now(),
      updated: DateTime.now(),
    );
    await _postProvider.insert(post);
  }

Git Commit

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

 

글쓰기 구현 · mugon-dev/flutter_firebase_blog@a76dbd0

Permalink This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository. Browse files 글쓰기 구현 Loading branch information Showing 12 changed files with 267 additions and 24 deletions. +20 −0 lib/

github.com

 

728x90

User Info Page

 현재 두가지 버전으로 user정보를 가져오는데 obx로 상태관리를 할때 argument로 가져온 데이터는 상태가 바뀌어도 ui가 변하지 않음

Obx(() => Column(
  children: [
    // argument 로 가져오는 데이터는 상태가 바뀌지 않음
    Text("회원 argument 유저네임: ${user.name}"),
    Text("회원 argument 이메일: ${user.email}"),
    Text("회원 argument nickname: ${user.nickname}"),
    Text("회원 유저네임 : ${a.firestoreUser.value!.name}"),
    Text("회원 이메일 : ${a.firestoreUser.value!.email}"),
    Text(
    "회원 nickname : ${a.firestoreUser.value!.nickname}"),
  ],
  )),
  SizedBox(height: 10),
  Padding(
    padding: const EdgeInsets.all(16.0),
    child: CustomElevatedButton(
    	text: "회원정보 수정",
    	funPageRoute: () {
    		Get.to(() => UserInfoUpdatePage(), arguments: user);
  	}),
),

User Update Page

Get.back을 통해 페이지 히스토리 관리

추후 update 결과에 따른 분기를 통해 페이지 이동 유무 넣기

class UserInfoUpdatePage extends StatelessWidget {
  final _formkey = GlobalKey<FormState>();
  final _nickname = TextEditingController();
  final _email = TextEditingController();
  UserModel user = Get.arguments;

  @override
  Widget build(BuildContext context) {
    _nickname.text = "${user.nickname}";
    _email.text = "${user.email}";
    return Scaffold(
        appBar: AppBar(
          title: Text("UserInfo Update"),
        ),
        body: ListView(
          children: [
            Container(
              width: 120,
              height: 120,
              child: Center(
                child: ClipRRect(
                  borderRadius: BorderRadius.circular(40),
                  child: Container(
                    width: 100,
                    height: 100,
                    child: Image.network(
                      "https://i.stack.imgur.com/l60Hf.png",
                      fit: BoxFit.fill,
                    ),
                  ),
                ),
              ),
            ),
            Padding(
              padding: const EdgeInsets.all(16.0),
              child: Center(
                child: _userInfoForm(),
              ),
            ),
          ],
        ));
  }

  Widget _userInfoForm() {
    return Form(
      key: _formkey,
      child: Column(
        children: [
          CustomTextFormField(
            controller: _nickname,
            hint: "Nickname",
            funValidator: validatorUsername(),
          ),
          SizedBox(height: 15),
          CustomElevatedButton(
            text: "회원 정보 수정",
            funPageRoute: () async {
              _formkey.currentState!.validate();
              UserController.to.updateUserDetail(_nickname.text, user.uid!);
              Get.back();
            },
          ),
          SizedBox(height: 15),
          CustomElevatedButton(
            text: "취소",
            funPageRoute: () {
              Get.back(closeOverlays: true);
            },
          ),
        ],
      ),
    );
  }
}

Controller

uid를 넘겨야 firebase user collection의 현재 로그인한 유저 정보를 찾을 수 있음

  Future<void> updateUserDetail(String nickname, String uid) async {
    await _userRepository.updateUserDetail(nickname, uid);
  }

UpdateReqDto

업데이트할 정보를 json으로 넘겨주기 위한 클래스

uid는 업데이트 할 필요없기 때문에 toJson의 파라미터 값으로 넣지 않음

class UpdateReqDto {
  final String? uid;
  final String? nickname;

  UpdateReqDto({this.nickname, this.uid});
  Map<String, dynamic> toJson() => {
        "nickname": nickname,
      };
}

Repository

  Future<void> updateUserDetail(String nickname, String uid) async {
    UpdateReqDto updateUser = UpdateReqDto(nickname: nickname, uid: uid);
    await _userProvider.updateUserDetail(updateUser);
  }

Provider

  Future<void> updateUserDetail(UpdateReqDto user) async =>
      await firebaseFirestore.doc('/users/${user.uid}').update(user.toJson());

Git commit

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

 

user update page 구현 · mugon-dev/flutter_firebase_blog@b633854

Permalink This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository. Browse files user update page 구현 Loading branch information Showing 7 changed files with 158 additions and 31 deletions. +9 −0

github.com

 

728x90

참고

앱 시작시 AuthController의 onReady에서 authStateChanges를 관찰하고 있기 때문에  로그아웃을 하지 않으면 자동 로그인이 되며 유저 정보 또한 저장되어 있음

AuthContorller

  Rxn<User> firebaseUser = Rxn<User>();
  Rxn<UserModel> firestoreUser = Rxn<UserModel>();

  @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());
    }
  }

  // Firebase user a realtime stream
  Stream<User?> get user => auth.authStateChanges();

  //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()!));
  }

1. user info 페이지 이동할때 argument에 넣어 이동

home page

UserModel user = await AuthController.to.getFirestoreUser();
Get.to(() => UserInfo(), arguments: user);

2. user info 페이지에서 불러오기

user info page

AuthController a = Get.put(AuthController());

Text("회원 유저네임 : ${a.firestoreUser.value!.name}")

UserInfoPage

class UserInfo extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    // UserController u = Get.put(UserController()); 앱 재시작할때 데이터가 없음
    AuthController a = Get.put(AuthController());
    UserModel user = Get.arguments;
    return Scaffold(
      appBar: AppBar(
        title: Text("userInfo"),
      ),
      body: Container(
        width: double.infinity,
        child: Column(
          children: [
            SizedBox(height: 50),
            Container(
              width: 120,
              height: 120,
              child: Center(
                child: ClipRRect(
                  borderRadius: BorderRadius.circular(40),
                  child: Container(
                    width: 100,
                    height: 100,
                    child: Image.network(
                      "https://i.stack.imgur.com/l60Hf.png",
                      fit: BoxFit.fill,
                    ),
                  ),
                ),
              ),
            ),
            SizedBox(height: 50),
            Text("회원 argument 유저네임: ${user.name}"),
            Text("회원 argument 이메일: ${user.email}"),
            Text("회원 유저네임 : ${a.firestoreUser.value!.name}"),
            Text("회원 이메일 : ${a.firestoreUser.value!.email}"),
            // Text("회원 가입날짜 : ${u.principal.value.created}"),
          ],
        ),
      ),
    );
  }
}

Git Commit

https://github.com/mugon-dev/flutter_firebase_blog/commit/2fe2b322187d351f1f13f3881d3cdeca84e464db

 

user info 페이지 구현 argument 방식과 controller 방식 · mugon-dev/flutter_firebase_blog@2fe2b32

Permalink This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository. Browse files user info 페이지 구현 argument 방식과 controller 방식 Loading branch information Showing 7 changed files with

github.com

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

 

user info 페이지에 프로필 사진 UI 추가 · mugon-dev/flutter_firebase_blog@a2dc7b2

Permalink This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository. Browse files user info 페이지에 프로필 사진 UI 추가 Loading branch information Showing 2 changed files with 31 additions

github.com

 

728x90

참고

https://firebase.flutter.dev/docs/auth/social

 

Social Authentication | FlutterFire

Social authentication is a multi-step authentication flow, allowing you to sign a user into an account or link

firebase.flutter.dev

Google icon event

InkWell(
  onTap: () async {
    int? result = await UserController.to.googleLogin();
    if (result != 1) {
    	Get.to(Splash());
    }
  },
)

UserController

  final UserRepository _userRepository = UserRepository();
  final principal = UserModel().obs;

  Future<int?> googleLogin() async {
    Map<String, dynamic> result = await _userRepository.googleLogin();
    if (result.containsKey("success")) {
      principal.value = await result["success"];
      print(principal.value.uid);
      return 1;
    } else {
      print(await result["fail"].code);
      return -1;
    }
  }

UserRepository

  final UserProvider _userProvider = UserProvider();

  Future<Map<String, dynamic>> googleLogin() async {
    GoogleSignInAccount? googleUser = await _userProvider.googleLogin();
    GoogleSignInAuthentication googleAuth = await googleUser!.authentication;
    final credential = GoogleAuthProvider.credential(
      accessToken: googleAuth.accessToken,
      idToken: googleAuth.idToken,
    );
    UserCredential userCredential =
        await FirebaseAuth.instance.signInWithCredential(credential);
    try {
      UserModel _newUser = UserModel(
        uid: userCredential.user!.uid,
        email: userCredential.user!.email,
        name: userCredential.user!.displayName,
        photoUrl: "photoUrl",
      );
      await _userProvider.joinDetail(_newUser);

      return {"success": _newUser};
    } on FirebaseAuthException catch (e) {
      return {"fail": e};
    }
  }

UserProvider

  Future<GoogleSignInAccount?> googleLogin() async =>
      await GoogleSignIn().signIn();

provider에서 google 로그인 요청 -> repository에서 결과값인 UserCredential을 UserModel로 받기 -> UserController에서 view로 결과 넘겨주기

 

Git Commit

https://github.com/mugon-dev/flutter_firebase_blog/commit/73f7631f3ef7300b17bebe384a954f17af56e1ea

 

구글 로그인 구현 및 클라우드 유저 정보 저장 · mugon-dev/flutter_firebase_blog@73f7631

Permalink This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository. Browse files 구글 로그인 구현 및 클라우드 유저 정보 저장 Loading branch information Showing 9 changed files with 1

github.com

 

728x90

로그인 버튼 함수

UserController.to.login(_email.text.trim(), _password.text.trim());

UserController

  Future<void> login(String email, String password) async {
    String result = await _userRepository.login(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);
    }
  }

LoginReqDto

class LoginReqDto {
  final String? email;
  final String? password;

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

UserRepository

  Future<String> login(String email, String password) async {
    LoginReqDto dto = LoginReqDto(email, password);
    try {
      await _userProvider.login(dto);
      return "success";
    } on FirebaseAuthException catch (e) {
      return e.code;
    }
  }

UserProvider

  Future<UserCredential> login(LoginReqDto dto) async =>
      await auth.signInWithEmailAndPassword(
          email: dto.email.toString(), password: dto.password.toString());

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

+ Recent posts