Flutter와 Firebase로 Android iOS 둘 다 만들기 4 움직이는 화면 만들기

3 분 소요

Stateful widget을 사용한 움직이는 화면에 대해 알아봅니다.

개요

지금까지 사용한 위젯들은 모두 Stateless(상태가 없는) 위젯이었습니다.

Stateless는 단한번 렌더링 되기 때문에 중간에 화면 변경이 안됩니다.

반대로 Stateful 위젯은 상태가 있다는 것이고 화면에 변화를 줄 수 있다는 것입니다.

지금껏 저장해가면서 화면이 변한 것은 핫리로딩이니 착각하면 안됩니다.

Stateful 위젯을 만들어서 화면 전환을 해보면 이해할 수 있습니다.

클래스 작성법

Stateful Widget을 사용하려면 클래스를 만들어야합니다.

아직 클래스가 뭔지도 모르는데 만들라니 난감할 수 있지만, 모든 공식도큐에 50%이상 코드가 실려있으니 잘 몰라도 그냥 따라서해보면 됩니다.

import 'package:flutter/material.dart';

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

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

class HomeWidget extends StatefulWidget {
  @override
  _HomeWidgetState createState() => _HomeWidgetState();  
}
class _HomeWidgetState extends State<HomeWidget> {
  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(
        title: Text('hiru~')
      ),
      body: Center(
        child: Text('hi')
      )
    );
  }
}

기존에는 메인 클래스 MyApp에서 위젯들을 덕지덕지 붙히면 끝났지만..

Stateful 위젯의 경우 바깥에서 만들어야합니다.

화면에서 사용할 클래스와 변화를 줄 클래스 두가지가 필요합니다.

먼저 화면에서 사용할 HomeWidget를 만들고 State를 만들어줘야합니다.

마치 C++나 Java 같은 생성자 느낌입니다. (_HomeWidgetState createState() => _HomeWidgetState(); ) 같은 단어를 중첩해서 매우 비효율적으로 느껴집니다. new라도 빠져서 다행…

그리고 해당 위젯 State에서 표시할 위젯들을 빌드해주는 형태입니다.

클래스 명의 언더바(_)는 private로 인식하게 해줍니다.

그래서 가급적 변수명 앞에 _를 넣지 않는 것이 좋습니다… 저같은 경우는 mongodb에서 값을 호출해야할 경우가 있는데 몽고디비의 도큐 식별자가(_id)라 많이 난감했습니다..

참고

vscode의 extension들을 이용하면 클래스 작성이 쉽습니다.

참고

화면 변화 주기

값 변경을 해서 센터를 다시 그려보도록 코드를 수정해봅니다.

class _HomeWidgetState extends State<HomeWidget> {
  int i = 0;
  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(
        title: Text('hiru~'),
        actions: <Widget>[
          IconButton(
            icon: Icon(Icons.send),
            onPressed: () {
              i++;
            },
          )
        ],
      ),
      body: Center(
        child: Text('hi ~ $i')
      )
    );
  }
}

원하는 것은 hi ~ 0, 1 증가가 될 것 같은데 안됩니다.

이유는 setState 라는 함수를 사용해야 변경이 가능한 것입니다.

State class 안에서만 사용 가능합니다.

onPressed: () {
  // i++
  setState(() => i++);
},

기존의 i++ 대신 setState를 사용해서 값을 변경하면 잘 됩니다.

alt change

body: Center(
  child: ((i % 2 == 0) ? Text('hi ~ $i') : Icon(Icons.sentiment_dissatisfied))
)

간단한 삼항연산자로 짝수일 때는 문자열, 홀수일 때는 아이콘을 표시하게도 할 수 있습니다.

여러 조건일 경우

삼항연산자는 2가지 다른 표현만 가능합니다.

if나 switch로 다양한 렌더링을 하려면 어떻게해야할까요?

바로 새로운 위젯을 만들면됩니다.

잘 생각해보면 Text('hi ~$i')는 아마도 Stateless 위젯이겠죠?

변화를 위해 Stateful 위젯을 만들 필요는 없습니다.

위젯 만들기

class XXX extends StatelessWidget {
  XXX({Key key, this.i: 0}) : super(key: key);
  final int i;
  @override
  Widget build(BuildContext context) {
    if (i == 0) return CircularProgressIndicator();
    else if (i == 1) return Icon(Icons.settings);
    else return Text('xxx: $i');
  }
}

클래스 바깥에 XXX라는 State 없는 위젯을 만들었습니다.

위젯 클래스 윗부분에 key니 this.i니 super니 하는 것들은 대체 뭘까요?

자세히 알 필요는 없습니다. 어짜피 대부분의 예제 코드가 저렇게 되어 있습니다.

그저 바로 상위 위젯으로 부터 전달 받을 i를 위한 것입니다.

참고로 이런류의 생성자는 제가 몹시 싫어하는 방식입니다.. default의 미학 부분에서 감점입니다.

변화가 감지되면 build하기 때문에 상위 위젯이 State형태면 되는 것입니다.

적용하기

body: Center(
  // child: ((i % 2 == 0) ? Text('hi ~ $i') : Icon(Icons.sentiment_dissatisfied))
  body: XXX(i: i)
)

alt cir

3가지 화면이 잘 출력되는 것을 볼 수 있습니다.

가운데서 출력하려면?

별 것 없지만.. 헷갈려하시는 분들이 감을 잡기 위해 한번 만들어봅니다.

class XXX extends StatelessWidget {
  XXX({Key key, this.i: 0}) : super(key: key);
  final int i;
  @override
  Widget build(BuildContext context) {
    return Center(
      child: _change(i)
    );
  }
  Widget _change (i) {
    if (i == 0) return CircularProgressIndicator();
    else if (i == 1) return Icon(Icons.settings);
    else return Text('yyy: $i');
  }
}

alt center

마치며

참고로 vuex의 상태관리(상태, 변이, 액션)를 다뤄 보신 분들은 쉽게 이해가 갈 파트인 것 같습니다.

이번화에서 다룬 것들이 사실 핵심입니다.

멈춰 있는 화면은 의미가 없기 때문이죠..

소스코드

https://github.com/fkkmemi/ff

영상

댓글남기기