Flutter Forms

2023년 9월 8일 수정

이 글은 Flutter에서 폼(Form)을 만드는 가장 기초적인 코드를 예제 위주로 설명한다.

Flutter Forms 예제

다짜고짜 그냥 예제만 보자. 필수적이면서도 기본적인 기능만 포함한 단순한 Dart 코드 예제다.

class MyFormWidget extends StatelessWidget {
  final _formKey = GlobalKey<FormState>();

  // 값을 액세스 해야 한다면 TextEditingController가 필요.
  // 여기서 someValue는 가상의 옵셔널 인스턴스.
  // 이 인스턴스가 동적이지 않다면 late는 빼도 된다.
  // 기본값이 필요 없다면 아예 내부의 필드를 다 빼도 된다.
  late final _textEditingController =
    TextEditingController(text: someValue ?? "default value");

  @override
  Widget build(BuildContext context) {
    ...
    Form(
      key: _formKey,
      child: Column(
        children: [
          // 일반적인 텍스트 필드
          TextFormField(
            // 기본값. someInitialValue는 가상의 값
            initialValue: someInitialValue,
            decoration: const InputDecoration(labelText: "Some Text Field 1"),
            onChanged: (value) {
              // 값이 바뀔 때마다 호출된다.
              print("value = $value");
            }),
          // 컨트롤러를 사용하는 텍스트 필드
          // 덤으로 우측에 삭제 버튼
          // 컨트롤러를 사용할 때는 initialValue 필드는 무의미해진다.
          TextFormField(
            controller: _textEditingController,
            decoration: const InputDecoration(
              labelText: "Some Text Field 2",
              suffixIcon: IconButton(
                onPressed: _textEditingController.clear,
                icon: Icon(Icons.clear)))),
          // 값 유효성 확인
          TextFormField(
            initialValue: "Validation Available",
            decoration: const InputDecoration(labelText: "Some Text Field 3"),
            validator: (value) {
              if (isValid(value)) {
                // 유효하지 않을 때 표시할 메세지
                return "The value is invalid";
              } else {
                return null;
              }
            }),
        ]
      )
    ),
    ...
    // 폼을 전송하는 용도의 버튼
    TextButton(
      child: const Text("Submit"),
      onPressed: () {
        // 컨트롤러를 통해 텍스트 필드의 값을 얻을 수 있음
        print("${_textEditingController.text}")

        // 유효성 체크
        if (_formKey.currentState!.validate()) {
          // 문제가 없는 경우
          // send()는 가상의 메서드
          send();
        } else {
          // 문제가 있을 때 취할 행동
          // showError()는 가상의 메서드
          showError("Failed to validates");
        }
      }
    )
  }
}

위 코드는 스테이트리스 위젯 클래스에서 폼에 필요한 부분만 남기고 ... 으로 표시된 부분은 많은 코드가 생략된 부분이니 그 점에 유의하자.

각 필드의 값 읽기

각 필드의 값을 읽는 방법은 몇 가지가 있는데 여기서는 두 가지만 알아보자.

onChanged

필드에 포커스를 준 뒤 뭔가를 입력하면 onChanged 필드의 클로저가 매번 호출된다. 여기서 값을 알 수 있다.

별도의 제출 버튼 없이 폼 필드만 수정하면 자동으로 업데이트 되게 하고 싶다면 이 필드를 이용하면 될 것 같다.

TextEditingController

만약 폼 필드 내부가 아닌 다른 로직에서 필드에 입력된 값을 액세스 해야 한다면 TextEditingController 인스턴스를 컨트롤러로 넘겨줘야 한다.

위 코드에서 _textEditingController 라고 이름이 붙은 프로퍼티가 그 부분이다.

값을 읽을 때는 이 컨트롤러의 값, 여기서는 .text 멤버 프로퍼티를 액세스 해서 텍스트 필드의 값을 읽을 수 있었다.

기타

FormKey

폼은 그룹으로 분류할 수 있으며 한 화면에 여러 그룹의 폼이 있을 수 있다. 이때 각 폼 그룹은 키를 이용해 구분한다.

심지어 각 키는 해당 그룹의 폼을 대상으로 몇 가지 기능을 수행하기도 한다. 예를 들어 각 필드의 validator 필드의 클로저는 FormKey의 validate() 메서드가 호출되지 않으면 실제로 실행되지 않는다.

따라서 Flutter의 폼(Forms)은 사실상 필수적으로 키가 필요하게 구현되어 있는 것 같다.

InputDecoration

InputDecoration을 이용해 필드에 여러 꾸미기나 부가기능을 제공할 수 있다. 예를 들자면 labelText 를 이용해 필드 이름을 표시해 주거나, prefixIcon 이나 suffixIcon 등으로 필드 앞과 뒤에 아이콘을 넣을 수도 있다. 위 예제처럼 아예 suffixIcon 에 버튼을 넣어 삭제 기능을 구현하는 등 다양한 활용이 가능하다.