피트니스 산업과 IT, 그리고 스타트업

IT

플러터에서 State 활용하기

핏더스트리 2025. 3. 8. 15:45
728x90
반응형

Flutter에서 UI를 만들다 보면 사용자 입력에 따라 화면이 바뀌거나, 특정 데이터가 변경될 때마다 UI를 업데이트해야 하는 경우가 많이 생겨요. 이런 동적인 변화가 필요한 상황에서는 state를 사용하면 화면을 보다 효과적으로 관리할 수 있답니다!

 

예를 들어, 버튼을 누를 때 숫자가 증가하는 카운터 앱, 체크박스를 클릭하면 선택 여부가 바뀌는 UI, API에서 데이터를 가져와 화면을 갱신해야 하는 경우 등이 있어요. 이처럼 state는 앱 서비스를 만드는데 있어서 아주 기초적인 것들을 구현할 수 있게 해주는 아주 중요한 개념이에요.

 

이번 글에서는 state가 어떻게 동작하는지, 그리고 어떻게 사용하야 하는지에 대해서 알아볼게요!

 


State 사용법과 간단한 예시 코드

 

Flutter에서 위젯은 상태가 변하지 않는 위젯인 StatelessWidget과 상태에 따라 UI가 변경되는 위젯인 StatefulWidget, 두 가지 유형으로 나뉘어요.

 

StatefulWidget을 사용하면 내부의 데이터를 변경하면서 화면을 다시 그릴 수 있어요. 가장 간단한 예제로 버튼을 누를 때 숫자가 증가하는 카운터 앱을 만들어볼게요.

 

import 'package:flutter/material.dart';

void main() {
  runApp(MyApp());
}

class MyApp extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      home: CounterScreen(),
    );
  }
}

class CounterScreen extends StatefulWidget {
  @override
  _CounterScreenState createState() => _CounterScreenState();
}

class _CounterScreenState extends State<CounterScreen> {
  int _counter = 0;

  void _incrementCounter() {
    setState(() {
      _counter++; // 상태 변경
    });
  }

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(title: Text("State 예제")),
      body: Center(
        child: Column(
          mainAxisAlignment: MainAxisAlignment.center,
          children: [
            Text("현재 카운트: $_counter", style: TextStyle(fontSize: 20)),
            SizedBox(height: 20),
            ElevatedButton(
              onPressed: _incrementCounter,
              child: Text("숫자 증가"),
            ),
          ],
        ),
      ),
    );
  }
}

 

여기서 핵심은 setState() 함수를 호출하여 _counter 변수를 변경하면 Flutter가 자동으로 화면을 다시 렌더링해준다는 점이에요.


 

부모의 State를 자식 요소에 상속하는 법

 

State가 필요한 상황에서는 부모 위젯에서 관리하는 상태를 자식 위젯에서도 사용할 일이 많아요. 부모에서 선언된 상태를 여러 자식 위젯에서 사용하도록 만들면 상태 관리가 보다 일관성 있게 이루어질 수 있어요.

 

예를 들어, 부모 위젯에서 _counter 값을 관리하고, 이를 자식 위젯에서 보여주도록 할 수 있어요.

 

import 'package:flutter/material.dart';

void main() {
  runApp(MyApp());
}

class MyApp extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      home: CounterScreen(),
    );
  }
}

class CounterScreen extends StatefulWidget {
  @override
  _CounterScreenState createState() => _CounterScreenState();
}

class _CounterScreenState extends State<CounterScreen> {
  int _counter = 0;

  void _incrementCounter() {
    setState(() {
      _counter++;
    });
  }

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(title: Text("부모 State를 자식에게 전달")),
      body: Column(
        mainAxisAlignment: MainAxisAlignment.center,
        children: [
          CounterDisplay(count: _counter), // 부모의 상태를 자식에게 전달
          SizedBox(height: 20),
          ElevatedButton(
            onPressed: _incrementCounter,
            child: Text("숫자 증가"),
          ),
        ],
      ),
    );
  }
}

class CounterDisplay extends StatelessWidget {
  final int count;

  CounterDisplay({required this.count});

  @override
  Widget build(BuildContext context) {
    return Center(
      child: Text(
        "현재 카운트: $count",
        style: TextStyle(fontSize: 20),
      ),
    );
  }
}

 

