티스토리 뷰

반응형

 

안녕하세요 :) Zedd입니다.

저번에 StatelessWidget을 공부했습니다. 

오늘은 StatefulWidget을 공부하고 만들어보겠습니다.

IDE를 켜시고 차근차근 코드를 복붙해가시면서....같이 해보면 좋을 것 같습니다!

 

StatefulWidget


플러터를 처음 만들면 보이는 예제코드에서도

class MyHomePage extends StatefulWidget {...}

이런게 있었던걸 보셨을 겁니다. 

StatefulWidget은 "변경 가능한 상태를 가진 Widget"입니다.

여기서 "상태"라는 건

1. Widget이 빌드 될 때 동기적으로 읽을 수 있고

2. Widget의 생명주기동안 변경될 수 있는 

정보입니다.

그래서 StatefulWidget은 UI가 동적으로 변경될 수 있는 경우에 유용해요. 

제가 하단의 플로팅버튼을 누르면 누른 횟수가 저 Count Text Widget에 표시되게 됩니다.

count라는 상태가 변함 -> UI의 변화가 있어야함

즉 상태에 따른 UI의 변화가 필요할  때 StatefulWidget을 사용하면 됩니다.

 

만드는 방법


먼저 만드는 방법부터 알아보겠습니다. 한땀한땀 코드로 쳐도 좋겠지만..

stful을 치고 엔터를 누르면

이렇게 클래스 2개가 만들어집니다.

빨간색 박스에 이름을 쓰면 초록색박스들도 같이 업데이트 되는 것을 볼 수 있습니다.

그럼 여기까지 코드에 적용해보겠습니다. 

import 'package:flutter/material.dart';
import 'package:flutter/cupertino.dart';

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

class ZeddApp extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return CupertinoApp(home: Scaffold(
        appBar: CupertinoNavigationBar(middle: Text("Hello, Zedd!")),
        body: ZeddPage() // ✅
    )
    );
  }
}

class ZeddPage extends StatefulWidget {
  @override
  _ZeddPageState createState() => _ZeddPageState();
}

class _ZeddPageState extends State<ZeddPage> {
  @override
  Widget build(BuildContext context) {
    return Container();
  }
}

StatefulWidget역시 Widget입니다!

그래서 그냥 body에 때려넣었어요.

이런식으로 나오게 됩니다. 

여기까진 쉽죠? 

 

Button과 Text만들기


그럼 저희도 예제코드랑 똑같이 Button과 이 버튼을 누른 숫자를 나타낼 Text Widget을 만들어보겠습니다.

class ZeddPage extends StatefulWidget {
  @override
  _ZeddPageState createState() => _ZeddPageState();
}

class _ZeddPageState extends State<ZeddPage> {
  @override
  Widget build(BuildContext context) {
    return Center(child:
      Column(children: [
        Text("0"),
        CupertinoButton(child: Text("버튼 제목"), onPressed: ()=>{})
    ]));
  }
}

_ZeddPageState부분을 이렇게 해주겠습니다. 

예제코드에서는 막 플로팅버튼으로 했었는데..UI가 중요한게 아니니 대충 만들어줍니다.

이렇게 만들어지게 됩니다.

 

count변수 만들기


자..지금은

Text("0")

이렇게 되어있는데..이렇게 static하게 들어가면 안되겠죠?

1. count변수가 있음

2. 내가 버튼을 누르면 count가 1씩 증가

3. count의 값을 Text Widget에 보여줌

이렇게 되어야할 것 같아요.

그럼 일단 count변수를 만들어줍시다.

class _ZeddPageState extends State<ZeddPage> {
 
  int count = 0; // ✅

  @override
  Widget build(BuildContext context) {
    return Center(child:
      Column(children: [
        Text("$count"), // ✅
        CupertinoButton(child: Text("버튼 제목"), onPressed: ()=>{})
    ]));
  }
}

count변수를 만들고, Text에 count를 표시하도록 해줬습니다.

1번은 한거고..이제 버튼을 눌렀을 때의 action을 지정해주면 될 것 같습니다. 

 

button을 누르면 count가 1이 증가도록 만들어주기


그럼 우리가 방금 만든 변수인 count를 1증가 시켜주어야합니다.

class _ZeddPageState extends State<ZeddPage> {

  int count = 0;

  @override
  Widget build(BuildContext context) {
    return Center(child:
    Column(children: [
      Text("$count"),
      CupertinoButton(child: Text("버튼 제목"), onPressed: this.incrementCounter)
    ]));
  }

  void incrementCounter() {
    setState(() {
      this.count++;
    });
  }
}

따로 함수를 만들어주었습니다. (예제 코드처럼요)

count를 증가시켜주는 코드가 setState로 감싸져있는데요, 

setState안에서의 호출은 State에서 무언가 변경되었음을 Flutter 프레임워크에 알려준다고 합니다.

이로 인해 UI에 업데이트 된 값이 반영될 수 있도록 build메소드가 다시 실행된다고 합니다!! 

setState를 사용하지 않고 count를 증가시키면 build메소드가 다시 호출되지 않으므로 UI가 변경되지도 않아요.

 

코드는 끝났습니다!!!

이렇게 간단하게 StatefulWidget사용 방법을 알아봤습니다.

