iOS 工程实现native 跳转指定的Flutter 页面

概要

前一篇文章中我们提到,iOS跳转到Flutter工程指定页面时(多个),Flutter只有单例,设置setInitialRouter 无效,如下

  let flutterViewController = FlutterViewController(engine: flutterEngine, nibName: nil, bundle: nil)!
        flutterViewController.setInitialRoute("test1")

基于不是很甘心,一直想实现完美的解决方案,所以最近几天又看了下解决各方面的解决方案,最终还是有了可行方案,步骤如下

1、设置delegate 代码

 这里代码 多了 FlutterBasicMessageChannel’  设置,其中_kReloadChannelName要和 flutter上的代码保持一致

let _kReloadChannelName = "reload"

@UIApplicationMain
class AppDelegate: UIResponder, UIApplicationDelegate ,FlutterAppLifeCycleProvider{
   
    
    static var shared: AppDelegate?
        
    var window: UIWindow?
 
    var _lifeCycleDelegate = FlutterPluginAppLifeCycleDelegate()
    var flutterEngine : FlutterEngine!
    var  flutterViewController : RKFlutterViewController!
    var  reloadMessageChannel : FlutterBasicMessageChannel!
    
    func addApplicationLifeCycleDelegate(_ delegate: FlutterPlugin) {
        _lifeCycleDelegate.add(delegate)
    }
    
    func flutterSetup(){
        flutterEngine = FlutterEngine(name: "rokid.flutter", project: nil)
        flutterEngine.run(withEntrypoint: nil)
        //全局引擎(解决启动加载时候m,无法处理和native交互问题)
        flutterViewController = RKFlutterViewController(engine: flutterEngine, nibName: nil, bundle: nil)!
        GeneratedPluginRegistrant.register(with: flutterEngine)
        //实现App 路由跳转
        reloadMessageChannel = FlutterBasicMessageChannel(name: _kReloadChannelName, binaryMessenger: flutterEngine, codec: FlutterStringCodec.sharedInstance())
    }
    
    func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplicationLaunchOptionsKey: Any]?) -> Bool {
        ...
        flutterSetup()
       ...
}    

2、iOS App 跳转指定路由 

   @objc func handleButtonAction() {
         self.engine().navigationChannel.invokeMethod("setInitialRoute", arguments: "test")
        self.reloadMessageChannel().sendMessage("test")
        let flutterViewController = FlutterViewController(engine: self.engine(), nibName: nil, bundle: nil)!
        self.navigationController?.pushViewController(flutterViewController, animated: true)
    }


 func engine() -> FlutterEngine {
        return (UIApplication.shared.delegate! as! AppDelegate).flutterEngine
    }
    
    func reloadMessageChannel() -> FlutterBasicMessageChannel {
        return (UIApplication.shared.delegate! as! AppDelegate).reloadMessageChannel
    }

3、flutter路由代码如下

import 'package:flutter/material.dart';
import 'package:flutter/services.dart';
import 'dart:ui' as ui;
import 'src/pages/playground/PlaygroundPage.dart';

/// Channel used to let the Flutter app know to reset the app to a specific
/// route.  See the [run] method.
///
/// Note that we shouldn't use the `setInitialRoute` method on the system
/// navigation channel, as that never gets propagated back to Flutter after the
/// initial call.
const String _kReloadChannelName = 'reload';
const BasicMessageChannel<String> _kReloadChannel =
BasicMessageChannel<String>(_kReloadChannelName, StringCodec());


void main(){
  // Start listening immediately for messages from the iOS side. ObjC calls
  // will be made to let us know when we should be changing the app state.
  _kReloadChannel.setMessageHandler(run);
  // Start off with whatever the initial route is supposed to be.
  run(ui.window.defaultRouteName);
}


Future<String> run(String name) async{
  // The platform-specific component will call [setInitialRoute] on the Flutter
  // view (or view controller for iOS) to set [ui.window.defaultRouteName].
  // We then dispatch based on the route names to show different Flutter
  // widgets.
  // Since we don't really care about Flutter-side navigation in this app, we're
  // not using a regular routes map.
  switch (name) {
    case "test":
      runApp(appRouter(title: "我是路由测试test00",));
      break;
    case "test1":
      runApp(appRouter(title: "我是路由测试test01",));
      break;
    case "test2":
      runApp(appRouter(title: "我是路由测试test02",));
      break;
    default:
      runApp(MyApp());
      break;
  }
  return '';
}

class  appRouter extends StatelessWidget {
  appRouter({this.title});

  final String title;
  // This widget is the root of your application.
  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      title: 'Flutter rokid',
      debugShowCheckedModeBanner: false,// 显示和隐藏
      theme: ThemeData(
        primarySwatch: Colors.blue,
      ),
      home: PlaygroundPage(title: '$title'),
    );
  }
}


class MyApp extends StatelessWidget {
  // This widget is the root of your application.
  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      title: 'Flutter rokid',
      debugShowCheckedModeBanner: false,// 显示和隐藏
      theme: ThemeData(
        // This is the theme of your application.
        //
        // Try running your application with "flutter run". You'll see the
        // application has a blue toolbar. Then, without quitting the app, try
        // changing the primarySwatch below to Colors.green and then invoke
        // "hot reload" (press "r" in the console where you ran "flutter run",
        // or press Run > Flutter Hot Reload in a Flutter IDE). Notice that the
        // counter didn't reset back to zero; the application is not restarted.
        primarySwatch: Colors.blue,
      ),
      home: PlaygroundPage(title: '若琪实验室'),
      routes: <String ,WidgetBuilder>{
        "router1": (_) => new PlaygroundPage(title: "我是内部路由测试test00",),
        "router2": (_) => new PlaygroundPage(title: "我是内部路由测试test01",)
    },
    );
  }
}

思考和总结

 上面代码可以实现:native -> 任意 flutter   ,但是flutter Engine是单例子,能否实现 native->flutter ->native->flutter呢?

功能和交互如何去选择呢???

参考资料

https://github.com/flutter/flutter/issues/27882

https://github.com/flutter/flutter/blob/master/dev/integration_tests/ios_add2app/ios_add2app/MainViewController.m

https://github.com/flutter/flutter/blob/master/dev/integration_tests/ios_add2app/flutterapp/lib/main.dart

猜你喜欢

转载自www.cnblogs.com/kingbo/p/11269026.html