라티의 작은 일기장

패스트캠퍼스 환급챌린지 11일차 미션(2월 11일): 15개 프로젝트로 실무까지 끝내는 Dart & Flutter 앱 개발 강의 후기 본문

Flutter & Dart

패스트캠퍼스 환급챌린지 11일차 미션(2월 11일): 15개 프로젝트로 실무까지 끝내는 Dart & Flutter 앱 개발 강의 후기

코드라티 2024. 2. 11. 15:33

오늘은 Flutter에서 지원하는 다양한 입력 위젯, 콜백과 Gesture에 대해 알아보려 한다.

일단 입력이 제대로 실행되는지 알기 위해 상태를 지원하는 Stateful 위젯을 활용해서 만들어보자.

처음으로 구현해볼 위젯은 Checkbox이다.

class _TestCheckBoxState extends State<TestCheckBox> {
  late List<bool> values;

  @override
  void initState() {
    super.initState();
    values = [false, false, false];
  }

  @override
  Widget build(BuildContext context) {
    return Row(
      children: [
        Checkbox(value: values[0], onChanged: (value) => changeValue(0, value: value)),
        Checkbox(value: values[1], onChanged: (value) => changeValue(1, value: value)),
        Checkbox(value: values[2], onChanged: (value) => changeValue(2, value: value))
      ],
    );
  }

  void changeValue(int index, {bool? value = false}) {
    setState(() {
      values[index] = value!; // nullable이지만 기본값 부여로 인해 null일 수 없음
    });
  }
}

내부적으로 관리하는 상태는 bool 타입의 List로 선언해주고, initState()로 상태를 초기화한다.

build는 3개의 Checkbox 위젯을 포함한 Row 위젯을 빌드하는데, 이 때 Checkbox의 경우 value 파라미터로는 checked / unchecked를 알 수 있도록 상태를 하나 넘겨주고, onChanged는 입력 등에 의해 상태가 변하면 실행할 콜백을 등록해준다.

그럼 이렇게 터치 반응에 따라 작동하는 체크박스를 확인할 수 있다.

 

비슷한 방식으로 Enum 기반의 상태를 활용하는 라디오 버튼도 만들 수 있다.

enum TestRadioValue { test1, test2, test3 }

class _ExampleRadioButtonState extends State<ExampleRadioButton> {
  TestRadioValue? selectValue;

  @override
  Widget build(BuildContext context) {
    return Column(
      children: [
        Radio<TestRadioValue>(
            value: TestRadioValue.test1,
            groupValue: selectValue,
            onChanged: (value) => setState(() {
                  selectValue = value;
                })),
        Radio<TestRadioValue>(
            value: TestRadioValue.test2,
            groupValue: selectValue,
            onChanged: (value) => setState(() {
                  selectValue = value;
                })),
        Radio<TestRadioValue>(
            value: TestRadioValue.test3,
            groupValue: selectValue,
            onChanged: (value) => setState(() {
                  selectValue = value;
                }))
      ],
    );
  }

만약 각 라디오버튼에Label을 달아주고 싶다면, ListTile이라는 위젯을 대신 사용할 수 있다.

ListTile(
  leading: Radio<TestRadioValue>(
    value: TestRadioValue.test1,
    groupValue: selectValue,
    onChanged: (value) => setState(() {
      selectValue = value;
    }),
  ),
  title: Text(TestRadioValue.test1.name),
),

 

볼륨이나 밝기 같은 값을 세밀하게 조절하기 위한 슬라이더라는 위젯도 있다.

bool 타입의 상태를 갖는 스위치도 만들어보았다.

위는 Material 디자인, 아래는 Cupertino 디자인의 스위치이다.

마지막으로 구현해본 위젯은 모던 웹 / 앱에서 라디오보다는 조금 더 자주 봤었던 팝업 메뉴이다. 나는 Selector라고 불렀었다.

입력을 위한 더 다양한 위젯은 플러터 공식 문서에서 확인할 수 있다고 한다.

 

다음으로 알아볼 것은 콜백과 제스처이다.

앞서 Flutter로 상태를 다루는 예제를 몇 번 구현해봤는데, 마치 React에서처럼 위젯이 관리하는 상태는 해당 위젯 내에서만 기본적으로 변경이 가능했다. 하지만 콜백을 통해 이 문제를 어느 정도 보완할 수 있다.

일단 이렇게 생긴 간단한 카운터를 만들었다. Counter의 수치를 표현하는 위젯과 Up 버튼은 별도의 위젯이고, 목표는 Up을 눌렀을 때 Counter의 수치가 증가하는 것(다른 위젯의 상태를 변화시키기)이다.

이걸 위해 활용해볼 수 있는 것은 Gesture이다. 앞서 Stateful 위젯을 실습하기 위해 잠깐 써봤었는데, GestureDetector는 말 그대로 제스처를 감지하는 녀석이다. addEventListener()와 같은 친구라고 할 수 있겠다. onTap, onDoubleTap, onLongPressDown... 등등 이벤트의 종류만큼 제스처도 다양하다. 추가로 InkWell이라는 감지기도 있는데, GestureDetector보다는 감지할 수 있는 제스처의 종류가 적지만 뭔가 있어보이는 스타일이 기본적으로 적용되어있다.

일단 여기에 이제 발생한 Gesture에 따른 콜백을 달아줄 수 있는데, 가장 많이 사용하는 onTap에다가 상태를 변하도록 하는 콜백을 달아주면 목표 달성이다.

 

그런데 상태를 변하게 하는 콜백은 어떻게 만들 수 있을까?

일단 해당 상태를 관리하는 위젯에서 만들어주긴 해야한다. 일종의 Setter함수로 생각하고 아래와 같이 간단하게 만들어주자.

그리고 IncreaseButton으로 넘겨주자.

class _TestWidgetState extends State<TestWidget> {
  int value = 0;

  @override
  Widget build(BuildContext context) {
    return Column(
      mainAxisAlignment: MainAxisAlignment.center,
      children: [
        Text('Counter: $value', style: TextStyle(fontSize: 30)),
        IncreaseButton(upCount)
      ],
    );
  }

  void upCount() {
    setState(() {
      value += 1;
    });
  }
}

 

 

그리고 upCount를 콜백으로 받기 위한 콜백을 Button 위젯에서 선언해줘야한다. 그리고 마치 웹 FE에서 상위 컴포넌트에서 prop으로 던져준 콜백을 가져오는 것처럼 매개변수로 callback을 등록해줘야한다.

// ...
child: GestureDetector(
  onTap: () => callback.call(),
  child: Center(
      child: Container(
    padding: EdgeInsets.symmetric(vertical: 8, horizontal: 4),
    decoration: BoxDecoration(border: Border.all()),
    child: Text(
      'Up!',
      style: TextStyle(fontSize: 20),
    ),
  )),
));

그리고 받아온 콜백을 화살표 함수로 감싸서 callback.call() 메서드로 호출하도록 작성한다.

그러면 이제 아래와 같이 콜백을 활용해 다른 위젯의 상태 변화를 확인할 수 있다.

 

 

본 포스팅은 패스트캠퍼스 환급 챌린지 참여를 위해 작성하였습니다.

https://bit.ly/48sS29N