省エネエンジニア

Flutter、vue.js修行中。

【Flutter】Flutter for Web使ってみた

こんにちは。
Google IO 2019にて発表されたFlutetr for webが発表されましたね。
どんなもんかと思ってさっそく使ってみました。

github.com

私が試した方法です。
上のリポジトリをCloneします。

hello_woldディレクトリをコピーしてディレクトリ名を変更します。

pubspec.ymlを以下のように書き換えます。

name: qkuronekop.dev

environment:
  # You must be using Flutter >=1.5.0 or Dart >=2.3.0
  sdk: '>=2.3.0-dev.0.1 <3.0.0'

dependencies:
  flutter_web: any
  flutter_web_ui: any

dev_dependencies:
  build_runner: ^1.4.0
  build_web_compilers: ^2.0.0

dependency_overrides:
  flutter_web:
    git:
      url: https://github.com/flutter/flutter_web
      path: packages/flutter_web
  flutter_web_ui:
    git:
      url: https://github.com/flutter/flutter_web
      path: packages/flutter_web_ui

サンプルのままだとflutter_webflutter_web_ui相対パスになっているので直しただけですね。

あと、nameのところを変更しました。

ここまでできればあとはlib/main.dartを自由に書き換えればOKですね。

動かし方はflutter_webのリポジトリのREADMEに詳しくあるのですが、flutter pub global activate webdevでwevdevをインストールしておきます。
先ほど作ったプロジェクトのルートでflutter pub upgradeします。
webdev serveしビルドに成功するとhttp://127.0.0.1:8080で接続できます。

開発中はwebdev serve --auto restartで起動するとホットリロードしてくれます。

せっかくなので、Firbase hostingでページを公開してみました。

flutter for webで作ったページをfirebaseで公開する方法はこちらの記事を参考にさせていただきました。

qiita.com

作ったページはこちら。

https://qkuronekop.dev/#/

すごく簡素な感じなのでおいおい色々試して行きたいです。 あと、スマホで見ると文字が切れてるかも。

main.dart

// Copyright 2018 The Chromium Authors. 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_web/material.dart';
import 'dart:js' as js;

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

