前言
相信大家开始接触 Flutter , 准备给 Flutter 添加一张图片,满心欢喜运行起来,常常会收到劝退通知。
Exception has occurred.
FlutterError (Unable to load asset: assets/images/flutter_candies.png)
总结下❌原因,不外乎下面几点:
- 忘记在
pubspec.yaml
中定义 pubspec.yaml
里面缩进问题/
和\
傻傻分不清楚- 写错字符串
- 在模块中,忘记添加
package
参数
我的建议是新手上来先好好看看 文档 ,再写代码。
This is for you
那么就没有一种安全快速的方式吗?其实 pub 上面已经有很多的 工具 , 其中 低调 的 fgen 是小伙伴们经常用的,评价最高的,也是最接近我需求的。
那么我为什么要自己做呢?
- 我之前已经做过一个 assets 相关的工具,不想半途而废。
- 希望能全自动,加入资源文件之后,就自动生成
pubspec.yaml
里面的定义以及资源文件字符串的 const 。 - const 命名,我不太习惯看全大写,所以我设计三种方式。
lowercase_with_underscores : "assets_images_xxx_jpg"
uppercase_with_underscores : "ASSETS_IMAGES_XXX_JPG"
lowerCamelCase : "assetsImagesXxxJpg"
- 对于在模块里面的资源文件,使用的时候必须指定
package
,所以我也贴心地生成了package
对应的 const 。
class Assets {
Assets._();
static const String package = 'module_a';
static const String assets_xxx_txt = 'assets/xxx.txt';
static const String assets_images_xxx_jpg = 'assets/images/xxx.jpg';
static const String assets_images_test_txt = 'assets/images/test.txt';
}
使用的时候这样
Image.asset(
Assets.assets_images_xxx_jpg,
package: Assets.package,
);
- 学(bai)习(piao) fgen 之后,感觉自己更强大了,就剩下把自己的想法转换成代码了。
总的来说吧,一句话
1024, This is for you.
写在 1024
,希望每个打工人都能更轻松地编程。
使用
环境准备
把 pub bin 的路径放到你的系统路径中。
Platform | Cache location |
---|---|
macOS or Linux | $HOME/.pub-cache/bin |
Windows* | %APPDATA%\Pub\Cache\bin |
激活 assets_generator
执行 pub global activate assets_generator
操作命令
帮助命令
agen -h
生成命令的例子
agen -t d -s -r lwu
全部命令
-h, --[no-]help 显示帮助信息
-p, --path Flutter 项目的根路径
(默认 ".")
-f, --folder assets 文件夹的名字
(默认 "assets")
-w, --[no-]watch 是否继续监听 assets 的变化
(默认 开启)
-t, --type pubsepec.yaml 生成配置的类型
"d" 代表以文件夹方式生成 "- assets/images/"
"f" 代表以文件方式生成 "- assets/images/xxx.jpg"
(默认 "d")
-s, --[no-]save 是否保存命令到本地
如果执行 "agen" 不带任何命令,将优先使用本地的命令进行执行
-o, --out const 类放置的位置
(默认放置在 "lib" 下面)
-r, --rule consts 的名字的命名规范
"lwu"(小写带下划线) : "assets_images_xxx_jpg"
"uwu"(大写带下划线) : "ASSETS_IMAGES_XXX_JPG"
"lcc"(小驼峰) : "assetsImagesXxxJpg"
(默认 "lwu")
-c, --class const 类的名字
(默认 "Assets")
--const-ignore 使用正则表达式忽略一些const(不是全部const都希望生成)
Dart
在单个项目中使用
Image.asset(Assets.assets_images_xxx_jpg);
在模块中使用
Image.asset(
Assets.assets_images_xxx_jpg,
package: Assets.package,
);
课后小结
args
args 真的是一个好库。我之前写法法路由的时候,解析命令全靠自己写。低调告诉我这个库之后效率提高 200%
。
低调 : you know nothing, 法法。
预置命令常用的参数
Option newOption(
//命令全称
String name,
//命令简称,必须只能一个字符长度
String abbr,
//帮助描述
String help,
//参数值的帮助描述
String valueHelp,
//默认值
defaultsTo) {
...
}
常用添加预置命令的方法
///bool 类型的命令
void addFlag(String name,
{String abbr,
String help,
bool defaultsTo = false,
bool negatable = true,
void Function(bool) callback,
bool hide = false})
///String 类型的命令
void addOption(String name,
{String abbr,
String help,
String valueHelp,
Iterable<String> allowed,
Map<String, String> allowedHelp,
String defaultsTo,
Function callback,)
///Iterable<String> 类型的命令, 可以接收多个值
void addMultiOption(String name,
{String abbr,
String help,
String valueHelp,
Iterable<String> defaultsTo,
bool splitCommas = true,
bool hide = false})
如何使用 args
void main(List<String> args) {
final ArgParser parser = ArgParser();
parser.addFlag('help', abbr: 'h', help: 'Help usage', defaultsTo: false);
final ArgResults results = parser.parse(args);
if (results.wasParsed('help')) {
print(parser.usage);
return;
}
yaml
用来解析 pubspec.yaml
,然后重新生成对 assets 的定义。之前法法路由中,我也有用到。
这里要注意一个坑,就是就算它包含 flutter
的 flag,但是 flutter
下面没有任何定义的话 yaml['flutter']
依然为空。
String yamlString = yamlFile.readAsStringSync();
final YamlMap yaml = loadYaml(yamlString) as YamlMap;
if (yaml.containsKey('flutter')) {
final YamlMap flutter = yaml['flutter'] as YamlMap;
if (flutter != null && flutter.containsKey('assets')) {
final YamlList assetsNode = flutter['assets'] as YamlList;
if (assetsNode != null) {
final int start =
assetsNode.nodes.first.span.start.offset - ' - '.length;
final int end = assetsNode.span.end.offset;
yamlString = yamlString.replaceRange(
start,
end,
newAssets,
);
}
//Empty
else {
final int end = yamlString.lastIndexOf('assets:') + 'assets:'.length;
yamlString = yamlString.replaceRange(end, end, '\n$newAssets');
}
} else if (flutter != null) {
final int end = flutter.span.end.offset;
yamlString =
yamlString.replaceRange(end, end, '\n assets:\n$newAssets');
}
//Empty
else {
final int end = yamlString.lastIndexOf('flutter:') + 'flutter:'.length;
yamlString =
yamlString.replaceRange(end, end, '\n assets:\n$newAssets');
}
} else {
final int end = yaml.span.end.offset;
yamlString =
yamlString.replaceRange(end, end, 'flutter:\n assets:\n$newAssets');
}
yamlFile.writeAsStringSync(yamlString);
watcher
这个也是看 低调 的 fgen 学(piao)来的,这也赋予了工具,监听文件夹变化的能力,每当我们新增一个资源文件的时候,就会触发,这样我们就可以重新生成。
我们可以根据不同的事件,给出不同的提示信息。
StreamSubscription<FileSystemEvent> _watch(FileSystemEntity file) {
if (FileSystemEntity.isWatchSupported) {
return file.watch().listen((FileSystemEvent data) {
if (data.isDirectory) {
final Directory directory = Directory(data.path);
//empty directory
if (directory.listSync().isEmpty) {
if (data.type == FileSystemEvent.delete) {
if (watchMap.containsKey(directory)) {
watchMap[watchMap].cancel();
}
dirList.remove(directory);
}
return;
}
_watch(directory);
dirList.add(directory);
}
String msg;
switch (data.type) {
case FileSystemEvent.create:
msg = green.wrap('create');
break;
case FileSystemEvent.delete:
msg = red.wrap('delete');
break;
case FileSystemEvent.move:
msg = yellow.wrap('move');
break;
case FileSystemEvent.modify:
break;
case FileSystemEvent.all:
msg = yellow.wrap('operate');
break;
default:
}
if (msg != null) {
print('\n$msg ${data.path}.\n');
assetsChanged?.call();
}
});
}
return null;
}
Watcher 我做了封装,拿去用吧 watcher.dart 。
结语
作为一个老的打工人,目前一共写了三个工具:
希望能够对大家有所帮助。
欢迎加入Flutter Candies,一起生产可爱的Flutter小糖果QQ群:181398081
最最后放上Flutter Candies全家桶,真香。