search
LoginSignup
8

More than 3 years have passed since last update.

posted at

updated at

Organization

Flutter入門[環境構築〜Todoアプリ]

たまたまFlutterを利用することになったのでこのテーマで投稿します。
今回はVSCodeとiOSシミュレータを使って入門レベルの説明を行います。

環境

macOS

インストールするもの(すでに入っている方は先へ)
Flutter SDK
VSCode
XCode

Flutterとは

スクリーンショット 2019-12-02 22.05.23.png

Flutter is Google’s UI toolkit for building beautiful, natively compiled applications for mobile, web, and desktop from a single codebase.

Flutterは、単一のコードベースからモバイル、ウェブ、デスクトップ向けの、美しくネイティブにコンパイルされたアプリケーションを構築するためのGoogleのUIツールキットです。

環境構築

インストール

インストールに関しては、公式のやり方を参考にすれば特に困ることはないと思います。

使用するエディタの候補は Android Studio、IntelliJ、VSCode などがあるみたいですが、私は普段VSCodeを利用しているので、そちらで進めていきます。

https://flutter.dev/docs/get-started/install
で自分のOSを選択し、FlutterSDKをインストールします。
解凍したflutterフォルダを ~/Developer 以下に移動させます。

ターミナルでvim ~/.bash_profileとして、

[~/.bash_profile]
export PATH="$PATH:/Users/[USER NAME]/Developer/flutter/bin"

を書き込み、source ~/.bash_profileで読み込みます。
ターミナルでflutter --versionコマンドを入力して、バージョンが表示されればOKです。

flutterにはflutter doctorコマンドがあり、これで、開発環境構築の状況がわかります。
何をしなくちゃいけないかが書いてあるので、指示通りやれば問題ないです。
例↓

[✗] iOS toolchain - develop for iOS devices
    ✗ Xcode installation is incomplete; a full installation is necessary for iOS development.
      Download at: https://developer.apple.com/xcode/download/
      Or install Xcode via the App Store.
      Once installed, run:
        sudo xcode-select --switch /Applications/Xcode.app/Contents/Developer

Xcodeインストール

XCodeがない方は、flutter doctor

Download at: https://developer.apple.com/xcode/download/
Or install Xcode via the App Store.

とある通り、Xcodeをインストールします。
gitが入っていないときはインストールするよう言われるかもしれませんので指示に従ってインストールしましょう。

VSCode設定

VSCodeがない方は https://code.visualstudio.com/ からVSCodeインストールします。
VSCode内の左下のボタンをクリックして Flutter と検索し、一番上に出てくる拡張機能をインストールします。
スクリーンショット 2019-12-02 22.10.41.png

これで、Flutterが使えるようになりました。
Flutterの拡張機能に加えて、Dartの拡張機能を入れてもいいと思います。

サンプルアプリを動かす

私は ~/flutter_apps というディレクトリを作りました。この中にアプリを作っていきます。

$ flutter create first_app

でアプリの雛形を作成します。
完了したら first_app フォルダをVSCodeで開きます。

lib/main.dartの中にはすでにコードが書かれているので、シミュレータで実行してみます。
スクリーンショット 2019-12-03 14.42.50.png

デバッグの開始、Start iOS Simulator をクリックします。(もしくはF5)

スクリーンショット 2019-12-02 22.10.41.png

こんなカウンターアプリが立ち上がりました。プラスボタンをタップするとカウントが+1されます。

Hello World

main.dartを以下の様に書き換えると

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

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

class MyApp extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      title: 'Flutter Demo',
      theme: ThemeData(
        primarySwatch: Colors.blue,
      ),
      home: MyHomePage(),
    );
  }
}

class MyHomePage extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(
        title: Text('First App'),
      ),
      body: Center(
        child: Text('Hello World'),
      ),
    );
  }
}

スクリーンショット 2019-12-02 22.10.41.png

Hello World が表示されました。Flutterには Hot Reload があるので、コードがすぐ画面に反映されていいですね。開発がサクサク進みそうです。

コードの説明を簡単にしてみます。

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

一行目のこれは

void main() {
  runApp(new MyApp());
}

と同じ意味です。main関数内で MyAppインスタンスを引数としてrunApp関数を呼んでいます。
インスタンス生成時の「new」は書かなくてもいいみたいです。

class MyApp extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      home: MyHomePage(),
    );
  }
}

MyAppクラスはStatelessWidgetクラスを継承しています。
StatelessWidgetクラスとはなんでしょう。
https://api.flutter.dev/flutter/widgets/StatelessWidget-class.html

公式ドキュメントには詳しい説明がありますが、ここでは簡単に、
state(状態)がなく、immutableな(再描画が必要ない)Widgetと考えます。
(もし状態を持たせたいなら StatefulWidgetを使うことを検討しましょう。)

