반응형


스타트업 회사의 이름을 랜덤으로 생성하여 제안해주는 첫번째 Flutter 앱을 작성해봅니다. 


먼저 랜덤으로 선택한 두 개의 단어를 결합하여 만든 스타트업 회사 이름을 보여주는 앱을 작성하고 

이후 생성된 스타트업 회사 이름들을 리스트뷰에 보여주도록 개선해봅니다. 


참고

https://codelabs.developers.google.com/codelabs/first-flutter-app-pt1/#0 




본 포스트에서는 비주얼 스튜디오 코드를 사용하여 Flutter 프로젝트를 생성하여 진행합니다. 

안드로이드 스튜디오에서 Flutter 프로젝트 생성하는 방법은 다음 포스트를 참고하세요.


Flutter 강좌 01 - 개발환경 만들기 및 앱 실행하는 방법

https://webnautes.tistory.com/1409




1. 비주얼 스튜디오 코드에서 Ctrl + Shift + P를 누른 후, 창에 flutter를 입력하여 검색되는 항목에서 Flutter: New Project를 선택합니다.





2. 프로젝트 이름으로 startup_namer를 입력하고 엔터를 누릅니다. 





3. 원하는 위치에 startup_namer 이름의 폴더를 생성하고 해당 폴더를 클릭하여 이동한 후, Select a folder to create the project in 버튼을 클릭합니다. 

Flutter 프로젝트가 생성됩니다. main.dart 파일이 보일때까지 기다렸다가 다음 단계를 진행해야 합니다.  





4. main.dart의 기존 코드를 모두 지우고 다음 코드로 대체합니다. 

화면 중앙에 ‘Hello World’를 보여주는 코드입니다. 


// Scaffold 위젯을 사용하기 위해 머티리얼(material) 라이브러리를 사용합니다.
import 'package:flutter/material.dart';


// runApp 메소드를 사용하여 앱이 실행되면 화면에 보여질 위젯을 지정해줍니다.
void main() => runApp(MyApp());

// StatelessWidget 클래스를 상속받아 위젯으로 앱을 생성합니다.
class MyApp extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      title: 'Welcome to Flutter',

      // Scaffold 위젯의 appBar, title, body 속성을 지정해줍니다.
      home: Scaffold(
        appBar: AppBar(
          title: Text('Welcome to Flutter'),  // 앱의 타이틀바에 보여질 문자열을 지정합니다.
        ),
        body: Center(  // Center 위젯을 사용하면 자식 위젯이 화면 중앙에 정렬됩니다.
          child: Text('Hello World'), // 자식 위젯으로 Text 위젯을 사용하여 Hello World 문자열을 화면에 출력합니다.
        ),
      ),
    );
  }
}




5. 비주얼 스튜디오 코드의 경우 Ctrl + S를 눌러 파일을 저장한 후,  F5를 눌러서 앱을 설치하고 실행합니다. 






6. 이제 무작위로 두 개의 단어를 선택하여 화면 중앙에 보여주도록 코드를 수정해봅니다. 




7. 우선 무작위로 두 개의 단어를 생성하기 위해 필요한 패키지를 추가합니다. 

english_words 패키지는 영어 단어 5천개와 단어 생성을 위한 함수를 제공합니다.



프로젝트 창에서 pubspec.yaml 파일을 선택합니다. 




다음 부분에  english_words 패키지를 사용하겠다고 추가해줍니다. 


dependencies:
  flutter:
    sdk: flutter

  # The following adds the Cupertino Icons font to your application.
  # Use with the CupertinoIcons class for iOS style icons.
  cupertino_icons: ^0.1.2
  english_words: ^3.1.0




수정 후, Ctrl + S를 눌러 pubspec.yaml 파일 저장하면 다음처럼 해당 패키지를 자동으로 가져옵니다. 




pubspec.lock 파일에 가져온 패키지 이름과 버전이 추가됩니다. 


  english_words:
    dependency: "direct main"
    description:
      name: english_words
      url: "https://pub.dartlang.org"
    source: hosted
    version: "3.1.5"




8. main.dart 파일에 english_words 패키지를 사용하겠다고 추가해줍니다. 


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




9. 무작위로 두 개의 영어 단어를 선택하여 화면에 보여주도록 수정합니다. 


