Flutter 和 Android原生(Activity、Fragment)相互跳转、传参

这篇具有很好参考价值的文章主要介绍了Flutter 和 Android原生(Activity、Fragment)相互跳转、传参。希望对大家有所帮助。如果存在错误或未考虑完全的地方,请大家不吝赐教,您也可以点击"举报违法"按钮提交疑问。

前言

本文主要讲解 Flutter 和 Android原生之间,页面相互跳转、传参,

但其中用到了两端相互通信的知识,非常建议先看完这篇 讲解通信的文章

Flutter 与 Android原生 相互通信:BasicMessageChannel、MethodChannel、EventChannel_flutter eventchannel methodchannel basemessagechan-CSDN博客

当前案例 Flutter SDK版本:3.13.2

Flutter使用多个轻量型引擎,进行混合开发,是从 2.0 开始的,大大的减轻了内存压力;

轻量型引擎开发-官方介绍视频:https://www.youtube.com/watch?v=p6cK_0jp2ag

Flutter和原生端的关系

Flutter 和 Android原生(Activity、Fragment)相互跳转、传参,Flutter + Android 混合开发,flutter,androidFlutter 和 Android原生(Activity、Fragment)相互跳转、传参,Flutter + Android 混合开发,flutter,android

混合路由栈

如果是纯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页面结果,还会创建FlutterActivityFlutterView进行绑定;

1.5 看到这,大家应该理解,为什么说Flutter只是UI框架了吧;

Flutter 和 Android原生(Activity、Fragment)相互跳转、传参,Flutter + Android 混合开发,flutter,android

Android 和 Flutter 跳转

Android 跳转 Flutter

Flutter 和 Android原生(Activity、Fragment)相互跳转、传参,Flutter + Android 混合开发,flutter,android

Flutter 跳转 Android

Flutter 和 Android原生(Activity、Fragment)相互跳转、传参,Flutter + Android 混合开发,flutter,android

效果图

Flutter 和 Android原生(Activity、Fragment)相互跳转、传参,Flutter + Android 混合开发,flutter,android

Android代码

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>

Flutter代码

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',
    );
  }
}

踩坑

  • 一个路由坑,Flutter 加载 根命名路由标识,默认是 " / ",如果你在Flutter端使用了 " / " 作为根路由页面,有时候,从Android 跳转 Flutter页面时,它会将 根路由页面 先push进栈,再push你的目标页面进栈,会多出一个页面
  • 所有我直接不用 " / " 标识,而使用页面对应的路由标识字符串,来指定根路由。
// 原来带坑的写法
// 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方式 进行 页面跳转、传参,下面直接使用引擎进行 页面跳转、传参,但实用价值不高,因为弊端太大;

比如:

  • 只提供了List<String>类型,进行传参;
  • 目标Flutter页面内的widget,默认参数会全部失效;
  • Flutter的Widget默认参数,是由主文件内的 MaterialApp 组件提供的,一个Flutter应用,一般只会使用一个 MaterialApp,它代表返回一个App,如果将目标Flutter页面套上 MaterialApp ,可以解决这个默认参数问题,但会引发 路由问题
  • 不过也有适用场景:让每个Flutter页面完全独立,之间没有任何交互,比如跳转、数据共享等等,这样路由就不需要了,让每个Flutter页面都使用 MaterialApp,当作App和原生交互;

综上所述,大家将这种方式当作扩展知识就好了。

Flutter 和 Android原生(Activity、Fragment)相互跳转、传参,Flutter + Android 混合开发,flutter,android

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 默认参数全部失效

  @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,
            ),
          )),
    );
  }
}

FlutterEngineGroup

  • 如果你接受以上缺点,可以使用 FlutterEngineGroup,这个东西就是换成了单例写法,我们自己写单例,需要处理一个问题,比如这个需求:同时获取两个Flutter引擎,这里就要通过判空新建一个Flutter引擎;
  • 而 FlutterEngineGroup 已经处理了这个问题,自首个Flutter引擎起,后面多出的Flutter引擎,都是其子类,如果只剩下最后一个,那么这个Flutter引擎将和首个Flutter引擎性能特征相同;
  • FlutterEngineGroup官方文档:多个 Flutter 页面或视图 - Flutter 中文文档 - Flutter 中文开发者网站 - Flutter
  • 我个人体验上来说,不使用单例,性能也足够了,如果有很多混合的Flutter页面,那当我没说😐。
  • 没找到创建子类Flutter引擎的相关代码,是从注解中发现的

Flutter 和 Android原生(Activity、Fragment)相互跳转、传参,Flutter + Android 混合开发,flutter,android

Debug 和 Release

我用真机做测试,发现在Debug模式下,第一次从 Android 跳转 Flutter 会出现黑屏现象,但 打完包 或在 Release模式 就看不出来了;

我们默认的运行模式就是 Debug模式,可以通过 修改IDE运行命令 --release,切换为 Release模式

Flutter 和 Android原生(Activity、Fragment)相互跳转、传参,Flutter + Android 混合开发,flutter,android

Flutter 和 Android原生(Activity、Fragment)相互跳转、传参,Flutter + Android 混合开发,flutter,android

源码地址

GitHub - LanSeLianMa/flutter_nav_android: Flutter 和 Android原生(Activity、Fragment)相互跳转、传参文章来源地址https://www.toymoban.com/news/detail-835006.html

