Fix loading init text in Flutter runtime.

The important work is done in _initTextAndImport. This functionality needs to be called by the user if they call RiveFile.import. See the changelog for some notes.

I needed to bump rive_common for this to work as we manually added our fixRequireJs fix to the HTML. We should always try to inject functionality that's required at runtime dynamically so it can apply to all apps (not just our editor).

Diffs=
d072cbde0 Fix loading init text in Flutter runtime. (#5758)

Co-authored-by: Luigi Rosso <luigi-rosso@users.noreply.github.com>
This commit is contained in:
luigi-rosso
2023-08-07 18:55:50 +00:00
parent a2bb7294c6
commit 7bf4ec8fda
5 changed files with 102 additions and 22 deletions

View File

@ -1 +1 @@
133fe44e47d37375d2a349437060e407bb2db1cb
d072cbde0977cca635db6427795b36aea914b82d

View File

@ -1,3 +1,8 @@
## 0.11.13
- Initializes Rive's text engine only when necessary when calling any of `RiveFile.asset`, `RiveFile.network`, or `RiveFile.file`.
- You'll need to manually call `RiveFile.initializeText` when calling `RiveFile.import` directly to use text features. You can optionally only call this if you know the file needs the text engine, or you can determine if it needs it by calling `RiveFile.needsTextRuntime`.
## 0.11.12
- Fixes a memory leak in the text engine.

View File

@ -1,12 +1,12 @@
name: rive_example
description: A collection of Rive Flutter examples
publish_to: "none" # Remove this line if you wish to publish to pub.dev
publish_to: 'none' # Remove this line if you wish to publish to pub.dev
version: 1.0.0+1
environment:
sdk: ">=2.17.0 <3.0.0"
sdk: '>=2.17.0 <3.0.0'
dependencies:
flutter:

View File

@ -13,6 +13,7 @@ import 'package:rive/src/generated/animation/entry_state_base.dart';
import 'package:rive/src/generated/animation/exit_state_base.dart';
import 'package:rive/src/generated/assets/font_asset_base.dart';
import 'package:rive/src/generated/nested_artboard_base.dart';
import 'package:rive/src/generated/text/text_base.dart';
import 'package:rive/src/local_file_io.dart'
if (dart.library.html) 'package:rive/src/local_file_web.dart';
import 'package:rive/src/rive_core/animation/blend_state_1d.dart';
@ -35,6 +36,7 @@ import 'package:rive/src/rive_core/component.dart';
import 'package:rive/src/rive_core/runtime/exceptions/rive_format_error_exception.dart';
import 'package:rive/src/rive_core/runtime/runtime_header.dart';
import 'package:rive/src/runtime_nested_artboard.dart';
import 'package:rive_common/rive_text.dart';
import 'package:rive_common/utilities.dart';
@ -70,6 +72,22 @@ Core<CoreContext>? _readRuntimeObject(
return object;
}
int _peekRuntimeObjectType(
BinaryReader reader, HashMap<int, CoreFieldType> propertyToField) {
int coreObjectKey = reader.readVarUint();
while (true) {
int propertyKey = reader.readVarUint();
if (propertyKey == 0) {
// Terminator. https://media.giphy.com/media/7TtvTUMm9mp20/giphy.gif
break;
}
_skipProperty(reader, propertyKey, propertyToField);
}
return coreObjectKey;
}
void _skipProperty(BinaryReader reader, int propertyKey,
HashMap<int, CoreFieldType> propertyToField) {
var field =
@ -91,23 +109,19 @@ class RiveFile {
final _artboards = <Artboard>[];
final FileAssetLoader? _assetLoader;
RiveFile._(
BinaryReader reader,
this.header,
this._assetLoader, {
bool loadEmbeddedAssets = true,
}) {
// List of core file types
static final indexToField = <CoreFieldType>[
RiveCoreContext.uintType,
RiveCoreContext.stringType,
RiveCoreContext.doubleType,
RiveCoreContext.colorType
];
static HashMap<int, CoreFieldType> _propertyToFieldLookup(
RuntimeHeader header) {
/// Property fields table of contents
final propertyToField = HashMap<int, CoreFieldType>();
// List of core file types
final indexToField = <CoreFieldType>[
RiveCoreContext.uintType,
RiveCoreContext.stringType,
RiveCoreContext.doubleType,
RiveCoreContext.colorType
];
header.propertyToFieldIndex.forEach((key, fieldIndex) {
if (fieldIndex < 0 || fieldIndex >= indexToField.length) {
throw RiveFormatErrorException('unexpected field index $fieldIndex');
@ -115,6 +129,36 @@ class RiveFile {
propertyToField[key] = indexToField[fieldIndex];
});
return propertyToField;
}
// Peek into the bytes to see if we're going to need to use the text runtime.
static bool needsTextRuntime(ByteData bytes) {
var reader = BinaryReader(bytes);
var header = RuntimeHeader.read(reader);
/// Property fields table of contents
final propertyToField = _propertyToFieldLookup(header);
while (!reader.isEOF) {
final coreType = _peekRuntimeObjectType(reader, propertyToField);
switch (coreType) {
case TextBase.typeKey:
return true;
}
}
return false;
}
RiveFile._(
BinaryReader reader,
this.header,
this._assetLoader, {
bool loadEmbeddedAssets = true,
}) {
/// Property fields table of contents
final propertyToField = _propertyToFieldLookup(header);
int artboardId = 0;
var artboardLookup = HashMap<int, Artboard>();
var importStack = ImportStack();
@ -298,6 +342,37 @@ class RiveFile {
);
}
static bool _initializedText = false;
/// Initialize Rive's text engine if it hasn't been yet.
static Future<void> initializeText() async {
if (!_initializedText) {
await Font.initialize();
_initializedText = true;
}
}
static Future<RiveFile> _initTextAndImport(
ByteData bytes, {
FileAssetLoader? assetLoader,
bool loadCdnAssets = true,
bool loadEmbeddedAssets = true,
}) async {
if (!_initializedText) {
/// If the file looks like needs the text runtime, let's load it.
if (RiveFile.needsTextRuntime(bytes)) {
await Font.initialize();
_initializedText = true;
}
}
return RiveFile.import(
bytes,
assetLoader: assetLoader,
loadCdnAssets: loadCdnAssets,
loadEmbeddedAssets: loadEmbeddedAssets,
);
}
/// Imports a Rive file from an asset bundle.
///
/// Default uses [rootBundle] from Flutter. Provide a custom [bundle] to load
@ -318,7 +393,7 @@ class RiveFile {
bundleKey,
);
return RiveFile.import(
return _initTextAndImport(
bytes,
assetLoader: assetLoader,
loadCdnAssets: loadCdnAssets,
@ -344,7 +419,7 @@ class RiveFile {
}) async {
final res = await http.get(Uri.parse(url), headers: headers);
final bytes = ByteData.view(res.bodyBytes.buffer);
return RiveFile.import(
return _initTextAndImport(
bytes,
assetLoader: assetLoader,
loadCdnAssets: loadCdnAssets,
@ -365,7 +440,7 @@ class RiveFile {
bool loadEmbeddedAssets = true,
}) async {
final bytes = await localFileBytes(path);
return RiveFile.import(
return _initTextAndImport(
ByteData.view(bytes!.buffer),
assetLoader: assetLoader,
loadEmbeddedAssets: loadEmbeddedAssets,

View File

@ -1,5 +1,5 @@
name: rive
version: 0.11.12
version: 0.11.13
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.
repository: https://github.com/rive-app/rive-flutter
@ -21,7 +21,7 @@ dependencies:
http: ^1.1.0
meta: ^1.3.0
plugin_platform_interface: ^2.0.2
rive_common: 0.2.4
rive_common: 0.2.5
dev_dependencies:
flutter_test:
sdk: flutter