// StatelessWidget 클래스를 상속받아 위젯으로 앱을 생성합니다.
class MyApp extends StatelessWidget {
  @override
  Widget build(BuildContext context) {

    // 무작위로 선택된 2개의 단어로 구성된 문자열을 만듭니다.
    final wordPair = WordPair.random();

    return MaterialApp(
      title: 'Welcome to Flutter',

      // Scaffold 위젯의 appBar, title, body 속성을 지정해줍니다.
      home: Scaffold(
        appBar: AppBar(
          title: Text('Welcome to Flutter'),  // 앱의 타이틀바에 보여질 문자열을 지정합니다.
        ),
        body: Center(  // Center 위젯을 사용하면 자식 위젯이 화면 중앙에 정렬됩니다.
          //child: Text('Hello World'), // 자식 위젯으로 Text 위젯을 사용하여 Hello World 문자열을 화면에 출력합니다.

          // 단어의 첫글자만 대문자로하여 두 개의 단어를 결합한 문자열을 리턴하여 Text 위젯에 보이도록 합니다. 
          child: Text(wordPair.asPascalCase),
        ),
      ),
    );
  }
}




10. 앱이 실행되는 상태를 유지하고 있었다면 Ctrl + S를 눌러 저장시 화면에 무작위로 선택된 2개의 단어로 구성된 문자열이 보입니다. 앱을 종료했었다면 F5를 눌러서 실행하세요. 


Ctrl + S를 눌러 다시 저장할때마다 앱에 보여지는 문자열이 바뀌는 것을 볼 수 있습니다. 





11. 현재 상속받아 사용중인 Stateless 위젯(StatelessWidget)은 앱이 실행되는 중에 위젯의 속성을 변경할 수 없습니다. 

Stateful 위젯을 사용하도록 변경하여 앱이 실행되는 중에 위젯의 속성을 변경할 수 있도록 수정합니다. 



Stateful 위젯은 2개의 클래스를 사용하여 구현해야 합니다. 

State 클래스의 인스턴스로 StatefulWidget을 생성합니다. 

StatefulWidget 인스턴스 자체는 변경할 수 없으며 변경 가능한 상태를 별도의 State 객체에 저장합니다



State 클래스로 RandomWordsState 클래스를 사용하는 Stateful 위젯,  RandomWords을 추가합니다. 

추후 Stateless 위젯인 MyApp의 자식으로 RandomWords를 사용하게 됩니다.

다음처럼 main.dart 파일 끝에 State 클래스를 생성하는 코드를 추가합니다.


class RandomWordsState extends State<RandomWords> {
  // TODO Add build method
}




State<RandomWords> 선언은 제네릭으로 State 클래스의 타입을  RandomWords으로 지정하는 것입니다. 

RandomWords 위젯의 상태를 관리하는 앱의 로직과 상태는 RandomWordsState  클래스에 존재하게 됩니다. 


사용자가 리스트뷰를 스크롤할때마다 무작위로 선택된 단어쌍을 이용하여 생성되는 문자열의 리스트를 RandomWordsState  클래스에 저장할 수 있습니다. 

사용자가 리스트뷰에 있는 하트 아이콘을 클릭하여 선호하는 단어를 추가하거나 삭제하는 기능은 다음 포스트에서 다룹니다.



RandomWordsState 클래스는 RandomWords  클래스에 의존적입니다. 

main.dart 파일 끝에 Stateful 위젯인 RandomWords 위젯을 추가합니다. 

RandomWords 위젯은 State 클래스를 생성하는 것외에는 다른 일을  거의 하지 않습니다. 


class RandomWords extends StatefulWidget {
  @override
  RandomWordsState createState() => RandomWordsState();
}

 



RandomWords  클래스를 추가한 후, IDE는 RandomWordsState 클래스에 build 메소드가 없다고 알려줍니다. 





MyApp 클래스에 있는 두 개의 단어를 무작위로 선택하여 만든 문자열을  Text 위젯에 보여주는 코드를 RandomWordsState 클래스의 build 메소드로 옮겨줘야 합니다. 


RandomWordsState에 build 메소드를 생성한 후, 다음처럼 코드를 가져옵니다. 


class RandomWordsState extends State<RandomWords> {
  @override                                
  Widget build(BuildContext context) {
    final WordPair wordPair = WordPair.random();
    return Text(wordPair.asPascalCase);
  }                                        
}




