「Write your first Flutter app, part 1」をしてみる!

 

最近よくみるFlutterでHello Worldしてみる

前回はFlutterを使ってHelloWorldをしたので、今回は。。。

Flutterの公式ドキュメントより、初めてのアプリを作ってみますー。わたしなりに日本語訳したのでお役に立てば嬉しいです^^

参照:
https://flutter.dev/docs/get-started/codelab

 

(おさらい)

ちょっと忘れかけていたので、appの新規作成とsimulatorでの実行方法をおさらいしておきます。分かる人は飛ばしてください😅

appの作成

$ flutter create myapp
$ cd myapp

simulatorを起動させておく

$ flutter emulators
2 available emulators:


Galaxy_Nexus_API_29 • Galaxy Nexus API 29 • Google • android
apple_ios_simulator • iOS Simulator       • Apple  • ios

To run an emulator, run 'flutter emulators --launch '.
To create a new emulator, run 'flutter emulators --create [--name xyz]'.

You can find more information on managing emulators at the links below:
  https://developer.android.com/studio/run/managing-avds
  https://developer.android.com/studio/command-line/avdmanager
$ flutter emulators --launch apple_ios_simulator

↑でapple_ios_simulatorが起動しますー

appのrun

simulatorを起動させておいた状態で、

$ flutter devices
1 connected device:

iPhone 11 • E1B5593C-D161-43EC-BD9D-733A9DC10494 • ios • com.apple.CoreSimulator.SimRuntime.iOS-13-4 (simulator)
$ flutter run
Launching lib/main.dart on iPhone 11 in debug mode...

Running Xcode build...

 └─Compiling, linking and signing...                        75.9s
Xcode build done.                                           100.4s
Syncing files to device iPhone 11...                               182ms

Flutter run key commands.
r Hot reload. 🔥🔥🔥
R Hot restart.
h Repeat this help message.
d Detach (terminate "flutter run" but leave application running).
c Clear the screen
q Quit (terminate the application on the device).
An Observatory debugger and profiler on iPhone 11 is available at: http://127.0.0.1:49386/TQwM5vCq3BY=/

simulatorに作ったappが起動しますー

では早速、はじめてのアプリを作っていきます🍻

 

Step 1: Create the starter Flutter app

lib/main.dartを以下に書き換える

// Copyright 2018 The Flutter team. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.

import 'package:flutter/material.dart';

void main() => runApp(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: Text('Hello World'),
        ),
      ),
    );
  }
}

  • リターンしているMaterial(App)とは…?
    モバイルおよびWebの標準であるビジュアルデザイン言語で、Flutterには、いろんなマテリアルウィジェットのセットが用意されているらしい。
  • Statelessウィジェットを拡張している(Step3でStatefulウィジェットを使います)Flutterでは、配置、パディング、レイアウトなど、ほとんどすべてがウィジェットらしい。
  • Material(App)の中のScaffoldウィジェットは、デフォルトのアプリバー、タイトル、およびホーム画面のウィジェットツリーを保持するbodyプロパティを提供する。
  • ウィジェットの主な仕事は、ウィジェットを他の低レベルのウィジェットで表示する方法を説明するbuild()メソッドを提供する。
  • この例のbodyは、Text子ウィジェットを含むCenterウィジェットで構成されていて、 Centerウィジェットは、ウィジェットサブツリーを画面の中央に配置する。

Step 2: Use an external package

外部パッケージを使う例。pub.devにあるパッケージを使うことができる様子。
今回は例として「english_words」というパッケージを追加する。

2-1. pubspec.yamlの依存リストに追加する

pubspec.yaml

dependencies:
  flutter:
    sdk: flutter
...
+ english_words: ^3.1.0

2-2. pub get(package get?) をする

Android Studioのpub getをすると、$ flutter pub getが走るっぽい。
これが完了すると、pubspec.lockにもenglish_wordsの依存バージョン情報が追記される。

小ネタ

Waiting for another flutter command to release the startup lock…
がでる時の解決策

$ flutter pub get
Waiting for another flutter command to release the startup lock...

flutterコマンドのcacheにlockfileがあるのが原因らしいので、
/flutter/bin/cache/lockfileをrmすると解決しましたー🙇‍♂️ありがたい
参考:https://qiita.com/konifar/items/29f2ed4bcc2a970014c1

