Skip to content

Instantly share code, notes, and snippets.

@saltyJeff
Created November 25, 2025 13:20
Show Gist options
  • Select an option

  • Save saltyJeff/4130d05955e791d2c08d791937fdf25e to your computer and use it in GitHub Desktop.

Select an option

Save saltyJeff/4130d05955e791d2c08d791937fdf25e to your computer and use it in GitHub Desktop.
Gemini wrote me this update to flame_riverpod to riverpod v3
import 'package:flame/components.dart';
import 'package:flame/game.dart';
import 'package:flutter/widgets.dart';
import 'package:riverpod/riverpod.dart';
import 'package:riverpod/misc.dart';
import 'package:hooks_riverpod/hooks_riverpod.dart';
/// A custom [Ref] object for Flame components.
///
/// It provides [watch], [listen], and [read] methods that are
/// adapted for the [Component] lifecycle.
class ComponentRef {
ComponentRef(this._container);
final ProviderContainer _container;
final _subscriptions = <ProviderSubscription>[];
/// Reads the current value of a provider *without* subscribing.
///
/// This is for one-time reads, often in `onLoad` or in response to an event.
T read<T>(ProviderListenable<T> provider) {
return _container.read(provider);
}
/// Subscribes to a provider and calls a [listener] callback on change.
///
/// The subscription is automatically disposed when the component is removed.
void listen<T>(
ProviderListenable<T> provider,
void Function(T? previous, T next) listener, {
bool fireImmediately = false,
}) {
final sub = _container.listen(
provider,
listener,
fireImmediately: fireImmediately,
);
_subscriptions.add(sub);
}
/// Gets the current value of a provider AND subscribes to future changes.
///
/// This is the primary method you'll use in `onLoad`.
/// It returns the *current* value, and sets up a [listener] to
/// react to *future* values (e.g., to update component state).
///
/// The subscription is automatically disposed when the component is removed.
T watch<T>(
ProviderListenable<T> provider,
void Function(T? previous, T next) listener,
) {
listen(provider, listener, fireImmediately: false);
return read(provider);
}
/// Disposes all subscriptions created by this ref.
/// Called automatically by the [RiverpodComponentMixin].
void dispose() {
for (final sub in _subscriptions) {
sub.close();
}
_subscriptions.clear();
}
}
/// A mixin for [FlameGame] to make it aware of the Riverpod [ProviderContainer].
///
/// Your game class must use this mixin to use [RiverpodComponentMixin] on its
/// components.
mixin RiverpodGameMixin on FlameGame {
ProviderContainer? _container;
/// The [ProviderContainer] for this game.
ProviderContainer get container {
assert(
_container != null,
'ProviderContainer not set. Did you forget to use RiverpodGameWidget?',
);
return _container!;
}
/// Called by [RiverpodGameWidget] to set the container.
void setProviderContainer(ProviderContainer container) {
_container = container;
}
}
/// The widget that bridges Flutter (Riverpod) and Flame.
///
/// Use this widget to render your [RiverpodGameMixin] game.
/// It finds the [ProviderContainer] from the [ProviderScope] and passes
/// it to your game instance.
class RiverpodGameWidget<T extends RiverpodGameMixin>
extends ConsumerStatefulWidget {
const RiverpodGameWidget({
super.key,
required this.game,
});
final T game;
@override
ConsumerState<RiverpodGameWidget<T>> createState() =>
_RiverpodGameWidgetState<T>();
}
class _RiverpodGameWidgetState<T extends RiverpodGameMixin>
extends ConsumerState<RiverpodGameWidget<T>> {
late final T _game;
@override
void initState() {
super.initState();
_game = widget.game;
}
@override
Widget build(BuildContext context) {
// Get the container from the context...
final container = ProviderScope.containerOf(context);
// ...and pass it to the game.
_game.setProviderContainer(container);
// GameWidget is the standard widget from Flame to render a game.
return GameWidget(game: _game);
}
}
/// The mixin you requested.
///
/// Add this mixin to any [Component] to get access to `ref.watch`,
/// `ref.listen`, and `ref.read`.
///
/// The component MUST be added to a game that uses [RiverpodGameMixin].
mixin RiverpodComponentMixin on Component {
late final ComponentRef ref;
ProviderContainer? _container;
/// Helper to get the game's [ProviderContainer].
ProviderContainer get _gameContainer {
if (_container != null) return _container!;
final game = findRootGame();
if (game is! RiverpodGameMixin) {
throw StateError(
'RiverpodComponentMixin can only be used on components added to a '
'game that uses RiverpodGameMixin. Please add RiverpodGameMixin to '
'${game.runtimeType}.',
);
}
_container = game.container;
return _container!;
}
@override
@mustCallSuper
void onLoad() {
super.onLoad();
// Create the ref and link it to the game's container.
ref = ComponentRef(_gameContainer);
}
@override
@mustCallSuper
void onRemove() {
super.onRemove();
// Clean up all subscriptions when the component is removed.
ref.dispose();
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment