内容简介:使用Flutter从零开始开发App是一件轻松惬意的事情,但对于一些成熟的产品来说,完全摒弃原有App的历史沉淀,全面转向Flutter是不现实的。因此使用Flutter去统一Android、iOS技术栈,把它作为已有原生App的扩展能力,通过有序推进来提升移动终端的开发效率。目前,想要在已有的原生App里嵌入一些Flutter页面主要有两种方案。一种是将原生工程作为Flutter工程的子工程,由Flutter进行统一管理,这种模式称为统一管理模式。另一种是将Flutter工程作为原生工程的子模块,维持原有
混合开发简介
使用Flutter从零开始开发App是一件轻松惬意的事情,但对于一些成熟的产品来说,完全摒弃原有App的历史沉淀,全面转向Flutter是不现实的。因此使用Flutter去统一Android、iOS技术栈,把它作为已有原生App的扩展能力,通过有序推进来提升移动终端的开发效率。
目前,想要在已有的原生App里嵌入一些Flutter页面主要有两种方案。一种是将原生工程作为Flutter工程的子工程,由Flutter进行统一管理,这种模式称为统一管理模式。另一种是将Flutter工程作为原生工程的子模块,维持原有的原生工程管理方式不变,这种模式被称为三端分离模式。
在Flutter框架出现早期,由于官方提供的混编方式以及资料有限,国内较早使用Flutter进行混合开发的团队大多使用的是统一管理模式。但是,随着业务迭代的深入,统一管理模式的弊端也随之显露,不仅三端(Android、iOS和Flutter)代码耦合严重,相关 工具 链耗时也随之大幅增长,最终导致开发效率降低。所以,后续使用Flutter进行混合开发的团队大多使用三端代码分离的模式来进行依赖治理,最终实现Flutter工程的轻量级接入。
除了可以轻量级接入外,三端代码分离模式还可以把Flutter模块作为原生工程的子模块,从而快速地接入Flutter模块,降低原生工程的改造成本。在完成对Flutter模块的接入后,Flutter工程可以使用Android Studio进行开发,无需再打开原生工程就可以对Dart代码和原生代码进行开发调试。
使用三端分离模式进行Flutter混合开发的关键是抽离Flutter工程,将不同平台的构建产物依照标准组件化的形式进行管理,即Android使用aar、iOS使用pod。也就是说,Flutter的混编方案其实就是将Flutter模块打包成aar或者pod库,然后在原生工程像引用其他第三方原生组件库那样引入Flutter模块即可。
Flutter模块
默认情况下,新创建的Flutter工程会包含Flutter目录和原生工程的目录。在这种情况下,原生工程会依赖Flutter工程的库和资源,并且无法脱离Flutter工程独立构建和运行。
在混合开发中,原生工程对Flutter的依赖主要分为两部分。一个是Flutter的库和引擎,主要包含Flutter的Framework 库和引擎库;另一个是Flutter模块工程,即Flutter混合开发中的Flutter功能模块,主要包括Flutter工程lib目录下的Dart代码实现。
对于原生工程来说,集成Flutter只需要在同级目录创建一个Flutter模块,然后构建iOS和Android各自的Flutter依赖库即可。接下来,我们只需要在原生项目的同级目录下,执行Flutter提供的构建模块命令创建Flutter模块即可,如下所示。
flutter create -t module flutter_library
其中,flutter_library为Flutter模块名。执行上面的命令后,会在原生工程的同级目录下生成一个flutter_library模块工程。Flutter模块也是Flutter工程,使用Android Studio打开它,其目录如下图所示。
可以看到,和普通的Flutter工程相比,Flutter模块工程也内嵌了Android工程和iOS工程,只不过默认情况下,Android工程和iOS工程是隐藏的。因此,对于Flutter模块工程来说,也可以像普通工程一样使用 Android Studio进行开发和调试。
同时,相比普通的Flutter工程,Flutter模块工程的Android工程目录下多了一个Flutter目录,此目录下的build.gradle配置就是我们构建aar时的打包配置。同样,在Flutter模块工程的iOS工程目录下也会找到一个Flutter目录,这也是Flutter模块工程既能像Flutter普通工程一样使用Android Studio进行开发调试,又能打包构建aar或pod的原因。
Android集成Flutter
在原生Android工程中集成Flutter,原生工程对Flutter的依赖主要包括两部分,分别是Flutter库和引擎,以及Flutter工程构建产物。
- Flutter库和引擎:包含icudtl.dat、libFlutter.so以及一些class文件,最终这些文件都会被封装到Flutter.jar中。
- Flutter工程产物:包括应用程序数据段 isolate_snapshot_data、应用程序指令段 isolate_snapshot_instr、虚拟机数据段vm_snapshot_data、虚拟机指令段vm_snapshot_instr以及资源文件flutter_assets。
和原生Android工程集成其他插件库的方式一样,在原生Android工程中引入Flutter模块需要先在settings.gradle中添加如下代码。
setBinding(new Binding([gradle: this])) evaluate(new File( settingsDir.parentFile, 'flutter_library/.android/include_flutter.groovy'))
其中,flutter_library为我们创建的Flutter模块。然后,在原生Android工程的app目录的build.gradle文件中添加如下依赖。
dependencies { implementation project(":flutter") }
然后编译并运行原生Android工程,如果没有任何错误则说明集成Flutter模块成功。需要说明的是,由于Flutter支持的最低版本为16,所以需要将Android项目的minSdkVersion修改为16。
如果出现“程序包android.support.annotation不存在”的错误,需要使用如下的命令来创建Flutter模块,因为最新版本的Android默认使用androidx来管理包。
flutter create --androidx -t module flutter_library
对于Android原生工程,如果还没有升级到androidx,可以在原生Android工程上右键,然后依次选择【Refactor】→【Migrate to Androidx】将Android工程升级到androidx包管理。
在原生Android工程中成功添加Flutter模块依赖后,打开原生Android工程,并在应用的入口MainActivity文件中添加如下代码。
public class MainActivity extends AppCompatActivity { @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); View flutterView = Flutter.createView(this, getLifecycle(), "route1"); FrameLayout.LayoutParams layoutParams = new FrameLayout.LayoutParams(ViewGroup.LayoutParams.MATCH_PARENT, ViewGroup.LayoutParams.MATCH_PARENT); addContentView(flutterView, layoutParams); } }
通过Flutter提供的createView()方法,可以将Flutter页面构建成Android能够识别的视图,然后将这个视图使用Android提供的addContentView()方法添加到父窗口即可。重新运行原生Android工程,最终效果如下图所示。
如果原生Android的MainActivity加载的是一个FrameLayout,那么加载只需要将Flutter页面构建成一个Fragment即可,如下所示。
public class MainActivity extends AppCompatActivity { @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); FragmentTransaction ft= getSupportFragmentManager().beginTransaction(); ft.replace(R.id.fragment_container, Flutter.createFragment("Hello Flutter")); ft.commit(); } }
除了使用Flutter模块方式集成外,还可以将Flutter模块打包成aar,然后再添加依赖。在flutter_library根目录下执行aar打包构建命令即可抽取Flutter依赖,如下所示。
flutter build apk --debug
此命令的作用是将Flutter库和引擎以及工程产物编译成一个aar包,上面命令编译的aar包是debug版本,如果需要构建release版本,只需要把命令中的debug换成release即可。
打包构建的flutter-debug.aar位于.android/Flutter/build/outputs/aar/目录下,可以把它拷贝到原生Android工程的app/libs目录下,然后在原生Android工程的app目录的打包配置build.gradle中添加对它的依赖,如下所示。
dependencies { implementation(name: 'flutter-debug', ext: 'aar') }
然后重新编译一下项目,如果没有任何错误提示则说明Flutter模块被成功集成到Android原生工程中。
iOS集成Flutter
原生iOS工程对Flutter的依赖包含Flutter库和引擎,以及Flutter工程编译产物。其中,Flutter 库和引擎指的是Flutter.framework等,Flutter工程编译产物指的是 App.framework等。
在原生iOS工程中集成Flutter需要先配置好CocoaPods,CocoaPods是iOS的类库管理工具,用来管理第三方开源库。在原生iOS工程中执行pod init命令创建一个Podfile文件,然后在Podfile文件中添加Flutter模块依赖,如下所示。
flutter_application_path = '../flutter_ library/ load File.join(flutter_application_path, '.ios', 'Flutter', 'podhelper.rb') target 'iOSDemo' do # Comment the next line if you don't want to use dynamic frameworks use_frameworks! install_all_flutter_pods(flutter_application_path) # Pods for iOSDemo … //省略其他脚本 end '
然后,关闭原生iOS工程,并在原生iOS工程的根目录执行pod install命令安装所需的依赖包。安装完成后,使用Xcode打开iOSDemo.xcworkspace原生工程。
默认情况下,Flutter是不支持Bitcode的,Bitcode是一种iOS编译程序的中间代码,在原生iOS工程中集成Flutter需要禁用Bitcode。在Xcode中依次选择【TAGETS】→【Build Setttings】→【Build Options】→【Enable Bitcode】来禁用Bitcode,如下图所示。
如果使用的是Flutter早期的版本,还需要添加build phase来支持构建Dart代码。依次选择【TAGGETS】→【Build Settings】→【Enable Phases】,然后点击左上角的加号新建一个“New Run Script Phase”,添加如下脚本代码。
"$FLUTTER_ROOT/packages/flutter_tools/bin/xcode_backend.sh" build "$FLUTTER_ROOT/packages/flutter_tools/bin/xcode_backend.sh" embed
不过,最新版本的Flutter已经不需要再添加脚本了。重新运行原生iOS工程,如果没有任何错误则说明iOS成功集成Flutter模块。
除了使用Flutter模块方式外,还可以将Flutter模块打包成可以依赖的动态库,然后再使用CocoaPods添加动态库。首先,在flutter_library根目录下执行打包构建命令生成framework动态库,如下所示。
flutter build ios --debug
上面命令是将Flutter工程编译成Flutter.framework和App.framework动态库。如果要生成release版本,只需要把命令中的debug换成release即可。
然后,在原生iOS工程的根目录下创建一个名为FlutterEngine的目录,并把生成的两个framework动态库文件拷贝进去。不过,iOS生成模块化产物要比Android多一个步骤,因为需要把Flutter工程编译生成的库手动封装成一个pod。首先,在flutter_ library该目录下创建FlutterEngine.podspec,然后添加如下脚本代码。
Pod::Spec.new do |s| s.name = 'FlutterEngine' s.version = '0.1.0' s.summary = 'FlutterEngine' s.description = <<-DESC TODO: Add long description of the pod here. DESC s.homepage = 'https://github.com/xx/FlutterEngine' s.license = { :type => 'MIT', :file => 'LICENSE' } s.author = { 'xzh' => '1044817967@qq.com' } s.source = { :git => "", :tag => "#{s.version}" } s.ios.deployment_target = '9.0' s.ios.vendored_frameworks = 'App.framework', 'Flutter.framework' end
然后,执行pod lib lint命令即可拉取Flutter模块所需的组件。接下来,在原生iOS工程的Podfile文件添加生成的库即可。
target 'iOSDemo' do pod 'FlutterEngine', :path => './' end
重新执行pod install命令安装依赖库,原生iOS工程集成Flutter模块就完成了。接下来,使用Xcode打开ViewController.m文件,然后添加如下代码。
#import "ViewController.h" #import <Flutter/Flutter.h> #import <FlutterPluginRegistrant/GeneratedPluginRegistrant.h> @interface ViewController () @end @implementation ViewController - (void)viewDidLoad { [super viewDidLoad]; UIButton *button = [[UIButton alloc]init]; [button setTitle:@"加载Flutter模块" forState:UIControlStateNormal]; button.backgroundColor=[UIColor redColor]; button.frame = CGRectMake(50, 50, 200, 100); [button setTitleColor:[UIColor redColor] forState:UIControlStateHighlighted]; [button addTarget:self action:@selector(buttonPrint) forControlEvents:UIControlEventTouchUpInside]; [self.view addSubview:button]; } - (void)buttonPrint{ FlutterViewController * flutterVC = [[FlutterViewController alloc]init]; [flutterVC setInitialRoute:@"defaultRoute"]; [self presentViewController:flutterVC animated:true completion:nil]; } @end
在上面的代码中,我们在原生iOS中创建了一个按钮,点击按钮时就会跳转到Flutter页面,最终效果如下图所示。
默认情况下,Flutter为提供了两种调用方式,分别是FlutterViewController和FlutterEngine。对于FlutterViewController来说,打开ViewController.m文件,在里面添加一个加载flutter页面的方法并且添加一个按钮看来调用。
Flutter模块调试
众所周知,Flutter的优势之一就是在开发过程中使用热重载功能来实现快速调试。默认情况下,在原生工程中集成Flutter模块后热重载功能是失效的,需要重新运行原生工程才能看到效果。如此一来,Flutter开发的热重载优势就失去了,并且开发效率也随之降低。
那么,能不能在混合项目中开启Flutter的热重载呢?答案是可以的,只需要经过如下步骤即可开启热重载功能。首先,关闭原生应用,此处所说的关闭是指关闭应用的进程,而不是简单的退出应用。在Flutter模块的根目录中输入flutter attach命令,然后再次打开原生应用,就会看到连接成功的提示,如下图所示。
如果同时连接了多台设备,可以使用flutter attach -d 命令来指定连接的设备。接下来,只需要按r键即可执行热重载,按R键即可执行热重启,按d键即可断开连接。
在Flutter工程中,我们可以直接点击debug按钮来进行代码调试,但在混合项目中,直接点击debug按钮是不起作用的。此时,可以使用Android Studio提供的flutter attach按钮来建立与flutter模块的连接,进行实现对flutter模块的代码调试,如图下图所示。
上面只是完成了在原生工程中引入Flutter模块,具体开发时还会遇到与Flutter模块的通信问题、路由管理问题,以及打包等。
以上就是本文的全部内容,希望本文的内容对大家的学习或者工作能带来一定的帮助,也希望大家多多支持 码农网
猜你喜欢:- Flutter混合开发小记
- 如何进行Flutter混合开发
- Flutter 混合开发 (交互通信)
- Flutter 混合开发框架模式探索
- 来了!Flutter混合开发专题一
- Flutter 混合开发探索与实践
本站部分资源来源于网络,本站转载出于传递更多信息之目的,版权归原作者或者来源机构所有,如转载稿涉及版权问题,请联系我们。
Machine Learning in Action
Peter Harrington / Manning Publications / 2012-4-19 / GBP 29.99
It's been said that data is the new "dirt"—the raw material from which and on which you build the structures of the modern world. And like dirt, data can seem like a limitless, undifferentiated mass. ......一起来看看 《Machine Learning in Action》 这本书的介绍吧!