More Related Content
More from Recruit Lifestyle Co., Ltd. (20)
Flutter移行の苦労と、乗り越えた先に得られたもの
- 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で表現する
- 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
- 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
● 一時的なメモリ圧迫も
起こらなくなる
- 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の画面を表示
- 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
- 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
- 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