이 코드에서 CounterScreen이 _counter 상태를 관리하고 있고, 이를 CounterDisplay라는 자식 위젯에 전달하고 있어요. 이렇게 하면 상태는 부모에서 한 번만 관리하고, 여러 자식 위젯에서 사용할 수 있어요.


자식 요소에서 부모 요소의 State를 변경하는 법

 

부모의 state를 자식 요소에 전달하는 것은 쉬운 편이지만, 반대로 자식 요소에서 부모 요소의 상태를 변경하려면 부모의 메서드를 자식에서 넘겨줘야 해요. 이를 통해 자식이 특정 이벤트를 발생시켰을 때 부모의 상태를 업데이트할 수 있어요.

 

예를 들어, 부모가 _counter 상태를 관리하고, 버튼을 자식 위젯에 따로 분리해서 자식 위젯에서 버튼을 클릭할 때 부모의 상태를 변경하는 구조를 만들어볼게요.

 

import 'package:flutter/material.dart';

void main() {
  runApp(MyApp());
}

class MyApp extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      home: CounterScreen(),
    );
  }
}

class CounterScreen extends StatefulWidget {
  @override
  _CounterScreenState createState() => _CounterScreenState();
}

class _CounterScreenState extends State<CounterScreen> {
  int _counter = 0;

  void _incrementCounter() {
    setState(() {
      _counter++;
    });
  }

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(title: Text("자식에서 부모 State 변경")),
      body: Column(
        mainAxisAlignment: MainAxisAlignment.center,
        children: [
          CounterDisplay(count: _counter),
          SizedBox(height: 20),
          CounterButton(onPressed: _incrementCounter), // 부모 메서드를 자식에게 전달
        ],
      ),
    );
  }
}

class CounterDisplay extends StatelessWidget {
  final int count;

  CounterDisplay({required this.count});

  @override
  Widget build(BuildContext context) {
    return Center(
      child: Text(
        "현재 카운트: $count",
        style: TextStyle(fontSize: 20),
      ),
    );
  }
}

class CounterButton extends StatelessWidget {
  final VoidCallback onPressed; // 부모의 메서드를 콜백으로 받음

  CounterButton({required this.onPressed});

  @override
  Widget build(BuildContext context) {
    return ElevatedButton(
      onPressed: onPressed, // 버튼을 클릭하면 부모의 상태 변경 메서드 호출
      child: Text("숫자 증가"),
    );
  }
}

 

이 코드에서 CounterScreen이 _counter 상태를 관리하고, 이를 CounterDisplay에서 출력하고 있어요. 하지만 버튼(CounterButton)은 자식 위젯으로 분리했고, 부모의 _incrementCounter() 메서드를 onPressed 콜백으로 자식에게 넘겨줬어요.

 

이렇게 하면 버튼을 누를 때 자식 위젯에서 부모 위젯의 상태를 변경할 수 있어요. 즉, 부모는 상태를 관리하고, 자식은 UI 요소를 담당하는 방식으로 코드가 더 깔끔해질 수 있어요.


 

마치며...

 

Flutter에서 state를 다루는 방법을 배우는 것은 UI를 동적으로 변경하는 데 필수적인 요소예요. StatefulWidget을 활용하면 내부적으로 상태를 변경하면서 화면을 업데이트할 수 있고, 부모에서 자식으로 데이터를 전달하는 방식으로 상태를 공유할 수도 있어요.

 

또한 자식 요소에서 부모 요소의 상태를 변경하는 방법을 알면 컴포넌트 기반 UI를 좀 더 깔끔하고 효율적으로 설계할 수 있어요. 이를 활용하면 여러 위젯 간의 데이터 흐름을 더 잘 관리할 수 있으며, 코드도 모듈화할 수 있어요.

 

Flutter에서 상태 관리는 매우 중요한 개념이기 때문에 기본적인 StatefulWidget과 setState()를 이해한 후, 나중에는 Provider, Riverpod, Bloc 같은 상태 관리 패턴도 함께 공부해보는 것이 좋아요.

 


마지막 세 줄 요약

  1. StatefulWidget을 사용하면 내부 상태를 변경하면서 UI를 업데이트할 수 있어요.
  2. 부모의 상태를 자식에게 전달하려면 생성자 매개변수를 활요하면 돼요.
  3. 자식 요소에서 부모의 상태를 변경하려면 부모의 메서드를 콜백으로 전달해야 해요.
728x90
반응형