在 Flutter 项目上创建 Android 个库
Create Android Library on Flutter project
我正在尝试在 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
。我还指定了 flutter 源位置。每次我 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.
我终于在一个 flutter wiki 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
这会在 .android/Flutter/build/outputs/aar/.
中生成 flutter-debug.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视图中显示哪个widget。 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),
);
}
}
这完全取决于您想要哪个路由字符串以及如何解释它们。
构建并运行安装您的应用程序
您构建和 运行 MyApp
的方式与添加 Flutter 模块依赖项之前完全相同,通常使用 Android Studio。编辑、调试和分析您的 Android 代码也是如此。
热 restart/reload 和调试 Dart 代码
支持使用混合应用程序的 Flutter/Dart 代码的完整 IDE 集成正在进行中。但基础知识已经通过 Flutter 命令行工具和 Dart Observatory 网络用户界面呈现。
连接设备或启动模拟器。然后让 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 使用 [=315= 生成新的 "Single View App" 项目得到的结构].如果您现有的应用程序具有不同的文件夹结构 and/or 现有 .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 以刷新 podhelper.rb
读取的插件列表脚本。然后 运行 从 some/path/MyApp
.
重新安装 pod
podhelper.rb 脚本将确保您的插件和 Flutter.framework 被添加到您的项目中,并确保对所有目标禁用位码。
添加用于构建 Dart 代码的构建阶段
Select 项目导航器中的顶级 MyApp
项目。 Select TARGET MyApp
在主视图的左侧,然后是 select Build Phases
选项卡。通过单击主视图左上角的 +
添加一个新的构建阶段。 Select New Run Script
相。展开新的 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
(引擎库)正在嵌入到您的
应用程序给你。这必须与发布类型相匹配
(debug/profile/release) 以及您应用的架构
(arm*、i386、x86_64 等)。 Cocoapods 将其作为一个 vendored 引入
框架并确保它嵌入到您的本机应用程序中。
App.framework
(您的 Flutter 应用程序二进制文件)嵌入到
你的应用程序。
flutter_assets
文件夹正在作为资源嵌入 - 它
包含字体、图像,在某些构建模式下它还包含
引擎在 运行 时需要的二进制文件。 这个有问题
文件夹可能会导致 运行 时间错误,例如“Could not 运行 engine for
配置”- 通常表示该文件夹不是
嵌入,或者您正尝试跨 JIT 应用程序
启用 AOT 的引擎,反之亦然!
- 任何插件都被添加为 Cocoapods。理论上应该是
也可以手动合并它们,但这变得很多
更具体到插件本身。
- 您项目中的每个目标都禁用了位码。这是一个
link 使用 Flutter Engine 的要求。
- Generated.xcconfig(含Flutter专用环境
变量)包含在发布和调试 .xcconfig 文件中
Cocoapods 生成。
构建阶段脚本 (xcode_backend.sh) 确保您构建的二进制文件与实际位于文件夹中的 Dart 代码保持同步。一旦 this pull request 登陆(已经登陆!),它还会尝试尊重您的构建配置设置。
编写代码以在您的主机应用程序中使用 FlutterViewController
执行此操作的正确位置将特定于您的主机应用程序。这是一个对 Xcode 10.0.
生成的主机应用程序的空白屏幕有意义的示例
首先将您的应用委托声明为 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 视图,其中包含标准的 Flutter Demo 计数应用程序。您可以使用路由在应用程序的不同位置显示不同的小部件,如上文 Android 部分所述。要设置路由,请调用
Objective-C:
[flutterViewController setInitialRoute:@"route1"];
Swift:
flutterViewController.setInitialRoute("route1")
在 FlutterViewController
构建之后立即(在呈现之前)。
您可以通过在 Dart 代码中调用 SystemNavigator.pop()
让 Flutter 应用自行关闭。
构建并运行安装您的应用程序
您使用 Xcode 构建和 运行 MyApp 的方式与添加 Flutter 模块依赖项之前的方式完全相同。编辑、调试和分析您的 iOS 代码也是如此。
热 restart/reload 和调试 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
默认连接到所有可用的 isolate。然后,从附加的 CLI 发送的任何命令都会转发到每个附加的隔离区。
通过从附加的 flutter
CLI 工具中键入 l
列出所有附加的分离株。如果未指定,隔离名称将自动从 dart 入口点文件和函数名称生成。
同时显示两个 Flutter 隔离的应用程序的 l
输出示例:
Connected views:
main.dart$main-517591213 (isolates/517591213)
main.dart$main-332962855 (isolates/332962855)
- 分两步附加到特定的分离株:
在其 Dart 源中命名感兴趣的 Flutter root isolate。
// 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
我正在尝试在 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
。我还指定了 flutter 源位置。每次我 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.
我终于在一个 flutter wiki 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
这会在 .android/Flutter/build/outputs/aar/.
flutter-debug.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视图中显示哪个widget。 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),
);
}
}
这完全取决于您想要哪个路由字符串以及如何解释它们。
构建并运行安装您的应用程序
您构建和 运行 MyApp
的方式与添加 Flutter 模块依赖项之前完全相同,通常使用 Android Studio。编辑、调试和分析您的 Android 代码也是如此。
热 restart/reload 和调试 Dart 代码
支持使用混合应用程序的 Flutter/Dart 代码的完整 IDE 集成正在进行中。但基础知识已经通过 Flutter 命令行工具和 Dart Observatory 网络用户界面呈现。
连接设备或启动模拟器。然后让 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 使用 [=315= 生成新的 "Single View App" 项目得到的结构].如果您现有的应用程序具有不同的文件夹结构 and/or 现有 .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 以刷新 podhelper.rb
读取的插件列表脚本。然后 运行 从 some/path/MyApp
.
podhelper.rb 脚本将确保您的插件和 Flutter.framework 被添加到您的项目中,并确保对所有目标禁用位码。
添加用于构建 Dart 代码的构建阶段
Select 项目导航器中的顶级 MyApp
项目。 Select TARGET MyApp
在主视图的左侧,然后是 select Build Phases
选项卡。通过单击主视图左上角的 +
添加一个新的构建阶段。 Select New Run Script
相。展开新的 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
(引擎库)正在嵌入到您的 应用程序给你。这必须与发布类型相匹配 (debug/profile/release) 以及您应用的架构 (arm*、i386、x86_64 等)。 Cocoapods 将其作为一个 vendored 引入 框架并确保它嵌入到您的本机应用程序中。App.framework
(您的 Flutter 应用程序二进制文件)嵌入到 你的应用程序。flutter_assets
文件夹正在作为资源嵌入 - 它 包含字体、图像,在某些构建模式下它还包含 引擎在 运行 时需要的二进制文件。 这个有问题 文件夹可能会导致 运行 时间错误,例如“Could not 运行 engine for 配置”- 通常表示该文件夹不是 嵌入,或者您正尝试跨 JIT 应用程序 启用 AOT 的引擎,反之亦然!- 任何插件都被添加为 Cocoapods。理论上应该是 也可以手动合并它们,但这变得很多 更具体到插件本身。
- 您项目中的每个目标都禁用了位码。这是一个 link 使用 Flutter Engine 的要求。
- Generated.xcconfig(含Flutter专用环境 变量)包含在发布和调试 .xcconfig 文件中 Cocoapods 生成。
构建阶段脚本 (xcode_backend.sh) 确保您构建的二进制文件与实际位于文件夹中的 Dart 代码保持同步。一旦 this pull request 登陆(已经登陆!),它还会尝试尊重您的构建配置设置。
编写代码以在您的主机应用程序中使用 FlutterViewController 执行此操作的正确位置将特定于您的主机应用程序。这是一个对 Xcode 10.0.
生成的主机应用程序的空白屏幕有意义的示例首先将您的应用委托声明为 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 视图,其中包含标准的 Flutter Demo 计数应用程序。您可以使用路由在应用程序的不同位置显示不同的小部件,如上文 Android 部分所述。要设置路由,请调用
Objective-C:
[flutterViewController setInitialRoute:@"route1"];
Swift:
flutterViewController.setInitialRoute("route1")
在 FlutterViewController
构建之后立即(在呈现之前)。
您可以通过在 Dart 代码中调用 SystemNavigator.pop()
让 Flutter 应用自行关闭。
构建并运行安装您的应用程序
您使用 Xcode 构建和 运行 MyApp 的方式与添加 Flutter 模块依赖项之前的方式完全相同。编辑、调试和分析您的 iOS 代码也是如此。
热 restart/reload 和调试 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
默认连接到所有可用的 isolate。然后,从附加的 CLI 发送的任何命令都会转发到每个附加的隔离区。
通过从附加的 flutter
CLI 工具中键入 l
列出所有附加的分离株。如果未指定,隔离名称将自动从 dart 入口点文件和函数名称生成。
同时显示两个 Flutter 隔离的应用程序的 l
输出示例:
Connected views:
main.dart$main-517591213 (isolates/517591213)
main.dart$main-332962855 (isolates/332962855)
- 分两步附加到特定的分离株:
在其 Dart 源中命名感兴趣的 Flutter root isolate。
// 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