note 毎日更新してます

【コピペで簡単開発】Flutterを使って新単語生成アプリをつくる【チュートリアル】

前回の続きです。↓

FlutterでHello Worldをしてみる【初心者】

macOSへの環境構築はこちらから。

こちらの公式ドキュメントに沿って進めていきます。

このチュートリアルで何をつくるか

公式ドキュメントより

上のgif動画のようなアプリを作ります。

英語2単語で構成されたランダムな単語リストで、下にスクロールすれば無限に単語を生成することができます。

学べること

  • iOS、Androidで自然に見えるFlutterアプリの作り方
  • Flutterアプリの基本構成
  • 機能拡張外部パッケージの探し方と使い方
  • ホットリロード機能を使った高速開発の回し方
  • stateful widgetの実装方法
  • 無限スクロールリストの作り方

開発の準備

FlutterでHello Worldをしてみる【初心者】

上記記事、前回のHello Worldまでを用意しましょう。

シミュレータ上でHello Worldが表示されたら準備完了です。

外部パッケージを使う

今回はenglish_wordsというよく使われる数千の英単語が収容されたパッケージを使います。

ちなみにこのようなFlutter用外部パッケージはこちらで探すことができるので、今後利用してみてください。

それではエディタ(VScode)を開いて先ほどのHello Worldアプリに変更を加えていきましょう。

まずエディタ左に表示されているフォルダから、pubspec.yamlを探してそこへenglish_words: ^3.1.0と書き込みます。

19行目dependencies:の下の階層に入れましょう。

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

cmd+Sを押してファイルの保存をすると、自動でパッケージをインストールしてくれます。

次にmain.dartに移り、import文を追加します。

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

この時点ではインポートされたパッケージが使われていないので、注意文が出たりしますが無視で大丈夫です。

この辺りでF5を押して、シミュレータの起動、そしてもう一度F5でデバッグモードでアプリを起動しましょう。今はまだHello Worldが表示されているはずです。

画面に表示されるHello Worldの代わりにenglish_wordsパッケージからの単語を表示するため、コードの書き換えをします。

MyAppクラスを自分のと見比べて更新してみましょう。(クラスごと丸々コピペでも大丈夫です)

class MyApp extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    final wordPair = WordPair.random();
    return MaterialApp(
      title: 'Welcome to Flutter',
      home: Scaffold(
        appBar: AppBar(
          title: Text('Welcome to Flutter'),
        ),
        body: Center(
          child: Text(wordPair.asPascalCase),
        ),
      ),
    );
  }
}

コードをcmd+S で保存すると、ホットリロードが効いて画面の文字が変更されるはずです。

Stateful widgetを追加する

Flutterアプリはウィジェットの積み重ねでできています。

Stateful widgetというのは、状態の変えることができるウィジェットになります。逆にStateless widgetは変えられません。

Stateful widgetをつくるために、最低限必要な2つのクラスを作っていきます。

1つめがStatefulWidget class、2つめがState classです。

下の2つのコードをMyAppクラスの下にコピペしましょう。

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

この中のWidget build()が画面の描画命令を出しています。

次にこのStateful widgetで処理した描画命令を受け取って他の情報と組み合わせるためにMyAppクラスに変更を加えます。

class MyApp extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      title: 'Welcome to Flutter',
      home: Scaffold(
        appBar: AppBar(
          title: Text('Welcome to Flutter'),
        ),
        body: Center(
          child: RandomWords(),
        ),
      ),
    );
  }
}

コードが完成したらアプリをリスタートしましょう。

表示の挙動はさっきのものと一緒ですが、今回はホットリロードをするたびに中の文字が変わるようになっているはずです。

無限スクロールリストをつくる

最後のステップでは、RandomWordsStateクラスを拡張して単語をスクロールで無限に生成できるようにします。

拡張をしたものがこちらです。

