본문 바로가기
Development/Flutter

Flutter - 2. Widget? StatelessWidget, StatefulWidget

by du.it.ddu 2021. 1. 3.
반응형

Flutter는 Widget으로 앱의 화면을 구성한다.
Flutter는 데이터 또는 상태를 Widget으로 표현하는 어플리케이션이라고 보면 될 것 같다. (지극히 개인적인 이해)

Widget은 크게 StatelessWidget과 StatefulWidget으로 나뉘며, Flutter 프로젝트를 생성하면 자동으로 만들어지는 main.dart를 보며 가볍게 이해해보도록 하겠다.

여기서 프로젝트 생성 시 작성되어 있는 주석은 모두 삭제하였다.


1. main.dart의 void main()과 MyApp

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

class MyApp extends StatelessWidget {
  // This widget is the root of your application.
  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      title: 'Flutter Demo',
      theme: ThemeData(
        primarySwatch: Colors.blue,
        visualDensity: VisualDensity.adaptivePlatformDensity,
      ),
      home: MyHomePage(title: 'Flutter Demo Home Page'),
    );
  }
}

프로젝트를 생성하면 lib 폴더 아래에 main.dart 파일이 있을 것이다.
파일을 열어보면 최상단에 main 함수와 MyApp이 작성되어 있다.

main 함수는 앱을 시작시키는 함수가 되며, runApp 함수 내부에 MyApp 인스턴스를 생성하여 전달하고 있다.
MyApp은 StatelessWidget을 상속하는 클래스이다.

여기서 알 수 있는것은 앱도 Widget중 하나라는 것이다.

그럼 StatelessWidget은 무엇일까? 해석을 하면 "상태가 없는 Widget" 이다.

StatelessWidget은 상태에 따른 변화가 없는 Widget으로, 정적인 UI를 의미한다.
그리고 StatelessWidget을 상속하는 Widget은 위 코드에서 볼 수 있듯이, "Widget build(BuildContext context)" 를 오버라이딩 해야한다.

그럼 build 함수를 통해 Widget이 표현하고자 하는 화면이 보여지게 된다.


2. main.dart의 MyHomePage()와 StatefulWidget

위의 MyApp에서 home 파라미터에 MyHomePage 인스턴스를 생성하여 넘겨주고 있다.
MyHomePage 코드를 살펴보자.

class MyHomePage extends StatefulWidget {
  MyHomePage({Key key, this.title}) : super(key: key);

  final String title;

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

MyHomePage는 StatefulWidget을 상속하고 있다.

StatelessWidget과 반대로, "상태에 따라 동적으로 변하는 Widget"을 의미한다.
StatefulWidget을 상속하는 Widget은 "State<StatefulWidget> createState()" 함수를 오버라이딩 해야한다.

위에선 _MyHomePageState 를 반환하도록 하여 오버라이딩 한 것을 알 수 있다.
_MyHomePageState는 State<StatefulWidget>을 상속하는 클래스임을 추론할 수 있다.

여기서 클래스 앞에 언더바(_) 를 붙인 것은 private를 의미하며 함수나 변수에도 동일하게 작용한다.


3. main.dart의 _MyHomePageState()와 State<StatefulWidget>

class _MyHomePageState extends State<MyHomePage> {
  int _counter = 0;

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

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(
        title: Text(widget.title),
      ),
      body: Center(
        child: Column(
          mainAxisAlignment: MainAxisAlignment.center,
          children: <Widget>[
            Text(
              'You have pushed the button this many times:',
            ),
            Text(
              '$_counter',
              style: Theme.of(context).textTheme.headline4,
            ),
          ],
        ),
      ),
      floatingActionButton: FloatingActionButton(
        onPressed: _incrementCounter,
        tooltip: 'Increment',
        child: Icon(Icons.add),
      ),
    );
  }
}

다소 길다. 실질적으로 Widget을 구현하는 클래스여서 그렇다.

보다시피 _MyHomePageState는 State<MyHomePage>를 상속한다. State는 제네릭으로 StatefulWidget을 받고, MyHomePage는 StatefulWidget을 상속하기 때문에 가능하다.

StatefulWidget은 StatelessWidget과 마찬가지로 "Widget build(BuildContext context)" 함수를 오버라이딩하며 원하는 상태를 "setState()" 함수 내에서 변경하면 "Widget build(BuildContext context)"를 통해 Widget을 다시 그리게 된다.

위 코드에선 FloatActionButton을 클릭하면 "_incrementCouner()" 함수가 호출되고 "_incrementCouner()" 함수 내부에서 "setState()" 함수를 호출하여 _counter의 상태를 변경(증가)시킨다.


4. 결과

앱을 실행하면 위와 같은 간단한 화면을 볼 수 있다.
우측하단의 플로팅 버튼을 누르면 숫자가 증가하면서 변하는 것을 볼 수 있다.

Android, iOS 두 디바이스에서 실행해도 동일한 화면과 동일한 기능을 확인할 수 있다.

만약 Flutter가 아니라 Android, iOS 네이티브로 둘을 따로 개발했다면 개발시간은 두 배 혹은 그 이상이 걸렸을 수 있고 화면도 차이가 생겼을 수 있다.

이런 점에선 Flutter가 강점을 지닌것은 맞지만, 완벽한 것은 없다. 단점도 분명히 존재하는 프레임워크 이므로 너무 찬양하지는 말자.

다음 포스팅에선 Flutter에서 활용 가능한 Widget들에 대해 다뤄 볼 것이다.

반응형