스마트시대
AUTHENTICATION4.3 Sign Up Form 4.4 Username Screen 4.5 FormButton 본문
AUTHENTICATION4.3 Sign Up Form 4.4 Username Screen 4.5 FormButton
스마트시대 2023. 5. 5. 00:284.3 Sign Up Form
email , username 을 입력받을 widget을 2개 만든다.
signup 화면에서 gesturedector 로 email AuthButton을 감싼다.
Method에 ‘_’ 접두어를 붙여서 private 와 같이 만들수 있다.
appBar, Scaffold Theme 를 main.dart에 만들기 Theme는 모든곳에서 적용됨. 오버라이드 가능 Sizes 는 ‘+’ 연산 가능
sign_up_screen
builder: (context) => const LoginScreen(),
),
);
}
----------------
// 버튼이나 글씨 누르면 다음 화면으로 넘어가는 함수
void _onEmailTap(BuildContext context) {
Navigator.of(context).push(
MaterialPageRoute(
builder: (context) => const EmailScreen(),
),
);
}
----------------
@override
Widget build(BuildContext context) {
textAlign: TextAlign.center,
),
Gaps.v40,
---------------- ----------------
// email인증 하기 위해 클릭 가능하게
GestureDetector(
onTap: () => _onEmailTap(context),
---------------- ----------------
child: const AuthButton(
icon: FaIcon(FontAwesomeIcons.user),
text: 'Use email and password',
),
),
Gaps.v16,
const AuthButton(
email_screen->이거 username_screen에 할 내용 4-4에서 수정
import 'package:flutter/material.dart';
import 'package:tiktok_clone/constants/gaps.dart';
import 'package:tiktok_clone/constants/sizes.dart';
class EmailScreen extends StatelessWidget {
const EmailScreen({super.key});
@override
Widget build(BuildContext context) {
return Scaffold(
backgroundColor: Colors.white,
appBar: AppBar(
title: const Text(
"Sign up",
),
),
// 좀더 텍스트 간격 정렬을 위해 padding
body: Padding(
padding: const EdgeInsets.symmetric(
horizontal: Sizes.size36,
),
child: Column(
// 텍스트 정렬하기
crossAxisAlignment: CrossAxisAlignment.start,
children: const [
Gaps.v40,
Text(
"Create username",
style: TextStyle(
fontSize: Sizes.size24,
fontWeight: FontWeight.w700,
),
),
Gaps.v8,
Text(
"You can always change this later.",
style: TextStyle(
fontSize: Sizes.size16,
color: Colors.black54,
fontWeight: FontWeight.w400,
),
),
],
),
),
);
}
}
main.dart
Widget build(BuildContext context) {
return MaterialApp(
title: 'TikTok Clone',
theme: ThemeData(
--------------
// 자세히 보면 scaffold는 약간 회색빛을 띄는데 이게 짜증나니 여기서 강제 흰색으로 지정
scaffoldBackgroundColor: Colors.white,
--------------
primaryColor: const Color(0xFFE9435A),
// appBar에서 글씨 지정하는 것도 어느정도 고정이니 여기서 전역적으로 지정하자
appBarTheme: const AppBarTheme(
foregroundColor: Colors.black,
backgroundColor: Colors.white,
// appbar와 navigationbar의 경계에 있는 그림자 수치
elevation: 0,
titleTextStyle: TextStyle(
color: Colors.black,
fontSize: Sizes.size16 + Sizes.size2,
fontWeight: FontWeight.w600,
),
),
),
-------------- --------------
home: const SignUpScreen(),
);
}
}
4.4 Username Screen
controller란? addlistener 가 controller에 어떤 식의 구조인지?
Controller는 user interaction을 받아들이고 state를 관리하는데 용이한 클래스. javascript에서 listener를 붙여서 user interaction에 해당하는 액션을 만들어주는거처럼 addlistener로 controller가 유저의 행동을 listen하고 그에따라 액션을 취함
TextField
InputDecoration : UnderlineInputBorder cursorColor , enableBorder, focusedBorder, 텍스트 입력을 위해서는 controller가 필요하므로 StateWidget으로 바꾸어주어야 함.
controller를 만들고, TextField 에 설정하고, TextField가 변할때 마다 setState를 하여 버튼이 틱톡 프라이머리 칼라 또는 회색이 되도록 해준다.
버튼 칼라가 부드럽게 바뀌게 하기 위해서는 AnimatedContainer를 사용함. animation은 duration 값으로 조정.



// Controller는 코드, 메서드 등으로 이 textField아 같은 위젯을 컨트롤할 수 있게 해준다.
Controller를 사용할 수 있게 해주기 위해서는 statelesswidget을 statefulwidget으로 바꿔줘야 한다.



user_screen.dart
import 'package:flutter/material.dart';
import 'package:tiktok_clone/constants/gaps.dart';
import 'package:tiktok_clone/constants/sizes.dart';
class UsernameScreen extends StatefulWidget {
const UsernameScreen({super.key});
@override
State<UsernameScreen> createState() => _UsernameScreenState();
}
class _UsernameScreenState extends State<UsernameScreen> {
// Controller는 코드, 메서드 등으로 이 textField아 같은 위젯을 컨트롤할 수 있게 해준다.
final TextEditingController _usernameController = TextEditingController();
// _username 정의하기
String _username = "";
// TextField의 텍스트 변화를 감지하기 위한 이벤트 리스너
@override
void initState() {
_usernameController.addListener(
() {
// print(_usernameController.text);
setState(() {
// 여기서 state를 저장해준다.
_username = _usernameController.text;
});
},
);
}
@override
Widget build(BuildContext context) {
return Scaffold(
backgroundColor: Colors.white,
appBar: AppBar(
title: const Text(
"Sign up",
),
),
// 좀더 텍스트 간격 정렬을 위해 padding
body: Padding(
padding: const EdgeInsets.symmetric(
horizontal: Sizes.size36,
),
child: Column(
// 텍스트 정렬하기
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Gaps.v40,
const Text(
"Create username",
style: TextStyle(
fontSize: Sizes.size24,
fontWeight: FontWeight.w700,
),
),
Gaps.v8,
const Text(
"You can always change this later.",
style: TextStyle(
fontSize: Sizes.size16,
color: Colors.black54,
),
),
Gaps.v16,
// 유저가 문자를 입력할 수 있는 위젯
TextField(
// TextEditingController 함수 만들었으니 여기다 정의
controller: _usernameController,
decoration: InputDecoration(
hintText: "Username",
// 글자 밑 부분 border 회색으로 바꿔주려면 이 2개 필요
enabledBorder: UnderlineInputBorder(
borderSide: BorderSide(
color: Colors.grey.shade400,
),
),
focusedBorder: UnderlineInputBorder(
borderSide: BorderSide(
color: Colors.grey.shade400,
),
),
),
// 핑크 커서 만들기
cursorColor: Theme.of(context).primaryColor,
),
Gaps.v16,
// Next button 만들기
FractionallySizedBox(
widthFactor: 1,
// 색변화할 때 효과를 주기 위해 AnimatedContainer로 변경
child: AnimatedContainer(
padding: const EdgeInsets.symmetric(
vertical: Sizes.size16,
),
decoration: BoxDecoration(
borderRadius: BorderRadius.circular(Sizes.size5),
color: _username.isEmpty
? Colors.grey.shade300 // if
: Theme.of(context).primaryColor, // else
),
// AnimatedContainer에는 이게 필요
duration: const Duration(microseconds: 500),
child: const Text(
"Next",
textAlign: TextAlign.center,
style: TextStyle(
color: Colors.white,
fontWeight: FontWeight.w600,
),
),
),
),
],
),
),
);
}
}
4.5 FormButton
TextStyle 애미메이션을 위해 AnimatedDefaultTextStyle 사용
하지만 child 까지 변화시켜주지는 않는다.

