序文
Flutter はプロジェクトを独立して完了できますが、既存のプロジェクトの場合、最良の方法は混合開発と段階的な移行です。このようにして、ネイティブ コードとフラッター コードが共存します。最も重要なことは、ネイティブがどのようにフラッター ページを開始するか、およびフラッターがネイティブとどのように対話するかです。
この記事では Android を例に、既存のプロジェクトに Flutter を導入する方法、Flutter を起動する方法、起動を高速化する方法、パラメータを渡す方法を説明します。
既存のプロジェクトに Flutter を導入する
既存の Android プロジェクトで、新しいフラッター モジュールを作成します。モジュールを作成すると、それがメイン モジュールに自動的に依存することがわかります。もちろん、このフラッター モジュールを他のプロジェクトで使用する場合、このステップは自動的に実行されないため、最初に次のように settings.gradle に登録する必要があります。
setBinding(new Binding([gradle: this]))
evaluate(new File(
settingsDir,
'flutter_module/.android/include_flutter.groovy'
))
include ':flutter_module'
次に、メインモジュールに依存します。
implementation project(path: ':flutter')
これによりハイブリッド開発が可能になります。
フラッターページを開始する
新しいフラッターモジュールを作成すると、メインページが自動的に作成されますが、ネイティブはこのページをどのように開くのでしょうか?
まずメイン モジュールのマニフェストを追加します。
<activity
android:name="io.flutter.embedding.android.FlutterActivity"
android:configChanges="orientation|keyboardHidden|keyboard|screenSize|locale|layoutDirection|fontScale|screenLayout|density|uiMode"
android:hardwareAccelerated="true"
android:windowSoftInputMode="adjustResize"
/>
次に、コードを使用して Flutter メイン ページを開きます
startActivity(FlutterActivity.createDefaultIntent(this))
では、他のページを開くにはどうすればよいでしょうか?
たとえば、2 番目に新しいフラッター ページを作成します。
import 'package:flutter/cupertino.dart';
import 'package:flutter/material.dart';
class SecondPage extends StatefulWidget{
@override
State<StatefulWidget> createState() {
return _SecondPage();
}
}
class _SecondPage extends State<SecondPage>{
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: Text("test"),
),
body:Text("test")
);
}
}
次に、このページを main.dart のアプリに登録します。
class MyApp extends StatelessWidget {
@override
Widget build(BuildContext context) {
return MaterialApp(
title: 'Flutter Demo',
theme: ThemeData(
primarySwatch: Colors.blue,
),
home: MyHomePage(title: 'Flutter Demo Home Page'),
routes: {
"second" : (BuildContext context) => SecondPage(), //也可以用其他方式注册
},
);
}
}
このように、次のコードを使用してこのページをフラッターで開くことができます。
Navigator.of(context).pushNamed("second");
Android では、次のコードを使用してページを開くことができます。
startActivity(FlutterActivity.withNewEngine().initialRoute("second").build(this))
始動を加速する
上記のコードを通じてフラッター ページを開くと、黒い画面が表示され、時間が短くならず、エクスペリエンスに大きな影響を与えます。新しいフラッター エンジンが作成されるたびに (createDefaultIntent 関数の内部は実際には withNewEngine().build(launchContext) です) ためです。
公式の解決策は、アプリケーションにキャッシュを追加するなど、エンジン キャッシュを使用することです。
var flutterEngine = FlutterEngine(this)
flutterEngine.dartExecutor.executeDartEntrypoint(
DartExecutor.DartEntrypoint.createDefault()
)
FlutterEngineCache.getInstance().put("main", flutterEngine)
次に、スタートアップを次のように変更します。
startActivity(FlutterActivity.withCachedEngine("main").build(this))
ただし、上記はメイン ページを開始する場合のみであり、2 番目のページなど、他のページを開始する場合は、引き続きキャッシュを追加する必要があります。
var flutterEngine2 = FlutterEngine(this)
flutterEngine2.navigationChannel.setInitialRoute("second")
flutterEngine2.dartExecutor.executeDartEntrypoint(
DartExecutor.DartEntrypoint.createDefault()
)
FlutterEngineCache.getInstance().put("second", flutterEngine2)
ここでルートは setInitialRoute によって設定されることに注意してください。次に、それを開始します。
startActivity(FlutterActivity.withCachedEngine("second").build(this))
エンジンをキャッシュすることにより、起動時の黒い画面時間が大幅に短縮され、ほとんど認識されなくなります (初回は画面がわずかに黒い場合があることに注意してください)。
開始パラメータの受け渡し
上記ではパラメーターを渡さずにメインページと 2 番目のページを開いたので、初期化に必要なパラメーターを渡したい場合はどうすればよいでしょうか?
現在、フラッター フレームワークはパラメーターを含む API をカプセル化していません。つまり、フラッターへのネイティブ ジャンプには正式にはパラメーターがありません。しかし、実際の現場ではこのような需要があるのですが、どのように対応すればよいのでしょうか?
公式は対応するAPIを提供していないため、ルートから方法を見つけるしかありません。まず、アプリにルートを登録する方法を変更します。上記はルートのマップ形式を直接使用しており、次のように onGenerateRoute の RouteFactory 形式に置き換えます。
onGenerateRoute: (RouteSettings settings) {
if(settings.name.startsWith("second")){
return MaterialPageRoute(builder: (BuildContext context) {
return SecondPage(settings.name);
});
}
else {
return MaterialPageRoute(builder: (BuildContext context) {
return Scaffold(
body: Center(
child: Text("page not found"),
),
);
});
}
},
ここでの settings.name はルートです。ルートの後にパラメータを追加して、最初からどのページであるかを判断できるようにしたいからです。
注: この例では、ルート URL がページに直接渡されていますが、実際にはここで解析され、マップの形式でページに渡される必要があります。
次に、2 ページ目を変更します。
class SecondPage extends StatefulWidget{
String url;
SecondPage(String url){
this.url = url;
}
@override
State<StatefulWidget> createState() {
return _SecondPage();
}
}
class _SecondPage extends State<SecondPage>{
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: Text("test"),
),
body:Text("test:${widget.url}")
);
}
}
ここでは解析は行われず、URL が直接表示され、パラメータを適切に渡すことが目的です。
最後に、次のコードをネイティブで使用します。
startActivity(FlutterActivity.withNewEngine().initialRoute("second?text=second test").build(this))
パラメータを渡すことができます。
しかし、上記の起動方法ではエンジンキャッシュを使用しないため、エンジンキャッシュを使用する場合にはアプリケーション側でルートをキャッシュに乗せられるように事前に設定する必要があるため、別の問題が発生します。しかし、パラメータを渡したいということは動的にルートを変更するということなので、この2つが矛盾してパラメータを渡すと起動が高速化できないということなのでしょうか?
パラメーターの受け渡し自体は公式の API 動作ではないため、公式のエンジン キャッシュには対応するサポートがありません。しかし、この問題は解決できないわけではなく、たとえば、Xianyu によってオープンされたフラッター ハイブリッド フレームワーク — flutter-boost は、フラッター ページを開くためのネイティブ ポーティング パラメーターを簡単に実現できます。ただし、ここには多くのことが関係しています。最も重要なことは、Flutter とネイティブの間の対話方法です。これは、私の別のブログ「[Flutter Advanced] ネイティブと対話する 3 つの方法」で説明されています。
要約する
以上で既存の Android プロジェクトに Flutter モジュールを導入してページを起動する方法を簡単に理解しましたが、既存のプロジェクトを大規模に再構築することなく Flutter を使って新規ページを開発し、段階的に Flutter に移行できるというメリットがあります。
この記事は Android プロジェクトについてのものですが、Ios プロジェクトについてはどうでしょうか? [Flutter 混合開発] 既存の iOS プロジェクトへの Flutter の導入を参照してください。