在Flutter项目上创建Android库

时间:2018-11-06 01:17:14

标签: android flutter android-library

我正在尝试在flutter项目之上创建一个android库以进行分发。我使用flutter开发了一个应用程序,但我想将其包装在Android库中。我不断收到错误消息Transform output file /Users/Dev/Documents/projects/LibExample/testlib/build/intermediates/flutter/flutter-x86.jar does not exist.我已经阅读了几乎所有在线搜索内容,但到目前为止没有任何内容。

LibExample是我使用该库的示例应用程序,而testlib是Android库。在testlib中,我设置了build.graddle来定位flutter.sdk。我还指定了颤振源的位置。每次我sync gradle文件时,都会收到错误/Users/Dev/Documents/projects/LibExample/testlib/build/intermediates/flutter/flutter-x86.jar不存在。 `

这是flutter doctor -v的输出。

flutter doctor -v
[✓] Flutter (Channel beta, v0.5.2-pre.1, on Mac OS X 10.13.6 17G65, locale en-US)
    • Flutter version 0.5.2-pre.1 at /Users/Dev/Downloads/flutter
    • Framework revision 142e2f41ba (9 weeks ago), 2018-09-03 12:50:53 +0100
    • Engine revision 1ed25ca7b7
    • Dart version 2.0.0-dev.58.0.flutter-f981f09760

[✓] Android toolchain - develop for Android devices (Android SDK 28.0.3)
    • Android SDK at /Users/Dev/Library/Android/sdk
    • Android NDK location not configured (optional; useful for native profiling support)
    • Platform android-28, build-tools 28.0.3
    • Java binary at: /Applications/Android Studio.app/Contents/jre/jdk/Contents/Home/bin/java
    • Java version OpenJDK Runtime Environment (build 1.8.0_152-release-1136-b06)
    • All Android licenses accepted.

[✓] iOS toolchain - develop for iOS devices (Xcode 10.1)
    • Xcode at /Applications/Xcode.app/Contents/Developer
    • Xcode 10.1, Build version 10B61
    • ios-deploy 1.9.2
    • CocoaPods version 1.5.3

[✓] Android Studio (version 3.2)
    • Android Studio at /Applications/Android Studio.app/Contents
    • Flutter plugin version 29.1.1
    • Dart plugin version 181.5656
    • Java version OpenJDK Runtime Environment (build 1.8.0_152-release-1136-b06)

[!] VS Code (version 1.28.0)
    • VS Code at /Applications/Visual Studio Code.app/Contents
    • Flutter extension not installed; install from
      https://marketplace.visualstudio.com/items?itemName=Dart-Code.flutter

[✓] Connected devices (1 available)
    • Android SDK built for x86 • emulator-5554 • android-x86 • Android 5.1.1 (API 22) (emulator)

! Doctor found issues in 1 category.

1 个答案:

答案 0 :(得分:2)

我终于在一个颤抖的维基page上偶然发现了此功能。 撰写本文时要注意的一件非常重要的事情;

“ add2app”支持处于预览状态,到目前为止仅在主频道上可用。

尽管此功能仍处于试验阶段,但仍可以正常使用(很少有障碍,例如从Android主机调用时启动flutter应用程序所需的时间)。

使用flutter create xxx创建的Flutter项目包括用于Flutter / Dart代码的非常简单的宿主应用程序(单个Activity Android宿主和单个ViewController iOS宿主)。您可以修改这些主机应用程序以适合您的需求,然后从那里构建。

但是,如果您要从任一平台的现有主机应用程序开始,则可能希望将Flutter项目作为某种形式的库包含在该应用程序中。

这是Flutter模块模板提供的。执行flutter create -t module xxx会产生一个Flutter项目,其中包含一个Android库和一个Cocoapods Pod,供您现有的主机应用程序使用。

Android

创建Flutter模块 假设您在some / path / MyApp上有一个现有的Android应用程序,并且希望Flutter项目作为同级项目:

$ cd some/path/
$ flutter create -t module my_flutter

这将创建一个some/path/my_flutter/ Flutter模块项目,其中包含一些Dart代码以帮助您入门,以及一个.android/隐藏的子文件夹,该子文件夹将模块项目包装在Android库中。

(以下内容不是必需的,如果您愿意,可以使用Gradle构建该库:

$ cd .android/
$ ./gradlew flutter:assembleDebug

这将在flutter-debug.aar中生成一个.android/Flutter/build/outputs/aar/.存档文件

使主机应用程序依赖Flutter模块

将Flutter模块作为子项目包含在主机应用的settings.gradle中:

// MyApp/settings.gradle
include ':app'                                     // assumed existing content
setBinding(new Binding([gradle: this]))                                 // new
evaluate(new File(                                                      // new
  settingsDir.parentFile,                                               // new
  'my_flutter/.android/include_flutter.groovy'                          // new
))                                                                      // new

绑定和脚本评估允许Flutter模块本身include(作为:flutter)和该模块使用的所有Flutter插件(:package_info:video_player等) )在您的settings.gradle的评估环境中。

在您的应用中引入对Flutter模块的implementation依赖项:

// MyApp/app/build.gradle
:
dependencies {
  implementation project(':flutter')
  :
}

使用Java代码中的Flutter模块

使用Flutter模块的Java API将Flutter视图添加到您的主机应用程序。这可以通过直接使用Flutter.createView来完成:

// MyApp/app/src/main/java/some/package/MainActivity.java
fab.setOnClickListener(new View.OnClickListener() {
  @Override
  public void onClick(View view) {
    View flutterView = Flutter.createView(
      MainActivity.this,
      getLifecycle(),
      "route1"
    );
    FrameLayout.LayoutParams layout = new FrameLayout.LayoutParams(600, 800);
    layout.leftMargin = 100;
    layout.topMargin = 200;
    addContentView(flutterView, layout);
  }
});

还可以创建一个FlutterFragment来单独处理生命周期:

// MyApp/app/src/main/java/some/package/SomeActivity.java
fab.setOnClickListener(new View.OnClickListener() {
  @Override
  public void onClick(View view) {
    FragmentTransaction tx = getSupportFragmentManager().beginTransaction();
    tx.replace(R.id.someContainer, Flutter.createFragment("route1"));
    tx.commit();
  }
});

上面,我们使用字符串"route1"告诉Dart代码在Flutter视图中显示哪个窗口小部件。 Flutter模块项目模板的lib/main.dart文件应位于(或以其他方式解释)所提供的路由字符串(switch可用)上的window.defaultRouteName,以确定要创建并传递给{{1 }}。

runApp

完全由您决定要路由哪个字符串以及如何解释它们。

构建并运行您的应用

您以与添加Flutter模块依赖项之前(通常使用Android Studio)完全相同的方式来构建和运行import 'dart:ui'; import 'package:flutter/material.dart'; void main() => runApp(_widgetForRoute(window.defaultRouteName)); Widget _widgetForRoute(String route) { switch (route) { case 'route1': return SomeWidget(...); case 'route2': return SomeOtherWidget(...); default: return Center( child: Text('Unknown route: $route', textDirection: TextDirection.ltr), ); } } 。编辑,调试和分析Android代码也是如此。

热重启/重新加载和调试Dart代码

正在进行全面的IDE集成以支持使用混合应用程序的Flutter / Dart代码。但是基本原理已经通过Flutter命令行工具和Dart Observatory Web用户界面提供。

连接设备或启动仿真器。然后使Flutter CLI工具监听您的应用程序:

MyApp

正在等待Nexus 5X上Flutter的连接... 从Android Studio(或您通常采用的任何一种方式)以调试模式启动$ cd some/path/my_flutter $ flutter attach 。导航到使用Flutter的应用程序区域。然后回到终端,您应该看到类似于以下内容的输出:

MyApp

您现在可以在Done. Syncing files to device Nexus 5X... 5.1s To hot reload changes while running, press "r". To hot restart (and rebuild state), press "R". An Observatory debugger and profiler on Nexus 5X is available at: http://127.0.0.1:59556/ For a more detailed help message, press "h". To quit, press "q". 中编辑Dart代码,并且可以通过在终端中按my_flutter来热加载更改。您也可以将上面的URL粘贴到浏览器中,以使用Dart天文台设置断点,分析内存保留和其他调试任务。

iOS 创建Flutter模块

假设您在r上已有一个iOS应用,并且希望Flutter项目作为同级项目:

some/path/MyApp

这将创建一个$ cd some/path/ $ flutter create -t module my_flutter Flutter模块项目,其中包含一些Dart代码以帮助您入门,以及一个some/path/my_flutter/隐藏的子文件夹,该子文件夹包装了包含一些Cocoapods和一个辅助Ruby脚本的模块项目。

使主机应用程序依赖Flutter模块

下面的描述假定您现有的iOS应用程序具有与您要求Xcode版本10.0使用Objective-C生成新的“单视图应用程序”项目所得到的结构类似的结构。如果您现有的应用程序具有不同的文件夹结构和/或现有的.ios/文件,则可以重用它们,但是可能需要相应地调整下面提到的一些相对路径。

假定的文件夹结构如下:

.xcconfig

将Flutter应用添加到Podfile

集成Flutter框架需要使用CocoaPods依赖管理器。这是因为Flutter框架也需要对my_flutter中可能包括的所有Flutter插件可用。

如果需要,请参阅cocoapods.org,了解如何在您的开发计算机上安装CocoaPods。

如果您的主机应用程序(some/path/ my_flutter/ lib/main.dart .ios/ MyApp/ MyApp/ AppDelegate.h AppDelegate.m (or swift) : )已经在使用Cocoapods,则只需执行以下操作即可与您的MyApp应用程序集成:

将以下几行添加到您的my_flutter

Podfile

运行flutter_application_path = 'path/to/flutter_app/' eval(File.read(File.join(flutter_application_path, '.ios', 'Flutter', 'podhelper.rb')), binding)

无论何时在pod install中更改Flutter插件的依赖性,都需要运行从some/path/my_flutter/pubspec.yaml获取的flutter软件包,以刷新some/path/my_flutter脚本读取的插件列表。然后从podhelper.rb重新运行pod install。

podhelper.rb脚本将确保将插件和Flutter.framework添加到项目中,并确保所有目标均禁用了位码。

添加用于构建Dart代码的构建阶段

在“项目”导航器中选择顶级some/path/MyApp项目。在主视图的左侧选择 TARGET MyApp,然后选择MyApp标签。通过单击主视图左上方的Build Phases添加一个新的构建阶段。选择+阶段。展开刚刚添加到阶段列表的新New Run Script

将以下内容粘贴到Shell字段下方的文本区域中:

Run Script

最后,将新的构建阶段拖到“目标依赖项”阶段之后。

您现在应该可以使用"$FLUTTER_ROOT/packages/flutter_tools/bin/xcode_backend.sh" build "$FLUTTER_ROOT/packages/flutter_tools/bin/xcode_backend.sh" embed 来构建项目。

引擎盖下 如果您出于某种原因手动执行此操作或调试为什么这些步骤无法执行,请执行以下操作:

  1. ⌘B(引擎库)已嵌入到您的 适用于您的应用。这必须与发布类型匹配 (调试/配置文件/发行版)以及您应用的架构 (arm *,i386,x86_64等)。 Cocoapods作为供应商将其引入 框架并确保将其嵌入到您的本机应用程序中。
  2. Flutter.framework(您的Flutter应用程序二进制文件)已嵌入 您的应用。
  3. App.framework文件夹已被嵌入为资源-它 包含字体,图像,并且在某些构建模式下还包含 引擎在运行时所需的二进制文件。 存在的问题 文件夹可能会导致运行时错误,例如“无法为之运行引擎 配置”-通常表示该文件夹不是 被嵌入,或者您试图与 启用AOT的引擎,反之亦然!
  4. 任何插件都被添加为Cocoapods。从理论上讲,应该 也可以手动合并它们,但这变得很多 更特定于插件本身。
  5. 项目中的每个目标都禁用了位码。这是一个 与Flutter Engine链接的要求。
  6. Generated.xcconfig(包含Flutter特定的环境 变量)包含在发行版和调试.xcconfig文件中, 椰子足产生。

构建阶段脚本(xcode_backend.sh)确保所构建的二进制文件与文件夹中实际存在的Dart代码保持最新。一旦this pull request登陆(已经成功!),它也会尝试遵循您的构建配置设置。

编写代码以从主机应用程序使用FlutterViewController 正确的位置将取决于您的主机应用程序。这是一个由Xcode 10.0生成的主机应用程序空白屏幕的示例。

首先声明您的应用程序委托为flutter_assets的子类。

FlutterAppDelegate中:

AppDelegate.h

这使#import <UIKit/UIKit.h> #import <Flutter/Flutter.h> @interface AppDelegate : FlutterAppDelegate @end 非常简单,除非您的主机应用需要在此处覆盖其他方法:

AppDelegate.m

如果您使用的是Swift语言,则可以在#import <FlutterPluginRegistrant/GeneratedPluginRegistrant.h> // Only if you have Flutter Plugins #include "AppDelegate.h" @implementation AppDelegate // This override can be omitted if you do not have any Flutter Plugins. - (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions { [GeneratedPluginRegistrant registerWithRegistry:self]; return [super application:application didFinishLaunchingWithOptions:launchOptions]; } @end

中执行以下操作
AppDelegate.swift:

.......

import UIKit
import Flutter
import FlutterPluginRegistrant // Only if you have Flutter Plugins.

@UIApplicationMain
class AppDelegate: FlutterAppDelegate {

  // Only if you have Flutter plugins.
  override func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplicationLaunchOptionsKey: Any]?) -> Bool {
    GeneratedPluginRegistrant.register(with: self);
    return super.application(application, didFinishLaunchingWithOptions: launchOptions);
  }

}

或者,使用Swift:

#import <Flutter/Flutter.h>
#import "ViewController.h"

@implementation ViewController
- (void)viewDidLoad {
    [super viewDidLoad];
    UIButton *button = [UIButton buttonWithType:UIButtonTypeCustom];
    [button addTarget:self
               action:@selector(handleButtonAction)
     forControlEvents:UIControlEventTouchUpInside];
    [button setTitle:@"Press me" forState:UIControlStateNormal];
    [button setBackgroundColor:[UIColor blueColor]];
    button.frame = CGRectMake(80.0, 210.0, 160.0, 40.0);
    [self.view addSubview:button];
}

- (void)handleButtonAction {
    FlutterViewController* flutterViewController = [[FlutterViewController alloc] init];
    [self presentViewController:flutterViewController animated:false completion:nil];
}
@end

您现在应该可以在模拟器或设备上构建并启动MyApp。按下按钮应使用标准Flutter Demo计数应用程序调出全屏Flutter视图。您可以使用路线在应用程序的不同位置显示不同的小部件,如上面的Android部分所述。要设置路线,请致电

Objective-C:

ViewController.swift:

import UIKit
import Flutter

class ViewController: UIViewController {
  override func viewDidLoad() {
    super.viewDidLoad()
    let button = UIButton(type:UIButtonType.custom)
    button.addTarget(self, action: #selector(handleButtonAction), for: .touchUpInside)
    button.setTitle("Press me", for: UIControlState.normal)
    button.frame = CGRect(x: 80.0, y: 210.0, width: 160.0, height: 40.0)
    button.backgroundColor = UIColor.blue
    self.view.addSubview(button)
  }

  @objc func handleButtonAction() {
    let flutterViewController = FlutterViewController()
    self.present(flutterViewController, animated: false, completion: nil)
  }
}

迅速:

[flutterViewController setInitialRoute:@"route1"];

在构建flutterViewController.setInitialRoute("route1") 之后(并在展示之前)。

您可以通过在Dart代码中调用FlutterViewController来关闭Flutter应用。

构建并运行您的应用

您使用Xcode构建和运行MyApp的方式与添加Flutter模块依赖项之前的方式完全相同。编辑,调试和分析iOS代码也是如此。

热重启/重新加载和调试Dart代码

连接设备或启动模拟器。然后使Flutter CLI工具监听您的应用程序:

SystemNavigator.pop()

在Xcode的调试模式下启动$ cd some/path/my_flutter $ flutter attach Waiting for a connection from Flutter on iPhone X... 。导航到使用Flutter的应用程序区域。然后回到终端,您应该看到类似于以下内容的输出:

MyApp

您现在可以在Done. Syncing files to device iPhone X... 4.7s To hot reload changes while running, press "r". To hot restart (and rebuild state), press "R". An Observatory debugger and profiler on iPhone X is available at: http://127.0.0.1:54741/ For a more detailed help message, press "h". To quit, press "q". 中编辑Dart代码,并且可以通过在终端中按my_flutter来热加载更改。您也可以将上面的URL粘贴到浏览器中,以使用Dart天文台设置断点,分析内存保留和其他调试任务。

调试Flutter的特定实例

可以向应用程序添加Flutter(r)的多个实例。 root isolates默认连接到所有可用的隔离。然后,将从附加的CLI发送的所有命令转发到每个附加的隔离区。

通过从附加的flutter attach CLI工具中键入l列出所有附加的隔离。如果未指定,将从dart入口点文件和函数名称中自动生成隔离名称。

同时显示两个Flutter隔离的应用程序的示例flutter输出:

l
  1. 分两个步骤附加到特定的隔离物中:

在其Dart来源中命名感兴趣的Flutter根分离物。

Connected views:
  main.dart$main-517591213 (isolates/517591213)
  main.dart$main-332962855 (isolates/332962855)
  1. 使用// main.dart import 'dart:ui' as ui; void main() { ui.window.setIsolateDebugName("debug isolate"); // ... } 选项运行flutter attach

--isolate-filter

$ flutter attach --isolate-filter='debug'

我发现您可能感兴趣的另一个功能是Obfuscating dart code