mirror of
https://github.com/xvrh/lottie-flutter.git
synced 2025-08-06 16:39:36 +08:00
Compare commits
2 Commits
v0.8.0-nul
...
v0.8.0-nul
Author | SHA1 | Date | |
---|---|---|---|
6831f475d4 | |||
bbfe04f00d |
BIN
example/assets/spinning_carrousel.zip
Normal file
BIN
example/assets/spinning_carrousel.zip
Normal file
Binary file not shown.
@ -21,7 +21,8 @@ class App extends StatelessWidget {
|
||||
appBar: AppBar(
|
||||
title: Text('Lottie Flutter'),
|
||||
),
|
||||
body: GridView.builder(
|
||||
body: Scrollbar(
|
||||
child: GridView.builder(
|
||||
itemCount: files.length,
|
||||
gridDelegate:
|
||||
SliverGridDelegateWithFixedCrossAxisCount(crossAxisCount: 4),
|
||||
@ -51,6 +52,7 @@ class App extends StatelessWidget {
|
||||
},
|
||||
),
|
||||
),
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
||||
|
@ -129,14 +129,14 @@ packages:
|
||||
name: logging
|
||||
url: "https://pub.dartlang.org"
|
||||
source: hosted
|
||||
version: "1.0.0-nullsafety.0"
|
||||
version: "1.0.0"
|
||||
lottie:
|
||||
dependency: "direct main"
|
||||
description:
|
||||
path: ".."
|
||||
relative: true
|
||||
source: path
|
||||
version: "0.8.0-nullsafety.0"
|
||||
version: "0.8.0-nullsafety.2"
|
||||
matcher:
|
||||
dependency: transitive
|
||||
description:
|
||||
|
74
example/test/dynamic_image_test.dart
Normal file
74
example/test/dynamic_image_test.dart
Normal file
@ -0,0 +1,74 @@
|
||||
import 'dart:async';
|
||||
import 'dart:io';
|
||||
import 'dart:ui' as ui;
|
||||
import 'package:flutter/widgets.dart';
|
||||
import 'package:flutter_test/flutter_test.dart';
|
||||
import 'package:lottie/lottie.dart';
|
||||
import 'utils.dart';
|
||||
|
||||
void main() {
|
||||
testWidgets('Can specify ImageProvider with zip file ', (tester) async {
|
||||
var size = Size(500, 400);
|
||||
tester.binding.window.physicalSizeTestValue = size;
|
||||
tester.binding.window.devicePixelRatioTestValue = 1.0;
|
||||
|
||||
var callCount = 0;
|
||||
LottieImageProviderFactory imageProviderFactory = (image) {
|
||||
++callCount;
|
||||
return FileImage(File('assets/Images/WeAccept/img_0.png'));
|
||||
};
|
||||
|
||||
var composition = (await tester.runAsync(() => FileLottie(
|
||||
File('assets/spinning_carrousel.zip'),
|
||||
imageProviderFactory: imageProviderFactory)
|
||||
.load()))!;
|
||||
|
||||
await tester.pumpWidget(FilmStrip(composition, size: size));
|
||||
|
||||
expect(callCount, 2);
|
||||
await expectLater(find.byType(FilmStrip),
|
||||
matchesGoldenFile('goldens/dynamic_image/zip_with_provider.png'));
|
||||
});
|
||||
|
||||
testWidgets('Can specify image delegate', (tester) async {
|
||||
var size = Size(500, 400);
|
||||
tester.binding.window.physicalSizeTestValue = size;
|
||||
tester.binding.window.devicePixelRatioTestValue = 1.0;
|
||||
|
||||
var image = await tester.runAsync(
|
||||
() => loadImage(FileImage(File('assets/Images/WeAccept/img_0.png'))));
|
||||
|
||||
var composition = (await tester.runAsync(
|
||||
() => FileLottie(File('assets/spinning_carrousel.zip')).load()))!;
|
||||
|
||||
var delegates = LottieDelegates(image: (composition, asset) {
|
||||
return image;
|
||||
});
|
||||
await tester.pumpWidget(FilmStrip(
|
||||
composition,
|
||||
size: size,
|
||||
delegates: delegates,
|
||||
));
|
||||
|
||||
await expectLater(find.byType(FilmStrip),
|
||||
matchesGoldenFile('goldens/dynamic_image/delegate.png'));
|
||||
});
|
||||
}
|
||||
|
||||
Future<ui.Image?> loadImage(ImageProvider provider) {
|
||||
var completer = Completer<ui.Image?>();
|
||||
var imageStream = provider.resolve(ImageConfiguration.empty);
|
||||
late ImageStreamListener listener;
|
||||
listener = ImageStreamListener((image, synchronousLoaded) {
|
||||
imageStream.removeListener(listener);
|
||||
|
||||
completer.complete(image.image);
|
||||
}, onError: (dynamic e, __) {
|
||||
imageStream.removeListener(listener);
|
||||
|
||||
completer.complete();
|
||||
});
|
||||
imageStream.addListener(listener);
|
||||
|
||||
return completer.future;
|
||||
}
|
BIN
example/test/goldens/all/spinning_carrousel.png
Normal file
BIN
example/test/goldens/all/spinning_carrousel.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 42 KiB |
BIN
example/test/goldens/dynamic_image/delegate.png
Normal file
BIN
example/test/goldens/dynamic_image/delegate.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 62 KiB |
BIN
example/test/goldens/dynamic_image/zip_with_provider.png
Normal file
BIN
example/test/goldens/dynamic_image/zip_with_provider.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 62 KiB |
@ -9,6 +9,7 @@ export 'src/model/marker.dart' show Marker;
|
||||
export 'src/options.dart' show LottieOptions;
|
||||
export 'src/providers/asset_provider.dart' show AssetLottie;
|
||||
export 'src/providers/file_provider.dart' show FileLottie;
|
||||
export 'src/providers/load_image.dart' show LottieImageProviderFactory;
|
||||
export 'src/providers/lottie_provider.dart' show LottieProvider;
|
||||
export 'src/providers/memory_provider.dart' show MemoryLottie;
|
||||
export 'src/providers/network_provider.dart' show NetworkLottie;
|
||||
|
@ -241,8 +241,7 @@ abstract class BaseStrokeContent
|
||||
var bounds = _path.getBounds();
|
||||
|
||||
var width = _widthAnimation.value;
|
||||
bounds = Rect.fromLTRB(bounds.left - width / 2.0, bounds.top - width / 2.0,
|
||||
bounds.right + width / 2.0, bounds.bottom + width / 2.0);
|
||||
bounds = bounds.inflate(width / 2.0);
|
||||
// Add padding to account for rounding errors.
|
||||
bounds = bounds.inflate(1);
|
||||
L.endSection('StrokeContent#getBounds');
|
||||
|
@ -34,12 +34,14 @@ class CompositionParameters {
|
||||
}
|
||||
|
||||
class LottieComposition {
|
||||
static Future<LottieComposition> fromByteData(ByteData data, {String? name}) {
|
||||
return fromBytes(data.buffer.asUint8List(), name: name);
|
||||
static Future<LottieComposition> fromByteData(ByteData data,
|
||||
{String? name, LottieImageProviderFactory? imageProviderFactory}) {
|
||||
return fromBytes(data.buffer.asUint8List(),
|
||||
name: name, imageProviderFactory: imageProviderFactory);
|
||||
}
|
||||
|
||||
static Future<LottieComposition> fromBytes(Uint8List bytes,
|
||||
{String? name}) async {
|
||||
{String? name, LottieImageProviderFactory? imageProviderFactory}) async {
|
||||
Archive? archive;
|
||||
if (bytes[0] == 0x50 && bytes[1] == 0x4B) {
|
||||
archive = ZipDecoder().decodeBytes(bytes);
|
||||
@ -55,8 +57,18 @@ class LottieComposition {
|
||||
var imagePath = p.posix.join(image.dirName, image.fileName);
|
||||
var found = archive.files.firstWhereOrNull(
|
||||
(f) => f.name.toLowerCase() == imagePath.toLowerCase());
|
||||
|
||||
ImageProvider? provider;
|
||||
if (imageProviderFactory != null) {
|
||||
provider = imageProviderFactory(image);
|
||||
}
|
||||
|
||||
if (provider != null) {
|
||||
image.loadedImage = await loadImage(composition, image, provider);
|
||||
}
|
||||
|
||||
if (found != null) {
|
||||
image.loadedImage = await loadImage(
|
||||
image.loadedImage ??= await loadImage(
|
||||
composition, image, MemoryImage(found.content as Uint8List));
|
||||
}
|
||||
}
|
||||
|
@ -381,14 +381,11 @@ class _LottieBuilderState extends State<LottieBuilder> {
|
||||
void _load() {
|
||||
var provider = widget.lottie;
|
||||
_loadingFuture = widget.lottie.load().then((composition) {
|
||||
print('Loaded');
|
||||
if (mounted && widget.onLoaded != null && widget.lottie == provider) {
|
||||
widget.onLoaded!(composition);
|
||||
}
|
||||
|
||||
return composition;
|
||||
}, onError: (Object e, StackTrace t) {
|
||||
print('Load error $e $t');
|
||||
});
|
||||
}
|
||||
|
||||
|
@ -1,4 +1,6 @@
|
||||
import 'dart:ui' as ui;
|
||||
import 'package:flutter/widgets.dart';
|
||||
import '../lottie.dart';
|
||||
import 'lottie_drawable.dart';
|
||||
import 'value_delegate.dart';
|
||||
|
||||
@ -43,12 +45,31 @@ class LottieDelegates {
|
||||
/// ```
|
||||
final List<ValueDelegate>? values;
|
||||
|
||||
//TODO(xha): imageDelegate to change the image to display?
|
||||
/// A callback to dynamically change an image of the animation.
|
||||
///
|
||||
/// Example:
|
||||
/// ```dart
|
||||
/// Lottie.asset(
|
||||
// 'assets/data.json',
|
||||
// delegates: LottieDelegates(
|
||||
// image: (composition, image) {
|
||||
// if (image.id == 'img_0' && _isMouseOver) {
|
||||
// return myCustomImage;
|
||||
// }
|
||||
//
|
||||
// // Use the default method: composition.images[image.id].loadedImage;
|
||||
// return null;
|
||||
// },
|
||||
// )
|
||||
// )
|
||||
/// ```
|
||||
final ui.Image? Function(LottieComposition, LottieImageAsset)? image;
|
||||
|
||||
LottieDelegates({
|
||||
this.text,
|
||||
TextStyle Function(LottieFontStyle)? textStyle,
|
||||
this.values,
|
||||
this.image,
|
||||
}) : textStyle = textStyle;
|
||||
|
||||
@override
|
||||
|
@ -79,7 +79,13 @@ class LottieDrawable {
|
||||
ui.Image? getImageAsset(String? ref) {
|
||||
var imageAsset = composition.images[ref];
|
||||
if (imageAsset != null) {
|
||||
return imageAsset.loadedImage;
|
||||
var imageDelegate = delegates?.image;
|
||||
ui.Image? image;
|
||||
if (imageDelegate != null) {
|
||||
image = imageDelegate(composition, imageAsset);
|
||||
}
|
||||
|
||||
return image ?? imageAsset.loadedImage;
|
||||
} else {
|
||||
return null;
|
||||
}
|
||||
|
@ -261,10 +261,7 @@ abstract class BaseLayer implements DrawingContent, KeyPathElement {
|
||||
void _clearCanvas(Canvas canvas, Rect bounds) {
|
||||
L.beginSection('Layer#clearLayer');
|
||||
// If we don't pad the clear draw, some phones leave a 1px border of the graphics buffer.
|
||||
canvas.drawRect(
|
||||
Rect.fromLTRB(bounds.left - 1, bounds.top - 1, bounds.right + 1,
|
||||
bounds.bottom + 1),
|
||||
_clearPaint);
|
||||
canvas.drawRect(bounds.inflate(1), _clearPaint);
|
||||
L.endSection('Layer#clearLayer');
|
||||
}
|
||||
|
||||
|
@ -101,14 +101,13 @@ class LottieCompositionParser {
|
||||
}
|
||||
layers.add(layer);
|
||||
layerMap[layer.id] = layer;
|
||||
|
||||
}
|
||||
if (imageCount > 4) {
|
||||
composition.addWarning(
|
||||
'You have $imageCount images. Lottie should primarily be '
|
||||
'used with shapes. If you are using Adobe Illustrator, convert the Illustrator layers'
|
||||
' to shape layers.');
|
||||
}
|
||||
}
|
||||
reader.endArray();
|
||||
}
|
||||
|
||||
|
@ -33,7 +33,8 @@ class AssetLottie extends LottieProvider {
|
||||
var data = await chosenBundle.load(keyName);
|
||||
|
||||
var composition = await LottieComposition.fromByteData(data,
|
||||
name: p.url.basenameWithoutExtension(keyName));
|
||||
name: p.url.basenameWithoutExtension(keyName),
|
||||
imageProviderFactory: imageProviderFactory);
|
||||
|
||||
for (var image in composition.images.values) {
|
||||
image.loadedImage ??= await _loadImage(composition, image);
|
||||
|
@ -18,7 +18,8 @@ class FileLottie extends LottieProvider {
|
||||
return sharedLottieCache.putIfAbsent(cacheKey, () async {
|
||||
var bytes = await io.loadFile(file);
|
||||
var composition = await LottieComposition.fromBytes(bytes,
|
||||
name: p.basenameWithoutExtension(io.filePath(file)));
|
||||
name: p.basenameWithoutExtension(io.filePath(file)),
|
||||
imageProviderFactory: imageProviderFactory);
|
||||
|
||||
for (var image in composition.images.values) {
|
||||
image.loadedImage ??= await _loadImage(composition, image);
|
||||
|
@ -4,7 +4,7 @@ import 'package:flutter/widgets.dart';
|
||||
import '../composition.dart';
|
||||
import '../lottie_image_asset.dart';
|
||||
|
||||
typedef LottieImageProviderFactory = ImageProvider Function(LottieImageAsset);
|
||||
typedef LottieImageProviderFactory = ImageProvider? Function(LottieImageAsset);
|
||||
|
||||
Future<ui.Image?> loadImage(LottieComposition composition,
|
||||
LottieImageAsset lottieImage, ImageProvider provider) {
|
||||
|
@ -18,7 +18,8 @@ class MemoryLottie extends LottieProvider {
|
||||
// TODO(xha): hash the list content
|
||||
var cacheKey = 'memory-${bytes.hashCode}-${bytes.lengthInBytes}';
|
||||
return sharedLottieCache.putIfAbsent(cacheKey, () async {
|
||||
var composition = await LottieComposition.fromBytes(bytes);
|
||||
var composition = await LottieComposition.fromBytes(bytes,
|
||||
imageProviderFactory: imageProviderFactory);
|
||||
for (var image in composition.images.values) {
|
||||
image.loadedImage ??= await _loadImage(composition, image);
|
||||
}
|
||||
|
@ -24,7 +24,8 @@ class NetworkLottie extends LottieProvider {
|
||||
var bytes = await network.loadHttp(resolved, headers: headers);
|
||||
|
||||
var composition = await LottieComposition.fromBytes(bytes,
|
||||
name: p.url.basenameWithoutExtension(url));
|
||||
name: p.url.basenameWithoutExtension(url),
|
||||
imageProviderFactory: imageProviderFactory);
|
||||
|
||||
for (var image in composition.images.values) {
|
||||
image.loadedImage ??= await _loadImage(resolved, composition, image);
|
||||
|
@ -1,6 +1,6 @@
|
||||
name: lottie
|
||||
description: Render After Effects animations natively on Flutter. This package is a pure Dart implementation of a Lottie player.
|
||||
version: 0.8.0-nullsafety.1
|
||||
version: 0.8.0-nullsafety.3
|
||||
homepage: https://github.com/xvrh/lottie-flutter
|
||||
|
||||
environment:
|
||||
|
Reference in New Issue
Block a user