class RandomWordsState extends State<RandomWords> {
  final _suggestions = <WordPair>[];
  final _biggerFont = const TextStyle(fontSize: 18.0);
  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(
        title: Text('Startup Name Generator'),
      ),
      body: _buildSuggestions(),
    );
  }

  Widget _buildSuggestions() {
    return ListView.builder(
        padding: const EdgeInsets.all(16.0),
        itemBuilder: /*1*/ (context, i) {
          if (i.isOdd) return Divider(); /*2*/

          final index = i ~/ 2; /*3*/
          if (index >= _suggestions.length) {
            _suggestions.addAll(generateWordPairs().take(10)); /*4*/
          }
          return _buildRow(_suggestions[index]);
        });
  }

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

少しずつ解説をしていきます。

まずこちら

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

上の行で、WordPair型の空配列をつくることによって、生成される単語の収納場所を確保します。

下の行はフォントサイズを変えるための一文です。

ちなみに変数の前の_(アンダーバー)はdartでPrivateの意味を持っており、クラスの外では呼び出せないようになっています。

次はこちら

  Widget _buildSuggestions() {
    return ListView.builder(
        padding: const EdgeInsets.all(16.0),
        itemBuilder: /*1*/ (context, i) {
          if (i.isOdd) return Divider(); /*2*/

          final index = i ~/ 2; /*3*/
          if (index >= _suggestions.length) {
            _suggestions.addAll(generateWordPairs().take(10)); /*4*/
          }
          return _buildRow(_suggestions[index]);
        });
  }

この関数では、ListViewのbuilderを返すことで無限長リストをつくれます。

ListViewのプロパティーitemBuilderを使いそれを再現します。

コード内番号の意味は以下の通りです。

  1. itemBuilderは単語が生成されるたびに呼ばれ、0から始まるiはListTileのたびに1、Dividerのたびに1ずつ足していきます。つまり、1つの単語が作られると、iは2増えることになります。
    iが偶数の時にListTileへ単語を挿入し、奇数の時に仕切りをつくるよう条件分岐しています。
  2. ListViewへ仕切りをつくるための一文です。
  3. i ~/ 2でi÷2(答えは切り捨て)の意味を持ちます。つまりindexは実際の単語の番号を持つことになります。
  4. indexが単語の入った配列の最後まで行ったとき、新しい単語を10個追加しています。
  Widget _buildRow(WordPair pair) {
    return ListTile(
      title: Text(
        pair.asPascalCase,
        style: _biggerFont,
      ),
    );
  }

そしてこの関数によって、表紙する単語のペアを作っています。

この関数は、_buildSuggestions() によって一単語に一度呼ばれます。

最後にMyAppクラスから不要になったパーツを整理します。

以下をMyAppと差し替えてください。

class MyApp extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      title: 'Startup Name Generator',
      home: RandomWords(),
    );
  }
}

アプリをリスタートしたら完成です。


お疲れ様でした。

めちゃくちゃ簡単にそれっぽいのが作れたと思います。

公式ドキュメントにはこんなチュートリアルが他にもたくさんあります。

どんどんこなしていきましょう。

おまけ(コピペ用)

完成版を載せておきます。とりあえず動くものをみたいって人はここから全部コピペしましょう。

import ‘package:flutter/material.dart’;
import ‘package:english_words/english_words.dart’;

void main() => runApp(MyApp());

class MyApp extends StatelessWidget {
@override
Widget build(BuildContext context) {
return MaterialApp(
title: ‘Startup Name Generator’,
home: RandomWords(),
);
}
}

class RandomWordsState extends State {
final _suggestions = [];
final _biggerFont = const TextStyle(fontSize: 18.0);
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: Text(‘Startup Name Generator’),
),
body: _buildSuggestions(),
);
}

Widget _buildSuggestions() {
return ListView.builder(
padding: const EdgeInsets.all(16.0),
itemBuilder: /*1*/ (context, i) {
if (i.isOdd) return Divider(); /*2*/

final index = i ~/ 2; /*3*/
if (index >= _suggestions.length) {
_suggestions.addAll(generateWordPairs().take(10)); /*4*/
}
return _buildRow(_suggestions[index]);
});
}

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

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

コメントを残す

メールアドレスが公開されることはありません。 * が付いている欄は必須項目です