うまとま君の技術めも

2015年新卒入社した社畜の勉強内容などなど

Flutter for Web

Flutter for Web

Flutter for Web

  • Android/iOSに加えてFlutterの対応プラットフォームにWebが入っている
  • Dartで書かれたソースコードJavaScriptへと変換し動作させる
  • 2020年4月時点では beta channel で使用可能(本番利用は非推奨)

使い方

仕組み

  • DartからJavaScriptへの変換処理はDart自体で提供されている仕組みを利用
  • DOM・CanvasCSSを使い各ブラウザで動作する描画処理をFlutter側で提供している

Dart Web

  • webdevを使いDartからJavaScriptへの変換処理を行う
    • 内部では、dartdevc と daert2js の2つのコンパイラが使われている
    • dartdevc
      • the Dart dev compiler
      • 開発用、差分ビルド等が使える
    • dart2js
  • HTMLから変換されたJavaScriptを呼び出すことで処理を実行する

DartでWebアプリケーション作成

$ dart --version
Dart VM version: 2.7.2 (Mon Mar 23 22:11:27 2020 +0100) on "macos_x64"

// DartからJavaScriptへの変換処理を行うためのツール
$ pub global activate webdev

// プロジェクト作成
$ mkdir dartwebapp
$ cd dartwebapp
$ vi pubspec.yaml
name: dartwebapp
description: Dart web application
environment:
  sdk: '>=2.7.0 <3.0.0'
dev_dependencies:
  # dependencies for webdev
  build_runner: ^1.8.1
  build_test: ^0.10.12+1
  build_web_compilers: ^2.9.0

$ vi web/index.html
<!DOCTYPE html>
<html lang="ja">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>dartwebapp</title>
    <!-- JavaScriptに変換されたmain.dartを読み込む -->
    <script defer src="main.dart.js"></script>
</head>
<body>
    <div id="hello"></div>
</body>
</html>

$ vi web/main.dart
import 'dart:html';

void main() {
  querySelector('#hello').text = 'Hello World!!';
}

// Webアプリケーション起動(開発用)
$ pub global run webdev serve
...
[INFO] Serving `web` on http://127.0.0.1:8080
...

// ビルド(本番用)
$ pub global run webdev build --release

Flutterでの描画処理

DomCanvasとBitmapCanvasの使われ方

import 'package:flutter/material.dart';

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

class MyApp extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      title: 'Flutter Demo',
      debugShowCheckedModeBanner: false,
      theme: ThemeData(
        primarySwatch: Colors.blue,
      ),
      home: Scaffold(
        appBar: AppBar(
          leading: FlutterLogo(),
        ),
        body: Center(
          child: Text('Hello World'),
        ),
      ),
    );
  }
}
<flt-scene flt-layer-state="updated" style="position: absolute;">
    <flt-transform
        flt-layer-state="updated"
        style="position: absolute; transform-origin: 0px 0px 0px;">
        <flt-offset
            flt-layer-state="retained"
            style="position: absolute; transform-origin: 0px 0px 0px; transform: translate(0px, 0px);">
            <flt-offset
                flt-layer-state="retained"
                style="position: absolute; transform-origin: 0px 0px 0px; transform: translate(0px, 0px);">
                <flt-clip
                    flt-layer-state="retained"
                    clip-type="physical-shape"
                    style="position: absolute; overflow: hidden; background-color: rgb(250, 250, 250); box-shadow: none; left: 0px; top: 0px; width: 552px; height: 815px; border-radius: 0px;">
                    <flt-clip-interior style="position: absolute; left: 0px; top: 0px;">
                        <flt-picture flt-layer-state="retained" style="position: absolute; transform: translate(0px, 0px);">

                            <!-- HTML&CSSのみで描画できる場合 → HTML&CSSを使う (DomCanvas) -->
                            <flt-dom-canvas style="position: absolute; top: 0px; right: 0px; bottom: 0px; left: 0px;">
                                <!-- Scaffold.body -->
                                <p style="font-size: 14px; font-weight: normal; font-family: Roboto, Arial, sans-serif; color: rgba(0, 0, 0, 0.867); position: absolute; white-space: pre-wrap; overflow-wrap: break-word; overflow: hidden; height: 16px; width: 72px; transform-origin: 0px 0px 0px; transform: matrix(1, 0, 0, 1, 240, 427.5);">Hello World</p>
                            </flt-dom-canvas>
                        </flt-picture>
                        <flt-clip
                            flt-layer-state="retained"
                            clip-type="physical-shape"
                            style="position: absolute; overflow: hidden; background-color: rgb(33, 150, 243); box-shadow: rgba(0, 0, 0, 0.4) 1.33333px 2.66667px 5.52px 0px; left: 0px; top: 0px; width: 552px; height: 56px; border-radius: 0px;">
                            <flt-clip-interior style="position: absolute; left: 0px; top: 0px;">
                                <flt-picture
                                    flt-layer-state="retained"
                                    style="position: absolute; transform: translate(0px, 0px);">

                                    <!-- HTML&CSSのみで描画できない場合 → Canvasを使う (BitmapCanvas) -->
                                    <flt-canvas style="position: absolute; transform: translate(6px, 1px);">
                                        <!-- Scaffold.appBar -->
                                        <canvas
                                            width="44"
                                            height="54"
                                            style="position: absolute; width: 44px; height: 54px; z-index: -1;"></canvas>
                                    </flt-canvas>
                                </flt-picture>
                            </flt-clip-interior>
                        </flt-clip>
                        <flt-picture
                            flt-layer-state="retained"
                            style="position: absolute; transform: translate(0px, 0px);"></flt-picture>
                    </flt-clip-interior>
                </flt-clip>
            </flt-offset>
        </flt-offset>
    </flt-transform>
</flt-scene>

Nativeコードを呼び出す(JavaScriptを呼び出す)

参考資料