import 'package:flutter/material.dart';
import 'package:flutter/cupertino.dart';

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

class ZeddApp extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return CupertinoApp(home: Scaffold(
        appBar: CupertinoNavigationBar(middle: Text("Hello, Zedd!")),
        body: ZeddPage())
    );
  }
}

class ZeddPage extends StatefulWidget {
  @override
  _ZeddPageState createState() => _ZeddPageState();
}

class _ZeddPageState extends State<ZeddPage> {

  int count = 0;

  @override
  Widget build(BuildContext context) {
    return Center(child:
    Column(children: [
      Text("$count"),
      CupertinoButton(child: Text("버튼 제목"), onPressed: this.incrementCounter)
    ]));
  }

  void incrementCounter() {
    setState(() {
      this.count++;
    });
  }
}

지금까지의 전체코드에요.

 

좀 더 개선해보겠습니다.

CupertinoButton(child: Text("버튼 제목"), onPressed: this.incrementCounter)

여기 버튼제목을 입력받고 싶습니다.

그럼 어떻게 해야하냐!?

class ZeddPage extends StatefulWidget {

  @override
  _ZeddPageState createState() => _ZeddPageState();
}

우리가 지금까지 한번도 안본..ZeddPage클래스에 가서 입력받고 싶은 프로퍼티들을 선언해주면 됩니다.

저는 버튼 제목을 입력받고 싶으니 String타입을 하나 선언해줄게요. 

class ZeddPage extends StatefulWidget {

  String buttonText;
  
  @override
  _ZeddPageState createState() => _ZeddPageState();
}

이렇게 해도 됩니다! 해도 되지만, IDE에서 warning이 뜰거에요. final로 선언하는게 좋지 않겠니~? 하는 warning입니다.

Q : final은 상수인데..StatefulWidget은 뭐 상태 변경 가능하다며!?

A :

StatefulWidget의 서브클래스에서 상태를 변경하는게 아니라 State쪽에서 상태를 변경하므로 

변경가능한 것들은 전부 State쪽에 선언하고, 

StatefulWidget 서브클래스에서는 변경 가능하지 않은 상수들을 선언한다고 합니다..!!

 

final로 선언해주겠습니다.

class ZeddPage extends StatefulWidget {
  final String buttonText;

  ZeddPage({this.buttonText});
  
  @override
  _ZeddPageState createState() => _ZeddPageState();
}

final이기 때문에 초기화가 되어야 하므로 생성자도 선언해줍니다.

class ZeddApp extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return CupertinoApp(home: Scaffold(
        appBar: CupertinoNavigationBar(middle: Text("Hello, Zedd!")),
        body: ZeddPage(buttonText: "누르삼")) ✅ 
    );
  }
}

class ZeddPage extends StatefulWidget {
  final String buttonText; ✅ 

  ZeddPage({this.buttonText}); ✅ 

  @override
  _ZeddPageState createState() => _ZeddPageState();
}

생성자를 넘겨주고, buttonText를 넘겨줍니다.

 

자...그럼.

class _ZeddPageState extends State<ZeddPage> {

  ...
  CupertinoButton(child: Text("버튼 제목"), onPressed: this.incrementCounter) ✅
  ...
}

이쪽에서 buttonText를 사용해야합니다. 

class _ZeddPageState extends State<ZeddPage> {

  ...
  CupertinoButton(child: Text(this.widget.buttonText), onPressed: this.incrementCounter) ✅
  ...
}

이렇게 widget으로 내가 StatefulWidget에 선언한 프로퍼티들에 접근이 가능합니다.

widget은 제가 뭐 따로 선언한게 아니라 그냥 기본적으로 접근이 가능합니다.

최종적으로 내가 넣은 Text가 button의 title이 되겠죠.

import 'package:flutter/material.dart';
import 'package:flutter/cupertino.dart';

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

class ZeddApp extends StatelessWidget {

  @override
  Widget build(BuildContext context) {
    return CupertinoApp(home: Scaffold(
        appBar: CupertinoNavigationBar(middle: Text("Hello, Zedd!")),
        body: ZeddPage(buttonText: "누르삼",))
    );
  }
}

class ZeddPage extends StatefulWidget {

  final String buttonText;

  ZeddPage({this.buttonText});

  @override
  _ZeddPageState createState() => _ZeddPageState();
}

class _ZeddPageState extends State<ZeddPage> {

  int count = 0;

  @override
  Widget build(BuildContext context) {
    return Center(child:
    Column(children: [
      Text("$count"),
      CupertinoButton(child: Text(this.widget.buttonText), onPressed: this.incrementCounter)
    ]));
  }

  void incrementCounter() {
    setState(() {
      this.count++;
    });
  }
}

전체 코드입니다. 

예제코드의 간단한 버전..이라고 할 수 있겠네요 ㅎㅎ..

 

참고

api.flutter.dev/flutter/widgets/StatefulWidget-class.html

 

StatefulWidget class - widgets library - Dart API

A widget that has mutable state. State is information that (1) can be read synchronously when the widget is built and (2) might change during the lifetime of the widget. It is the responsibility of the widget implementer to ensure that the State is promptl

api.flutter.dev

 

반응형