오래된 프로그래머의 요즘 이야기

그리고 소소한 일상들...

허니의 소소한 일상들이 켜켜이 쌓여가고......

Tech Story

[Flutter] 대화상자에서 setState() 사용

쭝허니 2025. 5. 31. 21:27

Flutter의 Dialog 안에서 setState 하기

오늘은 Flutter 개발 중에 꽤나 골치 아팠던 문제, 바로 **Dialog 안에서 setState를 사용하는 방법**에 대해 속 시원하게 알려드리려고 합니다.  저도 처음에 이 문제 때문에 몇 시간이고 끙끙댔던 기억이 나네요 😅  하지만 이제 걱정 끝! 이 글을 읽고 나면 Dialog 안에서 데이터 변경을 깔끔하게 처리할 수 있을 거예요.

 

**문제 상황: 왜 Dialog 안에서 setState가 안될까요?**

 

대부분의 Flutter 개발자들은 화면 업데이트를 위해 `setState`를 사용하는데 익숙하죠. 하지만 Dialog 안에서는 `setState`를 사용해도 화면이 갱신되지 않는 답답한 상황에 직면하게 됩니다.  왜 그럴까요?  간단히 말해, `setState`는 **해당 위젯 트리**를 재빌드하는 역할을 하기 때문입니다. Dialog는 메인 화면의 위젯 트리와는 별개의 트리를 가지고 있어서, Dialog 안에서 `setState`를 호출해도 메인 화면의 `setState`만 호출되는 겁니다.

 

**해결 방법: StatefulBuilder를 활용하세요! ✨**

 

자, 이제 해결책을 알려드릴게요! 바로 `StatefulBuilder` 위젯을 사용하는 겁니다.  `StatefulBuilder`는 자신만의 독립적인 상태를 관리하고, 내부에서 `StateSetter`를 이용해 UI를 업데이트할 수 있도록 해줍니다.  말로만 설명하면 어려우니, 바로 코드로 보여드릴게요!

 

**1. 프로젝트 구조**

 

먼저 프로젝트를 다음과 같이 구성해 봅시다. 간단하게 `main.dart`, `alter_dialog.dart`, `basic_button.dart` 세 개의 파일로 구성했습니다.

 

**2. main.dart**

 

```dart

import 'package:flutter/material.dart';

import 'alter_dialog.dart';

 

void main() {

  runApp(const MyApp());

}

 

class MyApp extends StatelessWidget {

  const MyApp({super.key});

 

  @override

  Widget build(BuildContext context) {

    return MaterialApp(

      title: 'Flutter Demo',

      theme: ThemeData(

        primarySwatch: Colors.blue,

      ),

      home: const DialogSetState(),

    );

  }

}

 

class DialogSetState extends StatefulWidget {

  const DialogSetState({super.key});

 

  @override

  State<DialogSetState> createState() => _DialogSetStateState();

}

 

class _DialogSetStateState extends State<DialogSetState> {

  int goalCount = 0;

 

  @override

  Widget build(BuildContext context) {

    return Scaffold(

      appBar: AppBar(

        title: const Text('Dialog SetState Example'),

      ),

      body: Center(

        child: ElevatedButton(

          onPressed: () => alterDialogSetState(

            goalCount: goalCount,

            context: context,

            closePressed: () {

              setState(() {

                // Dialog 닫을 때 메인 화면의 상태 업데이트 (필요에 따라 추가)

                Navigator.pop(context); 

              });

            },

          ),

          child: const Text('Show Dialog'),

        ),

      ),

    );

  }

}

```

 

**3. alter_dialog.dart (핵심!)**

 

```dart

import 'package:flutter/material.dart';

 

// 재사용 가능한 버튼 위젯

Widget basicTextButton({required String buttonText, required VoidCallback onPressed}) {

  return TextButton(

    onPressed: onPressed,

    child: Text(buttonText, style: const TextStyle(fontSize: 15)),

  );

}



alterDialogSetState({

  required BuildContext context,

  required Function() closePressed,

  required int goalCount,

}) {

  showDialog(

    context: context,

    builder: (context) => StatefulBuilder( // StatefulBuilder 사용!

      builder: (BuildContext context, StateSetter setDialog) { // StateSetter 받아오기

        return AlertDialog(

          title: const Text('Goal Counter'),

          content: Column(

            mainAxisSize: MainAxisSize.min,

            children: [

              Image.asset(

                'assets/son.png', // 이미지 경로 설정

                width: 120,

                height: 100,

              ),

              basicTextButton(

                buttonText: 'Goal!',

                onPressed: () {

                  setDialog(() { // StateSetter 사용하여 상태 업데이트

                    goalCount++;

                  });

                },

              ),

              Text('Goal Count: $goalCount'),

            ],

          ),

          actions: [

            basicTextButton(buttonText: 'Close', onPressed: closePressed),

          ],

        );

      },

    ),

  );

}

```

 

**4. assets/son.png**

 

`pubspec.yaml` 파일에 assets를 추가하고, assets 폴더에 적절한 이미지를 추가하세요. 예를 들어, 축구 선수 사진을 사용할 수 있습니다.



```yaml

flutter:

  assets:

    - assets/son.png

```



**핵심 코드 설명:**

 

* `StatefulBuilder`: Dialog의 내부 상태를 독립적으로 관리합니다.

* `StateSetter setDialog`: `StatefulBuilder`에서 제공하는 함수로, Dialog 내부의 상태를 변경하고 UI를 업데이트합니다.  `setDialog(() { ... });` 안에서 상태 변경 로직을 작성하면 됩니다.

 

**실행 결과 GIF**

 

(여기에 실행 결과 GIF를 삽입하세요.  위에 제공된 GIF 링크를 사용하거나, 직접 실행하여 GIF를 생성하여 삽입하면 됩니다.)



**결론:**

 

`StatefulBuilder`와 `StateSetter`를 사용하면 Dialog 안에서도 깔끔하게 데이터를 업데이트하고 화면을 갱신할 수 있습니다.  이제 Dialog 안에서 setState 때문에 고민할 필요가 없겠죠?