原生嵌入Flutter-Flutter module

如果你希望Flutter代码能够被原生所嵌入,那么此时应该这么创建官方教程一个Flutter module

  1. cd some/path/
  2. flutter create --template module my_flutter

image.png
此时看下当前的目录结构command+ shift+.显示隐藏文件,可以看到这里面也有.android文件和.ios文件,这里主要是为了做调试用的,不建议在这两个文件中添加任何的代码。因为这里是要嵌入到原生中,所以在.android文件和.ios文件中的任何修改都不会被打包到原生的项目中去的。

主要的代码还是写在main.dart
image.png

在iOS中集成Flutter module

接着我们创建一个空的iOS工程,示例是跟当前的my_flutter放在同一目录下,如下图所示
image.png
我使用的CocoaPods来集成,在iOS工程MyFlutterpod init,打开该Podfile文件,在Podfile中添加下面的代码

  1. flutter_application_path = '../my_flutter'
  2. load File.join(flutter_application_path, '.ios', 'Flutter', 'podhelper.rb')
  3. target 'MyApp' do
  4. install_all_flutter_pods(flutter_application_path)
  5. end

然后运行pod install,打开iOS的工程试下**import Flutter**如果能成功,就表明已经在原生的工程中加上了Flutter的代码。

  1. private let flutterVc = FlutterViewController()
  2. @objc
  3. private func tap1() {
  4. self.present(flutterVc, animated: true, completion: nil)
  5. }

image.png

注意:此时我们观察一下内存,可以看到当调用起Flutter相关代码的时候,内存的情况:从30MB直奔100+MB,并且我dissmiss掉当前的FlutterVC时,这个内存并没有减少,加载了就会一直在内存中了。
image.png
Products-> MyFlutter->Show in Finder-> 显示包内容->Frameworks
image.png
可以看到多的其实就是这些内容:Flutter渲染引擎和一些资源内容。

setRoute路由

通过路由来区分跳转页面,目前已经废弃了,但是我们还是可以实验一下:

  1. - (void)setInitialRoute:(NSString*)route
  2. FLUTTER_DEPRECATED("Use FlutterViewController initializer to specify initial route");

在iOS原生项目中,再创建一个按钮,点击不同的按钮跳转不同的Flutter页面

  1. @objc
  2. private func tap1() {
  3. let flutterVc = FlutterViewController()
  4. flutterVc.setInitialRoute("one")
  5. self.present(flutterVc, animated: true, completion: nil)
  6. }
  7. @objc
  8. private func tap2() {
  9. let flutterVc = FlutterViewController()
  10. flutterVc.setInitialRoute("two")
  11. self.present(flutterVc, animated: true, completion: nil)
  12. }

在Flutter中怎么实现呢?window.defaultRouteName这个就是从原生传递过来的值

  1. import 'dart:ui';
  2. import 'package:flutter/material.dart';
  3. void main() => runApp(MyApp(
  4. pageIndex: window.defaultRouteName,
  5. ));
  6. class MyApp extends StatelessWidget {
  7. final String pageIndex;
  8. const MyApp({Key? key, required this.pageIndex}) : super(key: key);
  9. // This widget is the root of your application.
  10. @override
  11. Widget build(BuildContext context) {
  12. return MaterialApp(
  13. title: 'Demo',
  14. home: Scaffold(
  15. appBar: AppBar(title: Text(pageIndex)),
  16. body: Center(child: Text(pageIndex)),
  17. ),
  18. );
  19. }
  20. }

command + shift + k清除iOS的缓存重新运行试下。ok了~
image.png

Channel定义的三种方式

Flutter定义了三种不同的Channel

  1. MethodChannel:传递方法调用,一次通讯
  2. BasicMessageChannel:持续通讯,收到消息之后还可以回复消息
  3. EventChannel:数据流,持续通讯

MethodChannel

我们也可以使用MethodChannel来通讯invokeMapMethod <==> setMethodCallHandler
在flutter中代码如下:

  1. onPressed: () {
  2. // 通讯
  3. const MethodChannel('one').invokeMapMethod('exit');
  4. },

在swift中对应的回调,这样点击中间的按钮就能dismiss掉。

  1. @objc
  2. private func tap1() {
  3. let methodChannel = FlutterMethodChannel(name: "one", binaryMessenger: self.flutterVc as! FlutterBinaryMessenger)
  4. methodChannel.setMethodCallHandler { call, result in
  5. if call.method == "exit"{
  6. self.dismiss(animated: true, completion: nil)
  7. }
  8. }
  9. self.present(flutterVc, animated: true, completion: nil)
  10. }

image.png

BasicMessageChannel

使用BasicMessageChannel的方式和上面的基本差不多,但是既然说了是持续通讯,那么使用TextField来验证下: send <==> setMethodCallHandler

  1. BasicMessageChannel _channel =
  2. const BasicMessageChannel('messageChannel', StandardMessageCodec());
  3. // ...
  4. TextField(
  5. onChanged: (String str) {
  6. _channel.send(str);
  7. },
  8. @override
  9. void initState() {
  10. // TODO: implement initState
  11. super.initState();
  12. _channel.setMessageHandler((message) {
  13. print('收到来自iOS的$message');
  14. return Future(() {});
  15. });
  16. }
  1. override func viewDidLoad() {
  2. super.viewDidLoad()
  3. messageChannel = FlutterBasicMessageChannel(name: "messageChannel", binaryMessenger: self.flutterVc.binaryMessenger)
  4. messageChannel.setMessageHandler { data, reply in
  5. print("收到了Flutter的数据\(String(describing: data))")
  6. }
  7. }
  8. override func touchesBegan(_ touches: Set<UITouch>, with event: UIEvent?) {
  9. a += 1;
  10. self.messageChannel.sendMessage("\(a)")
  11. }

image.png