真のクロスプラットフォームモバイル開発のためのGoogleのFlutterの使用

公開: 2022-03-10
簡単なまとめ↬Flutterを使用すると、クロスプラットフォームのモバイルアプリケーションを簡単に構築できます。 この記事では、Flutterを紹介し、他のモバイル開発プラットフォームと比較し、Flutterを使用してアプリの作成を開始する方法を示します。

Flutterは、Googleのオープンソースのクロスプラットフォームモバイル開発フレームワークです。 これにより、iOSおよびAndroid用の高性能で美しいアプリケーションを単一のコードベースから構築できます。 また、Googleの今後のFuchsiaオペレーティングシステムの開発プラットフォームでもあります。 さらに、カスタムのFlutterエンジンエンベッダーを介して他のプラットフォームに持ち込めるように設計されています。

Flutterが作成された理由とそれを使用する理由

クロスプラットフォームツールキットは、歴史的に次の2つのアプローチのいずれかを採用してきました。

  • Webビューをネイティブアプリでラップし、Webサイトであるかのようにアプリケーションを構築します。
  • それらはネイティブプラットフォームコントロールをラップし、それらに対してクロスプラットフォームの抽象化を提供します。

Flutterは、モバイル開発を改善するために別のアプローチを採用しています。 これは、アプリケーション開発者が取り組むフレームワークと、アプリケーションをホストするためのポータブルランタイムを備えたエンジンを提供します。 フレームワークはSkiaグラフィックライブラリに基づいて構築されており、ネイティブコントロールの単なるラッパーではなく、実際にレンダリングされるウィジェットを提供します。

このアプローチにより、Webラッパーオプションが提供するような完全にカスタムな方法でクロスプラットフォームアプリケーションを構築する柔軟性が得られますが、同時にスムーズなパフォーマンスが提供されます。 一方、Flutterに付属する豊富なウィジェットライブラリは、豊富なオープンソースウィジェットとともに、非常に機能豊富なプラットフォームで動作します。 簡単に言えば、Flutterは、妥協のないクロスプラットフォーム開発でモバイル開発者が持っていたものに最も近いものです。

ジャンプした後もっと! 以下を読み続けてください↓

ダート

Flutterアプリケーションは、元々Googleによって開発されたプログラミング言語であるDartで記述されています。 Dartはオブジェクト指向言語であり、事前コンパイルとジャストインタイムコンパイルの両方をサポートしているため、ネイティブアプリケーションの構築に最適であり、Flutterのホットリロードによる効率的な開発ワークフローを提供します。 Flutterは最近Dartバージョン2.0にも移行しました。

Dart言語は、ガベージコレクション、非同期待機、強い型付け、ジェネリックス、豊富な標準ライブラリなど、他の言語で見られる多くの機能を提供します。

Dartは、C#、JavaScript、F#、Swift、Javaなどのさまざまな言語の開発者に馴染みのある機能の共通部分を提供します。 さらに、DartはJavascriptにコンパイルできます。 Flutterと組み合わせると、Webおよびモバイルプラットフォーム間でコードを共有できます。

イベントの歴史的なタイムライン

  • 2015年4月
    Dart Developer Summitで表示されたFlutter(元々コードネームSky)
  • 2015年11月
    Skyの名前がFlutterに変更されました
  • 2018年2月
    モバイルワールドコングレス2018で発表されたFlutterベータ1
  • 2018年4月
    Flutterベータ2が発表されました
  • 2018年5月
    Google I / Oで発表されたFlutterベータ3。 GoogleはFlutterが本番アプリの準備ができていることを発表しました

他の開発プラットフォームとの比較

Apple / Androidネイティブ

ネイティブアプリケーションは、新機能を採用する際の摩擦を最小限に抑えます。 アプリケーションはプラットフォームベンダー自体(AppleまたはGoogle)のコントロールを使用して構築されており、多くの場合、これらのベンダーが設定した設計ガイドラインに従うため、ユーザーエクスペリエンスは特定のプラットフォームとより調和する傾向があります。 ほとんどの場合、ネイティブアプリケーションは、クロスプラットフォーム製品で構築されたアプリケーションよりもパフォーマンスが向上しますが、基盤となるクロスプラットフォームテクノロジによっては、多くの場合、違いは無視できます。

