Flutterでfirebase+Blocなアプリケーションを作る(その2)
やること
Flutter入門としてサンプルを使ったデモアプリを作成します。
はじめに
前回からの変更点
前回flutter_blocというライブラリを使ってデモアプリを動かしてみたのですが、かなりReduxよりのアーキテクチャになっていて、 例えばFirestoreのSnapshotを参照したりすると結構面倒な実装になりました。。。
(blocクラスとのIFがEvent/Stateになっていて直接Widget側にはStreamを渡せない) hnak.hatenablog.jp
複雑な状態管理を行うのであればいいのかも知れませんが、簡単なアプリを作るにはやりすぎな気もするので シンプルなbloc_providerのライブラリをベースに作り直すことにしました。
こちらのライブラリを使わせていただきました、今回のデモアプリもexample/complexをベースしています。 pub.dev
作ったもの
Githubにあげてます。 github.com
Firebase ログインの実装フロー
Step1. ログインボタンの呼び出し
lib/screens/login/login_form.dart
このクラスでログイン画面を構成しています。
下の方でGoogleLoginButtonのWidgetを作っています。
※通常のログインは適当実装なので動くかわかりません、Googleログインを使ってください。
lib/screens/login/google_login_button.dart このクラスでGoogleログインボタンを作り、ボタンを押すことでloginBlocのSinkにデータが流れます。
onPressed: () { loginBloc.googleLoginSink.add(null); },
Step2. ログインのビジネスロジック処理
lib/bloc/login/login_bloc.dart
ここではログインの状態管理とGoogleログインの実行を行なっています。
signInWithGoogleが成功するとログイン状態(LoginState)がsuccessになります。
_googleLoginController.stream.listen((onData) async { try { _loginState.add(LoginState.loading()); await userRepository.signOut(); await userRepository.signInWithGoogle(); _loginState.add(LoginState.success()); } catch (_) { _loginState.add(LoginState.failure()); } });
Step3. ログインの状態管理と画面遷移
lib/bloc/login/login_state.dart
LoginStateという状態管理用のクラスを定義していますが、flutter_blocライブラリのStateクラスではなくてStreamを流れるただのImmutableなオブジェクトです。
LoginFormではこのLoginStateの状態を監視していて状態が変わることで処理が動くようになっています。
初期化処理の中でLoginStateがSuccessになると別の画面へ遷移するようにしています。
@override void initState() { super.initState(); loginBloc = LoginProvider.of(context); loginBloc.loginStateStream.listen((onData) { if (onData.isSuccess) { Navigator.of(context).pushNamed(ProductScreen.routeName); } }); _emailController.addListener(_onEmailChanged); _passwordController.addListener(_onPasswordChanged); }
このLoginStateは画面コンポーネントのバリデーションにも使用しており、例えばパスワードのInputでは以下のように処理しています。
少しわかりにくいですが、bulderに記述されたsnapshotの中身は、streamで記載されたloginStateStreamから渡されるオブジェクトになります。
なので snapshot.data.isPasswordValid は LoginStateのisPasswordValidを呼び出すことになります。
StreamBuilder<LoginState>( stream: loginBloc.loginStateStream, initialData: loginBloc.loginStateStream.value, builder: (context, snapshot) => TextFormField( controller: _passwordController, decoration: const InputDecoration( icon: Icon(Icons.lock), labelText: 'Password', ), obscureText: true, autovalidate: true, autocorrect: false, validator: (_) { return !snapshot.data.isPasswordValid ? 'Invalid Password' : null; }, ), ),
さいごに
こんな感じでBlocなアプリケーションにもFirebaseを組み込むことができました。
あんまりFlutterの勉強時間が取れていませんが、これからFlutterを使って色々なアプリを作ってみたいと思います!
Flutterでfirebase+Blocなアプリケーションを作る(その1)
やること
Flutter入門としてサンプルを使ったデモアプリを作成します。
はじめに
Flutterとは
Googleが開発するクロスプラットフォームなモバイルアプリケーションSDK です。
iOS、Androidに対応していましたが、2019年5月にウェブ版のFlutter for Web(旧 hummingbird)が正式発表されました。
他のクロスプラットフォームとの大きな違いとして、独自のレンダリングエンジンを使用してUIを構築することが挙げられます。
このためiOS、Android間でも同じようなUIを構成することができます。
これまでのクラスプラットフォームの経験としてはCordova、期待しているのはNativeScript or Flutterですが、それぞれ比較してみると
- vs Cordova: WebViewを挟まず独自レンダリングできるのでパフォーマンスが良い。 というかもうCordovaは使いたくない :P
- vs NativeScript: UIを揃えられる分、プラグイン周りはObjective-C, Javaでのコーディングが必要になる(自作する場合)。あとまだプレビュー版ですがFlutterではウェブ版も作れる。
Blocとは
Bloc(Business Logic Component)の意味でGoogle Adwardsチームが発表したデザインパターンです。
ビジネスロジックをプレゼンテーションから分離し、以下のようなルールを定めるというものです。 (現状はDart自体がFlutter開発のための言語のようなものなのでBlocの実装ライブラリはFluttter用に開発されてるような感じです。)
- Inputs and outputs are simple Streams/Sinks only
- Dependencies must be injectable and platform agnostic
- No platform branching allowed
- Implementation can be whatever you want if you follow the previous rules
この資料が概要を理解するのに分かりやすかったのでリンクを貼っておきます。 slides.com
注意
その2ではライブラリを今回使用しているflutter_blocから変更しています。flutter_blocが不要であればこの記事は飛ばしてください。 hnak.hatenablog.jp
手順
0. 開発環境のセットアップ
FlutterSDK自体のセットアップについては割愛します。公式のチュートリアル通りに進めて問題ないはずです。
1. サンプルのチェックアウト
今回はBlocライブラリのレポジトリにあるFlutterのfirebaseサンプルを起動させてみたいと思います。
以下レポジトリをクローンします。
github.com
bloc/examples/flutter_firebase_login/ 下にfirebase loginを使ったサンプルソースがあります、作業を続ける前にFirebaseのセットアップをしておきます。
2. Firebaseのセットアップ
Firebaseで適当なプロジェクトを作成してiOSアプリの追加をします。
情報を埋めて新規iOSアプリの追加を行います、この時画面の指示に合わせてGoogleService-Info.plistをダウンロードします。
firebase authentificationの設定からGoogleログインをONにしてください。
3. サンプルの編集
bloc/examples/flutter_firebase_login/ios/Runner/GoogleService-Info.plist のファイルをダウンロードしたファイルで上書きしてください。
同じディレクトリにあるInfo.plistを開いて「paste-the-reversed-client-id-from-googleservice-info.plist-here」と記載されている場所にGoogleService-Info.plist内に記載されたREVERSED_CLIENT_IDの内容を上書きしてください。
4.サンプルの起動
以下コマンドでサンプルが起動するはずです。
# サンプルディレクトリに移動 $ cd bloc/examples/flutter_firebase_login # アプリを起動 $ flutter run
このソースを参考に独自にアプリを構築する場合はbloc/examples/flutter_firebase_loginを切り出すような形になると思うので、その場合はBlocライブラリのパスをローカルの相対パスからライブラリ参照に変更してください。
bloc/examples/flutter_firebase_login/pubspec.yaml
flutter_bloc: path: ../../packages/flutter_bloc
↓以下に変更
flutter_bloc: ^0.13.0
【Vue.js】Buefyでvuelidateを使ってバリデーション
やること
Vueのアプリ開発にBuefyというBulmaベースのライブラリを使おうかと考えていますが、 フォームごとや複雑なバリデーションを想定するとデフォルト機能のバリデーションだと心もとないなので Vuelidateというバリデーション用のライブラリと組み合わせて使ってみます。
事前準備
vue-cli 3.0 の環境で作りました。
vue create my-app
Buefyのインストール
yarn add buefy
Vuelidateのインストール
yarn add vuelidate
main.js
import Vue from 'vue'; import Buefy from 'buefy'; import 'buefy/dist/buefy.css'; import Vuelidate from 'vuelidate'; Vue.use(Buefy); Vue.use(Vuelidate);
バリデーションを掛けるフォーム画面の作成
ログインページの想定で作ってみます。 (全量は下にあります。パスワードコンポーネントはbuefyデフォルトのものになっています)
Buefyのb-inputタグのカスタマイズ
ここでは必須(required)属性をサンプルとして実装してみます。
- デフォルトのバリデーション属性は使わないので、requiredは外します。
- 代わりに@blurでblur時のイベント発火(vuelidateのバリデーション起動)をセットします。
- b-fieldにはvuelidateのerror条件判定でtype,messageが表示されるように設定します。
- スクリプトの方はloginIdのバリデーション定義をいれています。
- vue-property-decoratorというライブラリを使っているのでそのまま使う場合はyarn等でインストールしてください。
<b-field :type="$v.loginId.$error ? 'is-danger': ''" :message="$v.loginId.$error ? 'this is validation message' : ''" > <b-input ref="loginId" v-model="loginId" placeholder="ログインID" @blur="$v.loginId.$touch" /> </b-field> 〜省略〜 <script> import { Component, Vue } from 'vue-property-decorator'; import { validationMixin } from 'vuelidate'; import { required, maxLength } from 'vuelidate/lib/validators'; @Component({ mixins: [validationMixin], validations: { loginId: { required, minLength: maxLength(30) }, password: { required, minLength: maxLength(30) } } }) export default class Login extends Vue { loginId = ''; password = ''; error = false; 〜省略〜
まとめ
こんな感じでVuelidateと連携できるはずです!
Vuelidateだとthis.$v.$touch()を使ってのフォーム全体のバリデーション発火やthis.$v.$errorでの状態チェックが便利ですね!
Login.vue
<template> <section class="hero is-fullheight is-primary is-bold"> <div class="hero-body"> <div class="container has-text-centered"> <h1 class="title">TITLE</h1> <p class="subtitle">SUB</p> <div class="column is-4 is-offset-4"> <div class="box"> <b-field :type="$v.loginId.$error ? 'is-danger': ''" :message="$v.loginId.$error ? 'this is validation message' : ''" > <b-input ref="loginId" v-model="loginId" placeholder="ログインID" maxlength="30" @blur="$v.loginId.$touch" /> </b-field> <b-field> <b-input ref="password" v-model="password" placeholder="パスワード" required type="password" maxlength="30" password-reveal/> </b-field> <a :class="{ 'is-loading': loading }" class="button is-fullwidth is-primary" type="submit" @click="hoge()">LOGIN</a> </div> </div> </div> </div> </section> </template> <script> import { Component, Vue } from 'vue-property-decorator'; import { validationMixin } from 'vuelidate'; import { required, maxLength } from 'vuelidate/lib/validators'; @Component({ mixins: [validationMixin], validations: { loginId: { required, minLength: maxLength(30) }, password: { required, minLength: maxLength(30) } } }) export default class Login extends Vue { loginId = ''; password = ''; error = false; loading = false; hoge() { this.$v.$touch(); } } </script>
Vue + Spring SecurityでJWT認証を使う
やること
フロントエンドをVue、バックエンドをSpringで構成したアプリケーションで、JWTによる認証を行う
やりかた
バックエンド側の設定
以下の記事によくまとまっていたので参考にしました。
VueでAPIを呼び出した際にCORSで弾かれてしまったので、WebSecurityConfigに以下設定を追加しています。
フロントエンドをdev serverで起動しているので動作確認しているのでそのせいかも?
フロントエンド側でヘッダーに格納されるAuthorization情報が取得できなかったので expose 設定に追加しています。
それ以外は一般的なCORS設定です。
@EnableWebSecurity public class WebSecurityConfig extends WebSecurityConfigurerAdapter { // 省略 @Override protected void configure(HttpSecurity http) throws Exception { http .cors().configurationSource(this.corsConfigurationSource()) // 省略 } private CorsConfigurationSource corsConfigurationSource() { CorsConfiguration corsConfiguration = new CorsConfiguration(); corsConfiguration.addAllowedMethod(CorsConfiguration.ALL); corsConfiguration.addAllowedHeader(CorsConfiguration.ALL); corsConfiguration.addExposedHeader("Authorization"); corsConfiguration.addAllowedOrigin("http://hogehoge.com"); corsConfiguration.setAllowCredentials(true); UrlBasedCorsConfigurationSource corsSource = new UrlBasedCorsConfigurationSource(); corsSource.registerCorsConfiguration("/**", corsConfiguration); return corsSource; } }
フロントエンドの設定
ログイン処理の中でヘッダー情報のトークンをvuexのストアに保存します。
axiosの実装は下のJavascriptファイルを参考にしてください。
vuexへの保存は一般的な実装なので省略します。
// ログイン処理を行うメソッド内 const res = await HttpClient.getAxios().post('/api/login', { "userName" : this.loginId, "password" : this.password } ); const jwt: string = res.headers.authorization; this.$store.commit('saveJwt', jwt); // 省略
HTTP通信をするのにaxiosというモジュールを使っているので、これを介してHTTPクライアントを作成するときに
ヘッダー情報がセットされるようにしています。(ログインで保存しておいたvuexのストアから取得)
※初回リクエストとなるloginではjwtは空になりますが、サーバー側でloginAPIのみトークン認証がかからないようにしています。
import axios from 'axios' import store from 'store/index'; const URL = 'http://localhost:8081'; const instance = axios.create({ baseURL: URL, timeout: 10000, headers: { 'Content-Type': 'application/json' } }); export default class HttpClient { public static getAxios() { instance.defaults.headers.common['Authorization'] = 'Bearer ' + store.getters.jwt; return instance; } }
残作業
セッションのような状態を管理しないので、多重ログイン制御がこれ単体ではできない。
セッションの場合はRedisを使っていたので同じようにRedisに有効なJWTを保存してバリデーションをかける。
MacでDockerコンテナからホストにつなぐ方法
やること
MacでDockerコンテナからホストにつなぐ方法がわからないので調べる
やりかた
結論
ホスト名に host.docker.internal を指定する。
調べたメモ
バージョンによって使用できる方法が変わるようですが、バージョン18.03からは上記の方法が使えるようです。
「I WANT TO CONNECT FROM A CONTAINER TO A SERVICE ON THE HOST」のところをみてください。
仮想通貨をマイニングする その3
やること
マイニング1ヶ月間の結果発表
マイニング結果
約7000円の利益でした!
詳細
掘れたZCASHは0.5ZECくらいでした。
電気代は560Wくらいで落ち着いていたので19円/kWで計算すると、
0.56 * 19 * 24 * 30 = 7660.8円
今日のレート(29,511ZEC/JPY)で以下の感じなので、
0.5 ZEC = 14755.71 円
14755.71 - 7660.8 = 7094.91円 となりました。