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});
State<UsernameScreen> createState() => _UsernameScreenState();
}
class _UsernameScreenState extends State<UsernameScreen> {
final TextEditingController _usernameController = TextEditingController();
String _username = "";
void initState() {
super.initState();
_usernameController.addListener(() {
setState(() {
_username = _usernameController.text;
});
});
}
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: const Text(
"Sign up",
),
),
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(
controller: _usernameController,
decoration: InputDecoration(
hintText: "Username",
enabledBorder: UnderlineInputBorder(
borderSide: BorderSide(
color: Colors.grey.shade400,
),
),
focusedBorder: UnderlineInputBorder(
borderSide: BorderSide(
color: Colors.grey.shade400,
),
),
),
cursorColor: Theme.of(context).primaryColor,
),
Gaps.v28,
FractionallySizedBox(
widthFactor: 1,
child: AnimatedContainer(
padding: const EdgeInsets.symmetric(
vertical: Sizes.size16,
),
decoration: BoxDecoration(
borderRadius: BorderRadius.circular(Sizes.size5),
color: _username.isEmpty
? Colors.grey.shade300
: Theme.of(context).primaryColor,
),
duration: const Duration(milliseconds: 500),
child: const Text(
'Next',
textAlign: TextAlign.center,
style: TextStyle(
color: Colors.white,
fontWeight: FontWeight.w600,
),
),
),
),
],
),
),
);
}
}
위 코드는 UsernameScreen
위젯을 Flutter에서 정의하고 있습니다. 해당 화면의 주요 내용은 다음과 같습니다:
초기화:
TextField
의 텍스트를 관리하기 위해 _usernameController
라는 TextEditingController
이 정의되어 있습니다._username
이라는 private 변수가 있습니다.initState()
메서드 내에서, _usernameController
에 리스너가 추가되어 있습니다. 컨트롤러의 텍스트가 변경될 때마다 _username
변수가 업데이트되고, setState
가 호출되어 위젯을 다시 빌드합니다.Scaffold:
Scaffold
위젯으로 구성되어 있습니다.AppBar
가 설정되어 있습니다.Body:
Padding
위젯입니다.Column
위젯 내부에서 위젯들이 수직으로 배열됩니다.Text
위젯으로 사용자는 사용자명을 생성하는 과정을 소개받습니다.TextField
는 사용자가 사용자명을 입력할 수 있습니다.AnimatedContainer
는 버튼 역할을 합니다. _username
이 비어있는 경우 버튼의 색상은 회색입니다. 그렇지 않으면 앱의 주요 색상을 사용합니다. 사용자명이 입력되거나 제거될 때 버튼의 색상 전환은 500 밀리초 동안 애니메이션됩니다.TextField:
TextField
는 _usernameController
와 연결된 컨트롤러를 가지고 있습니다. 이 컨트롤러는 변경 사항을 감지하고 _username
변수를 그에 따라 업데이트합니다.TextField
의 장식은 "Username"라는 힌트와 사용자 정의 밑줄 테두리를 보여줍니다. 커서 색상은 앱의 주제의 주요 색상으로 설정됩니다.AnimatedContainer (Next 버튼):
FractionallySizedBox
는 AnimatedContainer(버튼)이 부모의 전체 너비로 늘어나도록 합니다._username
이 비어 있는지 여부에 따라 동적으로 변경됩니다. 비어 있으면 버튼은 Colors.grey.shade300
색상이 되고, 그렇지 않으면 앱의 주제의 주요 색상을 사용합니다.이 화면은 사용자가 사용자명을 입력할 수 있는 회원가입 과정의 일부로 보입니다. Next 버튼의 색상은 사용자가 진행할 수 있는지(버튼이 주요 색상일 때) 아니면 사용자명이 아직 입력되지 않았는지(버튼이 회색일 때)를 나타냅니다.
initState
메서드는 Flutter의 StatefulWidget
수명 주기 중에서 위젯이 State
객체에 최초로 만들어질 때 한 번만 호출됩니다. 이 메서드는 State
객체가 위젯 트리에 삽입될 때 초기 설정이 필요한 경우 사용합니다.
제공해주신 initState
내용을 살펴보면:
super.initState();
: 상위 클래스의 initState
를 호출합니다. 이 호출은 항상 initState
의 시작 부분에서 이루어져야 합니다.
_usernameController.addListener(() { ... });
: _usernameController
에 리스너를 추가합니다. TextEditingController
의 값이 변경될 때마다 이 리스너는 호출됩니다.
setState(() { ... });
: _usernameController
의 텍스트가 변경될 때마다 setState
를 사용하여 State
객체를 업데이트합니다. setState
내부에서 _username
변수의 값을 _usernameController.text
로 업데이트합니다. setState
를 호출하면 build
메서드가 다시 호출되어 UI가 업데이트됩니다.
이렇게 하면 사용자가 TextField
에 텍스트를 입력할 때마다 _username
변수의 값이 실시간으로 업데이트되고, 필요한 경우 UI도 업데이트됩니다.
import 'package:flutter/material.dart';
import 'package:tiktok_clone/constants/gaps.dart';
import 'package:tiktok_clone/constants/sizes.dart';
import 'package:tiktok_clone/features/authentication/password_screen.dart';
import 'package:tiktok_clone/features/authentication/widgets/form_button.dart';
class EmailScreen extends StatefulWidget {
const EmailScreen({super.key});
State<EmailScreen> createState() => _EmailScreenState();
}
class _EmailScreenState extends State<EmailScreen> {
final TextEditingController _emailController = TextEditingController();
String _email = "";
void initState() {
super.initState();
_emailController.addListener(() {
setState(() {
_email = _emailController.text;
});
});
}
void dispose() {
_emailController.dispose();
super.dispose();
}
String? _isEmailValid() {
if (_email.isEmpty) return null;
final regExp = RegExp(
r"^[a-zA-Z0-9.a-zA-Z0-9.!#$%&'*+-/=?^_`{|}~]+@[a-zA-Z0-9]+\.[a-zA-Z]+");
if (!regExp.hasMatch(_email)) {
return "Email not valid";
}
return null;
}
void _onScaffoldTap() {
FocusScope.of(context).unfocus();
}
void _onSubmit() {
if (_email.isEmpty || _isEmailValid() != null) return;
Navigator.push(
context,
MaterialPageRoute(
builder: (context) => const PasswordScreen(),
),
);
}
Widget build(BuildContext context) {
return GestureDetector(
onTap: _onScaffoldTap,
child: Scaffold(
appBar: AppBar(
title: const Text(
"Sign up",
),
),
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(
controller: _emailController,
keyboardType: TextInputType.emailAddress,
onEditingComplete: _onSubmit,
decoration: InputDecoration(
hintText: "Email",
errorText: _isEmailValid(),
enabledBorder: UnderlineInputBorder(
borderSide: BorderSide(
color: Colors.grey.shade400,
),
),
focusedBorder: UnderlineInputBorder(
borderSide: BorderSide(
color: Colors.grey.shade400,
),
),
),
cursorColor: Theme.of(context).primaryColor,
),
Gaps.v28,
GestureDetector(
onTap: _onSubmit,
child: FormButton(
disabled: _email.isEmpty || _isEmailValid() != null,
),
),
],
),
),
),
);
}
}
위의 코드는 Flutter에서 사용되는 TextField
위젯입니다. 여기서 사용자는 이메일 주소를 입력할 수 있습니다. 해당 위젯의 각 속성에 대해 간략히 설명드리겠습니다.
controller:
TextEditingController
의 인스턴스인 _emailController
를 사용합니다. keyboardType:
TextInputType.emailAddress
를 사용하여 키보드 타입을 이메일 주소 입력용으로 설정합니다. 따라서 키보드에 '@' 기호 등이 우선적으로 표시됩니다.onEditingComplete:
_onSubmit
메서드가 호출됩니다. 즉, 사용자가 키보드에서 '완료'나 '다음' 버튼을 누르면 _onSubmit
이 실행됩니다.decoration:
TextField
의 디자인 및 모양을 정의하는 InputDecoration
을 사용합니다.hintText
를 사용하여 텍스트 필드에 힌트 텍스트 "Email"을 표시합니다.errorText
는 _isEmailValid()
함수를 호출하여 이메일 주소의 유효성을 검사하고, 유효하지 않은 경우 에러 메시지를 표시합니다. errorText
속성은 문자열 값을 필요로 합니다. 따라서 errorText: _isEmailValid
와 같이 함수를 직접 참조하는 것은 올바르지 않습니다. 대신, errorText: _isEmailValid()
와 같이 함수를 호출하여 그 결과값 (이 경우 문자열 또는 null)을 errorText
속성에 제공해야 합니다.
정리하면, 현재 코드에서 errorText: _isEmailValid(),
는 올바르게 작성된 것입니다.
enabledBorder
와 focusedBorder
를 사용하여 텍스트 필드의 아래쪽 경계선의 디자인을 정의합니다.Theme.of(context).primaryColor
로 설정합니다.이 TextField
위젯은 사용자에게 이메일 주소를 입력하도록 안내하고, 입력된 이메일 주소의 유효성을 실시간으로 검사하여 에러 메시지를 표시하는 역할을 합니다.
autocorrect: false,
는 TextField
위젯의 속성 중 하나입니다. 이 속성은 자동 수정 기능을 활성화 또는 비활성화하는 데 사용됩니다.
예를 들어, 사용자가 이메일 주소나 비밀번호와 같은 특정 형식의 텍스트를 입력해야 할 때 자동 수정 기능이 활성화되어 있으면, 사용자가 원하는 텍스트가 아닌 다른 텍스트로 변경될 수 있습니다. 이러한 경우에 autocorrect: false
를 설정하여 자동 수정 기능을 비활성화할 수 있습니다.
따라서, 위에서 제공한 EmailScreen
예제에서 이메일 주소를 입력하는 TextField
에 autocorrect: false
를 추가하는 것은 좋은 아이디어입니다. 다음과 같이 코드에 추가할 수 있습니다:
TextField(
controller: _emailController,
keyboardType: TextInputType.emailAddress,
onEditingComplete: _onSubmit,
autocorrect: false, // 자동 수정 기능 비활성화
decoration: InputDecoration(
hintText: "Email",
errorText: _isEmailValid(),
enabledBorder: UnderlineInputBorder(
borderSide: BorderSide(
color: Colors.grey.shade400,
),
),
focusedBorder: UnderlineInputBorder(
borderSide: BorderSide(
color: Colors.grey.shade400,
),
),
),
cursorColor: Theme.of(context).primaryColor,
),
이렇게 하면 사용자가 이메일 주소를 입력할 때 자동 수정 기능에 의해 텍스트가 변경되지 않습니다.
TextField(
cursorColor: Theme.of(context).primaryColor,
decoration: InputDecoration(
hintText: "Add comment...",
border: OutlineInputBorder(
borderRadius: BorderRadius.circular(
Sizes.size12,
),
borderSide: BorderSide.none,
),
filled: true,
fillColor: Colors.grey.shade200,
contentPadding: const EdgeInsets.symmetric(
vertical: Sizes.size10,
horizontal: Sizes.size12,
),
),
)
cursorColor: 이 속성은 텍스트 필드가 포커스를 가질 때 깜빡이는 커서의 색상을 결정합니다. 여기서는 현재 테마의 주요 색상이 커서 색상으로 사용됩니다.
decoration: InputDecoration
은 입력 필드의 모양과 느낌을 정의하는 다양한 방법을 제공합니다. 그 속성들의 세부 정보는 다음과 같습니다:
OutlineInputBorder
가 사용됩니다.Sizes.size12
상수(앱의 상수에서 가져온 값)가 사용됩니다.BorderSide.none
은 테두리 자체가 어떤 선도 표시하지 않을 것임을 의미합니다.true
는 채우기 색상이 있음을 의미합니다.Colors.grey.shade200
)이 사용됩니다.요약하면, 이 TextField
위젯은 사용자가 댓글을 추가할 수 있는 입력 필드를 나타냅니다. 주요 색상의 커서, 사용자를 안내하는 힌트 텍스트, 눈에 보이지 않는 테두리와 둥근 및 채워진 디자인, 그리고 내부의 콘텐츠가 적절하게 간격을 둔 상태로 표시되도록 특정 패딩이 있습니다.
이 TextField
위젯의 속성들에 대해 설명드리겠습니다:
TextField(
onTap: _onStartWriting,
expands: true,
minLines: null,
maxLines: null,
textInputAction: TextInputAction.newline,
)
onTap: onTap
속성은 사용자가 해당 TextField
를 탭할 때 호출되는 콜백 함수입니다. 여기서는 _onStartWriting
라는 함수가 호출됩니다.
expands: expands
속성이 true
로 설정되면 TextField
의 크기가 부모 위젯의 사용 가능한 공간을 최대한 확장하려고 합니다. 이 속성을 사용하면 minLines
및 maxLines
속성은 무시됩니다.
minLines와 maxLines: 이 두 속성은 TextField
의 최소 및 최대 줄 수를 정의합니다. 둘 다 null
로 설정되어 있으면, TextField
는 부모 위젯의 사용 가능한 공간에 따라 크기가 자동으로 조절됩니다.
textInputAction: 이 속성은 소프트 키보드에서의 특정 동작을 정의합니다. 여기서 TextInputAction.newline
가 설정되어 있으므로, 사용자는 엔터(줄바꿈) 키를 통해 새 줄을 시작할 수 있습니다.
이 TextField
설정은 주로 여러 줄의 텍스트를 입력하도록 설정된 경우에 유용합니다. onTap
콜백을 통해 사용자가 입력을 시작할 때 특정 동작을 실행하도록 설정할 수 있습니다.