省エネエンジニア

Flutter、vue.js修行中。

【Flutter】VSCodeで開発環境を作る

qkuronekop.hatenablog.jp

以前、AndroidStudioでの開発環境の作り方を書いたのですが、VSCodeでも作ってみようと思います。

まずはここからFlutterSDKをダウンロードします。
DLしたらzipを展開して適当な場所に置きます。
flutter.dev

flutter/binにパスを通します。

パスが通ったか確認。

$ flutter doctor

Doctor summary (to see all details, run flutter doctor -v):
[✓] Flutter (Channel stable, v1.7.8+hotfix.4, on Mac OS X 10.14.6 18G87, locale ja-JP)
 
[!] Android toolchain - develop for Android devices (Android SDK version 29.0.2)
    ! Some Android licenses not accepted.  To resolve this, run: flutter doctor --android-licenses
[!] Xcode - develop for iOS and macOS (Xcode 10.3)
    ✗ CocoaPods not installed.
        CocoaPods is used to retrieve the iOS and macOS platform side's plugin code that responds to your plugin usage on the Dart side.
        Without CocoaPods, plugins will not work on iOS or macOS.
        For more info, see https://flutter.dev/platform-plugins
      To install:
        brew install cocoapods
        pod setup
    ! Brew can be used to install CocoaPods.
      Download brew at https://brew.sh/.
[✗] iOS tools - develop for iOS devices
    ✗ libimobiledevice and ideviceinstaller are not installed. To install with Brew, run:
        brew update
        brew install --HEAD usbmuxd
        brew link usbmuxd
        brew install --HEAD libimobiledevice
        brew install ideviceinstaller
    ✗ ios-deploy not installed. To install:
        brew install ios-deploy
    ! Brew can be used to install tools for iOS device development.
      Download brew at https://brew.sh/.
[!] Android Studio (version 3.5)
    ✗ Flutter plugin not installed; this adds Flutter specific functionality.
    ✗ Dart plugin not installed; this adds Dart specific functionality.
[!] VS Code (version 1.36.1)
    ✗ Flutter extension not installed; install from
      https://marketplace.visualstudio.com/items?itemName=Dart-Code.flutter
[✓] Connected device (1 available)

! Doctor found issues in 5 categories.

Android

ライセンスに同意する。

$ flutter doctor --android-licenses

iOS

brew update
brew install --HEAD usbmuxd
brew link usbmuxd
brew install --HEAD libimobiledevice
brew install ideviceinstaller

XCodeがインストールされていない場合には、XCodeをインストルして、

sudo xcode-select --switch /Applications/Xcode.app/Contents/Developer

コマンドを打てばOKです。

cocoa podがインストールされていない場合には、

brew install cocoapods
pod setup
brew install ios-deploy

VSCodeからFlutterプラグインDartをインストールします。

f:id:qkuroneko:20190829132630p:plain:w320 f:id:qkuroneko:20190829132648p:plain:w320

ここまでチェックがつけばOKだと思います。

$ flutter doctor
Doctor summary (to see all details, run flutter doctor -v):
[✓] Flutter (Channel stable, v1.7.8+hotfix.4, on Mac OS X 10.14.6 18G87, locale ja-JP)
 
[✓] Android toolchain - develop for Android devices (Android SDK version 29.0.2)
[✓] Xcode - develop for iOS and macOS (Xcode 10.3)
[✓] iOS tools - develop for iOS devices
[!] Android Studio (version 3.5)
    ✗ Flutter plugin not installed; this adds Flutter specific functionality.
    ✗ Dart plugin not installed; this adds Dart specific functionality.
[✓] VS Code (version 1.36.1)
[✓] Connected device (1 available)

ここまでできたらコマンドパレットからFlutter: New Projectを実行してください。

FlutterSDKの場所を聞かれたら「LocateSDK」ボタンを押して、flutterフォルダを選択してVSCodeを再起動してください。

次にプロジェクト名とどこにプロジェクトを作るか聞かれるので任意の名前と場所を入れてください。

はい、完成。

[f:id:qkuroneko:20190829133118p:plain:320]