MyApp 클래스에서 필요 없어진 코드를 주석처리하고 RandomWords 클래스를 사용하도록 수정합니다. 


// StatelessWidget 클래스를 상속받아 위젯으로 앱을 생성합니다.
class MyApp extends StatelessWidget {
  @override
  Widget build(BuildContext context) {

    //final wordPair = WordPair.random(); //주석처리

   return MaterialApp(
      title: 'Welcome to Flutter',

      // Scaffold 위젯의 appBar, title, body 속성을 지정해줍니다.
      home: Scaffold(
        appBar: AppBar(
          title: Text('Welcome to Flutter'),  // 앱의 타이틀바에 보여질 문자열을 지정합니다.
        ),
        body: Center(  // Center 위젯을 사용하면 자식 위젯이 화면 중앙에 정렬됩니다.

          //child: Text(wordPair.asPascalCase), // 주석처리
          child: RandomWords(), // 추가
        ),
      ),
    );
  }
}




앱을 실행시켜보면 화면에 보여지는 것은 이전과 별차이가 없습니다. 




12. 무한 스크롤할 수 있는 리스트뷰(ListView) 위젯에 추천 스타트업 이름이 보여지도록 수정해봅니다. 


무작위로 선택된 두 개의 단어를 결합한 문자열을 생성하고 화면에 보여주는 역할을 하는 RandomWordsState 클래스를 수정합니다. 



The builder factory constructor in ListView allows you to lazily build a list view on demand.


RandomWordsState 클래스에 다음 2줄을 추가합니다. 


_suggestions 리스트에 무작위로 생성된 단어쌍을 저장합니다. 

_biggerFont 변수를 사용하여 폰트 크기를 지정합니다. 


* Dart 언어에서는 이름 앞에 _를 붙이면 private으로 취급됩니다. 


class RandomWordsState extends State<RandomWords> {

  final List<WordPair> _suggestions = <WordPair>[];
  final TextStyle _biggerFont = const TextStyle(fontSize: 18);

  @override                                
  Widget build(BuildContext context) {
    final WordPair wordPair = WordPair.random();
    return Text(wordPair.asPascalCase);
  }    
}




RandomWordsState  클래스에 _buildSuggestions() 메소드를 추가합니다. 

이 메소드는 사용자의 스크롤에 따라 추가로 보여줘야 하는 추천된 단어쌍을 생성하여 리스트뷰에 보여주기 위해 사용됩니다. 


ListView 클래스는 

The ListView class provides a builder property, itemBuilder, that's a factory builder and callback function specified as an anonymous function. 


함수에 두 개의 파라미터가 전달됩니다. BuildContext와 행 이터레이터(row iterator)  i


itemBuilder: (BuildContext _context, int i)



이터레이터 i는 0부터 시작하며 함수가 호출될때마다 리스트뷰에 추가될 단어쌍을 위해 1씩 증가합니다. 

사용자가 리스트뷰를 무한히 스크롤하더라도 추천 단어쌍을 계속 보여줄 수 있습니다.


RandomWordsState 클래스에 _buildSuggestions 함수를 추가합니다. 


class RandomWordsState extends State<RandomWords> {

  final List<WordPair> _suggestions = <WordPair>[];
  final TextStyle _biggerFont = const TextStyle(fontSize: 18);

  @override                                
  Widget build(BuildContext context) {
    final WordPair wordPair = WordPair.random();
    return Text(wordPair.asPascalCase);
  }

  Widget _buildSuggestions() {
    return ListView.builder(
      padding: const EdgeInsets.all(16),
      // The itemBuilder callback is called once per suggested
      // word pairing, and places each suggestion into a ListTile
      // row. For even rows, the function adds a ListTile row for
      // the word pairing. For odd rows, the function adds a
      // Divider widget to visually separate the entries. Note that
      // the divider may be difficult to see on smaller devices.
      itemBuilder: (BuildContext _context, int i) {
        // Add a one-pixel-high divider widget before each row
        // in the ListView.
        if (i.isOdd) {
          return Divider();
        }

        // The syntax "i ~/ 2" divides i by 2 and returns an
        // integer result.
        // For example: 1, 2, 3, 4, 5 becomes 0, 1, 1, 2, 2.
        // This calculates the actual number of word pairings
        // in the ListView,minus the divider widgets.
        final int index = i ~/ 2;
        // If you've reached the end of the available word
        // pairings...
        if (index >= _suggestions.length) {
          // ...then generate 10 more and add them to the
          // suggestions list.
          _suggestions.addAll(generateWordPairs().take(10));
        }
        return _buildRow(_suggestions[index]);
      }
    );
  }
}




