-
Flutter - 코딩마스터하기|서치 페이지 대신 팔로/언팔로 페이지 만들기, 파이어베이스개발/Flutter 2021. 11. 8. 21:11반응형
팔로 언팔로 페이지 만들기
우선 screens 폴더에 search_screen.dart를 만든다. 그리고 이번에 팔로/언팔로를 위해서 ListView.separated를 사용해 볼 것이다. 우선 SafeArea를 해주고 그안에 ListView.seperated를 넣어준다. 그 안에는 itemBuilder과 separatorBuilder, itemCount를 넣어주면 된다. itemBuilder은 칸의 아이템을 넣을 수 있는 부분이고 separatorBuilder는 각 item을 나눠주는 부분이다. 우선 itemBuilder와 separatorBuilder에 (context, index){}를 해주고 itemBuilder에는 return ListTile을 해준다. leading에는 RoundedAvatar를 하고 title에는 Text로 username $index를 해주고 subtitle에도 Text로 user bio number $index를 해준다. trailing에는 버튼을 위해서 Container로 우선 해주고 width 80, height는 30 alignmnet는 center로 해준다. borderRadius에는 circular에 8을 준다. decoration에는 Boxdecoration으로 color에 red[50] boder는 Border.all()에 red와 width 0.5를 준다. Container child는 Text로 following에 textAlign은 center, bold를 주면 각 리스트 칸의 아이템부분의 틀은 완성이다. separatorBuilder의 return에는 Divider로 color에 grey를 주기만 하면 각 칸 사이가 회색선으로 나뉜다. 아이템갯수는 30개정도로 우선 해보자.
class _SearchScreenState extends State<SearchScreen> { @override Widget build(BuildContext context) { return SafeArea( child: ListView.separated( itemBuilder: (context, index) { return ListTile( leading: RoundedAvatar(index: index,), title: Text('username $index'), subtitle: Text('user bio number $index'), trailing: Container( height: 30, width: 80, alignment: Alignment.center, decoration: BoxDecoration( color: Colors.red[50], border: Border.all( color: Colors.red, width: 0.5, ), borderRadius: BorderRadius.circular(8), ), child: Text( 'Following', textAlign: TextAlign.center, style: TextStyle( fontWeight: FontWeight.bold, ), ), ), ); }, //(context, index) separatorBuilder: (context, index) { return Divider(color: Colors.grey,); }, //아이템 사이사이 부분 itemCount: 30, //아이템 갯수 ), ); } }
버튼 클릭시 follow unfollow 와 색 바꾸기
우선 follow를 구분하기 위해서 List<bool>을 만들어 전체 리스트의 상태를 언팔로우로 해준다. state class의 상단에 List<bool> followings = List.generate(30, (index) => false)로 30개의 false를 만들어 주고, itemCount를 30에서 followings.length로 바꿔준다. 그리고 ListTile의 onTap에 누르면 팔로에서 언팔로, 언팔로에서 팔로로 바뀌어야 하므로 setState안에 followings[index] = !followings[index]를 해주면 된다. BoxDecoration과 Border.all에 색부분에 followings[index]?red:blue로 해주고 실행하면 바뀌는 것을 알 수 있다.
class _SearchScreenState extends State<SearchScreen> { List<bool> followings = List.generate(30, (index) => false); @override Widget build(BuildContext context) { return SafeArea( child: ListView.separated( itemBuilder: (context, index) { return ListTile( onTap: () { setState(() { followings[index] = !followings[index]; }); }, leading: RoundedAvatar( index: index, ), title: Text('username $index'), subtitle: Text('user bio number $index'), trailing: Container( height: 30, width: 80, alignment: Alignment.center, decoration: BoxDecoration( color: followings[index] ? Colors.blue[50] : Colors.red[50], border: Border.all( color: followings[index] ? Colors.blue : Colors.red, width: 0.5, ), borderRadius: BorderRadius.circular(8), ), child: Text( followings[index] ?'Following' : 'Unfollowing', textAlign: TextAlign.center, style: TextStyle( fontWeight: FontWeight.bold, ), ), ), ); }, //(context, index) separatorBuilder: (context, index) { return Divider( color: Colors.grey, ); }, //아이템 사이사이 부분 itemCount: followings.length, //아이템 갯수 ), ); } }
파이어베이스
우선 프로젝트를 만든다. 그리고 그 프로젝트에서 Authentication에서 이메일과 암호를 켜준다. 그리고 프로젝트 설정에서 안드로이드 버튼을 눌러준다. andoriod 폴더에 app - src - main - androidmanifest에서 패키지 네임을 복사해서 넣어준다. 그리고 google-services.json파일을 다운받아서 app 폴더에 넣어준다. 그리고 andorid 폴더의 build.gradle에 classpath 'com.google.gms:google-services:4.3.3' 를 buildscript - dependencies에 넣어준다. pubspec.yaml에 firebase_auth: ^0.16.1를 추가해준다. 그 후 build.gradle에서 dependencies에서 가장 위의 tools.build:gradle를 4.0.1로 변경해준다. 에러가 나오면 읽고 경로를 따라서 해당부분을 변경해주면 된다.
firebase auth 상태 모델 생성.
firebase_auth: ^0.18.0부터는 변경 전에서 변경 후 값 사용
변경전 => 변경후
FirebaseUser => User
AuthResult => UserCredential
currentUser() => currentUser
onAuthStateChanged => authStateChanges()
models에 firebase_auth_state.dart를 만들어 준다. class FirebaseAuthState extends ChangeNotifier{}로 만들어 준다. 상태에 대한 부분을 위해 enum으로 FirebaseAuthStatus를 만들어 주고 signout, progress, signin을 생성한다. 그리고 상태에 대한 부분인 FirebaseAuthStatus _firebaseAuthStatus = FirebaseAuthStatus.progress; FirebaseUser _firebaseUse;을 만들어 준다. 그리고 메소드를 만들어 주는데 로그인에 대한 부분으로 다음과 같이 생성해준다.
class FirebaseAuthState extends ChangeNotifier { FirebaseAuthStatus _firebaseAuthStatus = FirebaseAuthStatus.progress; FirebaseUser _firebaseUse; void changeFirebaseAuthStatus([FirebaseAuthStatus firebaseAuthStatus]) { if (firebaseAuthStatus != null) { _firebaseAuthStatus = firebaseAuthStatus; } else { if (_firebaseUse != null) { _firebaseAuthStatus = FirebaseAuthStatus.signin; } else { _firebaseAuthStatus = FirebaseAuthStatus.signout; } } notifyListeners(); } } enum FirebaseAuthStatus { signout, progress, signin }
inject FirebaseAuthState via provider
상태에서 firebase에서 상태를 받아왔을때 변화시키는 부분을 만들어 주자. void로 watchAuthChange(){}를 만들어 준다. 우선 클래스에 FirebaseAuth _firebaseAuth = FirebaseAuth.instance를 만들어 주고 FirebaseUser _firebaseUser;도 만들어 준다. 그리고 _firebaseAuth.onAuthStateChanged를 이용해서 상태가 바뀔때마다 값을 받아온다. .listen(firebaseUser)를 해주고{} 안에 if문에 firebaseUser와 _firebaseUser가 둘다 null이면 그냥 return;을 해주고 두 값이 다르면 _firebaseUser = firebaseUser로 해준다. 그리고 changeFirebaseAUthStatus를 해서 아래의 메소드를 실행시켜주면 된다. 그리고 main에서도 각 값에 따라서 switch를 통해 아웃이면 인증페이지 인이면 메인페이지 그외는 로딩페이지로 가게 만들어 준다.
main.dart class MyApp extends StatelessWidget { FirebaseAuthState _firebaseAuthState = FirebaseAuthState(); @override Widget build(BuildContext context) { return ChangeNotifierProvider<FirebaseAuthState>.value( value: _firebaseAuthState, child: MaterialApp( home: Consumer<FirebaseAuthState>( builder: (BuildContext context, FirebaseAuthState firebaseAuthState, Widget widget) { switch (firebaseAuthState.firebaseAuthStatus) { case FirebaseAuthStatus.signout: return AuthScreen(); case FirebaseAuthStatus.progress: return MyProgressIndicator(); case FirebaseAuthStatus.signin: return HomePage(); default: return MyProgressIndicator(); } }, ), theme: ThemeData( primarySwatch: white, ), ), ); } }
firebase_auth_state.dart class FirebaseAuthState extends ChangeNotifier { FirebaseAuthStatus _firebaseAuthStatus = FirebaseAuthStatus.progress; FirebaseAuth _firebaseAuth = FirebaseAuth.instance; FirebaseUser _firebaseUser; void watchAuthChange() { _firebaseAuth.onAuthStateChanged.listen((firebaseUser) { if (firebaseUser == null && _firebaseUser == null) { return; } else if (firebaseUser != _firebaseUser) { _firebaseUser = firebaseUser; changeFirebaseAuthStatus(); } }); } void changeFirebaseAuthStatus([FirebaseAuthStatus firebaseAuthStatus]) { if (firebaseAuthStatus != null) { _firebaseAuthStatus = firebaseAuthStatus; } else { if (_firebaseUser != null) { _firebaseAuthStatus = FirebaseAuthStatus.signin; } else { _firebaseAuthStatus = FirebaseAuthStatus.signout; } } notifyListeners(); } FirebaseAuthStatus get firebaseAuthStatus => _firebaseAuthStatus; } enum FirebaseAuthStatus { signout, progress, signin }
testing sign in and sign out flow
기존 pushReplacement로 페이지를 변경하였지만 firebase를 통해서 sign in sign up sign out등의 기능을 작동시키려고 한다. Provider를 통해서 할 수 있다. 우선 sign up과 sign in 의 _submitButton의 onpressed에 페이지 이동부분을 삭제하고 Provider를 통해 이동한다. Provider.of<FirebaseAuthState>(context, listen: false,)를 해준다. 여기서는 listen을 사용하지 않기때문에 안쓰는것이 아닌 false를 해줘야 에러가 나지않고 작동한다. 그리고 .changeFirebaseAuthStatus(FirebaseAuthStatus.signin)을 해줘서 signin으로 바꿔준다. 실행하면 로그인이나 가입시 정상 기입하고 버튼을 누르면 작동하는 것을 알 수 있다. profile_side_menu에서 sign out에 해당하는 부분에서 위와같이 해주고 FirebaseAuthStatus.signout로 해주면 로그아웃 되는 것을 알 수 있다.
sign_in_fomr.dart, sign_up_form.dart FlatButton _submitButton(BuildContext context) { return FlatButton( onPressed: () { if (_formKey.currentState.validate()) { //3개다 true면 서버에 저장하거나 값 읽어오거나 하면됨. // Navigator.of(context).pushReplacement( // MaterialPageRoute( // builder: (context) => HomePage(), // ), // ); Provider.of<FirebaseAuthState>(context, listen: false,) //listen : false로 안해주면 에러남. .changeFirebaseAuthStatus(FirebaseAuthStatus.signin); } }, child: Text( 'Sign In', style: TextStyle(color: Colors.white), ), color: Colors.blue, shape: RoundedRectangleBorder( borderRadius: BorderRadius.circular(common_xxs_gap), ), ); }
profile_side_menu.dart ListTile( leading: Icon( Icons.exit_to_app, color: Colors.black87, ), title: Text('Log out'), onTap: () { Provider.of<FirebaseAuthState>(context, listen: false,) .changeFirebaseAuthStatus(FirebaseAuthStatus.signout); }, ),
using animated switcher for sign in-out transition
각 부분이 로그인 이나 아웃을 할때 화면의 효과를 위해 AnimatedSwitcher을 줄것이다. 우선 MyApp 클래스 내 상단에 현재 위젯을 정해줄 Widget _currentWidget;를 만들어 주고 switch를 _currentWidget = AuthScreen();과 같이 각 부분에 맞게 변경 해준다. 그리고 switch 밖 하단에 return AnimatedSwitcher()을 주고 duration에 duration을 주고, child에 _currentWidget를 해주면 정상 작동한다.
class MyApp extends StatelessWidget { FirebaseAuthState _firebaseAuthState = FirebaseAuthState(); Widget _currnetWidget; @override Widget build(BuildContext context) { return ChangeNotifierProvider<FirebaseAuthState>.value( value: _firebaseAuthState, child: MaterialApp( home: Consumer<FirebaseAuthState>( builder: (BuildContext context, FirebaseAuthState firebaseAuthState, Widget widget) { switch (firebaseAuthState.firebaseAuthStatus) { case FirebaseAuthStatus.signout: _currnetWidget = AuthScreen(); break; case FirebaseAuthStatus.signin: _currnetWidget = HomePage(); break; default: _currnetWidget = MyProgressIndicator(); break; } return AnimatedSwitcher( duration: duration, child: _currnetWidget, ); }, ), theme: ThemeData( primarySwatch: white, ), ), ); } }
sign out method 생성 후 사용
기존 sign out은 상태만 바꿔줬을뿐 값은 비워주지 않았기 때문에 firebase_auth_state.dart로 가서 void의 메소드를 만들어준다. 이름은 signOut로 해주고 _firebaseAuthStatus = FirebaseAuthStatus.signout;로 해준다. 또 if(_firebaseUser != null)로 유저값이 안비워져있다면 _firebaseUser = null로 해주고 _firebaseAuth.signOut를 해준뒤 if문 밖에 notifyListeners();를 해주면 된다. profile_side_menu에서 changeFirebaseAuthStatus 부분을 signOut()로 바꿔주고 실행하면 정상 작동 하는 것을 알 수있다.
void signOut(){ _firebaseAuthStatus = FirebaseAuthStatus.signout; if(_firebaseUser != null){ _firebaseUser = null; _firebaseAuth.signOut(); } notifyListeners(); }
wire sign in-up with firebase auth
firebase에 등록을 해서 register와 login이 되게 해보자. firebase_auth_state에 register와 login 메소드를 만든다. 그리고 그 안에 registerUser는 _firebaseAuth.createUserWithEmailAndPassword로()에 email에 email password에 password를 해준다. login은 _firebaseAuth.signInWithEmailAndPassword를 해주고 email,password를 넣어준다. sign_in_form에 provider에 .changeFirebaseAuthStatus(FirebaseAuthStatus.signin); 를 .login(email: _emailController.text,password: _pwController.text);로 해주고 sign_up_form도 .registerUser(email: _emailController.text, password: _pwController.text); 로 변경해준다. 이상태에서 실행하면 가입은 되지만 화면이 안바뀌는데 main에 _firebaseAuthState.watchAuthChange();를 추가해서 상태를 변경해줘야 페이지가 변경된다.
sign_up_form.dart FlatButton _submitButton(BuildContext context) { return FlatButton( onPressed: () { if (_formKey.currentState.validate()) { //3개다 true면 서버에 저장하거나 값 읽어오거나 하면됨. // Navigator.of(context).pushReplacement( // MaterialPageRoute( // builder: (context) => HomePage(), // ), // ); Provider.of<FirebaseAuthState>(context, listen: false,) //listen : false로 안해주면 에러남. .registerUser(email: _emailController.text, password: _pwController.text); } }, child: Text( 'Join', style: TextStyle(color: Colors.white), ), color: Colors.blue, shape: RoundedRectangleBorder( borderRadius: BorderRadius.circular(common_xxs_gap), ), );
sing_in_form.dart FlatButton _submitButton(BuildContext context) { return FlatButton( onPressed: () { if (_formKey.currentState.validate()) { //3개다 true면 서버에 저장하거나 값 읽어오거나 하면됨. // Navigator.of(context).pushReplacement( // MaterialPageRoute( // builder: (context) => HomePage(), // ), // ); Provider.of<FirebaseAuthState>(context, listen: false,) //listen : false로 안해주면 에러남. .login(email: _emailController.text,password: _pwController.text); } }, child: Text( 'Sign In', style: TextStyle(color: Colors.white), ), color: Colors.blue, shape: RoundedRectangleBorder( borderRadius: BorderRadius.circular(common_xxs_gap), ), );
firebase_auth_state.dart void registerUser({@required String email, @required String password}){ _firebaseAuth.createUserWithEmailAndPassword(email: email, password: password); } void login({@required String email, @required String password}){ _firebaseAuth.signInWithEmailAndPassword(email: email, password: password); }
error handling for sign up
에러를 잡아보자. 에러의 코드는 가입에는 ERROR_WEAK_PASSWORD, ERROR_WEAK_PASSWORD, ERROR_EMAIL_ALREADY_IN_USE 가 있다. 그래서 createUserWithEmailAndPassword 뒤에 .catchError((error){}) 의 {}안에 print로 알수 있다. 우선 메시지로 보여주기 위해서 String _message = '';을 만들어 주고 switch(error.code)로 각 에러별로 나눌수 있다. 각 case에 에러별로 알맞은 메시지를 넣어준 후, 하단에 SnackBar를 생성하고 SnackBar(content: Text(_message))를 담아준다. 그리고 Scaffold.of(context).showSnackBar(snackBar);로 스낵바를 보여준다. 이때 context를 받아와야 하므로 메소드에 BuildContext context를 추가해 context를 받아오면 된다. Scaffold안에 있어야 SnackBar를 사용할 수 있는데 sign_up_form은 AuthScreen에 있어서 Scaffold에 들어가 있어서 받아올수 있어 동작한다. 그리고 sign_up_form에 registerUser에 context도 추가해 준다.
void registerUser(BuildContext context,{@required String email, @required String password}) { _firebaseAuth .createUserWithEmailAndPassword(email: email.trim(), password: password.trim()) .catchError((error) { print(error); String _message = ''; switch (error.code) { case 'ERROR_WEAK_PASSWORD': _message = '패스워드의 보안이 약합니다.'; break; case 'ERROR_WEAK_PASSWORD': _message = '올바른 이메일을 넣어주세요'; break; case 'ERROR_EMAIL_ALREADY_IN_USE': _message = '이미 사용중인 아이디 입니다.'; break; } SnackBar snackBar = SnackBar(content: Text(_message,),); Scaffold.of(context).showSnackBar(snackBar); }); }
error handling on login
로그인 부분도 똑같이 에러를 핸들링 해주자. 에러코드를 찾아서 똑같이 해주면 된다. 그리고 sign_in_form에도 context를 추가해주고 실행하면 잘 작동하는것을 볼 수 있다.
void login(BuildContext context, {@required String email, @required String password}) { _firebaseAuth .signInWithEmailAndPassword( email: email.trim(), password: password.trim()) .catchError((error) { String _message = ''; switch (error.code) { case 'ERROR_INVALID_EMAIL': _message = '메일 주소가 유효하지 않습니다.'; break; case 'ERROR_WRONG_PASSWORD': _message = '암호가 틀렸습니다.'; break; case 'ERROR_USER_NOT_FOUND': _message = '가입하지 않은 아이디입니다.'; break; case 'ERROR_USER_DISABLED': _message = '해당유저는 금지되어 있습니다.'; break; case 'ERROR_TOO_MANY_REQUESTS': _message = '너무 많이 시도하였습니다. 나중에 다시 시도해주세요.'; break; case 'ERROR_OPERATION_NOT_ALLOWED': _message = '허용되지 않았습니다.'; break; } SnackBar snackBar = SnackBar( content: Text( _message, ), ); Scaffold.of(context).showSnackBar(snackBar); }); }
반응형'개발 > Flutter' 카테고리의 다른 글
Flutter - 코딩마스터하기|Firebase Firestore 사용하기 (0) 2021.11.11 Flutter - 코딩마스터하기|페이스북 로그인 (0) 2021.11.09 Flutter - 코딩마스터하기|갤러리 레이아웃 (0) 2021.11.04 Flutter - 코딩마스터하기|Provider, 카메라 페이지 만들기2 (0) 2021.11.04 Flutter - 코딩마스터하기|카메라 페이지 만들기 (0) 2021.11.03 댓글