꿈꾸는 시스템 디자이너

Flutter 강좌2 - 소스코드(위젯) 분할 본문

Development/Flutter

Flutter 강좌2 - 소스코드(위젯) 분할

독행소년 2019. 7. 22. 23:41

Flutter 강좌 시즌2 목록 : https://here4you.tistory.com/149

 

 

지난 강좌에서는 Hello World를 출력하기 위한 최소한의 소스코드를 작성하고 그 결과를 확인해봤다. 소스코드를 다시 한번 확인해보자.

import 'package:flutter/material.dart';
 
void main() {
  // 터미널에 Hello World를 출력
  print('Hello World');
 
  runApp(MyApp());
}
 
class MyApp extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      title: 'Hello World App', // 앱에 대한 설명
      home: Scaffold(
        body: Text('Hello World'), // 텍스트를 출력하는 위젯
      ),
    );
  }
}

소스코드의 구조를 보면 함수로는 메인함수가 1개 존재하고 클래스로는 StatelessWidget을 상속하는 클래스가 1개 존재한다.

앱이 실행되면 제일 먼저 메인함수가 호출되고 메인함수내에서 runApp 함수를 통해 MyApp 클래스가 실행되는 구조다.

MyApp이 상속하는 StatelessWidget 클래스는 말 그대로 상태를 가지지 않는 위젯을 구성하는 클래스다. 상태가 없다는 말은 화면상에 한번 뿌려지면 해당 화면이 전환되기 전에는 변화가 없다는 것을 의미한다. 그래서 StatelessWidget은 상태변화가 없는 정적인 위젯을 구현할 때 사용한다.

일반적으로 위젯이란 화면을 구성할 때 사용하는 여러가지 도구들을 의미한다. Flutter에서도 비슷한데 실제로는 대부분이 위젯이라고 해도 과언이 아니다. 앞서 살펴봤던 MeterialApp도 위젯의 일종이고, Scaffold도 위젯의 일종, AppBar, Text 모두 위젯들이다. 다시말해 가장 작은 단위라고 생각되는 Text도 위젯이지만 위젯을 담는 화면인 Scaffold도 위젯이고, 화면을 담는 MaterialApp도 위젯이다. 

StatelessWidget을 이용해서 화면을 구성할 때에는 build 메소드를 사용한다. StatelessWidget은 반드시 build 메소드를 구현해야하며, 해당 클래스가 생성되면 자동적으로 build 메소드가 호출되어 위젯들을 구성하여 반환되는 방식으로 화면을 구성할 수 있다. 위의 코드에서는 build 메소드에서 MaterialApp 위젯을 구성해서 리턴하고 있는데 MaterialApp 생성자를 통해 하위 계층인 Scaffold와 그 보다 더 하위 계층인 Text 위젯까지 구현하고 있다. 하나의 StatelessWidget 클래스에서 화면에 출력될 모든 위젯들을 구성할 수 있으나 화면의 구성이 복잡해질 수록 클래스의 구조도 복잡해질 것이다. 이럴 경우에는 다음과 같이 계층을 분할하여 구현할 수 있다.

다음의 소스코드를 살펴보자. 화면 구성은 위의 소스코드와 동일하지만 MaterialApp의 home 항목을 직접 구현하지 않고 별도의 클래스로 분할하여 구현한 것이다.

import 'package:flutter/material.dart';

void main() {
  // 터미널에 Hello World를 출력
  print('Hello World');
  runApp(new MyApp());
}

class MyApp extends StatelessWidget {
  final String title = "Hello World App";
  final String helloWorldText = "Hello World2";

  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      title: 'Hello World App', // 앱에 대한 설명
      home: MyHomePage(
        title: title,
        helloWorldText: helloWorldText,
      ),
    );
  }
}

class MyHomePage extends StatelessWidget {
  final String title;
  final String helloWorldText;

