Compare commits
4 Commits
Author | SHA1 | Date | |
---|---|---|---|
af9b69326a | |||
511094262f | |||
62a60208b2 | |||
c0c3ecbf5e |
@ -1,4 +1,10 @@
|
|||||||
## [0.3.0] - 2020-03-02
|
## [0.3.1] - 2020-03-05
|
||||||
|
- Support dashed path
|
||||||
|
|
||||||
|
## [0.3.0+1] - 2020-03-05
|
||||||
|
- Specify a version range for the dependency on `characters`.
|
||||||
|
|
||||||
|
## [0.3.0] - 2020-03-05
|
||||||
- Add `LottieDelegates` a group of options to customize the lottie animation at runtime.
|
- Add `LottieDelegates` a group of options to customize the lottie animation at runtime.
|
||||||
ie: Dynamically modify color, position, size, text... of every elements of the animation.
|
ie: Dynamically modify color, position, size, text... of every elements of the animation.
|
||||||
- Correctly display Linear and Radial Gradients
|
- Correctly display Linear and Radial Gradients
|
||||||
|
@ -83,7 +83,7 @@ class _MyAppState extends State<MyApp> with TickerProviderStateMixin {
|
|||||||
body: ListView(
|
body: ListView(
|
||||||
children: [
|
children: [
|
||||||
Lottie.network(
|
Lottie.network(
|
||||||
'https://raw.githubusercontent.com/xvrh/lottie-flutter/master/sample_app/assets/Mobilo/C.json',
|
'https://raw.githubusercontent.com/xvrh/lottie-flutter/master/example/assets/Mobilo/C.json',
|
||||||
controller: _controller,
|
controller: _controller,
|
||||||
onLoaded: (composition) {
|
onLoaded: (composition) {
|
||||||
// Configure the AnimationController with the duration of the
|
// Configure the AnimationController with the duration of the
|
||||||
@ -243,9 +243,8 @@ class _Animation extends StatelessWidget {
|
|||||||
## Limitations
|
## Limitations
|
||||||
This is a new library so usability, documentation and performance are still work in progress.
|
This is a new library so usability, documentation and performance are still work in progress.
|
||||||
|
|
||||||
The following features are not yet implemented:
|
Only the [supported features of Lottie Android](https://airbnb.io/lottie/#/supported-features)
|
||||||
- Dash path effects
|
are supported in this port.
|
||||||
- Transforms on gradients (stroke and fills)
|
|
||||||
|
|
||||||
## Flutter Web
|
## Flutter Web
|
||||||
Run the app with `flutter run -d Chrome --dart-define=FLUTTER_WEB_USE_SKIA=true --release`
|
Run the app with `flutter run -d Chrome --dart-define=FLUTTER_WEB_USE_SKIA=true --release`
|
||||||
|
@ -74,9 +74,8 @@ import 'example/lib/examples/simple_dynamic_properties.dart#example';
|
|||||||
## Limitations
|
## Limitations
|
||||||
This is a new library so usability, documentation and performance are still work in progress.
|
This is a new library so usability, documentation and performance are still work in progress.
|
||||||
|
|
||||||
The following features are not yet implemented:
|
Only the [supported features of Lottie Android](https://airbnb.io/lottie/#/supported-features)
|
||||||
- Dash path effects
|
are supported in this port.
|
||||||
- Transforms on gradients (stroke and fills)
|
|
||||||
|
|
||||||
## Flutter Web
|
## Flutter Web
|
||||||
Run the app with `flutter run -d Chrome --dart-define=FLUTTER_WEB_USE_SKIA=true --release`
|
Run the app with `flutter run -d Chrome --dart-define=FLUTTER_WEB_USE_SKIA=true --release`
|
||||||
|
@ -3,6 +3,7 @@ analyzer:
|
|||||||
errors:
|
errors:
|
||||||
strong-mode:
|
strong-mode:
|
||||||
implicit-casts: false
|
implicit-casts: false
|
||||||
|
implicit-dynamic: false
|
||||||
linter:
|
linter:
|
||||||
rules:
|
rules:
|
||||||
avoid_renaming_method_parameters: true
|
avoid_renaming_method_parameters: true
|
||||||
|
@ -31,7 +31,7 @@ class _MyAppState extends State<MyApp> with TickerProviderStateMixin {
|
|||||||
body: ListView(
|
body: ListView(
|
||||||
children: [
|
children: [
|
||||||
Lottie.network(
|
Lottie.network(
|
||||||
'https://raw.githubusercontent.com/xvrh/lottie-flutter/master/sample_app/assets/Mobilo/C.json',
|
'https://raw.githubusercontent.com/xvrh/lottie-flutter/master/example/assets/Mobilo/C.json',
|
||||||
controller: _controller,
|
controller: _controller,
|
||||||
onLoaded: (composition) {
|
onLoaded: (composition) {
|
||||||
// Configure the AnimationController with the duration of the
|
// Configure the AnimationController with the duration of the
|
||||||
|
@ -2,14 +2,13 @@ import 'package:flutter/cupertino.dart';
|
|||||||
import 'package:flutter/material.dart';
|
import 'package:flutter/material.dart';
|
||||||
import 'package:logging/logging.dart';
|
import 'package:logging/logging.dart';
|
||||||
import 'package:lottie/lottie.dart';
|
import 'package:lottie/lottie.dart';
|
||||||
import 'package:lottie/src/l.dart';
|
|
||||||
import 'src/all_files.g.dart';
|
import 'src/all_files.g.dart';
|
||||||
|
|
||||||
void main() {
|
void main() {
|
||||||
Logger.root
|
Logger.root
|
||||||
..level = Level.ALL
|
..level = Level.ALL
|
||||||
..onRecord.listen(print);
|
..onRecord.listen(print);
|
||||||
L.setTraceEnabled(true);
|
Lottie.traceEnabled = true;
|
||||||
runApp(App());
|
runApp(App());
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -43,8 +42,8 @@ class App extends StatelessWidget {
|
|||||||
),
|
),
|
||||||
),
|
),
|
||||||
onTap: () {
|
onTap: () {
|
||||||
Navigator.of(context).push(
|
Navigator.of(context).push(MaterialPageRoute<void>(
|
||||||
MaterialPageRoute(builder: (context) => Detail(assetName)));
|
builder: (context) => Detail(assetName)));
|
||||||
},
|
},
|
||||||
);
|
);
|
||||||
},
|
},
|
||||||
|
@ -216,9 +216,9 @@ final files = [
|
|||||||
'assets/lottiefiles/light.json',
|
'assets/lottiefiles/light.json',
|
||||||
'assets/lottiefiles/lightsaber.json',
|
'assets/lottiefiles/lightsaber.json',
|
||||||
'assets/lottiefiles/little_girl_jumping_-_loader.json',
|
'assets/lottiefiles/little_girl_jumping_-_loader.json',
|
||||||
'assets/lottiefiles/loading copy.json',
|
|
||||||
'assets/lottiefiles/loading disc.json',
|
|
||||||
'assets/lottiefiles/loading.json',
|
'assets/lottiefiles/loading.json',
|
||||||
|
'assets/lottiefiles/loading_copy.json',
|
||||||
|
'assets/lottiefiles/loading_disc.json',
|
||||||
'assets/lottiefiles/loading_semicircle.json',
|
'assets/lottiefiles/loading_semicircle.json',
|
||||||
'assets/lottiefiles/location.json',
|
'assets/lottiefiles/location.json',
|
||||||
'assets/lottiefiles/location_marker.json',
|
'assets/lottiefiles/location_marker.json',
|
||||||
|
@ -108,7 +108,7 @@ packages:
|
|||||||
path: ".."
|
path: ".."
|
||||||
relative: true
|
relative: true
|
||||||
source: path
|
source: path
|
||||||
version: "0.3.0"
|
version: "0.3.1"
|
||||||
matcher:
|
matcher:
|
||||||
dependency: transitive
|
dependency: transitive
|
||||||
description:
|
description:
|
||||||
|
BIN
example/test/goldens/strokes/g.png
Normal file
After Width: | Height: | Size: 9.5 KiB |
BIN
example/test/goldens/strokes/intelia_logo_animation.png
Normal file
After Width: | Height: | Size: 70 KiB |
BIN
example/test/goldens/strokes/landing_page.png
Normal file
After Width: | Height: | Size: 20 KiB |
BIN
example/test/goldens/strokes/little_girl_jumping_-_loader.png
Normal file
After Width: | Height: | Size: 44 KiB |
BIN
example/test/goldens/strokes/loading_disc.png
Normal file
After Width: | Height: | Size: 14 KiB |
BIN
example/test/goldens/strokes/permission.png
Normal file
After Width: | Height: | Size: 97 KiB |
BIN
example/test/goldens/strokes/playing.png
Normal file
After Width: | Height: | Size: 15 KiB |
BIN
example/test/goldens/strokes/stroke.png
Normal file
After Width: | Height: | Size: 5.6 KiB |
BIN
example/test/goldens/strokes/truecosmos.png
Normal file
After Width: | Height: | Size: 17 KiB |
BIN
example/test/goldens/strokes/win_result_2.png
Normal file
After Width: | Height: | Size: 62 KiB |
37
example/test/stroke_test.dart
Normal file
@ -0,0 +1,37 @@
|
|||||||
|
import 'dart:io';
|
||||||
|
import 'dart:ui';
|
||||||
|
import 'package:flutter/material.dart';
|
||||||
|
import 'package:flutter_test/flutter_test.dart';
|
||||||
|
import 'package:lottie/lottie.dart';
|
||||||
|
import 'package:path/path.dart' as p;
|
||||||
|
import 'utils.dart';
|
||||||
|
|
||||||
|
void main() {
|
||||||
|
testWidgets('Animations with stroke', (tester) async {
|
||||||
|
var size = Size(500, 400);
|
||||||
|
tester.binding.window.physicalSizeTestValue = size;
|
||||||
|
tester.binding.window.devicePixelRatioTestValue = 1.0;
|
||||||
|
|
||||||
|
for (var asset in [
|
||||||
|
'assets/Tests/Stroke.json',
|
||||||
|
'assets/lottiefiles/loading_disc.json',
|
||||||
|
'assets/Mobilo/G.json',
|
||||||
|
'assets/lottiefiles/truecosmos.json',
|
||||||
|
'assets/lottiefiles/intelia_logo_animation.json',
|
||||||
|
'assets/lottiefiles/landing_page.json',
|
||||||
|
'assets/lottiefiles/permission.json',
|
||||||
|
'assets/lottiefiles/little_girl_jumping_-_loader.json',
|
||||||
|
'assets/lottiefiles/playing.json',
|
||||||
|
'assets/lottiefiles/win_result_2.json',
|
||||||
|
]) {
|
||||||
|
var composition =
|
||||||
|
await LottieComposition.fromBytes(File(asset).readAsBytesSync());
|
||||||
|
|
||||||
|
await tester.pumpWidget(FilmStrip(composition, size: size));
|
||||||
|
|
||||||
|
var fileName = '${p.basenameWithoutExtension(asset)}.png'.toLowerCase();
|
||||||
|
await expectLater(find.byType(FilmStrip),
|
||||||
|
matchesGoldenFile(p.join('goldens/strokes', fileName)));
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
51
example/test/utils.dart
Normal file
@ -0,0 +1,51 @@
|
|||||||
|
import 'package:flutter/material.dart';
|
||||||
|
import 'package:lottie/lottie.dart';
|
||||||
|
|
||||||
|
class FilmStrip extends StatelessWidget {
|
||||||
|
final LottieComposition composition;
|
||||||
|
final Size size;
|
||||||
|
|
||||||
|
const FilmStrip(this.composition, {Key key, @required this.size})
|
||||||
|
: super(key: key);
|
||||||
|
|
||||||
|
@override
|
||||||
|
Widget build(BuildContext context) {
|
||||||
|
return CustomPaint(
|
||||||
|
size: size,
|
||||||
|
painter: _CustomerPainter(composition),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
class _CustomerPainter extends CustomPainter {
|
||||||
|
static const _columns = 5;
|
||||||
|
final LottieComposition composition;
|
||||||
|
|
||||||
|
_CustomerPainter(this.composition);
|
||||||
|
|
||||||
|
@override
|
||||||
|
void paint(Canvas canvas, Size size) {
|
||||||
|
var thumbSize = Size(size.width / _columns, size.width / _columns);
|
||||||
|
var drawable = LottieDrawable(composition);
|
||||||
|
|
||||||
|
var index = 0;
|
||||||
|
for (var progress = 0.0; progress <= 1; progress += 0.05) {
|
||||||
|
var x = index % _columns;
|
||||||
|
var y = index ~/ _columns;
|
||||||
|
|
||||||
|
var rect = Offset(x * thumbSize.width, y.toDouble() * thumbSize.height) &
|
||||||
|
thumbSize;
|
||||||
|
|
||||||
|
drawable
|
||||||
|
..setProgress(progress)
|
||||||
|
..draw(canvas, rect);
|
||||||
|
|
||||||
|
++index;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@override
|
||||||
|
bool shouldRepaint(CustomPainter oldDelegate) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
@ -11,6 +11,7 @@ import '../../model/content/shape_trim_path.dart';
|
|||||||
import '../../model/key_path.dart';
|
import '../../model/key_path.dart';
|
||||||
import '../../model/layer/base_layer.dart';
|
import '../../model/layer/base_layer.dart';
|
||||||
import '../../utils.dart';
|
import '../../utils.dart';
|
||||||
|
import '../../utils/dash_path.dart';
|
||||||
import '../../utils/misc.dart';
|
import '../../utils/misc.dart';
|
||||||
import '../../utils/utils.dart';
|
import '../../utils/utils.dart';
|
||||||
import '../../value/lottie_value_callback.dart';
|
import '../../value/lottie_value_callback.dart';
|
||||||
@ -132,7 +133,6 @@ abstract class BaseStrokeContent
|
|||||||
L.endSection('StrokeContent#draw');
|
L.endSection('StrokeContent#draw');
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
_applyDashPatternIfNeeded(parentMatrix);
|
|
||||||
|
|
||||||
if (_colorFilterAnimation != null) {
|
if (_colorFilterAnimation != null) {
|
||||||
paint.colorFilter = _colorFilterAnimation.value;
|
paint.colorFilter = _colorFilterAnimation.value;
|
||||||
@ -152,7 +152,7 @@ abstract class BaseStrokeContent
|
|||||||
}
|
}
|
||||||
L.endSection('StrokeContent#buildPath');
|
L.endSection('StrokeContent#buildPath');
|
||||||
L.beginSection('StrokeContent#drawPath');
|
L.beginSection('StrokeContent#drawPath');
|
||||||
canvas.drawPath(_path, paint);
|
canvas.drawPath(_withDashPattern(_path, parentMatrix), paint);
|
||||||
L.endSection('StrokeContent#drawPath');
|
L.endSection('StrokeContent#drawPath');
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -199,13 +199,13 @@ abstract class BaseStrokeContent
|
|||||||
}
|
}
|
||||||
var endValue = min((endLength - totalLength) / length, 1).toDouble();
|
var endValue = min((endLength - totalLength) / length, 1).toDouble();
|
||||||
Utils.applyTrimPathIfNeeded(_trimPathPath, startValue, endValue, 0.0);
|
Utils.applyTrimPathIfNeeded(_trimPathPath, startValue, endValue, 0.0);
|
||||||
canvas.drawPath(_trimPathPath, paint);
|
canvas.drawPath(_withDashPattern(_trimPathPath, parentMatrix), paint);
|
||||||
} else if (currentLength + length < startLength ||
|
} else if (currentLength + length < startLength ||
|
||||||
currentLength > endLength) {
|
currentLength > endLength) {
|
||||||
// Do nothing
|
// Do nothing
|
||||||
} else if (currentLength + length <= endLength &&
|
} else if (currentLength + length <= endLength &&
|
||||||
startLength < currentLength) {
|
startLength < currentLength) {
|
||||||
canvas.drawPath(_trimPathPath, paint);
|
canvas.drawPath(_withDashPattern(_trimPathPath, parentMatrix), paint);
|
||||||
} else {
|
} else {
|
||||||
double startValue;
|
double startValue;
|
||||||
if (startLength < currentLength) {
|
if (startLength < currentLength) {
|
||||||
@ -220,7 +220,7 @@ abstract class BaseStrokeContent
|
|||||||
endValue = (endLength - currentLength) / length;
|
endValue = (endLength - currentLength) / length;
|
||||||
}
|
}
|
||||||
Utils.applyTrimPathIfNeeded(_trimPathPath, startValue, endValue, 0);
|
Utils.applyTrimPathIfNeeded(_trimPathPath, startValue, endValue, 0);
|
||||||
canvas.drawPath(_trimPathPath, paint);
|
canvas.drawPath(_withDashPattern(_trimPathPath, parentMatrix), paint);
|
||||||
}
|
}
|
||||||
currentLength += length;
|
currentLength += length;
|
||||||
}
|
}
|
||||||
@ -250,11 +250,11 @@ abstract class BaseStrokeContent
|
|||||||
return bounds;
|
return bounds;
|
||||||
}
|
}
|
||||||
|
|
||||||
void _applyDashPatternIfNeeded(Matrix4 parentMatrix) {
|
Path _withDashPattern(Path path, Matrix4 parentMatrix) {
|
||||||
L.beginSection('StrokeContent#applyDashPattern');
|
L.beginSection('StrokeContent#applyDashPattern');
|
||||||
if (_dashPatternAnimations.isEmpty) {
|
if (_dashPatternAnimations.isEmpty) {
|
||||||
L.endSection('StrokeContent#applyDashPattern');
|
L.endSection('StrokeContent#applyDashPattern');
|
||||||
return;
|
return path;
|
||||||
}
|
}
|
||||||
|
|
||||||
var scale = parentMatrix.getScale();
|
var scale = parentMatrix.getScale();
|
||||||
@ -275,12 +275,12 @@ abstract class BaseStrokeContent
|
|||||||
}
|
}
|
||||||
_dashPatternValues[i] *= scale;
|
_dashPatternValues[i] *= scale;
|
||||||
}
|
}
|
||||||
//TODO(xha): implement DashPathEffect with https://github.com/dnfield/flutter_path_drawing/blob/master/lib/src/dash_path.dart
|
|
||||||
// var offset = _dashPatternOffsetAnimation == null
|
var newPath =
|
||||||
// ? 0.0
|
dashPath(path, dashArray: CircularIntervalList(_dashPatternValues));
|
||||||
// : _dashPatternOffsetAnimation.value * scale;
|
|
||||||
//paint.setPathEffect(DashPathEffect(_dashPatternValues, offset));
|
|
||||||
L.endSection('StrokeContent#applyDashPattern');
|
L.endSection('StrokeContent#applyDashPattern');
|
||||||
|
|
||||||
|
return newPath;
|
||||||
}
|
}
|
||||||
|
|
||||||
@override
|
@override
|
||||||
|
@ -35,7 +35,7 @@ class TransformKeyframeAnimation {
|
|||||||
final Matrix4 _skewMatrix3;
|
final Matrix4 _skewMatrix3;
|
||||||
|
|
||||||
BaseKeyframeAnimation<Offset, Offset> /*?*/ _anchorPoint;
|
BaseKeyframeAnimation<Offset, Offset> /*?*/ _anchorPoint;
|
||||||
BaseKeyframeAnimation<dynamic, Offset> /*?*/ _position;
|
BaseKeyframeAnimation<Offset, Offset> /*?*/ _position;
|
||||||
BaseKeyframeAnimation<Offset, Offset> /*?*/ _scale;
|
BaseKeyframeAnimation<Offset, Offset> /*?*/ _scale;
|
||||||
BaseKeyframeAnimation<double, double> /*?*/ _rotation;
|
BaseKeyframeAnimation<double, double> /*?*/ _rotation;
|
||||||
DoubleKeyframeAnimation /*?*/ _skew;
|
DoubleKeyframeAnimation /*?*/ _skew;
|
||||||
@ -44,12 +44,11 @@ class TransformKeyframeAnimation {
|
|||||||
BaseKeyframeAnimation<int, int> /*?*/ _opacity;
|
BaseKeyframeAnimation<int, int> /*?*/ _opacity;
|
||||||
BaseKeyframeAnimation<int, int> /*?*/ get opacity => _opacity;
|
BaseKeyframeAnimation<int, int> /*?*/ get opacity => _opacity;
|
||||||
|
|
||||||
BaseKeyframeAnimation<dynamic, double> /*?*/ _startOpacity;
|
BaseKeyframeAnimation<double, double> /*?*/ _startOpacity;
|
||||||
BaseKeyframeAnimation<dynamic, double> /*?*/ get startOpacity =>
|
BaseKeyframeAnimation<double, double> /*?*/ get startOpacity => _startOpacity;
|
||||||
_startOpacity;
|
|
||||||
|
|
||||||
BaseKeyframeAnimation<dynamic, double> /*?*/ _endOpacity;
|
BaseKeyframeAnimation<double, double> /*?*/ _endOpacity;
|
||||||
BaseKeyframeAnimation<dynamic, double> /*?*/ get endOpacity => _endOpacity;
|
BaseKeyframeAnimation<double, double> /*?*/ get endOpacity => _endOpacity;
|
||||||
|
|
||||||
void addAnimationsToLayer(BaseLayer layer) {
|
void addAnimationsToLayer(BaseLayer layer) {
|
||||||
layer.addAnimation(_opacity);
|
layer.addAnimation(_opacity);
|
||||||
|
@ -1,3 +1,5 @@
|
|||||||
|
import 'dart:developer';
|
||||||
|
|
||||||
class L {
|
class L {
|
||||||
static final int _maxDepth = 20;
|
static final int _maxDepth = 20;
|
||||||
static bool _traceEnabled = false;
|
static bool _traceEnabled = false;
|
||||||
@ -6,7 +8,8 @@ class L {
|
|||||||
static int _traceDepth = 0;
|
static int _traceDepth = 0;
|
||||||
static int _depthPastMaxDepth = 0;
|
static int _depthPastMaxDepth = 0;
|
||||||
|
|
||||||
static void setTraceEnabled(bool enabled) {
|
static bool get traceEnabled => _traceEnabled;
|
||||||
|
static set traceEnabled(bool enabled) {
|
||||||
if (_traceEnabled == enabled) {
|
if (_traceEnabled == enabled) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
@ -27,9 +30,7 @@ class L {
|
|||||||
}
|
}
|
||||||
_sections[_traceDepth] = section;
|
_sections[_traceDepth] = section;
|
||||||
_startTimeNs[_traceDepth] = DateTime.now().microsecondsSinceEpoch;
|
_startTimeNs[_traceDepth] = DateTime.now().microsecondsSinceEpoch;
|
||||||
//TODO(xha): use flutter trace infrastructure
|
Timeline.startSync('Lottie::$section');
|
||||||
//TraceCompat.beginSection(section);
|
|
||||||
//print(section);
|
|
||||||
_traceDepth++;
|
_traceDepth++;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -49,8 +50,7 @@ class L {
|
|||||||
throw StateError('Unbalanced trace call $section'
|
throw StateError('Unbalanced trace call $section'
|
||||||
'. Expected ${_sections[_traceDepth]}.');
|
'. Expected ${_sections[_traceDepth]}.');
|
||||||
}
|
}
|
||||||
//TODO(xha)
|
Timeline.finishSync();
|
||||||
//TraceCompat.endSection();
|
|
||||||
return (DateTime.now().microsecondsSinceEpoch - _startTimeNs[_traceDepth]) /
|
return (DateTime.now().microsecondsSinceEpoch - _startTimeNs[_traceDepth]) /
|
||||||
1000;
|
1000;
|
||||||
}
|
}
|
||||||
|
@ -3,6 +3,7 @@ import 'dart:typed_data';
|
|||||||
import 'package:flutter/services.dart';
|
import 'package:flutter/services.dart';
|
||||||
import 'package:flutter/widgets.dart';
|
import 'package:flutter/widgets.dart';
|
||||||
import '../lottie.dart';
|
import '../lottie.dart';
|
||||||
|
import 'l.dart';
|
||||||
import 'lottie_builder.dart';
|
import 'lottie_builder.dart';
|
||||||
import 'providers/load_image.dart';
|
import 'providers/load_image.dart';
|
||||||
|
|
||||||
@ -235,6 +236,11 @@ class Lottie extends StatefulWidget {
|
|||||||
/// and the font family in your assets.
|
/// and the font family in your assets.
|
||||||
final LottieDelegates delegates;
|
final LottieDelegates delegates;
|
||||||
|
|
||||||
|
static bool get traceEnabled => L.traceEnabled;
|
||||||
|
static set traceEnabled(bool enabled) {
|
||||||
|
L.traceEnabled = enabled;
|
||||||
|
}
|
||||||
|
|
||||||
@override
|
@override
|
||||||
_LottieState createState() => _LottieState();
|
_LottieState createState() => _LottieState();
|
||||||
}
|
}
|
||||||
|
@ -708,7 +708,7 @@ class JsonUtf8Reader extends JsonReader {
|
|||||||
builder ??= StringBuffer();
|
builder ??= StringBuffer();
|
||||||
builder.write(buffer.readUtf8(index));
|
builder.write(buffer.readUtf8(index));
|
||||||
buffer.readByte(); // '\'
|
buffer.readByte(); // '\'
|
||||||
builder.write(readEscapeCharacter());
|
builder.writeCharCode(readEscapeCharacter());
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -15,7 +15,7 @@ Future<ui.Image> loadImage(LottieComposition composition,
|
|||||||
imageStream.removeListener(listener);
|
imageStream.removeListener(listener);
|
||||||
|
|
||||||
completer.complete(image.image);
|
completer.complete(image.image);
|
||||||
}, onError: (e, __) {
|
}, onError: (dynamic e, __) {
|
||||||
imageStream.removeListener(listener);
|
imageStream.removeListener(listener);
|
||||||
|
|
||||||
composition.addWarning('Failed to load image ${lottieImage.id}: $e');
|
composition.addWarning('Failed to load image ${lottieImage.id}: $e');
|
||||||
|
@ -2,7 +2,7 @@ import 'package:flutter/cupertino.dart';
|
|||||||
import '../../lottie.dart';
|
import '../../lottie.dart';
|
||||||
import 'load_image.dart';
|
import 'load_image.dart';
|
||||||
|
|
||||||
abstract class LottieProvider<T> {
|
abstract class LottieProvider {
|
||||||
LottieProvider({this.imageProviderFactory});
|
LottieProvider({this.imageProviderFactory});
|
||||||
|
|
||||||
final LottieImageProviderFactory imageProviderFactory;
|
final LottieImageProviderFactory imageProviderFactory;
|
||||||
|
101
lib/src/utils/dash_path.dart
Normal file
@ -0,0 +1,101 @@
|
|||||||
|
// Copied from https://github.com/dnfield/flutter_path_drawing
|
||||||
|
// We don't depend directly on this package to save 2 dependencies
|
||||||
|
|
||||||
|
import 'dart:ui';
|
||||||
|
import 'package:meta/meta.dart';
|
||||||
|
|
||||||
|
/// Creates a new path that is drawn from the segments of `source`.
|
||||||
|
///
|
||||||
|
/// Dash intervals are controled by the `dashArray` - see [CircularIntervalList]
|
||||||
|
/// for examples.
|
||||||
|
///
|
||||||
|
/// `dashOffset` specifies an initial starting point for the dashing.
|
||||||
|
///
|
||||||
|
/// Passing in a null `source` will result in a null result. Passing a `source`
|
||||||
|
/// that is an empty path will return an empty path.
|
||||||
|
Path dashPath(
|
||||||
|
Path source, {
|
||||||
|
@required CircularIntervalList<double> dashArray,
|
||||||
|
DashOffset dashOffset,
|
||||||
|
}) {
|
||||||
|
assert(dashArray != null);
|
||||||
|
if (source == null) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
dashOffset = dashOffset ?? const DashOffset.absolute(0.0);
|
||||||
|
// TODO: Is there some way to determine how much of a path would be visible today?
|
||||||
|
|
||||||
|
var dest = Path();
|
||||||
|
for (final metric in source.computeMetrics()) {
|
||||||
|
var distance = dashOffset._calculate(metric.length);
|
||||||
|
var draw = true;
|
||||||
|
while (distance < metric.length) {
|
||||||
|
final len = dashArray.next;
|
||||||
|
if (draw) {
|
||||||
|
dest.addPath(metric.extractPath(distance, distance + len), Offset.zero);
|
||||||
|
}
|
||||||
|
distance += len;
|
||||||
|
draw = !draw;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return dest;
|
||||||
|
}
|
||||||
|
|
||||||
|
enum _DashOffsetType { absolute, percentage }
|
||||||
|
|
||||||
|
/// Specifies the starting position of a dash array on a path, either as a
|
||||||
|
/// percentage or absolute value.
|
||||||
|
///
|
||||||
|
/// The internal value will be guaranteed to not be null.
|
||||||
|
class DashOffset {
|
||||||
|
/// Create a DashOffset that will be measured as a percentage of the length
|
||||||
|
/// of the segment being dashed.
|
||||||
|
///
|
||||||
|
/// `percentage` will be clamped between 0.0 and 1.0; null will be converted
|
||||||
|
/// to 0.0.
|
||||||
|
DashOffset.percentage(double percentage)
|
||||||
|
: _rawVal = percentage.clamp(0.0, 1.0) as double ?? 0.0,
|
||||||
|
_dashOffsetType = _DashOffsetType.percentage;
|
||||||
|
|
||||||
|
/// Create a DashOffset that will be measured in terms of absolute pixels
|
||||||
|
/// along the length of a [Path] segment.
|
||||||
|
///
|
||||||
|
/// `start` will be coerced to 0.0 if null.
|
||||||
|
const DashOffset.absolute(double start)
|
||||||
|
: _rawVal = start ?? 0.0,
|
||||||
|
_dashOffsetType = _DashOffsetType.absolute;
|
||||||
|
|
||||||
|
final double _rawVal;
|
||||||
|
final _DashOffsetType _dashOffsetType;
|
||||||
|
|
||||||
|
double _calculate(double length) {
|
||||||
|
return _dashOffsetType == _DashOffsetType.absolute
|
||||||
|
? _rawVal
|
||||||
|
: length * _rawVal;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// A circular array of dash offsets and lengths.
|
||||||
|
///
|
||||||
|
/// For example, the array `[5, 10]` would result in dashes 5 pixels long
|
||||||
|
/// followed by blank spaces 10 pixels long. The array `[5, 10, 5]` would
|
||||||
|
/// result in a 5 pixel dash, a 10 pixel gap, a 5 pixel dash, a 5 pixel gap,
|
||||||
|
/// a 10 pixel dash, etc.
|
||||||
|
///
|
||||||
|
/// Note that this does not quite conform to an [Iterable<T>], because it does
|
||||||
|
/// not have a moveNext.
|
||||||
|
class CircularIntervalList<T> {
|
||||||
|
CircularIntervalList(this._vals);
|
||||||
|
|
||||||
|
final List<T> _vals;
|
||||||
|
int _idx = 0;
|
||||||
|
|
||||||
|
T get next {
|
||||||
|
if (_idx >= _vals.length) {
|
||||||
|
_idx = 0;
|
||||||
|
}
|
||||||
|
return _vals[_idx++];
|
||||||
|
}
|
||||||
|
}
|
@ -272,7 +272,7 @@ class ValueDelegate<T> {
|
|||||||
bool isSameProperty(ValueDelegate other) {
|
bool isSameProperty(ValueDelegate other) {
|
||||||
if (identical(this, other)) return true;
|
if (identical(this, other)) return true;
|
||||||
return other is ValueDelegate<T> &&
|
return other is ValueDelegate<T> &&
|
||||||
const ListEquality().equals(other.keyPath, keyPath) &&
|
const ListEquality<String>().equals(other.keyPath, keyPath) &&
|
||||||
other.property == property;
|
other.property == property;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1,6 +1,6 @@
|
|||||||
name: lottie
|
name: lottie
|
||||||
description: Render After Effects animations natively on Flutter. This package is a pure Dart implementation of a Lottie player.
|
description: Render After Effects animations natively on Flutter. This package is a pure Dart implementation of a Lottie player.
|
||||||
version: 0.3.0
|
version: 0.3.1
|
||||||
homepage: https://github.com/xvrh/lottie-flutter
|
homepage: https://github.com/xvrh/lottie-flutter
|
||||||
|
|
||||||
environment:
|
environment:
|
||||||
@ -10,7 +10,7 @@ dependencies:
|
|||||||
flutter:
|
flutter:
|
||||||
sdk: flutter
|
sdk: flutter
|
||||||
archive: ^2.0.0
|
archive: ^2.0.0
|
||||||
characters:
|
characters: ^0.5.0
|
||||||
charcode: ^1.0.0
|
charcode: ^1.0.0
|
||||||
logging: ^0.11.0
|
logging: ^0.11.0
|
||||||
meta: ^1.1.8
|
meta: ^1.1.8
|
||||||
|
@ -69,7 +69,7 @@ class DartProject {
|
|||||||
static String _getPackageName(String projectRoot) {
|
static String _getPackageName(String projectRoot) {
|
||||||
var pubspecContent =
|
var pubspecContent =
|
||||||
File(p.join(projectRoot, 'pubspec.yaml')).readAsStringSync();
|
File(p.join(projectRoot, 'pubspec.yaml')).readAsStringSync();
|
||||||
var loadedPubspec = loadYaml(pubspecContent);
|
var loadedPubspec = loadYaml(pubspecContent) as YamlMap;
|
||||||
|
|
||||||
return loadedPubspec['name'] as String;
|
return loadedPubspec['name'] as String;
|
||||||
}
|
}
|
||||||
|
@ -6,7 +6,7 @@ void main(List<String> args) {
|
|||||||
var pubspec = File('pubspec.yaml');
|
var pubspec = File('pubspec.yaml');
|
||||||
var content = loadYaml(pubspec.readAsStringSync()) as YamlMap;
|
var content = loadYaml(pubspec.readAsStringSync()) as YamlMap;
|
||||||
|
|
||||||
var pubspecVersion = content['version'];
|
var pubspecVersion = content['version'] as String;
|
||||||
var tagVersion = '';
|
var tagVersion = '';
|
||||||
if (args.isNotEmpty) {
|
if (args.isNotEmpty) {
|
||||||
tagVersion = args[0].split('/').last;
|
tagVersion = args[0].split('/').last;
|
||||||
|