ネイティブアプリケーションの大きな利点の1つは、サードパーティの統合を待たずに、必要に応じてAppleとGoogleがベータ版で作成した新しいテクノロジーをすぐに採用できることです。 ネイティブアプリケーションを構築することの主な欠点は、プラットフォーム間でコードを再利用できないことです。これにより、iOSとAndroidを対象とする場合、開発に費用がかかる可能性があります。

リアクトネイティブ

React Nativeを使用すると、JavaScriptを使用してネイティブアプリケーションを構築できます。 アプリケーションが使用する実際のコントロールはネイティブプラットフォームコントロールであるため、エンドユーザーはネイティブアプリの感覚を得ることができます。 React Nativeの抽象化が提供する以上のカスタマイズが必要なアプリの場合でも、ネイティブ開発が必要になる可能性があります。 必要なカスタマイズの量が多い場合、React Nativeの抽象化レイヤー内で作業することの利点は、場合によってはアプリをネイティブに開発する方が有益になるほど少なくなります。

Xamarin

Xamarinについて説明する場合、評価する必要のある2つの異なるアプローチがあります。 最もクロスプラットフォームなアプローチとして、Xamarin.Formsがあります。 テクノロジーはReactNativeとは大きく異なりますが、概念的には、ネイティブコントロールを抽象化するという点で同様のアプローチを提供します。 同様に、カスタマイズに関しても同様の欠点があります。

第二に、Xamarin-classicという用語がたくさんあります。 このアプローチでは、XamarinのiOS製品とAndroid製品を個別に使用して、Apple / Androidネイティブを直接使用する場合と同様に、プラットフォーム固有の機能を構築します。Xamarinの場合はC#またはF#のみを使用します。 Xamarinの利点は、ネットワーク、データアクセス、Webサービスなど、プラットフォーム固有ではないコードを共有できることです。

これらの代替手段とは異なり、Flutterは、コードの再利用、高性能で流動的なユーザーインターフェイス、優れたツールを備えた、より完全なクロスプラットフォームソリューションを開発者に提供しようとします。

Flutterアプリの概要

アプリの作成

Flutterをインストールした後、Flutterを使用してアプリを作成するのは、コマンドラインを開いてflutter create [app_name]と入力するか、VSコードで[Flutter:New Project]コマンドを選択するか、Androidで[Start a new FlutterProject]を選択するだけです。 StudioまたはIntelliJ。

IDEまたはコマンドラインを好みのエディターと一緒に使用することを選択したかどうかに関係なく、新しいFlutterアプリケーションテンプレートは、アプリケーションの開始点として適しています。

このアプリケーションは、 flutter / material.dartパッケージを取り込み、タイトルバー、マテリアルアイコン、テーマなど、アプリの基本的な足場を提供します。 また、アプリケーションの状態が変化したときにユーザーインターフェイスを更新する方法を示すステートフルウィジェットを設定します。

新しいFlutterアプリケーションテンプレート
iOSとAndroidで実行される新しいFlutterアプリケーション。 (大プレビュー)

ツーリングオプション

Flutterは、ツーリングに関して信じられないほどの柔軟性を提供します。 アプリケーションは、VS Code、Android Studio、IntelliJなどのサポートされているIDEから作成するのと同じように、コマンドラインから任意のエディターから簡単に開発できます。 採用するアプローチは、開発者の好みに大きく依存します。

Android Studioは、実行中のアプリケーションのウィジェットを分析したり、アプリケーションのパフォーマンスを監視したりするFlutterInspectorなどのほとんどの機能を提供します。 また、ウィジェット階層を開発するときに便利ないくつかのリファクタリングも提供します。