Androidエミュレータにインストールしてみました。

【Vue】【Quasar】カレンダーをポップアップさせる

こんにちは。
前回、前々回はfirebaseを使って認証機能を実装していきました。

qkuronekop.hatenablog.jp

qkuronekop.hatenablog.jp

今回はUIを作ってる途中で、カレンダーをポップアップさせるのにちょっと詰まったのでご紹介しようと思います。

カレンダーのUIはこちらにあります。

https://quasar.dev/vue-components/date

使いたいDatePickerを選んでコピーしてくれば、画面に貼り付けることができるのですが、
私が今回使いたかったのはこれ。

f:id:qkuroneko:20190819131654p:plain:w320

カレンダーのアイコンをクリックするとピッカーがポップアップするもの。

f:id:qkuroneko:20190819131813p:plain:w320

サンプルのコードをコピーするだけこんな感じで、最初からピッカーが表示されるし、ピッカーの様子もおかしい。

f:id:qkuroneko:20190819132021p:plain:w320

足りなかったのはここ。

// quasar.conf.js

return {
  framework: {
    components: [
      'QDate',
      'QInput',
      'QPopupProxy',
    ],
    directives: [
      'ClosePopup'
    ],
  }
}

QDataだけでなくPopup Proxyも必要でした。

https://quasar.dev/vue-components/popup-proxy

QInputに関しては別なところですでにinputを使っているので気にしてなかったのですが、これも必要です。

これでカレンダーが無事完成しました!

【Vue】【Quasar】Google認証

こんにちは。
今回はGoogle認証を作っていこうと思います。

Googleプラグインはすでに入っている前提で書いていきますので、そちらのインストールがお済みでない場合はこちらを参考にしてみてください!

qkuronekop.hatenablog.jp

Google認証はメールリンクに比べると手間が少ないですね。
scriptのところだけ書いておきます。

<script>
import firebase from 'firebase/app';
import 'firebase/auth';

export default {
  name: 'PageIndex',
  methods: {
    toGoogle() {
      const provider = new firebase.auth.GoogleAuthProvider();
      firebase.auth().signInWithPopup(provider)
        .then((result) => {
          console.log('success');
          console.log(result);
        })
        .catch((error) => {
          console.log('error');
          console.log(error);
        });
    },
  },
};
</script>

このtoGoogleをボタンイベントにつけておきます。
ボタンをクリックするとポップアップが立ち上がり、どのGoogleアカウント使うかの選択画面になります。
アカウントを選択すると(私の場合すでにログインしているので)認証完了です。
ログインしていない場合にはログインに進むと思います。

Google認証を使うと、メールアドレスやユーザー名も取得できます。

次は、カレンダーを使ってみようと思います。

【Vue】【Quasar】quasarアプリにfirebase auth のメールリンクを追加する

qkuronekop.hatenablog.jp

先日からvue.jsでアプリを作っているのですが、ユーザーを特定したいので認証機能を入れてみようと思います。
認証方法は今の所2種類を考えています。

これらの認証方法をFirebase Authenticationを使っていれてみようと思います。

Firebase を導入

f:id:qkuroneko:20190816113825p:plain:w320

firebaseを使ったことがある方ならお分かりかと思いますが、firebase consoleのAuthenticationメニューを開き、「ログイン方法」タブから「メール/パスワード」をONにします。
私の場合には、Google認証もいずれ追加したいと思っているのでこちらもONにしています。

今回はパスワードなしにしたいと思いますので、「パスワードなしでログインする」もONにしています。

src/router/index.js

import Vue from 'vue';
import VueRouter from 'vue-router';
import firebase from 'firebase';

import routes from './routes';


Vue.use(VueRouter);


const firebaseConfig = {
  apiKey: '<自分のやつ>',
  authDomain: '<自分のやつ>',
  databaseURL: '<自分のやつ>',
  projectId: '<自分のやつ>',
  storageBucket: '',
  messagingSenderId: '<自分のやつ>',
  appId: '<自分のやつ>',
};

firebase.initializeApp(firebaseConfig);

/*
 * If not building with SSR mode, you can
 * directly export the Router instantiation
 */

