본문 바로가기

Flutter/06 Basic

[Flutter] Basic - key

이번에는 Key 에 대해서 알아보겠습니다.

개발환경 : 윈도우11, 안드로이드 스튜디오, flutter 3.0.1

소스코드 위치 - Release 03_global_key · mike-bskim/key_test · GitHub

 

Release 03_global_key · mike-bskim/key_test

 

github.com

 

 

초기 화면은 아래와 같습니다.

 

 

 

Key 는 우리가 생각없이 지나치지만 위젯을 이동/삭제할 경우 아주 중요한 핵심 포인트입니다.

Todo 리스트 처럼 일정 순서를 이동/삭제 등 변경할때 주로 사용된다.

Flutter 는 기본적으로 위젯의 타입으로 각자 구분하지만

동일한 위젯을 여러개 사용할때 구분할수 있는 포인트가 key 입니다.

 

 

Key 종류는 아래와 같은데, 여기서는 상위 3개에 대해서 알아보겠습니다.

  • Unique key
  • Value key
  • Global key
  • Object key
  • Page storage key

 

 

Unique key 사용 예시 - floatingActionButton 클릭시 2개의 컨테이너가 서로 위치를 바꾼다

 

 

 

unique_key.dart

 

import 'package:flutter/material.dart';
import 'dart:math';
import 'package:fluttertoast/fluttertoast.dart';

class UniqueKeyTest extends StatefulWidget {
  const UniqueKeyTest({Key? key}) : super(key: key);

  @override
  State<StatefulWidget> createState() => UniqueKeyTestState();
}

class UniqueKeyTestState extends State<UniqueKeyTest> {

// key 를 주지않으면 2개의 변경결과를 rendering 시 매칭하지 못한다
  List<Widget> tiles = [
    StatefulColorfulTile(
      key: UniqueKey(),
    ),
    StatefulColorfulTile(
      key: UniqueKey(),
    ),
  ];

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(
        centerTitle: true,
        title: const Text('Unique Key'),
      ),
      body: Column(
        mainAxisAlignment: MainAxisAlignment.center,
        children: [
          Row(
            mainAxisAlignment: MainAxisAlignment.center,
            children: tiles,
          ),
        ],
      ),
      floatingActionButton: FloatingActionButton(
          child: const Icon(Icons.double_arrow_rounded),
          onPressed: () {
            swapTiles();
            flutterToast();
          }),
    );
  }

// 첫째 배열을 제거해서 그것을 2번째 배열에 넣는다
  void swapTiles() {
    setState(() {
      tiles.insert(1, tiles.removeAt(0));
    });
  }
}

class StatefulColorfulTile extends StatefulWidget {
  const StatefulColorfulTile({Key? key}) : super(key: key);

  @override
  StatefulColorfulTileState createState() => StatefulColorfulTileState();
}

class StatefulColorfulTileState extends State<StatefulColorfulTile> {
  Color myColor = getRandomColor();

  @override
  Widget build(BuildContext context) {
    debugPrint('StatefulColorfulTile.key :${widget.key}');
    return Container(
      color: myColor,
      child: const Padding(
        padding: EdgeInsets.all(70.0),
      ),
    );
  }
}

getRandomColor() {
  var r = Random();
  return Color.fromARGB(255, r.nextInt(255), r.nextInt(255), r.nextInt(255));
}

void flutterToast() {
  Fluttertoast.showToast(
      msg: 'Color has been changed',
      gravity: ToastGravity.BOTTOM,
      backgroundColor: Colors.blue,
      textColor: Colors.white,
      toastLength: Toast.LENGTH_SHORT);
}

 

 

Value key 사용 예시 - 버튼 클릭시, 첫번째 텍스트필드가 삭제되며 입력값을 유지하는지 확인

 

 

 

value_key.dart

 

import 'package:flutter/material.dart';

class ValueKeyTest extends StatefulWidget {
  const ValueKeyTest({Key? key}) : super(key: key);

  @override
  ValueKeyTestState createState() => ValueKeyTestState();
}

class ValueKeyTestState extends State<ValueKeyTest> {
  bool emailField = true;
  TextEditingController idController = TextEditingController();
  TextEditingController pwController = TextEditingController();

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(
        title: const Text('Value Key'),
        centerTitle: true,
      ),
      body: GestureDetector(
        onTap: () {
          FocusScope.of(context).unfocus();
        },
        child: Padding(
          padding: const EdgeInsets.all(16.0),
          child: Column(
            mainAxisAlignment: MainAxisAlignment.center,
            children: <Widget>[
              if (emailField)
                const TextField(
                  key: ValueKey(1),
                  // controller 를 사용하면 key 를 사용한 것과 동일한 결과임
                  // controller: idController,
                  decoration: InputDecoration(labelText: 'Enter "dice"'),
                  keyboardType: TextInputType.emailAddress,
                ),
              const TextField(
                key: ValueKey(2),
                // controller 를 사용하면 key 를 사용한 것과 동일한 결과임
                // controller: pwController,
                decoration: InputDecoration(labelText: 'Enter Password'),
                keyboardType: TextInputType.text,
              ),
              const SizedBox(
                height: 40.0,
              ),
              ButtonTheme(
                minWidth: 100.0,
                height: 50.0,
                child: ElevatedButton(
                  style: ElevatedButton.styleFrom(
                    minimumSize: const Size(150, 50),
                  ),
                  // color:
                  child: const Icon(
                    Icons.visibility_off,
                    color: Colors.white,
                    size: 35.0,
                  ),
                  onPressed: () {
                    setState(() {
                      emailField = false;
                    });
                  },
                ),
              ),
            ],
          ),
        ),
      ),
    );
  }
}

 

 

Global key

키값은 전체 프로그램에서 유일한 값이어야 한다.

요소들을 유일하게 구분한다.

Context 등과 같은 다른 요소들과 연관된 Object 들에 접근할수 있어야 하고 상태(StatefulWidget 경우)에 접근할 수 있다

하위 속성중 하나인 currentState 을 이용하면 특정위젯의 하위 객체에 접근 가능함

 

 

Global key 사용 예시 - 상위 위젯(GlobalKeyTest)에서 하위 위젯(Counter)의 객체를 접근

 

 

 

global_key.dart

 

import 'package:flutter/material.dart';

class GlobalKeyTest extends StatefulWidget {
  const GlobalKeyTest({Key? key}) : super(key: key);

  @override
  State<GlobalKeyTest> createState() => _GlobalKeyTestState();
}

class _GlobalKeyTestState extends State<GlobalKeyTest> {
  // 다른 StatefulWidget 의 상태를 접근하기 위해서 GlobalKey 키를 이용해서 접근
  final counterKey = GlobalKey<CounterState>();

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(
        title: const Text('Global key'),
      ),
      body: Center(
        child: Counter(
          key: counterKey,
        ),
      ),
      floatingActionButton: FloatingActionButton(
        child: const Icon(
          Icons.add,
          color: Colors.white,
        ),
        onPressed: () {
          // 다른 StatefulWidget 의 상태를 접근하기 위해서 GlobalKey 키를 이용해서 접근
          counterKey.currentState!.increment();
          debugPrint(counterKey.currentState!.count.toString());
        },
      ),
    );
  }
}

class Counter extends StatefulWidget {
  const Counter({Key? key}) : super(key: key);

  @override
  CounterState createState() => CounterState();
}

class CounterState extends State<Counter> {
  int count = 0;
  void increment() {
    setState(() {
      count++;
    });
    // debugPrint(count.toString());
  }

  @override
  Widget build(BuildContext context) {
    return Center(
      child: Text(
        'Count number: $count',
        style: const TextStyle(fontSize: 20.0),
      ),
    );
  }
}

 

 

 

 

 

 

[참고자료] 코딩셰프 -

https://www.youtube.com/watch?v=lQB6HjleLMs&list=PLQt_pzi-LLfoOpp3b-pnnLXgYpiFEftLB&index=20