#はじめに
Flutter for Android developersの要約と全訳の記事です。
改めて読んでみたところ、Flutterについてイメージしやすい、かなりいいドキュメントだと感じました。
Google翻訳オンリーで読むとわかりづらく、自分用にメモしていたのですが、内容がよかったため、この記事に残しておこうと思います。
めちゃめちゃ長いので、少しずつ読むことを推奨します。
後半の方がわかりやすいので、しんどくなったら下から読むといいかもしれません。
全訳が長いので要約
長いので、基本的なところをピックアップしています。
概要 | Androidでは? | Flutterでは? |
---|---|---|
View | View | Widget |
Viewの更新 | Viewを直接変更 | Widgetは不変でありStateを更新 |
レイアウト | XML | Widget treeを組む |
アニメーション | XML or animate() | Animationウィジェット |
Canvasのdraw/paint | CanvasとDrawables | CustomPaintとCustomPainter |
Intent1. 画面遷移 | Intent | NavigatorとRoutes |
Intent2. 外部コンポーネント呼び出し | Intent | プラグインを使用/ネイティブに実装 |
外部アプリからIntentを受け取る | Intent | MethodChannelでネイティブ実装 |
Activity/Fragment | Activity/Fragment | 同等のものは存在しない。RouteがActivityに近い |
startActivityForResult | startActivityForResult | Navigator |
runOnUiThread() | runOnUiThread() | 非同期のときだけasync/await使って処理 |
OKHttp | OKHttp | httpプラグイン |
文字列の国際化 | Intlプラグイン | |
Gradle | Gradle | pubspec.yaml |
LinearLayout | LinearLayout | RowまたはColumnウィジェット |
RelativeLayout | RelativeLayout | Column,Row,Stackの各ウィジェット |
ScrollView | ScrollView | ListViewウィジェット |
タップイベント | setOnClickListener() | ウィジェットのイベント検出パラメータ/GestureDetector |
ListView | ListView | ListView(Adaptor不要) |
リスト項目タップイベント | ListViewのonItemClickListener() | ウィジェットのタップ処理 |
カスタムフォント | TextViewのFontFamily | TextウィジェットのfontFamily |
文字のスタイリング | TextウィジェットのStyleパラメータ | |
hintと同等のものは? | Textウィジェットのdecorationパラメータ | |
Inputフォームのエラー表示 | Textウィジェットのdecorationパラメータ | |
GPSセンサーを使用するには? | geolocatorプラグイン | |
カメラにアクセスするには? | image_pickerプラグイン | |
Facebookログイン | flutter_facebook_loginプラグイン | |
Firebase機能を使うには | 各公式プラグイン | |
NDKを使用するには? | FlutterとAndroidでやりとり | |
テーマ | XML | MaterialAppのThemeData |
Shared Preferences | SharedPreferences | Shared_Preferencesプラグイン |
SQLite | SQLite | SQFliteプラグイン |
プッシュ通知 | Firebase Cloud Messagingをfirebase_messagingプラグインで |
全訳
View
Viewに相当するものは?
Androidではボタン、ツールバー、inputsなどのすべてがViewです。
Flutterでは、Viewとおおよそ同じものがウィジェットです。
ウィジェットはAndroidのViewに正確に対応しているわけではありませんが、Flutterがどのように機能するかを学んでいる間は、"UIを宣言および構築する方法"と考えることができます。
ただし、これらにはいくつか違いがあります。はじめに寿命が異なります。
ウィジェットは不変であり、変更が必要になるまで存続し続けます。
ウィジェットやそのウィジェットのStateが変更されるたびに、Flutterフレームワークは、新しいウィジェットツリーを作成します。
それに対して、AndroidViewは、一度描画されたら、invalidate()が呼ばれるまで再描画されません。
Flutterのウィジェットは、その不変性のために軽量です。
なぜなら、ウィジェットはViewそのものでなく、なにかを直接描画しておらず、実際のViewオブジェクトの中に潜んでいるUIとそのUIの定義です。
Flutterは、Materialコンポーネントライブラリが含まれています。
これらはMaterialデザインガイドラインが適用されたウィジェットです。
Materialデザインは、iOSを含むすべてのプラットフォームに最適化された柔軟なデザインシステムです。
しかし、Flutterは、あらゆるデザイン言語で実装するために十分な表現力と柔軟性があります。
たとえば、iOSでは、Cupertinoウィジェットを使用して、AppleのiOSデザインに見えるインターフェースを作成できます。
Widgetの更新方法は?
AndroidではViewを直接変更して更新します。ただし、Flutterではウィジェットは不変であり、直接更新されることはありません。
代わりにWidgetのStateを操作する必要があります。
これがStatefulWidgetとStatelessWidgetの概念の由来です。
StatelessWidgetは、どのようなものか。名前の通りStateを持たないWidgetです。
StatelessWidgetは、ユーザーインターフェースの一部が、オブジェクト内の構成要素以外に依存していない場合に役立ちます。
たとえば、Androidでロゴ付きのImageViewを配置するときに似ています。
ロゴは不変なのでFlutterのStatelessWidgetを使用してください。
HTTP呼び出しなど、受け取ったデータに基づいてUIを動的に変更したい場合は、StatefulWidgetを使用して、そのウィジェットを更新できるようにFlutterフレームワーク側にStateが更新されたことを伝えます。
ここでの注意点は、StatelessWidgetとStatefulWidgetが同じように動作することです。
これらはすべてのフレームでRebuildします。
違いはStatefulWidgetはStateオブジェクトを持っていて、フレーム間でStateを保存し、それを復元できることです。
疑問がある場合には、この規則だけは覚えておいてください。
たとえば、ウィジェットが更新される場合(たとえばユーザーの操作によって)、それはStatefulです。
ただし、ウィジェットが変更に反応しても、親ウィジェットが変更に反応しない限り、親ウィジェットはStatelessになる場合があります。
次の例は、StatelessWidgetの使用方法を示しています。
一般的なStatelessWidgetはTextウィジェットです。
Textウィジェットの実装をみると、StatelessWidgetのサブクラスであることがわかります。
Text(
'I like Flutter!',
style: TextStyle(fontWeight: FontWeight.bold),
);
ご覧のようにTextウィジェットにはState情報に依存していません。
コンストラクタに渡された内容がレンダリングされ、それ以外のものは表示しません。
しかし、例えば、FloatingActionButtonをクリックしたときに'I Like Flutter'に変更したい場合にはどうしますか。
これを実現するには、TextウィジェットをStatefulWidgetにラップして、ボタンをクリックしたときに、Stateを更新します。
###Widgetのレイアウト方法は?XMLファイルはどこですか?
AndroidではXMLにレイアウトを書きますが、Flutterではウィジェットツリーを組んでレイアウトします。
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: Text("Sample App"),
),
body: Center(
child: MaterialButton(
onPressed: () {},
child: Text('Hello'),
padding: EdgeInsets.only(left: 10.0, right: 10.0),
),
),
);
}
Flutterのレイアウトはウィジェットカタログで見ることができます。
###レイアウトにコンポーネントを追加または削除する方法は?
Androidでは親に対して、addChild()やremoveChild()を呼び出して子viewの追加や削除を行います。
Flutterでは、Widgetは不変であるため、addChild()などに相当するものはありません。
代わりに、Widgetをreturnする関数を親に渡して、その子を作成をbool値のフラグで制御します。
たとえば、FloatingActionButtonをクリックしたときに2つのウィジェットを切り替える方法は以下の通りです。
Androidでは、XMLを使用してアニメーションを作成するか、Viewに対してanimate()を呼び出します。
FlutterではAnimateWidgetを使用します。
Flutterでは、アニメーションを一時停止、シーク、停止、反転できるAnimationControllerを使用します。
vsyncが発生したとき通知して、実行中の各フレームで0と1の間を線形補間するTickerが必要になります。
次に1つ以上のアニメーションを作成して、それらをcontrollerにattachします。
具体例として、CurvedAnimationを使用して、補間曲線に沿ってアニメーションを実装できます。
CurvedAnimationはControllerのデフォルトの直線運動を曲線運動に置き換える計算します。
ウィジェットのように、Flutterのアニメーションはコンポジションで機能します。
ウィジェットツリーを構築するときに、FadeTransitionで不透明度などのプロパティを割り当てて、アニメーションを開始するようにControllerに指示します。
次の例は、FloatingActionButtonを押したときにロゴがフェードインするFadeTransitionを記述する方法を示しています。
詳しくは、Animation&Motionウィジェット、Animationsチュートリアル、およびAnimations overviewを参照してください。
###Canvasを使ってdraw/paintする方法は?
Androidでは、CanvasとDrawablesを使用して画像や図形を描画します。
Flutterは同じ低レベルのレンダリングエンジンSkiaに基づいているため、同様のCanvasAPIを持っています。
その結果、CanvasでペイントするのはAndroid開発者にとって非常に似ている作業です。
Flutterには、CustomPaintとCustomPainterという2つのクラスがあります。
後者は、Canvasに描画するためのアルゴリズムを実装します。
Flutterで実装する方法は、StackoverflowのCollinの回答をご覧ください。
###カスタムウィジェットを作成する方法は?
Androidでは、Viewをサブクラス化するか、既存のViewを使用して特定のメソッドをoverrideして実装します。
Flutterでは、小さなWidgetを構成することによってカスタムWidgetを構築します。
これは、AndroidでViewGroupをカスタムするのに少しだけ似ていますが、カスタムレイアウトロジックなどのように、異なる動作をします。
たとえば、コンストラクタでラベルを受け取るCustomButtonをどのように作成しますか。
RaisedButtonを拡張するのでなく、ラベルを使用してRaisedButtonを構成するCustomButtonを作成します。
##インテント
###Intentに相当するものは?
Androidでは、Intentは主に2つの使用ケースがあります。
Activity間の移動とコンポーネント間の通信です。
一方、FlutterにはIntentの概念は存在しません。
しかし、プラグインを使用して、Intentを使用することができます。
Flutterは、実際にActivityやFragmentと同等のものは持っていません。
FlutterではNavigatorとRoutesを使用して、すべて同じActivity内で画面間を移動します。
Routeはアプリの"screen"や"page"を抽象化したもので、Navigatorはroutesを管理するウィジェットです。
routeはおおよそActivityに対応しますが同じではありません。
Navigatorは画面から画面に移動するために、pushとpopします。
移動したい新しいrouteをpush()したり、戻るときにpop()したりできるスタックのように機能します。
Androidでは、AndroidManifestにActivityを宣言します。
Flutterでは、ページ間を移動する方法がいくつかあります。
・route名をMapに指定(MaterialApp)
・routeに直接移動する(WidgetApp)
次の例ではMapを構築しています。
void main() {
runApp(MaterialApp(
home: MyAppHome(), // becomes the route named '/'
routes: <String, WidgetBuilder> {
'/a': (BuildContext context) => MyPage(title: 'page A'),
'/b': (BuildContext context) => MyPage(title: 'page B'),
'/c': (BuildContext context) => MyPage(title: 'page C'),
},
));
}
Navigatorに名前をpushすることによって、routeに移動します。
Navigator.of(context).pushNamed('/b');
インテントの他の使用ケースとしてカメラやFilePickerなどの外部コンポーネントを呼び出すことです。
これは、ネイティブ側と統合する必要があります。(または既存のプラグインを使用します。)
この方法は、Developing Packages and Pluginsをご覧ください。
###外部アプリからのIntentはどうやって処理する?
Flutterは、Android層と直接通信してデータを要求することで、AndroidからのIntentを処理できます。
次の例では、Flutterコードを実行するネイティブActivityにテキストを共有するIntentFilterを登録しているため、他のアプリがFlutterアプリとテキスト共有できます。
基本的な流れは、最初にAndroidネイティブ側で共有テキストデータを(Activity内で)処理し、次にFlutterがMethodChannelを使用して、データを共有するIntentを受け取るために待機することです。
まず、AndroidManifestにIntentFilterを登録します。
次にMainActivityでIntentを処理して、共有されたテキストを保持します。
FlutterのプロセスでPlatformChannelでデータをリクエストし、ネイティブ側から送信されます。
最後に、レンダリングされるとき、Flutter側からデータを要求します。
###startActivityForResultに相当するものは?
NavigatorクラスはFlutterでルーティングを処理し、スタックにpushしたrouteから結果を返すために使用されます。
これは、push()によって返されるFutureの値をawaitすることで行われます。
たとえば、Userが自分の場所を選択できるようにするrouteを開始するには、次のようになります。
Map coordinates = await Navigator.of(context).pushNamed('/location');
そして、location route内でUserが選択したら、結果をスタックにpopすることができます。
Navigator.of(context).pop({"lat":43.821757,"long":-79.226392});
##AsyncUI
###runOnUiThread()に相当するものは?
Dartには、Isolate(別スレッドで実行する方法)、イベントループ、および非同期プログラミングをサポートする、Singleスレッド実行モデルがあります。
分離させない限り、メインのUIスレッドで動作し、イベントループによって駆動します。
Flutterのイベントループは、Androidのmain Looper、つまり、メインスレッドにAttachされているLooperと同じです。
Dartのシングルスレッドモデルでは、UIをフリーズさせる原因となるブロッキング操作をすべて実行する必要がある訳ではありません。
メインスレッドを常に解放しておく必要があるAndroidと異なり、Flutterでは、非同期処理を実行するためのasync/awaitなどのDart言語が提供する非同期機能を使用します。
C#,JavaScript,Kotlinのコルーチンを使用したことがある場合は、async/awaitに慣れているかもしれません。
たとえば、async/awaitで重い作業を実行することで、UIに影響なくネットワークコードを実行できます。
awaitしていたネットワーク呼び出しが完了したら、setState()でUIを更新します。
これによって、ウィジェットサブツリーの再構築が開始され、データが更新されます。
次の例では、データを非同期的にロードして、ListViewに表示します。
バックグラウンドでの作業の詳細、FlutterとAndroidの違いについては、次のセクションを参照してください。
###バックグラウンドスレッドでどのように処理しますか?
Androidでは、ネットワーク上のリソースにアクセスするには、バックグラウンドスレッドで移動し、メインスレッドに影響がないように作業を行なってANRを回避します。
例えば、AsyncTask、LiveData、IntentService、JobScheduler、RxJavaなど。
Flutterはシングルスレッドでイベントループを実行する(Node.jsのように)ので、スレッド管理やバックグラウンドスレッドの生成について考慮する必要がありません。
ディスクアクセスやネットワーク通信などI/Oに制限された作業は、async/awaitで安全に使用できます。
また、計算集約的な作業を行う場合は、Androidのメインスレッドから作業を行わないように分離します。
I/Oに制限された作業は、関数をasyncで宣言して、関数内で長時間実行される処理をawaitします。
loadData() async {
String dataURL = "https://jsonplaceholder.typicode.com/posts";
http.Response response = await http.get(dataURL);
setState(() {
widgets = json.decode(response.body);
});
}
これが、ネットワークまたはDB呼び出しを行う方法で、どちらもI/O操作です。
Androidでは、AsyncTaskをextendすると、onPreExecute()、doInBackground()、onPostExecute()の3つのメソッドをoverrideします。
Flutterには同等のものはありません。長時間実行される関数をawaitしていて、残りはDartのイベントループが処理します。
ただし、大量のデータを処理していると、UIが停止する場合があります。
Flutterでは、Isolateを使用して、複数のCPUコアを利用して長時間実行または計算集約型の処理を行います。
Isolateは、メインのメモリヒープとメモリを共有しない独立した実行スレッドです。
つまり、メインスレッドから変数にアクセスしたり、setState()を呼び出してUI更新することができません。
Androidスレッドと異なって、Isolateは名前の通り、メモリを共有することができません。(例えばstatic field)
以下の例は、シンプルなIsolateで、UIを更新するためにデータをメインスレッドに共有する方法を示しています。
ここで、dataLoader()は、独立した実行スレッドで動作します。
より大きなCPU処理(たとえば大きなJson解析など)を実行したり、暗号化や信号処理などの計算を実行できます。
以下、動かすことができるFullバージョンのサンプルです。
###OKHttpに相当するものは?
Flutterでネットワーク通信を行うには、人気のあるhttpパッケージを使用すると簡単です。
httpパッケージはOkHttpのすべての機能が含まれているわけではありませんが、通常、自分で実装しなければならない多くの部分が抽象化されていて、ネットワーク通信が簡単に実現できます。
httpパッケージを使用するためには、pubspec.yamlの依存関係を追加します。
dependencies:
...
http: ^0.11.3+16
ネットワークをcallするにはasync関数で、http.get()をawaitします。
import 'dart:convert';
import 'package:flutter/material.dart';
import 'package:http/http.dart' as http;
[...]
loadData() async {
String dataURL = "https://jsonplaceholder.typicode.com/posts";
http.Response response = await http.get(dataURL);
setState(() {
widgets = json.decode(response.body);
});
}
}
###長期実行タスクの進捗状況を表示する方法は?
Androidでは、一般的に、バックグラウンドスレッドで長時間タスクを実行しながら、UIにProgressBarを表示します。
Flutterでは、ProgressIndicatorウィジェットを使用してください。
bool値のフラグを使用して、タイミングを制御することで、進捗状況をプログラム的に表示します。
長期実行タスクが開始される前に表示し、終了時に非表示にするようにします。
次の例では、build関数は3つの異なる関数に分けられています。
showLoadingDialog()がtrue(widgets.length == 0)の場合は、ProgressIndicatorを表示します。
それ以外の場合はネットワークをcallして返されたデータを使用してListViewに表示します。
##プロジェクト構造とリソース
###解像度に依存する画像ファイルはどこに保存しますか?
Androidは、リソースとassetsを別々のアイテムとして扱います。
Flutterは、assetsしか存在しません。
Androidのres/drawable-* フォルダにあるすべてのリソースは、Flutterのassetsフォルダに配置されます。
FlutterはiOSのように、単純な密度ベースのフォーマットに従います。
assetsは、1.0x, 2.0x, 3.0xまたはその他の乗数になります。
Flutterはdpsが存在しませんが、論理的なpixelがあります。
これは基本的にデバイスに依存することがないピクセル値と同じです。
いわゆる、devicePixelRatioは、単一の論理ピクセルにおける物理ピクセルの比率を表します。
Androidの密度のグループと同等のものは次のとおりです。
ldpi: 0.75x
mdpi: 1.0x
hdpi: 1.5x
xhdpi: 2.0x
xxhdpi: 3.0x
xxxhdpi: 4.0x
assetsは任意のフォルダに配置します、Flutterには最初から定義済みのassetsフォルダはありません。
pubspec.yamlファイルでassetsを宣言すると、Flutterがそれを使用できるようになります。
Flutter1.0 beta2 以前は、Flutterで定義されたassetsはネイティブ側からアクセスできませんでした。
また、ネイティブ側のassetsとリソースは別フォルダに存在するため、Flutterでは使用できませんでした。
Flutter Beta2以降は、assetsはネイティブのassetフォルダに保存され、AndroidのAssetManagerを使用して、ネイティブ側でアクセスできます。
val flutterAssetStream = assetManager.open("flutter_assets/assets/my_flutter_asset.png")
Flutter Beta2以降、Flutterはネイティブresourceにアクセスすることも、ネイティブのassetにアクセスすることもできません。
たとえば、my_icon.pngという新しい画像をFlutterプロジェクトに追加し、それをimagesというフォルダに配置することは、ベースImage(×1.0)をImageフォルダに配置します。
サブフォルダないの他のassetsは、適切な比率乗数で呼び出されます。
images/my_icon.png // Base: 1.0x image
images/2.0x/my_icon.png // 2.0x image
images/3.0x/my_icon.png // 3.0x image
次に、これらの画像をpubspec.yamlに宣言する必要があります。
assets:
- images/my_icon.jpeg
そして、AssetImageウィジェットを使って画像にアクセスできます。
return AssetImage("images/a_dot_burr.jpeg");
それかImageウィジェットを使って直接呼び出します。
@override
Widget build(BuildContext context) {
return Image.asset("images/my_image.png");
}
###文字列をどこに保存しますか?国際化はどう対応しますか?
Flutterは現在、文字列専用のリソースを扱うようなシステムは持っていません。
現時点でのベストプラクティスは、コピーテキストを静的フィールドとしてクラスに保持し、そこからアクセスするようにすることです。
たとえば、
class Strings {
static String welcomeMessage = "Welcome To Flutter";
}
それから、コードの中で、文字列にアクセスすることができます。
Text(Strings.welcomeMessage)
FlutterはAndroidでのアクセシビリティを基本的にサポートしていますが、この機能はまだ開発中です。
Flutter開発者は、国際化と地域化のためにIntlパッケージを使うことが推奨されます。
###Gradleファイルに相当するものは?依存関係を追加するものとは?
AndroidではGradleビルドスクリプトを使用して、依存関係を追加します。
FlutterはDart自身のビルドシステムとPubパッケージマネージャーを使用しています。
このツールは、アプリのビルドを、ネイティブのAndroidとiOSそれぞれのビルドシステムに委任します。
FlutterプロジェクトのAndroidフォルダ配下にGradleファイルが存在しますが、プラットフォームごとに必要な依存関係を追加する場合のみ使用してください。
基本的に、Flutterで使用する外部依存関係を宣言するには、pubspec.yamlを使用します。
Flutterで使用できるパッケージを探すには、Pubが良い場所です。
##ActivityとFragment
###ActivityやFragmentと同等のものは何ですか?
Androidでは、Activityはユーザーができることに焦点を合わせて表現したものです。
Fragmentは、動作やユーザーインターフェースの一部を表します。
Fragmentは、コードをモジュール化し、大画面用に洗練されたユーザーインターフェースを構成し、アプリのUI拡張に役立ちます。
Flutterでは、これらの概念はどちらもWidgetの中に含まれています。
ActivityとFragmentを作成するためのUIの詳細は、Flutter For Android Developers : How to design an Activity UI in Flutterを参照してください。
Intentsのセクションで説明した通り、Flutterの画面はWidgetで表します。
すべてがFlutterのWidgetなのです。
異なる画面やページを移動する、または、同じデータの異なるStateを表示するためにNavigatorを使用します。
###AndroidのActivityライフサイクルイベントをlistenするにはどうすればよいですか?
Androidでは、Activityのメソッドをoverrideして、Activity自体のライフサイクルメソッドを取得したり、アプリにActivityLifecycleCallbacksを登録できたりします。
Flutterにはどちらの概念もありませんが、代わりにWidgetsBinding observerとdidChangeAppLifecycleState()のchangeイベントをlistenすることで実現できます。
観察できるライフサイクルは以下の通りです。
・inactive - アプリが非アクティブ状態であり、ユーザーInputを受信していません。Androidにマッピングする同等のイベントがないため、iOSでのみ動作します。
・paused - アプリはこの時にユーザーに見えず、ユーザーの入力には応答せず、バックグラウンドで実行されている状態です。これはAndroidのonPause()と同じです。
・resumed - アプリは表示されて、ユーザーの入力に応答します。これはAndroidのonPostResume()と同じです。
・suspending - アプリは一時的に中断されている状態です。AndroidのonStop()と同じです。iOSでは同等のイベントがないため、動作しません。
これらの状態について、詳しくは、AppLifecycleStatus documentationを参照してください。
すでに気づいているかもしれませんが、ごく少数のActivityライフサイクルイベントしか利用できません。
FlutterActivityは、ほぼすべてのActivityのライフサイクルイベントを内部的に取得して、Flutter engineに送信するため、ほとんどが保護されています。
Flutterはengineの起動と停止を制御します。
ほとんどの場合は、Flutter側でActivityのライフサイクルを観察する必要はありません。
ネイティブリソースの取得や解放をするためにライフサイクルを観察する必要がある場合には、どちらにせよネイティブ側で処理を行うはずです。
以下は、Activityのライフサイクルを監視する方法の例です。
##Layouts
###LinearLayoutと同等のものは?
AndroidではLinearLayoutを使用して、水平方向または垂直方向に直線的にレイアウトします。
Flutterでは、RowウィジェットまたはColumnウィジェットを使用することで、同じ結果を得ることができます。
2つのコードのサンプルが、RowウィジェットとColumnウィジェットを除いて同一であることに気がついたら、、子ウィジェット達は同じであり、同じ子ウィジェット達で、時間のかかる可能性のある複雑なレイアウトを開発できます。
LinearLayoutの構築の詳細については、 Flutter For Android Developers : How to design LinearLayout in Flutter?を参照してください。
###RelativeLayoutと同等のものは?
RelativeLayoutはお互いの位置関係に対してレイアウトします。
Column,Row,Stackの各ウィジェットを組み合わせて使用することで、RelativeLayoutの結果を得ることができます。
子が親に対してどのように配置されるかについては、ウィジェットコンストラクタの規則を指定できます。
FlutterでRelativeLayoutを構築するいい例については、StackoverflowのColinの回答を参照してください。
###ScrollViewと同等のものは?
Androidでは、ScrollViewを使用してウィジェットをレイアウトします。
ユーザーのデバイスの画面がコンテンツよりも小さい場合にはスクロールします。
Flutterで、これを最も簡単に行うには、ListViewウィジェットを使用することです。
これはAndroidと比べてやりすぎに感じるかもしれませんが、FlutterのListViewは、AndroidのScrollViewとListViewの両方です。
@override
Widget build(BuildContext context) {
return ListView(
children: <Widget>[
Text('Row One'),
Text('Row Two'),
Text('Row Three'),
Text('Row Four'),
],
);
}
###Flutterで横画面をどのように処理しますか?
AndroidManifest.xmlに以下のものが含まれている場合、FlutterViewでもこの変更を処理します。
android:configChanges="orientation|screenSize"
##ジェスチャーとタップイベントの処理
###FlutterのウィジェットにonClickリスナーを追加するにはどうすればいいですか?
Androidでは、setOnClickListener()メソッドを呼び出すことで、ボタンなどのViewにonClick処理をアタッチできます。
Flutterでは、リスナーを追加する方法は2つあります。
1. ウィジェットがイベント検出をサポートされている場合、その関数に定義して処理されます。
例えば、RaisedButtonには、onPressedパラメーターが存在します。
@override
Widget build(BuildContext context) {
return RaisedButton(
onPressed: () {
print("click");
},
child: Text("Button"));
}
2. ウィジェットがイベント検出をサポートしていない場合、ウィジェットをGestureDetectorで囲い、処理する関数をonTapパラメーターに渡します。
class SampleApp extends StatelessWidget {
@override
Widget build(BuildContext context) {
return Scaffold(
body: Center(
child: GestureDetector(
child: FlutterLogo(
size: 200.0,
),
onTap: () {
print("tap");
},
),
));
}
}
###ウィジェット上で他のジェスチャーを処理する方法は?
GestureDetectorを使用すると、以下のような幅広いジェスチャーをlistenすることができます。
- Tap
- onTapDown - タップのトリガーとなるポインタが画面に接触した
- onTapUp - タップのトリガーとなるポインタが画面との接触を終えた
- onTap - タップが発生した
- onTapCancel - 以前にonTapDownをトリガーしたポインタが、最終的にタップが発生しなかった
- Double Tap
- onDoubleTap - ユーザーが画面の同じ場所で素早く2回タップした
- Long Press
- onLongPress - ポインタがスクリーンの同じ場所で長時間接触した
- Vertical drag
- onVerticalDragStart - ポインタが画面に接触し、垂直方向に動き始めた
- onVerticalDragUpdate - 画面に接触しているポインタが垂直方向にさらに移動した
- onVerticalDragEnd - 以前は画面と接触していて垂直移動していたポインタが、画面と接触しなくなり、そのときに特定の速度でdragされていた。
- Horizontal drag
- onHorizontalDragStart - ポインタが画面に接触し、水平方向に動き始めた
- onHorizontalDragUpdate - 画面に接触しているポインタが水平方向にさらに移動した
- onHorizontalDragEnd - 以前は画面と接触していて水平移動していたポインタが、画面と接触しなくなり、そのときに特定の速度でdragされていた。
次の例は、Double TapでFlutterLogoを回転させるGestureDetectorを示しています。
##Listviews & adapters
###FlutterのListViewに代わるものはなんですか?
FlutterのListViewに相当するものは...ListViewです。
AndroidのListViewでは、Adapterを作成して、それをListViewに渡します。
ListViewは各行にAdapterが返すものを表示します。
ただし、行を確実にリサイクルする必要があります。
そうでないと、あらゆる種類の可視化した不具合やメモリの問題が発生します。
Flutterの不変のウィジェットパターンによって、あなたはListViewにリストとなるWidgetを渡します。
そして、Flutterは早くて滑らかなスクロールになるように処理します。
###どの項目がクリックされたか、どうすれば検知できますか?
Androidでは、ListViewには、onItemClickListenerという、クリックされた項目を見つけるためのメソッドがあります。
Flutterでは、渡されたウィジェットが提供するタッチ処理を使用してください。
###ListViewを動的に更新する方法は?
Androidでは、Adapterを更新してnotifyDataSetChangedを呼び出します。
Flutterでは、setState()内でウィジェットのリストを更新すると、データが視覚的に変化していないことがすぐにわかります。
これは、setState()が呼び出されると、Flutterレンダリングエンジンがウィジェットツリーを調べ、何かが変更されていないかどうかを確認するためです。
それがListViewに到達すると、==チェックがはしり、2つのListViewが同じであることが確定します。
なにも変わっていないため、更新する必要はありません。
ListViewを簡単に更新するには、setState()内で新しいListを作成して古いリストから新しいリストへコピーします。
この方法は簡単ですが、次の例に示すように、大きなデータセットを行う場合にはおすすめできません。
リストを作成するために推奨される効率的かつ効果的な方法は、ListView.Builderを使用することです。
この方法は、動的なListまたは大量のデータを含むListである場合に最適です。
これは、本質的には、AndroidのRecyclerViewと同等のもので、自動的にリストの要素を再利用します。
ListViewを作成する代わりに、2つの重要なパラメータ(リストのinitial lengthとItemBuilder関数)をとるListView.builderを作成します。
ItemBuilder関数は、AndroidのAdapterのgetView関数に似ています。
ポジションを取り、そのポジションにレンダリングしたい行を返します。
最後に、しかしながら最も重要なことに、onTap()関数はもうリストを再生成せず、代わりにそれを追加することに注意してください。
##Textを扱う
###Textウィジェットにカスタムフォントを設定する方法は?
Android SDK(Android O以降)では、Fontリソースファイルを作成し、それをTextViewのFontFamilyパラメーターに渡します。
Flutterでは、フォントファイルをフォルダ配下に配置して、pubspec.yamlでそのフォルダを参照します。
これは、画像をインポートする方法と似ています。
fonts:
- family: MyCustomFont
fonts:
- asset: fonts/MyCustomFont.ttf
- style: italic
そして、フォントをTextウィジェットに割り当てます。
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: Text("Sample App"),
),
body: Center(
child: Text(
'This is a custom font text',
style: TextStyle(fontFamily: 'MyCustomFont'),
),
),
);
}
###Textウィジェットをスタイルするには?
フォントと同じように、Textウィジェットの他のスタイル要素をカスタマイズすることができます。
TextウィジェットのStyleパラメータは、TextStyleオブジェクトをとります。
次のような多くのパラメータをカスタマイズすることができます。
・color
・decoration
・decorationColor
・decorationStyle
・fontFamily
・fontSize
・fontStyle
・fontWeight
・hashCode
・height
・inherit
・letterSpacing
・textBaseline
・wordSpacing
##フォーム入力
Formsの使用方法の詳細については、Flutter CookBookのRetrieve the value of a text fieldを参照してください。
###Inputのhintと同等のものは?
Flutterでは、TextウィジェットのdecorationパラメータにInputDecorationオブジェクトを追加することで、Input用のHintまたはPlaceholderテキストを簡単に表示できます。
body: Center(
child: TextField(
decoration: InputDecoration(hintText: "This is a hint"),
)
)
###検証エラーを表示する方法は?
あなたがHintを使うのと同じように、TextウィジェットのためのdecorationコンストラクタにInputDecorationオブジェクトを渡します。
ただし、最初からエラーを表示したくありません。
かわりに、ユーザーが無効なデータを入力したときに、Stateを更新して、新しいInputDecorationオブジェクトを渡します。
##Flutter Plugins
###GPSセンサーにアクセスするには?
geolocatorコミュニティプラグインを使用してください。
###カメラにアクセスするには?
image_pickerプラグインは、カメラにアクセスするのに人気があります。
###Facebookログインを行うには?
FaceBookでログインするには、flutter_facebook_loginコミュニティプラグインを使用してください。
###Firebaseの機能を使うには?
Firebaseのほとんどの機能は、ファーストパーティプラグインでカバーされています。
これらのプラグインは、Flutterチームによって管理されている公式プラグインです。
・firebase_admob : Firebase AdMob
・firebase_analytics : Firebase Analytics
・firebase_auth : Firebase Auth
・firebase_database : Firebase RTDB
・firebase_storage : Firebase Cloud Storage
・firebase_messaging : Firebase Messaging (FCM)
・flutter_firebase_ui : quick Firebase Auth integrations (Facebook, Google, Twitter and email)
・cloud_firestore : Firebase Cloud Firestore
また、PUBには、他社製のFirebaseプラグインがあります。
これらのプラグインは、公式のプラグインによって直接カバーされていません。
Flutterまたはコミュニティプラグインのない、プラットフォーム固有の機能がある場合には、開発中のプラグインとパッケージに従って独自の機能を実装することができます。
Flutterのプラグインアーキテクチャは、一言で言えば、Androidでイベントバスを使用するのと似ています。
メッセージを起動し、受信処理して結果を返すようにします。
この場合、ReceiverはAndroidまたはiOSのネイティブ側で実行されるコードです。
###FlutterアプリでNDKを使用する方法は?
現在AndroidアプリにてNDKを使用していて、Flutterアプリにネイティブライブラリを利用したい場合にはカスタムプラグインを作成することで可能となります。
あなたのカスタムプラグインは、あなたがJNI経由でネイティブ関数を呼び出し、Androidアプリとやりとりします。
応答できたら、そのメッセージをFlutterに送り返して結果をレンダリングします。
Flutterから直接ネイティブコードを呼び出すことは現在サポートされていません。
##Themes
###アプリのテーマはどうやって定義しますか?
Flutterには、MaterialDesignの美しい実装が付属しています。
一般的に行われる多くのスタイリングや必要とされるテーマ設定に対応します。
テーマをXMLで宣言して、AndroidManifest.xmlを使用してアプリに適応するAndoridとは異なり、Flutterでは最上位のウィジェットでテーマを宣言します。
アプリのMaterialCompornentを最大限に活用するには、アプリのエントリポイントである最上位ウィジェットのMaterialAppを宣言します。
MaterialAppは、MaterialDesignを実装するアプリに必要なさまざまなウィジェットをラップする便利なウィジェットです。
これは、Material固有の機能を追加することで、WidgetsAppを作りあげます。
アプリウィジェットとして、WidgetsAppを使用することもできます。
これは、同じ機能の一部を提供しますが、MaterialAppほど豊富ではありません。
childコンポーネントの色とスタイルをカスタマイズするには、ThemeDataオブジェクトをMaterialAppウィジェットに渡します。
たとえば、次のコードでは、主色見本は青に設定し、テキスト選択色は赤にしています。
##Databaseとローカルストレージ
###Shared Preferencesにはどうやってアクセスしますか?
Androidでは、SharedPreferences APIを使用してkeyとvalueの小さなコレクションを保存できます。
Flutterでは、Shared_Preferencesプラグインを使用して、この機能にアクセスします。
このプラグインはShared PreferencesとNSUserDefault(iOS版)の両方の機能をラップしています。
###FlutterでSQLiteにアクセスするには?
Androidでは、SQLiteを使用して、SQLでクエリできる構造化したデータを格納します。
Flutterでは、SQFliteプラグインを使用して、この機能にアクセスすることができます。
##Notification
###プッシュ通知を設定する方法は?
Androidでは、Firebase Cloud Messagingを使用してアプリのプッシュ通知を設定します。
Flutterでは、Firebase_Messagingプラグインを使用してこの機能にアクセスします。
Firebase Cloud Messaging APIを使用するための詳細については、firebase_messagingプラグインのドキュメントを参照してください。