diff --git a/packages/flutter_hooks/lib/src/hooks.dart b/packages/flutter_hooks/lib/src/hooks.dart index d1e4664..2ad681b 100644 --- a/packages/flutter_hooks/lib/src/hooks.dart +++ b/packages/flutter_hooks/lib/src/hooks.dart @@ -41,3 +41,4 @@ part 'transformation_controller.dart'; part 'tree_sliver_controller.dart'; part 'widget_states_controller.dart'; part 'widgets_binding_observer.dart'; +part 'snapshot_controller.dart'; diff --git a/packages/flutter_hooks/lib/src/snapshot_controller.dart b/packages/flutter_hooks/lib/src/snapshot_controller.dart new file mode 100644 index 0000000..d5b69ec --- /dev/null +++ b/packages/flutter_hooks/lib/src/snapshot_controller.dart @@ -0,0 +1,62 @@ +part of 'hooks.dart'; + +/// Creates and disposes a [SnapshotController]. +/// +/// Note that [allowSnapshotting] must be set to `true` +/// in order for this controller to actually do anything. +/// This is consistent with [SnapshotController.new]. +/// +/// If [allowSnapshotting] changes on subsequent calls to [useSnapshotController], +/// [SnapshotController.allowSnapshotting] will be called to update accordingly. +/// +/// ```dart +/// final controller = useSnapshotController(allowSnapshotting: true); +/// // is equivalent to +/// final controller = useSnapshotController(); +/// controller.allowSnapshotting = true; +/// ``` +/// +/// See also: +/// - [SnapshotController] +SnapshotController useSnapshotController({ + bool allowSnapshotting = false, +}) { + return use( + _SnapshotControllerHook( + allowSnapshotting: allowSnapshotting, + ), + ); +} + +class _SnapshotControllerHook extends Hook { + const _SnapshotControllerHook({ + required this.allowSnapshotting, + }); + + final bool allowSnapshotting; + + @override + HookState> createState() => + _SnapshotControllerHookState(); +} + +class _SnapshotControllerHookState + extends HookState { + late final controller = + SnapshotController(allowSnapshotting: hook.allowSnapshotting); + + @override + void didUpdateHook(_SnapshotControllerHook oldHook) { + super.didUpdateHook(oldHook); + controller.allowSnapshotting = hook.allowSnapshotting; + } + + @override + SnapshotController build(BuildContext context) => controller; + + @override + void dispose() => controller.dispose(); + + @override + String get debugLabel => 'useSnapshotController'; +} diff --git a/packages/flutter_hooks/test/use_snapshot_controller_test.dart b/packages/flutter_hooks/test/use_snapshot_controller_test.dart new file mode 100644 index 0000000..842691d --- /dev/null +++ b/packages/flutter_hooks/test/use_snapshot_controller_test.dart @@ -0,0 +1,75 @@ +import 'package:flutter/foundation.dart'; +import 'package:flutter/widgets.dart'; +import 'package:flutter_hooks/src/framework.dart'; +import 'package:flutter_hooks/src/hooks.dart'; + +import 'mock.dart'; + +void main() { + testWidgets('debugFillProperties', (tester) async { + await tester.pumpWidget( + HookBuilder(builder: (context) { + useSnapshotController(); + return const SizedBox(); + }), + ); + + await tester.pump(); + + final element = tester.element(find.byType(HookBuilder)); + + expect( + element + .toDiagnosticsNode(style: DiagnosticsTreeStyle.offstage) + .toStringDeep(), + equalsIgnoringHashCodes( + 'HookBuilder\n' + " │ useSnapshotController: Instance of 'SnapshotController'\n" + ' └SizedBox(renderObject: RenderConstrainedBox#00000)\n', + ), + ); + }); + + group('useSnapshotController', () { + testWidgets('initial values matches with real constructor', (tester) async { + late SnapshotController controller; + late SnapshotController controller2; + + await tester.pumpWidget( + HookBuilder(builder: (context) { + controller2 = SnapshotController(); + controller = useSnapshotController(); + return Container(); + }), + ); + + expect(controller.allowSnapshotting, controller2.allowSnapshotting); + }); + + testWidgets('passes hook parameters to the SnapshotController', + (tester) async { + late SnapshotController controller; + + await tester.pumpWidget( + HookBuilder(builder: (context) { + controller = useSnapshotController(allowSnapshotting: true); + return const SizedBox(); + }), + ); + + expect(controller.allowSnapshotting, true); + + late SnapshotController retrievedController; + await tester.pumpWidget( + HookBuilder(builder: (context) { + // ignore: avoid_redundant_argument_values + retrievedController = useSnapshotController(allowSnapshotting: false); + return const SizedBox(); + }), + ); + + expect(retrievedController, same(controller)); + expect(retrievedController.allowSnapshotting, false); + }); + }); +}