- <2>、Java 代码
实现思路和上面是一致的,只是使用了Java来实现:
package com.example.batterylevel2; import androidx.annotation.NonNull; import io.flutter.embedding.android.FlutterActivity; import io.flutter.embedding.engine.FlutterEngine; import io.flutter.plugins.GeneratedPluginRegistrant; import io.flutter.plugin.common.MethodChannel; import android.content.ContextWrapper; import android.content.Intent; import android.content.IntentFilter; import android.os.BatteryManager; import android.os.Build.VERSION; import android.os.Build.VERSION_CODES; import android.os.Bundle; public class MainActivity extends FlutterActivity { private static final String CHANNEL = "com.jk/battery"; @Override public void configureFlutterEngine(@NonNull FlutterEngine flutterEngine) { // 1.创建MethodChannel对象 MethodChannel methodChannel = new MethodChannel(flutterEngine.getDartExecutor().getBinaryMessenger(), CHANNEL); // 2.添加调用方法的回调 methodChannel.setMethodCallHandler( (call, result) -> { // 2.1.如果调用的方法是getBatteryInfo,那么正常执行 if (call.method.equals("getBatteryInfo")) { // 2.1.1.调用另外一个自定义方法回去电量信息 int batteryLevel = getBatteryLevel(); // 2.1.2. 判断是否正常获取到 if (batteryLevel != -1) { // 获取到返回结果 result.success(batteryLevel); } else { // 获取不到抛出异常 result.error("UNAVAILABLE", "Battery level not available.", null); } } else { // 2.2.如果调用的方法是getBatteryInfo,那么正常执行 result.notImplemented(); } } ); } private int getBatteryLevel() { int batteryLevel = -1; if (VERSION.SDK_INT >= VERSION_CODES.LOLLIPOP) { BatteryManager batteryManager = (BatteryManager) getSystemService(BATTERY_SERVICE); batteryLevel = batteryManager.getIntProperty(BatteryManager.BATTERY_PROPERTY_CAPACITY); } else { Intent intent = new ContextWrapper(getApplicationContext()). registerReceiver(null, new IntentFilter(Intent.ACTION_BATTERY_CHANGED)); batteryLevel = (intent.getIntExtra(BatteryManager.EXTRA_LEVEL, -1) * 100) / intent.getIntExtra(BatteryManager.EXTRA_SCALE, -1); } return batteryLevel; } }
二、嵌入原有项目
首先,我们先明确一点:Flutter设计初衷并不是为了和其它平台进行混合开发,它的目的是为了打造一个完整的跨平台应用程序。
但是,实际开发中,原有项目完全使用Flutter进行重构并不现实,对于原有项目我们更多可能采用混合开发的方式。
- 2.1、创建Flutter模块
- 对于需要进行混合开发的原有项目,Flutter可以作为一个库或者模块,继承进现有项目中。
- 模块引入到你的Android或iOS应用中,以使用Flutter渲染一部分的UI,或者共享的Dart代码。
- 在Flutter v1.12中,添加到现有应用的基本场景已经被支持,每个应用在同一时间可以集成一个全屏幕的Flutter实例。
- 但是,目前一些场景依然是有限制的:
- 运行多个Flutter实例,或在屏幕局部上运行Flutter可能会导致不可以预测的行为;
- 在后台模式使用Flutter的能力还在开发中(目前不支持);
- 将Flutter库打包到另一个可共享的库或将多个Flutter库打包到同一个应用中,都不支持;
- 添加到应用在Android平台的实现基于 FlutterPlugin 的 API,一些不支持 FlutterPlugin 的插件可能会有不可预知的行为。
- 创建 Flutter Module
flutter create --template module my_flutter
- 创建完成后,该模块和普通的Flutter项目一直,可以通过Android Studio或VSCode打开、开发、运行;
- 目录结构如下:
- 和之前项目不同的iOS和Android项目是一个隐藏文件,并且我们通常不会单独打开它们再来运行;
- 它们的作用是将Flutter Module进行编译,之后继承到现有的项目中;
my_flutter/ ├── .iOS/ ├── .android/ ├── lib/ │ └── main.dart ├── test/ └── pubspec.yaml
2.2、嵌入iOS项目
- 嵌入到现有iOS项目有多种方式:
- 可以使用 CocoaPods 依赖管理和已安装的 Flutter SDK ;
- 也可以通过手动编译 Flutter engine 、你的 dart 代码和所有 Flutter plugin 成 framework ,用 Xcode 手动集成到你的应用中,并更新编译设置;
- 目前iOS项目几乎都已经使用Cocoapods进行管理,所以推荐使用第一种CocoaPods方式;
- 我们按照如下的方式,搭建一个需要继承的iOS项目:我们暂且起名字:
testdemoios
- 1、为了进行测试,我们这里创建一个默认的iOS项目:使用Xcode创建即可
2、将项目加入CocoaPods进行管理,电脑上需要已经安装了CocoaPods,直接百度输入 CocoaPods即可搜到很多的教程,按着教程来就好
初始化CocoaPods:
cd 进入刚才创建的 testdemoios pod init
编译Podfile文件:
# platform :ios, '9.0' # 添加模块所在路径,记得 `command + s` 保存 flutter_application_path = '../../my_flutter/my_flutter' load File.join(flutter_application_path, '.ios', 'Flutter', 'podhelper.rb') target 'testdemoios' do use_frameworks! # 安装Flutter模块 install_all_flutter_pods(flutter_application_path) end
提示:
flutter_application_path = '../../my_flutter/my_flutter'
后面的路径,我们可以放一个统一的位置,方便团队开发
- 安装CocoaPods的依赖
pod install
- 2.2.1、Swift代码里面嵌入 上面 my_flutter 包
为了在既有的iOS应用中展示Flutter页面,需要启动 Flutter Engine和 FlutterViewController。
通常建议为我们的应用预热一个 长时间存活 的FlutterEngine:
我们将在应用启动的AppDelegate.swift
中创建一个FlutterEngine
,并作为属性暴露给外界。
import UIKit import Flutter @UIApplicationMain class AppDelegate: UIResponder, UIApplicationDelegate { var window: UIWindow? lazy var flutterEngine = FlutterEngine(name: "my flutter engine") func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]?) -> Bool { // 开启引擎 flutterEngine.run() return true } }
- 在启动的ViewController中,创建一个UIButton,并且点击这个Button时,弹出FlutterViewController
import UIKit import Flutter class ViewController: UIViewController { override func viewDidLoad() { super.viewDidLoad() view.backgroundColor = .green let button = UIButton(frame: CGRect(x: 0, y: 0, width: 200, height: 50)) button .setTitle("进入 Flutter 界面", for: .normal) button.backgroundColor = .brown button.center = view.center button.addTarget(self, action: #selector(click), for: .touchUpInside) view.addSubview(button) } @objc func click() { let flutterVC = FlutterViewController(engine: (UIApplication.shared.delegate as! AppDelegate).flutterEngine, nibName: nibName, bundle: nil) self .present(flutterVC, animated: true, completion: nil) } }
- 提示:我当时运行代码报错:
framework not found FlutterPluginRegistrant
,我进行了一下pod update
就好了\- 我们也可以省略预先创建的 FlutterEngine :不推荐这样来做,因为在第一针图像渲染完成之前,可能会出现明显的延迟。
func showFlutter() { let flutterViewController = FlutterViewController(project: nil, nibName: nil, bundle: nil) present(flutterViewController, animated: true, completion: nil) }
- 2.2.2、Objective-C代码如果上面的代码希望使用Objective-C也是可以实现的:代码的逻辑是完成一致的
- AppDelegate.h代码:
@import UIKit; @import Flutter; @interface AppDelegate : FlutterAppDelegate @property (nonatomic,strong) FlutterEngine *flutterEngine; @end
- AppDelegate.m代码:
#import <FlutterPluginRegistrant/GeneratedPluginRegistrant.h> // Used to connect plugins. #import "AppDelegate.h" @implementation AppDelegate - (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary<UIApplicationLaunchOptionsKey, id> *)launchOptions { self.flutterEngine = [[FlutterEngine alloc] initWithName:@"my flutter engine"]; [self.flutterEngine run]; [GeneratedPluginRegistrant registerWithRegistry:self.flutterEngine]; return [super application:application didFinishLaunchingWithOptions:launchOptions]; } @end
- ViewController.m代码
@import Flutter; #import "AppDelegate.h" #import "ViewController.h" @implementation ViewController - (void)viewDidLoad { [super viewDidLoad]; UIButton *button = [UIButton buttonWithType:UIButtonTypeCustom]; [button addTarget:self action:@selector(showFlutter) forControlEvents:UIControlEventTouchUpInside]; [button setTitle:@"Show Flutter!" forState:UIControlStateNormal]; button.backgroundColor = UIColor.blueColor; button.frame = CGRectMake(80.0, 210.0, 160.0, 40.0); [self.view addSubview:button]; } - (void)showFlutter { FlutterEngine *flutterEngine = ((AppDelegate *)UIApplication.sharedApplication.delegate).flutterEngine; FlutterViewController *flutterViewController = [[FlutterViewController alloc] initWithEngine:flutterEngine nibName:nil bundle:nil]; [self presentViewController:flutterViewController animated:YES completion:nil]; } @end
扩展:在原生项目开启 Flutter的热更新和热加载,也就是flutter代码修改完,原生项目不用再启动,就可以直接看到flutter代码的修改内容
步骤如下:
1、关闭模拟器开启的项目,杀死模拟器的APP
2、进入module项目的根目录,终端执行下面的代码,选择对用的设备
flutter attach
3、打开app,在flutter修改完项目后,在终端输入对应的指令,app界面跟着变化
- 2.3.嵌入Android项目嵌入到现有Android项目有多种方式:
- 编译为AAR文件(Android Archive):通过Flutter编译为aar,添加相关的依赖
- 依赖模块的源码方式,在gradle进行配置
- 这里我们采用第二种方式
- 1>、创建一个Android的测试项目,使用Android Studio创建
2>、添加相关的依赖
- 修改Android项目中的settings.gradle文件:
include ':app' rootProject.name = "testdemoandroid" setBinding(new Binding([gradle: this])) // new evaluate(new File( // new settingsDir.parentFile, // new '../my_flutter/my_flutter/.android/include_flutter.groovy' // new ))
提示:
File()
后面的路径是my_flutter
项目的路径,我放置的和上面iOS那个图一样
- 我们需要在Android项目工程的build.gradle中添加依赖:
dependencies { implementation project(':flutter') }
编译代码,可能会出现如下错误: 1、这是因为从Java8开始才支持接口方法;2、Flutter Android引擎使用了该Java8的新特性
解决办法:通过设置Android项目工程的build.gradle配置使用Java8编译:
compileOptions { sourceCompatibility 1.8 targetCompatibility 1.8 }
- 接下来,我们这里尝试添加一个Flutter的screen到Android应用程序中Flutter提供了一个FlutterActivity来展示Flutter界面在Android应用程序中,我们需要先对FlutterActivity进行注册:
- 在AndroidManifest.xml中进行注册
<activity android:name="io.flutter.embedding.android.FlutterActivity" android:theme="@style/AppTheme" android:configChanges="orientation|keyboardHidden|keyboard|screenSize|locale|layoutDirection|fontScale|screenLayout|density|uiMode" android:hardwareAccelerated="true" android:windowSoftInputMode="adjustResize" />
- 2.3.1、Java代码
package com.jk.testandroid; import androidx.appcompat.app.AppCompatActivity; import android.os.Bundle; import io.flutter.embedding.android.FlutterActivity; public class MainActivity extends AppCompatActivity { @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); // setContentView(R.layout.activity_main); startActivity( FlutterActivity.createDefaultIntent(this) ); } }
- 也可以在创建时,传入默认的路由:
package com.jk.testandroid; import androidx.appcompat.app.AppCompatActivity; import android.os.Bundle; import io.flutter.embedding.android.FlutterActivity; public class MainActivity extends AppCompatActivity { @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); // setContentView(R.layout.activity_main); startActivity( FlutterActivity .withNewEngine() .initialRoute("/my_route") .build(currentActivity) ); } }
- 2.3.2、Kotlin代码
package com.jk.test_demo_a_k import androidx.appcompat.app.AppCompatActivity import android.os.Bundle import io.flutter.embedding.android.FlutterActivity class MainActivity : AppCompatActivity() { override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) // setContentView(R.layout.activity_main) startActivity( FlutterActivity.createDefaultIntent(this) ) } }
- 也可以在创建时指定路由:
package com.coderwhy.test_demo_a_k import androidx.appcompat.app.AppCompatActivity import android.os.Bundle import io.flutter.embedding.android.FlutterActivity class MainActivity : AppCompatActivity() { override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) // setContentView(R.layout.activity_main) startActivity( FlutterActivity .withNewEngine() .initialRoute("/my_route") .build(this) ); } }
三、Flutter模块调试
一旦将Flutter模块继承到你的项目中,并且使用Flutter平台的API运行Flutter引擎或UI,那么就可以先普通的Android或者iOS一样来构建自己的Android或者iOS项目了
但是Flutter的有一个非常大的优势是其快速开发,也就是hot reload。
那么对应Flutter模块,我们如何使用hot reload加速我们的调试速度呢?
- 可以使用
flutter attach
# --app-id是指定哪一个应用程序 # -d是指定连接哪一个设备 flutter attach --app-id com.coderwhy.ios-my-test -d 3D7A877C-B0DD-4871-8D6E-0C5263B986CD