export default function (/* { store, ssrContext } */) {
  const Router = new VueRouter({
    scrollBehavior: () => ({ x: 0, y: 0 }),
    routes,

    // Leave these as is and change from quasar.conf.js instead!
    // quasar.conf.js -> build -> vueRouterMode
    // quasar.conf.js -> build -> publicPath
    mode: process.env.VUE_ROUTER_MODE,
    base: process.env.VUE_ROUTER_BASE,
  });

  return Router;
}

firebaseConfigの内容はfirebase consoleのsettingを開き、「全般」タブにあるFirebase SDK snippetから確認できます。
ラジオボタンの「構成」をチェックすると上のと同じものをコピーできます。

ただし、コピーしたままだとLintにひっかかるので、上のコードのように修正する必要があります。

メール入力画面

メールアドレスを入力してもらうための新しい画面を作成しました。

f:id:qkuroneko:20190816115116p:plain:w320

MailLink.vue

<template>
  <q-page>
    <p class="q-ma-md">メールアドレスを送信してください。</p>
    <q-input class="q-ma-md" v-model="email" type="email" label="メールアドレス" />
    <q-btn color="primary" class="float-right q-ma-md" @click="toMail">送信</q-btn>
  </q-page>
</template>

<script>

import firebase from 'firebase/app';
import 'firebase/auth';

export default {
  name: 'MailLink',
  data() {
  },
  methods: {
    toMail() {
      const actionCodeSettings = {
        // URL you want to redirect back to. The domain (www.example.com) for this
        // URL must be whitelisted in the Firebase Console.
        url: '<自分のアドレス>',
        // This must be true.
        handleCodeInApp: true,
      };
      firebase.auth().sendSignInLinkToEmail(this.email, actionCodeSettings)
        .then(() => {
          alert('Send mail.');
          // ここでローカルストレージにメールアドレスを保存しておく
         this.$q.localStorage.set('emailForSignIn', this.email);
        })
        .catch((error) => {
          alert(error.message);
        });
    },
  },
};
</script>

urlにはリダイレクト先のURLをいれます。
firebaseのAuthenticationで承認済みドメインに登録されているドメイン以外はエラーになります。

実際にこの画面からメールを送信すると、firebaseからメールが飛んできます。(メールの文章はfirebase consoleから編集できます)
メールに添付されているリンクをクリックすると、URLに書いておいたURLにキー付きでリダイレクトされます。

リダイレクト先モジュールはこんな感じにしました。

SignIn.vue

<template>
  <q-page>
    <p class="flex flex-center">認証中です。</p>
  </q-page>
</template>

<script>
import firebase from 'firebase/app';
import 'firebase/auth';

export default {
  name: 'SignIn',
  created() {
    if (firebase.auth().isSignInWithEmailLink(window.location.href)) {
      const mailaddress = this.$q.localStorage.getItem('emailForSignIn');
      firebase.auth().signInWithEmailLink(mailaddress, window.location.href)
        .then((result) => {
          console.log(result);
          window.location.href = './#/main';
        })
        .catch((error) => {
          alert(error.message);
        });
    }
  },
};
</script>

保存しておいたメールドレスと、キー付きのURLをsignInWithEmailLink()に渡します。
成功したら、メインの画面へ遷移するという風にしました。

次はGoogle認証を作っていきたいと思います。

【Vue】【Quasar】Quasarでアプリを作る

qkuronekop.hatenablog.jp

前回、firebaseにhostingしたアプリをデフォルト状態からちょっとづつ修正して行こうと思います。

デフォルト状態だと、MyLayout.vueの内容が最初の画面として表示されています。
これを別のファイルに差し替えてみようと思います。

src/layouts/TopPage.vue

ここに新しいファイルを作ります。(名前は任意)
MyLayout.vueを参考に、ツールバーを作ってみます。

TopPage.vue

<template>
<q-layout view="lHh Lpr lFf">
    <q-header elevated>
        <q-toolbar-title>
            タイトル
        </q-toolbar-title>
    </q-header>>
</q-layout>
</template>

ツールバーの色もデフォルトから変更したいので、ここのファイルを変えます。

