Flutter에서 사용자 입력을 어떻게 처리하나요?
질문
Flutter에서 사용자 입력을 어떻게 처리하나요?
답변
Flutter에서는 사용자 입력을 처리하기 위한 다양한 위젯과 기법을 제공합니다. 사용자 입력은 텍스트 입력, 제스처, 터치 이벤트 등 여러 형태로 발생할 수 있으며, 각 입력 유형에 맞는 적절한 처리 방법이 있습니다.
1. 텍스트 입력 처리
TextField 및 TextFormField 위젯
텍스트 입력을 위한 가장 기본적인 위젯은 TextField
와 TextFormField
입니다.
TextField 기본 사용법
TextField(
decoration: InputDecoration(
labelText: '이름',
hintText: '이름을 입력하세요',
border: OutlineInputBorder(),
),
onChanged: (value) {
// 텍스트가 변경될 때마다 호출
print('입력된 텍스트: $value');
},
onSubmitted: (value) {
// 제출(Enter 키)할 때 호출
print('제출된 텍스트: $value');
},
)
TextEditingController 사용
텍스트 입력에 대한 더 세밀한 제어를 위해 TextEditingController
를 사용합니다:
class _MyFormState extends State<MyForm> {
final TextEditingController _controller = TextEditingController();
@override
void initState() {
super.initState();
// 초기값 설정
_controller.text = '초기 텍스트';
// 컨트롤러를 통한 변경 감지
_controller.addListener(() {
print('현재 텍스트: ${_controller.text}');
});
}
@override
void dispose() {
// 컨트롤러 해제
_controller.dispose();
super.dispose();
}
@override
Widget build(BuildContext context) {
return TextField(
controller: _controller,
decoration: InputDecoration(labelText: '이름'),
);
}
}
TextFormField를 이용한 유효성 검사
TextFormField
는 TextField
를 확장하여 폼 유효성 검사 기능을 추가합니다:
Form(
key: _formKey,
child: TextFormField(
decoration: InputDecoration(labelText: '이메일'),
validator: (value) {
if (value == null || value.isEmpty) {
return '이메일을 입력해주세요';
}
if (!value.contains('@')) {
return '유효한 이메일 주소를 입력해주세요';
}
return null; // 유효성 검사 통과
},
onSaved: (value) {
_email = value;
},
),
)
다양한 키보드 유형 설정
입력 유형에 따라 적절한 키보드를 표시하려면 keyboardType
속성을 사용합니다:
TextField(
keyboardType: TextInputType.emailAddress,
decoration: InputDecoration(labelText: '이메일'),
)
TextField(
keyboardType: TextInputType.number,
decoration: InputDecoration(labelText: '나이'),
)
TextField(
keyboardType: TextInputType.phone,
decoration: InputDecoration(labelText: '전화번호'),
)
2. 버튼 처리
Flutter에서는 여러 종류의 버튼 위젯이 제공됩니다.
ElevatedButton
가장 기본적인 버튼 유형입니다:
ElevatedButton(
onPressed: () {
// 버튼 클릭 시 실행할 코드
print('버튼이 클릭되었습니다!');
},
child: Text('클릭하세요'),
)
TextButton
텍스트만 있는 평면 버튼입니다:
TextButton(
onPressed: () {
print('텍스트 버튼 클릭');
},
child: Text('취소'),
)
IconButton
아이콘 형태의 버튼입니다:
IconButton(
icon: Icon(Icons.favorite),
onPressed: () {
print('좋아요 버튼 클릭');
},
)
비활성화 상태 처리
버튼을 비활성화하려면 onPressed
에 null
을 할당합니다:
ElevatedButton(
onPressed: _isEnabled ? () {
print('버튼 클릭');
} : null, // null이면 버튼이 비활성화됨
child: Text('제출'),
)
3. 제스처 감지
GestureDetector
다양한 제스처 이벤트를 감지할 수 있는 위젯입니다:
GestureDetector(
onTap: () {
print('한 번 탭했습니다');
},
onDoubleTap: () {
print('두 번 탭했습니다');
},
onLongPress: () {
print('길게 눌렀습니다');
},
onPanUpdate: (details) {
print('드래그: ${details.delta.dx}, ${details.delta.dy}');
},
child: Container(
width: 200,
height: 200,
color: Colors.blue,
child: Center(child: Text('여기를 터치하세요')),
),
)
InkWell
Material Design 스타일의 물결 효과(ripple effect)가 있는 터치 감지 위젯입니다:
InkWell(
onTap: () {
print('탭 감지됨');
},
splashColor: Colors.red,
child: Container(
padding: EdgeInsets.all(12.0),
child: Text('터치하세요'),
),
)
4. 드래그 & 드롭
Draggable과 DragTarget
드래그 앤 드롭 기능을 구현하려면 Draggable
과 DragTarget
위젯을 사용합니다:
// 드래그 가능한 항목
Draggable<String>(
data: 'dragged-item',
child: Container(
height: 100.0,
width: 100.0,
color: Colors.blue,
child: Center(child: Text('드래그 미')),
),
feedback: Container(
height: 100.0,
width: 100.0,
color: Colors.blue.withOpacity(0.5),
child: Center(child: Text('드래그 중')),
),
childWhenDragging: Container(
height: 100.0,
width: 100.0,
color: Colors.grey,
),
)
// 드롭 대상
DragTarget<String>(
builder: (context, candidateData, rejectedData) {
return Container(
height: 100.0,
width: 100.0,
color: candidateData.isNotEmpty ? Colors.green : Colors.red,
child: Center(child: Text('여기에 드롭')),
);
},
onAccept: (data) {
print('$data가 드롭되었습니다');
},
)
5. 스크롤 이벤트 처리
스크롤 이벤트를 감지하고 처리하려면 ScrollController
를 사용합니다:
class _MyScrollViewState extends State<MyScrollView> {
final ScrollController _scrollController = ScrollController();
@override
void initState() {
super.initState();
_scrollController.addListener(_scrollListener);
}
void _scrollListener() {
if (_scrollController.offset >= _scrollController.position.maxScrollExtent &&
!_scrollController.position.outOfRange) {
print("스크롤이 맨 아래에 도달했습니다");
// 여기서 추가 데이터 로드 등의 작업 수행
}
if (_scrollController.offset <= _scrollController.position.minScrollExtent &&
!_scrollController.position.outOfRange) {
print("스크롤이 맨 위에 도달했습니다");
}
}
@override
void dispose() {
_scrollController.dispose();
super.dispose();
}
@override
Widget build(BuildContext context) {
return ListView.builder(
controller: _scrollController,
itemCount: 50,
itemBuilder: (context, index) {
return ListTile(title: Text('항목 $index'));
},
);
}
}
6. Focus 관리
TextField 간 포커스 이동을 제어하려면 FocusNode
와 FocusScope
를 사용합니다:
class _MyFormState extends State<MyForm> {
final FocusNode _emailFocus = FocusNode();
final FocusNode _passwordFocus = FocusNode();
@override
void dispose() {
_emailFocus.dispose();
_passwordFocus.dispose();
super.dispose();
}
void _nextField() {
// 이메일 필드에서 비밀번호 필드로 포커스 이동
FocusScope.of(context).requestFocus(_passwordFocus);
}
@override
Widget build(BuildContext context) {
return Column(
children: [
TextField(
focusNode: _emailFocus,
decoration: InputDecoration(labelText: '이메일'),
textInputAction: TextInputAction.next,
onSubmitted: (_) => _nextField(),
),
TextField(
focusNode: _passwordFocus,
decoration: InputDecoration(labelText: '비밀번호'),
obscureText: true,
textInputAction: TextInputAction.done,
onSubmitted: (_) {
// 로그인 처리 등
print('폼 제출');
},
),
],
);
}
}
7. 폼 제출 처리
전체 폼을 관리하려면 Form
위젯과 GlobalKey
를 사용합니다:
class _LoginFormState extends State<LoginForm> {
final _formKey = GlobalKey<FormState>();
String _email = '';
String _password = '';
void _submit() {
if (_formKey.currentState!.validate()) {
_formKey.currentState!.save();
// 폼 데이터로 로그인 처리
print('로그인: 이메일=$_email, 비밀번호=$_password');
}
}
@override
Widget build(BuildContext context) {
return Form(
key: _formKey,
child: Column(
children: [
TextFormField(
decoration: InputDecoration(labelText: '이메일'),
validator: (value) {
if (value == null || value.isEmpty || !value.contains('@')) {
return '유효한 이메일을 입력하세요';
}
return null;
},
onSaved: (value) => _email = value!,
),
TextFormField(
decoration: InputDecoration(labelText: '비밀번호'),
obscureText: true,
validator: (value) {
if (value == null || value.length < 6) {
return '비밀번호는 6자 이상이어야 합니다';
}
return null;
},
onSaved: (value) => _password = value!,
),
ElevatedButton(
onPressed: _submit,
child: Text('로그인'),
),
],
),
);
}
}
요약
Flutter에서 사용자 입력 처리는 다음과 같은 다양한 방법을 통해 이루어집니다:
- 텍스트 입력:
TextField
,TextFormField
,TextEditingController
- 버튼 입력:
ElevatedButton
,TextButton
,IconButton
- 제스처 처리:
GestureDetector
,InkWell
- 드래그 앤 드롭:
Draggable
,DragTarget
- 스크롤 이벤트:
ScrollController
- 포커스 관리:
FocusNode
,FocusScope
- 폼 관리:
Form
,GlobalKey<FormState>
이러한 다양한 입력 처리 방법을 적절히 조합하여 사용하면 직관적이고 반응성이 뛰어난 사용자 인터페이스를 구축할 수 있습니다.