我试图在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.
我终于在一个颤抖的wiki page上偶然发现了这个功能。这篇文章中需要注意的一件非常重要的事情;
“add2app”支持处于预览状态,目前仅在主通道上可用。
虽然这个功能仍然是实验性的,但它运行正常(很少有障碍,就像从Android主机调用时启动颤动应用程序所需的时间)。
使用flutter create xxx
创建的Flutter项目包括用于Flutter / Dart代码的非常简单的主机应用程序(单活动Android主机和单一ViewController iOS主机)。您可以修改这些主机应用程序以满足您的需求并从那里构建。
但是,如果您开始使用任一平台的现有主机应用程序,您可能希望将Flutter项目作为某种形式的库包含在该应用程序中。
这就是Flutter模块模板提供的功能。执行flutter create -t module xxx
会生成一个Flutter项目,其中包含一个Android库和一个Cocoapods pod,专为现有主机应用程序使用而设计。
Android的
创建一个Flutter模块让我们假设您在某些/ 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
本身(如:package_info
)和模块使用的任何Flutter插件(如: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
)来确定要创建哪个窗口小部件并传递给runApp
。示意性地,
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),
);
}
}
完全取决于您想要的路径字符串以及如何解释它们。
构建和运行您的应用程序
您以与添加Flutter模块依赖项之前完全相同的方式构建和运行MyApp
,通常使用Android Studio。编辑,调试和分析Android代码也是如此。
热重启/重新加载和调试Dart代码
完整的IDE集成以支持使用混合应用程序的Flutter / Dart代码正在进行中。但是已经通过Flutter命令行工具和Dart Observatory Web用户界面提供了基础知识。
连接设备或启动模拟器。然后使Flutter CLI工具监听您的应用程序:
$ cd some/path/my_flutter
$ flutter attach
在Nexus 5X上等待来自Flutter的连接...在Android Studio的调试模式下启动MyApp
(或者你通常采用哪种方式)。导航到使用Flutter的应用程序区域。然后转回终端,您应该看到类似于以下内容的输出:
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".
您现在可以在my_flutter
中编辑Dart代码,并且可以通过在终端中按r
来热重新加载更改。您还可以将上面的URL粘贴到浏览器中,以使用Dart Observatory设置断点,分析内存保留和其他调试任务。
iOS创建一个Flutter模块
假设您在some/path/MyApp
上有一个现有的iOS应用程序,并且您希望将Flutter项目作为兄弟:
$ cd some/path/
$ flutter create -t module my_flutter
这将创建一个some/path/my_flutter/
Flutter模块项目,其中包含一些Dart代码以帮助您入门,以及一个.ios/
隐藏的子文件夹,它包含了包含一些Cocoapods和一个帮助程序Ruby脚本的模块项目。
使主机应用程序依赖于Flutter模块
下面的描述假设您现有的iOS应用程序的结构类似于您通过要求Xcode版本10.0使用Objective-C生成新的“单一视图应用程序”项目所获得的结构。如果您现有的应用程序具有不同的文件夹结构和/或现有的.xcconfig
文件,您可以重用这些文件,但可能需要相应地调整下面提到的一些相对路径。
假定的文件夹结构如下:
some/path/
my_flutter/
lib/main.dart
.ios/
MyApp/
MyApp/
AppDelegate.h
AppDelegate.m (or swift)
:
将Flutter应用程序添加到Podfile
集成Flutter框架需要使用CocoaPods依赖项管理器。这是因为Flutter框架也需要可用于您可能包含在my_flutter中的任何Flutter插件。
如果需要,请参考cocoapods.org了解如何在开发机器上安装CocoaPods。
如果您的主机应用程序(MyApp
)已经在使用Cocoapods,您只需执行以下操作即可与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
。
每当你在some/path/my_flutter/pubspec.yaml
中更改Flutter插件依赖项时,你需要从some/path/my_flutter
运行flutter packages get来刷新podhelper.rb
脚本读取的插件列表。然后从some/path/MyApp
再次运行pod install。
podhelper.rb脚本将确保您的插件和Flutter.framework添加到您的项目中,并确保为所有目标禁用bitcode。
添加构建阶段以构建Dart代码
在项目导航器中选择顶级MyApp
项目。在主视图的左侧选择TARGET MyApp
,然后选择Build Phases
选项卡。通过单击主视图左上角的+
添加新的构建阶段。选择New Run Script
Phase。展开新的Run Script
,只是附加到阶段列表中。
将以下内容粘贴到Shell字段正下方的文本区域中:
"$FLUTTER_ROOT/packages/flutter_tools/bin/xcode_backend.sh" build
"$FLUTTER_ROOT/packages/flutter_tools/bin/xcode_backend.sh" embed
最后,将新构建阶段拖到目标依赖关系阶段之后。
您现在应该能够使用⌘B
构建项目。
引擎盖下如果您有某些理由要手动执行此操作或调试这些步骤无法正常工作的原因,请参阅以下内容:
Flutter.framework
(引擎库)已经嵌入到您的应用程序中。这必须与发布类型(调试/配置文件/发布)以及应用程序的架构(arm *,i386,x86_64等)相匹配。 Cocoapods将其作为一个销售框架,并确保它嵌入到您的原生应用程序中。App.framework
(您的Flutter应用程序二进制文件)已嵌入到您的应用程序中。flutter_assets
文件夹作为资源嵌入 - 它包含字体,图像,并且在某些构建模式下,它还包含引擎在运行时所需的二进制文件。此文件夹的问题可能导致运行时错误,例如“无法运行引擎进行配置” - 通常表示文件夹未嵌入,或者您尝试使用启用AOT的引擎交叉JIT应用程序,反之亦然!构建阶段脚本(xcode_backend.sh)确保您构建的二进制文件与实际位于文件夹中的Dart代码保持同步。一旦this pull request登陆(它已经做了!),它也会尝试尊重你的构建配置设置。
编写代码以使用主机应用程序中的FlutterViewController执行此操作的正确位置将特定于您的主机应用程序。这是一个对Xcode 10.0生成的主机应用程序的空白屏幕有意义的示例。
首先声明你的app委托是FlutterAppDelegate
的子类。
在AppDelegate.h
:
#import <UIKit/UIKit.h>
#import <Flutter/Flutter.h>
@interface AppDelegate : FlutterAppDelegate
@end
这允许AppDelegate.m
非常简单,除非你的主机应用程序需要覆盖其他方法:
#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
如果您使用Swift编写,则可以在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);
}
}
.......
#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
或者,使用Swift:
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)
}
}
您现在应该能够在模拟器或设备上构建和启动MyApp。按下按钮会显示带有标准Flutter Demo计数应用程序的全屏颤振视图。您可以使用路线在应用中的不同位置显示不同的小部件,如上面的Android部分所述。要设置路线,请致电
Objective-C的:
[flutterViewController setInitialRoute:@"route1"];
迅速:
flutterViewController.setInitialRoute("route1")
在建造FlutterViewController
之后(并且在展示之前)。
您可以通过在Dart代码中调用SystemNavigator.pop()
来让Flutter应用程序解散。
构建和运行您的应用程序
使用Xcode构建和运行MyApp的方式与添加Flutter模块依赖项之前完全相同。编辑,调试和分析iOS代码也是如此。
热重启/重新加载和调试Dart代码
连接设备或启动模拟器。然后使Flutter CLI工具监听您的应用程序:
$ cd some/path/my_flutter
$ flutter attach
Waiting for a connection from Flutter on iPhone X...
从Xcode以调试模式启动MyApp
。导航到使用Flutter的应用程序区域。然后转回终端,您应该看到类似于以下内容的输出:
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".
您现在可以在my_flutter
中编辑Dart代码,并且可以通过在终端中按r
来热重新加载更改。您还可以将上面的URL粘贴到浏览器中,以使用Dart Observatory设置断点,分析内存保留和其他调试任务。
调试Flutter的特定实例
可以将多个Flutter实例(root isolates
)添加到应用程序中。 flutter attach
默认连接到所有可用的分离株。然后,从连接的CLI发送的任何命令都会转发到每个连接的隔离区。
通过从附加的l
CLI工具中键入flutter
列出所有附加的分离物。如果未指定,则会从dart入口点文件和函数名称自动生成隔离名称。
示例l
输出,用于同时显示两个Flutter隔离的应用程序:
Connected views:
main.dart$main-517591213 (isolates/517591213)
main.dart$main-332962855 (isolates/332962855)
在其Dart源中命名感兴趣的Flutter根分离物。
// main.dart
import 'dart:ui' as ui;
void main() {
ui.window.setIsolateDebugName("debug isolate");
// ...
}
flutter attach
选项运行--isolate-filter
。$ flutter attach --isolate-filter='debug'
Waiting for a connection from Flutter...
Done.
Syncing files to device... 1.1s
🔥 To hot reload changes while running, press "r". To hot restart (and rebuild state), press "R".
An Observatory debugger and profiler is available at: http://127.0.0.1:43343/
For a more detailed help message, press "h". To detach, press "d"; to quit, press "q".
Connected view:
debug isolate (isolates/642101161)
另一个仍在实验中的功能我发现你可能感兴趣的是Obfuscating dart code