• Flutter入门-Dart端和Native端的页面跳转


    前面我们学习了Dart端页面的跳转是通过Navigator控制的,而在android原生开发中,采用的是Intent。那混合开发中,两者间的页面跳转又是如何实现的呢?

    Dart端跳转到Native端的实现

    消息通道

    1、在android原生的Manifest文件中注册FlutterActivity组件

     <activity
                android:name="io.flutter.embedding.android.FlutterActivity"
                android:configChanges="orientation|keyboardHidden|keyboard|screenSize|locale|layoutDirection|fontScale|screenLayout|density|uiMode"
                android:hardwareAccelerated="true"
                android:theme="@style/Theme.AppCompat"
                android:windowSoftInputMode="adjustResize" />
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6

    2、Dart端使用BasicMessageChannel或者MethodChannel

    2-1 BasicMessageChannel
    import 'package:flutter/material.dart';
    import 'package:flutter/services.dart';
    
    class MyBasicMessageChannel extends StatelessWidget {
      
      Widget build(BuildContext context) {
        return MaterialApp(
          home: BasicMessageChannelHome(),
        );
      }
    }
    
    class BasicMessageChannelHome extends StatefulWidget {
      
      State<BasicMessageChannelHome> createState() =>
          BasicMessageChannelHomeState();
    }
    
    class BasicMessageChannelHomeState extends State<BasicMessageChannelHome> {
      var _basicMessage = BasicMessageChannel("BasicChannel", StringCodec());
    
      
      Widget build(BuildContext context) {
        return MaterialApp(
          home: Scaffold(
            body: Center(
              child: GestureDetector(
                child: Text('发送BasicMessageChannel'),
                onTap: () {
                  _basicMessage.send('JumpNativeActivity').then((value) {
                    print(value);
                  });
                },
              ),
            ),
          ),
        );
      }
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    2-1 MethodChannel
    import 'package:flutter/material.dart';
    import 'package:flutter/services.dart';
    
    class FlutterMethodChannel extends StatelessWidget {
      MethodChannel methodChannel = MethodChannel('nativeCall');
    
      
      Widget build(BuildContext context) {
        return MaterialApp(
          home: Scaffold(
            body: GestureDetector(
              onTap: () {
                _callNativeMethod();
              },
              child: Center(
                child: Text('调用Native方法'),
              ),
            ),
          ),
        );
      }
    
      void _callNativeMethod() async {
        try {
          methodChannel.invokeMethod('JumpNativeActivity');
        } catch (e) {
          print(e);
        }
      }
    }
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31

    3、android原生端

    接收BasicMessageChannel
    package com.example.flutter_hello
    
    import android.content.Context
    import android.os.Bundle
    import android.util.Log
    import io.flutter.embedding.android.FlutterActivity
    import io.flutter.embedding.engine.FlutterEngine
    import io.flutter.plugin.common.BasicMessageChannel
    import io.flutter.plugin.common.StringCodec
    
    const val TAG:String="MainActivity"
    
    class MainActivity : FlutterActivity() {
    
        override fun configureFlutterEngine(flutterEngine: FlutterEngine) {
            super.configureFlutterEngine(flutterEngine)
            var channel = BasicMessageChannel(
                flutterEngine.dartExecutor.binaryMessenger,
                "BasicChannel",
                StringCodec.INSTANCE
            )
            channel.setMessageHandler { message, reply ->
                startActivity(
                    Intent(
                        this,
                        SecondActivity::class.java
                    )
                )
                reply.reply("成功跳转")
            }
    
        }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    接收MethodChannel
    package com.example.flutter_hello
    
    import android.content.Context
    import android.content.Intent
    import android.os.Bundle
    import android.util.Log
    import io.flutter.embedding.android.FlutterActivity
    import io.flutter.embedding.engine.FlutterEngine
    import io.flutter.plugin.common.MethodChannel
    import io.flutter.plugin.common.StringCodec
    
    const val TAG:String="MainActivity"
    
    class MainActivity : FlutterActivity() {
    
        override fun onCreate(savedInstanceState: Bundle?) {
            super.onCreate(savedInstanceState)
            Log.e(TAG,"onCreate")
            MethodChannel(
                flutterEngine!!.dartExecutor.binaryMessenger,
                "nativeCall"
            ).setMethodCallHandler { call, result ->
                run {
                    if (call.method.equals("JumpNativeActivity")) {
                        result.success("success")
                         startActivity(
                    Intent(
                        this,
                        SecondActivity::class.java
                    )
                )
                    }
                }
            }
     
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36

    android原生端跳转到Dart端页面

    一、基于FlutterEngine

    该方式是基于Navigator和routes的,所以,对于Dart端一定要配置好routes。

    Dart端

    import 'package:flutter/material.dart';
    import 'package:flutter/services.dart';
    
    class DartPages extends StatelessWidget {
      
      Widget build(BuildContext context) {
        return MaterialApp(
          routes: <String, WidgetBuilder>{
            'home': (BuildContext context) => DartHomePage(),
            'second': (BuildContext context) => DartSecondPage(),
          },
          initialRoute: 'home',
        );
      }
    }
    
    class DartHomePage extends StatelessWidget {
      
      Widget build(BuildContext context) {
        return Scaffold(
          appBar: AppBar(
            title: const Text('HomePage'),
            centerTitle: true,
          ),
          body: const Center(
            child: Text('欢迎来到Dart主页'),
          ),
          floatingActionButton: FloatingActionButton(
            onPressed: () {
              MethodChannel methodChannel=const MethodChannel("nativeCall");
              methodChannel.invokeMethod("toSecondActivity");
            },
            child: const Text('跳转'),
          ),
        );
      }
    }
    
    class DartSecondPage extends StatelessWidget {
      
      Widget build(BuildContext context) {
        return Scaffold(
          appBar: AppBar(
            title: const Text('HomePage'),
            centerTitle: true,
          ),
          body: const Center(
            child: Text('欢迎来到Dart第二页'),
          ),
        );
      }
    }
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47
    • 48
    • 49
    • 50
    • 51
    • 52
    • 53

    Android端

    MainActivity

    package com.example.flutter_hello
    
    import android.content.Context
    import android.content.Intent
    import android.os.Bundle
    import android.util.Log
    import io.flutter.embedding.android.FlutterActivity
    import io.flutter.embedding.engine.FlutterEngine
    import io.flutter.plugin.common.MethodChannel
    import io.flutter.plugin.common.StringCodec
    
    const val TAG:String="MainActivity"
    
    class MainActivity : FlutterActivity() {
        override fun onCreate(savedInstanceState: Bundle?) {
            super.onCreate(savedInstanceState)
            Log.e(TAG,"onCreate")
            MethodChannel(
                flutterEngine!!.dartExecutor.binaryMessenger,
                "nativeCall"
            ).setMethodCallHandler { call, result ->
                run {
                    if (call.method.equals("nativeMethod")) {
                        result.success("success")
                        Log.e(TAG, "Get Dart call method")
                    }else if(call.method.equals("toSecondActivity")){
                        startActivity(
                            Intent(
                                this,
                                SecondActivity::class.java
                            )
                        )
                    }
                }
            }
           
        }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    withNewEngine()

    该方法使用的是withNewEngine,页面跳转会出现卡顿感
    SecondActivity

    package com.example.flutter_hello
    
    import android.content.Context
    import android.content.Intent
    import android.os.Bundle
    import androidx.appcompat.app.AppCompatActivity
    import androidx.databinding.DataBindingUtil
    import com.example.flutter_hello.databinding.ActivitySecondBinding
    import io.flutter.embedding.android.FlutterActivity
    
    class SecondActivity : AppCompatActivity() {
        lateinit var mContext: Context
        override fun onCreate(savedInstanceState: Bundle?) {
            super.onCreate(savedInstanceState)
            mContext = this
            val binding: ActivitySecondBinding =
                DataBindingUtil.setContentView(this, R.layout.activity_second)
            binding.jumpToDartSecond.setOnClickListener {
                //方式1:跳转到已经在flutter的routers声明的命名路由页面(会卡顿)
                startActivity(FlutterActivity.withNewEngine().initialRoute("second").build(mContext))
            }
        }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23

    2、在Application里面先设置好FlutterEngine,使用的时候直接取缓存过得FlutterEngine
    自定义MyApplication

    package com.example.flutter_hello
    
    import android.app.Application
    import io.flutter.embedding.engine.FlutterEngine
    import io.flutter.embedding.engine.FlutterEngineCache
    import io.flutter.embedding.engine.dart.DartExecutor
    
    class MyApplication : Application() {
        lateinit var flutterEngineInit: FlutterEngine
        override fun onCreate() {
            super.onCreate()
            flutterEngineInit = FlutterEngine(this)
            // 开始执行Dart代码以预热FlutterEngine
            flutterEngineInit.navigationChannel.setInitialRoute("second")
            flutterEngineInit.getDartExecutor()
                .executeDartEntrypoint(DartExecutor.DartEntrypoint.createDefault())
            // 缓存FlutterActivity要使用的FlutterEngine
            FlutterEngineCache.getInstance().put("second", flutterEngineInit)
        }
    
        override fun onTerminate() {
            flutterEngineInit.destroy()
            super.onTerminate()
        }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25

    在Manifest文件里面注册该自定义的Application
    在这里插入图片描述

    withCachedEngine
    package com.example.flutter_hello
    
    import android.content.Context
    import android.content.Intent
    import android.os.Bundle
    import androidx.appcompat.app.AppCompatActivity
    import androidx.databinding.DataBindingUtil
    import com.example.flutter_hello.databinding.ActivitySecondBinding
    import io.flutter.embedding.android.FlutterActivity
    
    class SecondActivity : AppCompatActivity() {
        lateinit var mContext: Context
        override fun onCreate(savedInstanceState: Bundle?) {
            super.onCreate(savedInstanceState)
            mContext = this
            val binding: ActivitySecondBinding =
                DataBindingUtil.setContentView(this, R.layout.activity_second)
            binding.jumpToDartSecond.setOnClickListener {
                //方式2:跳转到initialRoute指定的页面(不卡顿)
                startActivity(
                    FlutterActivity
                        .withCachedEngine("second")
                        .build(mContext)
                )
            }
        }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27

    二、基于传参(window.defaultRouteName)

    PS:如果要使用window属性,必须导入 'dart:ui’的包

    Dart端

    import 'dart:ui';
    
    import 'package:flutter/material.dart';
    import 'package:flutter/services.dart';
    
    class DartPages extends StatelessWidget {
      
      Widget build(BuildContext context) {
        return MaterialApp(
          routes: <String, WidgetBuilder>{
            'home': (BuildContext context) => DartHomePage(),
            'second': (BuildContext context) => DartSecondPage(),
          },
          home: _widgetForRoute(window.defaultRouteName),
        );
      }
    }
    
    class DartHomePage extends StatelessWidget {
      
      Widget build(BuildContext context) {
        return Scaffold(
          appBar: AppBar(
            title: const Text('HomePage'),
            centerTitle: true,
          ),
          body: const Center(
            child: Text('欢迎来到Dart主页'),
          ),
          floatingActionButton: FloatingActionButton(
            onPressed: () {
              MethodChannel methodChannel=const MethodChannel("nativeCall");
              methodChannel.invokeMethod("toSecondActivity");
            },
            child: const Text('跳转'),
          ),
        );
      }
    }
    
    class DartSecondPage extends StatelessWidget {
      
      Widget build(BuildContext context) {
        return Scaffold(
          appBar: AppBar(
            title: const Text('HomePage'),
            centerTitle: true,
          ),
          body: const Center(
            child: Text('欢迎来到Dart第二页'),
          ),
        );
      }
    }
    
    Widget? _widgetForRoute(String route){
      switch(route){
        case "home":
          return DartHomePage();
        case "second":
          return DartSecondPage();
        default:
          return DartHomePage();
      }
    }
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47
    • 48
    • 49
    • 50
    • 51
    • 52
    • 53
    • 54
    • 55
    • 56
    • 57
    • 58
    • 59
    • 60
    • 61
    • 62
    • 63
    • 64
    • 65
    • 66

    Android端

    package com.example.flutter_hello
    
    import android.content.Context
    import android.content.Intent
    import android.os.Bundle
    import android.util.Log
    import io.flutter.embedding.android.FlutterActivity
    import io.flutter.embedding.engine.FlutterEngine
    
    var INIT_PARAMS = "initParams"
    const val TAG:String="MainActivity"
    
    class MainActivity : FlutterActivity() {
        private var initParams: String? = null
    
        override fun onCreate(savedInstanceState: Bundle?) {
            super.onCreate(savedInstanceState)
            Log.e(TAG,"onCreate")
            initParams = getIntent().getStringExtra(INIT_PARAMS)
        }
    
        override fun getInitialRoute(): String? {
            return if (initParams == null) super.getInitialRoute() else initParams
        }
    
        companion object {
            fun start(context: Context, initParams: String) {
                val intent = Intent(context, MainActivity::class.java)
                intent.putExtra(INIT_PARAMS, initParams)
                context.startActivity(intent)
            }
        }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33

    1、在继承FlutterActivity的类重写getInitialRoute方法

    调用
    package com.example.flutter_hello
    
    import android.content.Context
    import android.content.Intent
    import android.os.Bundle
    import androidx.appcompat.app.AppCompatActivity
    import androidx.databinding.DataBindingUtil
    import com.example.flutter_hello.databinding.ActivitySecondBinding
    import io.flutter.embedding.android.FlutterActivity
    
    class SecondActivity : AppCompatActivity() {
        lateinit var mContext: Context
        override fun onCreate(savedInstanceState: Bundle?) {
            super.onCreate(savedInstanceState)
            mContext = this
            val binding: ActivitySecondBinding =
                DataBindingUtil.setContentView(this, R.layout.activity_second)
            binding.jumpToDartSecond.setOnClickListener {
                MainActivity.start(mContext,"second")
            }
        }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22

    三、基于FlutterFragment,在android原生的Activity里面嵌入Dart页面

    Dart端

    import 'dart:ui';
    
    import 'package:flutter/material.dart';
    import 'package:flutter/services.dart';
    
    class DartPages extends StatelessWidget {
      
      Widget build(BuildContext context) {
        return MaterialApp(
          routes: <String, WidgetBuilder>{
            'home': (BuildContext context) => DartHomePage(),
            'second': (BuildContext context) => DartSecondPage(),
          },
          home: _widgetForRoute(window.defaultRouteName),
        );
      }
    }
    
    class DartHomePage extends StatelessWidget {
      
      Widget build(BuildContext context) {
        return Scaffold(
          appBar: AppBar(
            title: const Text('HomePage'),
            centerTitle: true,
          ),
          body: const Center(
            child: Text('欢迎来到Dart主页'),
          ),
          floatingActionButton: FloatingActionButton(
            onPressed: () {
              MethodChannel methodChannel=const MethodChannel("nativeCall");
              methodChannel.invokeMethod("toSecondActivity");
            },
            child: const Text('跳转'),
          ),
        );
      }
    }
    
    class DartSecondPage extends StatelessWidget {
      
      Widget build(BuildContext context) {
        return Scaffold(
          appBar: AppBar(
            title: const Text('HomePage'),
            centerTitle: true,
          ),
          body: const Center(
            child: Text('欢迎来到Dart第二页'),
          ),
        );
      }
    }
    
    Widget? _widgetForRoute(String route){
      switch(route){
        case "home":
          return DartHomePage();
        case "second":
          return DartSecondPage();
        default:
          return DartHomePage();
      }
    }
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47
    • 48
    • 49
    • 50
    • 51
    • 52
    • 53
    • 54
    • 55
    • 56
    • 57
    • 58
    • 59
    • 60
    • 61
    • 62
    • 63
    • 64
    • 65
    • 66

    Android端

    SecondActivity
    package com.example.flutter_hello
    
    import android.content.Context
    import android.content.Intent
    import android.os.Bundle
    import androidx.appcompat.app.AppCompatActivity
    import androidx.databinding.DataBindingUtil
    import com.example.flutter_hello.databinding.ActivitySecondBinding
    import io.flutter.embedding.android.FlutterActivity
    
    class SecondActivity : AppCompatActivity() {
        lateinit var mContext: Context
        override fun onCreate(savedInstanceState: Bundle?) {
            super.onCreate(savedInstanceState)
            mContext = this
            val binding: ActivitySecondBinding =
                DataBindingUtil.setContentView(this, R.layout.activity_second)
            binding.jumpToDartSecond.setOnClickListener {
                startActivity(Intent(mContext,ThreeActivity::class.java))
            }
        }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    ThreeActivity
    package com.example.flutter_hello
    
    import androidx.appcompat.app.AppCompatActivity
    import android.os.Bundle
    import androidx.fragment.app.FragmentTransaction
    import io.flutter.embedding.android.FlutterFragment
    //原生页面采用FlutterFragment引入Flutter的widgets
    class ThreeActivity : AppCompatActivity() {
        override fun onCreate(savedInstanceState: Bundle?) {
            super.onCreate(savedInstanceState)
            setContentView(R.layout.activity_three)
            var ft:FragmentTransaction=supportFragmentManager.beginTransaction()
            val dartView:FlutterFragment=FlutterFragment.withNewEngine().initialRoute("second").build()
            ft.replace(R.id.dartPageContain,dartView).commit()
        }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16

    总结:虽然混合开发中页面跳转能够实现,但是总体的页面切换的效果还是很差,估计用户体验不是很高,混合开发中跨平台页面渲染还有很大提升空间。

  • 相关阅读:
    数据库实践 Hw09
    深入理解安卓ARouter:集成与应用
    河北画报杂志河北画报杂志社河北画报编辑部2022年第20期目录
    Qt实战案例(58)——利用QObject类实现定时器功能
    What?JMeter做UI自动化!
    Spring Security多种用户定义方式
    大前端CPU优化技术--NEON技术
    涨知识!如何用9部分详细阐述Java虚拟机:JVM
    万字整理 | 深入理解工作队列
    第3.5章:StarRocks数据导入——Broker Load
  • 原文地址:https://blog.csdn.net/u011557841/article/details/127763738