User interaction
Forms
- 유효한 입력을 받는 Form 만들기
- 자동으로 입력 창에 초점 맞추기
- 입력 창에 입력된 값 회수하기
Gestures
- 어떻게 제스처를 인식하고 반응하는 지에 관한 내용
- ex) taps, drags, and scaling
Forms
어떻게 유효한 입력 값을 받는 Forms을 만들까?
- GlobalKey를 가진 Forms 생성.
- validation logic을 가지는 TextFormField 추가.
- 유효한 경우에만 Form을 제출하는 button 생성.
code</>
import 'package:flutter/material.dart';
// Define a custom Form widget.
class MyCustomForm extends StatefulWidget {
const MyCustomForm({super.key});
@override
MyCustomFormState createState() {
return MyCustomFormState();
}
}
// Define a corresponding State class.
// This class holds data related to the form.
class MyCustomFormState extends State<MyCustomForm> {
// Step 1. Create a global key that uniquely identifies the Form widget
// and allows validation of the form.
//
// Note: This is a `GlobalKey<FormState>`,
// not a GlobalKey<MyCustomFormState>.
final _formKey = GlobalKey<FormState>();
@override
Widget build(BuildContext context) {
// Build a Form widget using the _formKey created above.
return Form(
key: _formKey,
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
// Add TextFormFields and ElevatedButton here.
Step 2.
TextFormField(
// The validator receives the text that the user has entered.
validator: (value) {
if (value == null || value.isEmpty) {
return 'Please enter some text';
}
return null;
},
),
Step 3.
Padding(
padding: const EdgeInsets.symmetric(vertical: 16.0),
child: ElevatedButton(
onPressed: () {
// Validate returns true if the form is valid, or false otherwise.
if (_formKey.currentState!.validate()) {
// If the form is valid, display a snackbar. In the real world,
// you'd often call a server or save the information in a database.
ScaffoldMessenger.of(context).showSnackBar(
const SnackBar(content: Text('Processing Data')),
);
}
},
child: const Text('Submit'),
),
),
],
),
);
}
}
버튼이 눌러졌을 때 어떻게 입력 창에 focus를 두도록 만들까?
- FocusNode 생성.
- FocusNode 를 TextField에 전달.
- 버튼이 눌러졌을 때 focus 를 TextField에 전달.
code</>
// Define a custom Form widget.
class MyCustomForm extends StatefulWidget {
const MyCustomForm({super.key});
@override
State<MyCustomForm> createState() => _MyCustomFormState();
}
// Define a corresponding State class.
// This class holds data related to the form.
class _MyCustomFormState extends State<MyCustomForm> {
// Step 1. Define the focus node. To manage the lifecycle, create the FocusNode in
// the initState method, and clean it up in the dispose method.
late FocusNode myFocusNode;
@override
void initState() {
super.initState();
myFocusNode = FocusNode();
}
@override
void dispose() {
// Clean up the focus node when the Form is disposed.
myFocusNode.dispose();
super.dispose();
}
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: const Text('Text Field Focus'),
),
body: Padding(
padding: const EdgeInsets.all(16.0),
child: Column(
children: [
// The first text field is focused on as soon as the app starts.
const TextField(
autofocus: true,
),
// The second text field is focused on when a user taps the
// FloatingActionButton.
Step 2.
TextField(
focusNode: myFocusNode,
),
],
),
),
floatingActionButton: FloatingActionButton(
// When the button is pressed,
// Step 3. give focus to the text field using myFocusNode.
onPressed: () => myFocusNode.requestFocus(),
tooltip: 'Focus Second Text Field',
child: const Icon(Icons.edit),
), // This trailing comma makes auto-formatting nicer for build methods.
);
}
}
사용자가 입력한 값을 어떻게 회수할까?
- TextEditingController 이용.
- TextEditingController 를 TextField에 적용.
- 회수한 값을 조작(예를 들어 화면에 띄우기)
code</>
// Define a custom Form widget.
class MyCustomForm extends StatefulWidget {
const MyCustomForm({super.key});
@override
State<MyCustomForm> createState() => _MyCustomFormState();
}
// Define a corresponding State class.
// This class holds the data related to the Form.
class _MyCustomFormState extends State<MyCustomForm> {
// Step 1. Create a text controller and use it to retrieve the current value
// of the TextField.
final myController = TextEditingController();
@override
void dispose() {
// Clean up the controller when the widget is disposed.
myController.dispose();
super.dispose();
}
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: const Text('Retrieve Text Input'),
),
body: Padding(
padding: const EdgeInsets.all(16.0),
Step 2.
child: TextField(
controller: myController,
),
),
floatingActionButton: FloatingActionButton(
// When the user presses the button, show an alert dialog containing
// the text that the user has entered into the text field.
onPressed: () {
showDialog(
context: context,
builder: (context) {
return AlertDialog(
// Step 3. Retrieve the text the that user has entered by using the
// TextEditingController.
content: Text(myController.text),
);
},
);
},
tooltip: 'Show me the value!',
child: const Icon(Icons.text_fields),
),
);
}
}
Gestures
Flutter의 gesture system은 2가지 분리된 layer 들로 이루어집니다.
- Pointers: 포인터의 위치와 움직임에 대한 것
- Gestures: 여러 개의 포인터 움직임에 의미를 부여한 것
Pointers
- PointerDownEvent: 특정 위치에 접촉했을 때
- PointerMoveEvent: 다른 위치로 움직였을 때
- PointerUpEvent: 스크린에서 손을 떼었을 때
- PointerCancelEvent: 더 이상 입력이 없을 때
Tap
- onTapDown: 스크린 접촉
onTapUp: 스크린 접촉 중지
onTap: onTapDown => onTapUp
onTapCancel: 스크린에 접촉하고 있는 것 만으로 tap 을 유발하지 않음
Double tap
- onDoubleTap: 같은 위치에서 빠르게 두 번 터치 했을 때
Long press
- onLongPress: 오래 같은 위치에서 접촉한 상태를 유지할 때
Vertical drag
- onVerticalDragStart: 스크린에 접촉한 채 수직으로 움직일 때
onVerticalDragUpdate: 수직으로 움직이기 시작하고 계속 수직으로 움직일 때onVerticalDragEnd: 수직으로 움직이지 않고 접촉을 중단할 때
Horizontal drag
- onHorizontalDragStart: 수평 방향으로 움직이기 시작할 때
onHorizontalDragUpdate: 수평 방향으로 움직이기 시작하고 계속 수평 방향으로 움직일 때
onHorizontalDragEnd: 수평 방향으로 움직이지 않고 접촉을 중단할 때
Pan
- onPanStart: 스크린에 접촉한 후 수직 또는 수평 방향으로 움직이기 시작할 수도 있을 때,
- onHorizontalDragStart 또는 onVerticalDragStart 이 설정되어 있을 때 충돌을 일으킴 onPanUpdate: 움직이기 시작해서 계속 움직일 때
onHorizontalDragUpdate 또는 onVerticalDragUpdate 이 설정되어 있을 때 충돌을 일으킴
onPanEnd: 더 이상 움직이지 않고 접촉을 중단할 때
onHorizontalDragEnd 또는 onVerticalDragEnd 이 설정되어 있을 때 충돌을 일으킴
어떻게 Gestures을 감지할까?
code</>
- // The GestureDetector wraps the button.
- GestureDetector(
- // When the child is tapped, show a snackbar.
- onTap: () {
- const snackBar = SnackBar(content: Text('Tap'));
- ScaffoldMessenger.of(context).showSnackBar(snackBar);
- },
- // The custom button
- child: Container(
- padding: const EdgeInsets.all(12.0),
- decoration: BoxDecoration(
- color: Colors.lightBlue,
- borderRadius: BorderRadius.circular(8.0),
- ),
- child: const Text('My Button'),
- ),
- )