[Flutter] 11. 사용자 입력 - Form, TextField, 버튼
사용자 입력을 받고 처리하는 Form과 다양한 입력 위젯을 배웁니다.
TextField
기본 TextField
class InputExample extends StatefulWidget {
const InputExample({super.key});
@override
State<InputExample> createState() => _InputExampleState();
}
class _InputExampleState extends State<InputExample> {
final TextEditingController _controller = TextEditingController();
@override
void dispose() {
_controller.dispose(); // 메모리 해제 필수!
super.dispose();
}
@override
Widget build(BuildContext context) {
return Column(
children: [
TextField(
controller: _controller,
decoration: const InputDecoration(
labelText: '이름',
hintText: '이름을 입력하세요',
prefixIcon: Icon(Icons.person),
border: OutlineInputBorder(),
),
),
const SizedBox(height: 16),
ElevatedButton(
onPressed: () {
print('입력값: ${_controller.text}');
},
child: const Text('확인'),
),
],
);
}
}
TextField 옵션
// 비밀번호 입력
TextField(
obscureText: true,
decoration: const InputDecoration(
labelText: '비밀번호',
prefixIcon: Icon(Icons.lock),
),
)
// 숫자만 입력
TextField(
keyboardType: TextInputType.number,
decoration: const InputDecoration(labelText: '나이'),
)
// 여러 줄 입력
TextField(
maxLines: 5,
decoration: const InputDecoration(
labelText: '메모',
alignLabelWithHint: true,
border: OutlineInputBorder(),
),
)
// 글자 수 제한
TextField(
maxLength: 20,
decoration: const InputDecoration(labelText: '닉네임'),
)
Form과 유효성 검사
Form + TextFormField
class LoginForm extends StatefulWidget {
const LoginForm({super.key});
@override
State<LoginForm> createState() => _LoginFormState();
}
class _LoginFormState extends State<LoginForm> {
final _formKey = GlobalKey<FormState>();
String _email = '';
String _password = '';
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(title: const Text('로그인')),
body: Padding(
padding: const EdgeInsets.all(16),
child: Form(
key: _formKey,
child: Column(
children: [
// 이메일
TextFormField(
decoration: const InputDecoration(
labelText: '이메일',
prefixIcon: Icon(Icons.email),
border: OutlineInputBorder(),
),
keyboardType: TextInputType.emailAddress,
validator: (value) {
if (value == null || value.isEmpty) {
return '이메일을 입력하세요';
}
if (!value.contains('@')) {
return '올바른 이메일 형식이 아닙니다';
}
return null; // 유효함
},
onSaved: (value) => _email = value!,
),
const SizedBox(height: 16),
// 비밀번호
TextFormField(
decoration: const InputDecoration(
labelText: '비밀번호',
prefixIcon: Icon(Icons.lock),
border: OutlineInputBorder(),
),
obscureText: true,
validator: (value) {
if (value == null || value.isEmpty) {
return '비밀번호를 입력하세요';
}
if (value.length < 6) {
return '6자 이상 입력하세요';
}
return null;
},
onSaved: (value) => _password = value!,
),
const SizedBox(height: 24),
// 로그인 버튼
SizedBox(
width: double.infinity,
height: 48,
child: ElevatedButton(
onPressed: _submit,
child: const Text('로그인'),
),
),
],
),
),
),
);
}
void _submit() {
if (_formKey.currentState!.validate()) {
_formKey.currentState!.save();
print('이메일: $_email, 비밀번호: $_password');
// 로그인 처리
}
}
}
다양한 입력 위젯
체크박스 / 스위치
class SettingsPage extends StatefulWidget {
const SettingsPage({super.key});
@override
State<SettingsPage> createState() => _SettingsPageState();
}
class _SettingsPageState extends State<SettingsPage> {
bool _darkMode = false;
bool _notifications = true;
@override
Widget build(BuildContext context) {
return Column(
children: [
// 스위치
SwitchListTile(
title: const Text('다크 모드'),
subtitle: const Text('어두운 테마를 사용합니다'),
value: _darkMode,
onChanged: (value) {
setState(() => _darkMode = value);
},
),
// 체크박스
CheckboxListTile(
title: const Text('알림 받기'),
value: _notifications,
onChanged: (value) {
setState(() => _notifications = value!);
},
),
],
);
}
}
라디오 버튼
class GenderSelect extends StatefulWidget {
const GenderSelect({super.key});
@override
State<GenderSelect> createState() => _GenderSelectState();
}
class _GenderSelectState extends State<GenderSelect> {
String _gender = '남성';
@override
Widget build(BuildContext context) {
return Column(
children: [
RadioListTile<String>(
title: const Text('남성'),
value: '남성',
groupValue: _gender,
onChanged: (value) => setState(() => _gender = value!),
),
RadioListTile<String>(
title: const Text('여성'),
value: '여성',
groupValue: _gender,
onChanged: (value) => setState(() => _gender = value!),
),
],
);
}
}
드롭다운
class CityDropdown extends StatefulWidget {
const CityDropdown({super.key});
@override
State<CityDropdown> createState() => _CityDropdownState();
}
class _CityDropdownState extends State<CityDropdown> {
String _selectedCity = '서울';
final List<String> _cities = ['서울', '부산', '대구', '인천', '광주'];
@override
Widget build(BuildContext context) {
return DropdownButton<String>(
value: _selectedCity,
items: _cities.map((city) {
return DropdownMenuItem(value: city, child: Text(city));
}).toList(),
onChanged: (value) {
setState(() => _selectedCity = value!);
},
);
}
}
버튼 종류
Column(
children: [
// 기본 버튼 (배경색 있음)
ElevatedButton(
onPressed: () {},
child: const Text('ElevatedButton'),
),
// 텍스트 버튼 (배경 없음)
TextButton(
onPressed: () {},
child: const Text('TextButton'),
),
// 외곽선 버튼
OutlinedButton(
onPressed: () {},
child: const Text('OutlinedButton'),
),
// 아이콘 버튼
IconButton(
onPressed: () {},
icon: const Icon(Icons.favorite),
),
// 아이콘 + 텍스트
ElevatedButton.icon(
onPressed: () {},
icon: const Icon(Icons.send),
label: const Text('전송'),
),
],
)
- [Flutter] 18. 빌드와 배포 - APK, App Store
- [Flutter] 17. 실전 프로젝트 - Todo 앱 만들기
- [Flutter] 16. 패키지 활용 - 유용한 패키지 소개
- [Flutter] 15. 디자인 - 테마, 스타일, 반응형
- [Flutter] 14. 로컬 저장소 - SharedPreferences, SQLite
- [Flutter] 13. HTTP 통신 - REST API 연동
- [Flutter] 12. 상태관리 - setState, Provider
- [Flutter] 11. 사용자 입력 - Form, TextField, 버튼
- [Flutter] 10. 화면 이동 - Navigation, Route
- [Flutter] 09. 리스트와 스크롤 - ListView, GridView
- [Flutter] 08. 레이아웃 - Row, Column, Stack
- [Flutter] 07. 위젯 기초 - StatelessWidget, StatefulWidget
- [Flutter] 06. Flutter 소개 및 개발환경 설치
- [Flutter] 05. Dart 비동기 - Future, async/await, Stream
- [Flutter] 04. Dart 클래스 - OOP 기초
- [Flutter] 03. Dart 함수 - 선언, 매개변수, 람다
- [Flutter] 02. Dart 제어문 - 조건문, 반복문
- [Flutter] 01. Dart 언어 기초 - 변수, 타입, 연산자