์ ๋ ฅ๊ณผ ํผ - TextField, Form, GestureDetector
์ง๋ ๊ธ๊น์ง ํ๋ฉด์ ๋ฐฐ์นํ๊ณ ์คํฌ๋กค์ํค๋ ๋ฒ์ ๋ดค์ผ๋, ์ด๋ฒ์ ์ฌ์ฉ์ ์
๋ ฅ์ ๋ฐ๋ ์ฐจ๋ก๋ค. ์ด๋ฆ์ ์
๋ ฅํ๋ฉด ์๋์ "์๋
, ํ๊ธธ๋"์ด ๋จ๋ ํ๋ฉด์ ๋ง๋ค์ด๋ณด์. Compose ํ๋ ์ต๊ด๋๋ก TextField์ ๊ฐ์ ๋ฃ๊ณ ๋ณ์์ ๋ด์ผ๋ฉด ๋ ๊ฒ ๊ฐ๋ค.
class GreetingPage extends StatefulWidget {
const GreetingPage({super.key});
@override
State<GreetingPage> createState() => _GreetingPageState();
}
class _GreetingPageState extends State<GreetingPage> {
String name = '';
@override
Widget build(BuildContext context) {
return Column(
children: [
const TextField(), // ์
๋ ฅ๊ฐ์ ์ด๋์ ๋ฐ์ง?
Text('์๋
, $name'),
],
);
}
}๊ธ์๋ฅผ ์
๋ ฅํด๋ ์๋ ๊ธ์๋ ๊ณ์ ์๋
, ์ด๋ค. TextField๋ ์
๋ ฅ์ ๋ฐ์์ ํ๋ฉด์ ๋ณด์ฌ์ฃผ๊ธด ํ๋๋ฐ, ๊ทธ ๊ฐ์ ๋ด ๋ณ์ name์ ์๋ ค์ฃผ๋ ์ฐ๊ฒฐ์ด ์๋ค. ๊ทธ๋ฆฌ๊ณ StatefulWidget์ ์ฐ๊ณ ์์ด๋ setState๋ฅผ ๋ถ๋ฅด๋ ๊ณณ์ด ์์ผ๋ ํ๋ฉด์ด ๋ค์ ๊ทธ๋ ค์ง์ง ์๋๋ค.
ํด๊ฒฐ 1: onChanged๋ก ๊ฐ ๋ฐ๊ธฐ
TextField(
onChanged: (value) {
setState(() {
name = value;
});
},
),onChanged๋ ๊ธ์๊ฐ ๋ฐ๋ ๋๋ง๋ค ์ ๊ฐ์ ๋๊ฒจ์ฃผ๋ ์ฝ๋ฐฑ์ด๋ค. 3๋ฒ ๊ธ์์ ๋ฐฐ์ด "์๋ก ์๋ ค์ฃผ๊ธฐ" ํจํด์ด๊ณ , 10๋ฒ ๊ธ์์ ๋ฐฐ์ด ๋๋ก ํ๋ฉด์ ๋ณด์ด๋ ๊ฐ์ setState ์์์ ๋ฐ๊พผ๋ค. ์ด์ ์
๋ ฅํ ๋๋ง๋ค ์๋ ๊ธ์๊ฐ ๋ฐ๋๋ค.
๊ฐ์ ์ฝ๊ธฐ๋ง ํ๊ณ ํ๋ฉด์ ๋ฐ๋ก ๋ฐ์ํ ํ์๊ฐ ์๋ค๋ฉด setState๋ ํ์ ์๋ค. ์๋ ์ปจํธ๋กค๋ฌ ๋ฐฉ์์ด ๊ทธ๋ฐ ๊ฒฝ์ฐ์ ๋ง๋ค.
ํด๊ฒฐ 2: TextEditingController๋ก ๊ฐ ์ฝ๊ธฐ
class _GreetingPageState extends State<GreetingPage> {
late final TextEditingController controller;
@override
void initState() {
super.initState();
controller = TextEditingController();
}
@override
void dispose() {
controller.dispose(); // ์ด ๊ฑด ์ ๋ฆฌ
super.dispose();
}
@override
Widget build(BuildContext context) {
return Column(
children: [
TextField(controller: controller),
ElevatedButton(
onPressed: () => print('์
๋ ฅ๊ฐ: ${controller.text}'),
child: const Text('ํ์ธ'),
),
],
);
}
}TextEditingController๋ ์
๋ ฅ์ฐฝ๊ณผ ์ฐ๊ฒฐ๋ ์์ก์ด๋ค. controller.text๋ก ์ง๊ธ ์
๋ ฅ๋ ๊ฐ์ ์ฝ๊ณ , controller.text = 'ํ๊ธธ๋'์ผ๋ก ๊ฐ์ ๋ฃ์ ์๋ ์๋ค. ์์ late final์ initState์์ ์ฑ์ฐ๋ ์์๋ก ๋ดค๋ ๋ฐ๋ก ๊ทธ ํจํด์ด๊ณ , dispose์์ controller.dispose()๋ฅผ ๋ถ๋ฅด๋ ๊ฒ๋ ๋นผ๋จน์ผ๋ฉด ์ ๋๋ค. ์ปจํธ๋กค๋ฌ๋ ๋ฉ๋ชจ๋ฆฌ๋ฅผ ์ก๊ณ ์์ด์ ์ ๋ฆฌํ์ง ์์ผ๋ฉด ์์ด๋๊ฐ๋ค.
onChanged | controller | |
๊ฐ์ ๋ฐ๋ ์์ | ๊ธ์๊ฐ ๋ฐ๋ ๋๋ง๋ค | ๋ด๊ฐ ์ฝ๊ณ ์ถ์ ๋ |
์ ํฉํ ๊ฒฝ์ฐ | ์ ๋ ฅํ๋ ์ฆ์ ํ๋ฉด์ด ๋ฐ๋์ด์ผ ํ ๋ | ๋ฒํผ์ ๋๋ ์ ๋ ๊ฐ์ ํ ๋ฒ ์ฝ์ ๋ |
์ ๋ฆฌ(dispose) | ํ์ ์์ | ํ์ํจ |
์ ๋ ฅ์ฐฝ ๊พธ๋ฏธ๊ธฐ: InputDecoration
TextField(
controller: controller,
keyboardType: TextInputType.emailAddress, // ์ด๋ฉ์ผ์ฉ ํค๋ณด๋
obscureText: false, // true๋ฉด ๋น๋ฐ๋ฒํธ์ฒ๋ผ ๊ฐ๋ฆผ
decoration: const InputDecoration(
labelText: '์ด๋ฉ์ผ',
hintText: 'test@mail.com',
prefixIcon: Icon(Icons.email),
border: OutlineInputBorder(),
),
)decoration์ InputDecoration์ ๋๊ธฐ๋ฉด ๋ผ๋ฒจ, ํํธ, ์์ด์ฝ, ํ
๋๋ฆฌ๊ฐ ํ๊บผ๋ฒ์ ๋ถ๋๋ค. keyboardType์ ์ด๋ฉ์ผ, ์ซ์ ๊ฐ์ ํค๋ณด๋ ์ข
๋ฅ๋ฅผ ์ ํ๋ค.
์ฌ๋ฌ ์นธ ์ ๋ ฅ์ Form์ผ๋ก ๋ฌถ๋๋ค
ํ์๊ฐ์
์ฒ๋ผ ์
๋ ฅ์นธ์ด ์ฌ๋ฌ ๊ฐ์ด๊ณ "ํ์ธ" ๋ฒํผ์์ ์ ๋ถ ๊ฒ์ฌํ๊ณ ์ถ์ผ๋ฉด ์นธ๋ง๋ค ์ปจํธ๋กค๋ฌ๋ฅผ ๋ง๋ค์ด ์ง์ ๊ฒ์ฌํ๋ ๊ฑด ๋ฒ๊ฑฐ๋กญ๋ค. Form์ ์นธ๋ค์ ๋ฌถ์ด์ ํ ๋ฒ์ ๊ฒ์ฌํด์ค๋ค.
class _SignupPageState extends State<SignupPage> {
final _formKey = GlobalKey<FormState>(); // Form์ ๊ฐ๋ฆฌํค๋ ์ด์
@override
Widget build(BuildContext context) {
return Form(
key: _formKey,
child: Column(
children: [
TextFormField(
decoration: const InputDecoration(labelText: '์ด๋ฉ์ผ'),
validator: (value) {
if (value == null || !value.contains('@')) {
return '์ด๋ฉ์ผ ํ์์ด ์๋์์'; // ์๋ฌ ๋ฉ์์ง
}
return null; // null์ด๋ฉด ํต๊ณผ
},
),
ElevatedButton(
onPressed: () {
if (_formKey.currentState!.validate()) {
print('ํต๊ณผ');
}
},
child: const Text('๊ฐ์
'),
),
],
),
);
}
}์ธ ๊ฐ์ง๊ฐ ํ ์ธํธ๋ค.
TextFormField:Form์์์ ์ฐ๋TextField.validator๋ฅผ ๋ฌ ์ ์๋คvalidator: ๊ฐ์ ๋ฐ์์ ์๋ฌ ๋ฌธ๊ตฌ๋ฅผ ๋๋ ค์ฃผ๋ฉด ์คํจ,null์ ๋๋ ค์ฃผ๋ฉด ํต๊ณผ๋ค_formKey.currentState!.validate(): ์์ ์๋ ๋ชจ๋validator๋ฅผ ํ ๋ฒ์ ์คํํ๋ค
validator์ ๊ท์น์ด ์ฒ์์ ๋ฐ๋๋ก ๋๊ปด์ง๋ค. ํต๊ณผ๊ฐ true๊ฐ ์๋๋ผ **null**์ด๋ค. 2๋ฒ ๊ธ์์ ๋ณธ null safety ๊ทธ๋๋ก๋ค. value๊ฐ String?์ด๋ผ์ value == null์ ๋จผ์ ํ์ธํด์ผ ํ๊ณ , currentState!์ !๋ "Form์ด ํ๋ฉด์ ๋ถ์ด ์์ผ๋ null์ด ์๋๋ผ๊ณ ๋ด๊ฐ ๋ณด์ฅํ๋ค"๋ ๋ป์ด๋ค.
TextField๊ฐ ์๋ ๊ณณ์ ํฐ์นํ๋ ค๋ฉด: GestureDetector
Text๋ Container์๋ onPressed๊ฐ ์๋ค. ๊ธ์๋ฅผ ๋๋ ์ ๋ ๋ฐ์ํ๊ฒ ํ๋ ค๋ฉด GestureDetector๋ก ๊ฐ์ผ๋ค.
GestureDetector(
onTap: () => print('ํญ'),
onLongPress: () => print('๊ธธ๊ฒ ๋๋ฆ'),
onDoubleTap: () => print('๋ ๋ฒ ํญ'),
child: Container(
width: 100,
height: 100,
color: Colors.blue,
child: const Center(child: Text('๋๋ฌ๋ด')),
),
)Center, Expanded์ ๊ฐ์ ๋ฐ์์ด๋ค. "ํฐ์น ๊ฐ์ง"๋ ์์ฑ์ด ์๋๋ผ ์์ ฏ์ผ๋ก ๊ฐ์ผ๋ค. Compose์ Modifier.clickable, SwiftUI์ .onTapGesture๊ฐ Flutter์์๋ GestureDetector๋ค.
GestureDetector๋ ๋๋ ์ ๋ ๋ฌผ๊ฒฐ ํจ๊ณผ๊ฐ ์๋ค. ๋ฌผ๊ฒฐ ํจ๊ณผ๊ฐ ํ์ํ๋ฉด InkWell์ ์ด๋ค. ๋จ, InkWell์ ๋ค์ Material ์์ ฏ์ด ์์ด์ผ ๋ฌผ๊ฒฐ์ด ๋ณด์ธ๋ค.
InkWell(
onTap: () => print('ํญ'),
child: const Padding(
padding: EdgeInsets.all(16),
child: Text('๋ฌผ๊ฒฐ์ด ์๊ฒจ์'),
),
)ํค๋ณด๋๊ฐ ํ๋ฉด์ ๊ฐ๋ฆด ๋
์
๋ ฅ์ฐฝ์ด ํ๋ฉด ์๋์ชฝ์ ์์ผ๋ฉด ํค๋ณด๋๊ฐ ์ฌ๋ผ์ค๋ฉด์ ๊ฐ๋ฆฐ๋ค. Scaffold๋ ๊ธฐ๋ณธ์ผ๋ก ํค๋ณด๋ ๋์ด๋งํผ ๋ณธ๋ฌธ์ ์ฌ๋ ค์ฃผ๋๋ฐ, ์
๋ ฅ์นธ์ด ๋ง์ผ๋ฉด SingleChildScrollView๋ก ๊ฐ์ธ์ ์คํฌ๋กค๋๊ฒ ํ๋ค.
Scaffold(
body: SingleChildScrollView(
padding: const EdgeInsets.all(16),
child: Form(...),
),
)์ง๋ ๊ธ์์ ๋ฐฐ์ด "Column์ ์คํฌ๋กค์ด ์ ๋๋ค"๊ฐ ์ฌ๊ธฐ์ ๋ค์ ๋์จ๋ค. ํค๋ณด๋ ๋๋ฌธ์ ํ๋ฉด์ด ์ข์์ง๋ฉด Column์ด overflowed ์๋ฌ๋ฅผ ๋ด๊ธฐ ์ฝ๋ค.
Compose, SwiftUI์ ๋น๊ตํ๋ฉด
// Compose
var name by remember { mutableStateOf("") }
TextField(
value = name,
onValueChange = { name = it },
label = { Text("์ด๋ฆ") }
)// SwiftUI
@State private var name = ""
TextField("์ด๋ฆ", text: $name)Flutter | Compose | SwiftUI | |
๊ฐ ์ฐ๊ฒฐ ๋ฐฉ์ |
|
|
|
์ํ๋ฅผ ๋๊ฐ ๋ค๊ณ ์๋ | ์ปจํธ๋กค๋ฌ (์ ๋ ฅ์ฐฝ ์์ชฝ) | ๋ด ๋ณ์ ( | ๋ด ๋ณ์ ( |
ํผ ๊ฒ์ฆ |
| ์ง์ ๊ตฌํ | ์ง์ ๊ตฌํ |
ํฐ์น ๊ฐ์ง |
|
|
|
์ ๋ฆฌ |
| ์๋ | ์๋ |
๊ฐ์ฅ ํฐ ์ฐจ์ด๋ ์
๋ ฅ๊ฐ์ ๋๊ฐ ๋ค๊ณ ์๋๋๋ค. Compose์ SwiftUI๋ ๋ด ๋ณ์๊ฐ ๊ฐ์ ๋ค๊ณ ์๊ณ ์
๋ ฅ์ฐฝ์ ๊ทธ๊ฑธ ๋ณด์ฌ์ค ๋ฟ์ด๋ค. Flutter์ TextField๋ ์์ชฝ์ ์๊ธฐ ์ํ๋ฅผ ๋ค๊ณ ์์ด์, ๊ฐ์ ์ฝ์ผ๋ ค๋ฉด ์ปจํธ๋กค๋ฌ๋ผ๋ ์์ก์ด๋ฅผ ๋ฐ๋ก ์ฐ๊ฒฐํด์ผ ํ๋ค.
์ ๋ฆฌ
TextField๋ ์ ๋ ฅ๋ง ๋ฐ์ ๋ฟ ๊ฐ์ ๋ด ๋ณ์๋ก ์๋ ค์ฃผ์ง ์์ผ๋ฏ๋ก,onChanged๋TextEditingController๋ก ์ฐ๊ฒฐํ๋ค- ์
๋ ฅ ์ฆ์ ํ๋ฉด์ด ๋ฐ๋์ด์ผ ํ๋ฉด
onChanged+setState, ๋ฒํผ์ ๋๋ ์ ๋ ํ ๋ฒ ์ฝ์ผ๋ฉด ๋๋ฉด ์ปจํธ๋กค๋ฌ๋ฅผ ์ด๋ค - ์ปจํธ๋กค๋ฌ๋
initState์์ ๋ง๋ค๊ณdispose์์ ๋ฐ๋์ ์ ๋ฆฌํ๋ค - ์
๋ ฅ์นธ์ด ์ฌ๋ฌ ๊ฐ์ด๊ณ ํ ๋ฒ์ ๊ฒ์ฌํ๋ ค๋ฉด
Form+TextFormField+validator๋ฅผ ์ฐ๊ณ , ํต๊ณผ๋null, ์คํจ๋ ์๋ฌ ๋ฌธ๊ตฌ๋ฅผ ๋๋ ค์ค๋ค Text๋Container์ ํฐ์น ๋ฐ์์ ์ฃผ๋ ค๋ฉดGestureDetector๋ก ๊ฐ์ธ๊ณ , ๋ฌผ๊ฒฐ ํจ๊ณผ๊ฐ ํ์ํ๋ฉดInkWell์ ์ด๋ค- ํค๋ณด๋๊ฐ ํ๋ฉด์ ๊ฐ๋ฆฌ๋ฉด
SingleChildScrollView๋ก ๊ฐ์ผ๋ค
๋ค์ ๊ธ์์๋ ThemeData์ Material 3 ํ
๋ง, ๋คํฌ ๋ชจ๋๋ฅผ ๋ค๋ค๋ณด๊ฒ ๋ค.
