Flutter组件化方案尝试
1.安装Flutter
由于在国内访问Flutter有时可能会受到限制,Flutter官方为中国开发者搭建了临时镜像,大家可以将如下环境变量加入到用户环境变量中:
export PUB_HOSTED_URL=https://pub.flutter-io.cn export FLUTTER_STORAGE_BASE_URL=https://storage.flutter-io.cn
vim .bash_profile
sorce .bash_profile
解压安装包到你想安装的目录
cd ~/development unzip ~/Downloads/flutter_macos_v0.5.1-beta.zip
添加flutter相关工具到path中:
export PATH=/Users/fumeng/project/Flutter/flutter/bin:$PATH
运行 flutter doctor
flutter版本升级 flutter upgrade
二:在组件化中集成Flutter
在你的既有应用中集成 Flutter module,这里有两种方式可以将 Flutter 集成到你的既有应用中。
1.使用 CocoaPods 依赖管理和已安装的 Flutter SDK 。(推荐)
2.把 Flutter engine 、你的 dart 代码和所有 Flutter plugin 编译成 framework 。用 Xcode 手动集成到你的应用中,并更新编译设置。
https://flutter.cn/docs/development/add-to-app/ios/project-setup
1.创建FlutterModel
flutter create --template module my_flutter
flutter build ios
FlutterModel 可依赖到项目中的文件
engine: Flutter.framework
plugin: GeneratedPluginRegistrant
dependency: shared_preferences
在 `Podfile` 中添加下面代码:
flutter_application_path = '../my_flutter' load File.join(flutter_application_path, '.ios', 'Flutter', 'podhelper.rb')
每个需要集成 Flutter 的 [Podfile target][],执行 `install_all_flutter_pods(flutter_application_path)`
target 'MyApp' do install_all_flutter_pods(flutter_application_path) end
pod install
当你在 my_flutter/pubspec.yaml 改变了 Flutter plugin 依赖,需要在 Flutter module 目录运行 flutter pub get,来更新会被podhelper.rb 脚本用到的 plugin 列表,然后再次在你的应用目录 some/path/MyApp 运行 pod install.
podhelper.rb 脚本会把你的 plugins, Flutter.framework,和 App.framework 集成到你的项目中。
Framework
Framework 可以通俗的理解为封装了共享资源的具有层次结构的文件夹。共享资源可以是 nib文件、国际化字符串文件、头文件、库文件等等。它同时也是个 Bundle,里面的内容可以通过 Bundle 相关 API 来访问。Framework 可以是 static framework 或 dynamic framework。<font color=red> 在 iOS App 打包完成后,如果 Framework 包含了模拟器指令集(x86_64 或 i386),那么用 Xcode 发布 App 的时候,会报 unsupported architectures 的错误,所以需要我们手动或脚本去移除。</font>
XCFramework
XCFramework 是由 Xcode 创建的一个可分发的二进制包,它包含了 framework 或 library 的一个或多个变体,因此可以在多个平台(iOS、macOS、tvOS、watchOS) 上使用,包括模拟器。XCFramework 可以是静态的,也可以是动态的。xcframework 的好处就是用 Xcode 发布的时候,Xcode 会自动选用正确的指令集 Frameworks,省去了手动移除动态库中的模拟器指令集的工作。<font color=red>不过值得注意的是,Xcode 11 才引入 XCFramework 。</font>
解析 podhelper.rb源码
# Copyright 2014 The Flutter Authors. All rights reserved.
# Use of this source code is governed by a BSD-style license that can be
# found in the LICENSE file.
require 'json'
# Install pods needed to embed Flutter application, Flutter engine, and plugins
# from the host application Podfile.
#
# @example
# target 'MyApp' do
# install_all_flutter_pods 'my_flutter'
# end
# @param [String] flutter_application_path Path of the root directory of the Flutter module.
# Optional, defaults to two levels up from the directory of this script.
# MyApp/my_flutter/.ios/Flutter/../..
def install_all_flutter_pods(flutter_application_path = nil)
flutter_application_path ||= File.join('..', '..')
install_flutter_engine_pod
install_flutter_plugin_pods(flutter_application_path)
install_flutter_application_pod(flutter_application_path)
end
# Install Flutter engine pod.
#
# @example
# target 'MyApp' do
# install_flutter_engine_pod
# end
def install_flutter_engine_pod
current_directory = File.expand_path('..', __FILE__) // 脚本文件当前路径
engine_dir = File.expand_path('engine', current_directory)
framework_name = 'Flutter.xcframework'
copied_engine = File.expand_path(framework_name, engine_dir)
if !File.exist?(copied_engine)
# Copy the debug engine to have something to link against if the xcode backend script has not run yet.
# CocoaPods will not embed the framework on pod install (before any build phases can generate) if the dylib does not exist.
release_framework_dir = File.join(flutter_root, 'bin', 'cache', 'artifacts', 'engine', 'ios-release')
unless Dir.exist?(release_framework_dir)
# iOS artifacts have not been downloaded.
raise "#{release_framework_dir} must exist. Make sure \"flutter precache --ios\" has been run at least once"
end
FileUtils.cp_r(File.join(release_framework_dir, framework_name), engine_dir)
end
# Keep pod path relative so it can be checked into Podfile.lock.
# Process will be run from project directory.
engine_pathname = Pathname.new engine_dir
# defined_in_file is set by CocoaPods and is a Pathname to the Podfile.
project_directory_pathname = defined_in_file.dirname
relative = engine_pathname.relative_path_from project_directory_pathname
pod 'Flutter', :path => relative.to_s, :inhibit_warnings => true
end
# Install Flutter plugin pods.
#
# @example
# target 'MyApp' do
# install_flutter_plugin_pods 'my_flutter'
# end
# @param [String] flutter_application_path Path of the root directory of the Flutter module.
# Optional, defaults to two levels up from the directory of this script.
# MyApp/my_flutter/.ios/Flutter/../..
def install_flutter_plugin_pods(flutter_application_path)
flutter_application_path ||= File.join('..', '..')
# Keep pod path relative so it can be checked into Podfile.lock.
# Process will be run from project directory.
ios_project_directory_pathname = Pathname.new File.expand_path(File.join('..', '..'), __FILE__)
# defined_in_file is set by CocoaPods and is a Pathname to the Podfile.
project_directory_pathname = defined_in_file.dirname
relative = ios_project_directory_pathname.relative_path_from project_directory_pathname
pod 'FlutterPluginRegistrant', :path => File.join(relative, 'Flutter', 'FlutterPluginRegistrant'), :inhibit_warnings => true
symlinks_dir = File.join(relative, '.symlinks', 'plugins')
FileUtils.mkdir_p(symlinks_dir)
plugins_file = File.expand_path('.flutter-plugins-dependencies', flutter_application_path)
plugin_pods = flutter_parse_dependencies_file_for_ios_plugin(plugins_file)
plugin_pods.each do |plugin_hash|
plugin_name = plugin_hash['name']
plugin_path = plugin_hash['path']
if (plugin_name && plugin_path)
symlink = File.join(symlinks_dir, plugin_name)
FileUtils.rm_f(symlink)
File.symlink(plugin_path, symlink)
pod plugin_name, :path => File.join(symlink, 'ios'), :inhibit_warnings => true
end
end
end
# Install Flutter application pod.
#
# @example
# target 'MyApp' do
# install_flutter_application_pod '../flutter_settings_repository'
# end
# @param [String] flutter_application_path Path of the root directory of the Flutter module.
# Optional, defaults to two levels up from the directory of this script.
# MyApp/my_flutter/.ios/Flutter/../..
def install_flutter_application_pod(flutter_application_path)
current_directory_pathname = Pathname.new File.expand_path('..', __FILE__)
app_framework_dir = File.expand_path('App.framework', current_directory_pathname.to_path)
app_framework_dylib = File.join(app_framework_dir, 'App')
if !File.exist?(app_framework_dylib)
# Fake an App.framework to have something to link against if the xcode backend script has not run yet.
# CocoaPods will not embed the framework on pod install (before any build phases can run) if the dylib does not exist.
# Create a dummy dylib.
FileUtils.mkdir_p(app_framework_dir)
`echo "static const int Moo = 88;" | xcrun clang -x c -dynamiclib -o "#{app_framework_dylib}" -`
end
# Keep pod and script phase paths relative so they can be checked into source control.
# Process will be run from project directory.
# defined_in_file is set by CocoaPods and is a Pathname to the Podfile.
project_directory_pathname = defined_in_file.dirname
relative = current_directory_pathname.relative_path_from project_directory_pathname
pod 'long1_flutter_module', :path => relative.to_s, :inhibit_warnings => true
flutter_export_environment_path = File.join('${SRCROOT}', relative, 'flutter_export_environment.sh');
script_phase :name => 'Run Flutter Build long1_flutter_module Script',
:script => "set -e\nset -u\nsource \"#{flutter_export_environment_path}\"\nexport VERBOSE_SCRIPT_LOGGING=1 && \"$FLUTTER_ROOT\"/packages/flutter_tools/bin/xcode_backend.sh build",
:execution_position => :before_compile
end
# .flutter-plugins-dependencies format documented at
# https://flutter.dev/go/plugins-list-migration
def flutter_parse_dependencies_file_for_ios_plugin(file)
file_path = File.expand_path(file)
return [] unless File.exists? file_path
dependencies_file = File.read(file)
dependencies_hash = JSON.parse(dependencies_file)
# dependencies_hash.dig('plugins', 'ios') not available until Ruby 2.3
return [] unless dependencies_hash.has_key?('plugins')
return [] unless dependencies_hash['plugins'].has_key?('ios')
dependencies_hash['plugins']['ios'] || []
end
def flutter_root
generated_xcode_build_settings_path = File.expand_path(File.join('..', '..', 'Flutter', 'Generated.xcconfig'), __FILE__)
unless File.exist?(generated_xcode_build_settings_path) // unless与if相反 raise是抛出异常
raise "#{generated_xcode_build_settings_path} must exist. If you're running pod install manually, make sure flutter pub get is executed first"
end
File.foreach(generated_xcode_build_settings_path) do |line|
matches = line.match(/FLUTTER_ROOT\=(.*)/) // 只匹配第一次,返回为MatchData类型。https://blog.csdn.net/cz9025/article/details/90202839
return matches[1].strip if matches // strip 返回 str 的副本,移除了前导的空格和尾随的空格。
end
# This should never happen...
raise "FLUTTER_ROOT not found in #{generated_xcode_build_settings_path}. Try deleting Generated.xcconfig, then run flutter pub get"
end
Generated.xcconfig
// This is a generated file; do not edit or check into version control.
FLUTTER_ROOT=/Users/lilonglong/lllProject/newFlutter/flutter
FLUTTER_APPLICATION_PATH=/Users/lilonglong/Desktop/course/Flutter/helloword/ios_flutter_module
FLUTTER_TARGET=lib/main.dart
FLUTTER_BUILD_DIR=build
SYMROOT=${SOURCE_ROOT}/../build/ios
OTHER_LDFLAGS=$(inherited) -framework Flutter
FLUTTER_BUILD_NAME=1.0.0
FLUTTER_BUILD_NUMBER=1
DART_OBFUSCATION=false
TRACK_WIDGET_CREATION=false
TREE_SHAKE_ICONS=false
PACKAGE_CONFIG=.packages
.flutter-plugins-dependencies
{
"info":"This is a generated file; do not edit or check into version control.",
"plugins":{
"ios":[
{
"name":"shared_preferences",
"path":"/Users/lilonglong/lllProject/newFlutter/flutter/.pub-cache/hosted/pub.flutter-io.cn/shared_preferences-0.5.10/",
"dependencies":[
]
}
],
"android":[
{
"name":"shared_preferences",
"path":"/Users/lilonglong/lllProject/newFlutter/flutter/.pub-cache/hosted/pub.flutter-io.cn/shared_preferences-0.5.10/",
"dependencies":[
]
}
],
"macos":[
{
"name":"shared_preferences_macos",
"path":"/Users/lilonglong/lllProject/newFlutter/flutter/.pub-cache/hosted/pub.flutter-io.cn/shared_preferences_macos-0.0.1+10/",
"dependencies":[
]
}
],
"linux":[
{
"name":"path_provider_linux",
"path":"/Users/lilonglong/lllProject/newFlutter/flutter/.pub-cache/hosted/pub.flutter-io.cn/path_provider_linux-0.0.1+2/",
"dependencies":[
]
},
{
"name":"shared_preferences_linux",
"path":"/Users/lilonglong/lllProject/newFlutter/flutter/.pub-cache/hosted/pub.flutter-io.cn/shared_preferences_linux-0.0.2+2/",
"dependencies":[
"path_provider_linux"
]
}
],
"windows":[
],
"web":[
{
"name":"shared_preferences_web",
"path":"/Users/lilonglong/lllProject/newFlutter/flutter/.pub-cache/hosted/pub.flutter-io.cn/shared_preferences_web-0.1.2+7/",
"dependencies":[
]
}
]
},
"dependencyGraph":[
{
"name":"path_provider_linux",
"dependencies":[
]
},
{
"name":"shared_preferences",
"dependencies":[
"shared_preferences_linux",
"shared_preferences_macos",
"shared_preferences_web"
]
},
{
"name":"shared_preferences_linux",
"dependencies":[
"path_provider_linux"
]
},
{
"name":"shared_preferences_macos",
"dependencies":[
]
},
{
"name":"shared_preferences_web",
"dependencies":[
]
}
],
"date_created":"2020-09-10 18:59:22.806998",
"version":"1.20.2"
}
编译报错
Unexpected failure
解决办法
Pods-SPRViewModel_Example-resources.sh
删除
--app-icon "${ASSETCATALOG_COMPILER_APPICON_NAME}"
FlutterSDK 结构
1.Flutter.framwwork
提供 FlutterEngine
2.Plugin
链接FlutterEngine
3.APP.framework
包含用户编写的所有Dart应用程序代码的AOT快照以及 armv7 和 arm64 格式的Flutter框架和插件的Dart代码
-存放durt的编译产物 flutter页面
添加一个Flutter页面
为了在原生 iOS 应用中展示 Flutter 页面,需要使用到FlutterEngine 和 FlutterViewController。其中,FlutterEngine 充当 Dart VM 和 Flutter 运行时环境; FlutterViewController 依附于 FlutterEngine,给 Flutter 传递 UIKit 的输入事件,并展示被 FlutterEngine 渲染的每一帧画面
Flutter 调试热重载
在fluttermodule 中
flutter attach or flutter attach -d deviceId
flutter: Observatory listening on http://127.0.0.1:60455/
flutter attach --debug-port=60455
在vscode中
Select the correct device using the status bar in VS Code, then run the Flutter: Attach to Flutter on Device command from the command palette.
dart文件更新后,需要
flutter build ios --debug
flutter build ios --release
flutter build iOS (默认是release)
来更新app.framework
Flutter组件化方案尝试
By lilonglong
Flutter组件化方案尝试
- 81