到了这里,关于Flutter 和 Android原生(Activity、Fragment)相互跳转、传参的文章就介绍完了。如果您还想了解更多内容,请在右上角搜索TOY模板网以前的文章或继续浏览下面的相关文章,希望大家以后多多支持TOY模板网!

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处: 如若内容造成侵权/违法违规/事实不符,请点击违法举报进行投诉反馈,一经查实,立即删除!

领支付宝红包 赞助服务器费用

相关文章

  • Android adb命令获取当前Activity或者Fragment

    用adb命令查看下面Demo处于 NO.3 Fragment 时的 Activity 和 Fragment 信息。 输出结果如下: 上面的 com.example.who/com.example.who.ui.activity.SecondActivity} 可以分成两部分看: / 前的部分是包名, / 后的部分是当前 Activity 的类全名。 该命令会输出堆栈中全部的窗口信息。 输出结果如下, Se

    2024年02月15日
    浏览(55)
  • 【Android】怎么使用一个ViewModel用在多个Activity或者Fragment中

    项目需求 在多个Activity或者Fragment中使用同一个ViewModel 需求实现 1.使用ActivityScope或FragmentScope 想在一个Activity或Fragment中共享ViewModel实例,可以使用ActivityScope或FragmentScope。这两种范围会根据它们所绑定的Activity或Fragment自动管理ViewModel实例的生命周期。 例如,创建一个继承自

    2024年02月15日
    浏览(40)
  • uniapp中微信小程序和H5相互跳转及传参(webview)

    技术栈:uniapp-H5+uniapp-微信小程序(vue3+vite2+ts) 前言:在单位做项目的时候碰到一个需求,需要从微信小程序跳转到H5页面,这两个端都是使用uniapp编写的,查资料后决定使用webview来嵌入完成,然后考虑到还可能有参数(数据)需要传递,所以实现后记录一下。ps:以下代码

    2024年02月09日
    浏览(43)
  • Android之Fragment 跳转返回重复加载重复执行onCreateView的四种解决方法

    前言:  Fragment 跳转返回重复加载重复执行 onCreateView 的问题通常是因为 Fragment 生命周期方法的调用顺序和使用不当所导致的。 1,onSaveInstanceState()方法 一种解决方法是在 Fragment 中使用 onSaveInstanceState() 方法保存 Fragment 的状态,并在 onCreate() 方法中进行状态恢复,避免重复执

    2024年02月06日
    浏览(48)
  • Android Studio中如何在Activity跳转之间传递数据

    使用Intent操作可以完成两个Activity之间的跳转,有时候也相应的需要在两个跳转活动之间传递数据,这篇就详细的介绍一些在Android开发中一些基础的活动跳转时传递数据的方法 调用PutExtra()方法 由于Activity之间的数据传递有许多不同种类型,所以在Inten类中提供了多个重载的

    2023年04月08日
    浏览(74)
  • Android开发页面跳转异常且应用停止:android.content.ActivityNotFoundException: Unable to find explicit activity

    E/AndroidRuntime: FATAL EXCEPTION: main     Process: com.example.app01, PID: 13901     android.content.ActivityNotFoundException: Unable to find explicit activity class {com.example.app01/com.example.app01.RegisterActivity}; have you declared this activity in your AndroidManifest.xml? 根据日志信息不难发现问题的所在,就是我们需要将存

    2024年02月16日
    浏览(41)
  • adb shell命令查看当前屏幕可见最顶层Activity和Fragment及其调用栈

    (1)当前屏幕可见页面最顶层是哪个Activity: (2)当前屏幕可见页面最顶层是哪个Fragment: (3)当前屏幕可见最顶层Activity及其调用栈: android adb命令获取处于当前屏幕的Activity_adb获取当前页面activity-CSDN博客 Android adb shell dump当前手机设备的所有activity_zhangphil的博客-CSDN博客。

    2024年02月07日
    浏览(46)
  • flutter和android互相调用、android原生项目与flutter module之间的交互、如何在flutter module中使用原生的方法

    bridge.dart MainActivity.kt MainActivity中 Flutter中 android项目依赖flutter module的方式网上有很多,这里就不做讲解。主要说一下这种情况下android如何与flutter module之间进行方法的调用 踩坑:期初我是想让flutter module调用它内部中的.android文件夹下的原生代码。.android文件夹下有Flutter、

    2023年04月08日
    浏览(44)
  • 【flutter和android原生的异步】

    java android 中,是多线程的 1.flutter中不要以为异步就是多线程  2.flutter的线程隔离理论? UI 线程 : 在 Flutter 中,UI 线程负责处理用户界面的构建和渲染。所有与用户界面相关的操作,例如布局计算、绘制和处理用户输入等,都在 UI 线程中执行。 UI 线程是单线程的,也称为主

    2024年04月15日
    浏览(46)
  • Flutter 与原生交互(Android,iOS)

    本质上 Flutter 和 原生通信是通过 Channel 来完成的:Flutter中消息的传递是完全异步的; 消息使用 Channel(平台通道) 在客户端(UI) 和主机(p平台) Flutter 与 native端交互三种方式 1.BasicMessageChannel (用于传递字符串和半结构化信息,持续通信使用,例如dart端将服务器的数据陆续传入到

    2024年02月17日
    浏览(49)

觉得文章有用就打赏一下文章作者

支付宝扫一扫打赏

博客赞助

微信扫一扫打赏

请作者喝杯咖啡吧~博客赞助

支付宝扫一扫领取红包,优惠每天领

二维码1

领取红包

二维码2

领红包