-
Flutter - 인별 클론 코딩 V1.0,가입페이지개발/Flutter 2021. 10. 19. 17:44반응형
가입 페이지 만들기
screens에 signup_page.dart를 만들어주고 signin_page를 그대로 복사해온후 클래스명을 리네임해준다. signin_page에서 bool으로 바꿔줘도 되지만 새로 만들어준 이유는 하나로 만들었을때 깨끗해 보이지 않아서 그냥 두개를 만들어준것이다. flag로 바꿔줘도 상관없다. 그리고 signin과 signup 페이지의 각 페이지로 이동 하는 부분을 pushReplacement 해서 해당 페이지로 이동 하게 해준다.
class _SignInPageState extends State<SignInPage> { @override Widget build(BuildContext context) { return Scaffold( resizeToAvoidBottomInset: false,//키보드 올라오면 자동 사이즈 조절 되는 부분, 이메일 암호 부분이 아니라서 false body: SafeArea( child: Stack( children: [ SignInForm(), _goToSignUpPageBtn(context), ], ), ), ); } Positioned _goToSignUpPageBtn(BuildContext context) { //제일하단 부분 return Positioned( left: 0, right: 0, bottom: 0, height: 40, child: FlatButton( shape: Border( top: BorderSide(color: Colors.grey[300],), ), onPressed: () { final route = MaterialPageRoute(builder: (context)=>SignUpPage()); Navigator.pushReplacement(context, route); }, child: RichText( // Text 한가지에 2가지 스타일이 있어서 씀씀 textAlign: TextAlign.center, text: TextSpan( style: const TextStyle(), //이거는 2개 따로주는데에 먹히지 않음 안주면 작동안함 그냥 이대로 넣어두어야함, 왜인지는 모름 children: [ TextSpan( text: "Don't have an account?", style: TextStyle( fontWeight: FontWeight.w300, color: Colors.black54,), ), TextSpan( text: " Sign Up", style: TextStyle( fontWeight: FontWeight.bold, color: Colors.blue[600],), ) ], ), ), ), ); } }
가입 폼 만들기
우선 sign_in_form.dart 파일을 오른쪽클릭으로 refactor - copy를 해주고 이름을 sign_up_form.dart으로 해준후 클래스 명도 SignUpForm으로 rename 해준다. 그리고 TextEditingController을 _cpwController를 만들어준다. 암호확인을 위한 부분이다. disepose에도 _cpwController.dipose() 해준다. 그리고 암호 입력 부분을 복사해서 붙여넣어준뒤 controller 옵션을 _cpwController로 변경해주고 decoration도 힌트글씨를 넣어주는 부분을 ConfirmPassword로 해준다. 그리고 validator에서 암호가 일치하지 않을때 맞지 않다고 해주는 부분을 if문에 추가해준다. value != _pwController.text로 해주면 비교해서 맞지 않으면 return의 값을 보여준다.
class _SignUpFormState extends State<SignUpForm> { GlobalKey<FormState> _formKey = GlobalKey<FormState>(); TextEditingController _emailController = TextEditingController(); TextEditingController _pwController = TextEditingController(); TextEditingController _cpwController = TextEditingController(); @override void dispose() { // 페이지가 닫힐때 필요없는 부분 닫는거, dispose 안해주면 계속 동작으로 실행되서 좋지 않음 _emailController.dispose(); _pwController.dispose(); _cpwController.dispose(); super.dispose(); } @override Widget build(BuildContext context) { return Scaffold( resizeToAvoidBottomInset: true, // 키보드가 올라올때 화면이 올라가는 부분, 전체가 올라가는게 아님 다른데서 만든 밑에 가입 버튼은 키보드에 가려지고 여기에서 만든 부분만 됨 body: Padding( padding: const EdgeInsets.all(common_gap), child: Form( key: _formKey, child: Column( mainAxisAlignment: MainAxisAlignment.center, //정렬,위,아래,가운데 정하는것 mainAxisSize: MainAxisSize.max, //세로 공간 다 차지하게 crossAxisAlignment: CrossAxisAlignment.stretch, //column일때는 가로축 Row일때는 세로축 cross임, stretch는 좌우로 늘려서 공간 차지 children: [ Spacer( flex: 6, //공간차지 ), Image.asset("assets/insta_text_logo.png"), Spacer( flex: 1, ), TextFormField( controller: _emailController, decoration: getTextFieldDecor('email'), validator: (String value) { if (value.isEmpty || !value.contains("@")) { return 'Please enter your email address!'; } return null; }, //유효성 검사하는 부분 ), Spacer( flex: 1, ), TextFormField( controller: _pwController, decoration: getTextFieldDecor('Password'), validator: (String value) { if (value.isEmpty) { return 'Please enter any password!'; } return null; }, ), Spacer( flex: 1, ), TextFormField( controller: _cpwController, decoration: getTextFieldDecor('Confirm Password'), validator: (String value) { if (value.isEmpty || value != _pwController.text) { return 'Password does not match!'; } return null; }, ), Spacer( flex: 2, ), FlatButton( onPressed: () { if (_formKey.currentState.validate()) { // Form 안에 validator가 작동을 함. final route = MaterialPageRoute(builder: (context)=>MainPage()); Navigator.pushReplacement(context, route); } }, child: Text( "Sign Up", style: TextStyle( color: Colors.white, ), ), color: Colors.blue, shape: RoundedRectangleBorder( borderRadius: BorderRadius.circular(6), ), disabledColor: Colors.blue[100], ), Spacer( flex: 2, ), Stack( // or 부분은 따로 만드는 부분이 없어서 선위에 흰선을 겹친거임, 그위에 or 를 써서 인스타랑 비슷하게 만듬 alignment: Alignment.center, children: [ Positioned( // 선을 끝까지 주기 위해서하는 부분 left 0, right 0을 줌 left: 0, right: 0, height: 1, child: Container( color: Colors.grey[300], height: 1, ), ), Container( height: 3, width: 50, color: Colors.grey[50], //배경색이랑 같음 ), Text( "OR", style: TextStyle( color: Colors.grey, fontWeight: FontWeight.bold, ), ), ], ), Spacer( flex: 2, ), FlatButton.icon( //페이스북 로그인 부분 textColor: Colors.blue, onPressed: () { simpleSnackbar(context, 'facebook pressed'); }, icon: ImageIcon(AssetImage("assets/icon/facebook.png")), label: Text("Login with Facebook"), ), Spacer( flex: 2, ), Spacer( flex: 6, ), ], ), ), ), ); } InputDecoration getTextFieldDecor(String hint) { return InputDecoration( hintText: hint, enabledBorder: OutlineInputBorder( borderSide: BorderSide( color: Colors.grey[300], width: 1, ), borderRadius: BorderRadius.circular(12), ), focusedBorder: OutlineInputBorder( borderSide: BorderSide( color: Colors.grey[300], width: 1, ), borderRadius: BorderRadius.circular(12), ), fillColor: Colors.grey[100], filled: true, // true로 해줘야 fillcolor이 나타남 ); } }
로그인 사인 페이지 페이드로 바꾸기
screens 폴더에 auth_page.dart를 새로 만들어주고 signin_page를 그대로 복사해온후 클래스 명을 AuthPage로 rename 해준다. 그리고 클래스 안에 상단에 Widget currentWidget를 SignInForm()으로 준다. 그리고 Stack의 children에 기존 form을 넣어 주던 부분을 currentWidget로 해준 후, 애니메이션을 위해 AnimatedSwitcher로 감싸주고 duration을 준다.그리고 하단에 FlatButton에 onPressed에 setState를 주고 그 안에 if문에 currentWidget is SignInForm 일때는 currentWidget를 SignUpForm으로 바꿔주고 else일때는 SignInForm으로 변경 해준다. 누를때마다 폼을 바꿔주기 위해서 이다. 그리고 TextSpan에도 로그인과 사인업 일때 멘트를 다르게 작성 해준다. 마지막으로 profile_side_menu.dart에서 SignInPage로 가는 부분을 AuthPage()로 변경해주면 하나로 통합되고 합치기 전 밑에서 위로 올라오던 애니메이션이 페이드로 바뀌는 것을 알 수 있다.
그리고 AnimatedSwitcher에서 transitionBuilder에서 transitionBuilder: (Widget child, Animation<double> animation){
return ScaleTransition(child: child,scale: animation,); 이렇게 애니메이션을 변경 해 줄 수 도 있다. 지금은 사용하지 않으므로 주석처리.
class _AuthPageState extends State<AuthPage> { Widget currentWidget = SignInForm(); @override Widget build(BuildContext context) { return Scaffold( resizeToAvoidBottomInset: false, //키보드 올라오면 자동 사이즈 조절 되는 부분, 이메일 암호 부분이 아니라서 false body: SafeArea( child: Stack( children: [ AnimatedSwitcher( child: currentWidget, duration: Duration( milliseconds: 300, ), ), _goToSignUpPageBtn(context), ], ), ), ); } Positioned _goToSignUpPageBtn(BuildContext context) { //제일하단 부분 return Positioned( left: 0, right: 0, bottom: 0, height: 40, child: FlatButton( shape: Border( top: BorderSide( color: Colors.grey[300], ), ), onPressed: () { setState(() { if(currentWidget is SignInForm){ currentWidget = SignUpForm(); }else{ currentWidget = SignInForm(); } }); }, child: RichText( // Text 한가지에 2가지 스타일이 있어서 씀씀 textAlign: TextAlign.center, text: TextSpan( style: const TextStyle(), //이거는 2개 따로주는데에 먹히지 않음 안주면 작동안함 그냥 이대로 넣어두어야함, 왜인지는 모름 children: [ TextSpan( text: (currentWidget is SignInForm)?"Don't have an account?":"Already have an account?", style: TextStyle( fontWeight: FontWeight.w300, color: Colors.black54, ), ), TextSpan( text: (currentWidget is SignInForm)?" Sign Up":" Sign In", style: TextStyle( fontWeight: FontWeight.bold, color: Colors.blue[600], ), ) ], ), ), ), ); } }
키보드 올라올때 레이아웃 문제 해결
Column을 사용해서 노란검정줄이 생기는것. Column을 ListView로 바꿔주면 됨. 그리고 Spacer도 SizedBox로 변경해줘야함. Spacer은 남은 공간을 flex 비율로 나눠서 사용하기때문에 ListView같이 길이가 정해지는 부분에선 사용 불가함. 또한 상단 옵션3가지도 사용못해서 주석처리. 모두 변경 해주면 정상 작동. 이상으로 레이아웃 부분은 끝! 다음부터는 백엔드 작업 시작.
그리고 singin_page와 signup_page는 삭제해도 될듯? auth_page로 합쳐서...
class _SignInFormState extends State<SignInForm> { GlobalKey<FormState> _formKey = GlobalKey<FormState>(); TextEditingController _emailController = TextEditingController(); TextEditingController _pwCotroller = TextEditingController(); @override void dispose() { // 페이지가 닫힐때 필요없는 부분 닫는거, dispose 안해주면 계속 동작으로 실행되서 좋지 않음 _emailController.dispose(); _pwCotroller.dispose(); super.dispose(); } @override Widget build(BuildContext context) { return Scaffold( resizeToAvoidBottomInset: true, // 키보드가 올라올때 화면이 올라가는 부분, 전체가 올라가는게 아님 다른데서 만든 밑에 가입 버튼은 키보드에 가려지고 여기에서 만든 부분만 됨 body: Padding( padding: const EdgeInsets.all(common_gap), child: Form( key: _formKey, child: ListView( // 위의 3개옵션을 Column에서 사용 ListView에서는 사용 하지 못함. // mainAxisAlignment: MainAxisAlignment.center, //정렬,위,아래,가운데 정하는것 // mainAxisSize: MainAxisSize.max, //세로 공간 다 차지하게 // crossAxisAlignment: CrossAxisAlignment.stretch, //column일때는 가로축 Row일때는 세로축 cross임, stretch는 좌우로 늘려서 공간 차지 children: [ // Spacer( // flex: 6, //남은 공간을 비율로 차지, 리스트뷰에서는 못씀 // ), SizedBox( height: 6, ), Image.asset("assets/insta_text_logo.png"), SizedBox( height: 1, ), TextFormField( controller: _emailController, decoration: getTextFieldDecor('email'), validator: (String value) { if (value.isEmpty || !value.contains("@")) { return 'Please enter your email address!'; } return null; }, //유효성 검사하는 부분 ), SizedBox( height: 1, ), TextFormField( controller: _pwCotroller, decoration: getTextFieldDecor('Password'), validator: (String value) { if (value.isEmpty) { return 'Please enter any password!'; } return null; }, ), SizedBox( height: 1, ), Text( "Forgotten password?", textAlign: TextAlign.end, style: TextStyle( color: Colors.blue[700], fontWeight: FontWeight.w600, ), ), SizedBox( height: 2, ), FlatButton( onPressed: () { if (_formKey.currentState.validate()) { // Form 안에 validator가 작동을 함. final route = MaterialPageRoute(builder: (context)=>MainPage()); Navigator.pushReplacement(context, route); } }, child: Text( "Log in", style: TextStyle( color: Colors.white, ), ), color: Colors.blue, shape: RoundedRectangleBorder( borderRadius: BorderRadius.circular(6), ), disabledColor: Colors.blue[100], ), SizedBox( height: 2, ), Stack( // or 부분은 따로 만드는 부분이 없어서 선위에 흰선을 겹친거임, 그위에 or 를 써서 인스타랑 비슷하게 만듬 alignment: Alignment.center, children: [ Positioned( // 선을 끝까지 주기 위해서하는 부분 left 0, right 0을 줌 left: 0, right: 0, height: 1, child: Container( color: Colors.grey[300], height: 1, ), ), Container( height: 3, width: 50, color: Colors.grey[50], //배경색이랑 같음 ), Text( "OR", style: TextStyle( color: Colors.grey, fontWeight: FontWeight.bold, ), ), ], ), SizedBox( height: 2, ), FlatButton.icon( //페이스북 로그인 부분 textColor: Colors.blue, onPressed: () { simpleSnackbar(context, 'facebook pressed'); }, icon: ImageIcon(AssetImage("assets/icon/facebook.png")), label: Text("Login with Facebook"), ), SizedBox( height: 2, ), SizedBox( height: 6, ), ], ), ), ), ); } InputDecoration getTextFieldDecor(String hint) { return InputDecoration( hintText: hint, enabledBorder: OutlineInputBorder( borderSide: BorderSide( color: Colors.grey[300], width: 1, ), borderRadius: BorderRadius.circular(12), ), focusedBorder: OutlineInputBorder( borderSide: BorderSide( color: Colors.grey[300], width: 1, ), borderRadius: BorderRadius.circular(12), ), fillColor: Colors.grey[100], filled: true, // true로 해줘야 fillcolor이 나타남 ); } }
반응형'개발 > Flutter' 카테고리의 다른 글
Flutter - 코딩마스터하기|새로운 시작! instagram V2.0 (0) 2021.10.21 Flutter - 인별 클론 코딩 V1.0,백엔드 종료,강의가 오래되서 버전이 맞지않음 (0) 2021.10.19 Flutter - 인별 클론 코딩 V1.0, 팔로우/언팔로우목록, 로그인페이지 (0) 2021.10.12 Flutter - 인별 클론 코딩 V1.0, 프로필 화면-4 (0) 2021.10.08 Flutter - 인별 클론 코딩 V1.0, 프로필 화면-3 (0) 2021.10.08 댓글