Flutter - showDialog 상태변환하기 (다이얼로그 새로고침, setState, StatefulBuilder, StatefulWdiget)

반응형

Overview

다이얼로그 내에서는 상태변환이 이뤄져도 다이얼로그 창의 위젯이 다시 렌더링이 되지 않는다.

이유는 다이얼로그를 포함하는 클래스 자체가 StatelessWidget으로 구성되어 있기 때문이다.

이것을 어떻게 확인하냐면, IDE툴에서 showDialog 혹은 AlertDialog 위치에서 F12를 눌러보자.

그럼 Dialog 클래스가 StatelessWidget을 상속하는 것을 확인할 수 있다.

(필자는 VS Code를 사용하며, F12는 Go to Definition 기능으로, 정의된 부분을 바로 확인할 수 있는 기능을 제공한다.)

<AlertDialog의 Go to Definition 확인 부분>

 

즉, 다이얼로그 창 내에서 상태변환을 하여도 데이터가 업데이트되지 않고, 재호출해야 바뀐 값을 불러온다.

아래의 이미지는  확인 버튼을 누르면 값이 +1씩 증가하는 앱이다.

그런데 아무리 확인 버튼을 눌러도 숫자는 증가되지 않고,

취소를 누르고 다시 다이얼로그 창을 열면 그제서야 변경된 값으로 나온다.

 

문제는 여기서 발생한다.

우리는 다이얼로그 창 내에서도 변경된 값이 실시간으로 업데이트 되기를 바란다.

이럴 때, StatefulWidget 혹은 StatefulBuilder를 이용해서 다이얼로그를 상태변환이 가능하도록 바꿀 수 있다.

아래는 상태변환이 가능하도록 코드를 변경한 예시 이미지이다.

 

이번 포스팅에서는 다이얼로그 내에서 값이 즉각적으로 업데이트 되도록 하는 방법에 대해서 알아본다.

(=다이얼로그 내의 위젯이 setState로 인하여 리렌더링 되도록 하는 방법)


showDialog의 기본 사용 방법을 알고 싶은 분은 아래의 포스팅을 확인해주세요.

 

포스팅에서 진행한 프로젝트는 아래의 깃허브 주소에서 다운로드 가능합니다.


StatefulBuilder 사용하여 다이얼로그 setState하기

showDialog 의 반환 위젯을 StatefulBuilder로 변경해야 한다.


StatefulBuilder 기본 구문

StatefulBuilder의 기본 구문은 다음과 같다.

StatefulBuilder(builder: (BuildContext context, StateSetter setState) {
	return //반환 위젯, 예를 들어 AlertDialog
});

사용 방법

StatefulBuilder의 builder 파라미터는 BuildContext와 StateSetter이다.

  • BuildContext : context 를 기재해주자.
  • StateSetter : setState와 같은 기능을 하는 함수이다. 일반적으로 편의상 상태변환을 하는 'setState' 함수와 이름이 같도록 파라미터 이름을 setState로 한다. 물론 파라미터 이름은 임의로 네이밍 가능하다.
  • 상태 변환이 이루어지는 부분에 StateSetter의 파라미터명을 넣어 상태변환 코드를 작성한다.

사용 예시

다이얼로그 창 내에서 버튼을 누르면 값이 1씩 증가되는 것을 업데이트하도록 StatefulBuilder로 코드를 변경해보자.

int value = 0;

/* 메인 build 중략 */

Future<void> _dialogBuilder() {
    return showDialog<void>(
      context: context,
      builder: (context) {
        return StatefulBuilder(builder: (BuildContext context, StateSetter setState) {
          return AlertDialog(
            title: const Text('StatefulBuilder 활용'),
            content: Text('다이얼로그 내에서 값 변경하기 : $value'),
            actions: [
              ElevatedButton(
                onPressed: () {
                  setState(() => value++);
                },
                child: const Text('확인'),
              ),
              OutlinedButton(
                onPressed: () {
                  Navigator.pop(context);
                },
                child: const Text('취소'),
              ),
            ],
          );
        });
      },
    );
  }

 

코드를 확인해보면 setState의 부분을 확인해보자.

이 부분은 StateSetter에서 정의한 파라미터 이름이다.

만약 이 이름이 abc라고 한다면, 코드는 다음과 같을 것이다.

abc(() => /* 코드작성 */ );

// setState(() => ~~~ ); 와 동일

StatefulWidget 사용하여 다이얼로그 setState하기

StatefulWidget은 StateBuilder와 다르다.

StateBuilder는 다이얼로그를 상태변환이 가능한 빌더로 감싸는 반면,

StatefulWidget은 상태변환이 가능한 StatefulWidget을 상속하여 클래스를 만든 후, 하위 위젯에 다이얼로그를 위치해주는 방식이다.

(취향 차이겠지만, 개인적으로는 StateBuilder가 편한 것 같다.)


StatefulWidget 사용 방법

기본 구문은 따로 없다.

말 그대로 Class를 StatefulWidget을 상속하여 하위 위젯을 위치하면 된다.

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

  @override
  State<DialogPage> createState() => _DialogPageState();
}

class _DialogPageState extends State<DialogPage> {
  int value = 0;

  @override
  Widget build(BuildContext context) {
    return AlertDialog( /* 코드 작성 */ );
  }
}

사용 예시

다이얼로그 창 내에서 버튼을 누르면 값이 1씩 증가되는 것을 업데이트하도록 StatefulWidget으로 코드를 변경해보자.

StatefulBuilder와 다르게 StatefulWidget을 상속한 새로운 클래스를 선언하고 하위에 다이얼로그를 붙이면 된다.

class _MainPageState extends State<MainPage2> {
int value = 0;

/* 메인 build 중략 */

  Future<dynamic> _dialogStfWidget() async {
    await showDialog<void>(
      context: context,
      builder: (context) {
        return const DialogPage();
      },
    );
  }
}

// StatefulWidget을 상속한 클래스에 하위 위젯 AlertDialog 위치
class DialogPage extends StatefulWidget {
  const DialogPage({super.key});

  @override
  State<DialogPage> createState() => _DialogPageState();
}

class _DialogPageState extends State<DialogPage> {
  int value = 0;

  @override
  Widget build(BuildContext context) {
    return AlertDialog(
      title: const Text('StatefulWidget 활용'),
      content: Text('다이얼로그 내에서 값 변경하기 : $value'),
      actions: [
        ElevatedButton(
          onPressed: () {
            setState(() => value++);
          },
          child: const Text('확인'),
        ),
        OutlinedButton(
          onPressed: () {
            Navigator.pop(context);
          },
          child: const Text('취소'),
        ),
      ],
    );
  }
}

참고

 

반응형