2-3. lib/main.dartに追加したパッケージ(english_words)を取り込む

lib/main.dart

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

2-4. english_wordsを使うコードを書く

lib/main.dart

               Widget build(BuildContext context) {            
+                final wordPair = WordPair.random();            
                 return MaterialApp(            
                   title: 'Welcome to Flutter',            
                   home: Scaffold(              
                       title: Text('Welcome to Flutter'),            
                     ),            
                     body: Center(            
-                      child: Text('Hello World'),            
+                      child: Text(wordPair.asPascalCase),            
                     ),            
                   ),            
                 );

ランダムに英語の単語がキャメルケースで表示されるようになりました!
hot reload⚡️すると単語が変わるのがわかりますー

 

Step 3: Add a Stateful widget

今の状態はStatelessウィジェットで、その名の通り不変なためプロパティの変更ができないためStatefulウィジェットを追加しますー
Statefulウィジェットは、ウィジェットの有効期間中の変化を維持できるらしいですー
Statefulウィジェットの実装には、StatefulWidgetクラスとStateクラスが必要らしい。
StatefulWidgetクラス自体は不変だが、Stateクラスが有効期間中の変化を維持してくれるらしい。

今後、Stateクラスを作るRandomWordsのStatefulウィジェットを追加し、そのRandomWordsを既存のMyAppステートレスウィジェット内の子として使用します。

3-1. main.dartにRandomWordsStateクラスを作成する

lib/main.dart

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

State<RandomWords>はRandomWordsでの使用に特化した汎用のStateクラスを使用していることを示してるらしい。
アプリのロジックと状態のほとんどはStateにあり、RandomWordsウィジェットの状態を維持する。
このクラスは、3-2(スクロールにしたがって無限に生成したランダム単語を保存したり、単語をお気に入り登録したりする機能)で使う。

3-2. statefulなRandomWordsウィジェットをmain.dartに追加する

RandomWordsウィジェットは、Stateクラスを作成する以外にほとんど何もしません。

lib/main.dart

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

3-3. RandomWordsStateクラスのbuildメソッドを追加する

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

3-4. MyAppから単語生成の部分のコードを取り除いて、statefulなRandomWordsウィジェットを使うようにする

lib/main.dart

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

hot reload⚡️してもsimulatorのアプリはstep2と変わらないことを確認します。

Step 4: Create an infinite scrolling ListView

RandomWordsStateを修正して、単語リストを生成・表示する機能を追加する。
リストにListViewウィジェットを使い、ListViewのビルダーファクトリコンストラクターを使用すると、オンデマンドで遅延してリストビューを作成できるらしい。

4-1. 単語を保存する_suggestionsリストとフォントサイズを大きくする_biggerFont変数をRandomWordsStateクラスに追加する。

lib/main.dart

class RandomWordsState extends State {
+  final _suggestions = [];
+  final _biggerFont = const TextStyle(fontSize: 18.0);
  // ···
}

Unlike Java, Dart doesn’t have the keywords public, protected, and private. If an identifier starts with an underscore (_), it’s private to its library. For details, see Libraries and visibility.

「_」をつけると、dartではprivateになるらしい。

次に、RandomWordsStateクラスに単語リストを表示するListViewをbuildするbuildSuggestions()メソッドを作る。

4-2and4-3. RandomWordsStateクラスに_buildSuggestions(),_buildRow()を追加する

lib/main.dart

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,
    ),
  );
}
  • /*1*/
    itemBuilderは単語が提案されるたびにコールバックされ、
    偶数行で、その単語をListTileの行に追加する。
    奇数(isOdd)行では、視覚的に分離するための1pxのDividerウィジェットを追加する。
  • /*4*/
    もし単語リスト_suggestionsの単語の数が上限に達したら、さらに10コの単語を生成しリストに追加する。

4-4. RandomWordsStateクラスbuild()メソッドで_buildSuggestions()を使うようにする。

lib/main.dart

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

4-5. MyAppクラスのbuild()メソッドのtitleを変更し、RandomWordsウィジェットをhome直下に移動させる

lib/main.dart

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

 

最後に

つぎはPart2で、スクロールとお気に入り登録機能を追加します!がんばりまーす🙌