VS Codeは、Android Studio / IntelliJよりも速く起動する傾向があるという点で、より軽い開発エクスペリエンスを提供します。 各IDEは、コード補完などの組み込みの編集ヘルパーを提供し、さまざまなAPIの探索と優れたデバッグサポートを可能にします。

コマンドラインはflutterコマンドでも十分にサポートされており、エディター以外のツールに依存することなく、アプリケーションの作成、更新、起動を簡単に行うことができます。

フラッターツーリング
Flutterツールはさまざまな環境をサポートします。 (大プレビュー)

ホットリロード

ツールに関係なく、Flutterはアプリケーションのホットリロードに対して優れたサポートを維持します。 これにより、多くの場合、実行中のアプリケーションを変更し、アプリを停止したり、再構築したり、再デプロイしたりすることなく、状態を維持できます。

ホットリロードは、より迅速な反復を可能にすることにより、開発効率を劇的に向上させます。 それは本当にプラットフォームを操作する喜びにします。

テスト

Flutterには、テストのウィジェットと対話するためのWidgetTesterユーティリティが含まれています。 新しいアプリケーションテンプレートには、以下に示すように、テストを作成するときに使用する方法を示すサンプルテストが含まれています。

 // Test included with the new Flutter application template import 'package:flutter/material.dart'; import 'package:flutter_test/flutter_test.dart'; import 'package:myapp/main.dart'; void main() { testWidgets('Counter increments smoke test', (WidgetTester tester) async { // Build our app and trigger a frame. await tester.pumpWidget(new MyApp()); // Verify that our counter starts at 0. expect(find.text('0'), findsOneWidget); expect(find.text('1'), findsNothing); // Tap the '+' icon and trigger a frame. await tester.tap(find.byIcon(Icons.add)); await tester.pump(); // Verify that our counter has incremented. expect(find.text('0'), findsNothing); expect(find.text('1'), findsOneWidget); }); }

パッケージとプラグインの使用

Flutterはまだ始まったばかりですが、すでに豊富な開発者エコシステムがあります。開発者はすでに多数のパッケージとプラグインを利用できます。

パッケージまたはプラグインを追加するには、アプリケーションのルートディレクトリにあるpubspec.yamlファイルに依存関係を含めるだけです。 次に、コマンドラインまたはIDEを介してflutter packages get実行すると、Flutterのツールが必要なすべての依存関係を取り込みます。

たとえば、Flutter用の人気のある画像ピッカープラグインを使用するには、 pubspec.yamlは次のように依存関係としてリストするだけで済みます。

 dependencies: image_picker: "^0.4.1"

次に、 flutter packages getを実行すると、それを使用するために必要なすべてのものが取り込まれ、その後、インポートしてDartで使用できるようになります。

 import 'package:image_picker/image_picker.dart';

ウィジェット

Flutterのすべてがウィジェットです。 これには、 ListViewTextBoxImageなどのユーザーインターフェイス要素のほか、レイアウト、アニメーション、ジェスチャ認識、テーマなど、フレームワークの他の部分が含まれます。

すべてをウィジェットにすることで、アプリケーション全体(ちなみにウィジェットでもある)をウィジェット階層内で表すことができます。 すべてがウィジェットであるアーキテクチャを使用すると、アプリの一部に適用される特定の属性と動作がどこから来ているのかが明確になります。 これは、プロパティと動作を一貫性のない形で関連付ける他のほとんどのアプリケーションフレームワークとは異なり、階層内の他のコンポーネントから、またはコントロール自体にそれらをアタッチする場合があります。

シンプルなUIウィジェットの例

Flutterアプリケーションへのエントリポイントが主な機能です。 ユーザーインターフェイス要素のウィジェットを画面に配置するには、 main()runApp()を呼び出し、ウィジェット階層のルートとして機能するウィジェットを渡します。

 import 'package:flutter/material.dart'; void main() { runApp( Container(color: Colors.lightBlue) ); }

これにより、画面全体に水色のContainerウィジェットが表示されます。

最小限のフラッターアプリケーション
単一のコンテナで最小限のフラッターアプリケーション(大プレビュー)

