$30 off During Our Annual Pro Sale. View Details »

アプリ開発の新たな選択肢が登場!Google社製Flutterのご紹介

 アプリ開発の新たな選択肢が登場!Google社製Flutterのご紹介

2019年6月25日(火)開催のアシアル技術セミナーでの発表資料

アシアル株式会社

June 24, 2019
Tweet

More Decks by アシアル株式会社

Other Decks in Technology

Transcript

  1. アプリ開発の新たな選択肢が登場!
    Google社製Flutterのご紹介
    HIROAKI TSUTSUMI
    堤 啓彰
    アシアル株式会社

    View Slide

  2. 堤 啓彰 Tsutsumi Hiroaki
    エンジニア(主にフロントエンド)
    業務で使ったことのある⾔語:JavaScript PHP C#
    多少は書ける⾔語:Java Swift
    ここ1年ほどは、Cordovaでのアプリ開発
    ※ Flutterを業務で使った経験はなし

    View Slide

  3. Flutterとは
    Cordova/Flutterの⽐較
    Dartとは
    Flutterの基礎
    まとめ
    TODAY’s AGENDA

    View Slide

  4. Flutterとは
    Cordova/Flutterの⽐較
    Dartとは
    Flutterの基礎
    まとめ
    TODAY’s AGENDA

    View Slide

  5. Flutterとは
    Google製の、Android/iOS向けのモバイルアプリを
    開発できるクロスプラットフォーム開発ツール
    2018年12⽉4⽇、バージョン1.0が正式リリース
    - 現時点での最新版はバージョン1.5.4
    Githubでのスター数は67,000超え

    View Slide

  6. Flutterとは > Githubスター数の推移
    2018年2⽉27⽇、β版公開

    View Slide

  7. Flutterとは > モバイルアプリだけではないFlutter
    Flutter for web
    Flutterのコードからブラウザで実⾏可能な
    Webアプリケーションを⽣成する技術
    - 現在テクニカルプレビュー

    View Slide

  8. Flutterとは > モバイルアプリだけではないFlutter
    Desktop Embedding for Flutter
    Flutterのコードからデスクトップアプリケーション
    (Windows/MacOS/Linux)を⽣成する技術
    - 現在開発中

    View Slide

  9. Flutterとは > モバイルアプリだけではないFlutter
    Flutterを習得すれば、Android/iOSに加えて、
    Web/デスクトップアプリケーションも
    作れるように

    View Slide

  10. Flutterとは
    Cordova/Flutterの⽐較
    Dartとは
    Flutterの基礎
    まとめ
    TODAY’s AGENDA

    View Slide

  11. Cordova/Flutterの⽐較
    Cordova Flutter
    開発⾔語 HTML/CSS/JavaScript Dart
    レンダリング WebView ネイティブ
    ホットリロード 不可 可

    View Slide

  12. Cordova/Flutterの⽐較 > レンダリング:Cordova
    OS(Android/iOS)
    WebView
    UI
    UIはWebViewにより
    レンダリング
    ネイティブアプリよりも、
    低パフォーマンス

    View Slide

  13. Cordova/Flutterの⽐較 > レンダリング:Flutter
    OS(Android/iOS)
    UI
    UIはネイティブで
    レンダリング
    ネイティブアプリと
    遜⾊ないパフォーマンス

    View Slide

  14. Cordova/Flutterの⽐較 > ホットリロード
    ホットリロード
    ソースコードの変更を、
    アプリを再ビルドせず、状態を維持したまま反映
    - 修正が(基本的に)1秒未満で反映
    - フルリロードしても10秒程度
    Cordovaでは現状できない(ブラウザでは可能)

    View Slide

  15. Cordova/Flutterの⽐較 > ホットリロード
    Demo

    View Slide

  16. Cordova/Flutterの⽐較
    Flutterを使えば、ハイパフォーマンスな
    アプリを、ホットリロードを⽤いて
    ⾼速に開発可能

    View Slide

  17. Flutterとは
    Cordova/Flutterの⽐較
    Dartとは
    Flutterの基礎
    まとめ
    TODAY’s AGENDA

    View Slide

  18. Dartとは > Dartの歴史
    Googleによりリリース
    - JavaScriptを代替することが⽬的
    - ブラウザへのDartVMの搭載が⽬標
    2011

    View Slide

  19. Dartとは > Dartの歴史
    Googleによりリリース
    - JavaScriptを代替することが⽬的
    - ブラウザへのDartVMの搭載が⽬標
    ブラウザへのDartVMの搭載を断念
    2011
    2015

    View Slide

  20. Dartとは > Dartの歴史
    Googleによりリリース
    - JavaScriptを代替することが⽬的
    - ブラウザへのDartVMの搭載が⽬標
    ブラウザへのDartVMの搭載を断念
    Googleのフロントエンド開発の標準⾔語としてTypeScriptが採⽤
    2011
    2015
    2017

    View Slide

  21. Dartとは > Dartの歴史
    Googleによりリリース
    - JavaScriptを代替することが⽬的
    - ブラウザへのDartVMの搭載が⽬標
    2011
    2015
    ブラウザへのDartVMの搭載を断念
    2017
    Googleのフロントエンド開発の標準⾔語としてTypeScriptが採⽤
    現在
    Flutterの普及とともに、再び注⽬を集める

    View Slide

  22. Dartとは > Dartの特徴
    Dartの特徴
    • オブジェクト指向
    • 静的型付け

    View Slide

  23. Dartとは > サンプルコード
    class Person {
    String _firstName;
    String _lastName;
    Person(this._firstName, this._lastName);
    String getFullName() {
    return '$_lastName $_firstName';
    }
    }
    void main() {
    Person person = Person('Hiroaki', 'Tsutsumi');
    print(person.getFullName());
    }

    View Slide

  24. Dartとは > Dartを使ってみた所感
    所感
    • くせがなく、書きやすい
    • オブジェクト指向を理解していれば、習得は⽤意
    • Javaによく似ており、Javaを触ったことがあれば
    すぐ馴染める

    View Slide

  25. Dartとは > カスケード記法
    カスケード記法
    • 同⼀オブジェクトに対する操作を、
    戻り値がなくても連続して記述できる記法
    • 「..」(ドット2つ)で操作を連結して記述

    View Slide

  26. Dartとは > カスケード記法
    var button = querySelector('#confirm');
    button.text = 'Confirm';
    button.classes.add('important');
    button.onClick.listen((e) => window.alert('Confirmed!'));
    カスケード記法なし

    View Slide

  27. Dartとは > カスケード記法
    var button = querySelector('#confirm');
    button.text = 'Confirm';
    button.classes.add('important');
    button.onClick.listen((e) => window.alert('Confirmed!'));
    カスケード記法なし
    querySelector('#confirm')
    ..text = 'Confirm'
    ..classes.add('important')
    ..onClick.listen((e) => window.alert('Confirmed!'));
    カスケード記法あり

    View Slide

  28. Dartとは > なぜDartが採⽤されたのか
    Dartはコンパイル⽅式として、JIT(Just In Time)と
    AOT(Ahead Of Time)の両⽅をサポート
    - JIT:コンパイル速・パフォーマンス低
    - AOT:コンパイル遅・パフォーマンス⾼

    View Slide

  29. Dartとは > なぜDartが採⽤されたのか
    Dartはコンパイル⽅式として、JIT(Just In Time)と
    AOT(Ahead Of Time)の両⽅をサポート
    - JIT:コンパイル速・パフォーマンス低
    - AOT:コンパイル遅・パフォーマンス⾼
    開発時はJITによる開発サイクルの⾼速化
    プロダクションではAOTによるパフォーマンス向上
    その他にもいくつかの理由があり、詳細は
    https://flutter.dev/docs/resources/faq#why-did-flutter-choose-to-use-dart
    (英語)を参照

    View Slide

  30. Flutterとは
    Cordova/Flutterの⽐較
    Dartとは
    Flutterの基礎
    まとめ
    TODAY’s AGENDA

    View Slide

  31. Flutterの基礎
    すべてがWidget

    View Slide

  32. Flutterの基礎 > すべてがWidget
    FlutterアプリのUIは、Widgetの組み合わせ
    Widgetの中に⼊れ⼦でWidgetを追加して、
    Widget Treeを構成していくことでUIを構築

    View Slide

  33. Flutterの基礎 > すべてがWidget
    @override
    Widget build(BuildContext context) {
    return Scaffold(
    appBar: AppBar(title: Text('TODOリ
    スト')),
    body: Column(
    children: [...],
    ),
    );
    }

    View Slide

  34. Flutterの基礎 > すべてがWidget
    @override
    Widget build(BuildContext context) {
    return Scaffold(
    appBar: AppBar(title: Text('TODOリ
    スト')),
    body: Column(
    children: [...],
    ),
    );
    }

    View Slide

  35. @override
    Widget build(BuildContext context) {
    return Scaffold(
    ...,
    body: Column(
    children: [
    Row(
    children: [...],
    ),
    Expanded(
    child: ListView.builder(...),
    ),
    ],
    ),
    );
    }
    Flutterの基礎 > すべてがWidget

    View Slide

  36. @override
    Widget build(BuildContext context) {
    ...,
    body: Column(
    children: [
    Row(
    children: [
    Flexible(
    child: TextField(...),
    ),
    Padding(padding:
    EdgeInsets.only(right: 10.0)),
    RaisedButton(...),
    ],
    ),
    ...,
    Flutterの基礎 > すべてがWidget

    View Slide

  37. Flutterの基礎 > 豊富なWidget
    Flutterは標準で⼤量のWidgetを提供
    https://flutter.dev/docs/development/ui/widgets

    View Slide

  38. Flutterの基礎 > 豊富なWidget:マテリアルデザイン
    https://flutter.dev/docs/development/ui/widgets/material

    View Slide

  39. Flutterの基礎 > 豊富なWidget:Cupertino
    https://flutter.dev/docs/development/ui/widgets/cupertino

    View Slide

  40. Flutterの基礎 > 豊富なWidget
    どのようなWidgetがあり、
    そのWidgetで何ができるのかを
    把握することが重要

    View Slide

  41. Flutterの基礎
    StatelessとStateful

    View Slide

  42. Flutterの基礎 > StatelessとStateful
    Statelessは状態を持たないWidget
    Statefulは状態を持つWidget
    外部との通信やユーザの操作により
    状態が変化しないWidgetはStatelessに
    状態が変化するWidgetはStatefulに

    View Slide

  43. Flutterの基礎 > Statelessの例
    各タスクはStateless
    • 渡された⽂字列を表⽰しているだけ
    • ユーザの操作により、
    保持している情報は変化しないため

    View Slide

  44. Flutterの基礎 > StatelessWidget
    class TodoListItem extends StatelessWidget {
    final int _index;
    final String _todo;
    final Function _removeTodo;
    TodoListItem(this._index, this._todo, this._removeTodo);
    @override
    Widget build(BuildContext context) {
    return Card(
    child: ListTile(
    title: Text(_todo),
    trailing: IconButton(
    icon: Icon(Icons.remove_circle, color: Colors.red),
    onPressed: () => _removeTodo(_index),
    ),
    ...,
    }

    View Slide

  45. Flutterの基礎 > StatelessWidget
    class TodoListItem extends StatelessWidget {
    final int _index;
    final String _todo;
    final Function _removeTodo;
    TodoListItem(this._index, this._todo, this._removeTodo);
    @override
    Widget build(BuildContext context) {
    return Card(
    child: ListTile(
    title: Text(_todo),
    trailing: IconButton(
    icon: Icon(Icons.remove_circle, color: Colors.red),
    onPressed: () => _removeTodo(_index),
    ),
    ...,
    }
    StatelessWidgetを継承

    View Slide

  46. Flutterの基礎 > StatelessWidget
    class TodoListItem extends StatelessWidget {
    final int _index;
    final String _todo;
    final Function _removeTodo;
    TodoListItem(this._index, this._todo, this._removeTodo);
    @override
    Widget build(BuildContext context) {
    return Card(
    child: ListTile(
    title: Text(_todo),
    trailing: IconButton(
    icon: Icon(Icons.remove_circle, color: Colors.red),
    onPressed: () => _removeTodo(_index),
    ),
    ...,
    }
    buildメソッドをoverride

    View Slide

  47. Flutterの基礎 > 状態変化の例

    View Slide

  48. Flutterの基礎 > 状態変化の例

    View Slide

  49. Flutterの基礎 > StatefulWidget
    final _todos = [];
    ...
    @override
    Widget build(BuildContext context) {
    return Scaffold(
    ...,
    RaisedButton(
    ...,
    onPressed: () => setState(() => _todos.add(_todo)),
    ),
    ],
    ),
    ...
    child: ListView.builder(
    itemCount: _todos.length,
    itemBuilder: (BuildContext context, int index) =>
    TodoListItem(index, _todos[index], _removeTodo),
    ), ...

    View Slide

  50. Flutterの基礎 > StatefulWidget
    final _todos = [];
    ...
    @override
    Widget build(BuildContext context) {
    return Scaffold(
    ...,
    RaisedButton(
    ...,
    onPressed: () => setState(() => _todos.add(_todo)),
    ),
    ],
    ),
    ...
    child: ListView.builder(
    itemCount: _todos.length,
    itemBuilder: (BuildContext context, int index) =>
    TodoListItem(index, _todos[index], _removeTodo),
    ), ...
    State(状態)を定義

    View Slide

  51. Flutterの基礎 > StatefulWidget
    final _todos = [];
    ...
    @override
    Widget build(BuildContext context) {
    return Scaffold(
    ...,
    RaisedButton(
    ...,
    onPressed: () => setState(() => _todos.add(_todo)),
    ),
    ],
    ),
    ...
    child: ListView.builder(
    itemCount: _todos.length,
    itemBuilder: (BuildContext context, int index) =>
    TodoListItem(index, _todos[index], _removeTodo),
    ), ...
    Stateを更新(タスクを追加)

    View Slide

  52. Flutterの基礎 > StatefulWidget
    final _todos = [];
    ...
    @override
    Widget build(BuildContext context) {
    return Scaffold(
    ...,
    RaisedButton(
    ...,
    onPressed: () => setState(() => _todos.add(_todo)),
    ),
    ],
    ),
    ...
    child: ListView.builder(
    itemCount: _todos.length,
    itemBuilder: (BuildContext context, int index) =>
    TodoListItem(index, _todos[index], _removeTodo),
    ), ...
    Stateの更新を検知し、buildが呼ばれる

    View Slide

  53. Flutterの基礎 > StatefulWidget
    final _todos = [];
    ...
    @override
    Widget build(BuildContext context) {
    return Scaffold(
    ...,
    RaisedButton(
    ...,
    onPressed: () => setState(() => _todos.add(_todo)),
    ),
    ],
    ),
    ...
    child: ListView.builder(
    itemCount: _todos.length,
    itemBuilder: (BuildContext context, int index) =>
    TodoListItem(index, _todos[index], _removeTodo),
    ), ...
    State(タスク⼀覧)が変化しているため、
    ListViewが更新される

    View Slide

  54. Flutterの基礎
    Flutterでのアニメーション

    View Slide

  55. Flutterの基礎 > Flutterでのアニメーション
    Flutterでは、アニメーションもWidget
    アニメーションに関しても、豊富なWidgetを提供
    もちろん、⾃作も可能

    View Slide

  56. Flutterの基礎 > アニメーション例:Opacity

    View Slide

  57. Flutterの基礎 > アニメーション例:Opacity
    bool _visible = true;
    @override
    Widget build(BuildContext context) {
    ...
    body: Center(
    child: AnimatedOpacity(
    opacity: _visible ? 1.0 : 0.0,
    duration: Duration(seconds: 1),
    child: Container(width: 200.0, height: 200.0, color:
    Colors.blue),
    ),
    ),
    floatingActionButton: FloatingActionButton(
    child: Icon(Icons.flip),
    onPressed: () => setState(() => _visible = !_visible),
    ),

    View Slide

  58. Flutterの基礎 > アニメーション例:Opacity
    bool _visible = true;
    @override
    Widget build(BuildContext context) {
    ...
    body: Center(
    child: AnimatedOpacity(
    opacity: _visible ? 1.0 : 0.0,
    duration: Duration(seconds: 1),
    child: Container(width: 200.0, height: 200.0, color:
    Colors.blue),
    ),
    ),
    floatingActionButton: FloatingActionButton(
    child: Icon(Icons.flip),
    onPressed: () => setState(() => _visible = !_visible),
    ),

    View Slide

  59. Flutterの基礎 > アニメーション例:Opacity
    bool _visible = true;
    @override
    Widget build(BuildContext context) {
    ...
    body: Center(
    child: AnimatedOpacity(
    opacity: _visible ? 1.0 : 0.0,
    duration: Duration(seconds: 1),
    child: Container(width: 200.0, height: 200.0, color:
    Colors.blue),
    ),
    ),
    floatingActionButton: FloatingActionButton(
    child: Icon(Icons.flip),
    onPressed: () => setState(() => _visible = !_visible),
    ),
    Stateを定義

    View Slide

  60. Flutterの基礎 > アニメーション例:Opacity
    bool _visible = true;
    @override
    Widget build(BuildContext context) {
    ...
    body: Center(
    child: AnimatedOpacity(
    opacity: _visible ? 1.0 : 0.0,
    duration: Duration(seconds: 1),
    child: Container(width: 200.0, height: 200.0, color:
    Colors.blue),
    ),
    ),
    floatingActionButton: FloatingActionButton(
    child: Icon(Icons.flip),
    onPressed: () => setState(() => _visible = !_visible),
    ),
    Stateを更新

    View Slide

  61. Flutterの基礎 > アニメーション例:Opacity
    bool _visible = true;
    @override
    Widget build(BuildContext context) {
    ...
    body: Center(
    child: AnimatedOpacity(
    opacity: _visible ? 1.0 : 0.0,
    duration: Duration(seconds: 1),
    child: Container(width: 200.0, height: 200.0, color:
    Colors.blue),
    ),
    ),
    floatingActionButton: FloatingActionButton(
    child: Icon(Icons.flip),
    onPressed: () => setState(() => _visible = !_visible),
    ),
    Stateの変更を検知し、buildが呼ばれる

    View Slide

  62. Flutterの基礎 > アニメーション例:Opacity
    bool _visible = true;
    @override
    Widget build(BuildContext context) {
    ...
    body: Center(
    child: AnimatedOpacity(
    opacity: _visible ? 1.0 : 0.0,
    duration: Duration(seconds: 1),
    child: Container(width: 200.0, height: 200.0, color:
    Colors.blue),
    ),
    ),
    floatingActionButton: FloatingActionButton(
    child: Icon(Icons.flip),
    onPressed: () => setState(() => _visible = !_visible),
    ),
    Opacityの変更に応じて、
    アニメーション

    View Slide

  63. Flutterの基礎 > アニメーション例:Container

    View Slide

  64. Flutterの基礎 > アニメーション例:Container
    double _width = 100;
    double _height = 100;
    Color _color = Colors.blue;
    @override
    Widget build(BuildContext context) {
    ...
    child: AnimatedContainer(
    width: _width,
    height: _height,
    decoration: BoxDecoration(color: _color),
    duration: Duration(seconds: 1),
    curve: Curves.easeInOut,
    ),
    ...
    onPressed: () {
    setState(() {
    final random = Random();
    _width = random.nextInt(300).toDouble();
    _height = random.nextInt(300).toDouble();
    _color = Color.fromRGBO(random.nextInt(256), random.nextInt(256),
    random.nextInt(256), 1);
    });

    View Slide

  65. Flutterの基礎 > アニメーション例:Container
    double _width = 100;
    double _height = 100;
    Color _color = Colors.blue;
    @override
    Widget build(BuildContext context) {
    ...
    child: AnimatedContainer(
    width: _width,
    height: _height,
    decoration: BoxDecoration(color: _color),
    duration: Duration(seconds: 1),
    curve: Curves.easeInOut,
    ),
    ...
    onPressed: () {
    setState(() {
    final random = Random();
    _width = random.nextInt(300).toDouble();
    _height = random.nextInt(300).toDouble();
    _color = Color.fromRGBO(random.nextInt(256), random.nextInt(256),
    random.nextInt(256), 1);
    });
    Stateを定義

    View Slide

  66. Flutterの基礎 > アニメーション例:Container
    double _width = 100;
    double _height = 100;
    Color _color = Colors.blue;
    @override
    Widget build(BuildContext context) {
    ...
    child: AnimatedContainer(
    width: _width,
    height: _height,
    decoration: BoxDecoration(color: _color),
    duration: Duration(seconds: 1),
    curve: Curves.easeInOut,
    ),
    ...
    onPressed: () {
    setState(() {
    final random = Random();
    _width = random.nextInt(300).toDouble();
    _height = random.nextInt(300).toDouble();
    _color = Color.fromRGBO(random.nextInt(256), random.nextInt(256),
    random.nextInt(256), 1);
    });
    Stateを更新

    View Slide

  67. Flutterの基礎 > アニメーション例:Container
    double _width = 100;
    double _height = 100;
    Color _color = Colors.blue;
    @override
    Widget build(BuildContext context) {
    ...
    child: AnimatedContainer(
    width: _width,
    height: _height,
    decoration: BoxDecoration(color: _color),
    duration: Duration(seconds: 1),
    curve: Curves.easeInOut,
    ),
    ...
    onPressed: () {
    setState(() {
    final random = Random();
    _width = random.nextInt(300).toDouble();
    _height = random.nextInt(300).toDouble();
    _color = Color.fromRGBO(random.nextInt(256), random.nextInt(256),
    random.nextInt(256), 1);
    });
    Stateの変更を検知し、buildが呼ばれる

    View Slide

  68. Flutterの基礎 > アニメーション例:Container
    double _width = 100;
    double _height = 100;
    Color _color = Colors.blue;
    @override
    Widget build(BuildContext context) {
    ...
    child: AnimatedContainer(
    width: _width,
    height: _height,
    decoration: BoxDecoration(color: _color),
    duration: Duration(seconds: 1),
    curve: Curves.easeInOut,
    ),
    ...
    onPressed: () {
    setState(() {
    final random = Random();
    _width = random.nextInt(300).toDouble();
    _height = random.nextInt(300).toDouble();
    _color = Color.fromRGBO(random.nextInt(256), random.nextInt(256),
    random.nextInt(256), 1);
    });
    各Stateの変更に応じて、
    アニメーション

    View Slide

  69. Flutterとは
    Cordova/Flutterの⽐較
    Dartとは
    Flutterの基礎
    まとめ
    TODAY’s AGENDA

    View Slide

  70. まとめ
    メリット・デメリット
    l 学習コストは低くはない
    l Dart⾃体は馴染みやすい
    l Widgetを使いこなすのが⼤変
    l ⽇本語の情報はまだ少ない
    l ネイティブアプリに近い
    パフォーマンス
    l ホットリロードによる
    ⾼速な開発サイクル

    View Slide

  71. View Slide