mirror of
https://github.com/rive-app/rive-flutter.git
synced 2025-05-19 22:36:51 +08:00
bring flutter asset loading inline with cpp
basically does what it says, i'm also removing an example i used to check on things being garbage collected Diffs= c0411df0a bring flutter asset loading inline with cpp (#6135) Co-authored-by: Gordon Hayes <pggordonhayes@gmail.com> Co-authored-by: Maxwell Talbot <talbot.maxwell@gmail.com>
This commit is contained in:
@ -1 +1 @@
|
|||||||
db984dfd3dd7d0ebf6b4e9cfc9fed28269d8c8ed
|
c0411df0a74b2aafa18526375cda19f6779a2354
|
||||||
|
@ -1,6 +1,7 @@
|
|||||||
## Upcoming
|
## 0.12.0
|
||||||
|
|
||||||
- Increase HTTP dependency range for Rive and Rive Common
|
- Increase HTTP dependency range for Rive and Rive Common
|
||||||
|
- Update api for loading rive files, simplifying loading external images & fonts.
|
||||||
|
|
||||||
## 0.11.17
|
## 0.11.17
|
||||||
|
|
||||||
|
BIN
example/assets/acqua_text_out_of_band.riv
Normal file
BIN
example/assets/acqua_text_out_of_band.riv
Normal file
Binary file not shown.
Binary file not shown.
BIN
example/assets/image_out_of_band.riv
Normal file
BIN
example/assets/image_out_of_band.riv
Normal file
Binary file not shown.
Binary file not shown.
Binary file not shown.
@ -1,296 +0,0 @@
|
|||||||
import 'dart:async';
|
|
||||||
import 'dart:math';
|
|
||||||
import 'dart:typed_data';
|
|
||||||
|
|
||||||
import 'package:http/http.dart' as http;
|
|
||||||
|
|
||||||
import 'package:flutter/material.dart';
|
|
||||||
import 'package:rive/math.dart';
|
|
||||||
import 'package:rive/rive.dart';
|
|
||||||
|
|
||||||
class BasicTextCustomized extends StatefulWidget {
|
|
||||||
const BasicTextCustomized({Key? key}) : super(key: key);
|
|
||||||
|
|
||||||
@override
|
|
||||||
State<BasicTextCustomized> createState() => _BasicTextCustomizedState();
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Basic example playing a Rive animation from a packaged asset.
|
|
||||||
class _BasicTextCustomizedState extends State<BasicTextCustomized>
|
|
||||||
with SingleTickerProviderStateMixin {
|
|
||||||
late final AnimationController _animationController =
|
|
||||||
AnimationController(vsync: this, duration: const Duration(seconds: 10));
|
|
||||||
RiveArtboardRenderer? _artboardRenderer;
|
|
||||||
List<Artboard> artboards = [];
|
|
||||||
late RiveFile file;
|
|
||||||
FontAsset? fontAsset;
|
|
||||||
|
|
||||||
randomFont() async {
|
|
||||||
final urls = [
|
|
||||||
'https://cdn.rive.app/runtime/flutter/IndieFlower-Regular.ttf',
|
|
||||||
'https://cdn.rive.app/runtime/flutter/comic-neue.ttf',
|
|
||||||
'https://cdn.rive.app/runtime/flutter/inter.ttf',
|
|
||||||
'https://cdn.rive.app/runtime/flutter/inter-tight.ttf',
|
|
||||||
'https://cdn.rive.app/runtime/flutter/josefin-sans.ttf',
|
|
||||||
'https://cdn.rive.app/runtime/flutter/send-flowers.ttf',
|
|
||||||
];
|
|
||||||
|
|
||||||
final res = await http.get(
|
|
||||||
// pick a random url from the list of fonts
|
|
||||||
Uri.parse(urls[Random().nextInt(urls.length)]),
|
|
||||||
);
|
|
||||||
await fontAsset?.decode(Uint8List.view(res.bodyBytes.buffer));
|
|
||||||
}
|
|
||||||
|
|
||||||
addArtboard() async {
|
|
||||||
unawaited(randomFont());
|
|
||||||
setState(() {
|
|
||||||
final artboard = file.mainArtboard.instance();
|
|
||||||
final controller = StateMachineController.fromArtboard(
|
|
||||||
artboard,
|
|
||||||
'State Machine 1',
|
|
||||||
)!;
|
|
||||||
artboard.addController(controller);
|
|
||||||
artboards.add(artboard);
|
|
||||||
while (artboards.length > 5) {
|
|
||||||
artboards.removeAt(0);
|
|
||||||
}
|
|
||||||
_artboardRenderer = RiveArtboardRenderer(
|
|
||||||
fit: BoxFit.cover,
|
|
||||||
alignment: Alignment.center,
|
|
||||||
artboards: artboards,
|
|
||||||
);
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
Future<void> _load() async {
|
|
||||||
// You need to manage adding the controller to the artboard yourself,
|
|
||||||
// unlike with the RiveAnimation widget that handles a lot of this logic
|
|
||||||
// for you by simply providing the state machine (or animation) name.
|
|
||||||
file = await RiveFile.asset(
|
|
||||||
'assets/trans_text.riv',
|
|
||||||
loadEmbeddedAssets: false,
|
|
||||||
assetLoader: CallbackAssetLoader(
|
|
||||||
(asset) async {
|
|
||||||
if (asset is FontAsset) {
|
|
||||||
setState(() {
|
|
||||||
fontAsset = asset;
|
|
||||||
});
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
return false;
|
|
||||||
},
|
|
||||||
),
|
|
||||||
);
|
|
||||||
final artboard = file.mainArtboard.instance();
|
|
||||||
final controller = StateMachineController.fromArtboard(
|
|
||||||
artboard,
|
|
||||||
'State Machine 1',
|
|
||||||
)!;
|
|
||||||
artboard.addController(controller);
|
|
||||||
artboards.add(artboard);
|
|
||||||
unawaited(randomFont());
|
|
||||||
|
|
||||||
setState(
|
|
||||||
() => _artboardRenderer = RiveArtboardRenderer(
|
|
||||||
fit: BoxFit.cover,
|
|
||||||
alignment: Alignment.center,
|
|
||||||
artboards: [artboard],
|
|
||||||
),
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
@override
|
|
||||||
void initState() {
|
|
||||||
super.initState();
|
|
||||||
_animationController.repeat();
|
|
||||||
_load();
|
|
||||||
}
|
|
||||||
|
|
||||||
@override
|
|
||||||
void dispose() {
|
|
||||||
_animationController.dispose();
|
|
||||||
super.dispose();
|
|
||||||
}
|
|
||||||
|
|
||||||
@override
|
|
||||||
Widget build(BuildContext context) {
|
|
||||||
return Scaffold(
|
|
||||||
body: GestureDetector(
|
|
||||||
onTap: () {
|
|
||||||
addArtboard();
|
|
||||||
},
|
|
||||||
child: Center(
|
|
||||||
child: _artboardRenderer == null
|
|
||||||
? const SizedBox()
|
|
||||||
: CustomPaint(
|
|
||||||
painter: RiveCustomPainter(
|
|
||||||
_artboardRenderer!,
|
|
||||||
repaint: _animationController,
|
|
||||||
),
|
|
||||||
child: const SizedBox.expand(), // use all the size available
|
|
||||||
),
|
|
||||||
),
|
|
||||||
),
|
|
||||||
);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
class RiveCustomPainter extends CustomPainter {
|
|
||||||
final RiveArtboardRenderer artboardRenderer;
|
|
||||||
|
|
||||||
RiveCustomPainter(this.artboardRenderer, {super.repaint}) {
|
|
||||||
_lastTickTime = DateTime.now();
|
|
||||||
_elapsedTime = Duration.zero;
|
|
||||||
}
|
|
||||||
|
|
||||||
late DateTime _lastTickTime;
|
|
||||||
late Duration _elapsedTime;
|
|
||||||
|
|
||||||
void _calculateElapsedTime() {
|
|
||||||
final currentTime = DateTime.now();
|
|
||||||
_elapsedTime = currentTime.difference(_lastTickTime);
|
|
||||||
_lastTickTime = currentTime;
|
|
||||||
}
|
|
||||||
|
|
||||||
@override
|
|
||||||
void paint(Canvas canvas, Size size) {
|
|
||||||
_calculateElapsedTime(); // Calculate elapsed time since last tick.
|
|
||||||
|
|
||||||
// Advance the artboard by the elapsed time.
|
|
||||||
artboardRenderer.advance(_elapsedTime.inMicroseconds / 1000000);
|
|
||||||
|
|
||||||
final width = size.width / 3;
|
|
||||||
final height = size.height / 2;
|
|
||||||
final artboardSize = Size(width, height);
|
|
||||||
|
|
||||||
// First row
|
|
||||||
canvas.save();
|
|
||||||
artboardRenderer.render(canvas, artboardSize);
|
|
||||||
canvas.restore();
|
|
||||||
}
|
|
||||||
|
|
||||||
@override
|
|
||||||
bool shouldRepaint(covariant CustomPainter oldDelegate) {
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Keeps an `Artboard` instance and renders it to a `Canvas`.
|
|
||||||
///
|
|
||||||
/// This is a simplified version of the `RiveAnimation` widget and its
|
|
||||||
/// RenderObject
|
|
||||||
///
|
|
||||||
/// This accounts for the `fit` and `alignment` properties, similar to how
|
|
||||||
/// `RiveAnimation` works.
|
|
||||||
class RiveArtboardRenderer {
|
|
||||||
final List<Artboard> artboards;
|
|
||||||
final BoxFit fit;
|
|
||||||
final Alignment alignment;
|
|
||||||
|
|
||||||
RiveArtboardRenderer({
|
|
||||||
required this.fit,
|
|
||||||
required this.alignment,
|
|
||||||
required this.artboards,
|
|
||||||
});
|
|
||||||
|
|
||||||
void advance(double dt) {
|
|
||||||
for (var artboard in artboards) {
|
|
||||||
artboard.advance(dt, nested: true);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
late final aabb =
|
|
||||||
AABB.fromValues(0, 0, artboards.first.width, artboards.first.height);
|
|
||||||
|
|
||||||
void render(Canvas canvas, Size size) {
|
|
||||||
_paint(canvas, aabb, size);
|
|
||||||
}
|
|
||||||
|
|
||||||
final _transform = Mat2D();
|
|
||||||
final _center = Mat2D();
|
|
||||||
|
|
||||||
void _paint(Canvas canvas, AABB bounds, Size size) {
|
|
||||||
for (var artboard in artboards) {
|
|
||||||
_paintArtboard(artboard, canvas, bounds, size);
|
|
||||||
bounds = AABB.fromValues(bounds.left - 100, bounds.top - 100,
|
|
||||||
bounds.right - 100, bounds.bottom - 100);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void _paintArtboard(
|
|
||||||
Artboard artboard, Canvas canvas, AABB bounds, Size size) {
|
|
||||||
final contentWidth = bounds[2] - bounds[0];
|
|
||||||
final contentHeight = bounds[3] - bounds[1];
|
|
||||||
|
|
||||||
if (contentWidth == 0 || contentHeight == 0) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
final x = -1 * bounds[0] -
|
|
||||||
contentWidth / 2.0 -
|
|
||||||
(alignment.x * contentWidth / 2.0);
|
|
||||||
final y = -1 * bounds[1] -
|
|
||||||
contentHeight / 2.0 -
|
|
||||||
(alignment.y * contentHeight / 2.0);
|
|
||||||
|
|
||||||
var scaleX = 1.0;
|
|
||||||
var scaleY = 1.0;
|
|
||||||
|
|
||||||
canvas.save();
|
|
||||||
|
|
||||||
switch (fit) {
|
|
||||||
case BoxFit.fill:
|
|
||||||
scaleX = size.width / contentWidth;
|
|
||||||
scaleY = size.height / contentHeight;
|
|
||||||
break;
|
|
||||||
case BoxFit.contain:
|
|
||||||
final minScale =
|
|
||||||
min(size.width / contentWidth, size.height / contentHeight);
|
|
||||||
scaleX = scaleY = minScale;
|
|
||||||
break;
|
|
||||||
case BoxFit.cover:
|
|
||||||
final maxScale =
|
|
||||||
max(size.width / contentWidth, size.height / contentHeight);
|
|
||||||
scaleX = scaleY = maxScale;
|
|
||||||
break;
|
|
||||||
case BoxFit.fitHeight:
|
|
||||||
final minScale = size.height / contentHeight;
|
|
||||||
scaleX = scaleY = minScale;
|
|
||||||
break;
|
|
||||||
case BoxFit.fitWidth:
|
|
||||||
final minScale = size.width / contentWidth;
|
|
||||||
scaleX = scaleY = minScale;
|
|
||||||
break;
|
|
||||||
case BoxFit.none:
|
|
||||||
scaleX = scaleY = 1.0;
|
|
||||||
break;
|
|
||||||
case BoxFit.scaleDown:
|
|
||||||
final minScale =
|
|
||||||
min(size.width / contentWidth, size.height / contentHeight);
|
|
||||||
scaleX = scaleY = minScale < 1.0 ? minScale : 1.0;
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
|
|
||||||
Mat2D.setIdentity(_transform);
|
|
||||||
_transform[4] = size.width / 2.0 + (alignment.x * size.width / 2.0);
|
|
||||||
_transform[5] = size.height / 2.0 + (alignment.y * size.height / 2.0);
|
|
||||||
Mat2D.scale(_transform, _transform, Vec2D.fromValues(scaleX, scaleY));
|
|
||||||
Mat2D.setIdentity(_center);
|
|
||||||
_center[4] = x;
|
|
||||||
_center[5] = y;
|
|
||||||
Mat2D.multiply(_transform, _transform, _center);
|
|
||||||
|
|
||||||
canvas.translate(
|
|
||||||
size.width / 2.0 + (alignment.x * size.width / 2.0),
|
|
||||||
size.height / 2.0 + (alignment.y * size.height / 2.0),
|
|
||||||
);
|
|
||||||
|
|
||||||
canvas.scale(scaleX, scaleY);
|
|
||||||
canvas.translate(x, y);
|
|
||||||
|
|
||||||
artboard.draw(canvas);
|
|
||||||
|
|
||||||
canvas.restore();
|
|
||||||
}
|
|
||||||
}
|
|
@ -14,6 +14,8 @@ import 'package:http/http.dart' as http;
|
|||||||
/// and provide them instantly.
|
/// and provide them instantly.
|
||||||
///
|
///
|
||||||
/// See `custom_cached_asset_loading.dart` for an example of this.
|
/// See `custom_cached_asset_loading.dart` for an example of this.
|
||||||
|
///
|
||||||
|
/// See: https://help.rive.app/runtimes/loading-assets
|
||||||
class CustomAssetLoading extends StatefulWidget {
|
class CustomAssetLoading extends StatefulWidget {
|
||||||
const CustomAssetLoading({Key? key}) : super(key: key);
|
const CustomAssetLoading({Key? key}) : super(key: key);
|
||||||
|
|
||||||
@ -79,14 +81,18 @@ class _RiveRandomImageState extends State<_RiveRandomImage> {
|
|||||||
|
|
||||||
Future<void> _loadFiles() async {
|
Future<void> _loadFiles() async {
|
||||||
final imageFile = await RiveFile.asset(
|
final imageFile = await RiveFile.asset(
|
||||||
'assets/asset.riv',
|
'assets/image_out_of_band.riv',
|
||||||
loadEmbeddedAssets: false,
|
|
||||||
assetLoader: CallbackAssetLoader(
|
assetLoader: CallbackAssetLoader(
|
||||||
(asset) async {
|
(asset, bytes) async {
|
||||||
final res =
|
// Replace image assets that are not embedded in the rive file
|
||||||
await http.get(Uri.parse('https://picsum.photos/1000/1000'));
|
if (asset is ImageAsset && bytes == null) {
|
||||||
await asset.decode(Uint8List.view(res.bodyBytes.buffer));
|
final res =
|
||||||
return true;
|
await http.get(Uri.parse('https://picsum.photos/500/500'));
|
||||||
|
await asset.decode(Uint8List.view(res.bodyBytes.buffer));
|
||||||
|
return true;
|
||||||
|
} else {
|
||||||
|
return false; // use default asset loading
|
||||||
|
}
|
||||||
},
|
},
|
||||||
),
|
),
|
||||||
);
|
);
|
||||||
@ -102,9 +108,23 @@ class _RiveRandomImageState extends State<_RiveRandomImage> {
|
|||||||
return const Center(child: CircularProgressIndicator());
|
return const Center(child: CircularProgressIndicator());
|
||||||
}
|
}
|
||||||
|
|
||||||
return RiveAnimation.direct(
|
return Stack(
|
||||||
_riveImageSampleFile!,
|
children: [
|
||||||
fit: BoxFit.cover,
|
RiveAnimation.direct(
|
||||||
|
_riveImageSampleFile!,
|
||||||
|
stateMachines: const ['State Machine 1'],
|
||||||
|
fit: BoxFit.cover,
|
||||||
|
),
|
||||||
|
const Positioned(
|
||||||
|
child: Padding(
|
||||||
|
padding: EdgeInsets.all(8.0),
|
||||||
|
child: Text(
|
||||||
|
'This example loads a random image dynamically and asynchronously.\n\nHover to zoom.',
|
||||||
|
style: TextStyle(color: Colors.black),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
)
|
||||||
|
],
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -128,25 +148,29 @@ class _RiveRandomFontState extends State<_RiveRandomFont> {
|
|||||||
|
|
||||||
Future<void> _loadFiles() async {
|
Future<void> _loadFiles() async {
|
||||||
final fontFile = await RiveFile.asset(
|
final fontFile = await RiveFile.asset(
|
||||||
'assets/sampletext.riv',
|
'assets/acqua_text_out_of_band.riv',
|
||||||
loadEmbeddedAssets: false, // disable loading embedded assets.
|
|
||||||
assetLoader: CallbackAssetLoader(
|
assetLoader: CallbackAssetLoader(
|
||||||
(asset) async {
|
(asset, bytes) async {
|
||||||
final urls = [
|
// Replace font assets that are not embedded in the rive file
|
||||||
'https://cdn.rive.app/runtime/flutter/IndieFlower-Regular.ttf',
|
if (asset is FontAsset && bytes == null) {
|
||||||
'https://cdn.rive.app/runtime/flutter/comic-neue.ttf',
|
final urls = [
|
||||||
'https://cdn.rive.app/runtime/flutter/inter.ttf',
|
'https://cdn.rive.app/runtime/flutter/IndieFlower-Regular.ttf',
|
||||||
'https://cdn.rive.app/runtime/flutter/inter-tight.ttf',
|
'https://cdn.rive.app/runtime/flutter/comic-neue.ttf',
|
||||||
'https://cdn.rive.app/runtime/flutter/josefin-sans.ttf',
|
'https://cdn.rive.app/runtime/flutter/inter.ttf',
|
||||||
'https://cdn.rive.app/runtime/flutter/send-flowers.ttf',
|
'https://cdn.rive.app/runtime/flutter/inter-tight.ttf',
|
||||||
];
|
'https://cdn.rive.app/runtime/flutter/josefin-sans.ttf',
|
||||||
|
'https://cdn.rive.app/runtime/flutter/send-flowers.ttf',
|
||||||
|
];
|
||||||
|
|
||||||
final res = await http.get(
|
final res = await http.get(
|
||||||
// pick a random url from the list of fonts
|
// pick a random url from the list of fonts
|
||||||
Uri.parse(urls[Random().nextInt(urls.length)]),
|
Uri.parse(urls[Random().nextInt(urls.length)]),
|
||||||
);
|
);
|
||||||
await asset.decode(Uint8List.view(res.bodyBytes.buffer));
|
await asset.decode(Uint8List.view(res.bodyBytes.buffer));
|
||||||
return true;
|
return true;
|
||||||
|
} else {
|
||||||
|
return false; // use default asset loading
|
||||||
|
}
|
||||||
},
|
},
|
||||||
),
|
),
|
||||||
);
|
);
|
||||||
@ -162,9 +186,23 @@ class _RiveRandomFontState extends State<_RiveRandomFont> {
|
|||||||
return const Center(child: CircularProgressIndicator());
|
return const Center(child: CircularProgressIndicator());
|
||||||
}
|
}
|
||||||
|
|
||||||
return RiveAnimation.direct(
|
return Stack(
|
||||||
_riveFontSampleFile!,
|
children: [
|
||||||
fit: BoxFit.cover,
|
RiveAnimation.direct(
|
||||||
|
_riveFontSampleFile!,
|
||||||
|
stateMachines: const ['State Machine 1'],
|
||||||
|
fit: BoxFit.cover,
|
||||||
|
),
|
||||||
|
const Positioned(
|
||||||
|
child: Padding(
|
||||||
|
padding: EdgeInsets.all(8.0),
|
||||||
|
child: Text(
|
||||||
|
'This example loads a random font dynamically and asynchronously.\n\nClick to change drink.',
|
||||||
|
style: TextStyle(color: Colors.black),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
)
|
||||||
|
],
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -14,6 +14,8 @@ import 'package:rive/rive.dart';
|
|||||||
///
|
///
|
||||||
/// The example also shows how to swap out the assets multiple times by
|
/// The example also shows how to swap out the assets multiple times by
|
||||||
/// keeping a reference to the asset and swapping it out.
|
/// keeping a reference to the asset and swapping it out.
|
||||||
|
///
|
||||||
|
/// See: https://help.rive.app/runtimes/loading-assets
|
||||||
class CustomCachedAssetLoading extends StatefulWidget {
|
class CustomCachedAssetLoading extends StatefulWidget {
|
||||||
const CustomCachedAssetLoading({Key? key}) : super(key: key);
|
const CustomCachedAssetLoading({Key? key}) : super(key: key);
|
||||||
|
|
||||||
@ -38,7 +40,7 @@ class _CustomCachedAssetLoadingState extends State<CustomCachedAssetLoading> {
|
|||||||
Future<void> _warmUpCache() async {
|
Future<void> _warmUpCache() async {
|
||||||
final futures = <Future>[];
|
final futures = <Future>[];
|
||||||
loadImage() async {
|
loadImage() async {
|
||||||
final res = await http.get(Uri.parse('https://picsum.photos/1000/1000'));
|
final res = await http.get(Uri.parse('https://picsum.photos/500/500'));
|
||||||
final body = Uint8List.view(res.bodyBytes.buffer);
|
final body = Uint8List.view(res.bodyBytes.buffer);
|
||||||
final image = await ImageAsset.parseBytes(body);
|
final image = await ImageAsset.parseBytes(body);
|
||||||
if (image != null) {
|
if (image != null) {
|
||||||
@ -160,10 +162,9 @@ class __RiveRandomCachedImageState extends State<_RiveRandomCachedImage> {
|
|||||||
|
|
||||||
Future<void> _loadRiveFile() async {
|
Future<void> _loadRiveFile() async {
|
||||||
final imageFile = await RiveFile.asset(
|
final imageFile = await RiveFile.asset(
|
||||||
'assets/asset.riv',
|
'assets/image_out_of_band.riv',
|
||||||
loadEmbeddedAssets: false,
|
|
||||||
assetLoader: CallbackAssetLoader(
|
assetLoader: CallbackAssetLoader(
|
||||||
(asset) async {
|
(asset, bytes) async {
|
||||||
if (asset is ImageAsset) {
|
if (asset is ImageAsset) {
|
||||||
asset.image = _imageCache[Random().nextInt(_imageCache.length)];
|
asset.image = _imageCache[Random().nextInt(_imageCache.length)];
|
||||||
// Maintain a reference to the image asset
|
// Maintain a reference to the image asset
|
||||||
@ -188,9 +189,23 @@ class __RiveRandomCachedImageState extends State<_RiveRandomCachedImage> {
|
|||||||
return Column(
|
return Column(
|
||||||
children: [
|
children: [
|
||||||
Expanded(
|
Expanded(
|
||||||
child: RiveAnimation.direct(
|
child: Stack(
|
||||||
_riveImageSampleFile!,
|
children: [
|
||||||
fit: BoxFit.cover,
|
RiveAnimation.direct(
|
||||||
|
_riveImageSampleFile!,
|
||||||
|
stateMachines: const ['State Machine 1'],
|
||||||
|
fit: BoxFit.cover,
|
||||||
|
),
|
||||||
|
const Positioned(
|
||||||
|
child: Padding(
|
||||||
|
padding: EdgeInsets.all(8.0),
|
||||||
|
child: Text(
|
||||||
|
'This example caches images and swaps them out instantly.\n\nHover to zoom.',
|
||||||
|
style: TextStyle(color: Colors.black),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
],
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
Padding(
|
Padding(
|
||||||
@ -200,7 +215,7 @@ class __RiveRandomCachedImageState extends State<_RiveRandomCachedImage> {
|
|||||||
_imageAsset?.image =
|
_imageAsset?.image =
|
||||||
_imageCache[Random().nextInt(_imageCache.length)];
|
_imageCache[Random().nextInt(_imageCache.length)];
|
||||||
},
|
},
|
||||||
child: const Text('Random image asset'),
|
child: const Text('Random image'),
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
],
|
],
|
||||||
@ -234,10 +249,9 @@ class __RiveRandomCachedFontState extends State<_RiveRandomCachedFont> {
|
|||||||
|
|
||||||
Future<void> _loadRiveFile() async {
|
Future<void> _loadRiveFile() async {
|
||||||
final fontFile = await RiveFile.asset(
|
final fontFile = await RiveFile.asset(
|
||||||
'assets/sampletext.riv',
|
'assets/acqua_text_out_of_band.riv',
|
||||||
loadEmbeddedAssets: false,
|
|
||||||
assetLoader: CallbackAssetLoader(
|
assetLoader: CallbackAssetLoader(
|
||||||
(asset) async {
|
(asset, bytes) async {
|
||||||
if (asset is FontAsset) {
|
if (asset is FontAsset) {
|
||||||
asset.font = _fontCache[Random().nextInt(_fontCache.length)];
|
asset.font = _fontCache[Random().nextInt(_fontCache.length)];
|
||||||
_fontAssets.add(asset);
|
_fontAssets.add(asset);
|
||||||
@ -262,9 +276,23 @@ class __RiveRandomCachedFontState extends State<_RiveRandomCachedFont> {
|
|||||||
return Column(
|
return Column(
|
||||||
children: [
|
children: [
|
||||||
Expanded(
|
Expanded(
|
||||||
child: RiveAnimation.direct(
|
child: Stack(
|
||||||
_riveFontSampleFile!,
|
children: [
|
||||||
fit: BoxFit.cover,
|
RiveAnimation.direct(
|
||||||
|
_riveFontSampleFile!,
|
||||||
|
stateMachines: const ['State Machine 1'],
|
||||||
|
fit: BoxFit.cover,
|
||||||
|
),
|
||||||
|
const Positioned(
|
||||||
|
child: Padding(
|
||||||
|
padding: EdgeInsets.all(8.0),
|
||||||
|
child: Text(
|
||||||
|
'This example caches fonts and swaps them out instantly.\n\nClick to change drink.',
|
||||||
|
style: TextStyle(color: Colors.black),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
],
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
Padding(
|
Padding(
|
||||||
@ -275,7 +303,7 @@ class __RiveRandomCachedFontState extends State<_RiveRandomCachedFont> {
|
|||||||
element?.font = _fontCache[Random().nextInt(_fontCache.length)];
|
element?.font = _fontCache[Random().nextInt(_fontCache.length)];
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
child: const Text('Random font asset'),
|
child: const Text('Random font'),
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
],
|
],
|
||||||
|
@ -1,5 +1,4 @@
|
|||||||
import 'package:flutter/material.dart';
|
import 'package:flutter/material.dart';
|
||||||
import 'package:rive_example/basic_text_customized.dart';
|
|
||||||
|
|
||||||
import 'package:rive_example/custom_asset_loading.dart';
|
import 'package:rive_example/custom_asset_loading.dart';
|
||||||
import 'package:rive_example/custom_cached_asset_loading.dart';
|
import 'package:rive_example/custom_cached_asset_loading.dart';
|
||||||
@ -46,7 +45,6 @@ class RiveExampleApp extends StatefulWidget {
|
|||||||
class _RiveExampleAppState extends State<RiveExampleApp> {
|
class _RiveExampleAppState extends State<RiveExampleApp> {
|
||||||
// Example animations
|
// Example animations
|
||||||
final _pages = [
|
final _pages = [
|
||||||
const _Page('Basic Text Customized', BasicTextCustomized()),
|
|
||||||
const _Page('Simple Animation - Asset', SimpleAssetAnimation()),
|
const _Page('Simple Animation - Asset', SimpleAssetAnimation()),
|
||||||
const _Page('Simple Animation - Network', SimpleNetworkAnimation()),
|
const _Page('Simple Animation - Network', SimpleNetworkAnimation()),
|
||||||
const _Page('Play/Pause Animation', PlayPauseAnimation()),
|
const _Page('Play/Pause Animation', PlayPauseAnimation()),
|
||||||
@ -61,8 +59,8 @@ class _RiveExampleAppState extends State<RiveExampleApp> {
|
|||||||
const _Page('Skinning Demo', SkinningDemo()),
|
const _Page('Skinning Demo', SkinningDemo()),
|
||||||
const _Page('Animation Carousel', AnimationCarousel()),
|
const _Page('Animation Carousel', AnimationCarousel()),
|
||||||
const _Page('Basic Text', BasicText()),
|
const _Page('Basic Text', BasicText()),
|
||||||
const _Page('Custom Asset Loading', CustomAssetLoading()),
|
const _Page('Asset Loading', CustomAssetLoading()),
|
||||||
const _Page('Custom Cached Asset Loading', CustomCachedAssetLoading()),
|
const _Page('Cached Asset Loading', CustomCachedAssetLoading()),
|
||||||
const _Page('Event Open URL Button', EventOpenUrlButton()),
|
const _Page('Event Open URL Button', EventOpenUrlButton()),
|
||||||
const _Page('Event Sounds', EventSounds()),
|
const _Page('Event Sounds', EventSounds()),
|
||||||
const _Page('Event Star Rating', EventStarRating()),
|
const _Page('Event Star Rating', EventStarRating()),
|
||||||
|
@ -9,9 +9,9 @@ import 'package:rive/src/utilities/utilities.dart';
|
|||||||
///
|
///
|
||||||
/// See [CallbackAssetLoader] and [LocalAssetLoader] for an example of how to
|
/// See [CallbackAssetLoader] and [LocalAssetLoader] for an example of how to
|
||||||
/// use this.
|
/// use this.
|
||||||
|
// ignore: one_member_abstracts
|
||||||
abstract class FileAssetLoader {
|
abstract class FileAssetLoader {
|
||||||
Future<bool> load(FileAsset asset);
|
Future<bool> load(FileAsset asset, Uint8List? embeddedBytes);
|
||||||
bool isCompatible(FileAsset asset) => true;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Loads assets from Rive's CDN.
|
/// Loads assets from Rive's CDN.
|
||||||
@ -23,10 +23,12 @@ class CDNAssetLoader extends FileAssetLoader {
|
|||||||
CDNAssetLoader();
|
CDNAssetLoader();
|
||||||
|
|
||||||
@override
|
@override
|
||||||
bool isCompatible(FileAsset asset) => asset.cdnUuid.isNotEmpty;
|
Future<bool> load(FileAsset asset, Uint8List? embeddedBytes) async {
|
||||||
|
// if the asset is embedded, or does not have a cdn uuid, do not attempt
|
||||||
@override
|
// to load it
|
||||||
Future<bool> load(FileAsset asset) async {
|
if (embeddedBytes != null || asset.cdnUuid.isEmpty) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
// TODO (Max): Do we have a URL builder?
|
// TODO (Max): Do we have a URL builder?
|
||||||
// TODO (Max): We should aim to get loading errors exposed where
|
// TODO (Max): We should aim to get loading errors exposed where
|
||||||
// possible, this includes failed network requests but also
|
// possible, this includes failed network requests but also
|
||||||
@ -81,7 +83,11 @@ class LocalAssetLoader extends FileAssetLoader {
|
|||||||
}) : _assetBundle = assetBundle ?? rootBundle;
|
}) : _assetBundle = assetBundle ?? rootBundle;
|
||||||
|
|
||||||
@override
|
@override
|
||||||
Future<bool> load(FileAsset asset) async {
|
Future<bool> load(FileAsset asset, Uint8List? embeddedBytes) async {
|
||||||
|
// do not load embedded assets.
|
||||||
|
if (embeddedBytes != null) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
String? assetPath;
|
String? assetPath;
|
||||||
switch (asset.type) {
|
switch (asset.type) {
|
||||||
case Type.unknown:
|
case Type.unknown:
|
||||||
@ -109,7 +115,6 @@ class LocalAssetLoader extends FileAssetLoader {
|
|||||||
///
|
///
|
||||||
/// This callback will be triggered for any **referenced** assets.
|
/// This callback will be triggered for any **referenced** assets.
|
||||||
///
|
///
|
||||||
/// Set [loadEmbeddedAssets] to false to disable loading embedded assets
|
|
||||||
///
|
///
|
||||||
/// Set [loadCdnAssets] to false to disable loading
|
/// Set [loadCdnAssets] to false to disable loading
|
||||||
/// assets from the Rive CDN.
|
/// assets from the Rive CDN.
|
||||||
@ -120,10 +125,9 @@ class LocalAssetLoader extends FileAssetLoader {
|
|||||||
/// ```dart
|
/// ```dart
|
||||||
/// final riveFile = await RiveFile.asset(
|
/// final riveFile = await RiveFile.asset(
|
||||||
/// 'assets/asset.riv',
|
/// 'assets/asset.riv',
|
||||||
/// loadEmbeddedAssets: true,
|
|
||||||
/// loadCdnAssets: true,
|
/// loadCdnAssets: true,
|
||||||
/// assetLoader: CallbackAssetLoader(
|
/// assetLoader: CallbackAssetLoader(
|
||||||
/// (asset) async {
|
/// (asset, bytes) async {
|
||||||
/// final res =
|
/// final res =
|
||||||
/// await http.get(Uri.parse('https://picsum.photos/1000/1000'));
|
/// await http.get(Uri.parse('https://picsum.photos/1000/1000'));
|
||||||
/// await asset.decode(Uint8List.view(res.bodyBytes.buffer));
|
/// await asset.decode(Uint8List.view(res.bodyBytes.buffer));
|
||||||
@ -133,13 +137,13 @@ class LocalAssetLoader extends FileAssetLoader {
|
|||||||
/// );
|
/// );
|
||||||
/// ```
|
/// ```
|
||||||
class CallbackAssetLoader extends FileAssetLoader {
|
class CallbackAssetLoader extends FileAssetLoader {
|
||||||
Future<bool> Function(FileAsset) callback;
|
Future<bool> Function(FileAsset asset, Uint8List? embeddedBytes) callback;
|
||||||
|
|
||||||
CallbackAssetLoader(this.callback);
|
CallbackAssetLoader(this.callback);
|
||||||
|
|
||||||
@override
|
@override
|
||||||
Future<bool> load(FileAsset asset) async {
|
Future<bool> load(FileAsset asset, Uint8List? embeddedBytes) async {
|
||||||
return callback(asset);
|
return callback(asset, embeddedBytes);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -157,13 +161,10 @@ class FallbackAssetLoader extends FileAssetLoader {
|
|||||||
FallbackAssetLoader(this.fileAssetLoaders);
|
FallbackAssetLoader(this.fileAssetLoaders);
|
||||||
|
|
||||||
@override
|
@override
|
||||||
Future<bool> load(FileAsset asset) async {
|
Future<bool> load(FileAsset asset, Uint8List? embeddedBytes) async {
|
||||||
for (var i = 0; i < fileAssetLoaders.length; i++) {
|
for (var i = 0; i < fileAssetLoaders.length; i++) {
|
||||||
final resolver = fileAssetLoaders[i];
|
final resolver = fileAssetLoaders[i];
|
||||||
if (!resolver.isCompatible(asset)) {
|
final success = await resolver.load(asset, embeddedBytes);
|
||||||
continue;
|
|
||||||
}
|
|
||||||
final success = await resolver.load(asset);
|
|
||||||
if (success) {
|
if (success) {
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
@ -13,38 +13,32 @@ abstract class FileAssetResolver {
|
|||||||
class FileAssetImporter extends ImportStackObject {
|
class FileAssetImporter extends ImportStackObject {
|
||||||
final FileAssetLoader? assetLoader;
|
final FileAssetLoader? assetLoader;
|
||||||
final FileAsset fileAsset;
|
final FileAsset fileAsset;
|
||||||
final bool loadEmbeddedAssets;
|
Uint8List? embeddedBytes;
|
||||||
|
|
||||||
FileAssetImporter(
|
FileAssetImporter(
|
||||||
this.fileAsset,
|
this.fileAsset,
|
||||||
this.assetLoader, {
|
this.assetLoader,
|
||||||
this.loadEmbeddedAssets = true,
|
);
|
||||||
});
|
|
||||||
|
|
||||||
bool _contentsResolved = false;
|
|
||||||
|
|
||||||
void resolveContents(FileAssetContents contents) {
|
void resolveContents(FileAssetContents contents) {
|
||||||
if (loadEmbeddedAssets) {
|
embeddedBytes = contents.bytes;
|
||||||
_contentsResolved = true;
|
|
||||||
fileAsset.decode(contents.bytes);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@override
|
@override
|
||||||
bool resolve() {
|
bool resolve() {
|
||||||
if (!_contentsResolved) {
|
// allow our loader to load the file asset.
|
||||||
// try to get them out of band
|
assetLoader?.load(fileAsset, embeddedBytes).then((loaded) {
|
||||||
assetLoader?.load(fileAsset).then((loaded) {
|
if (!loaded && embeddedBytes != null) {
|
||||||
|
fileAsset.decode(embeddedBytes!);
|
||||||
|
} else if (!loaded) {
|
||||||
// TODO: improve error logging
|
// TODO: improve error logging
|
||||||
if (!loaded) {
|
printDebugMessage(
|
||||||
printDebugMessage(
|
'''Rive asset (${fileAsset.name}) was not able to load:
|
||||||
'''Rive asset (${fileAsset.name}) was not able to load:
|
|
||||||
- Unique file name: ${fileAsset.uniqueFilename}
|
- Unique file name: ${fileAsset.uniqueFilename}
|
||||||
- Asset id: ${fileAsset.id}''',
|
- Asset id: ${fileAsset.id}''',
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
}
|
|
||||||
return super.resolve();
|
return super.resolve();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -5,9 +5,6 @@ class StateMachineBool extends StateMachineBoolBase {
|
|||||||
@override
|
@override
|
||||||
void valueChanged(bool from, bool to) {}
|
void valueChanged(bool from, bool to) {}
|
||||||
|
|
||||||
@override
|
|
||||||
void publicChanged(bool from, bool to) {}
|
|
||||||
|
|
||||||
@override
|
@override
|
||||||
bool isValidType<T>() => T == bool;
|
bool isValidType<T>() => T == bool;
|
||||||
}
|
}
|
||||||
|
@ -18,7 +18,4 @@ abstract class StateMachineInput extends StateMachineInputBase {
|
|||||||
bool isValidType<T>() => false;
|
bool isValidType<T>() => false;
|
||||||
}
|
}
|
||||||
|
|
||||||
class _StateMachineUnknownInput extends StateMachineInput {
|
class _StateMachineUnknownInput extends StateMachineInput {}
|
||||||
@override
|
|
||||||
void publicChanged(bool from, bool to) {}
|
|
||||||
}
|
|
||||||
|
@ -4,7 +4,4 @@ export 'package:rive/src/generated/animation/state_machine_number_base.dart';
|
|||||||
class StateMachineNumber extends StateMachineNumberBase {
|
class StateMachineNumber extends StateMachineNumberBase {
|
||||||
@override
|
@override
|
||||||
void valueChanged(double from, double to) {}
|
void valueChanged(double from, double to) {}
|
||||||
|
|
||||||
@override
|
|
||||||
void publicChanged(bool from, bool to) {}
|
|
||||||
}
|
}
|
||||||
|
@ -4,9 +4,6 @@ export 'package:rive/src/generated/animation/state_machine_trigger_base.dart';
|
|||||||
class StateMachineTrigger extends StateMachineTriggerBase {
|
class StateMachineTrigger extends StateMachineTriggerBase {
|
||||||
void fire() {}
|
void fire() {}
|
||||||
|
|
||||||
@override
|
|
||||||
void publicChanged(bool from, bool to) {}
|
|
||||||
|
|
||||||
@override
|
@override
|
||||||
bool isValidType<T>() => T == bool;
|
bool isValidType<T>() => T == bool;
|
||||||
}
|
}
|
||||||
|
@ -113,6 +113,11 @@ class Artboard extends ArtboardBase with ShapePaintContainer {
|
|||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Find all components of a specific type.
|
||||||
|
Iterable<T> components<T>() {
|
||||||
|
return _components.whereType<T>();
|
||||||
|
}
|
||||||
|
|
||||||
@override
|
@override
|
||||||
Artboard get artboard => this;
|
Artboard get artboard => this;
|
||||||
|
|
||||||
|
@ -117,7 +117,7 @@ class NestedArtboard extends NestedArtboardBase {
|
|||||||
}
|
}
|
||||||
|
|
||||||
bool advance(double elapsedSeconds) {
|
bool advance(double elapsedSeconds) {
|
||||||
if (mountedArtboard == null) {
|
if (mountedArtboard == null || isCollapsed) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -30,7 +30,6 @@ import 'package:rive/src/rive_core/animation/state_machine_listener.dart';
|
|||||||
import 'package:rive/src/rive_core/animation/state_transition.dart';
|
import 'package:rive/src/rive_core/animation/state_transition.dart';
|
||||||
import 'package:rive/src/rive_core/artboard.dart';
|
import 'package:rive/src/rive_core/artboard.dart';
|
||||||
import 'package:rive/src/rive_core/assets/file_asset.dart';
|
import 'package:rive/src/rive_core/assets/file_asset.dart';
|
||||||
import 'package:rive/src/rive_core/assets/file_asset_contents.dart';
|
|
||||||
import 'package:rive/src/rive_core/assets/image_asset.dart';
|
import 'package:rive/src/rive_core/assets/image_asset.dart';
|
||||||
import 'package:rive/src/rive_core/backboard.dart';
|
import 'package:rive/src/rive_core/backboard.dart';
|
||||||
import 'package:rive/src/rive_core/component.dart';
|
import 'package:rive/src/rive_core/component.dart';
|
||||||
@ -154,9 +153,8 @@ class RiveFile {
|
|||||||
RiveFile._(
|
RiveFile._(
|
||||||
BinaryReader reader,
|
BinaryReader reader,
|
||||||
this.header,
|
this.header,
|
||||||
this._assetLoader, {
|
this._assetLoader,
|
||||||
bool loadEmbeddedAssets = true,
|
) {
|
||||||
}) {
|
|
||||||
/// Property fields table of contents
|
/// Property fields table of contents
|
||||||
final propertyToField = _propertyToFieldLookup(header);
|
final propertyToField = _propertyToFieldLookup(header);
|
||||||
|
|
||||||
@ -175,12 +173,7 @@ class RiveFile {
|
|||||||
}
|
}
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
// TODO: Question (Max): two options, either tell the fileAssetImporter,
|
|
||||||
// or simply skip the object. I think we should skip the object.
|
|
||||||
if (!loadEmbeddedAssets && object is FileAssetContentsBase) {
|
|
||||||
// suppress importing embedded assets
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
ImportStackObject? stackObject;
|
ImportStackObject? stackObject;
|
||||||
var stackType = object.coreType;
|
var stackType = object.coreType;
|
||||||
switch (object.coreType) {
|
switch (object.coreType) {
|
||||||
@ -247,7 +240,6 @@ class RiveFile {
|
|||||||
stackObject = FileAssetImporter(
|
stackObject = FileAssetImporter(
|
||||||
object as FileAsset,
|
object as FileAsset,
|
||||||
_assetLoader,
|
_assetLoader,
|
||||||
loadEmbeddedAssets: loadEmbeddedAssets,
|
|
||||||
);
|
);
|
||||||
stackType = FileAssetBase.typeKey;
|
stackType = FileAssetBase.typeKey;
|
||||||
break;
|
break;
|
||||||
@ -322,10 +314,10 @@ class RiveFile {
|
|||||||
///
|
///
|
||||||
/// Set [loadCdnAssets] to `false` to disable loading assets from the CDN.
|
/// Set [loadCdnAssets] to `false` to disable loading assets from the CDN.
|
||||||
///
|
///
|
||||||
/// Set [loadEmbeddedAssets] to `false` to disable loading embedded assets.
|
|
||||||
///
|
|
||||||
/// Whether an assets is embedded/cdn/referenced is determined by the Rive
|
/// Whether an assets is embedded/cdn/referenced is determined by the Rive
|
||||||
/// file - as set in the editor.
|
/// file - as set in the editor.
|
||||||
|
///
|
||||||
|
/// Loading assets documentation: https://help.rive.app/runtimes/loading-assets
|
||||||
/// {@endtemplate}
|
/// {@endtemplate}
|
||||||
///
|
///
|
||||||
/// Will throw [RiveFormatErrorException] if data is malformed. Will throw
|
/// Will throw [RiveFormatErrorException] if data is malformed. Will throw
|
||||||
@ -335,7 +327,6 @@ class RiveFile {
|
|||||||
@Deprecated('Use `assetLoader` instead.') FileAssetResolver? assetResolver,
|
@Deprecated('Use `assetLoader` instead.') FileAssetResolver? assetResolver,
|
||||||
FileAssetLoader? assetLoader,
|
FileAssetLoader? assetLoader,
|
||||||
bool loadCdnAssets = true,
|
bool loadCdnAssets = true,
|
||||||
bool loadEmbeddedAssets = true,
|
|
||||||
}) {
|
}) {
|
||||||
var reader = BinaryReader(bytes);
|
var reader = BinaryReader(bytes);
|
||||||
return RiveFile._(
|
return RiveFile._(
|
||||||
@ -347,7 +338,6 @@ class RiveFile {
|
|||||||
if (loadCdnAssets) CDNAssetLoader(),
|
if (loadCdnAssets) CDNAssetLoader(),
|
||||||
],
|
],
|
||||||
),
|
),
|
||||||
loadEmbeddedAssets: loadEmbeddedAssets,
|
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -365,7 +355,6 @@ class RiveFile {
|
|||||||
ByteData bytes, {
|
ByteData bytes, {
|
||||||
FileAssetLoader? assetLoader,
|
FileAssetLoader? assetLoader,
|
||||||
bool loadCdnAssets = true,
|
bool loadCdnAssets = true,
|
||||||
bool loadEmbeddedAssets = true,
|
|
||||||
}) async {
|
}) async {
|
||||||
if (!_initializedText) {
|
if (!_initializedText) {
|
||||||
/// If the file looks like needs the text runtime, let's load it.
|
/// If the file looks like needs the text runtime, let's load it.
|
||||||
@ -378,7 +367,6 @@ class RiveFile {
|
|||||||
bytes,
|
bytes,
|
||||||
assetLoader: assetLoader,
|
assetLoader: assetLoader,
|
||||||
loadCdnAssets: loadCdnAssets,
|
loadCdnAssets: loadCdnAssets,
|
||||||
loadEmbeddedAssets: loadEmbeddedAssets,
|
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -396,7 +384,6 @@ class RiveFile {
|
|||||||
AssetBundle? bundle,
|
AssetBundle? bundle,
|
||||||
FileAssetLoader? assetLoader,
|
FileAssetLoader? assetLoader,
|
||||||
bool loadCdnAssets = true,
|
bool loadCdnAssets = true,
|
||||||
bool loadEmbeddedAssets = true,
|
|
||||||
}) async {
|
}) async {
|
||||||
final bytes = await (bundle ?? rootBundle).load(
|
final bytes = await (bundle ?? rootBundle).load(
|
||||||
bundleKey,
|
bundleKey,
|
||||||
@ -406,7 +393,6 @@ class RiveFile {
|
|||||||
bytes,
|
bytes,
|
||||||
assetLoader: assetLoader,
|
assetLoader: assetLoader,
|
||||||
loadCdnAssets: loadCdnAssets,
|
loadCdnAssets: loadCdnAssets,
|
||||||
loadEmbeddedAssets: loadEmbeddedAssets,
|
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -424,7 +410,6 @@ class RiveFile {
|
|||||||
@Deprecated('Use `assetLoader` instead.') FileAssetResolver? assetResolver,
|
@Deprecated('Use `assetLoader` instead.') FileAssetResolver? assetResolver,
|
||||||
FileAssetLoader? assetLoader,
|
FileAssetLoader? assetLoader,
|
||||||
bool loadCdnAssets = true,
|
bool loadCdnAssets = true,
|
||||||
bool loadEmbeddedAssets = true,
|
|
||||||
}) async {
|
}) async {
|
||||||
final res = await http.get(Uri.parse(url), headers: headers);
|
final res = await http.get(Uri.parse(url), headers: headers);
|
||||||
final bytes = ByteData.view(res.bodyBytes.buffer);
|
final bytes = ByteData.view(res.bodyBytes.buffer);
|
||||||
@ -432,7 +417,6 @@ class RiveFile {
|
|||||||
bytes,
|
bytes,
|
||||||
assetLoader: assetLoader,
|
assetLoader: assetLoader,
|
||||||
loadCdnAssets: loadCdnAssets,
|
loadCdnAssets: loadCdnAssets,
|
||||||
loadEmbeddedAssets: loadEmbeddedAssets,
|
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -446,13 +430,11 @@ class RiveFile {
|
|||||||
String path, {
|
String path, {
|
||||||
FileAssetLoader? assetLoader,
|
FileAssetLoader? assetLoader,
|
||||||
bool loadCdnAssets = true,
|
bool loadCdnAssets = true,
|
||||||
bool loadEmbeddedAssets = true,
|
|
||||||
}) async {
|
}) async {
|
||||||
final bytes = await localFileBytes(path);
|
final bytes = await localFileBytes(path);
|
||||||
return _initTextAndImport(
|
return _initTextAndImport(
|
||||||
ByteData.view(bytes!.buffer),
|
ByteData.view(bytes!.buffer),
|
||||||
assetLoader: assetLoader,
|
assetLoader: assetLoader,
|
||||||
loadEmbeddedAssets: loadEmbeddedAssets,
|
|
||||||
loadCdnAssets: loadCdnAssets,
|
loadCdnAssets: loadCdnAssets,
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
@ -1,5 +1,5 @@
|
|||||||
name: rive
|
name: rive
|
||||||
version: 0.11.17
|
version: 0.12.0
|
||||||
homepage: https://rive.app
|
homepage: https://rive.app
|
||||||
description: Rive 2 Flutter Runtime. This package provides runtime functionality for playing back and interacting with animations built with the Rive editor available at https://rive.app.
|
description: Rive 2 Flutter Runtime. This package provides runtime functionality for playing back and interacting with animations built with the Rive editor available at https://rive.app.
|
||||||
repository: https://github.com/rive-app/rive-flutter
|
repository: https://github.com/rive-app/rive-flutter
|
||||||
|
@ -46,25 +46,26 @@ void main() {
|
|||||||
);
|
);
|
||||||
|
|
||||||
// each artboard adds file asset referencer.
|
// each artboard adds file asset referencer.
|
||||||
var count = 20;
|
var count = 19;
|
||||||
while (count-- > 0) {
|
while (count-- > 0) {
|
||||||
riveFile.artboards.first.instance();
|
riveFile.artboards.first.instance();
|
||||||
}
|
}
|
||||||
|
|
||||||
final image = riveFile.artboards.first.component<Image>(assetName);
|
final image = riveFile.artboards.first.component<Image>(assetName);
|
||||||
final asset = image!.asset!;
|
final asset = image!.asset!;
|
||||||
expect(asset.fileAssetReferencers.length, 21);
|
expect(asset.fileAssetReferencers.length, 20);
|
||||||
await Future<void>.delayed(const Duration(milliseconds: 10));
|
await Future<void>.delayed(const Duration(milliseconds: 100));
|
||||||
// ok, kinda lame, but the above allows garbage collection to kick in
|
// ok, kinda lame, but the above allows garbage collection to kick in
|
||||||
// which will remove referencers, its not really deterministic though
|
// which will remove referencers, its not really deterministic though
|
||||||
|
|
||||||
expect(
|
expect(
|
||||||
asset.fileAssetReferencers.length < 5,
|
asset.fileAssetReferencers.length < 5,
|
||||||
true,
|
true,
|
||||||
reason: "Expected ${asset.fileAssetReferencers.length} < 5",
|
reason: "Expected ${asset.fileAssetReferencers.length} < 5",
|
||||||
);
|
);
|
||||||
},
|
},
|
||||||
// skipping because it only works when we run this test directly,
|
// skipping because it does not work, you can see things get
|
||||||
// not when running it as part of other tests.
|
// finalized but this does not consistently happen in tests.
|
||||||
skip: true,
|
skip: true,
|
||||||
);
|
);
|
||||||
});
|
});
|
||||||
|
@ -35,22 +35,6 @@ void main() {
|
|||||||
verifyNever(() => mockHttpClient.openUrl(any(), any()));
|
verifyNever(() => mockHttpClient.openUrl(any(), any()));
|
||||||
});
|
});
|
||||||
|
|
||||||
test('Disabling embedded assets also does not hit a url', () async {
|
|
||||||
final mockHttpClient = getMockHttpClient();
|
|
||||||
|
|
||||||
await HttpOverrides.runZoned(() async {
|
|
||||||
final riveBytes = loadFile('assets/sample_image.riv');
|
|
||||||
|
|
||||||
RiveFile.import(
|
|
||||||
riveBytes,
|
|
||||||
loadEmbeddedAssets: false,
|
|
||||||
);
|
|
||||||
}, createHttpClient: (_) => mockHttpClient);
|
|
||||||
|
|
||||||
// by default we try to make a network request
|
|
||||||
verifyNever(() => mockHttpClient.openUrl(any(), any()));
|
|
||||||
});
|
|
||||||
|
|
||||||
test('Disabling cdn also does not hit a url', () async {
|
test('Disabling cdn also does not hit a url', () async {
|
||||||
final mockHttpClient = getMockHttpClient();
|
final mockHttpClient = getMockHttpClient();
|
||||||
|
|
||||||
@ -59,7 +43,6 @@ void main() {
|
|||||||
runZonedGuarded(() {
|
runZonedGuarded(() {
|
||||||
RiveFile.import(
|
RiveFile.import(
|
||||||
riveBytes,
|
riveBytes,
|
||||||
loadEmbeddedAssets: false,
|
|
||||||
loadCdnAssets: false,
|
loadCdnAssets: false,
|
||||||
);
|
);
|
||||||
}, (error, stack) {
|
}, (error, stack) {
|
||||||
@ -76,16 +59,18 @@ void main() {
|
|||||||
verifyNever(() => mockHttpClient.openUrl(any(), any()));
|
verifyNever(() => mockHttpClient.openUrl(any(), any()));
|
||||||
// by default we try to check for assets
|
// by default we try to check for assets
|
||||||
});
|
});
|
||||||
|
|
||||||
test('test importing rive file, make sure we get a good callback',
|
test('test importing rive file, make sure we get a good callback',
|
||||||
() async {
|
() async {
|
||||||
// lets just return an image
|
// lets just return an image
|
||||||
final riveBytes = loadFile('assets/sample_image.riv');
|
final riveBytes = loadFile('assets/sample_image.riv');
|
||||||
final imageBytes = loadFile('assets/file.png');
|
final imageBytes = loadFile('assets/file.png');
|
||||||
final parameters = [];
|
final assets = [];
|
||||||
RiveFile.import(riveBytes, loadEmbeddedAssets: false,
|
final byteList = [];
|
||||||
assetLoader: CallbackAssetLoader(
|
RiveFile.import(riveBytes, assetLoader: CallbackAssetLoader(
|
||||||
(asset) async {
|
(asset, bytes) async {
|
||||||
parameters.add(asset);
|
assets.add(asset);
|
||||||
|
byteList.add(bytes);
|
||||||
await asset.decode(Uint8List.sublistView(
|
await asset.decode(Uint8List.sublistView(
|
||||||
imageBytes,
|
imageBytes,
|
||||||
));
|
));
|
||||||
@ -93,7 +78,7 @@ void main() {
|
|||||||
},
|
},
|
||||||
));
|
));
|
||||||
|
|
||||||
final asset = parameters.first;
|
final asset = assets.first;
|
||||||
|
|
||||||
expect(asset is ImageAsset, true);
|
expect(asset is ImageAsset, true);
|
||||||
final fileAsset = asset as ImageAsset;
|
final fileAsset = asset as ImageAsset;
|
||||||
@ -101,7 +86,38 @@ void main() {
|
|||||||
expect(fileAsset.type, Type.image);
|
expect(fileAsset.type, Type.image);
|
||||||
expect(fileAsset.name, assetName);
|
expect(fileAsset.name, assetName);
|
||||||
expect(fileAsset.assetId, 42981);
|
expect(fileAsset.assetId, 42981);
|
||||||
|
|
||||||
expect(fileAsset.id, -1);
|
expect(fileAsset.id, -1);
|
||||||
|
|
||||||
|
expect(byteList.first.length, 202385);
|
||||||
|
});
|
||||||
|
|
||||||
|
test('test we load embedded assets if loaders are not provided', () async {
|
||||||
|
// lets just return an image
|
||||||
|
final riveBytes = loadFile('assets/sample_image.riv');
|
||||||
|
|
||||||
|
final assets = [];
|
||||||
|
RiveFile.import(riveBytes, assetLoader: CallbackAssetLoader(
|
||||||
|
(asset, bytes) async {
|
||||||
|
assets.add(asset);
|
||||||
|
return false;
|
||||||
|
},
|
||||||
|
));
|
||||||
|
|
||||||
|
final asset = assets.first;
|
||||||
|
|
||||||
|
expect(asset is ImageAsset, true);
|
||||||
|
final fileAsset = asset as ImageAsset;
|
||||||
|
expect(fileAsset.extension, Extension.png);
|
||||||
|
expect(fileAsset.type, Type.image);
|
||||||
|
expect(fileAsset.name, assetName);
|
||||||
|
expect(fileAsset.assetId, 42981);
|
||||||
|
// file asset will not be loaded
|
||||||
|
expect(fileAsset.image, null);
|
||||||
|
|
||||||
|
expect(fileAsset.id, -1);
|
||||||
|
await Future<void>.delayed(const Duration(milliseconds: 100));
|
||||||
|
expect(fileAsset.image != null, true);
|
||||||
});
|
});
|
||||||
|
|
||||||
test('Make sure the image gets the dimensions once the image is loaded',
|
test('Make sure the image gets the dimensions once the image is loaded',
|
||||||
@ -113,9 +129,8 @@ void main() {
|
|||||||
|
|
||||||
final file = RiveFile.import(
|
final file = RiveFile.import(
|
||||||
riveBytes,
|
riveBytes,
|
||||||
loadEmbeddedAssets: false,
|
|
||||||
assetLoader: CallbackAssetLoader(
|
assetLoader: CallbackAssetLoader(
|
||||||
(asset) async {
|
(asset, bytes) async {
|
||||||
await asset.decode(Uint8List.sublistView(
|
await asset.decode(Uint8List.sublistView(
|
||||||
imageBytes,
|
imageBytes,
|
||||||
));
|
));
|
||||||
@ -159,30 +174,12 @@ void main() {
|
|||||||
)).called(1);
|
)).called(1);
|
||||||
});
|
});
|
||||||
|
|
||||||
test('Disabling embedded assets also hits a url', () async {
|
|
||||||
await HttpOverrides.runZoned(() async {
|
|
||||||
final riveBytes = loadFile('assets/cdn_image.riv');
|
|
||||||
runZonedGuarded(() {
|
|
||||||
RiveFile.import(
|
|
||||||
riveBytes,
|
|
||||||
loadEmbeddedAssets: false,
|
|
||||||
);
|
|
||||||
}, (error, stack) {
|
|
||||||
print('what?');
|
|
||||||
});
|
|
||||||
}, createHttpClient: (_) => mockHttpClient);
|
|
||||||
|
|
||||||
// by default we try to make a network request
|
|
||||||
verify(() => mockHttpClient.openUrl(any(), any())).called(1);
|
|
||||||
});
|
|
||||||
|
|
||||||
test('Disabling cdn will mean no url hit', () async {
|
test('Disabling cdn will mean no url hit', () async {
|
||||||
await HttpOverrides.runZoned(() async {
|
await HttpOverrides.runZoned(() async {
|
||||||
final riveBytes = loadFile('assets/cdn_image.riv');
|
final riveBytes = loadFile('assets/cdn_image.riv');
|
||||||
|
|
||||||
RiveFile.import(
|
RiveFile.import(
|
||||||
riveBytes,
|
riveBytes,
|
||||||
loadEmbeddedAssets: false,
|
|
||||||
loadCdnAssets: false,
|
loadCdnAssets: false,
|
||||||
);
|
);
|
||||||
}, createHttpClient: (_) => mockHttpClient);
|
}, createHttpClient: (_) => mockHttpClient);
|
||||||
@ -199,10 +196,8 @@ void main() {
|
|||||||
final parameters = [];
|
final parameters = [];
|
||||||
await HttpOverrides.runZoned(() async {
|
await HttpOverrides.runZoned(() async {
|
||||||
final riveBytes = loadFile('assets/cdn_image.riv');
|
final riveBytes = loadFile('assets/cdn_image.riv');
|
||||||
|
RiveFile.import(riveBytes, assetLoader: CallbackAssetLoader(
|
||||||
RiveFile.import(riveBytes, loadEmbeddedAssets: false,
|
(asset, bytes) async {
|
||||||
assetLoader: CallbackAssetLoader(
|
|
||||||
(asset) async {
|
|
||||||
parameters.add(asset);
|
parameters.add(asset);
|
||||||
await asset.decode(Uint8List.sublistView(
|
await asset.decode(Uint8List.sublistView(
|
||||||
imageBytes,
|
imageBytes,
|
||||||
@ -226,9 +221,8 @@ void main() {
|
|||||||
await HttpOverrides.runZoned(() async {
|
await HttpOverrides.runZoned(() async {
|
||||||
final riveBytes = loadFile('assets/cdn_image.riv');
|
final riveBytes = loadFile('assets/cdn_image.riv');
|
||||||
|
|
||||||
RiveFile.import(riveBytes, loadEmbeddedAssets: false,
|
RiveFile.import(riveBytes, assetLoader: CallbackAssetLoader(
|
||||||
assetLoader: CallbackAssetLoader(
|
(asset, bytes) async {
|
||||||
(asset) async {
|
|
||||||
parameters.add(asset);
|
parameters.add(asset);
|
||||||
return false;
|
return false;
|
||||||
},
|
},
|
||||||
|
Reference in New Issue
Block a user