Dart Programmer 되기 [33]

< Flutter 활용하기 – StatelessWidget Example >

지금까지 글들을 이해하고 실행해 왔다면, 이제 직접 스마트폰과 태블릿에서 컨텐츠가 바뀌지 않는 프로그램의 개발은 가능하게 되었습니다. 이번 글에서는 앞서의 내용을 제대로 이해하고 있는지를 재확인하는 차원에서 Flutter 공식 사이트의 실습 과정을 직접 해보는 것으로 대체하고자 합니다.

Building Layouts – Layout in Flutter

Flutter 공식 사이트의 https://flutter.dev/docs/development/ui/layout 에 위치한 Layouts in Flutter는 앞서의 글에서도 인용하여 설명한 내용들이 들어가 있습니다. 이 글의 내용을 하나 하나 읽고 직접 따라해 보기 바랍니다. 이를 통해서, 사용자 인터페이스를 만드는데 사용되는 Widget class들을 다시 재확인하게 됩니다. 그리고 사용자 인터페이스를 채우는 요소들에 해당하는 widget들과 layout의 개념에 대해서 확인하게 됩니다. 아울러 단순한 widget들을 사용하여 복잡한 widget을 만들어 내는 과정을 경험합니다.

Building Layouts – Tutorial

Flutter 공식 사이트의 https://flutter.dev/docs/development/ui/layout/tutorial 에 위치한 Building Layouts는 StatelessWidget을 사용하여 간단한 앱을 만드는 과정입니다. 이 글의 내용을 하나 하나 읽고 직접 따라해 보기 바랍니다. 이를 통해서, Flutter의 layout에 대한 이해가 좀 더 고도화 되며, row/column 기반으로 widget들을 배치하는 부분을 이해하게 됩니다.

Write your own StatelessWidget application

앞에서 실습한 내용을 토대로 본인만의 StatelessWidget application을 만들어 봅니다. 저의 경우는 경희대학교를 소개하는 고정 컨텐츠 페이지를 아래의 그림과 같이 만들어 보았습니다. 앞서의 예제에서 일부 수정하는 방식으로 작성하였습니다.

[그림] darttutorial-33-01.dart 실행 화면

이미지를 바꾸고, 내용을 일부 바꾸는 minor한 수정을 하였으며, 이에 대한 소스 코드인 darttutorial-33-01.dart는 아래와 같습니다.

// darttutorial-33-01.dart
// Based on : https://flutter.dev/docs/development/ui/layout

import 'package:flutter/material.dart';

void main() {
  var parameter = {
    'appBarTitle': 'Flutter Layout Demo Program',
    'titleImage': 'images/lake.jpg',
    'titleSectionHeader': '경희대학교 국제캠퍼스',
    'titleSectionBody': '경기도 용인시 기흥구 덕영대로 1732번지',
    'titleSectionScore': 41,
    'textSection':
        '국제캠퍼스는 공학, 응용과학, 생명과학, 전자정보, 소프트웨어, 실용예술, 국제학, '
        '외국어, 체육 중심의 캠퍼스로, 특성 있는 학문분야가 많습니다. '
        '주요 시설로는 수용 인원 8천여명 규모의 노천극장 및 종합체육관, 천문대, 원자로실 등이 있습니다. '
        '경희대는 국제캠퍼스를 포함하여 서울캠퍼스, 광름캠퍼스를 보유하고 있습니다. '
        '교명인 경희대(慶熙大)는 영정조 시대의 치세가 펼쳐진 조선시대의 정궁 경희궁(慶熙宮)에서 따온 것으로, '
        '임진왜란과 병자호란의 폐허를 딛고 문예를 부흥시킨 조선 후기 영정조 시대처럼, '
        '한국 전쟁으로 피폐해진 이 땅에 다시 문화적인 르네상스가 오기를 바라는 마음으로 '
        '경희학원(고황재단)의 설립자인 조영식에 의해 명명되었습니다.',
  };

  runApp(MyApp(parameter));
}

class MyApp extends StatelessWidget {
  // Constructor with a Map type input parameter.
  MyApp(this.internalStorage);

  // Internal storage to save a page content which initialized by constructor.
  final Map internalStorage;

  // build method.
  @override
  Widget build(BuildContext context) {
    Widget titleImage = _buildTitleImage(internalStorage['titleImage']);

    Widget titleSection = _buildTitleSection(
        internalStorage['titleSectionHeader'],
        internalStorage['titleSectionBody'],
        internalStorage['titleSectionScore']);

    Widget buttonSection = _buildButtonSection(Theme.of(context).primaryColor);

    String textSectionMessage = internalStorage['textSection'];

    Widget textSection = _buildTextSection(textSectionMessage);

    return MaterialApp(
      title: internalStorage['appBarTitle'],
      home: Scaffold(
        appBar: AppBar(
          title: Text(internalStorage['appBarTitle']),
        ),
        body: ListView(
          children: [
            titleImage,
            titleSection,
            buttonSection,
            textSection,
          ],
        ),
      ),
    );
  }