class MyApp extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return MaterialApp(
        title: 'Flutter Demo',
        theme: ThemeData(
            // This is the theme of your application.
            //
            // Try running your application with "flutter run". You'll see the
            // application has a blue toolbar. Then, without quitting the app, try
            // changing the primarySwatch below to Colors.green and then invoke
            // "hot reload" (press "r" in the console where you ran "flutter run",
            // or simply save your changes to "hot reload" in a Flutter IDE).
            // Notice that the counter didn't reset back to zero; the application
            // is not restarted.
            primarySwatch: Colors.deepPurple,
            primaryColorDark: Colors.deepPurpleAccent),
        home: Scaffold(
            appBar: AppBar(
              title: Text(
                'qkuronekop.dev',
                textDirection: TextDirection.ltr,
              ),
            ),
            body: SingleChildScrollView(
              child: Stack(
                children: <Widget>[
                  Column(
                    children: <Widget>[
                      Container(
                        margin: EdgeInsets.all(16.0),
                        child: Column(
                          children: <Widget>[
                            SizedBox(
                              height: 16.0,
                            ),
                            Center(
                              child: Text(
                                'Profile',
                                textDirection: TextDirection.ltr,
                                style: TextStyle(
                                    fontSize: 28.0,
                                    fontWeight: FontWeight.bold),
                              ),
                            ),
                            SizedBox(
                              height: 24.0,
                            ),
                            Center(
                              child: Row(
                                mainAxisAlignment: MainAxisAlignment.center,
                                crossAxisAlignment: CrossAxisAlignment.start,
                                children: <Widget>[
                                  Container(
                                    height: 48.0,
                                    width: 48.0,
                                    decoration: BoxDecoration(
                                      image: DecorationImage(
                                          image: NetworkImage(
                                              '画像URL'),
                                          fit: BoxFit.fill),
                                      borderRadius: BorderRadius.circular(24.0),
                                      boxShadow: [
                                        BoxShadow(
                                            color: Colors.black38,
                                            offset: Offset(2.0, 2.0))
                                      ],
                                    ),
                                  ),
                                  SizedBox(
                                    width: 16.0,
                                  ),
                                  Container(
                                    width: 400.0,
                                    child: Column(
                                      crossAxisAlignment:
                                          CrossAxisAlignment.start,
                                      children: <Widget>[
                                        Text('こんにちは。\n辻村里美(@qkuronekop)と申します。'),
                                        Text(
                                            'Androidアプリ開発が得意です。\n現在は、KotlinでのAndroid開発やFlutterでのAndroid、iOS開発をしています。'),
                                        Text('上記以外に興味があるのはGo言語でのWebアプリ開発です。'),
                                      ],
                                    ),
                                  )
                                ],
                              ),
                            ),
                            Container(
                              margin: EdgeInsets.symmetric(vertical: 32.0),
                              width: 300.0,
                              height: 2.0,
                              color: Colors.purpleAccent,
                            ),
                            Center(
                              child: Text(
                                'Github',
                                textDirection: TextDirection.ltr,
                                style: TextStyle(
                                    fontSize: 28.0,
                                    fontWeight: FontWeight.bold),
                              ),
                            ),
                            SizedBox(
                              height: 24.0,
                            ),
                            GestureDetector(
                              child: Text(
                                'https://github.com/qkuronekop',
                                style: TextStyle(
                                  color: Colors.deepPurpleAccent,
                                  decoration: TextDecoration.underline,
                                  decorationColor: Colors.deepPurpleAccent,
                                  decorationStyle: TextDecorationStyle.solid,
                                ),
                              ),
                              onTap: () {
                                js.context.callMethod(
                                    "open", ["https://github.com/qkuronekop"]);
                              },
                            ),
                            Container(
                              margin: EdgeInsets.symmetric(vertical: 32.0),
                              width: 300.0,
                              height: 2.0,
                              color: Colors.purpleAccent,
                            ),
                            Center(
                              child: Text(
                                'Blog',
                                textDirection: TextDirection.ltr,
                                style: TextStyle(
                                    fontSize: 28.0,
                                    fontWeight: FontWeight.bold),
                              ),
                            ),
                            SizedBox(
                              height: 24.0,
                            ),
                            GestureDetector(
                              child: Text('http://qkuronekop.hatenablog.jp/',
                                  style: TextStyle(
                                    color: Colors.deepPurpleAccent,
                                    decoration: TextDecoration.underline,
                                    decorationColor: Colors.deepPurpleAccent,
                                    decorationStyle: TextDecorationStyle.solid,
                                  )),
                              onTap: () {
                                js.context.callMethod("open",
                                    ["http://qkuronekop.hatenablog.jp/"]);
                              },
                            ),
                            Container(
                              margin: EdgeInsets.symmetric(vertical: 32.0),
                              width: 300.0,
                              height: 2.0,
                              color: Colors.purpleAccent,
                            ),
                            SizedBox(
                              height: 24.0,
                            ),
                            Center(
                              child: Text(
                                'App',
                                textDirection: TextDirection.ltr,
                                style: TextStyle(
                                    fontSize: 28.0,
                                    fontWeight: FontWeight.bold),
                              ),
                            ),
                            SizedBox(
                              height: 24.0,
                            ),
                            GestureDetector(
                              child: Container(
                                width: 320.0,
                                child: Image.network(
                                    '画像URL'),
                              ),
                              onTap: () {
                                js.context.callMethod("open", [
                                  "https://play.google.com/store/apps/developer?id=qkuronekop&hl=ja"
                                ]);
                              },
                            )
                          ],
                        ),
                      ),
                    ],
                  ),
                ],
              ),
            )));
  }
}

現状感じていること

  • 外部リンクができないのでdart:jsで外部リンクを開くスクリプトを書かねばならない。リンクではなくonTapを使っているのでPCでみた時にカーソルが変化しないのがちょっと微妙かな。
  • 当たり前だけど今あるNativeアプリに依存するようなpackageは使えないのでweb用のライブラリとかが充実してくるといいな。
  • Image.assetが使えないんだけど私だけ???
  • CSS書かなくてもいい感じのUIが作れるのは最高。   

2019/6/26追記

Image.assetについて勘違いしていたことがあって、Nativeの時みたいにpubspec.ymlにパスかいて使うのではなくweb/assets/にリソースを置いて、そこのパスを書けばいいんですね。
しかし、ローカルでは表示されるけどfirebase hostingにアップすると表示されなくなる……なぜだ。。。

ちょっと色々デザインいじってみました。 gitにも公開したので興味ある方はみてください!

github.com