Flutter TextField 입력값 받는 방법

Flutter TextField 입력값 받는 방법

lutter에서 TextField 입력값을 받는 방법은 처음 배우는 분들이 가장 자주 마주치는 부분입니다. 눈으로는 입력이 되는데, 막상 그 값을 변수에 넣거나 버튼 눌렀을 때 가져오려고 하면 헷갈리기 쉽습니다. 이번 글에서는 가장 많이 쓰는 방식부터 실전 예제까지 하나씩 아주 쉽게 정리해드리겠습니다.

TextField 기본 이해

Flutter의 TextField는 사용자가 글자를 입력하는 입력창입니다.
문제는 입력창에 글자가 보이는 것과, 그 값을 코드에서 가져오는 것은 별개라는 점입니다.

예를 들어 사용자가 이름을 입력했다고 해도, 개발자가 따로 값을 받아두지 않으면 버튼을 눌렀을 때 그 내용을 제대로 사용할 수 없습니다.

보통은 아래 3가지 방식으로 많이 처리합니다.

  • 글자가 바뀔 때마다 받기
  • 버튼을 눌렀을 때 받기
  • 입력이 끝났을 때 받기

실무에서는 이 중에서도 onChangedTextEditingController를 가장 많이 사용합니다.

onChanged 사용 방법

입력할 때마다 값 받기

가장 쉬운 방법은 onChanged입니다.
이 방식은 사용자가 한 글자씩 입력할 때마다 바로 값을 받을 수 있습니다.

import 'package:flutter/material.dart';

void main() {
  runApp(const MyApp());
}

class MyApp extends StatelessWidget {
  const MyApp({super.key});

  @override
  Widget build(BuildContext context) {
    return const MaterialApp(
      home: TextFieldExample(),
    );
  }
}

class TextFieldExample extends StatefulWidget {
  const TextFieldExample({super.key});

  @override
  State createState() => _TextFieldExampleState();
}

class _TextFieldExampleState extends State {
  String inputText = '';

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(title: const Text('TextField 예제')),
      body: Padding(
        padding: const EdgeInsets.all(16),
        child: Column(
          children: [
            TextField(
              onChanged: (value) {
                setState(() {
                  inputText = value;
                });
              },
              decoration: const InputDecoration(
                labelText: '이름을 입력하세요',
                border: OutlineInputBorder(),
              ),
            ),
            const SizedBox(height: 20),
            Text('입력한 값: $inputText'),
          ],
        ),
      ),
    );
  }
}

이 코드의 핵심은 아래 부분입니다.

onChanged: (value) {
  setState(() {
    inputText = value;
  });
}

사용자가 입력할 때마다 value 안에 현재 문자열이 들어옵니다.
그리고 그 값을 inputText 변수에 넣어 화면에 보여주는 방식입니다.

onChanged가 잘 맞는 경우

onChanged는 아래처럼 바로 반응해야 하는 상황에서 특히 편합니다.

  • 검색어 입력하면서 바로 목록 바꾸기
  • 글자 수 실시간 표시
  • 비밀번호 조건 확인
  • 입력값 미리보기

예를 들어 검색창을 만들 때는 사용자가 입력할 때마다 값을 받아야 하므로 onChanged가 잘 맞습니다.

Controller 사용 방법

버튼 누를 때 값 가져오기

실제로 가장 많이 쓰는 방법은 TextEditingController입니다.
이 방식은 입력창을 하나의 객체로 연결해두고, 필요할 때 값을 꺼내 쓰는 방식입니다.

import 'package:flutter/material.dart';

void main() {
  runApp(const MyApp());
}

class MyApp extends StatelessWidget {
  const MyApp({super.key});

  @override
  Widget build(BuildContext context) {
    return const MaterialApp(
      home: ControllerExample(),
    );
  }
}

class ControllerExample extends StatefulWidget {
  const ControllerExample({super.key});

  @override
  State createState() => _ControllerExampleState();
}

class _ControllerExampleState extends State {
  final TextEditingController nameController = TextEditingController();
  String result = '';

  @override
  void dispose() {
    nameController.dispose();
    super.dispose();
  }

  void getInputValue() {
    setState(() {
      result = nameController.text;
    });
  }

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(title: const Text('Controller 예제')),
      body: Padding(
        padding: const EdgeInsets.all(16),
        child: Column(
          children: [
            TextField(
              controller: nameController,
              decoration: const InputDecoration(
                labelText: '이름을 입력하세요',
                border: OutlineInputBorder(),
              ),
            ),
            const SizedBox(height: 20),
            ElevatedButton(
              onPressed: getInputValue,
              child: const Text('값 확인'),
            ),
            const SizedBox(height: 20),
            Text('입력한 값: $result'),
          ],
        ),
      ),
    );
  }
}

