Asset naming has two parts in this earlier Dart script: normalize a PNG filename to a lowercase underscore form, then place resolution variants in 2.0x and 3.0x directories.
The script contains its own filename examples and uses synchronous rename operations after listing a directory. Its configured input directory is ./res.
Read the historical asset-renaming script
import 'dart:async';
import 'dart:io';
extension FileExtention on FileSystemEntity {
String get name {
return this?.path?.split("/")?.last;
}
String get directoryPath {
return this?.path?.replaceAll("/${name}", '');
}
}
// @nhancv 10/24/2019: Get file in directory
Future<List<FileSystemEntity>> dirContents(Directory dir) {
var files = <FileSystemEntity>[];
var completer = Completer<List<FileSystemEntity>>();
var lister = dir.list(recursive: false);
lister.listen((file) => files.add(file),
// should also register onError
onDone: () => completer.complete(files));
return completer.future;
}
// @nhancv 10/24/2019: Convert invalid file name in folder
void renameFile(List<FileSystemEntity> fileList) {
fileList.forEach((f) {
String fileName = f.name;
String filePath = f.directoryPath;
String newFileName = standardName(fileName);
if (newFileName != null) {
String newFilePath = '$filePath/$newFileName';
f.renameSync(newFilePath);
}
});
}
// Convert abcXyz.png = to abc_xyz.png
// From: addNew.png
// => add_new.png
String standardName(String originalName) {
RegExp validPattern = RegExp(r'^[a-z](?:_?[a-z0-9]+)*.png$');
if (validPattern.stringMatch(originalName) == null) {
// Detect addNew.png pattern
RegExp pattern = RegExp(r'[a-z]{1,}[A-Z].{0,}.png');
String fileName = originalName.replaceAll(' ', '_');
String invalidName = pattern.stringMatch(fileName);
if (invalidName != null) {
// Trim space, remove first _
String newName = invalidName.splitMapJoin(RegExp(r'[A-Z]+'),
onMatch: (m) => '_${m.group(0).toLowerCase()}',
onNonMatch: (n) => n.trim());
fileName = fileName.replaceAll(pattern, newName);
}
return fileName
.splitMapJoin(RegExp(r'^_[a-z]+'),
onMatch: (m) => '${m.group(0).substring(1)}',
onNonMatch: (n) => n.trim())
.replaceAll(RegExp(r'_+.png'), '.png')
.toLowerCase();
}
return null;
}
// Move <name>@2x.png to 2.0x/<name>.png
// Move <name>@3x.png to 3.0x/<name>.png
void moveFile(List<FileSystemEntity> fileList) {
fileList.forEach((f) {
String fileName = f.name;
String filePath = f.directoryPath;
// Move <name>@2x.png to 2.0x/<name>.png
RegExp pattern2x = RegExp(r'@2x.png');
if (pattern2x.stringMatch(fileName) != null) {
String nameWithoutSuffix =
fileName.substring(0, fileName.length - '@2x.png'.length);
Directory('$filePath/2.0x').createSync(recursive: true);
f.renameSync('$filePath/2.0x/$nameWithoutSuffix.png');
}
// Move <name>@3x.png to 3.0x/<name>.png
RegExp pattern3x = RegExp(r'@3x.png');
if (pattern3x.stringMatch(fileName) != null) {
String nameWithoutSuffix =
fileName.substring(0, fileName.length - '@3x.png'.length);
Directory('$filePath/3.0x').createSync(recursive: true);
f.renameSync('$filePath/3.0x/$nameWithoutSuffix.png');
}
});
}
// Run test standard function
void test() {
Map<String, String> testMap = {
'abc.png': null,
'ic_test.png': null,
'ic_test_2.png': null,
'ic_test_2_.png': 'ic_test_2.png',
'ic test.png': 'ic_test.png',
'_ic test.png': 'ic_test.png',
'addNew.png': 'add_new.png',
'abcXYZ.png': 'abc_xyz.png',
'abc zyz.png': 'abc_zyz.png',
'abc XYZ.png': 'abc_xyz.png',
'Zabc.png': 'zabc.png',
};
bool totalRes = true;
testMap.forEach((input, expect) {
String output = standardName(input);
bool res = output == expect;
if (totalRes && res == false) {
totalRes = false;
}
print(
'Input: $input - Output: $output - Expect: $expect - ${res ? 'TRUE' : 'FALSE'}');
});
print('Total: ${totalRes ? 'TRUE' : 'FALSE'}');
}
// - Standard file name in Directory
// - Standard place of file
// Context:
// ./Assets/addNew.png
// ./Assets/addNew@2x.png
// ./Assets/addNew@3x.png
//
// Output
// ./Assets/add_new.png
// ./Assets/2.0x/add_new.png
// ./Assets/3.0x/add_new.png
// --------
/// How to run:
/// Setup PATH point to: flutter\bin\cache\dart-sdk\bin
/// * Run:
/// * dart flutter_rename_file.dart
void main() async {
// Test
// test();
String directory = './res';
renameFile(await dirContents(Directory(directory)));
moveFile(await dirContents(Directory(directory)));
}
Review before changing files
The filename patterns and slash-based path handling are narrow assumptions. Existing destination files, naming collisions, non-PNG entries, and directory-listing errors need explicit handling before reuse. The listing itself notes the missing stream error handler.
This code predates current Dart null-safety expectations and has not been run for this revision. The embedded example expectations are source data, not newly verified test results.
Source: original Gist revision.