_buildSuggestions 함수는 단어쌍이 추가될때마다 _buildRow를 한번씩 호출합니다. 

_buildRow 함수는 리스트뷰의 ListTile에 단어쌍을 보여주는 역활을 합니다. 즉 리스트뷰의 줄마다 하나의 단어쌍이 보이도록 합니다.


RandomWordsState 클래스에 _buildRow  함수를 추가합니다. 


class RandomWordsState extends State<RandomWords> {

  final List<WordPair> _suggestions = <WordPair>[];
  final TextStyle _biggerFont = const TextStyle(fontSize: 18);

  @override                                
  Widget build(BuildContext context) {
    final WordPair wordPair = WordPair.random();
    return Text(wordPair.asPascalCase);
  }

  Widget _buildSuggestions() {
    return ListView.builder(
      padding: const EdgeInsets.all(16),
      // The itemBuilder callback is called once per suggested
      // word pairing, and places each suggestion into a ListTile
      // row. For even rows, the function adds a ListTile row for
      // the word pairing. For odd rows, the function adds a
      // Divider widget to visually separate the entries. Note that
      // the divider may be difficult to see on smaller devices.
      itemBuilder: (BuildContext _context, int i) {
        // Add a one-pixel-high divider widget before each row
        // in the ListView.
        if (i.isOdd) {
          return Divider();
        }

        // The syntax "i ~/ 2" divides i by 2 and returns an
        // integer result.
        // For example: 1, 2, 3, 4, 5 becomes 0, 1, 1, 2, 2.
        // This calculates the actual number of word pairings
        // in the ListView,minus the divider widgets.
        final int index = i ~/ 2;
        // If you've reached the end of the available word
        // pairings...
        if (index >= _suggestions.length) {
          // ...then generate 10 more and add them to the
          // suggestions list.
          _suggestions.addAll(generateWordPairs().take(10));
        }
        return _buildRow(_suggestions[index]);
      }
    );
  }

  Widget _buildRow(WordPair pair) {
    return ListTile(
      title: Text(
        pair.asPascalCase,
        style: _biggerFont,
      ),
    );
  }
}




RandomWordsState 클래스의 build 메소드에서 _buildSuggestions() 함수를 사용하도록 수정합니다. 


class RandomWordsState extends State<RandomWords> {

  final List<WordPair> _suggestions = <WordPair>[];
  final TextStyle _biggerFont = const TextStyle(fontSize: 18);

  @override                                
  Widget build(BuildContext context) {
    //final WordPair wordPair = WordPair.random();  // 주석처리
    //return Text(wordPair.asPascalCase);           // 주석처리

    return Scaffold (                  
      appBar: AppBar(
        title: Text('Startup Name Generator'),
      ),
      body: _buildSuggestions(),
    );      
  }




앱의 타이틀바에 보일 내용과 RandomWords 위젯을 사용하도록 MyApp 클래스의 build 메소드를 수정합니다. 


class MyApp extends StatelessWidget {

  @override
  Widget build(BuildContext context) {

    return MaterialApp(
      title: 'Startup Name Generator',
      home: RandomWords(),

    );
  }
}





실행시켜보면 리스트뷰에 추천 스타트업 회사 이름이 보여집니다.

리스트뷰를 스크롤함에 따라 계속 새로운 단어들이 보여집니다.





최초작성. 2020. 4. 15





관련 포스트


Flutter 강좌 01 - 개발환경 만들기 및 앱 실행하는 방법

https://webnautes.tistory.com/1409





반응형

포스트 작성시에는 문제 없었지만 이후 문제가 생길 수 있습니다.
질문을 남겨주면 가능한 빨리 답변드립니다.

여러분의 응원으로 좋은 컨텐츠가 만들어집니다.
지금 본 내용이 도움이 되었다면 유튜브 구독 부탁드립니다. 감사합니다 ~~

유튜브 구독하기


제가 쓴 책도 한번 검토해보세요 ^^

+ Recent posts