赞
踩
本文主要讲解 Flutter 和 Android原生之间,页面相互跳转、传参,
但其中用到了两端相互通信的知识,非常建议先看完这篇 讲解通信的文章:
当前案例 Flutter SDK版本:3.13.2
Flutter使用多个轻量型引擎,进行混合开发,是从 2.0 开始的,大大的减轻了内存压力;
轻量型引擎开发-官方介绍视频:https://www.youtube.com/watch?v=p6cK_0jp2ag
如果是纯Flutter开发,只会有一个Flutter引擎,如果是混合开发,原生端 需要为每个从原生端跳转的Flutter页面创建独立的引擎 (也可以单例,但单例写法需要处理一些问题,这个到 FlutterEngineGroup 章节会具体讲解);
比如:
1.0 从 Android_A页面 ==== 跳转到 ==== Flutter_A页面,Android端需要为Flutter_A页面,创建Flutter引擎;
1.1 紧接着,从 Flutter_A页面 ==== 跳转到 ==== Flutter_B页面,就不需要,它俩共用一个引擎;
1.2 每个Flutter引擎都有自己的路由栈,且这个路由栈只能管理Flutter页面;
1.3 使用Flutter提供的 Navigator.pop(context); 方法,可以从 Flutter_B页面 回退到 Flutter_A页面,但无法从 Flutter_A页面 回退到 Android_A,会黑屏,因为Flutter栈里面没有Android页面,可以使用 Navigator.canPop(context); 来检查Flutter路由栈中,是否还有其他路由;
1.4 如果不使用 Navigator.pop(context); 回退方法,使用手机自带的 Back按键 / 左滑屏幕 进行回退,是没有问题的,因为这种回退方式调用的是原生API,Android原生不光提供FlutterView渲染Flutter页面结果,还会创建FlutterActivity和FlutterView进行绑定;
1.5 看到这,大家应该理解,为什么说Flutter只是UI框架了吧;
FlutterRouterManager.kt
- package com.example.flutter_nav_android.util
-
- import android.app.Activity
- import io.flutter.embedding.android.FlutterActivity
- import io.flutter.embedding.android.FlutterActivityLaunchConfigs
- import io.flutter.embedding.engine.FlutterEngine
- import io.flutter.embedding.engine.FlutterEngineCache
- import io.flutter.embedding.engine.dart.DartExecutor
-
- class FlutterRouterManager(
- val targetRoute: String,
- val mEngineId: String,
- val mContext: Activity
- ) {
-
- var mEngine: FlutterEngine? = null
-
- init {
- createCachedEngine()
- }
-
- companion object {
-
- /**
- * 获取缓存中的 Flutter引擎
- */
- @JvmStatic
- fun getEngineCacheInstance(engineId: String): FlutterEngine? {
- return FlutterEngineCache.getInstance().get(engineId)
- }
-
- }
-
- /**
- * 1、创建Flutter引擎
- * 2、将初始命名路由,修改为目标页面路由
- * 3、缓存Flutter引擎
- */
- fun createCachedEngine(): FlutterEngine {
- val flutterEngine = FlutterEngine(mContext) // 创建Flutter引擎
-
- // 将初始命名路由,修改为目标页面路由
- flutterEngine.navigationChannel.setInitialRoute(targetRoute)
-
- // 这一步,是在执行相关Dart文件入口的 main函数,将Flutter页面渲染出结果
- // 原生端获取结果,进行最终渲染上屏
- flutterEngine.dartExecutor.executeDartEntrypoint(
- DartExecutor.DartEntrypoint.createDefault()
- )
-
- // 将加载好的引擎,存储起来
- FlutterEngineCache.getInstance().put(mEngineId, flutterEngine)
-
- mEngine = flutterEngine
-
- return flutterEngine
- }
-
- /**
- * 根据引擎ID,前往指定的Flutter页面
- */
- fun push() {
- // 创建新的引擎(了解即可)
- // mContext.startActivity(
- // FlutterActivity
- // .withNewEngine() // 创建引擎
- // .backgroundMode(FlutterActivityLaunchConfigs.BackgroundMode.transparent) // 背景改为透明,不然切换页面时,会闪烁黑色
- // .build(mContext))
-
- // 使用缓存好的引擎(推荐)
- mContext.startActivity(
- FlutterActivity
- .withCachedEngine(mEngineId) // 获取缓存好的引擎
- .backgroundMode(FlutterActivityLaunchConfigs.BackgroundMode.transparent) // 背景改为透明,不然切换页面时,会闪烁黑色
- .build(mContext)
- )
- }
-
- /**
- * 销毁当前Flutter引擎
- */
- fun destroy() {
- mEngine?.destroy()
- }
-
- }
PersonalActivity.kt
- package com.example.flutter_nav_android.ui.activity
-
- import android.graphics.Color
- import android.os.Bundle
- import android.text.SpannableString
- import android.text.Spanned
- import android.text.style.ForegroundColorSpan
- import android.view.View
- import androidx.appcompat.app.AppCompatActivity
- import com.example.flutter_nav_android.databinding.ActivityPersonalBinding
- import com.example.flutter_nav_android.util.FlutterRouterManager
- import io.flutter.embedding.engine.FlutterEngine
- import io.flutter.plugin.common.MethodCall
- import io.flutter.plugin.common.MethodChannel
-
- class PersonalActivity : AppCompatActivity(), MethodChannel.MethodCallHandler, View.OnClickListener {
-
- private lateinit var bind: ActivityPersonalBinding
-
- private lateinit var homeFlutterEngine: FlutterEngine
- private lateinit var loginRouterManager: FlutterRouterManager
-
- private lateinit var loginMethodChannel: MethodChannel
- private lateinit var homeMethodChannel: MethodChannel
-
- private val METHOD_CHANNEL_LOGIN = "com.example.flutter_nav_android/login/method"
- private val METHOD_CHANNEL_HOME = "com.example.flutter_nav_android/home/method"
- private val NAV_FLUTTER_LOGIN_NOTICE = "navFlutterLoginNotice"
- private val POP_NOTICE = "popNotice"
- private val PERSONAL_POP_NOTICE = "personalPopNotice"
-
- override fun onCreate(savedInstanceState: Bundle?) {
- super.onCreate(savedInstanceState)
- bind = ActivityPersonalBinding.inflate(layoutInflater)
- setContentView(bind.root)
- initView()
-
- loginRouterManager = FlutterRouterManager("/login", "login_engine", this)
-
- // 两端建立通信
- loginMethodChannel = MethodChannel(loginRouterManager.mEngine!!.dartExecutor, METHOD_CHANNEL_LOGIN)
- loginMethodChannel.setMethodCallHandler(this)
-
- // 获取 Flutter Home页面的引擎,并且建立通信
- homeFlutterEngine = FlutterRouterManager.getEngineCacheInstance("home_engine")!!
- homeMethodChannel = MethodChannel(homeFlutterEngine.dartExecutor,METHOD_CHANNEL_HOME)
- }
-
- /**
- * 监听来自 Flutter端 的消息通道
- *
- * call: Android端 接收到 Flutter端 发来的 数据对象
- * result:Android端 给 Flutter端 执行回调的接口对象
- */
- override fun onMethodCall(call: MethodCall, result: MethodChannel.Result) {
- val methodName: String = call.method
- when (methodName) { // 销毁 Flutter Login 页面
- POP_NOTICE -> {
- val age = call.argument<Int>("age")
- getResult(age.toString())
- loginRouterManager.mEngine!!.navigationChannel.popRoute()
- }
- else -> {
- result.notImplemented()
- }
- }
- }
-
- override fun onClick(v: View?) {
- when (v) {
- bind.toFlutter -> { // 前往 Flutter Login 页面
- val map: MutableMap<String, String> = mutableMapOf<String, String>()
- map["name"] = "老王"
- loginMethodChannel.invokeMethod(NAV_FLUTTER_LOGIN_NOTICE,map)
- loginRouterManager.push()
- }
- bind.pop -> { // 销毁 Android Personal 页面
- val map: MutableMap<String, Int> = mutableMapOf<String, Int>()
- map["age"] = 18
- homeMethodChannel.invokeMethod(PERSONAL_POP_NOTICE,map)
- finish()
- }
- }
- }
-
- /**
- * 初始化页面
- */
- private fun initView() {
- bind.toFlutter.setOnClickListener(this)
- bind.pop.setOnClickListener(this)
-
- var name = intent.getStringExtra("name")
- val title = "接收初始化参数:"
- val msg = title + name
-
- val ss = SpannableString(msg)
- ss.setSpan(
- ForegroundColorSpan(Color.RED),
- title.length,
- msg.length,
- Spanned.SPAN_INCLUSIVE_EXCLUSIVE
- )
- bind.initV.text = ss
- }
-
- /**
- * 获取上一页的返回参数
- */
- private fun getResult(age: String) {
- val title = "接收上一页返回参数:"
- val msg = title + age
-
- val ss = SpannableString(msg)
- ss.setSpan(
- ForegroundColorSpan(Color.RED),
- title.length,
- msg.length,
- Spanned.SPAN_INCLUSIVE_EXCLUSIVE
- )
- bind.resultV.text = ss
- }
-
- override fun onDestroy() {
- super.onDestroy()
- loginRouterManager.destroy()
- }
-
- }
SchoolActivity.kt
- package com.example.flutter_nav_android.ui.activity
-
- import android.content.Intent
- import android.os.Bundle
- import androidx.appcompat.app.AppCompatActivity
- import com.example.flutter_nav_android.databinding.ActivitySchoolBinding
- import com.example.flutter_nav_android.util.FlutterRouterManager
- import io.flutter.embedding.android.FlutterFragment
- import io.flutter.embedding.android.TransparencyMode
- import io.flutter.embedding.engine.FlutterEngineCache
- import io.flutter.plugin.common.MethodChannel
-
-
- class SchoolActivity : AppCompatActivity() {
-
- private lateinit var bind: ActivitySchoolBinding
- private lateinit var bookFragment: FlutterFragment
- private lateinit var studentFragment: FlutterFragment
-
- private val METHOD_CHANNEL_BOOK = "com.example.flutter_nav_android/book/method"
- private val METHOD_CHANNEL_STUDENT = "com.example.flutter_nav_android/student/method"
-
- private val NAV_FLUTTER_BOOK_NOTICE = "navFlutterBookNotice"
- private val NAV_FLUTTER_STUDENT_NOTICE = "navFlutterStudentNotice"
-
- override fun onCreate(savedInstanceState: Bundle?) {
- super.onCreate(savedInstanceState)
- bind = ActivitySchoolBinding.inflate(layoutInflater)
- setContentView(bind.root)
- initView()
- initChannel()
- }
-
- /**
- * 建立通信
- */
- private fun initChannel() {
- val bookEngine = FlutterRouterManager.getEngineCacheInstance("book_engine")
- val bookChannel = MethodChannel(bookEngine!!.dartExecutor,METHOD_CHANNEL_BOOK)
- val map: MutableMap<String, String> = mutableMapOf<String, String>()
- map["title"] = "Book"
- bookChannel.invokeMethod(NAV_FLUTTER_BOOK_NOTICE,map)
-
- val studentEngine = FlutterRouterManager.getEngineCacheInstance("student_engine")
- val studentChannel = MethodChannel(studentEngine!!.dartExecutor,METHOD_CHANNEL_STUDENT)
- val map2: MutableMap<String, String> = mutableMapOf<String, String>()
- map2["title"] = "Student"
- studentChannel.invokeMethod(NAV_FLUTTER_STUDENT_NOTICE,map2)
- }
-
- /**
- * 初始化页面
- */
- private fun initView() {
- bookFragment = FlutterFragment.withCachedEngine("book_engine")
- .transparencyMode(TransparencyMode.transparent) // 背景透明,避免切换页面,出现闪烁
- .shouldAttachEngineToActivity(false) // 是否让Flutter控制Activity,true:可以 false:不可以,默认值 true
- .build()
-
- supportFragmentManager
- .beginTransaction()
- .add(bind.bookFragment.id, bookFragment)
- .commit()
-
- studentFragment = FlutterFragment.withCachedEngine("student_engine")
- .transparencyMode(TransparencyMode.transparent) // 背景透明,避免切换页面,出现闪烁
- .shouldAttachEngineToActivity(false) // 是否让Flutter控制Activity,true:可以 false:不可以,默认值 true
- .build()
-
- supportFragmentManager
- .beginTransaction()
- .add(bind.studentFragment.id, studentFragment)
- .commit()
- }
-
- // ================================ 这些是固定写法,Flutter需要这些回调 ================================
-
- override fun onPostResume() {
- super.onPostResume()
- bookFragment.onPostResume()
- studentFragment.onPostResume()
- }
-
- override fun onNewIntent(intent: Intent) {
- super.onNewIntent(intent)
- bookFragment.onNewIntent(intent)
- studentFragment.onNewIntent(intent)
- }
-
- override fun onBackPressed() {
- bookFragment.onBackPressed()
- studentFragment.onBackPressed()
- }
-
- override fun onRequestPermissionsResult(
- requestCode: Int,
- permissions: Array<String?>,
- grantResults: IntArray
- ) {
- super.onRequestPermissionsResult(requestCode, permissions, grantResults)
- bookFragment.onRequestPermissionsResult(
- requestCode,
- permissions,
- grantResults
- )
- studentFragment.onRequestPermissionsResult(
- requestCode,
- permissions,
- grantResults
- )
- }
-
- override fun onUserLeaveHint() {
- bookFragment.onUserLeaveHint()
- studentFragment.onUserLeaveHint()
- }
-
- override fun onTrimMemory(level: Int) {
- super.onTrimMemory(level)
- bookFragment.onTrimMemory(level)
- studentFragment.onTrimMemory(level)
- }
-
- }
MainActivity.kt
- package com.example.flutter_nav_android.ui.activity
-
- import android.content.Intent
- import android.graphics.Color
- import android.text.SpannableString
- import android.text.Spanned
- import android.text.style.ForegroundColorSpan
- import android.view.View
- import androidx.appcompat.app.AppCompatActivity
- import com.example.flutter_nav_android.databinding.ActivityMainBinding
- import com.example.flutter_nav_android.util.FlutterRouterManager
- import io.flutter.plugin.common.MethodCall
- import io.flutter.plugin.common.MethodChannel
-
-
- class MainActivity : AppCompatActivity(), MethodChannel.MethodCallHandler, View.OnClickListener {
-
- private lateinit var bind: ActivityMainBinding
-
- private lateinit var homeMethodChannel: MethodChannel
-
- private val METHOD_CHANNEL_HOME = "com.example.flutter_nav_android/home/method"
- private val NAV_ANDROID_PERSONAL_NOTICE = "navAndroidPersonalNotice"
- private val NAV_FLUTTER_HOME_NOTICE = "navFlutterHomeNotice"
- private val POP_NOTICE = "popNotice"
-
- private lateinit var homeRouterManager: FlutterRouterManager
- private lateinit var bookRouterManager: FlutterRouterManager
- private lateinit var studentRouterManager: FlutterRouterManager
-
- override fun onCreate(savedInstanceState: android.os.Bundle?) {
- super.onCreate(savedInstanceState)
- bind = ActivityMainBinding.inflate(layoutInflater)
- setContentView(bind.root)
- bind.toFlutter.setOnClickListener(this)
- bind.toFlutterFragment.setOnClickListener(this)
-
- homeRouterManager = FlutterRouterManager("/home", "home_engine", this)
-
- // 两端建立通信
- homeMethodChannel = MethodChannel(homeRouterManager.mEngine!!.dartExecutor,METHOD_CHANNEL_HOME)
- homeMethodChannel.setMethodCallHandler(this)
-
- // 这里Fragment案例的
- bookRouterManager = FlutterRouterManager("/book", "book_engine", this)
- studentRouterManager = FlutterRouterManager("/student", "student_engine", this)
- }
-
- /**
- * 监听来自 Flutter端 的消息通道
- *
- * call: Android端 接收到 Flutter端 发来的 数据对象
- * result:Android端 给 Flutter端 执行回调的接口对象
- */
- override fun onMethodCall(call: MethodCall, result: MethodChannel.Result) {
- val methodName: String = call.method
- when (methodName) {
- NAV_ANDROID_PERSONAL_NOTICE -> { // Flutter Home 页面 前往 Android Personal 页面
- val intent = Intent(this, PersonalActivity::class.java)
- intent.putExtra("name",call.argument<String>("name"))
- startActivity(intent)
- }
- POP_NOTICE -> { // 销毁 Flutter Home 页面
- val age = call.argument<Int>("age")
- getResult(age.toString())
- homeRouterManager.mEngine!!.navigationChannel.popRoute()
- }
- else -> {
- result.notImplemented()
- }
- }
- }
-
- override fun onClick(v: View?) {
- when (v) {
- bind.toFlutter -> { // 前往 Flutter Home 页面
- val map: MutableMap<String, String> = mutableMapOf<String, String>()
- map["name"] = "张三"
- homeMethodChannel.invokeMethod(NAV_FLUTTER_HOME_NOTICE,map)
- homeRouterManager.push()
- }
- bind.toFlutterFragment -> {
- val intent = Intent(this, SchoolActivity::class.java)
- startActivity(intent)
- }
- }
- }
-
- /**
- * 获取上一页的返回参数
- */
- private fun getResult(age: String) {
- val title = "接收上一页返回参数:"
- val msg = title + age
-
- val ss = SpannableString(msg)
- ss.setSpan(
- ForegroundColorSpan(Color.RED),
- title.length,
- msg.length,
- Spanned.SPAN_INCLUSIVE_EXCLUSIVE
- )
- bind.resultV.text = ss
- }
-
- override fun onDestroy() {
- super.onDestroy()
- homeRouterManager.destroy()
- bookRouterManager.destroy()
- studentRouterManager.destroy()
- }
-
- }
activity_personal.xml
- <?xml version="1.0" encoding="utf-8"?>
- <layout xmlns:android="http://schemas.android.com/apk/res/android"
- xmlns:app="http://schemas.android.com/apk/res-auto">
-
- <data>
-
- </data>
-
- <androidx.constraintlayout.widget.ConstraintLayout
- android:layout_width="match_parent"
- android:layout_height="match_parent">
-
- <TextView
- android:id="@+id/app_bar"
- android:layout_width="match_parent"
- android:layout_height="60dp"
- android:layout_marginBottom="16dp"
- android:background="@color/cardview_shadow_start_color"
- android:gravity="center_vertical"
- android:paddingStart="16dp"
- android:text="Android Personal"
- android:textColor="@color/material_dynamic_primary60"
- android:textSize="26sp"
- android:textStyle="bold"
- app:layout_constraintLeft_toLeftOf="parent"
- app:layout_constraintTop_toTopOf="parent" />
-
- <TextView
- android:id="@+id/init_v"
- android:layout_width="wrap_content"
- android:layout_height="wrap_content"
- android:gravity="center_vertical"
- android:text="接收初始化参数:"
- android:textColor="@color/material_dynamic_primary60"
- android:textSize="20sp"
- android:textStyle="bold"
- app:layout_constraintBottom_toBottomOf="parent"
- app:layout_constraintLeft_toLeftOf="parent"
- app:layout_constraintRight_toRightOf="parent"
- app:layout_constraintTop_toTopOf="parent" />
-
- <TextView
- android:id="@+id/result_v"
- android:layout_width="wrap_content"
- android:layout_height="wrap_content"
- android:layout_marginTop="20dp"
- android:text="接收上一页返回参数:"
- android:textColor="@color/material_dynamic_primary60"
- android:textSize="20sp"
- android:textStyle="bold"
- app:layout_constraintLeft_toLeftOf="parent"
- app:layout_constraintRight_toRightOf="parent"
- app:layout_constraintTop_toBottomOf="@id/init_v" />
-
- <Button
- android:id="@+id/to_flutter"
- android:layout_width="wrap_content"
- android:layout_height="wrap_content"
- android:layout_marginTop="20dp"
- android:text="前往 Flutter Login"
- android:textSize="20sp"
- android:textAllCaps="false"
- app:layout_constraintLeft_toLeftOf="parent"
- app:layout_constraintRight_toRightOf="parent"
- app:layout_constraintTop_toBottomOf="@id/result_v" />
-
- <Button
- android:id="@+id/pop"
- android:layout_width="wrap_content"
- android:layout_height="wrap_content"
- android:layout_marginTop="16dp"
- android:text="返回 上一页"
- android:textSize="20sp"
- android:textAllCaps="false"
- app:layout_constraintLeft_toLeftOf="parent"
- app:layout_constraintRight_toRightOf="parent"
- app:layout_constraintTop_toBottomOf="@id/to_flutter" />
-
- </androidx.constraintlayout.widget.ConstraintLayout>
-
- </layout>
activity_school.xml
- <?xml version="1.0" encoding="utf-8"?>
- <layout xmlns:android="http://schemas.android.com/apk/res/android">
-
- <data>
-
- </data>
-
- <LinearLayout
- android:layout_width="match_parent"
- android:layout_height="match_parent"
- android:paddingTop="20dp"
- android:orientation="vertical">
-
- <TextView
- android:id="@+id/app_bar"
- android:layout_width="match_parent"
- android:layout_height="60dp"
- android:background="@color/cardview_shadow_start_color"
- android:gravity="center_vertical"
- android:paddingStart="16dp"
- android:text="Android School"
- android:textColor="@color/material_dynamic_primary60"
- android:textSize="26sp"
- android:textStyle="bold" />
-
- <FrameLayout
- android:id="@+id/book_fragment"
- android:layout_weight="1"
- android:layout_width="match_parent"
- android:layout_height="0dp" />
-
- <FrameLayout
- android:id="@+id/student_fragment"
- android:layout_weight="1"
- android:layout_width="match_parent"
- android:layout_height="0dp" />
-
- </LinearLayout>
- </layout>
activity_main.xml
- <?xml version="1.0" encoding="utf-8"?>
- <layout xmlns:android="http://schemas.android.com/apk/res/android"
- xmlns:app="http://schemas.android.com/apk/res-auto">
-
- <data>
-
- </data>
-
- <androidx.constraintlayout.widget.ConstraintLayout
- android:layout_width="match_parent"
- android:layout_height="match_parent">
-
- <TextView
- android:id="@+id/app_bar"
- android:layout_width="match_parent"
- android:layout_height="60dp"
- android:layout_marginBottom="16dp"
- android:background="@color/cardview_shadow_start_color"
- android:gravity="center_vertical"
- android:paddingStart="16dp"
- android:text="Android Main"
- android:textColor="@color/material_dynamic_primary60"
- android:textSize="26sp"
- android:textStyle="bold"
- app:layout_constraintLeft_toLeftOf="parent"
- app:layout_constraintTop_toTopOf="parent" />
-
- <TextView
- android:id="@+id/result_v"
- android:layout_width="wrap_content"
- android:layout_height="wrap_content"
- android:gravity="center_vertical"
- android:text="接收上一页返回参数:"
- android:textColor="@color/material_dynamic_primary60"
- android:textSize="20sp"
- android:textStyle="bold"
- app:layout_constraintLeft_toLeftOf="parent"
- app:layout_constraintTop_toTopOf="parent"
- app:layout_constraintRight_toRightOf="parent"
- app:layout_constraintBottom_toBottomOf="parent"/>
-
- <Button
- android:id="@+id/to_flutter"
- android:layout_width="wrap_content"
- android:layout_height="wrap_content"
- android:layout_marginTop="20dp"
- android:text="前往 Flutter Home"
- android:textSize="20sp"
- android:textAllCaps="false"
- app:layout_constraintLeft_toLeftOf="parent"
- app:layout_constraintTop_toBottomOf="@id/result_v"
- app:layout_constraintRight_toRightOf="parent" />
-
- <Button
- android:id="@+id/to_flutter_fragment"
- android:layout_width="wrap_content"
- android:layout_height="wrap_content"
- android:layout_marginTop="20dp"
- android:text="前往 Flutter Fragment"
- android:textSize="20sp"
- android:textAllCaps="false"
- app:layout_constraintLeft_toLeftOf="parent"
- app:layout_constraintTop_toBottomOf="@id/to_flutter"
- app:layout_constraintRight_toRightOf="parent" />
-
- </androidx.constraintlayout.widget.ConstraintLayout>
-
- </layout>
AndroidManifest.xml
- <manifest xmlns:android="http://schemas.android.com/apk/res/android">
-
-
- <application
- android:name="${applicationName}"
- android:icon="@mipmap/ic_launcher"
- android:label="flutter_nav_android"
- android:theme="@style/AppTheme">
-
- <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" />
-
- <activity android:name=".ui.activity.SchoolActivity" android:exported="true"/>
-
- <activity android:name=".ui.activity.PersonalActivity" android:exported="true"/>
-
- <activity
- android:name=".ui.activity.MainActivity"
- android:configChanges="orientation|keyboardHidden|keyboard|screenSize|smallestScreenSize|locale|layoutDirection|fontScale|screenLayout|density|uiMode"
- android:exported="true"
- android:hardwareAccelerated="true"
- android:launchMode="singleTop"
- android:windowSoftInputMode="adjustResize">
- <!-- Specifies an Android theme to apply to this Activity as soon as
- the Android process has started. This theme is visible to the user
- while the Flutter UI initializes. After that, this theme continues
- to determine the Window background behind the Flutter UI. -->
- <meta-data
- android:name="io.flutter.embedding.android.NormalTheme"
- android:resource="@style/NormalTheme" />
- <intent-filter>
- <action android:name="android.intent.action.MAIN" />
- <category android:name="android.intent.category.LAUNCHER" />
- </intent-filter>
- </activity>
- <!-- Don't delete the meta-data below.
- This is used by the Flutter tool to generate GeneratedPluginRegistrant.java -->
- <meta-data
- android:name="flutterEmbedding"
- android:value="2" />
- </application>
- <!-- Required to query activities that can process text, see:
- https://developer.android.com/training/package-visibility?hl=en and
- https://developer.android.com/reference/android/content/Intent#ACTION_PROCESS_TEXT.
- In particular, this is used by the Flutter engine in io.flutter.plugin.text.ProcessTextPlugin. -->
- <queries>
- <intent>
- <action android:name="android.intent.action.PROCESS_TEXT" />
- <data android:mimeType="text/plain" />
- </intent>
- </queries>
- </manifest>
book.dart
- import 'package:flutter/material.dart';
- import 'package:flutter/services.dart';
-
- class Book extends StatefulWidget {
- const Book({super.key});
-
- @override
- State<Book> createState() => _BookState();
- }
-
- class _BookState extends State<Book> {
-
- String title = '';
-
- static const String METHOD_CHANNEL_BOOK = 'com.example.flutter_nav_android/book/method';
- static const String NAV_FLUTTER_BOOK_NOTICE = 'navFlutterBookNotice';
-
- @override
- void initState() {
- super.initState();
- MethodChannel bookMethodChannel = const MethodChannel(METHOD_CHANNEL_BOOK);
- bookMethodChannel.setMethodCallHandler(methodHandler);
- }
-
- /// 监听来自 Android端 的消息通道
- /// Android端调用了函数,这个handler函数就会被触发
- Future<dynamic> methodHandler(MethodCall call) async {
- final String methodName = call.method;
- switch (methodName) {
- case NAV_FLUTTER_BOOK_NOTICE: // 进入当前页面
- {
- title = call.arguments['title'];
- setState(() {});
- return 0;
- }
- default:
- {
- return PlatformException(
- code: '-1',
- message: '未找到Flutter端具体实现函数',
- details: '具体描述'); // 返回给Android端
- }
- }
- }
-
- @override
- Widget build(BuildContext context) {
- return Scaffold(
- backgroundColor: Colors.amberAccent,
- body: Container(
- width: MediaQuery.of(context).size.width,
- height: MediaQuery.of(context).size.height,
- alignment: Alignment.center,
- child: Text(
- 'Flutter $title',
- style: const TextStyle(
- fontWeight: FontWeight.bold,
- color: Colors.red,
- fontSize: 20,
- ),
- ),
- ),
- );
- }
-
- }
home.dart
- import 'package:flutter/material.dart';
- import 'package:flutter/services.dart';
-
- class Home extends StatefulWidget {
- const Home({super.key});
-
- @override
- State<Home> createState() => _HomeState();
- }
-
- class _HomeState extends State<Home> {
- late MethodChannel homeMethodChannel;
-
- String name = '';
- String age = '';
-
- static const String METHOD_CHANNEL_HOME = 'com.example.flutter_nav_android/home/method';
- static const String NAV_ANDROID_PERSONAL_NOTICE = 'navAndroidPersonalNotice';
- static const String NAV_FLUTTER_HOME_NOTICE = 'navFlutterHomeNotice';
- static const String POP_NOTICE = 'popNotice';
- static const String PERSONAL_POP_NOTICE = 'personalPopNotice';
-
- @override
- void initState() {
- super.initState();
- homeMethodChannel = const MethodChannel(METHOD_CHANNEL_HOME);
- homeMethodChannel.setMethodCallHandler(methodHandler);
- }
-
- /// 监听来自 Android端 的消息通道
- /// Android端调用了函数,这个handler函数就会被触发
- Future<dynamic> methodHandler(MethodCall call) async {
- final String methodName = call.method;
- switch (methodName) {
- case NAV_FLUTTER_HOME_NOTICE: // 进入当前页面
- {
- name = call.arguments['name'];
- setState(() {});
- return 0;
- }
- case PERSONAL_POP_NOTICE: // Android Personal 页面 销毁了
- {
- age = '${call.arguments['age']}';
- setState(() {});
- return 0;
- }
- default:
- {
- return PlatformException(
- code: '-1',
- message: '未找到Flutter端具体实现函数',
- details: '具体描述'); // 返回给Android端
- }
- }
- }
-
- /// 销毁当前页面
- popPage() {
- if (Navigator.canPop(context)) { // 检查Flutter路由栈中,是否还有其他路由
- Navigator.pop(context);
- } else {
- Map<String, int> map = {'age': 12};
- homeMethodChannel.invokeMethod(POP_NOTICE, map);
- }
- }
-
- /// 前往 Android Personal 页面
- navAndroidPersonal() {
- Map<String, String> map = {'name': '李四'};
- homeMethodChannel.invokeMethod(NAV_ANDROID_PERSONAL_NOTICE, map);
- }
-
- @override
- Widget build(BuildContext context) {
- return Scaffold(
- appBar: AppBar(
- backgroundColor: Colors.blue,
- title: const Text(
- 'Flutter Home',
- style: TextStyle(
- fontWeight: FontWeight.w500,
- fontSize: 26,
- color: Colors.yellow),
- )),
- body: SizedBox(
- width: MediaQuery.of(context).size.width,
- height: MediaQuery.of(context).size.height,
- child: Column(
- mainAxisAlignment: MainAxisAlignment.center,
- crossAxisAlignment: CrossAxisAlignment.center,
- children: [
- Padding(
- padding: const EdgeInsets.only(bottom: 16),
- child: RichText(
- text: TextSpan(
- text: '接收初始化参数:',
- style: const TextStyle(color: Colors.black, fontSize: 20),
- children: [
- TextSpan(
- text: name,
- style: const TextStyle(color: Colors.red,fontWeight: FontWeight.bold),
- )
- ])),
- ),
- Padding(
- padding: const EdgeInsets.only(bottom: 16),
- child: RichText(
- text: TextSpan(
- text: '接收上一页返回参数:',
- style: const TextStyle(color: Colors.black, fontSize: 20),
- children: [
- TextSpan(
- text: age,
- style: const TextStyle(color: Colors.red,fontWeight: FontWeight.bold),
- )
- ])),
- ),
- Padding(
- padding: const EdgeInsets.only(bottom: 8),
- child: ElevatedButton(
- onPressed: navAndroidPersonal,
- child: const Text(
- '前往 Android Personal',
- style: TextStyle(fontSize: 20),
- ),
- ),
- ),
- ElevatedButton(
- onPressed: popPage,
- child: const Text(
- '返回 上一页',
- style: TextStyle(fontSize: 20),
- ),
- ),
- ],
- ),
- ),
- );
- }
- }
login.dart
- import 'package:flutter/material.dart';
- import 'package:flutter/services.dart';
-
- class Login extends StatefulWidget {
- const Login({super.key});
-
- @override
- State<Login> createState() => _LoginState();
- }
-
- class _LoginState extends State<Login> {
- late MethodChannel loginMethodChannel;
-
- String name = '';
-
- final String METHOD_CHANNEL_LOGIN = 'com.example.flutter_nav_android/login/method';
- static const String NAV_FLUTTER_LOGIN_NOTICE = 'navFlutterLoginNotice';
- final String POP_NOTICE = 'popNotice';
-
- @override
- void initState() {
- super.initState();
- loginMethodChannel = MethodChannel(METHOD_CHANNEL_LOGIN);
- loginMethodChannel.setMethodCallHandler(methodHandler);
- }
-
- /// 监听来自 Android端 的消息通道
- /// Android端调用了函数,这个handler函数就会被触发
- Future<dynamic> methodHandler(MethodCall call) async {
- final String methodName = call.method;
- switch (methodName) {
- case NAV_FLUTTER_LOGIN_NOTICE: // 进入当前页面
- {
- name = call.arguments['name'];
- setState(() {});
- return 0;
- }
- default:
- {
- return PlatformException(code: '-1', message: '未找到Flutter端具体实现函数', details: '具体描述'); // 返回给Android端
- }
- }
- }
-
- /// 销毁当前页面
- popPage() {
- if (Navigator.canPop(context)) { // 检查Flutter路由栈中,是否还有其他路由
- Navigator.pop(context);
- } else {
- Map<String, int> map = {'age': 28};
- loginMethodChannel.invokeMethod(POP_NOTICE, map);
- }
- }
-
- @override
- Widget build(BuildContext context) {
- return Scaffold(
- appBar: AppBar(
- backgroundColor: Colors.blue,
- title: const Text(
- 'Flutter Login',
- style: TextStyle(
- fontWeight: FontWeight.w500,
- fontSize: 26,
- color: Colors.yellow),
- )),
- body: SizedBox(
- width: MediaQuery.of(context).size.width,
- height: MediaQuery.of(context).size.height,
- child: Column(
- mainAxisAlignment: MainAxisAlignment.center,
- crossAxisAlignment: CrossAxisAlignment.center,
- children: [
- Padding(
- padding: const EdgeInsets.only(bottom: 16),
- child: RichText(
- text: TextSpan(
- text: '接收初始化参数:',
- style: const TextStyle(color: Colors.black, fontSize: 20),
- children: [
- TextSpan(
- text: name,
- style: const TextStyle(color: Colors.red,fontWeight: FontWeight.bold),
- )
- ])),
- ),
- ElevatedButton(
- onPressed: popPage,
- child: const Text(
- '返回 上一页',
- style: TextStyle(fontSize: 20),
- ),
- ),
- ],
- ),
- ),
- );
- }
- }
student.dart
- import 'package:flutter/material.dart';
- import 'package:flutter/services.dart';
-
- class Student extends StatefulWidget {
- const Student({super.key});
-
- @override
- State<Student> createState() => _StudentState();
- }
-
- class _StudentState extends State<Student> {
-
- String title = '';
-
- static const String METHOD_CHANNEL_STUDENT = 'com.example.flutter_nav_android/student/method';
- static const String NAV_FLUTTER_STUDENT_NOTICE = 'navFlutterStudentNotice';
-
- @override
- void initState() {
- super.initState();
- MethodChannel bookMethodChannel = const MethodChannel(METHOD_CHANNEL_STUDENT);
- bookMethodChannel.setMethodCallHandler(methodHandler);
- }
-
- /// 监听来自 Android端 的消息通道
- /// Android端调用了函数,这个handler函数就会被触发
- Future<dynamic> methodHandler(MethodCall call) async {
- final String methodName = call.method;
- switch (methodName) {
- case NAV_FLUTTER_STUDENT_NOTICE: // 进入当前页面
- {
- title = call.arguments['title'];
- setState(() {});
- return 0;
- }
- default:
- {
- return PlatformException(
- code: '-1',
- message: '未找到Flutter端具体实现函数',
- details: '具体描述'); // 返回给Android端
- }
- }
- }
-
- @override
- Widget build(BuildContext context) {
- return Scaffold(
- backgroundColor: Colors.cyan,
- body: Container(
- width: MediaQuery.of(context).size.width,
- height: MediaQuery.of(context).size.height,
- alignment: Alignment.center,
- child: Text(
- 'Flutter $title',
- style: const TextStyle(
- fontWeight: FontWeight.bold,
- color: Colors.white,
- fontSize: 20,
- ),
- ),
- ),
- );
- }
-
- }
main.dart
- import 'package:flutter/material.dart';
- import 'package:flutter_nav_android/book.dart';
- import 'package:flutter_nav_android/student.dart';
-
- import 'home.dart';
- import 'login.dart';
-
- void main() {
- runApp(const MyApp());
- }
-
- class MyApp extends StatelessWidget {
- const MyApp({super.key});
-
- @override
- Widget build(BuildContext context) {
- return MaterialApp(
- title: 'Flutter Demo',
- debugShowCheckedModeBanner: false,
- theme: ThemeData(
- colorScheme: ColorScheme.fromSeed(
- seedColor: Colors.deepPurple,
- ),
- useMaterial3: true,
- ),
- routes: {
- '/home': (context) => const Home(),
- '/login': (context) => const Login(),
- '/book': (context) => const Book(),
- '/student': (context) => const Student(),
- },
- initialRoute: '/home',
- );
- }
- }
- // 原来带坑的写法
- // routes: {
- // '/': (context) => const Home(),
- // '/login': (context) => const Login(),
- // '/personal': (context) => const Personal()
- // },
- // initialRoute: '/',
-
-
- // 解决方式的写法
- routes: {
- '/home': (context) => const Home(),
- '/login': (context) => const Login(),
- '/personal': (context) => const Personal()
- },
- initialRoute: '/home',
-
在案例中,我使用的是 命名路由 和 Channel方式 进行 页面跳转、传参,下面直接使用引擎进行 页面跳转、传参,但实用价值不高,因为弊端太大;
比如:
综上所述,大家将这种方式当作扩展知识就好了。
Android代码
- class MainActivity : AppCompatActivity(), View.OnClickListener {
-
- ... ...
-
- override fun onCreate(savedInstanceState: android.os.Bundle?) {
- super.onCreate(savedInstanceState)
-
- ... ...
-
- val flutterEngine = FlutterEngine(this)
-
- // 定义参数
- val dartEntrypointArgs = mutableListOf<String>()
- dartEntrypointArgs.add("张三")
-
- // 这种方式传参数,会导致这个Flutter页面中的Widget默认参数,全部失效,
-
- // 这种只有 Dart主入口文件,比如main.dart的 main函数才能接收到参数
- // void main(List<String args>) {}
- // flutterEngine.dartExecutor.executeDartEntrypoint(
- // DartExecutor.DartEntrypoint.createDefault(),
- // dartEntrypointArgs
- // )
-
- // 这种可以指定页面接收,但需要在主入口文件里先声明
- // void showPersonal(List<String> args) {}
- flutterEngine.dartExecutor.executeDartEntrypoint(
- DartExecutor.DartEntrypoint(
- FlutterInjector.instance().flutterLoader().findAppBundlePath(),
- "showPersonal"), // 找到目标Flutter页面提供的 暴露函数
- dartEntrypointArgs)
- FlutterEngineCache.getInstance().put("personal_engine", flutterEngine)
- }
-
-
- override fun onClick(v: View?) {
- ... ...
-
- val map: MutableMap<String, String> = mutableMapOf<String, String>()
- map["name"] = "张三"
- startActivity(
- FlutterActivity
- .withCachedEngine("personal_engine") // 获取缓存好的引擎
- .backgroundMode(FlutterActivityLaunchConfigs.BackgroundMode.transparent) // 背景改为透明,不然切换页面时,会闪烁黑色
- .build(this))
- }
-
- }
-
Flutter代码
main.dart(主文件)
- import 'package:flutter/material.dart';
- import 'package:flutter_nav_android/personal.dart';
-
- void main(List<String> args) {
- debugPrint('args:$args');
- runApp(const MyApp());
- }
-
- /// 注解说明文档:https://mrale.ph/dartvm/compiler/aot/entry_point_pragma.html
- /// 注解:表明它可以在 AOT 模式下直接从本机或 VM 代码解析、分配或调用
- @pragma("vm:entry-point")
- void showPersonal(List<String> args) { // 在主文件入口暴露出来
- debugPrint('args:$args');
- runApp(Personal(title: args.first));
- }
-
- class MyApp extends StatelessWidget {
- const MyApp({super.key});
-
- @override
- Widget build(BuildContext context) {
- return MaterialApp(
- title: 'Flutter Demo',
- debugShowCheckedModeBanner: false,
- theme: ThemeData(
- colorScheme: ColorScheme.fromSeed(
- seedColor: Colors.deepPurple,
- ),
- useMaterial3: true,
- ),
- );
- }
- }
personal.dart(目标页面)
- import 'package:flutter/material.dart';
-
- class Personal extends StatefulWidget {
- final String? title;
- const Personal({super.key,this.title});
-
- @override
- State<Personal> createState() => _PersonalState();
- }
-
- class _PersonalState extends State<Personal> {
-
- /// 在这个页面中,使用的Widget 默认参数全部失效,外层套上 Material组件 也无效
-
- @override
- Widget build(BuildContext context) {
- return Container(
- color: Colors.white,
- width: MediaQuery.of(context).size.width,
- height: MediaQuery.of(context).size.height,
- alignment: Alignment.center,
- child: Directionality(
- textDirection: TextDirection.ltr,
- child: Text(
- widget.title ?? '',
- style: const TextStyle(
- color: Colors.lightBlue,
- fontSize: 30,
- ),
- )),
- );
- }
- }
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。