여기서 중요한 부분은 nameController.text입니다.

result = nameController.text;

이 한 줄로 현재 입력창에 들어 있는 값을 가져올 수 있습니다.

dispose를 꼭 써야 하는 이유

TextEditingController를 만들었으면 화면이 사라질 때 정리도 해줘야 합니다.

@override
void dispose() {
  nameController.dispose();
  super.dispose();
}

이 부분을 빼먹으면 메모리 낭비가 생길 수 있습니다.
처음에는 잘 돌아가는 것처럼 보여도, 습관처럼 꼭 넣는 것이 좋습니다.

onSubmitted 사용 방법

입력 완료 시점에 값 받기

사용자가 키보드에서 완료 버튼을 눌렀을 때 값을 받고 싶다면 onSubmitted를 사용할 수 있습니다.

TextField(
  onSubmitted: (value) {
    print('입력 완료 값: $value');
  },
)

이 방식은 아래처럼 쓰기 좋습니다.

  • 검색창에서 엔터 누를 때 검색 실행
  • 입력 마친 뒤 바로 다음 동작 처리
  • 채팅 입력 후 전송

onChanged는 입력 중간마다 실행되고,
onSubmitted는 입력이 끝났을 때 실행된다는 차이가 있습니다.

가장 많이 쓰는 실전 예제

TextField와 버튼 함께 쓰기

실제로는 입력창에 값을 적고 버튼을 눌러서 확인하는 경우가 많습니다.
아래 예제 하나만 이해해도 기본은 충분히 잡을 수 있습니다.

import 'package:flutter/material.dart';

void main() {
  runApp(const MaterialApp(
    home: SimpleInputPage(),
  ));
}

class SimpleInputPage extends StatefulWidget {
  const SimpleInputPage({super.key});

  @override
  State createState() => _SimpleInputPageState();
}

class _SimpleInputPageState extends State {
  final TextEditingController emailController = TextEditingController();
  String message = '아직 입력값이 없습니다.';

  @override
  void dispose() {
    emailController.dispose();
    super.dispose();
  }

  void checkValue() {
    final text = emailController.text.trim();

    setState(() {
      if (text.isEmpty) {
        message = '이메일을 입력해주세요.';
      } else {
        message = '입력한 이메일: $text';
      }
    });
  }

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(
        title: const Text('입력값 받기'),
      ),
      body: Padding(
        padding: const EdgeInsets.all(16),
        child: Column(
          children: [
            TextField(
              controller: emailController,
              keyboardType: TextInputType.emailAddress,
              decoration: const InputDecoration(
                labelText: '이메일 입력',
                hintText: 'example@email.com',
                border: OutlineInputBorder(),
              ),
            ),
            const SizedBox(height: 16),
            SizedBox(
              width: double.infinity,
              child: ElevatedButton(
                onPressed: checkValue,
                child: const Text('확인하기'),
              ),
            ),
            const SizedBox(height: 16),
            Text(message),
          ],
        ),
      ),
    );
  }
}

이 예제에서 꼭 봐야 할 부분

첫 번째는 공백 제거입니다.

final text = emailController.text.trim();

사용자가 앞뒤에 띄어쓰기를 넣는 경우가 있기 때문에 trim()을 자주 함께 씁니다.

두 번째는 빈 값 검사입니다.

if (text.isEmpty) {
  message = '이메일을 입력해주세요.';
}

값을 받았다고 끝이 아니라, 비어 있는지 확인하는 과정도 꼭 필요합니다.

여러 개 입력창 받을 때 방법

변수 따로 만들기

입력창이 여러 개라면 컨트롤러도 각각 따로 만들어야 합니다.

final TextEditingController idController = TextEditingController();
final TextEditingController pwController = TextEditingController();

그리고 버튼을 누를 때 각각 값을 꺼내 쓰면 됩니다.

final id = idController.text;
final pw = pwController.text;

예제는 아래처럼 만들 수 있습니다.

import 'package:flutter/material.dart';

void main() {
  runApp(const MaterialApp(
    home: LoginPage(),
  ));
}

class LoginPage extends StatefulWidget {
  const LoginPage({super.key});

  @override
  State createState() => _LoginPageState();
}

