Submit Search
Upload
Flutter移行の苦労と、乗り越えた先に得られたもの
•
2 likes
•
4,729 views
K
KeisukeKiriyama
Follow
じゃらんにおいてFlutter移行を行った際の知見の共有
Read less
Read more
Engineering
Report
Share
Report
Share
1 of 115
Download now
Download to read offline
Recommended
FlutterをRenderObjectまで理解する
FlutterをRenderObjectまで理解する
KeisukeKiriyama
2024 State of Marketing Report – by Hubspot
2024 State of Marketing Report – by Hubspot
Marius Sescu
Everything You Need To Know About ChatGPT
Everything You Need To Know About ChatGPT
Expeed Software
Product Design Trends in 2024 | Teenage Engineerings
Product Design Trends in 2024 | Teenage Engineerings
Pixeldarts
How Race, Age and Gender Shape Attitudes Towards Mental Health
How Race, Age and Gender Shape Attitudes Towards Mental Health
ThinkNow
AI Trends in Creative Operations 2024 by Artwork Flow.pdf
AI Trends in Creative Operations 2024 by Artwork Flow.pdf
marketingartwork
Skeleton Culture Code
Skeleton Culture Code
Skeleton Technologies
PEPSICO Presentation to CAGNY Conference Feb 2024
PEPSICO Presentation to CAGNY Conference Feb 2024
Neil Kimberley
Recommended
FlutterをRenderObjectまで理解する
FlutterをRenderObjectまで理解する
KeisukeKiriyama
2024 State of Marketing Report – by Hubspot
2024 State of Marketing Report – by Hubspot
Marius Sescu
Everything You Need To Know About ChatGPT
Everything You Need To Know About ChatGPT
Expeed Software
Product Design Trends in 2024 | Teenage Engineerings
Product Design Trends in 2024 | Teenage Engineerings
Pixeldarts
How Race, Age and Gender Shape Attitudes Towards Mental Health
How Race, Age and Gender Shape Attitudes Towards Mental Health
ThinkNow
AI Trends in Creative Operations 2024 by Artwork Flow.pdf
AI Trends in Creative Operations 2024 by Artwork Flow.pdf
marketingartwork
Skeleton Culture Code
Skeleton Culture Code
Skeleton Technologies
PEPSICO Presentation to CAGNY Conference Feb 2024
PEPSICO Presentation to CAGNY Conference Feb 2024
Neil Kimberley
Content Methodology: A Best Practices Report (Webinar)
Content Methodology: A Best Practices Report (Webinar)
contently
How to Prepare For a Successful Job Search for 2024
How to Prepare For a Successful Job Search for 2024
Albert Qian
Social Media Marketing Trends 2024 // The Global Indie Insights
Social Media Marketing Trends 2024 // The Global Indie Insights
Kurio // The Social Media Age(ncy)
Trends In Paid Search: Navigating The Digital Landscape In 2024
Trends In Paid Search: Navigating The Digital Landscape In 2024
Search Engine Journal
5 Public speaking tips from TED - Visualized summary
5 Public speaking tips from TED - Visualized summary
SpeakerHub
ChatGPT and the Future of Work - Clark Boyd
ChatGPT and the Future of Work - Clark Boyd
Clark Boyd
Getting into the tech field. what next
Getting into the tech field. what next
Tessa Mero
Google's Just Not That Into You: Understanding Core Updates & Search Intent
Google's Just Not That Into You: Understanding Core Updates & Search Intent
Lily Ray
How to have difficult conversations
How to have difficult conversations
Rajiv Jayarajah, MAppComm, ACC
Introduction to Data Science
Introduction to Data Science
Christy Abraham Joy
Time Management & Productivity - Best Practices
Time Management & Productivity - Best Practices
Vit Horky
The six step guide to practical project management
The six step guide to practical project management
MindGenius
Beginners Guide to TikTok for Search - Rachel Pearson - We are Tilt __ Bright...
Beginners Guide to TikTok for Search - Rachel Pearson - We are Tilt __ Bright...
RachelPearson36
Unlocking the Power of ChatGPT and AI in Testing - A Real-World Look, present...
Unlocking the Power of ChatGPT and AI in Testing - A Real-World Look, present...
Applitools
12 Ways to Increase Your Influence at Work
12 Ways to Increase Your Influence at Work
GetSmarter
ChatGPT webinar slides
ChatGPT webinar slides
Alireza Esmikhani
More than Just Lines on a Map: Best Practices for U.S Bike Routes
More than Just Lines on a Map: Best Practices for U.S Bike Routes
Project for Public Spaces & National Center for Biking and Walking
Ride the Storm: Navigating Through Unstable Periods / Katerina Rudko (Belka G...
Ride the Storm: Navigating Through Unstable Periods / Katerina Rudko (Belka G...
DevGAMM Conference
Barbie - Brand Strategy Presentation
Barbie - Brand Strategy Presentation
Erica Santiago
Good Stuff Happens in 1:1 Meetings: Why you need them and how to do them well
Good Stuff Happens in 1:1 Meetings: Why you need them and how to do them well
Saba Software
More Related Content
Featured
Content Methodology: A Best Practices Report (Webinar)
Content Methodology: A Best Practices Report (Webinar)
contently
How to Prepare For a Successful Job Search for 2024
How to Prepare For a Successful Job Search for 2024
Albert Qian
Social Media Marketing Trends 2024 // The Global Indie Insights
Social Media Marketing Trends 2024 // The Global Indie Insights
Kurio // The Social Media Age(ncy)
Trends In Paid Search: Navigating The Digital Landscape In 2024
Trends In Paid Search: Navigating The Digital Landscape In 2024
Search Engine Journal
5 Public speaking tips from TED - Visualized summary
5 Public speaking tips from TED - Visualized summary
SpeakerHub
ChatGPT and the Future of Work - Clark Boyd
ChatGPT and the Future of Work - Clark Boyd
Clark Boyd
Getting into the tech field. what next
Getting into the tech field. what next
Tessa Mero
Google's Just Not That Into You: Understanding Core Updates & Search Intent
Google's Just Not That Into You: Understanding Core Updates & Search Intent
Lily Ray
How to have difficult conversations
How to have difficult conversations
Rajiv Jayarajah, MAppComm, ACC
Introduction to Data Science
Introduction to Data Science
Christy Abraham Joy
Time Management & Productivity - Best Practices
Time Management & Productivity - Best Practices
Vit Horky
The six step guide to practical project management
The six step guide to practical project management
MindGenius
Beginners Guide to TikTok for Search - Rachel Pearson - We are Tilt __ Bright...
Beginners Guide to TikTok for Search - Rachel Pearson - We are Tilt __ Bright...
RachelPearson36
Unlocking the Power of ChatGPT and AI in Testing - A Real-World Look, present...
Unlocking the Power of ChatGPT and AI in Testing - A Real-World Look, present...
Applitools
12 Ways to Increase Your Influence at Work
12 Ways to Increase Your Influence at Work
GetSmarter
ChatGPT webinar slides
ChatGPT webinar slides
Alireza Esmikhani
More than Just Lines on a Map: Best Practices for U.S Bike Routes
More than Just Lines on a Map: Best Practices for U.S Bike Routes
Project for Public Spaces & National Center for Biking and Walking
Ride the Storm: Navigating Through Unstable Periods / Katerina Rudko (Belka G...
Ride the Storm: Navigating Through Unstable Periods / Katerina Rudko (Belka G...
DevGAMM Conference
Barbie - Brand Strategy Presentation
Barbie - Brand Strategy Presentation
Erica Santiago
Good Stuff Happens in 1:1 Meetings: Why you need them and how to do them well
Good Stuff Happens in 1:1 Meetings: Why you need them and how to do them well
Saba Software
Featured
(20)
Content Methodology: A Best Practices Report (Webinar)
Content Methodology: A Best Practices Report (Webinar)
How to Prepare For a Successful Job Search for 2024
How to Prepare For a Successful Job Search for 2024
Social Media Marketing Trends 2024 // The Global Indie Insights
Social Media Marketing Trends 2024 // The Global Indie Insights
Trends In Paid Search: Navigating The Digital Landscape In 2024
Trends In Paid Search: Navigating The Digital Landscape In 2024
5 Public speaking tips from TED - Visualized summary
5 Public speaking tips from TED - Visualized summary
ChatGPT and the Future of Work - Clark Boyd
ChatGPT and the Future of Work - Clark Boyd
Getting into the tech field. what next
Getting into the tech field. what next
Google's Just Not That Into You: Understanding Core Updates & Search Intent
Google's Just Not That Into You: Understanding Core Updates & Search Intent
How to have difficult conversations
How to have difficult conversations
Introduction to Data Science
Introduction to Data Science
Time Management & Productivity - Best Practices
Time Management & Productivity - Best Practices
The six step guide to practical project management
The six step guide to practical project management
Beginners Guide to TikTok for Search - Rachel Pearson - We are Tilt __ Bright...
Beginners Guide to TikTok for Search - Rachel Pearson - We are Tilt __ Bright...
Unlocking the Power of ChatGPT and AI in Testing - A Real-World Look, present...
Unlocking the Power of ChatGPT and AI in Testing - A Real-World Look, present...
12 Ways to Increase Your Influence at Work
12 Ways to Increase Your Influence at Work
ChatGPT webinar slides
ChatGPT webinar slides
More than Just Lines on a Map: Best Practices for U.S Bike Routes
More than Just Lines on a Map: Best Practices for U.S Bike Routes
Ride the Storm: Navigating Through Unstable Periods / Katerina Rudko (Belka G...
Ride the Storm: Navigating Through Unstable Periods / Katerina Rudko (Belka G...
Barbie - Brand Strategy Presentation
Barbie - Brand Strategy Presentation
Good Stuff Happens in 1:1 Meetings: Why you need them and how to do them well
Good Stuff Happens in 1:1 Meetings: Why you need them and how to do them well
Flutter移行の苦労と、乗り越えた先に得られたもの
1.
Flutter移行の苦労と、 乗り越えた先に得られたもの Recruit Co., Ltd.
Keisuke Kiriyama 1
2.
• Recruit Co.,
Ltd. • iOS / Flutter • じゃらんアプリ開発T Keisuke Kiriyama
3.
3 旅を、もっと豊かに 宿・ホテル予約アプリ
4.
現在じゃらんは Flutterへの移行に挑戦しています! 4
5.
Flutterとは ● Google製のクロスプラットフォームSDK ● 単一のソースコードで、複数のプラットフォームの アプリケーションを構築可能 ●
開発言語: Dart 5
6.
Flutterのコミュニティ ● 2018年12月のver1.0リリース以降、 Flutterを使用する開発者は増え続け 現在は200万人を超えた ● 国内においてもFlutterの記事や話題を目にする機会が増 え、日に日に盛り上がりを感じている 6 Flutter
Spring 2020 Update.:https://medium.com/flutter/flutter-spring-2020-update-f723d898d7af, (参照2020-08-02)
7.
国内におけるFlutterのプロダクション採用 ● しかし、国内においてFlutterを プロダクションに採用している例はそれほど多くない ● 弊社においてもFlutterを採用したのはじゃらんが初 7
8.
Flutterどうなの? 8 実際メリット 得られるの? 課題はないの?
9.
Flutterを採用して実際どうだったのかをお伝えします 話すこと 9
10.
Flutterを採用して実際どうだったのかをお伝えします 1. どんな技術的課題に直面したのか 話すこと 10
11.
Flutterを採用して実際どうだったのかをお伝えします 1. どんな技術的課題に直面したのか 2. 課題を乗り越えた結果、どんなメリットを得られたのか 話すこと 11
12.
発表のゴール ● Flutter開発経験者 →直面した課題と得られたメリットを知り 技術選定の際の判断材料になる ● Flutter開発未経験者 →まずはFlutter触ってみたいと思ってもらう 12
13.
1. 前提の共有 ○ じゃらんのFlutter移行 ○
Flutterのレイアウト構築 2. 直面した課題 3. 得られたメリット 4. まとめ 説明の流れ 13
14.
じゃらんのFlutter移行 14
15.
Flutter採用の背景 ● じゃらんアプリはiOS/Android共にリリースから 10年を迎え、長年に渡る開発が行われてきた ● 上記課題を解決するために、リプレースを検討 15 プロジェクトの大規模化によ るビルド時間の増加 プロジェクト全体の コードが古くなっている
16.
じゃらんアプリのリプレース検討 ● クロスプラットフォーム技術の検討 ○ iOS/Android開発工数 ○
リプレースコスト ● クロスプラットフォーム技術の中でも Flutterの開発生産性が最も高いと実感し Flutterの採用を決断 16 半減
17.
17
18.
18
19.
19 ここから先の画面は 全てFlutterで実装
20.
Flutterへの段階的移行 ● Add-to-app(Add Flutter
to existing app)を使用 ● 既存のネイティブプロジェクトにFluterプロジェクトを部分的 に組み込む仕組み 20 じゃらん アプリ Swift Objective-c じゃらん遊び・体験 Flutterプロジェクト
21.
Flutterへの段階的移行 ● Add-to-app(Add Flutter
to existing app)を使用 ● 既存のネイティブプロジェクトにFluterプロジェクトを部分的 に組み込む仕組み 21 じゃらん アプリ Swift Objective-c じゃらん遊び・体験 Flutterプロジェクト Flutterモジュール
22.
Flutterのレイアウト構築 22
23.
Flutterのレイアウト構築 ● Widget ○ UIの構成情報を保持するクラス ●
Widgetをツリー上に構成することによって UIの構築を行う 23
24.
24 class HomePage extends
StatelessWidget { @override Widget build(BuildContext context) { return Scaffold( appBar: AppBar( title: Text('Flutter Demo Home Page'), ), body: Center( child: Text( 'Hello iOSDC!!', style: TextStyle( fontSize: 30, ), ...
25.
25 class HomePage extends
StatelessWidget { @override Widget build(BuildContext context) { return Scaffold( appBar: AppBar( title: Text('Flutter Demo Home Page'), ), body: Center( child: Text( 'Hello iOSDC!!', style: TextStyle( fontSize: 30, ), ...
26.
26 class HomePage extends
StatelessWidget { @override Widget build(BuildContext context) { return Scaffold( appBar: AppBar( title: Text('Flutter Demo Home Page'), ), body: Center( child: Text( 'Hello iOSDC!!', style: TextStyle( fontSize: 30, ), ...
27.
27 class HomePage extends
StatelessWidget { @override Widget build(BuildContext context) { return Scaffold( appBar: AppBar( title: Text('Flutter Demo Home Page'), ), body: Center( child: Text( 'Hello iOSDC!!', style: TextStyle( fontSize: 30, ), ... ● 』UI部品だけではなく、 「画面の中心に表示」 の様なUIの構成情報も Widgetで表現する
28.
直面した課題 28
29.
直面した課題 29 1. タブ切り替えのパフォーマンス 2. Flutterの画面が初期化されない 3.
ネットワーク通信がproxyサーバーを経由しない 4. Google Mapのクラッシュ
30.
直面した課題 30 1. タブ切り替えのパフォーマンス 2. Flutterの画面が初期化されない 3.
ネットワーク通信がproxyサーバーを経由しない 4. Google Mapのクラッシュ
31.
● タブを使用して表示する情報を 切り替えるページが存在する ● このタブのページでは、 口コミの一覧や、プランの一覧を リストで表示する タブの切り替えをする画面 31
32.
発生した問題 ● リストのアイテムを大量に読み込んでタブを切り替える ● タブ切り替えのアニメーションが重くなってしまう ●
まれにタブ切り替えのタイミングで アプリがクラッシュすることがある 32
33.
class TabPage extends
StatelessWidget { @override Widget build(BuildContext context) { return DefaultTabController( ... child: Scaffold( appBar: AppBar( title: Text('Tab Sample'), bottom: TabBar(tabs: <Widget>[ const Tab(child: Text('Tab A')), const Tab(child: Text('Tab B')) ])), body: TabBarView( children: <Widget>[ TabA(), TabB(), ], ● タブのページを作成する サンプルコード: タブのページ 33
34.
class TabPage extends
StatelessWidget { @override Widget build(BuildContext context) { return DefaultTabController( ... child: Scaffold( appBar: AppBar( title: Text('Tab Sample'), bottom: TabBar(tabs: <Widget>[ const Tab(child: Text('Tab A')), const Tab(child: Text('Tab B')) ])), body: TabBarView( children: <Widget>[ TabA(), TabB(), ], ● 2つのタブ ● TabA() ● TabB() サンプルコード: タブのページ 34
35.
class TabA extends
StatelessWidget { @override Widget build(BuildContext context) { return Center( child: Text('Tab A'), ); } } ● TabA()は画面の中心に “Tab A”を表示するだけ サンプルコード: TabA 35
36.
class TabB extends
StatelessWidget { @override Widget build(BuildContext context) { return ListView.builder( key: PageStorageKey('TabB'), itemCount: 1000, itemBuilder: (BuildContext context, int index) { return ListTile( title: Text('Item $index'), ); ... ● TabB()はリストを保持 ● 1000個のアイテムを表示 サンプルコード: TabB 36
37.
37 タブ切り替え TabA TabB
38.
38 TabB • Tab Bのリストを下までスクロールする
39.
39 TabB TabA TabAに 切り替え TabBに 切り替え TabB
40.
40 TabB TabA TabAに 切り替え TabBに 切り替え TabB ● タブ切り替えのアニメーションが非 常に重くなる ●
まれにクラッシュする🤔
41.
なぜアニメーション重くなる? ● タブを切り替えた際、表示 されないタブはWidget ツリーから除外される 41 TabA表示時 TabB表示時
42.
● 再度タブを表示する際には、表示するタブの レイアウトを再計算する必要がある なぜアニメーション重くなる? ● タブを切り替えた際、表示 されないタブはWidget ツリーから除外される 42 TabA表示時
TabB表示時
43.
タブが保持するリストのアイテムの高さが可変の場合 ● 1つ目のアイテムから順にレイアウト を計算して、高さを決定しないと なぜアニメーション重くなる? 43 …
44.
タブが保持するリストのアイテムの高さが可変の場合 ● 1つ目のアイテムから順にレイアウト を計算して、高さを決定しないと ● 前回のスクロール位置の アイテムを表示できない なぜアニメーション重くなる? 44 … 前回の スクロール位置
45.
タブが保持するリストのアイテムの高さが可変の場合 ● 1つ目のアイテムから順にレイアウト を計算して、高さを決定しないと ● 前回のスクロール位置の アイテムを表示できない ●
この演算のために パフォーマンスが低下 なぜアニメーション重くなる? 45 … 前回の スクロール位置
46.
なぜまれにクラッシュする? ● リストのレイアウトを決定する演算を行うことで、 一時的にメモリを圧迫する ● この一時的な圧迫で許容値を超えてしまった場合に クラッシュが発生していた 46 タブ切り替え時
47.
どう回避したか ● タブのWidgetにAutomaticKeepAliveClientMixin を適用する ● 非表示になったタブもWidgetツリーから除外されなくなるた め、再度レイアウトの演算が不要 47 TabA表示時にTabBが除外されない
48.
class TabB extends
StatefulWidget { ... class _TabBState extends State<TabB> with AutomaticKeepAliveClientMixin { @override Widget build(BuildContext context) { super.build(context); return ListView.builder( key: PageStorageKey('TabB'), itemCount: 1000, itemBuilder: (BuildContext context, int index) { return ListTile( title: Text('Item $index'), ... @override bool get wantKeepAlive => true; } ● TabのWidgetを StatefulWidgetに変更する タブにAutomaticKeepAliveClientMixinを適用 48
49.
class TabB extends
StatefulWidget { ... class _TabBState extends State<TabB> with AutomaticKeepAliveClientMixin { @override Widget build(BuildContext context) { super.build(context); return ListView.builder( key: PageStorageKey('TabB'), itemCount: 1000, itemBuilder: (BuildContext context, int index) { return ListTile( title: Text('Item $index'), ... @override bool get wantKeepAlive => true; } ● Stateに AutomaticKeepAlive ClientMixin を適用する タブにAutomaticKeepAliveClientMixinを適用 49
50.
class TabB extends
StatefulWidget { ... class _TabBState extends State<TabB> with AutomaticKeepAliveClientMixin { @override Widget build(BuildContext context) { super.build(context); return ListView.builder( key: PageStorageKey('TabB'), itemCount: 1000, itemBuilder: (BuildContext context, int index) { return ListTile( title: Text('Item $index'), ... @override bool get wantKeepAlive => true; } タブにAutomaticKeepAliveClientMixinを適用 50 ● super.buildの呼び出し ● wantKeepAliveのgetterで trueを返す
51.
class TabB extends
StatefulWidget { ... class _TabBState extends State<TabB> with AutomaticKeepAliveClientMixin { @override Widget build(BuildContext context) { super.build(context); return ListView.builder( key: PageStorageKey('TabB'), itemCount: 1000, itemBuilder: (BuildContext context, int index) { return ListTile( title: Text('Item $index'), ... @override bool get wantKeepAlive => true; } タブにAutomaticKeepAliveClientMixinを適用 51 ● 一時的なメモリ圧迫も 起こらなくなる
52.
直面した課題 52 1. タブ切り替えのパフォーマンス 2. Flutterの画面が初期化されない 3.
ネットワーク通信がproxyサーバーを経由しない 4. Google Mapのクラッシュ
53.
53 Nativeの画面 Flutterの画面
54.
発生した問題 ● Flutterの画面を閉じて再度開く ● 前回開いた画面の状態が残ってしまっている 54
55.
55 Nativeの画面 Flutterの画面
56.
56 Nativeの画面 • Flutterの画面に遷移する
57.
57 Flutterの画面 • +のFABをタップ • 画面の中心にタップした回数が表示
58.
58 Nativeの画面 Flutterの画面 dismiss
59.
59 Nativeの画面 Flutterの画面 present dismiss Flutterの画面
60.
60 Nativeの画面 Flutterの画面 present dismiss Flutterの画面 ● 前回のFlutterの画面の 状態が残ってしまっている ● 画面を破棄して再生成したら、初 期状態になるのでは?🤔
61.
61 じゃらんTOP present dismiss 遊び・体験(Flutter) 遊び・体験(Flutter) 検索条件指定 じゃらん遊び・体験予約 を再度開く 前回の検索条件のまま
62.
62 じゃらんTOP present dismiss 遊び・体験(Flutter) 遊び・体験(Flutter) 検索条件指定 遊び・体験を再度開く 前回の検索条件のまま ●
Add-to-appでFlutterの画面を表示する方法の説明 ↓ ● この問題の原因の説明
63.
おさらい: Add-to-app ● 既存のネイティブプロジェクトにFluterプロジェクトを部分的 に組み込む仕組み 63 じゃらん アプリ Swift Objective-c じゃらん遊び・体験 Flutterプロジェクト Flutterモジュール
64.
Flutterの画面を表示するために重要なクラス 64 FlutterEngine FlutterViewController FlutterView
65.
Flutter View Controller View Controller Flutterの画面を表示するために重要なクラス 65 FlutterEngine FlutterViewController FlutterView ● ViewControllerの派生クラス ● FlutterViewControllerに遷移する ことでFlutterの画面を表示 画面遷移
66.
Flutterの画面を表示するために重要なクラス 66 FlutterEngine FlutterViewController FlutterView ● FlutterViewControllerに 乗っているView ● FlutterモジュールのUIが描画される FlutterViewController FlutterView
67.
Flutterの画面を表示するために重要なクラス 67 FlutterEngine FlutterViewController FlutterView ● Dartを実行して、FlutterViewに FlutterモジュールのUIを描画する FlutterViewController FlutterView FlutterEngine
68.
Flutterの画面を表示するために重要なクラス 68 FlutterEngine FlutterViewController FlutterView ● Dartを実行して、FlutterViewに FlutterモジュールのUIを描画する FlutterViewController FlutterView FlutterEngine • Flutterの画面を描画するためには、 FlutterEngineの初期化が必要
69.
class AppDelegate: FlutterAppDelegate
{ lazy var flutterEngine = FlutterEngine(name: "my flutter engine") override func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]?) -> Bool { flutterEngine.run(); GeneratedPluginRegistrant.register(with: self.flutterEngine); return super.application(application, didFinishLaunchingWithOptions: launchOptions); } } FlutterEngineの初期化 69 • AppDelegateにおいてFlutterEngineインスタンスの生成
70.
class AppDelegate: FlutterAppDelegate
{ lazy var flutterEngine = FlutterEngine(name: "my flutter engine") override func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]?) -> Bool { flutterEngine.run(); GeneratedPluginRegistrant.register(with: self.flutterEngine); return super.application(application, didFinishLaunchingWithOptions: launchOptions); } } FlutterEngineの初期化 70 • Dartのmainを実行し、FlutterEngineの初期化を行う • FlutterEngineの初期化は時間がかかるため、予め呼ぶ必要がある
71.
class ViewController: UIViewController
{ override func viewDidLoad() { super.viewDidLoad() } @IBAction func showFlutter(_ sender: Any) { let flutterEngine = (UIApplication.shared.delegate as! AppDelegate).flutterEngine let flutterViewController = FlutterViewController(engine: flutterEngine, nibName: nil, bundle: nil) present(flutterViewController, animated: true, completion: nil) } } FlutterViewControllerへ遷移 71 • FlutterEngineを指定して、FlutterViewControllerをインスタンス化 • FlutterViewControllerに画面遷移することでFlutterの画面を表示
72.
何故画面を再生成しても初期状態にならない? 72 FlutterEngineFlutterViewController ● Flutterの画面を閉じた段階で FlutterViewControlerは破棄される
73.
● FlutterEngineはAppDelegateで初期化し、 参照を保持しておくので、破棄されない 何故画面を再生成しても初期状態にならない? 73 FlutterEngine class AppDelegate:
FlutterAppDelegate { lazy var flutterEngine = FlutterEngine(name: "my flutter engine") FlutterViewController
74.
● FlutterEngineはAppDelegateで初期化し、 参照を保持しておくので、破棄されない 何故画面を再生成しても初期状態にならない? 74 FlutterEngine ● Dartを実行しているのはFlutterEngine ●
Flutterの画面を閉じても、Dart内で破棄 していないStateは残ってしまう FlutterViewController
75.
● FlutterEngineはAppDelegateで初期化し、 参照を保持しておくので、破棄されない 何故画面を再生成しても初期状態にならない? 75 FlutterEngine ● Dartを実行しているのはFlutterEngine ●
Flutterの画面を閉じても、Dart内で破棄 していないStateは残ってしまう ● 時間がかかるためFlutterEngineを毎回初期化 するわけにもいかない FlutterViewController
76.
● Flutterモジュールの最初に空の画面を挿入(InitialPage) ● FlutterViewController遷移時に ○
InitialPage以外のページを全て破棄 ○ 本来最初に表示したいページを生成して、 即座に遷移する どう回避したか 76
77.
FlutterViewControllerに遷移するコード class ViewController: UIViewController
{ ... @IBAction func showFlutter(_ sender: Any) { let flutterEngine = (UIApplication.shared.delegate as! AppDelegate).flutterEngine let flutterViewController = FlutterViewController(engine: flutterEngine, nibName: nil, bundle: nil) let channel = FlutterMethodChannel(name: "channel", binaryMessenger: flutterViewController.binaryMessenger) channel.invokeMethod("setup", arguments: nil); flutterViewController.modalPresentationStyle = .fullScreen present(flutterViewController, animated: true, completion: nil) } } 77 • Method Channelを使用して、setupのDartコードを呼び出す
78.
FlutterViewControllerに遷移するコード class ViewController: UIViewController
{ ... @IBAction func showFlutter(_ sender: Any) { let flutterEngine = (UIApplication.shared.delegate as! AppDelegate).flutterEngine let flutterViewController = FlutterViewController(engine: flutterEngine, nibName: nil, bundle: nil) let channel = FlutterMethodChannel(name: "channel", binaryMessenger: flutterViewController.binaryMessenger) channel.invokeMethod("setup", arguments: nil); flutterViewController.modalPresentationStyle = .fullScreen present(flutterViewController, animated: true, completion: nil) } } 78 • その後FlutterViewControllerへ画面遷移する
79.
Initial Pageのコード class InitialPage
extends StatelessWidget { static const MethodChannel channel = MethodChannel('channel'); @override Widget build(BuildContext context) { channel.setMethodCallHandler((MethodCall call) async { switch (call.method) { case 'setup': return Navigator.pushNamedAndRemoveUntil<void>( context, TopPage.routeName, (Route<dynamic> route) => route.isFirst, ); } 79 • 空のInitial pageを作成 • Flutterモジュールの先頭の 画面に設定
80.
Initial Pageのコード class InitialPage
extends StatelessWidget { static const MethodChannel channel = MethodChannel('channel'); @override Widget build(BuildContext context) { channel.setMethodCallHandler((MethodCall call) async { switch (call.method) { case 'setup': return Navigator.pushNamedAndRemoveUntil<void>( context, TopPage.routeName, (Route<dynamic> route) => route.isFirst, ); } 80 • setupのMethod Channelが 呼び出された際に 実行されるコード
81.
• pushNamedAndRemoveUntil 条件が満たされるまで、 画面を破棄する。 その後新たな画面をpush Initial Pageのコード class
InitialPage extends StatelessWidget { static const MethodChannel channel = MethodChannel('channel'); @override Widget build(BuildContext context) { channel.setMethodCallHandler((MethodCall call) async { switch (call.method) { case 'setup': return Navigator.pushNamedAndRemoveUntil<void>( context, TopPage.routeName, (Route<dynamic> route) => route.isFirst, ); } 81
82.
● 条件 一番最初の画面であること。 すなわちInitial Pageに到達す るまで画面が破棄される Initial
Pageのコード class InitialPage extends StatelessWidget { static const MethodChannel channel = MethodChannel('channel'); @override Widget build(BuildContext context) { channel.setMethodCallHandler((MethodCall call) async { switch (call.method) { case 'setup': return Navigator.pushNamedAndRemoveUntil<void>( context, TopPage.routeName, (Route<dynamic> route) => route.isFirst, ); } 82
83.
● 条件を満たしたタイミングで本 来表示したかった 最初の画面がpushされる Initial Pageのコード class
InitialPage extends StatelessWidget { static const MethodChannel channel = MethodChannel('channel'); @override Widget build(BuildContext context) { channel.setMethodCallHandler((MethodCall call) async { switch (call.method) { case 'setup': return Navigator.pushNamedAndRemoveUntil<void>( context, TopPage.routeName, (Route<dynamic> route) => route.isFirst, ); } 83
84.
84 Nativeの画面 Flutterの画面 present dismiss Flutterの画面 ● 再度表示した際に、初期化される
85.
直面した課題 85 1. タブ切り替えのパフォーマンス 2. Flutterの画面が初期化されない 3.
ネットワーク通信がproxyサーバーを経由しない 4. Google Mapのクラッシュ
86.
● 詳細なデバッグや試験を行う際に パケットモニタリングを使用したい ● iOSプロジェクトにおいては Wi-Fi設定からproxyサーバーの IPアドレスとポート番号を入力する ことでパケットモニタリング可能 (例:
Charles) iOSプロジェクトでパケットモニタリング 86
87.
● 同様のWi-Fi設定をFlutterプロジェクトに行っても、通信が Proxyサーバーを経由せず パケットモニタリングを使用できない 発生した問題 87
88.
proxyサーバーを経由するためには 88 ● HttpClientクラスに プロキシ自動設定(PAC)を 明示的に指定する必要がある
89.
final httpClient =
HttpClient(); httpClient.findProxy = (url) { return 'PROXY localhost:8888; DIRECT'; }; final request = await httpClient .getUrl(Uri.https('jsonplaceholder.typicode.com', '/posts')); final response = await request.close(); ● HttpClientのfindProxyに PACを指定する PACを指定する方法(HttpClient) 89
90.
Future<void> main() async
{ HttpOverrides.global = _HttpOverrides(); runApp(Application()); } class _HttpOverrides extends HttpOverrides { @override HttpClient createHttpClient(SecurityContext context) { return super.createHttpClient(context) ..findProxy = (uri) { return 'PROXY localhost:8888; DIRECT'; }; } ● HttpOverridesを継承した クラスを定義 PACを指定する方法(httpパッケージ) 90
91.
Future<void> main() async
{ HttpOverrides.global = _HttpOverrides(); runApp(Application()); } class _HttpOverrides extends HttpOverrides { @override HttpClient createHttpClient(SecurityContext context) { return super.createHttpClient(context) ..findProxy = (uri) { return 'PROXY localhost:8888; DIRECT'; }; } ● HttpOverridesの createHttpClientメソッドを overrideする ● 作成されるHttpClientクラス にfindProxyを指定する PACを指定する方法(httpパッケージ) 91
92.
Future<void> main() async
{ HttpOverrides.global = _HttpOverrides(); runApp(Application()); } class _HttpOverrides extends HttpOverrides { @override HttpClient createHttpClient(SecurityContext context) { return super.createHttpClient(context) ..findProxy = (uri) { return 'PROXY localhost:8888; DIRECT'; }; } ● HttpOverridesの派生クラス のインスタンスを HttpOverrides.globalに 指定する PACを指定する方法(httpパッケージ) 92
93.
Future<void> main() async
{ HttpOverrides.global = _HttpOverrides(); runApp(Application()); } class _HttpOverrides extends HttpOverrides { @override HttpClient createHttpClient(SecurityContext context) { return super.createHttpClient(context) ..findProxy = (uri) { return 'PROXY localhost:8888; DIRECT'; }; } ● PACをベタ書きしている PACを指定する方法(httpパッケージ) 93
94.
システムProxyからPACを指定する ● じゃらんではsystem_proxyパッケージを使用 94 pub system_proxy:https://pub.dev/packages/system_proxy,
(参照2020-08-10)
95.
void main() async
{ WidgetsFlutterBinding.ensureInitialized(); Map<String, String> proxy = await SystemProxy.getProxySettings(); ... HttpOverrides.global = _HttpOverrides(proxy['host'], proxy['port']); runApp(Application()); } class _HttpOverrides extends HttpOverrides { _HttpOverrides(this._host, this._port); final String _host; final String _port; @override HttpClient createHttpClient(SecurityContext context) { return super.createHttpClient(context) ..findProxy = (uri) { return _host != null ? "PROXY $_host:$_port;" : 'DIRECT'; }; ● システムのProxy設定を 取得 ● その情報を使用してPACを findProxyに指定 システムProxyからPACを指定する 95
96.
直面した課題 96 1. タブ切り替えのパフォーマンス 2. Flutterの画面が初期化されない 3.
ネットワーク通信がproxyサーバーを経由しない 4. Google Mapのクラッシュ
97.
Google Mapの使用 ● レジャー施設の場所や集合場所を示 すために、地図(Google
Map)を表示す る ● google_maps_flutterパッケージを使用 97 pub google_maps_flutter:https://pub.dev/packages/google_maps_flutter, (参照2020-08-10)
98.
● google_maps_flutterはDevelopers Preview ●
実際に使用すると、 Google Mapを何度も表示した際に アプリがクラッシュする問題が発覚 ● Google Mapを閉じてもメモリが解放 されない ● 地図を開くたびにメモリを圧迫してしまい、 最終的にクラッシュしてしまっていた 発生した問題 98 Memory Usage
99.
原因と問題の回避 ● GoogleMapが内部で使用しているPlatformViewに おいて循環参照があり、それによってGoogleMapが 解放されなくなっていた ● 当時使用していたFlutter
ver1.12.13+hotfix.7から Flutter ver1.15.17にアップデートしたことで、 メモリが解放される様になり、回避することができた 99
100.
ライブラリのステータス ● developers preview等のライブラリや機能に関する 既知の問題には、issueにタグが付与されている ●
その様なライブラリや機能を使用する際には、 タグでフィルタリングして、関連issueを確認することで 事前に問題を把握すると吉 100 pub google_maps_flutter:https://pub.dev/packages/google_maps_flutter, (参照2020-08-10)
101.
直面した課題まとめ ● Flutterを採用してみると、いくつかの課題に直面した ● GoogleMapがdevelopers
previewである等、 プラットフォームの未成熟な部分は若干ある? ● しかし、いずれの直面した課題も回避することは できていて、プロダクション採用不可能となる様な 事態には直面しなかった 101
102.
これらの課題を乗り越えた結果 どんなメリットを得られたのか 102
103.
工数の 削減 開発効率 の向上 最も大きく得られたメリット 103
104.
● 開発効率は著しく向上した 1. 既成部品の充実 2.
hot reload/restart 3. IDE(Android Studio)の機能の充実 得られたメリット:開発効率の向上 104 開発効率 の向上
105.
1. 既成部品の充実 105 ● Widgetの種類がとても充実 している ●
じゃらんにおいては、これら既成 部品でほぼ事足りた ● 既成部品を積極的に使用できた ことが、開発効率向上に 寄与した Widget catalog:https://flutter.dev/docs/development/ui/widgets, (参照2020-08-10)
106.
● コードを修正した際に、ビルドし直さなくても その修正が即座にアプリに反映される仕組み ○ hot
reload: 約0.5s ○ hot restart: 約3s ● じゃらんはビルド時間が大分増加してしまって いたので、この仕組みの開発効率向上への 寄与は大きかった 2. hot reload/ restart 106
107.
● Widgetの上でoption+Enterを押すことで、包む Widget等の候補を表示。 Widgetツリーの構築をサクサクできる ● “stless”や”stful”と打つことで Stateless
WidgetやStateful Widget を自動生成 3. IDE(Android Studio)の機能の充実 107 ● XCodeで開発する場合に比べて開発スピードが向上した
108.
1. iOS/Androidの開発工数削減 2. 開発以外の工数削減 3.
移行工数の削減 得られたメリット:工数の削減 108 工数の 削減
109.
● じゃらんはメディアであり、 プラットフォーム固有の機能が少ない ● 完全移行が完了すれば、iOS/Androidの開発工数を ほぼ半分にすることができそう 1.
iOS/Androidの開発工数削減 109
110.
● iOSとAndroidの仕様差分をなるべく減らす ● デザインをマテリアルデザインに統一 ●
開発以外の工数も削減することができている 2. 開発以外の工数削減 110 開発工数 要件検討工数 デザイン作成工数 5割減 5割減 3割減
111.
● 段階的移行を行っていることにより、各プラットフォームの 実装が多少必要になっている ○ 例えば、ネイティブ側が保持するアプリの設定情報を Flutterモジュールに伝播する処理 ●
しかし、大部分は共通化できていて、その点 移行コストも大きく削減することができている 3. 移行工数の削減 111
112.
開発効率向上、工数削減以外にも多くのメリット ● 宣言的UI構築が素晴らしい ○ 参考:
宣言的UI そな太さん https://speakerdeck.com/sonatard/xuan-yan-de-ui ● FlutterがOSSであることで、内部処理を確認できる ● パフォーマンスモニタリングが充実している ● 全てDartで記述するため、コードレビューや コンフリクトの解消がしやすい などなど... 112
113.
まとめ 113
114.
● じゃらんでは現在Flutterへの段階的移行を行っている ● 複数課題に直面したものの、回避することはできた ●
直面した課題を乗り越えたことで、開発効率向上や開発 工数削減など多くのメリットを得ることができた ● 完全移行に向けて引き続きFlutter頑張ります💪 まとめ 114
115.
ありがとうございました! 115
Download now