【Flutter】main.dartの基本操作

Flutterの「Hello World」でmain.dartの基本構成をみていきます。

【操作1】プロジェクトの作成

① Android Studioを起動します。

【操作2】アプリの実行

【操作3】アプリの編集

Flutterでは、基本的に「lib/main.dart」を編集してアプリを作っていきます。

① lib/main.dartファイルを以下の内容に書き換えます。

import 'package:flutter/material.dart';

// MyAppインスタンスを渡してViewをbuildし、アプリを実行
void main() => runApp(new MyApp());

// 全体のView
class MyApp extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    // title, home, bodyプロパティ、任意のview widgetを渡して画面を描画
    return new MaterialApp(
      title: 'Welcome to Flutter',
      home: new Scaffold(
        appBar: new AppBar(
          title: new Text('Welcome to Flutter'),
        ),
        body: new Center(
          child: new Text('Hello World'),
        ),
      ),
    );
  }
} 

② アプリを実行します。

【操作4】外部のパッケージの利用

lutterのパッケージはFlutter Packagesで検索できます。
また、パッケージ管理は「pubspec.yaml」で行います。

■pubspec.yaml

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.0
  english_words: ^3.1.0 # ここにenglish_wordsを追加

IntelliJの上の方にPackages getというのがでるのでそれを押せばpackageが導入できます。
lib/main.dartを以下のように書き換えます。

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

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

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

これで,bodyに表示される文字列が変わるリロードする度に変わります。

【操作5】Stateful Widgetを追加

Stateless Widgetはその名の通り,動きがないウィジェットです。
Stateful Widgetのライフタイムの中で変化する状態を保持します。
Stateful Widgetは少なくとも2つのクラスを使用します。

クラス名 機能
StatefulWidget Stateクラスのインスタンスを生成するクラスで,それ自体では変化することはないですが,StateクラスはWidgetのライフタイムを保持しています。
RandomWordsState Widgetに表示されたお気に入りワードのペアを保持します。

① stateful RandomWordsウィジェットをmain.dartに追加します。

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

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

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

// RandomWordsクラスの追加
class RandomWords extends StatefulWidget {
  @override
  State createState();
  // MyAppクラスからRandomWordsStateクラスへワード生成のコードを移す
  createState() => new RandomWordsState();
}

// RandomWordsStateクラスの追加
class RandomWordsState extends State {
  @override
  // ワードペアの生成を行う
  Widget build(BuildContext context) {
    final wordPair = new WordPair.random();
    return (new Text(wordPair.asPascalCase));
  }
}

全てのアプリのコードは,RandomWordsウィジェットの状態を保持するこのクラスに属します。

RandomWordsStateクラスの機能
1 ユーザのスクロールによって生成されるワードのペアを保持
2 ユーザがハートアイコンをトグルすることでリストに追加/削除されるお気に入りワードのペアを保持

MyAppクラスからRandomWordsStateクラスへワード生成のコードを移します。

class RandomWords extends StatefulWidget {
@override

}

MyAppの方のワード生成のコードは削除します。

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

ここで実行すると、リロードする度に中央のワードが変更されるという動作を確認できます。

【操作6】無限Scroll Viewを作成

RandomWordsStateクラスを拡張してユーザがスクロールすると自動でワードを生成して表示するリストビューが完成です。
ListViewのbuilderファクトリーコンストラクタは,要求に応じてリストビューを作成します。

まず,提案されたワードを保持する_suggestionsリストをRandomWordsStateクラスに追加します。

class MyApp extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return new MaterialApp(
      title: 'Welcome to Flutter',
      home: new RandomWords(),
    );
  }
} 

class RandomWordsState extends State {
  final _suggestions = [];

  final _biggerFont = const TextStyle(fontSize: 18.0);
  @override

  
  final _suggestions = [];
  // フォントサイズを大きくするために定義
  final _biggerFont = const TextStyle(fontSize: 18.0);
  @override
  Widget build(BuildContext context) {
    return new Scaffold(
      appBar: new AppBar(
        title: new Text('Startup Name Generator'),
      ),
      body: _buildSuggestions(),
    );
  }
  // 提案されたワードを生成するリストビューを作成
  Widget _buildSuggestions() {
    // ビルダープロパティitemBuilder(ファクトリビルダーとコールバック関数)
BuildContextとイテレータ i がこの関数に渡されるパラメータ
    // イテレータは0から始まり、ワードを提案する度に関数が呼ばれてインクリメントされる
    return new ListView.builder(
      padding: const EdgeInsets.all(16.0),
      // itemBuilderのコールバックはワードが提案される度に呼ばれ,ListTitleの行にそれを配置
      itemBuilder: (context, i) {
        if (i.isOdd) return new Divider();
        final index = i ~/ 2;
        if (index >= _suggestions.length) {
          _suggestions.addAll(generateWordPairs().take(10));
        }
        return _buildRow(_suggestions[index]);
      }
    );
  }
  
  Widget _buildRow(WordPair pair) {
    return new ListTile(
      title: new Text(
        pair.asPascalCase, style: _biggerFont,
      ),
    );
  }
}

【】インタラクティブなアプリを作成

各行にお気に入りボタンを追加します。

