Files
rive-flutter/example/lib/custom_cached_asset_loading.dart
HayesGordon ca712600bc refactor: asset resolving
Adds some cleanup to this previous PR: https://github.com/rive-app/rive/pull/5411/files

- Brings back some classes and parameters and mark them as deprecated.
- Changed the naming for parameters
- Removed asset resolving from the high level `RiveAnimation` widget, prefer to manage this by loading in your own `RiveFile`. This is to avoid introducing too many changes that we may revert down the line
- Removed code comments that were intended as questions, marked some as TODOs
- Improved documentation and cleaned up example

The cached asset example now has a button to hot swap out assets at runtime by keeping a reference to the asset. This works for images, but not for Fonts.
- We can either remove this example for the time being
- Or investigate why it does not swap out (I would expect it to)

Diffs=
94e3490ae refactor: asset resolving (#5563)
bae069339 Stop automatically pruning empty segments in RawPath (#5557)
2d2d8c413 Line Height & Paragraph Spacing (#5552)

Co-authored-by: Gordon <pggordonhayes@gmail.com>
2023-07-13 14:06:41 +00:00

287 lines
7.2 KiB
Dart

// ignore_for_file: public_member_api_docs, sort_constructors_first
import 'dart:math';
import 'dart:typed_data';
import 'package:flutter/material.dart';
import 'package:http/http.dart' as http;
import 'package:rive/rive.dart';
/// An example showing how to load image or font assets dynamically.
///
/// In this example there is no delay in the assets loading, as they are
/// cached in memory.
///
/// The example also shows how to swap out the assets multiple times by
/// keeping a reference to the asset and swapping it out.
class CustomCachedAssetLoading extends StatefulWidget {
const CustomCachedAssetLoading({Key? key}) : super(key: key);
@override
State<CustomCachedAssetLoading> createState() =>
_CustomCachedAssetLoadingState();
}
class _CustomCachedAssetLoadingState extends State<CustomCachedAssetLoading> {
var _index = 0;
var _ready = false;
final _imageCache = [];
final _fontCache = [];
@override
void initState() {
super.initState();
_warmUpCache();
}
/// Create a cache of images and fonts to swap out instantly.
Future<void> _warmUpCache() async {
final futures = <Future>[];
loadImage() async {
final res = await http.get(Uri.parse('https://picsum.photos/1000/1000'));
final body = Uint8List.view(res.bodyBytes.buffer);
final image = await ImageAsset.parseBytes(body);
if (image != null) {
_imageCache.add(image);
}
}
loadFont(url) async {
final res = await http.get(Uri.parse(url));
final body = Uint8List.view(res.bodyBytes.buffer);
final font = await FontAsset.parseBytes(body);
if (font != null) {
_fontCache.add(font);
}
}
for (var i = 0; i <= 10; i++) {
futures.add(loadImage());
}
for (var url in [
'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',
]) {
futures.add(loadFont(url));
}
await Future.wait(futures);
setState(() => _ready = true);
}
void next() {
setState(() => _index += 1);
}
void previous() {
setState(() => _index -= 1);
}
@override
Widget build(BuildContext context) {
if (!_ready) {
return const Scaffold(
body: Center(
child: Column(
mainAxisSize: MainAxisSize.min,
children: [
Padding(
padding: EdgeInsets.all(8.0),
child: Text('Warming up cache. Loading files from network...'),
),
CircularProgressIndicator(
strokeWidth: 2,
),
],
),
),
);
}
return Scaffold(
appBar: AppBar(
title: const Text('Custom Cached Asset Loading'),
),
body: Center(
child: Row(
children: [
GestureDetector(
onTap: previous,
child: const Icon(Icons.arrow_back),
),
Expanded(
child: (_index % 2 == 0)
? _RiveRandomCachedImage(imageCache: _imageCache)
: _RiveRandomCachedFont(fontCache: _fontCache),
),
GestureDetector(
onTap: next,
child: const Icon(Icons.arrow_forward),
),
],
),
),
);
}
}
class _RiveRandomCachedImage extends StatefulWidget {
const _RiveRandomCachedImage({
Key? key,
required this.imageCache,
}) : super(key: key);
final List imageCache;
@override
State<_RiveRandomCachedImage> createState() => __RiveRandomCachedImageState();
}
class __RiveRandomCachedImageState extends State<_RiveRandomCachedImage> {
List get _imageCache => widget.imageCache;
@override
void initState() {
super.initState();
_loadRiveFile();
}
RiveFile? _riveImageSampleFile;
// A reference to the Rive image. Can be use to swap out the image at any
// point.
ImageAsset? _imageAsset;
Future<void> _loadRiveFile() async {
final imageFile = await RiveFile.asset(
'assets/asset.riv',
loadEmbeddedAssets: false,
assetLoader: CallbackAssetLoader(
(asset) async {
if (asset is ImageAsset) {
asset.image = _imageCache[Random().nextInt(_imageCache.length)];
// Maintain a reference to the image asset
// so we can swap it out later instantly.
_imageAsset = asset;
return true;
}
return false;
},
),
);
setState(() => _riveImageSampleFile = imageFile);
}
@override
Widget build(BuildContext context) {
if (_riveImageSampleFile == null) {
return const Center(child: CircularProgressIndicator());
}
return Column(
children: [
Expanded(
child: RiveAnimation.direct(
_riveImageSampleFile!,
fit: BoxFit.cover,
),
),
Padding(
padding: const EdgeInsets.all(8.0),
child: ElevatedButton(
onPressed: () {
_imageAsset?.image =
_imageCache[Random().nextInt(_imageCache.length)];
},
child: const Text('Random image asset'),
),
),
],
);
}
}
class _RiveRandomCachedFont extends StatefulWidget {
const _RiveRandomCachedFont({
Key? key,
required this.fontCache,
}) : super(key: key);
final List fontCache;
@override
State<_RiveRandomCachedFont> createState() => __RiveRandomCachedFontState();
}
class __RiveRandomCachedFontState extends State<_RiveRandomCachedFont> {
List get _fontCache => widget.fontCache;
@override
void initState() {
super.initState();
_loadRiveFile();
}
RiveFile? _riveFontSampleFile;
final List<FontAsset?> _fontAssets = [];
Future<void> _loadRiveFile() async {
final fontFile = await RiveFile.asset(
'assets/sampletext.riv',
loadEmbeddedAssets: false,
assetLoader: CallbackAssetLoader(
(asset) async {
if (asset is FontAsset) {
// TODO: setting this will then not allow later updates to the font
// asset.font = _fontCache[Random().nextInt(_fontCache.length)];
_fontAssets.add(asset);
return true;
}
return false;
},
),
);
setState(() {
_riveFontSampleFile = fontFile;
});
}
@override
Widget build(BuildContext context) {
if (_riveFontSampleFile == null) {
return const Center(child: CircularProgressIndicator());
}
return Column(
children: [
Expanded(
child: RiveAnimation.direct(
_riveFontSampleFile!,
fit: BoxFit.cover,
),
),
Padding(
padding: const EdgeInsets.all(8.0),
child: ElevatedButton(
onPressed: () {
for (var element in _fontAssets) {
element?.font = _fontCache[Random().nextInt(_fontCache.length)];
}
},
child: const Text('Random font asset'),
),
),
],
);
}
}