여기서 보면 글씨는 바로 회색에서 흰색으로 바뀌는걸 알 수 있다.
그 때 child에 필요한 위젯은 AnimatedDefaultTextStyle 이다.
--------------------
// child에 별도로 이 위젯이 필요
child: AnimatedDefaultTextStyle(
duration: const Duration(microseconds: 500),
// Duration과 함께 style도 여기로 옮겨야 함
style: TextStyle(
color: _username.isEmpty ? Colors.grey : Colors.white,
fontWeight: FontWeight.w600,
),
--------------------
child: const Text(
"Next",
재사용을 위해 FormButton을 widget으로 만듬 →
-------------------

전구에서 ExtractWidget 하고 에러나는 부분 import 넣어주고.
Widget으로 만들때는 생성자 매개변수을 어떻게 넣어주어야 하는지 고민필요.
패러메터 정의를 좀 더 수월하게 하기 위해 아래와 같이 코드 변경


),
Gaps.v16,
-------------------
// Next button 만들기
FormButton(
disabled: _username.isEmpty,
),
],
),
),
);
}
}
class FormButton extends StatelessWidget {
const FormButton({
super.key,
required this.disabled,
});
final bool disabled;
-------------------
@override
Widget build(BuildContext context) {
return FractionallySizedBox(
widthFactor: 1,
// 색변화할 때 효과를 주기 위해 AnimatedContainer로 변경
child: AnimatedContainer(
padding: const EdgeInsets.symmetric(
vertical: Sizes.size16,
),
decoration: BoxDecoration(
borderRadius: BorderRadius.circular(Sizes.size5),
-------------------
color: disabled
? Colors.grey.shade300 // if
: Theme.of(context).primaryColor, // else
),
-------------------
// AnimatedContainer에는 이게 필요
duration: const Duration(microseconds: 500),
// child에 별도로 이 위젯이 필요
child: AnimatedDefaultTextStyle(
duration: const Duration(microseconds: 500),
// Duration과 함께 style도 여기로 옮겨야 함
style: TextStyle(
-------------------
color: disabled ? Colors.grey : Colors.white,
fontWeight: FontWeight.w600,
-------------------
),
child: const Text(
"Next",
textAlign: TextAlign.center,
),
),
그리고 form_button.dart파일을 새로 만들어줌
import 'package:flutter/material.dart';
import '../../../constants/sizes.dart';
class FormButton extends StatelessWidget {
const FormButton({
super.key,
required this.disabled,
});
final bool disabled;
@override
Widget build(BuildContext context) {
return FractionallySizedBox(
widthFactor: 1,
// 색변화할 때 효과를 주기 위해 AnimatedContainer로 변경
child: AnimatedContainer(
padding: const EdgeInsets.symmetric(
vertical: Sizes.size16,
),
decoration: BoxDecoration(
borderRadius: BorderRadius.circular(Sizes.size5),
color: disabled
? Colors.grey.shade300 // if
: Theme.of(context).primaryColor, // else
),
// AnimatedContainer에는 이게 필요
duration: const Duration(microseconds: 500),
// child에 별도로 이 위젯이 필요
child: AnimatedDefaultTextStyle(
duration: const Duration(microseconds: 500),
// Duration과 함께 style도 여기로 옮겨야 함
style: TextStyle(
color: disabled ? Colors.grey : Colors.white,
fontWeight: FontWeight.w600,
),
child: const Text(
"Next",
textAlign: TextAlign.center,
),
),
),
);
}
}
@Controller을 initState에서 설정하고 사용한 다음 사용한 widget에서 dispose함수를 override하여 controller를 dispose해야 한다. 아니면 메모리 에러난다.
// TextField의 텍스트 변화를 감지하기 위한 이벤트 리스너
@override
void initState() {
_usernameController.addListener(
() {
// print(_usernameController.text);
setState(() {
// 여기서 state를 저장해준다.
_username = _usernameController.text;
});
},
);
}
// 위에 이벤트 리스너를 dispose해주지 않으면 비디오 재생등에서 메모리 부족으로 앱이 crash하는 걸 방지
@override
void dispose() {
_usernameController.dispose();
super.dispose();
}
EmailScreen은 usernameScreen 과 거의 비슷. 연결시 GestureDetector 사용
email_screen.dart
import 'package:flutter/material.dart';
import 'package:tiktok_clone/constants/gaps.dart';
import 'package:tiktok_clone/constants/sizes.dart';
class EmailScreen extends StatefulWidget {
const EmailScreen({super.key});
@override
State<EmailScreen> createState() => _EmailScreenState();
}
class _EmailScreenState extends State<EmailScreen> {
// Controller는 코드, 메서드 등으로 이 textField아 같은 위젯을 컨트롤할 수 있게 해준다.
final TextEditingController _usernameController = TextEditingController();
// _username 정의하기
String _username = "";
// TextField의 텍스트 변화를 감지하기 위한 이벤트 리스너
@override
void initState() {
_usernameController.addListener(
() {
// print(_usernameController.text);
setState(() {
// 여기서 state를 저장해준다.
_username = _usernameController.text;
});
},
);
}
// 위에 이벤트 리스너를 dispose해주지 않으면 비디오 재생등에서 메모리 부족으로 앱이 crash하는 걸 방지
@override
void dispose() {
_usernameController.dispose();
super.dispose();
}
@override
Widget build(BuildContext context) {
return Scaffold(
backgroundColor: Colors.white,
appBar: AppBar(
title: const Text(
"Sign up",
),
),
// 좀더 텍스트 간격 정렬을 위해 padding
body: Padding(
padding: const EdgeInsets.symmetric(
horizontal: Sizes.size36,
),
child: Column(
// 텍스트 정렬하기
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Gaps.v40,
const Text(
"What is your email?",
style: TextStyle(
fontSize: Sizes.size24,
fontWeight: FontWeight.w700,
),
),
Gaps.v16,
// 유저가 문자를 입력할 수 있는 위젯
TextField(
// TextEditingController 함수 만들었으니 여기다 정의
controller: _usernameController,
decoration: InputDecoration(
hintText: "Email",
// 글자 밑 부분 border 회색으로 바꿔주려면 이 2개 필요
enabledBorder: UnderlineInputBorder(
borderSide: BorderSide(
color: Colors.grey.shade400,
),
),
focusedBorder: UnderlineInputBorder(
borderSide: BorderSide(
color: Colors.grey.shade400,
),
),
),
// 핑크 커서 만들기
cursorColor: Theme.of(context).primaryColor,
),
Gaps.v16,
// Next button 만들기
FormButton(
disabled: _username.isEmpty,
),
],
),
),
);
}
}
class FormButton extends StatelessWidget {
const FormButton({
super.key,
required this.disabled,
});
final bool disabled;
@override
Widget build(BuildContext context) {
return FractionallySizedBox(
widthFactor: 1,
// 색변화할 때 효과를 주기 위해 AnimatedContainer로 변경
child: AnimatedContainer(
padding: const EdgeInsets.symmetric(
vertical: Sizes.size16,
),
decoration: BoxDecoration(
borderRadius: BorderRadius.circular(Sizes.size5),
color: disabled
? Colors.grey.shade300 // if
: Theme.of(context).primaryColor, // else
),
// AnimatedContainer에는 이게 필요
duration: const Duration(microseconds: 1000),
// child에 별도로 이 위젯이 필요
child: AnimatedDefaultTextStyle(
duration: const Duration(microseconds: 1000),
// Duration과 함께 style도 여기로 옮겨야 함
style: TextStyle(
color: disabled ? Colors.grey : Colors.white,
fontWeight: FontWeight.w600,
),
child: const Text(
"Next",
textAlign: TextAlign.center,
),
),
),
);
}
}
마지막으로 next 버튼 누른 다음 행동을 함수 정의
// next 버튼 누른 다음 행동을 함수 정의
void _onNextTap() {
if (_username.isEmpty) return;
Navigator.of(context).push(
MaterialPageRoute(
builder: (context) => const EmailScreen(),
),
);
}
-------------------
// 핑크 커서 만들기
cursorColor: Theme.of(context).primaryColor,
),
Gaps.v28,
-------------------
// Next button 만들기, // next 버튼 누른 다음 행동을 함수 정의
GestureDetector(
onTap: _onNextTap,
child: FormButton(
disabled: _username.isEmpty,
),
),

지금까지의 구조

'Programing > Flutter' 카테고리의 다른 글
| ONBOARDING 5.1 Interests Screen 5.2 Scroll Animations 5.3 Tutorial Screen 5.4 AnimatedCrossFade (0) | 2023.05.08 |
|---|---|
| AUTHENTICATION 4.6 Email Screen 4.7 Password Screen 4.8 Birthday Screen 4.9 Login Form (0) | 2023.05.06 |
| AUTHENTICATION 4.1 Login Screen tiktok 4.2 AuthButton (0) | 2023.04.25 |
| AUTHENTICATION 4. sign up screen (0) | 2023.04.24 |
| 이런 데서 코드 작성해서 결과 확인 vscode (0) | 2023.04.09 |