在对 flutter module 进行修改的时候,不知道为什么,会发现 .android 目录下和 .ios 目录下的文件会被修改覆盖掉;后来发现,只要我们变动 pubspec.yaml
的文件, 然后执行命令 flutter packages get
,就会重新从flutter 模板中替换 .android
和.ios
目录; 因为我们的 .android
目录下有自己定义 gradle 脚本(主要为了解决打包aar的问题);就不希望这个 gradle 被覆盖;
解决方法
- 在 flutter-sdk 中修改模板,把自己写好的 gradle 放到模板中;
- 找到执行
flutter packages get
背后的逻辑,通过修改逻辑代码,不去覆盖现有的代码;
替换模板的目录
所有的模板目录都在Flutter_HOME/packages/flutter_tools/templates
下面:

以上红色箭头的地方,是我替换和添加的模板代码;ios 应该也可以找到相应的模板进行添加和修改;
查看 flutter 的 shell 脚本命令
1. flutter 命令的开始
因为是 flutter 命令所以我们可以去看下 flutter 命令的源码;用文本编辑器打开编辑 FLUTTER_HOME/bin/flutter 文件;最后一行;
"$DART" --packages="$FLUTTER_TOOLS_DIR/.packages" $FLUTTER_TOOL_ARGS "$SNAPSHOT_PATH" "$@"
为了方便观察打印(echo)一下这个命令:
echo "$DART" --packages="$FLUTTER_TOOLS_DIR/.packages" $FLUTTER_TOOL_ARGS "$SNAPSHOT_PATH" "$@"
执行命令 flutter --no-color packages get
得到如下:
/Users/xxxx/flutter/bin/cache/dart-sdk/bin/dart --packages=/Users/xxxx/flutter/packages/flutter_tools/.packages /Users/xxxx/flutter/bin/cache/flutter_tools.snapshot --no-color packages get
很明显他是根据 flutter_tools.snapshot
这个文件进行执行的;参数是 --no-color packages get
; 那么 flutter_tools.snapshot
这个二进制文件是怎么生成的呢?
2. flutter_tools.snapshot 文件的生成
可以继续查看一下 bin/flutter
这个文件;
"$DART" $FLUTTER_TOOL_ARGS --snapshot="$SNAPSHOT_PATH" --packages="$FLUTTER_TOOLS_DIR/.packages" "$SCRIPT_PATH"
这个命令主要是用来编译flutter_tools.snapshot
的,我们可以打印(echo)一下这个命令; 修改一下 shell 脚本,让它进入 if 语句(怎么修改可以看下第3
段)里面;打印出来的结果如下;
/Users/xxxx/flutter/bin/cache/dart-sdk/bin/dart --snapshot=/Users/xxxx/flutter/bin/cache/flutter_tools.snapshot --packages=/Users/xxx/flutter/packages/flutter_tools/.packages /Users/xxxx/flutter/packages/flutter_tools/bin/flutter_tools.dart
从上面的命令我们可以看出来所有的源码主要了来自 /Users/xxxx/flutter/packages/flutter_tools
这里目录里面,进去看一下,都是dart 源码;
3. 稍微看下需要编译 flutter_tools.snapshot 的条件是什么
if [[
! -f "$SNAPSHOT_PATH"
|| ! -s "$STAMP_PATH"
|| "$(cat "$STAMP_PATH")" != "$revision"
|| "$FLUTTER_TOOLS_DIR/pubspec.yaml" -nt "$FLUTTER_TOOLS_DIR/pubspec.lock"
]]; then
echo Building flutter tool...
fi
从上面来看,只要满足这里4个条件其中一个,就会去编译 flutter_tools 生成一个 flutter_tools.snapshot
;
- flutter_tools.snapshot 文件不存在
- STAMP_PATH 这个文件的 size 为0, 主要用来缓存一个 git commit
- 如果被缓存的 git commit 和 revision 不相同也会触发,revision =
git rev-parse HEAD
- 如果
pubspec.yaml
文件最后修改时间大于pubspec.lock
文件 (nt: new then)
经过以上3 点,接下来,我们可以去看下 flutter_tools 的源码了;
查看 flutter_tools 的源码
我们把源码导入 Android studio 查看更加方便;main 方法从 flutter_tools/bin/flutter_tools.dart
文件开始;这个类非常简单:
import 'package:flutter_tools/executable.dart' as executable;
void main(List<String> args) {
executable.main(args);
}
所以,所有的开始应该在 executeable.dart 里面;里面主要是把所有的命令都封装成一对象,然后放到一个数组里面注册,因为我们主要是观察 flutter packages get
这个命令,所以我们去看下 PackagesCommand

从类构造来看,他含有子命令; PackagesGetCommand
; 先不管;因为所有的 FlutterCommand 都会执行 Future<FlutterCommandResult> runCommand()
这个方法;所以我们来看下这个的逻辑;
@override
Future<FlutterCommandResult> runCommand() async {
// .........省略没必要的
await _runPubGet(target);
final FlutterProject rootProject = FlutterProject.fromPath(target);
// 下面这行代码主要是用来刷新 .android 和 .ios的目录
await rootProject.ensureReadyForPlatformSpecificTooling(checkProjects: true);
// Get/upgrade packages in example app as well
if (rootProject.hasExampleApp) {
final FlutterProject exampleProject = rootProject.example;
await _runPubGet(exampleProject.directory.path);
await exampleProject.ensureReadyForPlatformSpecificTooling(checkProjects: true);
}
// 省略没必要的
}
}
主要刷新逻辑在 ensureReadyForPlatformSpecificTooling
/// Generates project files necessary to make Gradle builds work on Android
/// and CocoaPods+Xcode work on iOS, for app and module projects only.
Future<void> ensureReadyForPlatformSpecificTooling({bool checkProjects = false}) async {
if (!directory.existsSync() || hasExampleApp) {
return;
}
refreshPluginsList(this); // 这里更新 .flutter-plugin 文件
if ((android.existsSync() && checkProjects) || !checkProjects) {
await android.ensureReadyForPlatformSpecificTooling();// 这里更新
}
if ((ios.existsSync() && checkProjects) || !checkProjects) {
await ios.ensureReadyForPlatformSpecificTooling();// 这里更新
}
await injectPlugins(this, checkProjects: checkProjects);// 把一些channel 注册到对应的平台
}
- refreshPluginsList(this): 刷新
.flutter-plugins
文件 - android.ensureReadyForPlatformSpecificTooling(): 刷新 .android 文件
- await ios.ensureReadyForPlatformSpecificTooling(): 刷新 .ios 文件
- injectPlugins(this, checkProjects: checkProjects): 把一些channel 注册到对应的平台, GeneratedPluginRegistrant 里面相关的代码生成;
从上面的代码可以看出, 要想 flutter package get
不去刷新,重新创建模板,只要把对应 ensureReadyForPlatformSpecificTooling() 代码不执行就好了;然后修改源码,重新编译;编译的方法在 flutter 脚本中的 4 种方式里面;