src/css/quasar.variables.styl
$primary   = #ff6f00
$secondary = #ffe57f
$accent    = #c43e00

$positive  = #21BA45
$negative  = #C10015
$info      = #31CCEC
$warning   = #F2C037

とりあえず上3つだけ変えてみました。
ツールバーの色はprimaryを変更します。

src/router/routes.js

このファイルを開いて、ルートのコンポーネントを変更します。

const routes = [
  {
    path: '/',
    component: () => import('layouts/TopPage.vue'),
    children: [
      { path: '', component: () => import('pages/Index.vue') },
    ],
  },
];

// Always leave this as last one
if (process.env.MODE !== 'ssr') {
  routes.push({
    path: '*',
    component: () => import('pages/Error404.vue'),
  });
}

export default routes;

これで、最初に開くページが差し代わりました。

次は認証機能を作ろうと思います。

qkuronekop.hatenablog.jp

【Vue】Quasarで作ったアプリをfirebase hostingにデプロイする

近頃、Vue.jsが気になっていて少しいじってみたので記録。

目標

  • Quasarでアプリを作る
  • firebase にhostingする

Vueのインストール

npm i vue-cli -g

## quasarのインストール

npm install -g @quasar/cli

プロジェクトを作成します

$ quasar create <folder_name>

? Project name (internal usage for dev) <praise>
? Project product name (official name; must start with a letter if you will build mobile apps) <Quasar App>
? Project description <A Quasar Framework app>
? Author <qkuronekop>
? Check the features needed for your project: (Press <space> to select, <a> to toggle all, <i> to invert selection)<ESLint, Vuex, Axios, Vue
-i18n, IE11 support>
? Pick an ESLint preset <Airbnb>
? Cordova id (disregard if not building mobile apps) <dev.qkuronekop.praise>
? Should we run `npm install` for you after the project has been created? (recommended) <NPM>

プロジェクトを作ると色々聞かれますので答えていってください。
<>内は私の場合なので適宜変更してください。

動かす

では、早速出来立てのアプリを動かしてみましょう。

$ cd <folder_name>
$ quasar dev

http://localhost:8080/でアプリが立ち上がります。

f:id:qkuroneko:20190808094209p:plain:w320

なにもいじってない状態だとこんな感じ。

ビルドする

quasar build

buildするとdistというフォルダが作られますのでdeployする前に一度buildしておきます。

Firebaseにhostingする

  • 先にfirebase consoleでプロジェクトを作成しておいてください。
$ firebase init hosting

↑のコマンドを実行すると、プロジェクトを選択するように促されますので、ホスティングしたいプロジェクトを選択します。

? What do you want to use as your public directory? dist/spa
? Configure as a single-page app (rewrite all urls to /index.html)? No
  • 何か聞かれるので、↑のように答えます。

上手くdeployができればfirebase consoleのHostingの項目を開くとデフォルトのURLが発行されていますので、そこから先ほどdeployしたページを見ることができます。

次はUIをいじってみます。

qkuronekop.hatenablog.jp

【Flutter】Androidで以前作ったViewをflutterで実現する

こんにちは。
以前お仕事で下記のようなデザインをAndroidで作ったのですが、それをFlutterでどう実現できるかやってみました。
ランキングを色と番号で表現しています。

f:id:qkuroneko:20190718122715j:plain:w320

Androidで作った時もどう実現しようか悩んで最初は、Canvasに描くという方法をとりました。
なぜかというとランキング毎に色が違うので色だけ渡せばViewを使い回しできると思ったのです。

しかし、この方法だとListViewのスクロールがもたつく結果となり諦めました。

次にとったのが、xmlで半円のdrawableを色の数だけ作成し、描画時にランキングが何位かでxmlを変更するという方法です。

結局、ListViewがスムーズに動くのでこの方法に落ち着きました。
しかし、これは大変手間がかかり作るのがとても面倒でした。

さて、Flutterならどうすればいいでしょうか。

