diff --git a/README.md b/README.md index 3de22f9bf..09c99af16 100644 --- a/README.md +++ b/README.md @@ -400,7 +400,7 @@ Widget build(BuildContext context) { | [flutter_svg](https://pub.dev/packages/flutter_svg) | .svg | `flutter_svg: true` | Assets.images.icons.paint.**svg()** | | [flare_flutter](https://pub.dev/packages/flare_flutter) | .flr | `flare_flutter: true` | Assets.flare.penguin.**flare()** | | [rive](https://pub.dev/packages/rive) | .flr | `rive: true` | Assets.rive.vehicles.**rive()** | -| [lottie](https://pub.dev/packages/lottie) | .json | `lottie: true` | Assets.lottie.hamburgerArrow.**lottie()** | +| [lottie](https://pub.dev/packages/lottie) | .json, .zip | `lottie: true` | Assets.lottie.hamburgerArrow.**lottie()** | In other cases, the asset is generated as String class. diff --git a/examples/example/assets/lottie/spinning_carrousel.zip b/examples/example/assets/lottie/spinning_carrousel.zip new file mode 100644 index 000000000..83b298ba8 Binary files /dev/null and b/examples/example/assets/lottie/spinning_carrousel.zip differ diff --git a/examples/example/assets/lottie/wrong/dummy.zip b/examples/example/assets/lottie/wrong/dummy.zip new file mode 100644 index 000000000..4bea61724 Binary files /dev/null and b/examples/example/assets/lottie/wrong/dummy.zip differ diff --git a/examples/example/lib/gen/assets.gen.dart b/examples/example/lib/gen/assets.gen.dart index b785f9083..7791cc530 100644 --- a/examples/example/lib/gen/assets.gen.dart +++ b/examples/example/lib/gen/assets.gen.dart @@ -88,12 +88,20 @@ class $AssetsLottieGen { LottieGenImage get hamburgerArrow => const LottieGenImage('assets/lottie/hamburger_arrow.json'); + /// File path: assets/lottie/spinning_carrousel.zip + LottieGenImage get spinningCarrousel => + const LottieGenImage('assets/lottie/spinning_carrousel.zip'); + /// Directory path: assets/lottie/wrong $AssetsLottieWrongGen get wrong => const $AssetsLottieWrongGen(); /// List of all assets - List get values => - [alarmClockLottieV440, geometricalAnimation, hamburgerArrow]; + List get values => [ + alarmClockLottieV440, + geometricalAnimation, + hamburgerArrow, + spinningCarrousel + ]; } class $AssetsMixGen { @@ -193,11 +201,14 @@ class $AssetsImagesIconsGen { class $AssetsLottieWrongGen { const $AssetsLottieWrongGen(); + /// File path: assets/lottie/wrong/dummy.zip + String get dummy => 'assets/lottie/wrong/dummy.zip'; + /// File path: assets/lottie/wrong/rocket-lottie-v439.json String get rocketLottieV439 => 'assets/lottie/wrong/rocket-lottie-v439.json'; /// List of all assets - List get values => [rocketLottieV439]; + List get values => [dummy, rocketLottieV439]; } class MyAssets { diff --git a/packages/core/lib/generators/integrations/lottie_integration.dart b/packages/core/lib/generators/integrations/lottie_integration.dart index c10060b21..f7ae4cd74 100644 --- a/packages/core/lib/generators/integrations/lottie_integration.dart +++ b/packages/core/lib/generators/integrations/lottie_integration.dart @@ -1,6 +1,8 @@ import 'dart:convert'; import 'dart:io'; +import 'package:archive/archive_io.dart'; +import 'package:collection/collection.dart'; import 'package:flutter_gen_core/generators/integrations/integration.dart'; import 'package:path/path.dart' as p; import 'package:pub_semver/pub_semver.dart'; @@ -19,6 +21,11 @@ class LottieIntegration extends Integration { 'layers', // Must include layers ]; + static const _supportedMimeTypes = [ + 'application/json', + 'application/zip', + ]; + String get packageExpression => isPackage ? ' = package' : ''; @override @@ -110,12 +117,33 @@ ${isPackage ? "\n static const String package = '$packageName';" : ''} bool get isConstConstructor => true; bool isLottieFile(AssetType type) { - if (type.mime != 'application/json') { + if (!_supportedMimeTypes.contains(type.mime)) { return false; } + if (type.mime == 'application/zip') { + final inputStream = InputFileStream(type.fullPath); + final archive = ZipDecoder().decodeBuffer(inputStream); + final jsonFile = archive.files.firstWhereOrNull( + (e) => e.name.endsWith('.json'), + ); + if (jsonFile?.isFile != true) { + return false; + } + final content = utf8.decode(jsonFile!.content); + return _isValidJsonFile(type, overrideInput: content); + } + return _isValidJsonFile(type); + } + + bool _isValidJsonFile(AssetType type, {String? overrideInput}) { try { - final absolutePath = p.join(type.rootPath, type.path); - String input = File(absolutePath).readAsStringSync(); + final String input; + if (overrideInput != null) { + input = overrideInput; + } else { + final absolutePath = p.join(type.rootPath, type.path); + input = File(absolutePath).readAsStringSync(); + } final fileKeys = jsonDecode(input) as Map; if (lottieKeys.every(fileKeys.containsKey) && fileKeys['v'] != null) { var version = Version.parse(fileKeys['v']); diff --git a/packages/core/lib/settings/asset_type.dart b/packages/core/lib/settings/asset_type.dart index 9455bca08..d058a11da 100644 --- a/packages/core/lib/settings/asset_type.dart +++ b/packages/core/lib/settings/asset_type.dart @@ -3,7 +3,6 @@ import 'package:flutter_gen_core/utils/identifer.dart'; import 'package:flutter_gen_core/utils/string.dart'; import 'package:mime/mime.dart' show lookupMimeType; import 'package:path/path.dart' as p; -import 'package:path/path.dart'; /// https://github.com/dart-lang/mime/blob/master/lib/src/default_extension_map.dart class AssetType { @@ -40,12 +39,15 @@ class AssetType { bool get isUnKnownMime => mime == null; - String get extension => p.extension(path); + /// Returns a name for this asset. + String get name => p.withoutExtension(path); String get baseName => p.basenameWithoutExtension(path); + String get extension => p.extension(path); + /// Returns the full absolute path for reading the asset file. - String get fullPath => join(rootPath, path); + String get fullPath => p.join(rootPath, path); // Replace to Posix style for Windows separator. String get posixStylePath => path.replaceAll(r'\', r'/'); @@ -56,10 +58,12 @@ class AssetType { _children.add(type); } - /// Returns a name for this asset. - String get name { - return withoutExtension(path); - } + @override + String toString() => 'AssetType(' + 'rootPath: $rootPath, ' + 'path: $path, ' + 'flavors: $flavors' + ')'; } /// Represents a AssetType with modifiers on it to mutate the [name] to ensure @@ -99,15 +103,14 @@ class UniqueAssetType extends AssetType { String get name { // Omit root directory from the name if it is either asset or assets. // TODO(bramp): Maybe move this into the _flatStyleDefinition - String p = path.replaceFirst(RegExp(r'^asset(s)?[/\\]'), ''); + String result = path.replaceFirst(RegExp(r'^asset(s)?[/\\]'), ''); if (basenameOnly) { - p = basename(p); + result = p.basename(result); } if (!needExtension) { - p = withoutExtension(p); + result = p.withoutExtension(result); } - - return style(convertToIdentifier(p)) + suffix; + return style(convertToIdentifier(result)) + suffix; } @override diff --git a/packages/core/pubspec.yaml b/packages/core/pubspec.yaml index b451dec3b..64fa4b85c 100644 --- a/packages/core/pubspec.yaml +++ b/packages/core/pubspec.yaml @@ -25,6 +25,7 @@ dependencies: glob: ^2.0.0 dart_style: ^2.2.4 + archive: ^3.4.0 args: ^2.0.0 pub_semver: ^2.0.0 vector_graphics_compiler: ^1.1.9 diff --git a/packages/core/test/config_test.dart b/packages/core/test/settings_test.dart similarity index 56% rename from packages/core/test/config_test.dart rename to packages/core/test/settings_test.dart index 53cc117d6..73c9450a7 100644 --- a/packages/core/test/config_test.dart +++ b/packages/core/test/settings_test.dart @@ -1,8 +1,34 @@ import 'package:collection/collection.dart'; +import 'package:flutter_gen_core/settings/asset_type.dart'; import 'package:flutter_gen_core/settings/flavored_asset.dart'; import 'package:test/test.dart'; void main() { + group(AssetType, () { + test('constructor', () { + final assetType = AssetType( + rootPath: 'root', + path: 'assets/single.jpg', + flavors: {'flavor'}, + ); + expect(assetType, isA()); + expect(assetType.name, 'assets/single'); + expect(assetType.baseName, 'single'); + expect(assetType.extension, '.jpg'); + expect(assetType.isUnKnownMime, false); + expect( + assetType, + predicate( + (e) => SetEquality().equals(e.flavors, {'flavor'}), + ), + ); + expect( + assetType.toString(), + 'AssetType(rootPath: root, path: assets/single.jpg, flavors: {flavor})', + ); + }); + }); + group(FlavoredAsset, () { test('constructor', () { expect( diff --git a/packages/core/test_resources/actual_data/assets_lottie_integrations.gen.dart b/packages/core/test_resources/actual_data/assets_lottie_integrations.gen.dart index 6cdabfb82..0a4780ebc 100644 --- a/packages/core/test_resources/actual_data/assets_lottie_integrations.gen.dart +++ b/packages/core/test_resources/actual_data/assets_lottie_integrations.gen.dart @@ -17,8 +17,12 @@ class $AssetsLottieGen { LottieGenImage get hamburgerArrow => const LottieGenImage('assets/lottie/hamburger_arrow.json'); + /// File path: assets/lottie/spinning_carrousel.zip + LottieGenImage get spinningCarrousel => + const LottieGenImage('assets/lottie/spinning_carrousel.zip'); + /// List of all assets - List get values => [hamburgerArrow]; + List get values => [hamburgerArrow, spinningCarrousel]; } class Assets { diff --git a/packages/core/test_resources/assets/lottie/spinning_carrousel.zip b/packages/core/test_resources/assets/lottie/spinning_carrousel.zip new file mode 100644 index 000000000..83b298ba8 Binary files /dev/null and b/packages/core/test_resources/assets/lottie/spinning_carrousel.zip differ diff --git a/packages/core/test_resources/pubspec_assets_lottie_integrations.yaml b/packages/core/test_resources/pubspec_assets_lottie_integrations.yaml index 08e80a261..26ff2b161 100644 --- a/packages/core/test_resources/pubspec_assets_lottie_integrations.yaml +++ b/packages/core/test_resources/pubspec_assets_lottie_integrations.yaml @@ -10,3 +10,4 @@ flutter_gen: flutter: assets: - assets/lottie/hamburger_arrow.json + - assets/lottie/spinning_carrousel.zip