티스토리 뷰

Mobile/Flutter

[Flutter] State

춘햄 2022. 7. 11. 22:30

지금까지 예제를 다루면서 거의 모든 클래스는 StatelessWidget을 상속 받아서 작성을 했었다. 이는 간단히 말해서 딱히 상태 변화를 주어야할 변수를 클래스 내에 가지고 있지 않았기 때문에 굳이 State라는 개념을 사용하지 않은 것이다.

 

오늘은 직전 포스팅에서 사용했던 FirstScreen을 가지고 State, 상태라는 개념을 살펴보려고 한다.

 

State, 상태는 Flutter에서 변할 수 있는 데이터를 뜻한다. 그리고 이 데이터가 변할 때 화면 전체가 다시 빌드되는 것이 아닌, 데이터와 관련이 있는 위젯만 다시 빌드가 된다. 

 

이전까지 StatelessWidget을 사용했었는데, 이 StatelessWidget에서는 변수를 선언하거나 사용하면 const로 선언해달라는 경고가 뜨는 이유가 상태를 사용하지 않는 stateless widget에서는 변수의 값이 변하는 것이 의미가 없기 때문이다.

 

바로 한번 FirstScreen 클래스를 수정하면서 확인해보자.


우선 Flutter 프로젝트를 처음 생성할 때와 마찬가지로 값을 증가/감소 시킬 수 있는 카운터 어플을 하나 만들어보자.

 

◎first_screen.dart

import 'package:flutter/material.dart';
import 'package:first_flutter_ex/screens/second_screen.dart';
class FirstScreen extends StatefulWidget {
  _FirstScreenState createState() => _FirstScreenState();
}

class _FirstScreenState extends State<FirstScreen> {
  int count = 0;

  void increase() {
    setState(() {
      count = count + 1;
    });
  }

  void decrease() {
    setState(() {
      count = count - 1;
    });
  }

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(
        title: const Text('카운터 앱')
      ),
      body: Center(
        child: Column(
          mainAxisAlignment: MainAxisAlignment.center,
          children: [
            Text('카운트: $count', style: TextStyle(fontSize: 25)),
            Padding(padding: EdgeInsets.all(20)),
            Row(
              mainAxisAlignment: MainAxisAlignment.spaceEvenly,
              children: [
                ElevatedButton(onPressed: decrease, child: Text('- 감소')),
                ElevatedButton(onPressed: increase, child: Text('+ 증가'))
              ],
            )
          ],
        ),
      )
    );
  }
}

기존 StatelessWidget에서 StatefulWidget으로 변경하고, count라는 int 변수를 하나 선언하였다. 이 변수 State이기 때문에 변경하기 위해서는 반드시 위와 같이 dart의 setState 함수 안에 새로운 함수를 작성하여 변경해줘야 한다. 

 

앱을 한번 실행시켜보면 아래와 같이 버튼을 누를 때마다 count 변수가 변경되는 것을 확인할 수 있다.

위 increase() 코드를 아래와 같이 setState()를 사용하지 않고 작성을 한다면?

void increase() {
	count = count + 1;
	print("카운트 $count");
}

당연한 이야기겠지만, print로 나오는 count의 값은 증가하지만 화면에 표시될 State값의 변화가 없기 때문에 화면에 표시되는 카운트의 값은 변화가 없다.

 

정리해서, 어떤 변수나 데이터를 리로드하여 화면에 표시되는 값을 변경하려면 반드시 State 개념을 사용해야 한다는 것이다.


끝으로, StatefulWidget의 기본적인 구조를 생명주기 메서드 2개와 함께 확인해보자.

import 'package:flutter/material.dart';
import 'package:first_flutter_ex/screens/second_screen.dart';
class FirstScreen extends StatefulWidget {
  /*
  * StatefulWidget 를 상속 받은 위젯은 바로 Widget build 를 하는 것이 아닌,
  * StatefulWidget 내부에서 사용할 State 를 생성하기만 하고 나머지는
  * State 에게 맡긴다. createState() 는 말 그대로 State 를 생성하는 메서드이며,
  * StatefulWidget 의 시작은 createState()로 State 를 생성하는 것이다.
  * */
  _FirstScreenState createState() => _FirstScreenState();
}

/*
 * State 또한 Flutter 내부에서 선언된 클래스이다.
 * 이를 상속받아 FirstScreen 에 대한 상태임을 <> 안에 넣어 표현해줘야 한다.
 * 해당 클래스 앞에 _를 붙이는 것은 관례적인 표현이다.
 * */
class _FirstScreenState extends State<FirstScreen> {

 /*
 * 앞서 생성한 State 를 초기화하는 단계, 필수적으로 오버라이드해햐 하는 메서드는 아니며,
 * 상태값에 대한 초기화를 해야하는 경우에 사용한다.
 * */
  @override
  void initState() {
    super.initState();
  }

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(),
      body: Center(),
    );
  }

  /*
 * 사용된 위젯이 사라질 때 실행되는 단계이다. 앞선 단계에서 사용하던 데이터 등을
 * 정리해야 한다면, dispose() 단계에서 수행할 수 있다.
 * 역시 필수로 작성해야 하는 메서드는 아니다.
 * */
  @override
  void dispose() {
    super.dispose();
  }
}

주석으로 대부분 설명이 쓰여 있지만, 가장 중요한 것은 StatefulWidget은 필수적이지 않은 오버라이드 메서드를 제외하고 반드시 StatefulWidget을 상속받는 클래스 하나와 State를 상속하는 _표현식의 클래스 하나를 가지는 구조로 선언해야 한다는 것이다.

 

 

끝!

'Mobile > Flutter' 카테고리의 다른 글

[Flutter] 전역 상태 관리 기법: Bloc  (0) 2022.07.25
[Flutter] API 연동  (0) 2022.07.24
[Flutter] 화면 전환  (0) 2022.07.09
[Flutter] Buttons  (0) 2022.07.09
[Flutter] Widget  (0) 2022.07.09
Comments