FlutterのContainerというWidgetにはdecorationをつけることができます。
Androidだと面倒だった円や丸角の背景が結構簡単につけられます。

 Container(
    width: 64.0,
    height: 64.0,
    decoration: BoxDecoration(color: Colors.blue, shape: BoxShape.circle),
)

f:id:qkuroneko:20190718124629j:plain

  Container(
    margin: EdgeInsets.symmetric(vertical: 8.0),
    width: 32.0,
    height: 64.0,
    decoration: BoxDecoration(
      color: Colors.blueAccent,
      borderRadius: BorderRadius.only(
          topRight: Radius.circular(32.0),
          bottomRight: Radius.circular(32.0)),
    ),
  )

borderRadiusで丸角をつけることができるのですが、全部を丸角にするのではなく一部にだけつけることができます。

上のコードのようにすることで、右側だけ丸い半円が描けます。

あとはリストのindexをとって、番号に準じて色を変えればいいだけなので簡単に実現できますね。

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(title: 'Flutter Demo Home Page'),
    );
  }
}

class MyHomePage extends StatefulWidget {
  MyHomePage({Key key, this.title}) : super(key: key);
  final String title;

  @override
  _MyHomePageState createState() => _MyHomePageState();
}

class _MyHomePageState extends State<MyHomePage> {
  @override
  Widget build(BuildContext context) {
    List<Widget> widgetList = List();
    for (var i = 0; i < 50; i++) {
      widgetList.add(_content(i));
      widgetList.add(Divider(height: 1.0,));
    }

    return Scaffold(
        appBar: AppBar(
          // Here we take the value from the MyHomePage object that was created by
          // the App.build method, and use it to set our appbar title.
          title: Text(widget.title),
        ),
        body: ListView(
          children: widgetList,
        ));
  }

  Widget _content(int index) {
    return Container(
      child: Row(
        children: <Widget>[
          Stack(
            alignment: Alignment.centerLeft,
            children: <Widget>[
//              Container(
//                margin: EdgeInsets.symmetric(vertical: 8.0),
//                width: 33.0,
//                height: 65.5,
//                decoration: BoxDecoration(
//                    color: Colors.blue,
//                    borderRadius: BorderRadius.only(
//                        topRight: Radius.circular(32.0),
//                        bottomRight: Radius.circular(32.0)),),
//              ),
              Container(
                margin: EdgeInsets.symmetric(vertical: 8.0),
                width: 32.0,
                height: 64.0,
                decoration: BoxDecoration(
                  color: index == 0
                      ? Colors.red
                      : index == 1
                          ? Colors.blueAccent
                          : index == 2 ? Colors.amber : Colors.lime,
                  borderRadius: BorderRadius.only(
                      topRight: Radius.circular(32.0),
                      bottomRight: Radius.circular(32.0)),
                    boxShadow: [BoxShadow(color: Colors.black26, offset: Offset(0.0, 2.0))]
                ),
                child: Center(
                  child: Text(
                    '${index + 1}',
                    style: TextStyle(
                        color: index < 3 ? Colors.white : Colors.black),
                  ),
                ),
              )
            ],
          ),
          Container(
            margin: EdgeInsets.all(16.0),
            child: Text('aaaaaaaaaa'),
          ),
        ],
      ),
    );
  }
}

f:id:qkuroneko:20190718125507p:plain:w320

これでこんな感じに色を変更しつつランキングを表現できます。
影はboxShadowでつけることができます。

影ではなくborderをつけたいと思ったところがコメントアウトのところなのですが、どうやらborderRadiusをつけていると一部にだけborderをつけることができなくなるみたいです。
border: Border.all(color: Colors.blueAccent, width: 2.0)をつけると左側にもボーダーが表示されるのでいまいちですよね。

f:id:qkuroneko:20190718125911p:plain:w320

そこで、Containerを重ねて、下に少しだけ大きい半円を置いたらいいのでは?と考えました。

StackalignmentAlignment.centerLeftにして左側に揃えます。

それがこちら。

f:id:qkuroneko:20190718130145p:plain:w320

じゃっかん太さにムラがあるけど、ボーダーを細めにすれば気にならないレベルかなと思います。

個人的にはAndroidSDKで作るより簡単だったように感じました。