① ユーザがタップできるようするために、RandomWordsStateに_savedセットを追加します。

class RandomWordsState extends State {
  final _suggestions = [];

  final _saved = new Set();

  final _biggerFont = const TextStyle(fontSize: 18.0);
 ...

} 

② _buildRow関数にお気に入りに追加されているか確認するため、alreadySavedを追加してハート型のアイコンをリストに表示します。

class RandomWordsState extends State {
  ...

  Widget _buildRow(WordPair pair) {
    final alreadySaved = _saved.contains(pair);
    return new ListTile(
      title: new Text(
        pair.asPascalCase, style: _biggerFont,
      ),
      trailing: new Icon(
        alreadySaved ? Icons.favorite : Icons.favorite_border,
        color: alreadySaved ? Colors.red : null,
      ),
    );
} 

※リロードし、リストの行の右端にハート型のアイコンが表示されていれば成功

③ _buildRow関数を変更し、タップできるようにします。

Widget _buildRow(WordPair pair) {
    final alreadySaved = _saved.contains(pair);
    return new ListTile(
      title: new Text(
        pair.asPascalCase, style: _biggerFont,
      ),
      trailing: new Icon(
        alreadySaved ? Icons.favorite : Icons.favorite_border,
        color: alreadySaved ? Colors.red : null,
      ),
      onTap: () {
        setState(() {
           if (alreadySaved) {
             _saved.remove(pair);
           } else {
             _saved.add(pair);
           }
        });
      },
    );
  } 

【】画面遷移処理

お気に入りリスト画面を追加して画面遷移を実装します。
Navigatorがアプリのルートを含むスタックを管理しており、Navigatorのスタックにルートがプッシュされると,そのルートに画面が更新されます。
逆にNavigatorのスタックからポップされると、前のルートに戻ります。

① RandomWordsStateのbuildメソッドでAppBarにリストアイコンを追加します。
ユーザがこれをクリックすると,お気に入りが追加要素をもった新しいルートがNavigatorにプッシュされ,アイコンを表示します。

class RandomWordsState extends State {
  ...
  @override
  Widget build(BuildContext context) {
    return new Scaffold(
      appBar: new AppBar(
        title: new Text('Startup Name Generator'),
        actions: [
          new IconButton(icon: new Icond(Icons.list), onPressed: _pushSaved)
        ],
      ),
      body: _buildSuggestions(),
    );
  }
  ...
} 

② RandomWordsStateクラスに_pushSaved()関数を追加します。
ここでアプリをホットリロードしてみると,AppBarにリストアイコンが追加されていることがわかります。

class RandomWordsState extends State {
  ...
  void _pushSaved() {}
} 

ユーザがAppBarのリストアイコンをタップした際に,ルートを生成してNavigatorスタックにプッシュします。
このアクションで,新しいルートを表示して画面を変更します。

新しいページの内容は,無名関数MaterialPageRouteのbuilderプロパティでビルドされます。

下のように,NavigatorのスタックにルートをプッシュするNavigator.pushを追加し,MaterialPageRouteとそのビルダーを追加します。
ここでは,ListTileの行を生成するコードを追加しておきます。
ListTileのdivideTiles()メソッドが各ListTileの間に水平のスペースを入れてくれます。
devided変数が最後の行を保持していて,これは簡易関数toList()によってリストに変換されます。

  void _pushSaved() {
    Navigator.of(context).push(
      new MaterialPageRoute(
          builder: (context) {
            final tiles = _saved.map(
                (pair) {
                  return new ListTile(
                    title: new Text(
                      pair.asPascalCase,
                      style: _biggerFont,
                    ),
                  );
                }
            );
            final divided = ListTile.divideTiles(
                tiles: tiles,
                context: context
            ).toList();
          })
    );
  } 

ビルダープロパティはScaffoldを返します。Scaffoldは新しいルートのためのAppBar,”SavedSuggestions”を持っています。新しいルートのbodyはListTiles行を含むListViewで構成されていて,各行はdividerによって分割されています。

void _pushSaved() {
    Navigator.of(context).push(
      new MaterialPageRoute(
          builder: (context) {
            final tiles = _saved.map(
                (pair) {
                  return new ListTile(
                    title: new Text(
                      pair.asPascalCase,
                      style: _biggerFont,
                    ),
                  );
                }
            );
            final divided = ListTile.divideTiles(
                tiles: tiles,
                context: context
            ).toList();

            return new Scaffold(
                appBar: new AppBar(
                  title: new Text('Saved Suggestions'),
                ),
                body: new ListView(children: divided)
            );
          }
      )
    );
  } 

前の画面に戻るためにNavigator.popを実装しなくても、自動的に新しい画面のAppBarに[戻る]ボタンが追加されます。

【】テーマの変更

アプリのテーマはThemeDataクラスで設定されています(デフォルトは白ベース)。

class MyApp extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return new MaterialApp(
      title: 'Welcome to Flutter',
      theme: new ThemeData(
        primaryColor: Colors.amber // ここでテーマの配色を設定
      ),
      home: new RandomWords(),
    );
  }
} 

ホットリロードすると色が変化します。

関連記事
1 【Flutter入門】iOS、Android、Windowsアプリ開発
flutter
技術雑記

コメント

タイトルとURLをコピーしました