class _LoginPageState extends State {
  final TextEditingController idController = TextEditingController();
  final TextEditingController pwController = TextEditingController();
  String result = '';

  @override
  void dispose() {
    idController.dispose();
    pwController.dispose();
    super.dispose();
  }

  void login() {
    final id = idController.text.trim();
    final pw = pwController.text.trim();

    setState(() {
      result = '아이디: $id / 비밀번호: $pw';
    });
  }

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(title: const Text('로그인 예제')),
      body: Padding(
        padding: const EdgeInsets.all(16),
        child: Column(
          children: [
            TextField(
              controller: idController,
              decoration: const InputDecoration(
                labelText: '아이디',
                border: OutlineInputBorder(),
              ),
            ),
            const SizedBox(height: 12),
            TextField(
              controller: pwController,
              obscureText: true,
              decoration: const InputDecoration(
                labelText: '비밀번호',
                border: OutlineInputBorder(),
              ),
            ),
            const SizedBox(height: 16),
            ElevatedButton(
              onPressed: login,
              child: const Text('로그인'),
            ),
            const SizedBox(height: 16),
            Text(result),
          ],
        ),
      ),
    );
  }
}

입력 검증 같이 처리하기

빈칸 검사부터 해보기

입력값을 받는 것만큼 중요한 것이 검증입니다.
아무 값이나 그대로 넘기면 회원가입, 로그인, 검색 기능에서 문제가 생길 수 있습니다.

아주 간단한 예시는 아래와 같습니다.

void checkInput() {
  final text = nameController.text.trim();

  if (text.isEmpty) {
    print('값을 입력해주세요.');
    return;
  }

  print('입력 완료: $text');
}

글자 수 검사 넣기

예를 들어 닉네임은 2글자 이상만 받도록 하고 싶다면 이렇게 쓸 수 있습니다.

void checkNickname() {
  final nickname = nameController.text.trim();

  if (nickname.isEmpty) {
    print('닉네임을 입력해주세요.');
    return;
  }

  if (nickname.length < 2) {
    print('닉네임은 2글자 이상 입력해주세요.');
    return;
  }

  print('사용 가능한 입력값입니다.');
}

입력값을 받는 것과 검사하는 것은 거의 항상 같이 다닌다고 생각하면 됩니다.

자주 하는 실수 정리

controller 연결 안 한 경우

컨트롤러를 만들었는데 TextField에 연결하지 않으면 값이 안 잡힙니다.

TextField(
  controller: nameController,
)

이 연결을 빼먹지 않아야 합니다.

dispose 안 한 경우

TextEditingController를 사용했으면 dispose()도 꼭 써야 합니다.
작은 예제에서는 티가 안 나도 실제 앱에서는 중요합니다.

setState 빠뜨린 경우

화면에 결과를 보여주는 코드인데 setState()를 안 쓰면 값은 바뀌어도 화면이 그대로일 수 있습니다.

setState(() {
  result = nameController.text;
});

trim 안 써서 공백까지 저장한 경우

사용자가 실수로 띄어쓰기를 넣을 수 있으니, 저장하거나 검사하기 전에 trim()을 붙이는 습관이 좋습니다.

어떤 방법을 써야 하는지 정리

상황별 선택 방법

입력 중간마다 바로 받고 싶다
onChanged

버튼 눌렀을 때 가져오고 싶다
TextEditingController

입력이 끝났을 때 한 번만 받고 싶다
onSubmitted

처음에는 TextEditingController부터 익히는 것이 가장 좋습니다.
버튼 클릭, 로그인, 검색, 회원가입처럼 자주 만드는 기능에 바로 쓸 수 있기 때문입니다.

결론

Flutter에서 TextField 입력값을 받는 방법은 생각보다 어렵지 않습니다. 가장 많이 쓰는 방식은 onChanged, TextEditingController, onSubmitted 세 가지이며, 목적에 따라 골라 쓰면 됩니다. 입력할 때마다 바로 값을 확인하고 싶다면 onChanged가 편하고, 버튼을 눌렀을 때 현재 입력값을 가져오고 싶다면 TextEditingController가 가장 실용적입니다. 입력 완료 시점에 한 번만 처리하고 싶을 때는 onSubmitted가 잘 맞습니다.

처음 배우는 단계라면 TextEditingController부터 익히는 편이 좋습니다. 로그인, 회원가입, 검색창, 문의 폼처럼 실제 앱에서 자주 쓰이기 때문입니다. 여기에 trim()으로 공백을 정리하고, 빈칸 검사와 dispose()까지 함께 익혀두면 실무에서도 바로 활용하기 좋습니다.

