Last active
May 20, 2024 23:22
-
-
Save timmaffett/57b74391da32c8896f16e3b64043ed59 to your computer and use it in GitHub Desktop.
Flutter: AssetBundleFindHelpers extension class on AssetBundle
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
import 'package:flutter/services.dart'; | |
/// Implements @matthew-carroll 's proposal in https://github.com/flutter/flutter/issues/137043 | |
/// | |
/// Example use/results | |
/// | |
/// in main(): | |
/// | |
/// await rootBundle.initAssetBundleFindHelpers(); //strictly speaking this does not need to be awaited | |
/// | |
/// | |
/// somewhere in a build(): | |
/// | |
/// debugPrint('assets() list is ${DefaultAssetBundle.of(context).assets}'); | |
/// debugPrint('Assets with PNG extension ${DefaultAssetBundle.of(context).findAssetsByExtension('png')}'); | |
/// debugPrint('Assets with SVG extension ${DefaultAssetBundle.of(context).findAssetsByExtension('.svg')}'); | |
/// debugPrint('Assets with "barcode" name ${DefaultAssetBundle.of(context).findAssetsByName('barcode')}'); | |
/// debugPrint('Assets with "barcode.png" name ${DefaultAssetBundle.of(context).findAssetsByName('barcode.png')}'); | |
/// debugPrint('Assets with "images/barcode" name ${DefaultAssetBundle.of(context).findAssetsByName('images/barcode')}'); | |
/// debugPrint('Assets with "images/barcode.png" name ${DefaultAssetBundle.of(context).findAssetsByName('images/barcode.png')}'); | |
/// debugPrint('Assets with "car_icon" name ${DefaultAssetBundle.of(context).findAssetsByName('car_icon')}'); | |
/// debugPrint('Assets with "arrow-left" extension ${DefaultAssetBundle.of(context).findAssetsByName('arrow-left')}'); | |
/// | |
/// | |
/// Console displays: | |
/// | |
/// assets() list is [assets/barcode.svg,assets/images/ae.png,assets/images/barcode-big.png,assets/images/barcode.png, | |
/// assets/images/other-barcode.png,assets/images/calendar.png,assets/images/car_icon.png,assets/images/car_icon_on_white.png, | |
/// assets/images/card.png,assets/barcode.svg,assets/images/24-support.svg,assets/images/alarm.svg,assets/images/arrow-down.svg, | |
/// assets/images/arrow-left.svg] | |
/// Assets with PNG extension [assets/images/ae.png,assets/images/barcode-big.png,assets/images/barcode.png,assets/images/other-barcode.png | |
/// assets/images/calendar.png,assets/images/car_icon.png,assets/images/car_icon_on_white.png,assets/images/card.png] | |
/// Assets with SVG extension [assets/barcode.svg,assets/images/24-support.svg,assets/images/alarm.svg, | |
/// assets/images/arrow-down.svg,assets/images/arrow-left.svg] | |
/// Assets with "barcode" name [assets/barcode.svg, assets/images/barcode.png] | |
/// Assets with "barcode.png" name [assets/images/barcode.png] | |
/// Assets with "images/barcode" name [assets/images/barcode.png] | |
/// Assets with "images/barcode.png" name [assets/images/barcode.png] | |
/// Assets with "car_icon" name [assets/images/car_icon.png] | |
/// Assets with "arrow-left" extension [assets/images/arrow-left.svg] | |
extension AssetBundleFindHelpers on AssetBundle { | |
// Some cached info for extension (static maps because extensions can't add instance fields) | |
static Map<AssetBundle,AssetManifest> bundleToManifestMap = {}; | |
static Map<AssetBundle,List<String>> bundleToAssetListMap = {}; | |
static RegExp parseAssetNameWithExtensionRegEx = RegExp(r'^(.+\/)*(.+)\.(.+)$'); // group 1 is path, group 2 is filename, group 3 is extension | |
static RegExp parseAssetPathNameNoExtRegEx = RegExp(r'^(.+\/)*(.+)$'); // group 1 is path, group 2 is filename | |
/// Retrieve and cache the AssetManifest for the bundle. | |
/// It is required to call this before any other helper methods. | |
/// Typically this would be something like `await rootBundle.initAssetBundleFindHelpers();` | |
Future<AssetManifest> initAssetBundleFindHelpers() { | |
if(bundleToManifestMap.containsKey(this)) { | |
return Future.value(bundleToManifestMap[this]!); | |
} | |
final retAM = AssetManifest.loadFromAssetBundle(this); | |
retAM.then((am) { | |
// store AssetManifest in our cache map | |
bundleToManifestMap[this] = am; | |
}); | |
return retAM; | |
} | |
/// Return list of assets for which the filename matches the specified [regex]. | |
List<String> findAssetsByNameThatMatch(RegExp regex) { | |
if(!bundleToManifestMap.containsKey(this)) { | |
throw ('Call initAssetBundleFindHelpers() for this AssetBundle before calling this api extension.'); | |
} | |
return assets.where((item) => regex.hasMatch(item)).toList(); | |
} | |
/// Return list of assets for which match the specified [name]. | |
/// The assets whole filename must match the specified [name]. | |
/// Optionally a required prefix path can be added to [name]. | |
/// Optionally a required extension can be added to [name]. | |
List<String> findAssetsByName(String name) { | |
if(!bundleToManifestMap.containsKey(this)) { | |
throw ('Call initAssetBundleFindHelpers() for this AssetBundle before calling this api extension.'); | |
} | |
String? targetPath, targetExt; | |
String targetName = name; | |
var match = parseAssetNameWithExtensionRegEx.firstMatch(name); | |
if(match!=null) { | |
// name has extension (and possibly path) | |
targetPath = match.group(1); | |
targetName = match.group(2) ?? name; | |
targetExt = match.group(3); | |
} else { | |
// name has no extension (and possibly path) | |
match = parseAssetPathNameNoExtRegEx.firstMatch(name); | |
if(match!=null) { | |
targetPath = match.group(1); | |
targetName = match.group(2) ?? name; | |
} | |
} | |
// escape any regex chars in name | |
final escapedName = RegExp.escape(targetName); | |
// make regexp to match this name with any prefix path or extension | |
// (name can contain subpath) | |
final regExForThisName = RegExp(r'^(.+\/)*' + escapedName + r'\.(.+)'); | |
final prelimMatches = findAssetsByNameThatMatch(regExForThisName); | |
// now prune down prelim match list as required by possible path or extension on incoming name | |
return prelimMatches.where((item) { | |
final match = parseAssetNameWithExtensionRegEx.firstMatch(item); | |
return match!=null && | |
match.group(2)==targetName && | |
(targetPath==null || (match.group(1)!=null && match.group(1)!.endsWith(targetPath))) && | |
(targetExt==null || (match.group(3)!=null && match.group(3)==targetExt)); | |
}).toList(); | |
} | |
/// Return list of all assets with specified [extension]. | |
/// The [extension] can optionally included the leading '.', ie. 'png' or '.png'. | |
List<String> findAssetsByExtension(String extension) { | |
if(!bundleToManifestMap.containsKey(this)) { | |
throw ('Call initAssetBundleFindHelpers() for this AssetBundle before calling this api extension.'); | |
} | |
if(!extension.startsWith('.')) extension = '.$extension'; | |
return assets.where((item) => item.endsWith(extension)).toList(); | |
} | |
// Return a list of all assets in the manifest. | |
List<String> get assets { | |
if(!bundleToManifestMap.containsKey(this)) { | |
throw ('Call initAssetBundleFindHelpers() for this AssetBundle before calling this api extension.'); | |
} | |
// cache the asset list so we don't keep recreating it | |
if(bundleToAssetListMap.containsKey(this)) { | |
return bundleToAssetListMap[this]!; | |
} | |
return (bundleToAssetListMap[this] = bundleToManifestMap[this]!.listAssets()); | |
} | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment