SlideShare a Scribd company logo
1 of 31
Download to read offline
Flutter for Web で遊んでみよう。
CSS も HTML も使わない
Webアプリ開発
完全に理解した人達の
「Web技術」Talk #3
1
自己紹介
名前
robo (兼高理恵)
好きなもの
モバイル端末
おしごと
アプリの設計から実装まで
所属
GDG Kyoto(staff) WTM Kyoto(staff)
2
サンプルWebアプリ紹介
東京都 新型コロナウイルス感染症 対策サイト の
最新検査状況を表示する Web アプリを作ってみました。
3
ホスティングしていないので、
ローカルサーバで動作させて
います。
4
検証ツールで中身を見ると、
index.html の中身は、
スカスカですね。
矢印で記した、my_app.dart …etc が
この Webアプリ の本体なのです。
実はアプリ作成に、HTMLも
CSSも触っていません。
Webアプリは、Flutter for Web で作成しました
Flutter for Web は、dart という型安全な言語で、
画面レイアウトやUI のみならず JSONフェッチなどの機能も
全てコードで作成します。
dart コードは、最終的に JavaScript にトランスパイルされ、
CSS も HTML も触ることなく、Web アプリが作れます。
ただし現在ベータ版なことは、御了承ください。
5
はじめに
このLTは、Flutter 未体験者や初学者向けです。
● Flutter は、どんなものなのかな?
● Flutter を、触ってみようかな?(でも開発環境インストールや設定が …)
そんなあなたに、
サンプルWebアプリの概要について紹介し、
一人でも手軽に Flutter 体験できるシェルスクリプト flexp を紹介します。
Fluttr ⇒ iOS/Android/Web アプリが作れるフレームワーク
6
Flutter for Web でのプログラミング
Webアプリを作るためのプログラミングは、
Flutter だとどのように変わってくれるのか紹介します。
まずは、画面作成から。
7
画面作成の全ソース(1/4)
8
import 'package:flutter/material.dart';
import 'tokyo_stop_covid19.dart';
PositiveSituation jsonModel;
Future<void> main() async {
jsonModel = await TokyoStopCovid19.createPositiveSituation();
runApp(MyApp());
}
class MyApp extends StatelessWidget {
@override
Widget build(BuildContext context) {
return MaterialApp(
theme: ThemeData(
primarySwatch: Colors.blue,
),
home: const MyPage(),
);
}
}
class MyPage extends StatelessWidget {
const MyPage({Key key}) : super(key: key);
Widget _customText(String label, {double fontSize = 12.0,
FontWeight fontWeight = FontWeight.normal}) {
return Expanded(
child: Container(
alignment: Alignment.center,
margin: const EdgeInsets.all(4.0),
child: Text('$label', style: TextStyle(fontSize:
fontSize, fontWeight: fontWeight))
),
);
}
画面を作る my_app.dart の全ソースは、
4ページ分しかありません。
この後、全ソースのダウンロード先を紹介します。
画面作成の全ソース(2/4)
9
@override
Widget build(BuildContext context) {
print('jsonModel=$jsonModel');
return Scaffold(
backgroundColor: Colors.white70,
body: Center(
child: Container(
decoration: BoxDecoration(
boxShadow: [
BoxShadow(
color: Colors.black54,
spreadRadius: 1.0,
blurRadius: 10.0,
offset: Offset(10, 10),
),
],
),
child:Card(
color: Colors.white,
child: Container(
上の画面は、
この4ページ分のコードで仕上がります。
画面作成の全ソース(3/4)
10
child:Card(
color: Colors.white,
child: Container(
width: 500.0,
margin: EdgeInsets.fromLTRB(10.0, 10.0, 10.0, 0.0),
child:Column(
mainAxisSize: MainAxisSize.min,
children:[
Container(
alignment: Alignment.center,
margin: const EdgeInsets.all(4.0),
child: Text('東京都 検査陽性者の状況', style: TextStyle(fontSize: 18.0, fontWeight: FontWeight.w600)),
),
Row(
mainAxisAlignment: MainAxisAlignment.center,
mainAxisSize: MainAxisSize.max,
children: <Widget>[
_customText('累計'),
_customText('入院中'),
_customText('軽症〜中等症'),
_customText('重症'),
_customText('死亡'),
_customText('退院'),
],
),
Divider(height: 14.0, color: Colors.black12),
Flutter には、
Material Design に準拠した
Widget と呼ばれる UI やレイアウトのコン
ポーネント・パーツが
たくさん用意されています。
画面作成の全ソース(4/4)
11
Divider(height: 14.0, color: Colors.black12),
Row(
mainAxisAlignment: MainAxisAlignment.center,
mainAxisSize: MainAxisSize.max,
children: <Widget>[
_customText(jsonModel?.cumulative.toString() ?? '0'),
_customText(jsonModel?.hospitalized.toString() ?? '0'),
_customText(jsonModel?.mild2moderate.toString() ?? '0'),
_customText(jsonModel?.severe.toString() ?? '0'),
_customText(jsonModel?.death.toString() ?? '0'),
_customText(jsonModel?.discharge.toString() ?? '0'),
],
),
Container(
alignment: Alignment.centerRight,
margin: const EdgeInsets.all(4.0),
child: Text('${jsonModel?.updateString() ?? DateTime.now()} 更新', style: TextStyle(fontSize: 14.0)),
),
],
),
),
),
),
),
);
}
}
_customText() (1ページめの定義参照) のように、
特定UIをこなしてくれる、独自のコンポーネントを
プログラムで定義することもできます。
Flutter for Web でのプログラミング
サンプルの Webアプリでは、最新の検査状況を
東京都 新型コロナウイルス感染症 対策サイトページから
スクレイピングして JSON データに変換しています。
続いて、HTML スクレイピング。
12
スクレイピングの全ソース(1/4)
13
import 'dart:convert'; // ignore:directives_ordering
import 'package:http/http.dart' as http;
/// 東京都 都内の最新感染動向 を表すクラス
class TokyoStopCovid19 {
/// HTML スクレイピングによる 検査陽性者状況 JSON 取得
static Future<Map<String, dynamic>> scrapingPositiveSituation() async {
http.Response response;
// 東京都 都内の最新幹線動向 サイト
const String host = ‘stopcovid19.metoro.tokyo.lg.jp';
const String path = '/';
const String url = 'https://' + host + path;
print('beore scrapingPositiveSituation() => request.url=$url');
response = await http.get(url);
print('after scrapingPositiveSituation() => request.url=$url');
if (response.statusCode != 200) {
throw new StateError('HTTP Response status code is ${response.statusCode}');
}
// 都内の最新幹線動向 ページの HTML body テキスト
final String htmlBody = utf8.decode(response.bodyBytes);
// 検査要請者の状況ブロックのみ HTMLテキスト抽出
東京都のページから、
最新の検査状況をスクレイピングする
tokyo_stop_covid19.dart の
全ソースは4ページ分だけです。
https://stopcovid19.metro.tokyo.lg.jp/ から、
HTML テキストを直接取得して、
スクレイピングしています。
スクレイピングの全ソース(2/4)
14
// 都内の最新幹線動向 ページの HTML body テキスト
final String htmlBody = utf8.decode(response.bodyBytes);
// 検査要請者の状況ブロックのみ HTMLテキスト抽出
bool isExtracting = false;
final String extracts = htmlBody.split(RegExp('n')).where(
(String line) {
if (line.contains(RegExp('検査陽性者の状況'))) isExtracting = true;
if (line.contains(RegExp('検査実施状況'))) isExtracting = false;
return isExtracting;
}
).toList().join();
// 種別〜人数ブロックのみ HTMLテキスト抽出
isExtracting = false;
List<String> contents = extracts.split(RegExp('> ?<')).where(
(String line) {
if (line.contains(RegExp('class="content_1gFZ-"'))) isExtracting = true;
if (line.contains(RegExp('class="unit_25C4Z"'))) isExtracting = false;
return isExtracting;
}
).join().split(RegExp('div class="content_1gFZ-"'));
// 更新日付ブロックのみ HTMLテキスト抽出
isExtracting = false;
String datetime = extracts.split(RegExp('> ?<')).where((String line) {
return line.contains(RegExp('time datetime='));
}).join();
文字が小さくて読めないと思いますので、
この後、全ソースのダウンロード先を紹介します。
Dart 言語は、
JavaScript(ES2015)に似ています。
class や async / await、アロー関数、
コレクションのストリーム処理などが
使えます。
スクレイピングの全ソース(3/4)
15
// 種別〜人数+更新日付ブロックのみ HTMLテキスト
contents.add(datetime);
// 抽出 HTML テキストから JSON を生成する。
final Map<String, dynamic> json = contents.map<Map<String,dynamic>>(
(String content) {
Map<String, dynamic> el;
if (content.contains(RegExp('累計'))) el = {'cumulative': _extractNum(content)};
if (content.contains(RegExp('入院'))) el = {'hospitalized': _extractNum(content)};
if (content.contains(RegExp('軽症'))) el = {'mild2moderate': _extractNum(content)};
if (content.contains(RegExp('重症'))) el = {'severe': _extractNum(content)};
if (content.contains(RegExp('死亡'))) el = {'death': _extractNum(content)};
if (content.contains(RegExp('退院'))) el = {'discharge': _extractNum(content)};
if (content.contains(RegExp('更新'))) el = {'updatetime': _extractDatetime(content)};
return el;
}
).reduce(
(Map<String, dynamic> prev, Map<String, dynamic> curr) {
curr.addAll(prev ?? {});
return curr;
}
);
print('json=$json');
return json;
}
/// 数値抽出関数
スクレイピングした HTML テキストから、
JSON データ ⇒ Map<String, dynamic> に
変換しています。
スクレイピングの全ソース(4/4)
16
/// 数値抽出関数
static int _extractNum(String content) {
final String num = content
.replaceFirst(RegExp('^[^0-9]*'), '')
.replaceFirst(RegExp('[^0-9]*$'), '');
return int.parse(num);
}
/// 日付抽出関数
static DateTime _extractDatetime(String content) {
final String date = content
.replaceFirst(RegExp('^time datetime="'), '')
.replaceFirst(RegExp('".*$'), '');
return DateTime.parse(date);
}
}
こちらは、内部で利用する、
ユーティリティ関数となります。
全体的に JavaScript に似かよったプログラムになっています。
スクレイピングの補足
17
検査陽性者の状況</h3> </div> <div class="DataView-Description"></div>
<div><p class="note_3ycFr">(注)チャーター機帰国者、クルーズ船乗客等は含まれていない
</p></div> <div class="DataView-CardText"> <ul aria-label="検査陽性者の状況"
class="container_37WEO"><li class="box_2dey9 parent_23pyN confirmed_P-yFc">
<div class="pillar_F62Yl"><div class="content_1gFZ-"><span>陽性者数<br>(累計)</span>
<span><strong>154</strong> <span class="unit_25C4Z">人</span></span></div></div>
<ul class="group_1ksdS"><li class="box_2dey9 parent_23pyN hospitalized_3w7PT">
<div class="pillar_F62Yl"><div class="content_1gFZ-"><span>入院中</span>
<span><strong>119</strong> <span class="unit_25C4Z">人</span></span></div></div>
<ul class="group_1ksdS"><li class="box_2dey9 minor_1RUXC"><div class="pillar_F62Yl">
〜 省略 〜
<li class="box_2dey9 recovered_JzYjB"><div class="pillar_F62Yl"><div
class="content_1gFZ-"><span>退院</span> <span><strong>31</strong>
<span class="unit_25C4Z">人</span></span>
</div></div></li></ul></li></ul></div> <div class="DataView-Description"></div>
〜 省略 〜
<div class="DataView-Footer"><div class="Footer-Left"><div><!----></div> <div>
<a href="/cards/details-of-confirmed-cases" class="Permalink">
<time datetime="2020-03-23T21:15:00">2020/03/23 21:15 更新</time></a></div></div>
<div class="Footer-Right"><button class="DataView-Share-Opener">
<svg width="14" height="16" viewBox="0 0 14 16" fill="none"
xmlns="http://www.w3.org/2000/svg"
role="img" aria-label="検査陽性者の状況のグラフをシェア">
〜 省略 〜
<div class="DataCard col-md-6 col-12" data-v-59bfe8e7>
<div class="DataView v-card v-sheet theme--light"><div class="DataView-Inner">
<div class="DataView-Header"><h3 class="DataView-Title">
検査陽性者の状況 部から抽出した
HTML テキスト
<div class="content_1gFZ-">
<span>入院中</span>
<span>
<strong>119</strong>
<span class="unit_25C4Z">人</span>
</span>
</div>
3月23日のコンテンツでは、
種別ごとの DIV タグには、
class="content_1gFZ-" が割り当てられ、
人数を表す SPAN タグには、
class="unit_25C4Z" が割り当てられている
…ことを利用して抽出しています。
お詫び
dart言語は、最終的に JavaScript に変換されるため、
外部サイト(東京都サイト)からの HTML テキスト取得は、
サイトをまたがる CORS ポリシー違反になります。
このためサンプル Web アプリは、
ブラウザの CORSポリシーを無効 にしなければ、
実行できません。
18
Flutter での Webアプリ開発
画面のレイアウトや UI を全てコードで記述することや
Dart言語は、JavaScript とあまり変わらないことを
確認いただけましたでしょうか。  
Flutter 公式資料
Flutter for web developers ⇒ CSS や HTML と Flutter コードの違い説明
https://flutter.dev/docs/get-started/flutter-for/web-devs
Building a web application with Flutter ⇒ Flutter での Webアプリ作成手順
https://flutter.dev/docs/get-started/web
19
実際に触ってみる
Flutter を使った Webアプリ開発は、習うより慣れろで、
実際にコードを確認して動かしてみることが早道でしょう。
手軽に Flutter 体験できるシェルスクリプト flexp を使って、
Flutter SDK の簡単インストールと テンプレートプロジェクト作成、
サンプル全コードのダウンロードおよびテンプレートコードとの差替え、
Webアプリのビルド、ローカルサーバを使った Webアプリ配信、
CORS ポリシーを無効化した Chrome での動作確認の手順を紹介します。
flexp には、インストールした Flutter SDK のアンインストール機能も含まれています。
20
flexp / flutter 体験シェルスクリプト紹介
 bash シェルスクリプト flexp ファイルは、
 Flutter未体験の方に、Flutter を簡易に体験してもらえるよう、
 SDK インストールやアプリの実行を提供するスクリプトです。
 注意1)git コマンドが実行できることが必須要件となります。
 注意2)SDK インストールには、700MB のディスクスペースが必要です。
 flexp ⇒ flutter experimence シェルスクリプト・ダウンロード先
 https://drive.google.com/open?id=1eLGvMXMje5t6I1-N-x34q-rh3vcgakFI
 flexp 紹介ドキュメント ⇒ Flutter を体験してみよう・ダウンロード先
 https://drive.google.com/open?id=1XbHAhfxg_9N71aGmU2PFu9u6aqXoAxyZCg6Oq_Uwp2E
21
サンプル Web アプリコード一式のダウンロード
 flutter_for_web_scraping_sample.zip ⇒ サンプル一式・ダウンロード先
 https://drive.google.com/open?id=14uKhm2_9vvk-VMPcc2Yt_gjR5kTZCyst
22
flutter_for_web_scraping_sample
|
+experience (プロジェクトディレクトリ )
| |
| +pubspec.yaml
| |
| +lib
| +main.dart
| +my_app.dart
| +tokyo_stop_covid19.dart
|
+flexp
|
+Flutterを体験してみよう .pdf
テンプレートプロジェクトと差し替える、
サンプル Web アプリのコードディレクトリ
pubspec.yaml と lib ディレクトリを
テンプレートのファイルに上書します。
ZIPファイルは、
右図のファイル構成に展開されます。
flexp や Flutterを体験してみよう は、
こちらにも含まれています。
サンプル Webアプリの実行手順(1/2)
1. ダウンロードした flexp を自分のホームディレクトリに配置する。
ターミナルを開き、ホームディレクトリで以下を実行します。
2. chmod +x flexp ⇒ 実行権限を付与する。
3. ./flexp -install ⇒ Flutter SDK をインストールする。
ホームディレクトリに flutter_experience ディレクトリが生成される。
4. flexp -create ⇒ プロジェクトテンプレートを新規生成する。
flutter_experience に experience ディレクトリ(プロジェクトテンプレート)が生成される。
5. プロジェクトテンプレートの experience ディレクトリに、
flutter_for_web_scraping_sample の experience フォルダを上書きコピーする。
pubspec.yaml、lib/main.dart、lib/my_app.dart、lib/tokyo_stop_covid19.dart 一式をコピーする。
6. flexp -build ⇒ プロジェクトをビルドする。
23
サンプル Webアプリの実行手順(2/2)
7. CORS 無効にした Chrome を起動するため、以下を実行する。 (実際のコマンドは、一行です)
MacOS の場合
open -n -a /Applications/Google Chrome.app/Contents/MacOS/Google Chrome --args
--user-data-dir="/tmp/chrome_dev_test" --disable-web-security
Windows 10 の場合
"C:Program Files (x86)GoogleChromeApplicationchrome.exe" --disable-web-security
--disable-gpu --user-data-dir=~/chromeTemp
Linux の場合
google-chrome --user-data-dir="/tmp/chrome_dev_test" --disable-web-security
8. flexp -server ⇒ ローカルサーバを起動する。
9. CORS 無効の Chorme ブラウザで、Webアプリ URL(http://サーバ IPアドレス:8080/index.html )を開く
flexp の詳しい使い方は、Flutterを体験してみよう.pdf を参照ください。
24
実際の操作状況例
25
26
27
検証ツールでコンソールを確認すれば、
画面表示時にスクレイピングが行われて、
検査状況より JSON データを作っていることが
確認できます。
Flutter や Dart を勉強してみたいかたは
● flutter アプリは、Dart言語を使ってプログラミングします。
Dart言語については、公式サイトのサンプルなどで勉強できます。
Dart言語公式サイト ⇒ https://dart.dev
Dart言語仕様ツアー ⇒ https://dart.dev/guides/language/language-tour
28
Flutter や Dart を勉強してみたいかたは
● flutter フレームワークについては、公式サイトを覗いてみましょう。
flutter公式サイト ⇒ https://flutter.dev/
Get Started ⇒ https://flutter.dev/docs/get-started/
Learn More ⇒ https://flutter.dev/docs/get-started/learn-more
29
Flutter や Dart を勉強してみる
● 英語が苦手でしたら、Flutter 入門書籍も販売されていますよ。
30
31
ご清聴、
ありがとうございました。
情報修正があれば更新します
最新版スライドは こちらから

More Related Content

More from cch-robo

明示的アニメで、Flutterアニメーション入門
明示的アニメで、Flutterアニメーション入門明示的アニメで、Flutterアニメーション入門
明示的アニメで、Flutterアニメーション入門cch-robo
 
DartPad+CodePenで、Flutterを体験してみよう
DartPad+CodePenで、Flutterを体験してみようDartPad+CodePenで、Flutterを体験してみよう
DartPad+CodePenで、Flutterを体験してみようcch-robo
 
Dartでサーバレスサービス
DartでサーバレスサービスDartでサーバレスサービス
Dartでサーバレスサービスcch-robo
 
Pin-point rebuildable and non-rebuild custom widget
Pin-point rebuildable and non-rebuild custom widgetPin-point rebuildable and non-rebuild custom widget
Pin-point rebuildable and non-rebuild custom widgetcch-robo
 
FlutterでのWidgetツリーへの状態伝播とアクセス制限の基本戦略
FlutterでのWidgetツリーへの状態伝播とアクセス制限の基本戦略FlutterでのWidgetツリーへの状態伝播とアクセス制限の基本戦略
FlutterでのWidgetツリーへの状態伝播とアクセス制限の基本戦略cch-robo
 
Before lunch オプションを使って Flutterでstaging/release環境を切り替える
Before lunch オプションを使って Flutterでstaging/release環境を切り替えるBefore lunch オプションを使って Flutterでstaging/release環境を切り替える
Before lunch オプションを使って Flutterでstaging/release環境を切り替えるcch-robo
 
Flutter のリアクティブ戦略 set state 〜 redux まで
Flutter のリアクティブ戦略 set state 〜 redux までFlutter のリアクティブ戦略 set state 〜 redux まで
Flutter のリアクティブ戦略 set state 〜 redux までcch-robo
 
Android lint-srp-practice
Android lint-srp-practiceAndroid lint-srp-practice
Android lint-srp-practicecch-robo
 
Loose and fluffy_ddd_intro
Loose and fluffy_ddd_introLoose and fluffy_ddd_intro
Loose and fluffy_ddd_introcch-robo
 
Firebase Test Lab 無料枠を使ってみました。
Firebase Test Lab 無料枠を使ってみました。Firebase Test Lab 無料枠を使ってみました。
Firebase Test Lab 無料枠を使ってみました。cch-robo
 
ZTE OPEN を日本語化(バージョンアップ)してみる
ZTE OPEN を日本語化(バージョンアップ)してみるZTE OPEN を日本語化(バージョンアップ)してみる
ZTE OPEN を日本語化(バージョンアップ)してみるcch-robo
 
FirefoxOSで学ぶJavaScript作法
FirefoxOSで学ぶJavaScript作法FirefoxOSで学ぶJavaScript作法
FirefoxOSで学ぶJavaScript作法cch-robo
 

More from cch-robo (12)

明示的アニメで、Flutterアニメーション入門
明示的アニメで、Flutterアニメーション入門明示的アニメで、Flutterアニメーション入門
明示的アニメで、Flutterアニメーション入門
 
DartPad+CodePenで、Flutterを体験してみよう
DartPad+CodePenで、Flutterを体験してみようDartPad+CodePenで、Flutterを体験してみよう
DartPad+CodePenで、Flutterを体験してみよう
 
Dartでサーバレスサービス
DartでサーバレスサービスDartでサーバレスサービス
Dartでサーバレスサービス
 
Pin-point rebuildable and non-rebuild custom widget
Pin-point rebuildable and non-rebuild custom widgetPin-point rebuildable and non-rebuild custom widget
Pin-point rebuildable and non-rebuild custom widget
 
FlutterでのWidgetツリーへの状態伝播とアクセス制限の基本戦略
FlutterでのWidgetツリーへの状態伝播とアクセス制限の基本戦略FlutterでのWidgetツリーへの状態伝播とアクセス制限の基本戦略
FlutterでのWidgetツリーへの状態伝播とアクセス制限の基本戦略
 
Before lunch オプションを使って Flutterでstaging/release環境を切り替える
Before lunch オプションを使って Flutterでstaging/release環境を切り替えるBefore lunch オプションを使って Flutterでstaging/release環境を切り替える
Before lunch オプションを使って Flutterでstaging/release環境を切り替える
 
Flutter のリアクティブ戦略 set state 〜 redux まで
Flutter のリアクティブ戦略 set state 〜 redux までFlutter のリアクティブ戦略 set state 〜 redux まで
Flutter のリアクティブ戦略 set state 〜 redux まで
 
Android lint-srp-practice
Android lint-srp-practiceAndroid lint-srp-practice
Android lint-srp-practice
 
Loose and fluffy_ddd_intro
Loose and fluffy_ddd_introLoose and fluffy_ddd_intro
Loose and fluffy_ddd_intro
 
Firebase Test Lab 無料枠を使ってみました。
Firebase Test Lab 無料枠を使ってみました。Firebase Test Lab 無料枠を使ってみました。
Firebase Test Lab 無料枠を使ってみました。
 
ZTE OPEN を日本語化(バージョンアップ)してみる
ZTE OPEN を日本語化(バージョンアップ)してみるZTE OPEN を日本語化(バージョンアップ)してみる
ZTE OPEN を日本語化(バージョンアップ)してみる
 
FirefoxOSで学ぶJavaScript作法
FirefoxOSで学ぶJavaScript作法FirefoxOSで学ぶJavaScript作法
FirefoxOSで学ぶJavaScript作法
 

Recently uploaded

IoT in the era of generative AI, Thanks IoT ALGYAN.pptx
IoT in the era of generative AI, Thanks IoT ALGYAN.pptxIoT in the era of generative AI, Thanks IoT ALGYAN.pptx
IoT in the era of generative AI, Thanks IoT ALGYAN.pptxAtomu Hidaka
 
20240412_HCCJP での Windows Server 2025 Active Directory
20240412_HCCJP での Windows Server 2025 Active Directory20240412_HCCJP での Windows Server 2025 Active Directory
20240412_HCCJP での Windows Server 2025 Active Directoryosamut
 
PHP-Conference-Odawara-2024-04-000000000
PHP-Conference-Odawara-2024-04-000000000PHP-Conference-Odawara-2024-04-000000000
PHP-Conference-Odawara-2024-04-000000000Shota Ito
 
スマートフォンを用いた新生児あやし動作の教示システム
スマートフォンを用いた新生児あやし動作の教示システムスマートフォンを用いた新生児あやし動作の教示システム
スマートフォンを用いた新生児あやし動作の教示システムsugiuralab
 
Postman LT Fukuoka_Quick Prototype_By Daniel
Postman LT Fukuoka_Quick Prototype_By DanielPostman LT Fukuoka_Quick Prototype_By Daniel
Postman LT Fukuoka_Quick Prototype_By Danieldanielhu54
 
UPWARD_share_company_information_20240415.pdf
UPWARD_share_company_information_20240415.pdfUPWARD_share_company_information_20240415.pdf
UPWARD_share_company_information_20240415.pdffurutsuka
 
Amazon SES を勉強してみる その12024/04/12の勉強会で発表されたものです。
Amazon SES を勉強してみる その12024/04/12の勉強会で発表されたものです。Amazon SES を勉強してみる その12024/04/12の勉強会で発表されたものです。
Amazon SES を勉強してみる その12024/04/12の勉強会で発表されたものです。iPride Co., Ltd.
 
[DevOpsDays Tokyo 2024] 〜デジタルとアナログのはざまに〜 スマートビルディング爆速開発を支える 自動化テスト戦略
[DevOpsDays Tokyo 2024] 〜デジタルとアナログのはざまに〜 スマートビルディング爆速開発を支える 自動化テスト戦略[DevOpsDays Tokyo 2024] 〜デジタルとアナログのはざまに〜 スマートビルディング爆速開発を支える 自動化テスト戦略
[DevOpsDays Tokyo 2024] 〜デジタルとアナログのはざまに〜 スマートビルディング爆速開発を支える 自動化テスト戦略Ryo Sasaki
 
新人研修のまとめ 2024/04/12の勉強会で発表されたものです。
新人研修のまとめ       2024/04/12の勉強会で発表されたものです。新人研修のまとめ       2024/04/12の勉強会で発表されたものです。
新人研修のまとめ 2024/04/12の勉強会で発表されたものです。iPride Co., Ltd.
 

Recently uploaded (9)

IoT in the era of generative AI, Thanks IoT ALGYAN.pptx
IoT in the era of generative AI, Thanks IoT ALGYAN.pptxIoT in the era of generative AI, Thanks IoT ALGYAN.pptx
IoT in the era of generative AI, Thanks IoT ALGYAN.pptx
 
20240412_HCCJP での Windows Server 2025 Active Directory
20240412_HCCJP での Windows Server 2025 Active Directory20240412_HCCJP での Windows Server 2025 Active Directory
20240412_HCCJP での Windows Server 2025 Active Directory
 
PHP-Conference-Odawara-2024-04-000000000
PHP-Conference-Odawara-2024-04-000000000PHP-Conference-Odawara-2024-04-000000000
PHP-Conference-Odawara-2024-04-000000000
 
スマートフォンを用いた新生児あやし動作の教示システム
スマートフォンを用いた新生児あやし動作の教示システムスマートフォンを用いた新生児あやし動作の教示システム
スマートフォンを用いた新生児あやし動作の教示システム
 
Postman LT Fukuoka_Quick Prototype_By Daniel
Postman LT Fukuoka_Quick Prototype_By DanielPostman LT Fukuoka_Quick Prototype_By Daniel
Postman LT Fukuoka_Quick Prototype_By Daniel
 
UPWARD_share_company_information_20240415.pdf
UPWARD_share_company_information_20240415.pdfUPWARD_share_company_information_20240415.pdf
UPWARD_share_company_information_20240415.pdf
 
Amazon SES を勉強してみる その12024/04/12の勉強会で発表されたものです。
Amazon SES を勉強してみる その12024/04/12の勉強会で発表されたものです。Amazon SES を勉強してみる その12024/04/12の勉強会で発表されたものです。
Amazon SES を勉強してみる その12024/04/12の勉強会で発表されたものです。
 
[DevOpsDays Tokyo 2024] 〜デジタルとアナログのはざまに〜 スマートビルディング爆速開発を支える 自動化テスト戦略
[DevOpsDays Tokyo 2024] 〜デジタルとアナログのはざまに〜 スマートビルディング爆速開発を支える 自動化テスト戦略[DevOpsDays Tokyo 2024] 〜デジタルとアナログのはざまに〜 スマートビルディング爆速開発を支える 自動化テスト戦略
[DevOpsDays Tokyo 2024] 〜デジタルとアナログのはざまに〜 スマートビルディング爆速開発を支える 自動化テスト戦略
 
新人研修のまとめ 2024/04/12の勉強会で発表されたものです。
新人研修のまとめ       2024/04/12の勉強会で発表されたものです。新人研修のまとめ       2024/04/12の勉強会で発表されたものです。
新人研修のまとめ 2024/04/12の勉強会で発表されたものです。
 

Flutter for Web で遊んでみよう、CSSもHTMLも使わないWebアプリ開発

  • 1. Flutter for Web で遊んでみよう。 CSS も HTML も使わない Webアプリ開発 完全に理解した人達の 「Web技術」Talk #3 1
  • 3. サンプルWebアプリ紹介 東京都 新型コロナウイルス感染症 対策サイト の 最新検査状況を表示する Web アプリを作ってみました。 3 ホスティングしていないので、 ローカルサーバで動作させて います。
  • 4. 4 検証ツールで中身を見ると、 index.html の中身は、 スカスカですね。 矢印で記した、my_app.dart …etc が この Webアプリ の本体なのです。 実はアプリ作成に、HTMLも CSSも触っていません。
  • 5. Webアプリは、Flutter for Web で作成しました Flutter for Web は、dart という型安全な言語で、 画面レイアウトやUI のみならず JSONフェッチなどの機能も 全てコードで作成します。 dart コードは、最終的に JavaScript にトランスパイルされ、 CSS も HTML も触ることなく、Web アプリが作れます。 ただし現在ベータ版なことは、御了承ください。 5
  • 6. はじめに このLTは、Flutter 未体験者や初学者向けです。 ● Flutter は、どんなものなのかな? ● Flutter を、触ってみようかな?(でも開発環境インストールや設定が …) そんなあなたに、 サンプルWebアプリの概要について紹介し、 一人でも手軽に Flutter 体験できるシェルスクリプト flexp を紹介します。 Fluttr ⇒ iOS/Android/Web アプリが作れるフレームワーク 6
  • 7. Flutter for Web でのプログラミング Webアプリを作るためのプログラミングは、 Flutter だとどのように変わってくれるのか紹介します。 まずは、画面作成から。 7
  • 8. 画面作成の全ソース(1/4) 8 import 'package:flutter/material.dart'; import 'tokyo_stop_covid19.dart'; PositiveSituation jsonModel; Future<void> main() async { jsonModel = await TokyoStopCovid19.createPositiveSituation(); runApp(MyApp()); } class MyApp extends StatelessWidget { @override Widget build(BuildContext context) { return MaterialApp( theme: ThemeData( primarySwatch: Colors.blue, ), home: const MyPage(), ); } } class MyPage extends StatelessWidget { const MyPage({Key key}) : super(key: key); Widget _customText(String label, {double fontSize = 12.0, FontWeight fontWeight = FontWeight.normal}) { return Expanded( child: Container( alignment: Alignment.center, margin: const EdgeInsets.all(4.0), child: Text('$label', style: TextStyle(fontSize: fontSize, fontWeight: fontWeight)) ), ); } 画面を作る my_app.dart の全ソースは、 4ページ分しかありません。 この後、全ソースのダウンロード先を紹介します。
  • 9. 画面作成の全ソース(2/4) 9 @override Widget build(BuildContext context) { print('jsonModel=$jsonModel'); return Scaffold( backgroundColor: Colors.white70, body: Center( child: Container( decoration: BoxDecoration( boxShadow: [ BoxShadow( color: Colors.black54, spreadRadius: 1.0, blurRadius: 10.0, offset: Offset(10, 10), ), ], ), child:Card( color: Colors.white, child: Container( 上の画面は、 この4ページ分のコードで仕上がります。
  • 10. 画面作成の全ソース(3/4) 10 child:Card( color: Colors.white, child: Container( width: 500.0, margin: EdgeInsets.fromLTRB(10.0, 10.0, 10.0, 0.0), child:Column( mainAxisSize: MainAxisSize.min, children:[ Container( alignment: Alignment.center, margin: const EdgeInsets.all(4.0), child: Text('東京都 検査陽性者の状況', style: TextStyle(fontSize: 18.0, fontWeight: FontWeight.w600)), ), Row( mainAxisAlignment: MainAxisAlignment.center, mainAxisSize: MainAxisSize.max, children: <Widget>[ _customText('累計'), _customText('入院中'), _customText('軽症〜中等症'), _customText('重症'), _customText('死亡'), _customText('退院'), ], ), Divider(height: 14.0, color: Colors.black12), Flutter には、 Material Design に準拠した Widget と呼ばれる UI やレイアウトのコン ポーネント・パーツが たくさん用意されています。
  • 11. 画面作成の全ソース(4/4) 11 Divider(height: 14.0, color: Colors.black12), Row( mainAxisAlignment: MainAxisAlignment.center, mainAxisSize: MainAxisSize.max, children: <Widget>[ _customText(jsonModel?.cumulative.toString() ?? '0'), _customText(jsonModel?.hospitalized.toString() ?? '0'), _customText(jsonModel?.mild2moderate.toString() ?? '0'), _customText(jsonModel?.severe.toString() ?? '0'), _customText(jsonModel?.death.toString() ?? '0'), _customText(jsonModel?.discharge.toString() ?? '0'), ], ), Container( alignment: Alignment.centerRight, margin: const EdgeInsets.all(4.0), child: Text('${jsonModel?.updateString() ?? DateTime.now()} 更新', style: TextStyle(fontSize: 14.0)), ), ], ), ), ), ), ), ); } } _customText() (1ページめの定義参照) のように、 特定UIをこなしてくれる、独自のコンポーネントを プログラムで定義することもできます。
  • 12. Flutter for Web でのプログラミング サンプルの Webアプリでは、最新の検査状況を 東京都 新型コロナウイルス感染症 対策サイトページから スクレイピングして JSON データに変換しています。 続いて、HTML スクレイピング。 12
  • 13. スクレイピングの全ソース(1/4) 13 import 'dart:convert'; // ignore:directives_ordering import 'package:http/http.dart' as http; /// 東京都 都内の最新感染動向 を表すクラス class TokyoStopCovid19 { /// HTML スクレイピングによる 検査陽性者状況 JSON 取得 static Future<Map<String, dynamic>> scrapingPositiveSituation() async { http.Response response; // 東京都 都内の最新幹線動向 サイト const String host = ‘stopcovid19.metoro.tokyo.lg.jp'; const String path = '/'; const String url = 'https://' + host + path; print('beore scrapingPositiveSituation() => request.url=$url'); response = await http.get(url); print('after scrapingPositiveSituation() => request.url=$url'); if (response.statusCode != 200) { throw new StateError('HTTP Response status code is ${response.statusCode}'); } // 都内の最新幹線動向 ページの HTML body テキスト final String htmlBody = utf8.decode(response.bodyBytes); // 検査要請者の状況ブロックのみ HTMLテキスト抽出 東京都のページから、 最新の検査状況をスクレイピングする tokyo_stop_covid19.dart の 全ソースは4ページ分だけです。 https://stopcovid19.metro.tokyo.lg.jp/ から、 HTML テキストを直接取得して、 スクレイピングしています。
  • 14. スクレイピングの全ソース(2/4) 14 // 都内の最新幹線動向 ページの HTML body テキスト final String htmlBody = utf8.decode(response.bodyBytes); // 検査要請者の状況ブロックのみ HTMLテキスト抽出 bool isExtracting = false; final String extracts = htmlBody.split(RegExp('n')).where( (String line) { if (line.contains(RegExp('検査陽性者の状況'))) isExtracting = true; if (line.contains(RegExp('検査実施状況'))) isExtracting = false; return isExtracting; } ).toList().join(); // 種別〜人数ブロックのみ HTMLテキスト抽出 isExtracting = false; List<String> contents = extracts.split(RegExp('> ?<')).where( (String line) { if (line.contains(RegExp('class="content_1gFZ-"'))) isExtracting = true; if (line.contains(RegExp('class="unit_25C4Z"'))) isExtracting = false; return isExtracting; } ).join().split(RegExp('div class="content_1gFZ-"')); // 更新日付ブロックのみ HTMLテキスト抽出 isExtracting = false; String datetime = extracts.split(RegExp('> ?<')).where((String line) { return line.contains(RegExp('time datetime=')); }).join(); 文字が小さくて読めないと思いますので、 この後、全ソースのダウンロード先を紹介します。 Dart 言語は、 JavaScript(ES2015)に似ています。 class や async / await、アロー関数、 コレクションのストリーム処理などが 使えます。
  • 15. スクレイピングの全ソース(3/4) 15 // 種別〜人数+更新日付ブロックのみ HTMLテキスト contents.add(datetime); // 抽出 HTML テキストから JSON を生成する。 final Map<String, dynamic> json = contents.map<Map<String,dynamic>>( (String content) { Map<String, dynamic> el; if (content.contains(RegExp('累計'))) el = {'cumulative': _extractNum(content)}; if (content.contains(RegExp('入院'))) el = {'hospitalized': _extractNum(content)}; if (content.contains(RegExp('軽症'))) el = {'mild2moderate': _extractNum(content)}; if (content.contains(RegExp('重症'))) el = {'severe': _extractNum(content)}; if (content.contains(RegExp('死亡'))) el = {'death': _extractNum(content)}; if (content.contains(RegExp('退院'))) el = {'discharge': _extractNum(content)}; if (content.contains(RegExp('更新'))) el = {'updatetime': _extractDatetime(content)}; return el; } ).reduce( (Map<String, dynamic> prev, Map<String, dynamic> curr) { curr.addAll(prev ?? {}); return curr; } ); print('json=$json'); return json; } /// 数値抽出関数 スクレイピングした HTML テキストから、 JSON データ ⇒ Map<String, dynamic> に 変換しています。
  • 16. スクレイピングの全ソース(4/4) 16 /// 数値抽出関数 static int _extractNum(String content) { final String num = content .replaceFirst(RegExp('^[^0-9]*'), '') .replaceFirst(RegExp('[^0-9]*$'), ''); return int.parse(num); } /// 日付抽出関数 static DateTime _extractDatetime(String content) { final String date = content .replaceFirst(RegExp('^time datetime="'), '') .replaceFirst(RegExp('".*$'), ''); return DateTime.parse(date); } } こちらは、内部で利用する、 ユーティリティ関数となります。 全体的に JavaScript に似かよったプログラムになっています。
  • 17. スクレイピングの補足 17 検査陽性者の状況</h3> </div> <div class="DataView-Description"></div> <div><p class="note_3ycFr">(注)チャーター機帰国者、クルーズ船乗客等は含まれていない </p></div> <div class="DataView-CardText"> <ul aria-label="検査陽性者の状況" class="container_37WEO"><li class="box_2dey9 parent_23pyN confirmed_P-yFc"> <div class="pillar_F62Yl"><div class="content_1gFZ-"><span>陽性者数<br>(累計)</span> <span><strong>154</strong> <span class="unit_25C4Z">人</span></span></div></div> <ul class="group_1ksdS"><li class="box_2dey9 parent_23pyN hospitalized_3w7PT"> <div class="pillar_F62Yl"><div class="content_1gFZ-"><span>入院中</span> <span><strong>119</strong> <span class="unit_25C4Z">人</span></span></div></div> <ul class="group_1ksdS"><li class="box_2dey9 minor_1RUXC"><div class="pillar_F62Yl"> 〜 省略 〜 <li class="box_2dey9 recovered_JzYjB"><div class="pillar_F62Yl"><div class="content_1gFZ-"><span>退院</span> <span><strong>31</strong> <span class="unit_25C4Z">人</span></span> </div></div></li></ul></li></ul></div> <div class="DataView-Description"></div> 〜 省略 〜 <div class="DataView-Footer"><div class="Footer-Left"><div><!----></div> <div> <a href="/cards/details-of-confirmed-cases" class="Permalink"> <time datetime="2020-03-23T21:15:00">2020/03/23 21:15 更新</time></a></div></div> <div class="Footer-Right"><button class="DataView-Share-Opener"> <svg width="14" height="16" viewBox="0 0 14 16" fill="none" xmlns="http://www.w3.org/2000/svg" role="img" aria-label="検査陽性者の状況のグラフをシェア"> 〜 省略 〜 <div class="DataCard col-md-6 col-12" data-v-59bfe8e7> <div class="DataView v-card v-sheet theme--light"><div class="DataView-Inner"> <div class="DataView-Header"><h3 class="DataView-Title"> 検査陽性者の状況 部から抽出した HTML テキスト <div class="content_1gFZ-"> <span>入院中</span> <span> <strong>119</strong> <span class="unit_25C4Z">人</span> </span> </div> 3月23日のコンテンツでは、 種別ごとの DIV タグには、 class="content_1gFZ-" が割り当てられ、 人数を表す SPAN タグには、 class="unit_25C4Z" が割り当てられている …ことを利用して抽出しています。
  • 18. お詫び dart言語は、最終的に JavaScript に変換されるため、 外部サイト(東京都サイト)からの HTML テキスト取得は、 サイトをまたがる CORS ポリシー違反になります。 このためサンプル Web アプリは、 ブラウザの CORSポリシーを無効 にしなければ、 実行できません。 18
  • 19. Flutter での Webアプリ開発 画面のレイアウトや UI を全てコードで記述することや Dart言語は、JavaScript とあまり変わらないことを 確認いただけましたでしょうか。   Flutter 公式資料 Flutter for web developers ⇒ CSS や HTML と Flutter コードの違い説明 https://flutter.dev/docs/get-started/flutter-for/web-devs Building a web application with Flutter ⇒ Flutter での Webアプリ作成手順 https://flutter.dev/docs/get-started/web 19
  • 20. 実際に触ってみる Flutter を使った Webアプリ開発は、習うより慣れろで、 実際にコードを確認して動かしてみることが早道でしょう。 手軽に Flutter 体験できるシェルスクリプト flexp を使って、 Flutter SDK の簡単インストールと テンプレートプロジェクト作成、 サンプル全コードのダウンロードおよびテンプレートコードとの差替え、 Webアプリのビルド、ローカルサーバを使った Webアプリ配信、 CORS ポリシーを無効化した Chrome での動作確認の手順を紹介します。 flexp には、インストールした Flutter SDK のアンインストール機能も含まれています。 20
  • 21. flexp / flutter 体験シェルスクリプト紹介  bash シェルスクリプト flexp ファイルは、  Flutter未体験の方に、Flutter を簡易に体験してもらえるよう、  SDK インストールやアプリの実行を提供するスクリプトです。  注意1)git コマンドが実行できることが必須要件となります。  注意2)SDK インストールには、700MB のディスクスペースが必要です。  flexp ⇒ flutter experimence シェルスクリプト・ダウンロード先  https://drive.google.com/open?id=1eLGvMXMje5t6I1-N-x34q-rh3vcgakFI  flexp 紹介ドキュメント ⇒ Flutter を体験してみよう・ダウンロード先  https://drive.google.com/open?id=1XbHAhfxg_9N71aGmU2PFu9u6aqXoAxyZCg6Oq_Uwp2E 21
  • 22. サンプル Web アプリコード一式のダウンロード  flutter_for_web_scraping_sample.zip ⇒ サンプル一式・ダウンロード先  https://drive.google.com/open?id=14uKhm2_9vvk-VMPcc2Yt_gjR5kTZCyst 22 flutter_for_web_scraping_sample | +experience (プロジェクトディレクトリ ) | | | +pubspec.yaml | | | +lib | +main.dart | +my_app.dart | +tokyo_stop_covid19.dart | +flexp | +Flutterを体験してみよう .pdf テンプレートプロジェクトと差し替える、 サンプル Web アプリのコードディレクトリ pubspec.yaml と lib ディレクトリを テンプレートのファイルに上書します。 ZIPファイルは、 右図のファイル構成に展開されます。 flexp や Flutterを体験してみよう は、 こちらにも含まれています。
  • 23. サンプル Webアプリの実行手順(1/2) 1. ダウンロードした flexp を自分のホームディレクトリに配置する。 ターミナルを開き、ホームディレクトリで以下を実行します。 2. chmod +x flexp ⇒ 実行権限を付与する。 3. ./flexp -install ⇒ Flutter SDK をインストールする。 ホームディレクトリに flutter_experience ディレクトリが生成される。 4. flexp -create ⇒ プロジェクトテンプレートを新規生成する。 flutter_experience に experience ディレクトリ(プロジェクトテンプレート)が生成される。 5. プロジェクトテンプレートの experience ディレクトリに、 flutter_for_web_scraping_sample の experience フォルダを上書きコピーする。 pubspec.yaml、lib/main.dart、lib/my_app.dart、lib/tokyo_stop_covid19.dart 一式をコピーする。 6. flexp -build ⇒ プロジェクトをビルドする。 23
  • 24. サンプル Webアプリの実行手順(2/2) 7. CORS 無効にした Chrome を起動するため、以下を実行する。 (実際のコマンドは、一行です) MacOS の場合 open -n -a /Applications/Google Chrome.app/Contents/MacOS/Google Chrome --args --user-data-dir="/tmp/chrome_dev_test" --disable-web-security Windows 10 の場合 "C:Program Files (x86)GoogleChromeApplicationchrome.exe" --disable-web-security --disable-gpu --user-data-dir=~/chromeTemp Linux の場合 google-chrome --user-data-dir="/tmp/chrome_dev_test" --disable-web-security 8. flexp -server ⇒ ローカルサーバを起動する。 9. CORS 無効の Chorme ブラウザで、Webアプリ URL(http://サーバ IPアドレス:8080/index.html )を開く flexp の詳しい使い方は、Flutterを体験してみよう.pdf を参照ください。 24
  • 26. 26
  • 28. Flutter や Dart を勉強してみたいかたは ● flutter アプリは、Dart言語を使ってプログラミングします。 Dart言語については、公式サイトのサンプルなどで勉強できます。 Dart言語公式サイト ⇒ https://dart.dev Dart言語仕様ツアー ⇒ https://dart.dev/guides/language/language-tour 28
  • 29. Flutter や Dart を勉強してみたいかたは ● flutter フレームワークについては、公式サイトを覗いてみましょう。 flutter公式サイト ⇒ https://flutter.dev/ Get Started ⇒ https://flutter.dev/docs/get-started/ Learn More ⇒ https://flutter.dev/docs/get-started/learn-more 29
  • 30. Flutter や Dart を勉強してみる ● 英語が苦手でしたら、Flutter 入門書籍も販売されていますよ。 30