diff --git a/assets/rive/game_button.bk.riv b/assets/rive/game_button.bk.riv new file mode 100644 index 0000000..784de01 Binary files /dev/null and b/assets/rive/game_button.bk.riv differ diff --git a/assets/rive/game_button.riv b/assets/rive/game_button.riv index 784de01..c2f2bd6 100644 Binary files a/assets/rive/game_button.riv and b/assets/rive/game_button.riv differ diff --git a/lib/bootstrap.dart b/lib/bootstrap.dart index 1bcf5bc..3203a7f 100644 --- a/lib/bootstrap.dart +++ b/lib/bootstrap.dart @@ -31,11 +31,12 @@ Future bootstrap(FutureOr Function() builder) async { Bloc.observer = const AppBlocObserver(); // Add cross-flavor configuration here + WidgetsFlutterBinding.ensureInitialized(); HydratedBloc.storage = await HydratedStorage.build( storageDirectory: kIsWeb ? HydratedStorage.webStorageDirectory : await getApplicationDocumentsDirectory(), ); - unawaited(RiveFile.initialize()); + unawaited(RiveNative.init()); runApp(await builder()); } diff --git a/lib/core/common/widgets/game_button.dart b/lib/core/common/widgets/game_button.dart index 8ab4a92..920caf8 100644 --- a/lib/core/common/widgets/game_button.dart +++ b/lib/core/common/widgets/game_button.dart @@ -4,7 +4,6 @@ import 'package:flutter/material.dart'; import 'package:flutter/services.dart'; import 'package:rive/rive.dart'; -import 'package:sozzle/core/extensions/rive_extensions.dart'; import 'package:sozzle/core/res/media.dart'; class GameButton extends StatefulWidget { @@ -18,9 +17,9 @@ class GameButton extends StatefulWidget { } class _GameButtonState extends State { - RiveFile? _riveFile; + File? _riveFile; - StateMachineController? controller; + late RiveWidgetController controller; bool clicked = false; @@ -32,15 +31,34 @@ class _GameButtonState extends State { @override void dispose() { - controller?.dispose(); + controller.dispose(); super.dispose(); } - void _preload() { - rootBundle.load(Media.gameButton).then((data) { - setState(() { - _riveFile = RiveFile.import(data); - }); + Future _preload() async { + final data = await rootBundle.load(Media.gameButton); + _riveFile = await File.decode( + data.buffer.asUint8List(), + riveFactory: Factory.rive, + ); + controller = RiveWidgetController(_riveFile!); + _onInit(controller.artboard); + setState(() {}); + } + + void _onInit(Artboard artboard) { + artboard.setText('buttonText', widget.text); + + final viewModelInstance = controller.dataBind(DataBind.auto()); + + final currentState = viewModelInstance.string('currentState'); + currentState?.addListener((value) { + if (value == 'click') { + clicked = true; + } else if (value == 'rest' && clicked) { + clicked = false; + widget.onPressed?.call(); + } }); } @@ -51,26 +69,11 @@ class _GameButtonState extends State { child: SizedBox( width: 200, height: 50, - child: RiveAnimation.direct( - _riveFile!, - fit: BoxFit.cover, - stateMachines: const ['Button Animation'], - onInit: (artboard) { - artboard.textRun('buttonText')!.text = widget.text; - controller = StateMachineController.fromArtboard( - artboard, - 'Button Animation', - onStateChange: (stateMachine, stateName) { - if (stateName == 'Click') { - clicked = true; - } else if (stateName == 'Rest' && clicked) { - clicked = false; - widget.onPressed?.call(); - } - }, - ); - artboard.addController(controller!); - }, + child: RiveWidget( + controller: controller, + fit: Fit.cover, + cursor: SystemMouseCursors.click, + // stateMachines: const ['Button Animation'], ), ), ); diff --git a/lib/core/extensions/rive_extensions.dart b/lib/core/extensions/rive_extensions.dart deleted file mode 100644 index 4e43a25..0000000 --- a/lib/core/extensions/rive_extensions.dart +++ /dev/null @@ -1,6 +0,0 @@ -// coverage:ignore-file -import 'package:rive/rive.dart'; - -extension TextExtension on Artboard { - TextValueRun? textRun(String name) => component(name); -} diff --git a/lib/src/game_play/view/components/hint.dart b/lib/src/game_play/view/components/hint.dart index 3a28afb..4970864 100644 --- a/lib/src/game_play/view/components/hint.dart +++ b/lib/src/game_play/view/components/hint.dart @@ -21,9 +21,12 @@ class Hint extends StatefulWidget { } class _HintState extends State { - RiveFile? _hintFile; - SMIBool? _luminance; - SMIBool? _theme; + File? _hintFile; + late RiveWidgetController _controller; + late StateMachine? _stateController; + late StateMachine? _themeController; + BooleanInput? _luminance; + BooleanInput? _theme; @override void initState() { @@ -31,30 +34,24 @@ class _HintState extends State { preload(); } - void preload() { - rootBundle.load(Media.animatedHint).then((data) { - setState(() { - _hintFile = RiveFile.import(data); - }); - }); + Future preload() async { + final data = await rootBundle.load(Media.animatedHint); + _hintFile = await File.decode( + data.buffer.asUint8List(), + riveFactory: Factory.rive, + ); + _controller = RiveWidgetController(_hintFile!); + _onInit(_controller.artboard); + setState(() {}); } void _onInit(Artboard artboard) { - final stateController = StateMachineController.fromArtboard( - artboard, - 'bulb', - ); - final themeController = StateMachineController.fromArtboard( - artboard, - 'theme', - ); - artboard - ..addController(stateController!) - ..addController(themeController!); - _luminance = stateController.findInput('pressed')! as SMIBool; - _theme = themeController.findInput('isDark')! as SMIBool; + _stateController = artboard.stateMachine('bulb'); + _themeController = artboard.stateMachine('theme'); + _luminance = _stateController?.boolean('pressed'); + _theme = _themeController?.boolean('isDark'); flipBulbByBooster(context.read().state); - _theme?.change(context.read().state is ThemeStateDark); + _theme?.value = context.read().state is ThemeStateDark; } void flipBulbByBooster(UserStatsState statsState) { @@ -62,12 +59,22 @@ class _HintState extends State { (booster) => booster is UseAHint && booster.boosterCount > 0, ); if (userHasHint) { - _luminance?.change(true); + _luminance?.value = true; } else { - _luminance?.change(false); + _luminance?.value = false; } } + @override + void dispose() { + _controller.dispose(); + _stateController?.dispose(); + _themeController?.dispose(); + _luminance?.dispose(); + _theme?.dispose(); + super.dispose(); + } + @override Widget build(BuildContext context) { if (_hintFile == null) return const SizedBox.shrink(); @@ -75,7 +82,7 @@ class _HintState extends State { message: 'Use a hint', child: BlocConsumer( listener: (context, themeState) { - _theme?.change(themeState is ThemeStateDark); + _theme?.value = themeState is ThemeStateDark; }, builder: (context, themeState) { return BlocConsumer( @@ -123,11 +130,10 @@ class _HintState extends State { baseline: 45, child: SizedBox( width: 50, - child: RiveAnimation.direct( + child: RiveWidget( key: UniqueKey(), - _hintFile!, - fit: BoxFit.cover, - onInit: _onInit, + controller: _controller, + fit: Fit.cover, ), ), ), diff --git a/lib/src/level_won/view/level_complete_page.dart b/lib/src/level_won/view/level_complete_page.dart index e852d09..731a37b 100644 --- a/lib/src/level_won/view/level_complete_page.dart +++ b/lib/src/level_won/view/level_complete_page.dart @@ -3,16 +3,46 @@ import 'package:go_router/go_router.dart'; import 'package:level_data/level_data.dart'; import 'package:rive/rive.dart'; import 'package:sozzle/core/common/widgets/sozzle_app_bar.dart'; +import 'package:sozzle/core/res/media.dart'; import 'package:sozzle/src/game_play/view/game_play_page.dart'; import 'package:sozzle/src/home/view/home_page.dart'; import 'package:sozzle/src/level_won/level_won.dart'; -class LevelCompletePage extends StatelessWidget { +class LevelCompletePage extends StatefulWidget { const LevelCompletePage({required this.levelData, super.key}); static const path = '/won'; final LevelData levelData; + @override + State createState() => _LevelCompletePageState(); +} + +class _LevelCompletePageState extends State { + late File file; + late RiveWidgetController controller; + + bool isInitialized = false; + + @override + void initState() { + super.initState(); + initRive(); + } + + Future initRive() async { + file = (await File.asset(Media.lake, riveFactory: Factory.rive))!; + controller = RiveWidgetController(file); + setState(() => isInitialized = true); + } + + @override + void dispose() { + file.dispose(); + controller.dispose(); + super.dispose(); + } + @override Widget build(BuildContext context) { return Scaffold( @@ -30,10 +60,10 @@ class LevelCompletePage extends StatelessWidget { builder: (scaffoldContext) { return Stack( children: [ - const RiveAnimation.asset( - 'assets/rive/small_lake_on_a_rainy_day.riv', - fit: BoxFit.cover, - ), + if (!isInitialized) + const Center(child: CircularProgressIndicator()) + else + RiveWidget(controller: controller, fit: Fit.cover), Center( child: SafeArea( child: Column( @@ -63,7 +93,7 @@ class LevelCompletePage extends StatelessWidget { child: NextLevelButton( onPressed: () { context.go( - '${GamePlayPage.path}/${levelData.levelId + 1}', + '${GamePlayPage.path}/${widget.levelData.levelId + 1}', ); }, ), diff --git a/linux/flutter/generated_plugin_registrant.cc b/linux/flutter/generated_plugin_registrant.cc index b2e5bb8..a7b1429 100644 --- a/linux/flutter/generated_plugin_registrant.cc +++ b/linux/flutter/generated_plugin_registrant.cc @@ -7,13 +7,13 @@ #include "generated_plugin_registrant.h" #include -#include +#include void fl_register_plugins(FlPluginRegistry* registry) { g_autoptr(FlPluginRegistrar) audioplayers_linux_registrar = fl_plugin_registry_get_registrar_for_plugin(registry, "AudioplayersLinuxPlugin"); audioplayers_linux_plugin_register_with_registrar(audioplayers_linux_registrar); - g_autoptr(FlPluginRegistrar) rive_common_registrar = - fl_plugin_registry_get_registrar_for_plugin(registry, "RivePlugin"); - rive_plugin_register_with_registrar(rive_common_registrar); + g_autoptr(FlPluginRegistrar) rive_native_registrar = + fl_plugin_registry_get_registrar_for_plugin(registry, "RiveNativePlugin"); + rive_native_plugin_register_with_registrar(rive_native_registrar); } diff --git a/linux/flutter/generated_plugins.cmake b/linux/flutter/generated_plugins.cmake index 66f822d..913291c 100644 --- a/linux/flutter/generated_plugins.cmake +++ b/linux/flutter/generated_plugins.cmake @@ -4,7 +4,7 @@ list(APPEND FLUTTER_PLUGIN_LIST audioplayers_linux - rive_common + rive_native ) list(APPEND FLUTTER_FFI_PLUGIN_LIST diff --git a/macos/Flutter/GeneratedPluginRegistrant.swift b/macos/Flutter/GeneratedPluginRegistrant.swift index a90e4b9..ee46efa 100644 --- a/macos/Flutter/GeneratedPluginRegistrant.swift +++ b/macos/Flutter/GeneratedPluginRegistrant.swift @@ -7,12 +7,12 @@ import Foundation import audioplayers_darwin import path_provider_foundation -import rive_common +import rive_native import shared_preferences_foundation func RegisterGeneratedPlugins(registry: FlutterPluginRegistry) { AudioplayersDarwinPlugin.register(with: registry.registrar(forPlugin: "AudioplayersDarwinPlugin")) PathProviderPlugin.register(with: registry.registrar(forPlugin: "PathProviderPlugin")) - RivePlugin.register(with: registry.registrar(forPlugin: "RivePlugin")) + RiveNativePlugin.register(with: registry.registrar(forPlugin: "RiveNativePlugin")) SharedPreferencesPlugin.register(with: registry.registrar(forPlugin: "SharedPreferencesPlugin")) } diff --git a/pubspec.yaml b/pubspec.yaml index b5fca86..2158b17 100644 --- a/pubspec.yaml +++ b/pubspec.yaml @@ -31,10 +31,10 @@ dependencies: path: packages/level_data localstore: ^1.4.0 path_provider: ^2.1.3 - rive: ^0.13.5 uuid: ^4.4.0 # TODO(RIVE): Reinstall flutter on linux and uncomment level_complete_page.dart # rive: ^0.12.3 + rive: ^0.14.0-dev.11 dev_dependencies: bloc_test: ^9.1.7 diff --git a/windows/flutter/generated_plugin_registrant.cc b/windows/flutter/generated_plugin_registrant.cc index 1374e7b..af57c00 100644 --- a/windows/flutter/generated_plugin_registrant.cc +++ b/windows/flutter/generated_plugin_registrant.cc @@ -7,11 +7,11 @@ #include "generated_plugin_registrant.h" #include -#include +#include void RegisterPlugins(flutter::PluginRegistry* registry) { AudioplayersWindowsPluginRegisterWithRegistrar( registry->GetRegistrarForPlugin("AudioplayersWindowsPlugin")); - RivePluginRegisterWithRegistrar( - registry->GetRegistrarForPlugin("RivePlugin")); + RiveNativePluginRegisterWithRegistrar( + registry->GetRegistrarForPlugin("RiveNativePlugin")); } diff --git a/windows/flutter/generated_plugins.cmake b/windows/flutter/generated_plugins.cmake index 92a4a8a..6c857a6 100644 --- a/windows/flutter/generated_plugins.cmake +++ b/windows/flutter/generated_plugins.cmake @@ -4,7 +4,7 @@ list(APPEND FLUTTER_PLUGIN_LIST audioplayers_windows - rive_common + rive_native ) list(APPEND FLUTTER_FFI_PLUGIN_LIST