前言
實際開發混合項目時,常常會有頁面跳轉的需求
如果是原生界面和flutter界面需要互相跳轉
這種情況應該怎么處理呢?
今天這篇博客主要就來介紹下這個情況
其實想一下,這個問題可以拆成四個小的問題來分析:
1:原生界面自己怎么跳轉?
2:Flutter界面互相跳轉
2:原生界面跳轉Flutter
3:Flutter跳轉原生界面
第一點很好理解
拿android來說
就是startActivity
本篇博客主要講一下后面三點的辦法
當然,本篇博客主要講我知道的方法
如果有更好的方法,歡迎評論區留言
Flutter界面互相跳轉
Navigator跳轉
Flutter界面互相跳轉,一般使用Navigator類
比如我們要跳轉到另外一個頁面,我們可以這樣寫:
final result = await Navigator.push(context, MaterialPageRoute(builder: (context) => SecondPage(params: "firstpage",)));
使用Navigator.push進行跳轉,參數放在目標頁面的構造方法內
result 為頁面返回后傳遞的值
而SecondPage退出的時候,則可以這樣寫:
Navigator.pop(context, "hi good");
第二個參數是返回給上一個頁面的數據
getx跳轉
除了使用自帶的Navigator,我們還可以借助第三方庫
先引入getx:
get: 4.6.0
跳轉的時候:
final result = await Get.to(GetxSecondPage(params: 'firstpage',));
返回的時候:
Get.back(result: "hi good");
當然,這是最基礎的用法
我們還可以自定義路由
首先我們要使用GetMaterialApp:
class GetxJump extends StatelessWidget {const GetxJump({super.key});// This widget is the root of your application.Widget build(BuildContext context) {return GetMaterialApp(title: 'Flutter Demo',theme: ThemeData(primarySwatch: Colors.blue,),initialRoute: "/",getPages: [GetPage(name: '/', page: () => GetxFirstPage()),GetPage(name: '/GetxSecondPage', page: () => GetxSecondPage()),],// home: GetxFirstPage(),);}
}
在getPages里定義我們的路由,'/'為默認的第一個頁面
這時候就不用配置home屬性了
然后GetxFirstPage跳轉到GetxSecondPage的時候:
final result = await Get.toNamed("/GetxSecondPage",arguments: "firstpage--arguments");
返回的邏輯還是一樣的,用Get.back即可
原生界面跳轉Flutter
fragment顯示Flutter頁面
Flutter的頁面,其實都是存在于一個叫做FlutterActivity的安卓頁面上的
我們可以打開我們的任意一個Flutter項目,可以看到MainActivity是繼承的FlutterActivity
其實我們也可以把Flutter的頁面理解為一個webview
在講跳轉之前,我們先嘗試,用一個fragment來顯示Flutter頁面
我們在Flutter項目中創建一個Activity,布局如下:
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"android:layout_width="match_parent"android:layout_height="match_parent"android:orientation="vertical"android:gravity="center"><FrameLayoutandroid:id="@+id/flutterframelayout"android:layout_width="match_parent"android:layout_height="500dp"/></LinearLayout>
然后我們在Activity的onCreate方法中寫如下代碼:
var flutterFragment = FlutterFragment.createDefault()supportFragmentManager.beginTransaction().add(R.id.flutterframelayout, flutterFragment).commit()
注意,我們的Activity并沒有繼承FlutterActivity
而是直接繼承最基礎的AppCompatActivity
這時候我們運行代碼,會發現我們也顯示出了Flutter的Hello world界面
這時候flutter的頁面大小就是我們fragment的大小,所以看起來有點怪
其實我們還可以讓這個flutter頁面和我們的Activity通信
這樣一個頁面就又有原生又有flutter界面了
具體通信可以查看這篇博客:
Flutter與Native通信的方式:MethodChannel
講這個主要是為了幫助大家更好的認識Flutter
講完這點,再來介紹一個東西:
FlutterEngine
我們剛剛用FlutterFragment.createDefault()來創建了一個FlutterFragment
其實FlutterFragment還有兩種創建方式
這邊都列出來:
FlutterFragment.withNewEngine().build<FlutterFragment>() 重新創建一個Engine,指定main方法作為Flutter程序入口
FlutterFragment.createDefault() 使用默認的,指定main方法作為Flutter程序入口
FlutterFragment.withCachedEngine("engine_id") 根據engine_id找到Engine,然后Engine可以配置自己指定的程序入口
可以看到,這里面有一個Engine,那么Engine是什么呢?
這里的Engine其實指的是FlutterEngine
下面就介紹下它
認識Engine
Engine翻譯過來有引擎,發動機的意思
說明它的確是Flutter中很重要的一部分
Engine有一個很重要的作用就是負責配置Flutter頁面的方法入口
比如,我們在寫最簡單的Flutter項目時,會發現Flutter的代碼都是以main方法作為入口
就像這樣:
void main() => runApp(const MyApp());
加上前面說的,Flutter的頁面是類似以webview的形式放在安卓的Activity上面的
那么他們是怎么關聯起來的呢?
下面就會一一講到
這部分內容有點多,如果想直接看用法,
可以點到“跳轉到一個FlutterActivity”目錄查看怎么使用就好了
FlutterFragment如何被創建
我們先看下一個FlutterFragmentActivity是怎么處理的
在FlutterFragmentActivity的onCreate方法里,我們找到了一個ensureFlutterFragmentCreated方法:
這個方法的代碼我貼出來:
private void ensureFlutterFragmentCreated() {if (flutterFragment == null) {// If both activity and fragment have been destroyed, the activity restore may have// already recreated a new instance of the fragment again via the FragmentActivity.onCreate// and the FragmentManager.flutterFragment = retrieveExistingFlutterFragmentIfPossible();}if (flutterFragment == null) {// No FlutterFragment exists yet. This must be the initial Activity creation. We will create// and add a new FlutterFragment to this Activity.flutterFragment = createFlutterFragment();FragmentManager fragmentManager = getSupportFragmentManager();fragmentManager.beginTransaction().add(FRAGMENT_CONTAINER_ID, flutterFragment, TAG_FLUTTER_FRAGMENT).commit();}}
可以看到,就是調用createFlutterFragment去創建了一個Fragment
其實這個Fragment就是顯示我們Flutter頁面的Fragment
不信再來看下createFlutterFragment方法:
FlutterFragment createFlutterFragment() {final BackgroundMode backgroundMode = getBackgroundMode();final RenderMode renderMode = getRenderMode();final TransparencyMode transparencyMode =backgroundMode == BackgroundMode.opaque? TransparencyMode.opaque: TransparencyMode.transparent;final boolean shouldDelayFirstAndroidViewDraw = renderMode == RenderMode.surface;if (getCachedEngineId() != null) {Log.v(TAG,"Creating FlutterFragment with cached engine:\n"+ "Cached engine ID: "+ getCachedEngineId()+ "\n"+ "Will destroy engine when Activity is destroyed: "+ shouldDestroyEngineWithHost()+ "\n"+ "Background transparency mode: "+ backgroundMode+ "\n"+ "Will attach FlutterEngine to Activity: "+ shouldAttachEngineToActivity());return FlutterFragment.withCachedEngine(getCachedEngineId()).renderMode(renderMode).transparencyMode(transparencyMode).handleDeeplinking(shouldHandleDeeplinking()).shouldAttachEngineToActivity(shouldAttachEngineToActivity()).destroyEngineWithFragment(shouldDestroyEngineWithHost()).shouldDelayFirstAndroidViewDraw(shouldDelayFirstAndroidViewDraw).build();} else {Log.v(TAG,"Creating FlutterFragment with new engine:\n"+ "Background transparency mode: "+ backgroundMode+ "\n"+ "Dart entrypoint: "+ getDartEntrypointFunctionName()+ "\n"+ "Dart entrypoint library uri: "+ (getDartEntrypointLibraryUri() != null ? getDartEntrypointLibraryUri() : "\"\"")+ "\n"+ "Initial route: "+ getInitialRoute()+ "\n"+ "App bundle path: "+ getAppBundlePath()+ "\n"+ "Will attach FlutterEngine to Activity: "+ shouldAttachEngineToActivity());return FlutterFragment.withNewEngine().dartEntrypoint(getDartEntrypointFunctionName()).dartLibraryUri(getDartEntrypointLibraryUri()).dartEntrypointArgs(getDartEntrypointArgs()).initialRoute(getInitialRoute()).appBundlePath(getAppBundlePath()).flutterShellArgs(FlutterShellArgs.fromIntent(getIntent())).handleDeeplinking(shouldHandleDeeplinking()).renderMode(renderMode).transparencyMode(transparencyMode).shouldAttachEngineToActivity(shouldAttachEngineToActivity()).shouldDelayFirstAndroidViewDraw(shouldDelayFirstAndroidViewDraw).build();}}
protected
太長不想看的話直接看結論:
其實就是通過判斷getCachedEngineId(緩存的id)是否有緩存的id
然后來創建Fragment
如果有id,withCachedEngine方法會創建一個CachedEngineFragmentBuilder
否則就是withNewEngine創建一個NewEngineFragmentBuilder
兩個FragmentBuilder的build方法其實都是利用反射去創建了FlutterFragment
并且在createArgs方法內進行了傳參
如下:
<T extends FlutterFragment> T build() {try {("unchecked")T frag = (T) fragmentClass.getDeclaredConstructor().newInstance();if (frag == null) {throw new RuntimeException("The FlutterFragment subclass sent in the constructor ("+ fragmentClass.getCanonicalName()+ ") does not match the expected return type.");}Bundle args = createArgs();frag.setArguments(args);return frag;} catch (Exception e) {throw new RuntimeException("Could not instantiate FlutterFragment subclass (" + fragmentClass.getName() + ")", e);}}
public
等下,這種創建Fragment的方式和我們上面寫的fragment顯示Flutter頁面的例子不是一樣嗎?
對!
我們上面其實就是自己寫了個簡單的FlutterFragmentActivity
我們自己創建了FlutterFragment然后顯示了出來
而FlutterFragmentActivity默認幫我們創建了,并且默認入口是:main()方法
查看上面代碼
發現如果找不到getCachedEngineId
走的是:
FlutterFragment.withNewEngine()
所以我們再來看FlutterFragment創建的三種方式:
FlutterFragment.withNewEngine().build<FlutterFragment>() 重新創建一個Engine,指定main方法作為Flutter程序入口
FlutterFragment.createDefault() 使用默認的,指定main方法作為Flutter程序入口
FlutterFragment.withCachedEngine("engine_id") 根據engine_id找到Engine,然后Engine可以配置自己指定的程序入口
其實第二種內部調用的就是第一種,二者是一樣的
static CachedEngineFragmentBuilder withCachedEngine( String engineId) {return new CachedEngineFragmentBuilder(engineId);} public static FlutterFragment createDefault() {return new NewEngineFragmentBuilder().build();}//可以看到是一樣的 public static NewEngineFragmentBuilder withNewEngine() {return new NewEngineFragmentBuilder();}
public
那么,NewEngineFragmentBuilder和CachedEngineFragmentBuilder有什么區別呢?
FragmentBuilder
剛剛已經分析了,通過判斷getCachedEngineId(緩存的id)是否有緩存的id
有就通過CachedEngineFragmentBuilder創建Fragment
否則通過NewEngineFragmentBuilder創建Fragment
先看CachedEngineFragmentBuilder
它拿到緩存的engineId后通過Bundle 傳給了創建的FlutterFragment
Bundle args = new Bundle();
args.putString(ARG_CACHED_ENGINE_ID, engineId);
FlutterFragment通過getCachedEngineId方法得到了engineId
String getCachedEngineId() {return getArguments().getString(ARG_CACHED_ENGINE_ID, null);}
public
那么getCachedEngineId是什么時候調用的呢?
會發現這個方法是來自于FlutterActivityAndFragmentDelegate.Host接口
FlutterFragment剛好實現了這個接口
然后FlutterActivityAndFragmentDelegate的setupFlutterEngine方法中會調用getCachedEngineId
先得到緩存的cachedEngineId
然后用單例類FlutterEngineCache根據cachedEngineId就得到了FlutterEngine
String cachedEngineId = host.getCachedEngineId();if (cachedEngineId != null) {flutterEngine = FlutterEngineCache.getInstance().get(cachedEngineId);isFlutterEngineFromHost = true;if (flutterEngine == null) {throw new IllegalStateException("The requested cached FlutterEngine did not exist in the FlutterEngineCache: '"+ cachedEngineId+ "'");}return;}
而setupFlutterEngine方法是被FlutterActivityAndFragmentDelegate的onAttach調用
FlutterActivityAndFragmentDelegate的onAttach方法最終又被FlutterFragment的onAttach方法調用
如下代碼:
void onAttach( Context context) {super.onAttach(context);delegate = delegateFactory.createDelegate(this);delegate.onAttach(context);if (getArguments().getBoolean(ARG_SHOULD_AUTOMATICALLY_HANDLE_ON_BACK_PRESSED, false)) {requireActivity().getOnBackPressedDispatcher().addCallback(this, onBackPressedCallback);}context.registerComponentCallbacks(this);}
public
這時候我們就清楚了,在FlutterFragment調用onAttach生命周期方法的時候
就根據傳遞的cachedEngineId得到了緩存的FlutterEngine
那么如果用的是NewEngineFragmentBuilder,
這時候沒有cachedEngineId會怎么辦呢?其實就會直接創建一個FlutterEngine
所以我們大概了解了
兩個FragmentBuilder的區別主要在于創建FlutterFragment是否有緩存的Engineid
然后根據這種不同來決定是否使用緩存的FlutterEngine還是直接創建新的
那么FlutterEngine又有什么作用呢?
FlutterEngine
顧名思義,FlutterEngine是Flutter的引擎和發動機
我們還是看兩個NewEngineFragmentBuilder
發現FlutterFragment.withNewEngine()創建了NewEngineFragmentBuilder之后
馬上調用了dartEntrypoint(getDartEntrypointFunctionName())方法
里面調用的getDartEntrypointFunctionName方法源碼如下
String getDartEntrypointFunctionName() {try {Bundle metaData = getMetaData();String desiredDartEntrypoint =metaData != null ? metaData.getString(DART_ENTRYPOINT_META_DATA_KEY) : null;return desiredDartEntrypoint != null ? desiredDartEntrypoint : DEFAULT_DART_ENTRYPOINT;} catch (PackageManager.NameNotFoundException e) {return DEFAULT_DART_ENTRYPOINT;}}
public
可以看到,是根據key來取一個字符串
取不到這返回默認的DEFAULT_DART_ENTRYPOINT
而這個默認的值為
static final String DEFAULT_DART_ENTRYPOINT = "main";
這下我們終于看到了,就是main方法
getDartEntrypointFunctionName得到的字符串
會賦值給NewEngineFragmentBuilder的dartEntrypoint
并最終通過Bundle傳遞給FlutterFragment
args.putString(ARG_DART_ENTRYPOINT, dartEntrypoint);
FlutterFragment在getDartEntrypointFunctionName方法中得到這個參數
可以看到,得不到的時候默認取main
String getDartEntrypointFunctionName() {return getArguments().getString(ARG_DART_ENTRYPOINT, "main");}
public
現在我們可以做一個初步的總結:
FlutterFragmentActivity創建了Fragment用于顯示Flutter頁面,
然后通過創建NewEngineFragmentBuilder并調用NewEngineFragmentBuilder的dartEntrypoint方法
從而設置了Flutter的程序入口
那么具體是怎么設置的呢?
其實getDartEntrypointFunctionName方法也是來自于FlutterActivityAndFragmentDelegate.Host接口
并且被FlutterActivityAndFragmentDelegate的doInitialFlutterViewRun方法調用
doInitialFlutterViewRun方法又在FlutterActivityAndFragmentDelegate的onStart方法中被調用
FlutterActivityAndFragmentDelegate的onStart方法,被FlutterFragment的onStart方法調用
這時候我們就知道了,歸根結底還是FlutterFragment的生命周期
然后我們繼續看doInitialFlutterViewRun方法在得到這個字符串后會做什么
重點來了:
DartExecutor.DartEntrypoint entrypoint =libraryUri == null? new DartExecutor.DartEntrypoint(appBundlePathOverride, host.getDartEntrypointFunctionName()): new DartExecutor.DartEntrypoint(appBundlePathOverride, libraryUri, host.getDartEntrypointFunctionName());flutterEngine.getDartExecutor().executeDartEntrypoint(entrypoint, host.getDartEntrypointArgs());
可以看到,根據getDartEntrypointFunctionName返回的程序入口
從而創建了DartExecutor.DartEntrypoint類
并且調用flutterEngine.getDartExecutor().executeDartEntrypoint(entrypoint, host.getDartEntrypointArgs());
從而完成了入口的設置
這時候我們就清楚了,
在FlutterFragment調用onAttach生命周期方法的時候
就根據傳遞的cachedEngineId得到了緩存的FlutterEngine
沒有緩存則創建新的FlutterEngine
然后,FlutterFragment調用onStart生命周期方法的時候
會觸發FlutterActivityAndFragmentDelegate的onStart方法里面的doInitialFlutterViewRun方法
方法內部調用getDartEntrypointFunctionName從而得到程序入口
并且把這個入口配置給到了FlutterEngine
那么,下一個問題
NewEngineFragmentBuilder配置的入口可以改嗎?
當然!
我們也可以通過調用flutterEngine.getDartExecutor().executeDartEntrypoint(entrypoint, host.getDartEntrypointArgs());
完成入口的設置
文章后面就會講具體怎么設置
回到上面兩個FragmentBuilder
我們也可以知道
不管是緩存的,還是新創建的FragmentBuilder
歸根結底都要得到一個FlutterEngine
而FlutterEngine才是真正設置入口的類
他的executeDartEntrypoint方法需要傳入一個DartEntrypoint類
DartEntrypoint類的dartEntrypointFunctionName屬性用來配置入口方法的名稱
最后看下executeDartEntrypoint方法吧
內部利用flutterJNI去尋找入口,這塊我們就不繼續看下去了
public void executeDartEntrypoint( DartEntrypoint dartEntrypoint, List<String> dartEntrypointArgs) {if (isApplicationRunning) {Log.w(TAG, "Attempted to run a DartExecutor that is already running.");return;}TraceSection.begin("DartExecutor#executeDartEntrypoint");try {Log.v(TAG, "Executing Dart entrypoint: " + dartEntrypoint);flutterJNI.runBundleAndSnapshotFromLibrary(dartEntrypoint.pathToBundle,dartEntrypoint.dartEntrypointFunctionName,dartEntrypoint.dartEntrypointLibrary,assetManager,dartEntrypointArgs);isApplicationRunning = true;} finally {TraceSection.end();}}
知道了這一塊,我們做個最終總結:
1:FlutterFragmentActivity根據Intent是否傳遞過來緩存的engineid決定采用不同方式創建FlutterFragment
有緩存,則用CachedEngineFragmentBuilder
無緩存,則用NewEngineFragmentBuilder
二者都是利用反射創建了FlutterFragment
并且將engineid傳遞給了FlutterFragment
2:FlutterFragment根據是否有engineid,來得到一個FlutterEngine
有engineid,通過單例類FlutterEngineCache的Map得到緩存的FlutterEngine
無engineid,創建新的FlutterEngine
這一步在FlutterFragment的onAttach方法執行
3:FlutterFragment的onStart方法判斷是否有ARG_DART_ENTRYPOINT參數
沒有就默認返回main作為程序入口
ARG_DART_ENTRYPOINT參數的值賦值給了創建的DartExecutor.DartEntrypoint類
并最終被FlutterEngine的executeDartEntrypoint使用了這個類
從而完成入口的配置
這是關于FlutterFragmentActivity的整體邏輯
繼承的是FragmentActivity
其實還有另外一個FlutterActivity類
繼承的是Activity
并實現FlutterActivityAndFragmentDelegate.Host接口
FlutterActivity類也是可以顯示Flutter頁面的Activity
和FlutterFragmentActivity有一些區別
這塊我沒有細看了
貌似他是用FlutterActivityAndFragmentDelegate創建不同的flutterView來顯示Flutter頁面的
這塊感興趣的可以看下,目前我基本都是用FlutterFragmentActivity
了解了這么多,我們跳轉到一個FlutterActivity其實就很簡單了
跳轉到一個FlutterActivity
和上面講的NewEngineFragmentBuilder、CachedEngineFragmentBuilder一樣
Flutter提供了兩個IntentBuilder方便我們跳轉
NewEngineIntentBuilder和CachedEngineIntentBuilder
區別其實就是是否有緩存的engineid
下面介紹具體跳轉方式
NewEngineIntentBuilder
這個就比較簡單了,首先在我們的目標Activity里面創建NewEngineIntentBuilder
注意目標Activity要繼承FlutterFragmentActivity
companion object {fun NewEngineIntentBuilder(): NewEngineIntentBuilder {return NewEngineIntentBuilder(MainActivity3::class.java)}}
然后跳轉代碼為:
startActivity(MainActivity3.NewEngineIntentBuilder().build(this))
可以看到,創建了NewEngineIntentBuilder并調用它的build方法即可
build方法其實就是返回了一個intent
Intent build( Context context) {Intent intent =new Intent(context, activityClass).putExtra(EXTRA_INITIAL_ROUTE, initialRoute).putExtra(EXTRA_BACKGROUND_MODE, backgroundMode).putExtra(EXTRA_DESTROY_ENGINE_WITH_ACTIVITY, true);if (dartEntrypointArgs != null) {intent.putExtra(EXTRA_DART_ENTRYPOINT_ARGS, new ArrayList(dartEntrypointArgs));}return intent;}
public
通過這種方式,最終目標Activity創建Fragment的時候
用的將會是NewEngineFragmentBuilder
從而創建新的FlutterEngine
而這種跳轉頁面的方式,只能跳轉到Flutter的main方法
也就是不能決定Flutter的程序入口
要改變入口,還是需要FlutterEngine
CachedEngineIntentBuilder
使用CachedEngineIntentBuilder跳轉
一樣在目標Activity里創建CachedEngineIntentBuilder
companion object {fun withCachedEngine(cachedEngineId: String): CachedEngineIntentBuilder {return CachedEngineIntentBuilder(MainActivity3::class.java, cachedEngineId)}}
跳轉的代碼這樣寫:
startActivity(MainActivity3.withCachedEngine(App.your_engine_id).build(this).apply {putExtra("method", "showtestpage")putExtra("params", "參數params")})
其實也是創建了CachedEngineIntentBuilder并調用它的build方法即可
build方法其實也是返回了一個intent
Intent build( Context context) {return new Intent(context, activityClass).putExtra(EXTRA_CACHED_ENGINE_ID, cachedEngineId).putExtra(EXTRA_DESTROY_ENGINE_WITH_ACTIVITY, destroyEngineWithActivity).putExtra(EXTRA_BACKGROUND_MODE, backgroundMode);}
public
那么區別在哪呢?
就在于withCachedEngine方法內傳遞的cachedEngineId
這個cachedEngineId需要我們定義好
我這里是定義在Application里
定義好之后,我們再去創建FlutterEngine
private val flutterEngine by lazy {//顯示默認的頁面,會找flutter的main方法FlutterEngine(this).apply {dartExecutor.executeDartEntrypoint(DartExecutor.DartEntrypoint.createDefault())}}
然后把創建的FlutterEngine存入到單例類FlutterEngineCache中
FlutterEngineCache.getInstance().put(your_engine_id, flutterEngine)
這里your_engine_id就是key
要和withCachedEngine方法內傳遞的cachedEngineId保持一致才可以找到對應的FlutterEngine
通過這種方式,最終目標Activity創建Fragment的時候
用的將會是CachedEngineFragmentBuilder
從而找到緩存的FlutterEngine
而這種跳轉頁面的方式,
可以通過創建DartExecutor.DartEntrypoint類
并且調用flutterEngine.getDartExecutor().executeDartEntrypoint(entrypoint, host.getDartEntrypointArgs());
便完成了入口的設置
入口和傳參
那么具體怎么設置呢?
其實就在于我們創建FlutterEngine的方式
其實創建FlutterEngine的方式和創建FlutterFragment創建的方式差不多:
FlutterFragment提供這三種方式創建(第一種和第二種其實一樣):
FlutterFragment.withNewEngine().build<FlutterFragment>() 重新創建一個Engine,指定main方法作為Flutter程序入口
FlutterFragment.createDefault() 使用默認的,指定main方法作為Flutter程序入口
FlutterFragment.withCachedEngine("engine_id") 根據engine_id找到Engine,然后Engine可以配置自己指定的程序入口
FlutterEngine則是提供三種方式創建(第一種和第二種其實一樣):
FlutterEngine(this).apply {dartExecutor.executeDartEntrypoint(DartExecutor.DartEntrypoint.createDefault())
}//createDefault內部其實就是這樣寫的
FlutterEngine(this).apply {var entrypoint=DartEntrypoint(FlutterMain.findAppBundlePath(), "main")dartExecutor.executeDartEntrypoint(entrypoint)
}FlutterEngine(this).apply {var entrypoint = DartExecutor.DartEntrypoint(FlutterMain.findAppBundlePath(), "testMethod")dartExecutor.executeDartEntrypoint(entrypoint)
}
這里testMethod就是我們定義的程序入口了
這樣在跳轉到我們目標Activity后
Flutter就會加載main.dart文件里的testMethod()方法作為程序入口了
注意:
testMethod()必須寫在main.dart文件里
這塊我暫時也沒找到其他方法
頁面跳轉有了,那么傳參呢?
可以看到我剛剛跳轉的時候傳遞了兩個參數給Intent
putExtra("method", "showtestpage")putExtra("params", "參數params")
那么在目標Activity的configureFlutterEngine方法里我們可以這樣處理:
override fun configureFlutterEngine(flutterEngine: FlutterEngine) {super.configureFlutterEngine(flutterEngine)var channel = MethodChannel(flutterEngine.dartExecutor.binaryMessenger, "paramsChannel")intent?.apply {val path = getStringExtra("method") ?: ""val params = getStringExtra("params") ?: ""Log.d("MainActivity3", "path是:"+path)Log.d("MainActivity3", "params是:"+params)channel.invokeMethod(path, params)}}
會發現其實還是用MethodChannel進行通信
然后來到Flutter端的代碼
定義MethodChannel
var commonNativeChannel = MethodChannel('paramsChannel');
在initState里面:
void initState() {commonNativeChannel.setMethodCallHandler((MethodCall call) async {print('拿到參數: = ${call.method}');switch (call.method) {case 'showtestpage':print('拿到參數: = ${call.arguments}');//這里可以根據參數設置跳轉不同page,我這里刷新一下顯示,不做跳轉處理setState(() {params = call.arguments.toString();});break;default:print('Unknowm method ${call.method}');//觸發Android端的notImplemented方法throw MissingPluginException();}});super.initState();}
這樣,指定跳轉Flutter頁面和傳參就完成了
Flutter跳轉原生界面
Flutter跳轉指定原生頁面需要用到MethodChannel進行通信
對MethodChannel不了解的可以看我的這篇博客:
Flutter與Native通信的方式:MethodChannel
下面展示具體的用法:
首先是在Flutter端定義好MethodChannel的字段和方法
這里用兩個按鈕的點擊事件來觸發跳轉
分別調用安卓端的jumpActivity和finish方法
當然也可以傳參,具體傳參可以看上面的MethodChannel博客
class _MyjumpAndroidPageState extends State<MyjumpAndroidPage> {var commonNativeChannel = MethodChannel('ForNativePlugin');void initState() {super.initState();}Widget build(BuildContext context) {return Scaffold(appBar: AppBar(title: Text(widget.title),),body: Center(child: Column(mainAxisAlignment: MainAxisAlignment.center,children: <Widget>[ElevatedButton(onPressed: () {commonNativeChannel.invokeMethod("jumpActivity");}, child: Text("點擊跳轉到android頁面")),ElevatedButton(onPressed: () {commonNativeChannel.invokeMethod("finish");}, child: Text("點擊關閉當前Flutter頁面"))],),),);}
}
然后來到我們安卓端
繼承FlutterFragmentActivity類
照樣創建MethodChannel
在configureFlutterEngine方法中
對這些被調用的方法進行處理即可
我這邊只是打印了下日志
正常情況下StartActivity即可
class MainActivity4 : FlutterFragmentActivity() {private lateinit var flutterMethodChannel: MethodChannelcompanion object {fun withCachedEngine(cachedEngineId: String): CachedEngineIntentBuilder {return CachedEngineIntentBuilder(MainActivity4::class.java, cachedEngineId)}}override fun configureFlutterEngine(flutterEngine: FlutterEngine) {super.configureFlutterEngine(flutterEngine)flutterMethodChannel = MethodChannel(flutterEngine.dartExecutor, "ForNativePlugin")flutterMethodChannel.setMethodCallHandler { methodCall, result ->when (methodCall.method) {"finish" -> {Toast.makeText(this, "關閉頁面", Toast.LENGTH_SHORT).show()finish()}"jumpActivity" -> {Toast.makeText(this, "跳轉頁面", Toast.LENGTH_SHORT).show()}else -> {// 表明沒有對應實現result.notImplemented()}}}}
}
源碼
關于Flutter與Native頁面互相跳轉大體上就是這么多內容了
下面分享下這篇文章涉及到的源碼地址:
new_gradlesetting_native_jump_flutter