環境

  • 作業日: 2020/03/07
  • OS: macOS Big Sur 11.2.1
  • PC: MacBook Pro (13-inch, M1, 2020)

初めに

Flutterの勉強をするには公式のチュートリアルをやるのが一番だという情報を得たのでやっていき!

このチュートリアルでは、リストの無限スクロールをやりました。

Step1 アプリを作る

Flutterのプロジェクトを新規に作ります。

lib/main.dartの中身を一度全て削除し、以下のように画面中央に"Hello World"と表示するように書き換えます。

‘helloworld’

import 'package:flutter/material.dart';

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

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

コードをコピペした際に、インデントがズレた時の対処法がありましたが、 VSCodeのプラグインが入っていればコードを保存する(⌘ + S)をするたびにフォーマットが走っていました。

android studio VS Code Terminal
コードを選択し右クリックして\nReformat Code with dartfmt コードを選択し右クリックして Format Document flutter format <filename>

Step1まとめ

  • マテリアルデザインという標準的なデザインを採用
  • main() メソッドは矢印 (=>) 記法を使用し簡潔に描けるようになっている
  • アプリは StatelessWidget を拡張し、アプリ自体がウィジェット
  • Flutter では、整列、パディング、レイアウトなど、ほとんどすべてがウィジェットになっている

Step2 外部パッケージを利用する

このステップでは外部パッケージを利用してアプリを作っていくっぽいです。

今回利用するのは、english_wordsという最も多く使われている5000個の英単語といくつかのユーティリティ関数を含むパッケージ

他の多くのオープンソースパッケージも同様にpub.devにあるので、何かパッケージを探したい時はここをチェキすれば良さそう!

パッケージを利用するのにpabspec.yamlというファイルをいじる必要があり、

pubspec.yamlはFlutterアプリのアセットと依存関係を管理してくれます。

以下のようにdependenciesに使いたいパッケージを記入します。

name: flutter_tutorial_part01
description: A new Flutter project.
publish_to: 'none' 
version: 1.0.0+1

environment:
  sdk: ">=2.7.0 <3.0.0"

dependencies:
  flutter:
    sdk: flutter
  cupertino_icons: ^1.0.2
  english_words: ^3.1.5

dev_dependencies:
  flutter_test:
    sdk: flutter
  integration_test:
    sdk: flutter

flutter:
  uses-material-design: true

english_words: ^3.1.5これはenglish_wordsのバージョン3.1.5以上を利用すること示しています。

記入したら、以下のコマンドを打つか、VSCodeならプラグインが入っていればyamlファイルを保存した時点でpub getが走るのでパッケージがプロジェクトに取り込まれます。

flutter pub get

pub get を実行すると、プロジェクトに取り込まれたすべてのパッケージとそのバージョン番号のリストを含む pubspec.lockファイルも自動生成されます。

パッケージを利用していくには、main.dartの先頭に以下を追記します。

import 'package:english_words/english_words.dart';

そうすると、今はまだ使っていないので当たり前なのですが、利用されていませんという表示になっています。

先ほど記述したHello Worldの代わりに、english_wordsを使ってランダムな英単語を表示させていきます。

english_words

class MyApp extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    final wordPair = WordPair.random();

    return MaterialApp(
      title: 'Welcom to Flutter',
      home: Scaffold(
        appBar: AppBar(
          title: Text('Welcome to Flutter'),
        ),
        body: Center(
          child: Text(wordPair.asPascalCase),
        ),
      ),
    );
  }
}

final wordPair = WordPair.random();でランダムな単語のペアを生成し、その単語のペアをwordPair.asPascalCaseでパスカルケース(キャメルケース)で表示しています。

ホットリロードが走るたびに、ランダムな文字列が表示されることが確認できると思います。

Step3 Stateful widgetの追加

今まで使っていたのはステートレスウィジェットと呼ばれるもので、プロパティは変更できず、不変のものとのこと!

ステートフルウィジェットはウィジェットが生きている間に変更される可能性がある状態を維持し続ける(?)