ステートレスウィジェットとステートフルウィジェット

ウィジェットには、ステートレスとステートフルの2つの種類があります。 ステートレスウィジェットは、作成および初期化された後はコンテンツを変更しませんが、ステートフルウィジェットは、アプリケーションの実行中に、たとえばユーザーの操作に応じて変更される可能性のある状態を維持します。

この例では、 FlatButtonウィジェットとTextウィジェットが画面に描画されます。 Textウィジェットは、その状態のデフォルトのStringで始まります。 ボタンを押すと状態が変化し、 Textウィジェットが更新され、新しいStringが表示されます。

ウィジェットをカプセル化するには、 StatelessWidgetまたはStatefulWidgetのいずれかから派生するクラスを作成します。 たとえば、水色のContainerは次のように記述できます。

 class MyWidget extends StatelessWidget { @override Widget build(BuildContext context) { return Container(color: Colors.lightBlue); } }

Flutterは、ウィジェットツリーに挿入されるときにウィジェットのbuildメソッドを呼び出すため、UIのこの部分をレンダリングできます。

ステートフルウィジェットの場合、 StatefulWidgetから派生します。

 class MyStatefulWidget extends StatefulWidget { MyStatefulWidget(); @override State createState() { return MyWidgetState(); } } class MyStatefulWidget extends StatefulWidget { MyStatefulWidget(); @override State createState() { return MyWidgetState(); } }

ステートフルウィジェットは、特定の状態のウィジェットツリーの構築を担当するStateクラスを返します。 状態が変化すると、ウィジェットツリーの関連部分が再構築されます。

次のコードでは、ボタンがクリックされると、 StateクラスがStringを更新します。

 class MyWidgetState extends State { String text = "some text"; @override Widget build(BuildContext context) { return Container( color: Colors.lightBlue, child: Padding( padding: const EdgeInsets.all(50.0), child: Directionality( textDirection: TextDirection.ltr, child: Column( children: [ FlatButton( child: Text('Set State'), onPressed: () { setState(() { text = "some new text"; }); }, ), Text( text, style: TextStyle(fontSize: 20.0)), ], ) ) ) ); } } class MyWidgetState extends State { String text = "some text"; @override Widget build(BuildContext context) { return Container( color: Colors.lightBlue, child: Padding( padding: const EdgeInsets.all(50.0), child: Directionality( textDirection: TextDirection.ltr, child: Column( children: [ FlatButton( child: Text('Set State'), onPressed: () { setState(() { text = "some new text"; }); }, ), Text( text, style: TextStyle(fontSize: 20.0)), ], ) ) ) ); } }

状態は、 setState()に渡される関数で更新されます。 setState()が呼び出されると、この関数は、この例の文字列など、任意の内部状態を設定できます。 次に、 buildメソッドが呼び出され、ステートフルウィジェットのツリーが更新されます。

状態変化
ユーザーインタラクションからの状態変化の処理(大プレビュー)

また、 Directionalityウィジェットを使用して、 Textウィジェットなど、サブツリー内のウィジェットを必要とするウィジェットのテキスト方向を設定することにも注意してください。 ここでの例はゼロからコードを構築しているため、ウィジェット階層のどこかでDirectionalityが必要です。 ただし、デフォルトのアプリケーションテンプレートなどでMaterialAppウィジェットを使用すると、テキストの方向が暗黙的に設定されます。

レイアウト

runApp関数は、デフォルトでウィジェットを膨らませて画面全体に表示します。 ウィジェットのレイアウトを制御するために、Flutterはさまざまなレイアウトウィジェットを提供しています。 子ウィジェットを垂直または水平に配置し、ウィジェットを拡張して特定のスペースを埋め、ウィジェットを特定の領域に制限し、画面の中央に配置し、ウィジェットを互いにオーバーラップさせるレイアウトを実行するウィジェットがあります。

一般的に使用される2つのウィジェットは、 RowColumnです。 これらのウィジェットは、子ウィジェットを水平方向(行)または垂直方向(列)に表示するためのレイアウトを実行します。

これらのレイアウトウィジェットを使用するには、子ウィジェットのリストをラップするだけです。 mainAxisAlignmentは、ウィジェットをレイアウト軸に沿って、中央、開始、終了、またはさまざまな間隔オプションで配置する方法を制御します。

次のコードは、 RowまたはColumnの複数の子ウィジェットを整列させる方法を示しています。

 class MyStatelessWidget extends StatelessWidget { @override Widget build(BuildContext context) { return Row( //change to Column for vertical layout mainAxisAlignment: MainAxisAlignment.center, children: [ Icon(Icons.android, size: 30.0), Icon(Icons.pets, size: 10.0), Icon(Icons.stars, size: 75.0), Icon(Icons.rowing, size: 25.0), ], ); } } 
行ウィジェット
水平レイアウトを示す行ウィジェット(大プレビュー)

タッチへの応答

タッチ操作は、 GestureDetectorクラスにカプセル化されたジェスチャで処理されます。 ウィジェットでもあるため、ジェスチャ認識の追加は、 GestureDetectorウィジェットをラップするのと同じくらい簡単です。

たとえば、アイコンにタッチ処理を追加するには、 IconGestureDetectorの子にし、キャプチャするジェスチャの検出器のハンドラーを設定します。

 class MyStatelessWidget extends StatelessWidget { @override Widget build(BuildContext context) { return GestureDetector( onTap: () => print('you tapped the star'), onDoubleTap: () => print('you double tapped the star'), onLongPress: () => print('you long pressed the star'), child: Icon(Icons.stars, size: 200.0), ); } }

この場合、アイコンをタップ、ダブルタップ、または長押しすると、関連するテキストが印刷されます。

 To hot reload your app on the fly, press "r". To restart the app entirely, press "R". An Observatory debugger and profiler on iPhone X is available at: https://127.0.0.1:8100/ For a more detailed help message, press "h". To quit, press "q". flutter: you tapped the star flutter: you double tapped the star flutter: you long pressed the star

単純なタップジェスチャに加えて、パンやスケーリングからドラッグまで、あらゆるものに対応する豊富なレコグナイザーがあります。 これらにより、インタラクティブなアプリケーションの構築が非常に簡単になります。

ペインティング

Flutterには、不透明度の変更、クリッピングパスの設定、装飾の適用など、さまざまなウィジェットを使用してペイントすることもできます。 CustomPaintウィジェット、および関連するCustomPainterクラスとCanvasクラスを介したカスタムペイントもサポートします。

ペイントウィジェットの1つの例は、 BoxDecorationを画面にペイントできるDecoratedBoxです。 次の例は、これを使用して画面をグラデーション塗りつぶしで塗りつぶす方法を示しています。

 class MyStatelessWidget extends StatelessWidget { @override Widget build(BuildContext context) { return new DecoratedBox( child: Icon(Icons.stars, size: 200.0), decoration: new BoxDecoration( gradient: LinearGradient( begin: Alignment.topCenter, end: Alignment.bottomCenter, colors: [Colors.red, Colors.blue, Colors.green], tileMode: TileMode.mirror ), ), ); } } 
グラデーションの背景
グラデーションの背景をペイントする(大きなプレビュー)

アニメーション

Flutterには、アニメーションの開始と停止、アニメーションの値の変更など、時間の経過に伴うアニメーションの再生を制御するAnimationControllerクラスが含まれています。 さらに、 AnimationControllerと組み合わせてアニメーションを作成できるAnimatedBuilderウィジェットがあります。

前に示した装飾された星などのウィジェットは、そのプロパティをアニメーション化できます。 たとえば、アニメーションは状態変化であるため、コードをStatefulWidgetにリファクタリングし、 AnimationControllerStateクラスに渡すと、ウィジェットの作成時にアニメーション値を使用できるようになります。

 class StarWidget extends StatefulWidget { @override State createState() { return StarState(); } } class StarState extends State with SingleTickerProviderStateMixin { AnimationController _ac; final double _starSize = 300.0; @override void initState() { super.initState(); _ac = new AnimationController( duration: Duration(milliseconds: 750), vsync: this, ); _ac.forward(); } @override Widget build(BuildContext context) { return new AnimatedBuilder( animation: _ac, builder: (BuildContext context, Widget child) { return DecoratedBox( child: Icon(Icons.stars, size: _ac.value * _starSize), decoration: BoxDecoration( gradient: LinearGradient( begin: Alignment.topCenter, end: Alignment.bottomCenter, colors: [Colors.red, Colors.blue, Colors.green], tileMode: TileMode.mirror ), ), ); } ); } } class StarWidget extends StatefulWidget { @override State createState() { return StarState(); } } class StarState extends State with SingleTickerProviderStateMixin { AnimationController _ac; final double _starSize = 300.0; @override void initState() { super.initState(); _ac = new AnimationController( duration: Duration(milliseconds: 750), vsync: this, ); _ac.forward(); } @override Widget build(BuildContext context) { return new AnimatedBuilder( animation: _ac, builder: (BuildContext context, Widget child) { return DecoratedBox( child: Icon(Icons.stars, size: _ac.value * _starSize), decoration: BoxDecoration( gradient: LinearGradient( begin: Alignment.topCenter, end: Alignment.bottomCenter, colors: [Colors.red, Colors.blue, Colors.green], tileMode: TileMode.mirror ), ), ); } ); } } class StarWidget extends StatefulWidget { @override State createState() { return StarState(); } } class StarState extends State with SingleTickerProviderStateMixin { AnimationController _ac; final double _starSize = 300.0; @override void initState() { super.initState(); _ac = new AnimationController( duration: Duration(milliseconds: 750), vsync: this, ); _ac.forward(); } @override Widget build(BuildContext context) { return new AnimatedBuilder( animation: _ac, builder: (BuildContext context, Widget child) { return DecoratedBox( child: Icon(Icons.stars, size: _ac.value * _starSize), decoration: BoxDecoration( gradient: LinearGradient( begin: Alignment.topCenter, end: Alignment.bottomCenter, colors: [Colors.red, Colors.blue, Colors.green], tileMode: TileMode.mirror ), ), ); } ); } }

この場合、値はウィジェットのサイズを変更するために使用されます。 アニメーション化された値が変更されるたびにbuilder関数が呼び出され、星のサイズが750ミリ秒にわたって変化し、効果的なスケールが作成されます。

アニメーション
アイコンサイズのアニメーション

ネイティブ機能の使用

プラットフォームチャネル

AndroidおよびiOSでネイティブプラットフォームAPIへのアクセスを提供するために、Flutterアプリケーションはプラットフォームチャネルを使用できます。 これらにより、FlutterDartコードはホスティングiOSまたはAndroidアプリケーションにメッセージを送信できます。 利用可能なオープンソースプラグインの多くは、プラットフォームチャネルを介したメッセージングを使用して構築されています。 プラットフォームチャネルの操作方法を学ぶために、Flutterドキュメントには、ネイティブバッテリーAPIへのアクセスを示す優れたドキュメントが含まれています。

結論

ベータ版でも、Flutterはクロスプラットフォームアプリケーションを構築するための優れたソリューションを提供します。 優れたツールとホットリロードにより、非常に快適な開発体験をもたらします。 豊富なオープンソースパッケージと優れたドキュメントにより、簡単に使い始めることができます。 将来的には、Flutter開発者はiOSとAndroidに加えてFuchsiaをターゲットにすることができるようになります。 エンジンのアーキテクチャの拡張性を考えると、Flutterが他のさまざまなプラットフォームに着陸するのを見ても驚かないでしょう。 コミュニティが成長している今、参加する絶好の機会です。

次のステップ

  • Flutterをインストールする
  • ダートランゲージツアー
  • FlutterCodelabs
  • FlutterUdacityコース
  • 記事のソースコード