결국 중요한 것은 입력창에 글자가 보이는 것만으로 끝나는 것이 아니라, 그 값을 원하는 시점에 정확하게 가져와서 검사하고 활용하는 것입니다. 이 부분만 제대로 이해하면 TextField는 훨씬 쉽게 다룰 수 있습니다.

FAQ

Flutter에서 TextField 입력값은 가장 보통 어떤 방식으로 받나요?

가장 많이 쓰는 방식은 TextEditingController입니다. 버튼을 눌렀을 때 값을 가져오거나, 저장이나 검사 같은 작업을 할 때 편하게 사용할 수 있기 때문입니다. 간단한 실시간 반응이 필요할 때는 onChanged도 자주 사용합니다.

onChanged와 TextEditingController는 어떤 차이가 있나요?

onChanged는 사용자가 글자를 입력할 때마다 바로 실행됩니다. 반면 TextEditingController는 개발자가 원하는 순간에 controller.text로 값을 꺼내 쓸 수 있습니다. 실시간 반응은 onChanged, 버튼 클릭 후 처리나 제출용은 TextEditingController가 더 많이 쓰입니다.

TextEditingController를 쓰면 dispose를 꼭 해야 하나요?

네, 꼭 하는 것이 좋습니다. TextEditingController는 사용 후 정리하지 않으면 메모리 관리에 좋지 않을 수 있습니다. 작은 예제에서는 바로 문제가 보이지 않을 수 있지만, 습관처럼 dispose()에서 정리하는 것이 안전합니다.

입력값 앞뒤 공백은 왜 trim으로 지워야 하나요?

사용자가 실수로 앞이나 뒤에 띄어쓰기를 넣는 경우가 자주 있기 때문입니다. 이메일, 아이디, 이름처럼 정확한 값이 필요한 입력에서는 공백 때문에 비교나 검사가 틀어질 수 있습니다. 그래서 값을 가져온 뒤 trim()으로 한 번 정리해주는 경우가 많습니다.

버튼을 눌렀을 때 입력값을 가져오려면 어떻게 해야 하나요?

가장 쉬운 방법은 TextField에 controller를 연결한 뒤, 버튼의 onPressed 안에서 controller.text를 사용하는 것입니다. 이 방식이면 현재 입력창에 적혀 있는 값을 바로 가져와서 검사하거나 화면에 보여줄 수 있습니다.

여러 개 TextField가 있으면 어떻게 처리하나요?

입력창이 여러 개라면 보통 컨트롤러도 각각 따로 만듭니다. 예를 들어 아이디 입력창에는 idController, 비밀번호 입력창에는 pwController처럼 나눠서 사용합니다. 그리고 제출할 때 각각의 .text 값을 꺼내 쓰면 됩니다.

입력값이 비어 있는지 확인하는 방법도 같이 써야 하나요?

네, 거의 항상 같이 처리하는 편이 좋습니다. 사용자가 아무것도 입력하지 않았는데 그대로 다음 단계로 넘어가면 오류가 생기기 쉽습니다. 그래서 text.isEmpty로 빈칸 여부를 확인하고, 비어 있으면 안내 문구를 보여주는 방식이 많이 쓰입니다.

onSubmitted는 언제 쓰면 좋나요?

onSubmitted는 사용자가 키보드에서 완료 버튼이나 엔터를 눌렀을 때 실행됩니다. 검색창에서 검색 실행, 채팅 입력 후 전송, 입력 완료 직후 다음 작업으로 넘기는 경우에 잘 맞습니다. 입력 도중마다 반응할 필요가 없을 때 유용합니다.

화면에 입력값을 보여주는데 값이 안 바뀌는 이유는 뭔가요?

대부분 setState()를 빠뜨린 경우가 많습니다. 값은 바뀌었지만 화면을 다시 그리지 않아서 그대로 보이는 경우입니다. 화면에 결과를 바로 보여줘야 한다면 상태 변경 부분을 setState() 안에서 처리해야 정상적으로 반영됩니다.

Flutter 초보자는 어떤 방식부터 익히는 게 좋나요?

초보자라면 TextEditingController부터 익히는 것이 가장 좋습니다. 입력값을 가져오는 시점이 분명해서 이해하기 쉽고, 실제 앱에서 활용하기도 좋기 때문입니다. 그다음으로 onChangedonSubmitted를 익히면 TextField 활용 범위가 훨씬 넓어집니다.

댓글 남기기