Stateful Widgetの実装に必要な2つのもの

  1. Stateful Widgetクラス
  • ステートフルウィジェットクラスはそれ自体が不変
  • 捨てたり再生成したりすることができる
  1. Stateful Widgetクラスを生成するStateクラス
  • ステートクラスはウィジェットの有効期間中は持続

このステップでは、RandomWordsを作り、そのステートクラスである_RandomWordsStateを作成、それを既存のMyApp(ステートレスウィジェット)の子としてWordsRandomを利用します!

main.dartの最後尾にカーソルを移動し、

stful

と入力すると、以下のようにStateful Widgetを作るために必要なボイラープレートコードを自動生成してくれます!(便利!)

stful

このカーソルの状態のまま、ウィジェット名RandomWordsを入力します。

すると以下のような形になると思います。

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

class _RandomWordsState extends State<RandomWords> {
  @override
  Widget build(BuildContext context) {
    return Container(
      
    );
  }
}

Stateクラスの前に_(アンダースコア)がついていますが、これはDart言語でプライバシー強化するためのもので、

これはStateオブジェクトのベストプラクティスとして推奨されているようです。

アプリのロジックのほとんどは_RandomWordsStateにあり、RandomWordsの状態を保持し続け、生成された単語ペアのリストを保存し、ユーザーがスクロールすると無限に増えていきます。

_RandomWordsStatebuildメソッドを以下のように更新します。

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

そして、今回作ったRandomWordsMyAppで使うように修正します。

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

Step4 無限スクロールするListViewを作る

_RandomWordsStateを変更し、ユーザーがスクロールするたびに無限に単語のペアを生成し表示させていきます。

ListViewbuilder factory constrorを利用すると、必要に応じてリストのビューを構築することができます。

english_wordsのランダムで作られた単語のペアを保存するために、_RandomWordsStateクラスに_suggestionsというリストと、フォントサイズを大きくするために_biggerFontを用意します。

class _RandomWordsState extends State<RandomWords> {
  final _suggestions = <WordPair>[];
  final _biggerFont = TextStyle(fontSize: 18);
...

次に、_suggestionsをListViewとして表示するために_buildSuggestions()というメソッドを追加していきます。

ListViewにはitemBuilderというものがあり、これを実装するとリストを作ることができるそう!

itemBuilderには2つのパラメータ(BuildContextと行イテレータ)が渡されます。

イテレータは0から始まり、関数が呼び出されるたびにインクリメントします。

_RandomWordsStateクラスに_buildSuggestions() を実装していきましょう!

※ここではまだ_buildRowを実装していないのでエラーが表示されますが気にせず進みます。

  Widget _buildSuggestions() {
    return ListView.builder(
      padding: EdgeInsets.all(16.0),
      itemBuilder: /*1*/ (BuildContext context, int 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]);
      },
    );
  }

/1/ itemBuilderのコールバックは生成された単語のペアごとに1回呼び出される。 偶数行の時は、単語のペアを表示するListTitle行を追加 機数行の時は、仕切りの線であるDividerWidgetを追加

/2/ ListViewの各行の前に高さ1pxの仕切り線を追加

/3/ i ~/ 2は式であり、iを2で割った整数になります。 (ex: i -> 0のとき 0, i -> 1のとき 0, i -> 2のとき 1, i -> 3のとき 1, i -> 4のとき 2, i -> 5のとき 2)

これにより、_suggestionsで指定するindexDividerを除いた数になります。

/4/ 使用可能な単語のペアの最後に達したら、さらに10個追加して_suggestionsに追加する

次に、_buildRow 関数を作成していきます。

_RandomWordsStateに追加します。

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

これは単語のペアからListTitleを作り、先ほど作った_biggerFontstyleとして当てています。

次に、_RandomWordsStateクラスで単語生成クラスを直接呼び出すのではなく、_buildSuggestions()を使用するようにbuild()を更新します。

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(title: Text('Startup Name Generator')),
      body: _buildSuggestions(),
    );
  }

最後にMyAppクラスを修正します。

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

タイトルを変更し、homeをRandomWords()に変更しました。

アプリを再起動してみてください!

動作しているツイートをぶら下げておきます!

https://twitter.com/tsuzuki817/status/1370050258391105538?ref_src=twsrc%5Etfw

以上Flutter Tutorial part1でした!