ちなみにWidget(ウィジェット)とは、コンピュータの操作画面を構成する、何らかの機能を持った表示・操作要素のことです。

StatelessWidgetはbuildメソッドを持っています。これでWidgetクラス等を返すようです。
上のコードでは、MaterialApp Widgetを返しています。

Material Widgetの引数で、MyHomePageインスタンスが呼ばれています。
続いてMyHomePageクラスを見てみます。

class MyHomePage extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(
        title: Text('First App'),
      ),
      body: Center(
        child: Text('Hello World'),
      ),
    );
  }
}

こちらもStateless Widgetを継承しているのでbuildメソッドを持っています。
同じように、Scaffold Widgetを返しており、引数で AppBar、Center Widgetが呼ばれています。

Flutterではこんな感じでWidgetの中でWidgetを呼んで、入れ子状にして構築するようです。

Todoアプリ

ここから、追加だけの簡単なTodoアプリを作りたいと思います。
main.dartを書き換えます。

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

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

class MyApp extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      home: MyHomePage(),
    );
  }
}

class MyHomePage extends StatefulWidget {
  @override
  _MyHomePageState createState() => _MyHomePageState();
}

class _MyHomePageState extends State<MyHomePage> {
  TextEditingController _controller = TextEditingController();
  final List<TodoItem> _items = List();

  @override
  void dispose() {
    _controller.dispose();
    super.dispose();
  }

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(
        title: Text('Todo App'),
      ),
      body: Column(
        children: <Widget>[
          Row(
            children: <Widget>[
              Expanded(
                flex: 3,
                child: TextField(
                  controller: _controller,
                ),
              ),
              Expanded(
                flex: 1,
                child: RaisedButton(
                  child: Text("追加"),
                  color: Colors.orange[600],
                  textColor: Colors.white,
                  onPressed: () {
                    setState(() {
                      _items.add(TodoItem(content: _controller.text,));
                      _controller.clear();
                    });
                  },
                ),
              )
            ],
          ),
          Expanded(
            child: Column(
              children: _items,
            ),
          ),
        ],
      ),
    );
  }
}


class TodoItem extends StatelessWidget {
  final String content;

  TodoItem({@required this.content});

  @override
  Widget build(BuildContext context) {
    return ConstrainedBox(
      constraints: BoxConstraints.expand(height: 50.0),
      child: Container(
        decoration: BoxDecoration(
          border: Border(bottom: BorderSide(color: Colors.lightBlue[200]))
        ),
        child: Center(
          child: Text(
            content,
            style: TextStyle(
              fontSize: 18.0,
            )
          ),
        ),
      ),
    );
  }
}

スクリーンショット 2019-12-02 22.10.41.png

編集も削除もできない追加だけのシンプルなTodoアプリができました。

簡単にコードの説明をします。

class MyHomePage extends StatefulWidget {
  @override
  _MyHomePageState createState() => _MyHomePageState();
}

今回は再描画が必要なので MyHomePageクラスは StatefulWidgetを継承しています。
StateクラスがUIの状態を持っているので、ここでは特に処理を書かず、_MyHomePageStateクラスに処理を書いていきます。

class _MyHomePageState extends State<MyHomePage> {
  TextEditingController _controller = TextEditingController();
  final List<TodoItem> _items = List();

  @override
  void dispose() {
    _controller.dispose();
    super.dispose();
  }

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(
        title: Text('Todo App'),
      ),
      body: Column(
        children: <Widget>[
          Row(
            children: <Widget>[
              Expanded(
                flex: 3,
                child: TextField(
                  controller: _controller,
                ),
              ),
              Expanded(
                flex: 1,
                child: RaisedButton(
                  child: Text("追加"),
                  color: Colors.orange[600],
                  textColor: Colors.white,
                  // ボタンを押したときの状態の変化をここに書く
                  onPressed: () {
                    setState(() {
                      _items.add(TodoItem(content: _controller.text,),);
                      _controller.clear();
                    });
                  },
                ),
              )
            ],
          ),
          Expanded(
            child: Column(
              children: _items,
            ),
          ),
        ],
      ),
    );
  }
}

build関数の中は先ほどと同じ様にWidgetを入れ子にしてUIを作っています。

onPressd: のところに状態がどう変わるかを書いています。
_itemsというTodoItemのListを入れる変数をつくり、そこにTodoItemを追加していく感じです。

追加していくTodoItemの内容は、 TextEditingControllerのインスタンスを入れる_controller変数をTextFieldのcontroller: に設定しておけば、_controller.textで取り出すことができます。

終わりに

データの永続化にはFirebaseが相性が良いようです。
今後、FirebaseやBLoCパターン等も学習していけたらと思います。

私も最近触り始めたレベルなので間違っている部分があるかもしれませんが、そのときはコメントお願いします。
最後まで読んでいただき、ありがとうございました。

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
What you can do with signing up
8