Flutter

[Flutter] GlobalKey란? 유효성 검사 예시

참깨빵위에참깨빵_ 2024. 10. 20. 22:58
728x90
반응형

GlobalKey는 클래스로 공식문서의 설명은 아래와 같다.

 

https://api.flutter.dev/flutter/widgets/GlobalKey-class.html

 

GlobalKey class - widgets library - Dart API

A key that is unique across the entire app. Global keys uniquely identify elements. Global keys provide access to other objects that are associated with those elements, such as BuildContext. For StatefulWidgets, global keys also provide access to State. Wi

api.flutter.dev

전체 앱에서 고유한 키다. GlobalKey는 요소를 고유하게 식별한다. 이것은 해당 요소와 연관된 다른 객체(BuildContext 등)에 대한 접근을 제공한다. StatefulWidget의 경우 GlobalKey는 상태에 대한 접근도 제공한다. GlobalKey를 가진 위젯은 트리의 어떤 위치에서 다른 위치로 이동할 때 하위 트리를 다시 부모로 지정한다. 하위 트리의 부모를 변경하려면 위젯이 트리의 이전 위치에서 제거된 애니메이션 프레임과 같은 애니메이션 프레임에서 트리의 새 위치에 도착해야 한다. GlobalKey를 써서 엘리먼트 부모를 재설정하는 건 상대적으로 비용이 많이 들며 이 작업을 수행하면 연결된 State와 모든 자식에서 State.deactivate가 호출되고 InheritedWidget에 의존하는 모든 위젯이 재빌드된다. 위에 나열된 기능이 필요하지 않은 경우 Key, ValueKey, ObjectKey 또는 UniqueKey를 대신 쓰는 게 좋다. 같은 GlobalKey를 가진 두 위젯을 트리에 동시 포함할 수 없다. 이렇게 할 경우 런타임에 어설트가 발생한다
GlobalKey는 모든 빌드에서 재생성하면 안 된다. 일반적으로 State 객체가 갖는 수명이 긴 객체여야 한다. 모든 빌드에서 GlobalKey를 만들면 이전 키와 연결된 서브트리의 상태가 삭제되고 새 키에 대한 서브트리가 생성된다. 이러면 성능이 저하될 뿐 아니라 서브트리의 위젯에서 예상하지 못한 동작이 발생할 수 있다...(중략)...State 객체가 GlobalKey를 갖게 하고 State.initState 같은 빌드 메서드 밖에서 인스턴스화하는 게 좋은 방법이다

 

GlobalKey는 어떤 위젯을 고유하게 식별해야 할 때 사용할 수 있는 클래스다. 전체 위젯 트리에서 어떤 위젯의 상태, 다른 객체에 접근할 수 있게 도와주는데, 이 특징 때문에 복잡한 상호작용과 상태 관리에 유용하게 쓸 수 있다.

예를 들어 어떤 위젯에서 발생한 이벤트에 반응해야 하는데 위젯이 자주 리빌드될 경우 GlobalKey가 없으면 매번 새 위젯이 만들어져서 상태를 유지하거나 참조할 수 없다. GlobalKey를 쓰면 플러터가 요소들을 각각 고유하게 인식할 수 있게 하기 때문에 무분별한 재빌드를 성능 향상을 노릴 수 있다.

GlobalKey를 사용할 수 있는 경우는 아래와 같다.

 

  • 상태 접근 : StatefulWidget의 상태에 접근하고 그 상태를 조작할 때 쓴다
  • 위젯 트리 재빌드 중에도 특정 위젯 참조 : 위젯 트리가 재구성되더라도 같은 위젯을 참조할 수 있다
  • 전역 접근 가능한 위젯에 접근 : Navigator에 접근할 때마다 일일이 BuildContext를 전달하긴 번거로운데 GlobalKey를 써서 Navigator를 전역으로 관리하면 어디서든 쉽게 접근할 수 있다

 

아래는 GlobalKey를 사용한 간단한 유효성 검사의 예시다. GlobalKey를 써서 Form 위젯에 접근해 유효성 검사를 실행하는 예시다.

 

import 'package:flutter/material.dart';

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

  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      home: Scaffold(
        appBar: AppBar(
          title: Text("GlobalKey 테스트"),
        ),
        body: MyForm(),
      ),
    );
  }
}

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

  @override
  State<MyForm> createState() => _MyFormState();
}

class _MyFormState extends State<MyForm> {

  final _formKey = GlobalKey<FormState>();

  @override
  Widget build(BuildContext context) {
    return Padding(
      padding: const EdgeInsets.all(16.0),
      child: Form(
        key: _formKey,
        child: Column(
          children: [
            TextFormField(
              decoration: InputDecoration(
                labelText: "이름을 입력하세요",
              ),
              validator: (value) {
                if (value == null || value.isEmpty) {
                  return "이름이 입력되지 않았습니다";
                }
                return null;
              },
            ),
            SizedBox(height: 20.0),
            ElevatedButton(
              onPressed: () {
                if (_formKey.currentState!.validate()) {
                  ScaffoldMessenger.of(context).showSnackBar(
                    SnackBar(content: Text("처리 중..."))
                  );
                }
              },
              child: Text("확인"),
            ),
          ],
        ),
      ),
    );
  }
}

 

메인 함수에서 GlobalKeyWithForm 클래스를 runApp()으로 실행하면 된다. 실행하면 아래 화면이 표시된다.

 

 

입력값이 null이거나 비었는지 검사하는 로직 때문에 아무것도 입력하지 않고 확인 버튼을 누르면 아래처럼 변한다.

 

 

뭔가 입력하고 확인 버튼을 누르면 스낵바가 표시된다.

 

 

반응형