开发者社区> JoanKing> 正文

Day22 - Flutter - 混合开发(下)

简介: Day22 - Flutter - 混合开发(下)
+关注继续查看
      • <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


image.png

image.png


2.2、嵌入iOS项目


  • 嵌入到现有iOS项目有多种方式:
    • 可以使用 CocoaPods 依赖管理和已安装的 Flutter SDK ;
    • 也可以通过手动编译 Flutter engine 、你的 dart 代码和所有 Flutter plugin 成 framework ,用 Xcode 手动集成到你的应用中,并更新编译设置;
  • 目前iOS项目几乎都已经使用Cocoapods进行管理,所以推荐使用第一种CocoaPods方式;
  • 我们按照如下的方式,搭建一个需要继承的iOS项目:我们暂且起名字:testdemoios
    • 1、为了进行测试,我们这里创建一个默认的iOS项目:使用Xcode创建即可


image.png


image.png

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' 后面的路径,我们可以放一个统一的位置,方便团队开发


image.png

image.png

image.png

image.png

    • 安装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)
   }
}

image.png

image.png


  • 提示:我当时运行代码报错: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



image.png


3、打开app,在flutter修改完项目后,在终端输入对应的指令,app界面跟着变化

  • 2.3.嵌入Android项目嵌入到现有Android项目有多种方式:
    • 编译为AAR文件(Android Archive):通过Flutter编译为aar,添加相关的依赖
    • 依赖模块的源码方式,在gradle进行配置
  • 这里我们采用第二种方式
    • 1>、创建一个Android的测试项目,使用Android Studio创建



image.png

2>、添加相关的依赖

  • 修改Android项目中的settings.gradle文件:


image.png


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中添加依赖:

image.png


dependencies {
   implementation project(':flutter')
}


编译代码,可能会出现如下错误: 1、这是因为从Java8开始才支持接口方法;2、Flutter Android引擎使用了该Java8的新特性


image.png

解决办法:通过设置Android项目工程的build.gradle配置使用Java8编译:


image.png

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



版权声明:本文内容由阿里云实名注册用户自发贡献,版权归原作者所有,阿里云开发者社区不拥有其著作权,亦不承担相应法律责任。具体规则请查看《阿里云开发者社区用户服务协议》和《阿里云开发者社区知识产权保护指引》。如果您发现本社区中有涉嫌抄袭的内容,填写侵权投诉表单进行举报,一经查实,本社区将立刻删除涉嫌侵权内容。

相关文章
如何设置阿里云服务器安全组?阿里云安全组规则详细解说
阿里云安全组设置详细图文教程(收藏起来) 阿里云服务器安全组设置规则分享,阿里云服务器安全组如何放行端口设置教程。阿里云会要求客户设置安全组,如果不设置,阿里云会指定默认的安全组。那么,这个安全组是什么呢?顾名思义,就是为了服务器安全设置的。安全组其实就是一个虚拟的防火墙,可以让用户从端口、IP的维度来筛选对应服务器的访问者,从而形成一个云上的安全域。
18580 0
阿里云服务器如何登录?阿里云服务器的三种登录方法
购买阿里云ECS云服务器后如何登录?场景不同,阿里云优惠总结大概有三种登录方式: 登录到ECS云服务器控制台 在ECS云服务器控制台用户可以更改密码、更换系.
27721 0
阿里云服务器安全组设置内网互通的方法
虽然0.0.0.0/0使用非常方便,但是发现很多同学使用它来做内网互通,这是有安全风险的,实例有可能会在经典网络被内网IP访问到。下面介绍一下四种安全的内网互联设置方法。 购买前请先:领取阿里云幸运券,有很多优惠,可到下文中领取。
21933 0
阿里云服务器端口号设置
阿里云服务器初级使用者可能面临的问题之一. 使用tomcat或者其他服务器软件设置端口号后,比如 一些不是默认的, mysql的 3306, mssql的1433,有时候打不开网页, 原因是没有在ecs安全组去设置这个端口号. 解决: 点击ecs下网络和安全下的安全组 在弹出的安全组中,如果没有就新建安全组,然后点击配置规则 最后如上图点击添加...或快速创建.   have fun!  将编程看作是一门艺术,而不单单是个技术。
19979 0
阿里云服务器ECS登录用户名是什么?系统不同默认账号也不同
阿里云服务器Windows系统默认用户名administrator,Linux镜像服务器用户名root
15287 0
腾讯云服务器 设置ngxin + fastdfs +tomcat 开机自启动
在tomcat中新建一个可以启动的 .sh 脚本文件 /usr/local/tomcat7/bin/ export JAVA_HOME=/usr/local/java/jdk7 export PATH=$JAVA_HOME/bin/:$PATH export CLASSPATH=.
14852 0
使用OpenApi弹性释放和设置云服务器ECS释放
云服务器ECS的一个重要特性就是按需创建资源。您可以在业务高峰期按需弹性的自定义规则进行资源创建,在完成业务计算的时候释放资源。本篇将提供几个Tips帮助您更加容易和自动化的完成云服务器的释放和弹性设置。
20878 0
+关注
433
文章
0
问答
文章排行榜
最热
最新
相关电子书
更多
JS零基础入门教程(上册)
立即下载
性能优化方法论
立即下载
手把手学习日志服务SLS,云启实验室实战指南
立即下载