  MyHomePage({Key key, this.title, this.helloWorldText}) : super(key: key);

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

MaterialApp의 home 항목에는 보통 화면을 구성하는 Scaffold 위젯이 추가된다. 위 소스코드에서는 Scaffold를 리턴하는 별도의 클래스인 MyHomePage 클래스를 구현하여 그 생성자를 호출하는 방식으로 코드를 분할하고 있다.

자 우선 MyHomePage도 StatelessWidget을 상속하고 있고, 위젯을 구성하기 위해 build 메소드를 재정의하고 있으며 build 메소드에서 Scaffold를 생성해서 리턴한다.

순서대로 추적해 보면, MaterialApp의 home 항목에서 MyHomePage 클래스의 생성자를 호출하면서 title과 helloWorldText를 파라미터로 전달한다. MyHomePage 생성자에서는 이 값을 아규먼트로 받아서 각각의 해당 필드에 저장한다.

MyHomePage 클래스도 StatelessWidget을 상속하므로 자동으로 build 메소드가 호출되게 되고, build 메소드에서 Scaffold를 생성하여 리턴하는데, Scaffold 생성자를 보면 AppBar와 body를 구성할 때 생성자로 전달받은 아규먼트를 이용하고 있다.

생성자의 첫번째 아규먼트인 Key는 아직은 위젯의 식별자라고만 이해하자.

위의 소스코드는 하나의 클래스로 구현되었던 앱을 MaterialApp 위젯을 가지는 클래스와 Scaffold와 그 하위 위젯들로 구성되는 클래스로 분리된 것이다.

Scaffold의 구성을 다음과 같이 한번 더 분할할 수 있다.

import 'package:flutter/material.dart';

void main() {
  // 터미널에 Hello World를 출력
  print('Hello World');
  runApp(new MyApp());
}

class MyApp extends StatelessWidget {
  final String title = "Hello World App";
  final String helloWorldText = "Hello World";

  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      title: 'Hello World App', // 앱에 대한 설명
      home: MyHomePage(
        title: title,
        helloWorldText: helloWorldText,
      ),
    );
  }
}

class MyHomePage extends StatelessWidget {
  final String title;
  final String helloWorldText;

  MyHomePage({Key key, this.title, this.helloWorldText})
      : super(key: key);

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(title: Text(title)),
      body: MyBody(helloWorldText: helloWorldText),
    );
  }
}

class MyBody extends StatelessWidget {
  final String helloWorldText;

  const MyBody({Key key, this.helloWorldText}) : super(key: key);

  @override
  Widget build(BuildContext context) {
    return Text(helloWorldText);
  }
}

위 코드는 Scaffold의 body 항목을 직접 구현하지 않고, MyBody라는 별도의 StatelessWidget 기반의 클래스로 구현하고 그 생성자를 통해 Text 위젯을 추가하고 있다.

 

자 그러면 꼭 클래스로만 분할할 수 있는 것일까? 아니다 메소드로 분할할 수 있다. 다음의 코드를 보자

import 'package:flutter/material.dart';

void main() {
  // 터미널에 Hello World를 출력
  print('Hello World');
  runApp(new MyApp());
}

class MyApp extends StatelessWidget {
  final String title = "Hello World App";
  final String helloWorldText = "Hello World";

  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      title: 'Hello World App', // 앱에 대한 설명
      home: MyHomePage(
        title: title,
        helloWorldText: helloWorldText,
      ),
    );
  }
}

class MyHomePage extends StatelessWidget {
  final String title;
  final String helloWorldText;

  MyHomePage({Key key, this.title, this.helloWorldText})
      : super(key: key);

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(title: Text(title)),
      //body: MyBody(helloWorldText: helloWorldText),
      body: buildBody(),
    );
  }

  // 별도의 클래스가 아닌 메소드로 분리할 수도 있음
  Widget buildBody(){
    return Text(helloWorldText);
  }
}

class MyBody extends StatelessWidget {
  final String helloWorldText;

  const MyBody({Key key, this.helloWorldText}) : super(key: key);

  @override
  Widget build(BuildContext context) {
    return Text(helloWorldText);
  }
}

Scaffold의 body 항목에서 Text 위젯을 추가하기 위해 MyBody 클래스의 생성자를 이용하던 것을 buildBody 메소드를 호출하는 방식으로 변경한 것이며 그 결과는 동일하다.

그럼 두 방식에는 무슨 차이가 있을까?

메소드 방식의 경우 메소드가 직접 호출되고, 위젯을 직접 반환하는 심플한 구조를 가진다. 하지만 클래스 방식은 클래스 생성자를 호출하는 방식이고 생성자는 반환값이 없는 메소드다 그러므로 반드시 build 메소드를 재정의해서 위젯이 리턴될 수 있도록 구현해야 한다.

 


Flutter Code Examples 강좌를 추천합니다.

  • 제 블로그에서 Flutter Code Examples 프로젝트를 시작합니다.
  • Flutter의 다양한 예제를 소스코드와 실행화면으로 제공합니다.
  • 또한 모든 예제는 Flutter Code Examples 앱을 통해 테스트 가능합니다.

Flutter Code Examples 강좌로 메뉴로 이동

Flutter Code Examples 강좌 목록 페이지로 이동

Flutter Code Examples 앱 설치 | Google Play Store로 이동

 

Flutter Code Examples - Google Play 앱

Are you a beginner at Flutter? Check out the various features of Flutter through the demo. Source code for all demos is also provided.

play.google.com

Comments