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" />

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

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

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("成功跳转")
        }

    }
}

接收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
                )
            )
                }
            }
        }
 
}

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

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
                        )
                    )
                }
            }
        }
       
    }
}

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))
        }
    }
}

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()
    }
}

在Manifest文件里面注册该自定义的Application
Flutter入门-Dart端和Native端的页面跳转_第1张图片

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)
            )
        }
    }
}

二、基于传参(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();
  }
}

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、在继承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")
        }
    }
}

三、基于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();
  }
}

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))
        }
    }
}

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()
    }
}

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

你可能感兴趣的:(Flutter入门系列,flutter,android,android,studio)