  // Build function to create title image Widget
  dynamic _buildTitleImage(String imageName) {
    return Image.asset(
      imageName,
      width: 600,
      height: 240,
      fit: BoxFit.cover,
    );
  }

  // Build function to create title section Widget
  Container _buildTitleSection(String name, String addr, int count) {
    return Container(
      padding: const EdgeInsets.all(32),
      child: Row(
        children: [
          Expanded(
            child: Column(
              crossAxisAlignment: CrossAxisAlignment.start,
              children: [
                Container(
                  padding: const EdgeInsets.only(bottom: 8),
                  child: Text(
                    name,
                    style: TextStyle(
                      fontWeight: FontWeight.bold,
                    ),
                  ),
                ),
                Text(
                  addr,
                  style: TextStyle(
                    color: Colors.grey[500],
                  ),
                ),
              ],
            ),
          ),
          Icon(
            Icons.star,
            color: Colors.red[500],
          ),
          Text('$count'),
        ],
      ),
    );
  }

  // Build function to create button section Widget
  Container _buildButtonSection(Color color) {
    return Container(
      child: Row(
        mainAxisAlignment: MainAxisAlignment.spaceEvenly,
        children: [
          _buildButtonColumn(color, Icons.call, '전화'),
          _buildButtonColumn(color, Icons.near_me, '경로'),
          _buildButtonColumn(color, Icons.share, '공유'),
        ],
      ),
    );
  }

  // Build function to create button with icon and sub-title for _buildButtonSection()
  Column _buildButtonColumn(Color color, IconData icon, String label) {
    return Column(
      mainAxisSize: MainAxisSize.min,
      mainAxisAlignment: MainAxisAlignment.center,
      children: [
        Icon(icon, color: color),
        Container(
          margin: const EdgeInsets.only(top: 8),
          child: Text(
            label,
            style: TextStyle(
              fontSize: 13,
              fontWeight: FontWeight.w400,
              color: color,
            ),
          ),
        ),
      ],
    );
  }

  // Build function to create text description section Widget
  Container _buildTextSection(String section) {
    return Container(
      padding: const EdgeInsets.all(32),
      child: Text(
        section,
        softWrap: true,
      ),
    );
  }
}

사용한 이미지는 아래와 같으며, 경희대학교에서 이미지에 대한 라이센스를 가지고 있으니 참조하기 바랍니다.

[그림] darttutorial-33-01.dart에서 사용하는 lake.jpg 화일

앞서의 실습을 토대로 직접 위와 같이 만들고자 한다면, 문제가 없겠지만, darttutorial-33-01.dart를 copy & paste하여 실행하고자 한다면, Flutter 공식 사이트에 대한 실습에서 설명하는 부분중 다음을 이해해야 합니다. 즉, 프로젝트 폴더의 root 디렉토리에 images 서브 디렉토리를 만든후 (위와 같이 본인이 사용할) lake.jpg 이미지 화일을 이 디렉토리에 복사합니다. 그리고 pubspec.yaml 화일의 flutter: 하단에 assets: 를 활성화 한후, 그 밑에 – images/lake.jpg를 추가합니다. Flutter가 자동 생성한, Start App의 pubspec.yaml 화일 안에 보면, 이렇게 이미지를 추가하는 경우에 대한 설명이 주석으로 되어 있으니 한번 읽어 보기 바랍니다. 만약 프로그램을 실행하는 중에, main.dart의 수정과 이미지 화일의 추가를 하였다면, MS Visual Code의 디버그 단추 중 “다시 고침” 단추를 클릭해야 합니다.

마무리

StatelessWidget은 고정적인 컨텐츠를 표현하기에 사용하므로 정적인 동작을 합니다. 따라서, 동적인 동작을 하는 StatefulWidget을 이해하기 위한 필수적인 단계로 볼 수 있습니다. 특히 다음 글에서는 darttutorial-33-01.dart 프로그램이 저이상 정적이지 않고, 사용자와의 인터액션을 통해서 동적으로 반응하게 만드는 과정에 대해서 설명합니다. 따라서, 최소한 darttutorial-33-01.dart 프로그램을 이해하고 실행해 보도록 합니다.

Creative Commons License (CC BY-NC-ND)

댓글 남기기

이메일은 공개되지 않습니다. 필수 입력창은 * 로 표시되어 있습니다