Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add useOverlayPortalController #450

Merged
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
47 changes: 24 additions & 23 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -337,29 +337,30 @@ They will take care of creating/updating/disposing an object.

A series of hooks with no particular theme.

| Name | Description |
| -------------------------------------------------------------------------------------------------------------------------------------- | --------------------------------------------------------------------------------------------------------------------------- |
| [useReducer](https://pub.dev/documentation/flutter_hooks/latest/flutter_hooks/useReducer.html) | An alternative to `useState` for more complex states. |
| [usePrevious](https://pub.dev/documentation/flutter_hooks/latest/flutter_hooks/usePrevious.html) | Returns the previous argument called to [usePrevious]. |
| [useTextEditingController](https://pub.dev/documentation/flutter_hooks/latest/flutter_hooks/useTextEditingController-constant.html) | Creates a `TextEditingController`. |
| [useFocusNode](https://pub.dev/documentation/flutter_hooks/latest/flutter_hooks/useFocusNode.html) | Creates a `FocusNode`. |
| [useTabController](https://pub.dev/documentation/flutter_hooks/latest/flutter_hooks/useTabController.html) | Creates and disposes a `TabController`. |
| [useScrollController](https://pub.dev/documentation/flutter_hooks/latest/flutter_hooks/useScrollController.html) | Creates and disposes a `ScrollController`. |
| [usePageController](https://pub.dev/documentation/flutter_hooks/latest/flutter_hooks/usePageController.html) | Creates and disposes a `PageController`. |
| [useFixedExtentScrollController](https://pub.dev/documentation/flutter_hooks/latest/flutter_hooks/useFixedExtentScrollController.html) | Creates and disposes a `FixedExtentScrollController`. |
| [useAppLifecycleState](https://pub.dev/documentation/flutter_hooks/latest/flutter_hooks/useAppLifecycleState.html) | Returns the current `AppLifecycleState` and rebuilds the widget on change. |
| [useOnAppLifecycleStateChange](https://pub.dev/documentation/flutter_hooks/latest/flutter_hooks/useOnAppLifecycleStateChange.html) | Listens to `AppLifecycleState` changes and triggers a callback on change. |
| [useTransformationController](https://pub.dev/documentation/flutter_hooks/latest/flutter_hooks/useTransformationController.html) | Creates and disposes a `TransformationController`. |
| [useIsMounted](https://pub.dev/documentation/flutter_hooks/latest/flutter_hooks/useIsMounted.html) | An equivalent to `State.mounted` for hooks. |
| [useAutomaticKeepAlive](https://pub.dev/documentation/flutter_hooks/latest/flutter_hooks/useAutomaticKeepAlive.html) | An equivalent to the `AutomaticKeepAlive` widget for hooks. |
| [useOnPlatformBrightnessChange](https://pub.dev/documentation/flutter_hooks/latest/flutter_hooks/useOnPlatformBrightnessChange.html) | Listens to platform `Brightness` changes and triggers a callback on change. |
| [useSearchController](https://pub.dev/documentation/flutter_hooks/latest/flutter_hooks/useSearchController.html) | Creates and disposes a `SearchController`. |
| [useWidgetStatesController](https://pub.dev/documentation/flutter_hooks/latest/flutter_hooks/useWidgetStatesController.html) | Creates and disposes a `WidgetStatesController`. |
| [useExpansionTileController](https://api.flutter.dev/flutter/material/ExpansionTileController-class.html) | Creates a `ExpansionTileController`. |
| [useDebounced](https://pub.dev/documentation/flutter_hooks/latest/flutter_hooks/useDebounced.html) | Returns a debounced version of the provided value, triggering widget updates accordingly after a specified timeout duration |
| [useDraggableScrollableController](https://api.flutter.dev/flutter/widgets/DraggableScrollableController-class.html) | Creates a `DraggableScrollableController`. |
| [useCarouselController](https://pub.dev/documentation/flutter_hooks/latest/flutter_hooks/useCarouselController.html) | Creates and disposes a `CarouselController`. |
| [useTreeSliverController](https://pub.dev/documentation/flutter_hooks/latest/flutter_hooks/useTreeSliverController.html) | Creates a `TreeSliverController`. |
| Name | Description |
| -------------------------------------------------------------------------------------------------------------------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------ |
| [useReducer](https://pub.dev/documentation/flutter_hooks/latest/flutter_hooks/useReducer.html) | An alternative to `useState` for more complex states. |
| [usePrevious](https://pub.dev/documentation/flutter_hooks/latest/flutter_hooks/usePrevious.html) | Returns the previous argument called to [usePrevious]. |
| [useTextEditingController](https://pub.dev/documentation/flutter_hooks/latest/flutter_hooks/useTextEditingController-constant.html) | Creates a `TextEditingController`. |
| [useFocusNode](https://pub.dev/documentation/flutter_hooks/latest/flutter_hooks/useFocusNode.html) | Creates a `FocusNode`. |
| [useTabController](https://pub.dev/documentation/flutter_hooks/latest/flutter_hooks/useTabController.html) | Creates and disposes a `TabController`. |
| [useScrollController](https://pub.dev/documentation/flutter_hooks/latest/flutter_hooks/useScrollController.html) | Creates and disposes a `ScrollController`. |
| [usePageController](https://pub.dev/documentation/flutter_hooks/latest/flutter_hooks/usePageController.html) | Creates and disposes a `PageController`. |
| [useFixedExtentScrollController](https://pub.dev/documentation/flutter_hooks/latest/flutter_hooks/useFixedExtentScrollController.html) | Creates and disposes a `FixedExtentScrollController`. |
| [useAppLifecycleState](https://pub.dev/documentation/flutter_hooks/latest/flutter_hooks/useAppLifecycleState.html) | Returns the current `AppLifecycleState` and rebuilds the widget on change. |
| [useOnAppLifecycleStateChange](https://pub.dev/documentation/flutter_hooks/latest/flutter_hooks/useOnAppLifecycleStateChange.html) | Listens to `AppLifecycleState` changes and triggers a callback on change. |
| [useTransformationController](https://pub.dev/documentation/flutter_hooks/latest/flutter_hooks/useTransformationController.html) | Creates and disposes a `TransformationController`. |
| [useIsMounted](https://pub.dev/documentation/flutter_hooks/latest/flutter_hooks/useIsMounted.html) | An equivalent to `State.mounted` for hooks. |
| [useAutomaticKeepAlive](https://pub.dev/documentation/flutter_hooks/latest/flutter_hooks/useAutomaticKeepAlive.html) | An equivalent to the `AutomaticKeepAlive` widget for hooks. |
| [useOnPlatformBrightnessChange](https://pub.dev/documentation/flutter_hooks/latest/flutter_hooks/useOnPlatformBrightnessChange.html) | Listens to platform `Brightness` changes and triggers a callback on change. |
| [useSearchController](https://pub.dev/documentation/flutter_hooks/latest/flutter_hooks/useSearchController.html) | Creates and disposes a `SearchController`. |
| [useWidgetStatesController](https://pub.dev/documentation/flutter_hooks/latest/flutter_hooks/useWidgetStatesController.html) | Creates and disposes a `WidgetStatesController`. |
| [useExpansionTileController](https://api.flutter.dev/flutter/material/ExpansionTileController-class.html) | Creates a `ExpansionTileController`. |
| [useDebounced](https://pub.dev/documentation/flutter_hooks/latest/flutter_hooks/useDebounced.html) | Returns a debounced version of the provided value, triggering widget updates accordingly after a specified timeout duration |
| [useDraggableScrollableController](https://api.flutter.dev/flutter/widgets/DraggableScrollableController-class.html) | Creates a `DraggableScrollableController`. |
| [useCarouselController](https://pub.dev/documentation/flutter_hooks/latest/flutter_hooks/useCarouselController.html) | Creates and disposes a **`CarouselController`**. |
| [useTreeSliverController](https://pub.dev/documentation/flutter_hooks/latest/flutter_hooks/useTreeSliverController.html) | Creates a `TreeSliverController`. |
| [useOverlayPortalController](https://api.flutter.dev/flutter/widgets/OverlayPortalController-class.html) | Creates and manages an `OverlayPortalController` for controlling the visibility of overlay content. The controller will be automatically disposed when no longer needed. |

## Contributions

Expand Down
1 change: 1 addition & 0 deletions packages/flutter_hooks/CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@

- Add `useCarouselController` (thanks to @riscait)
- Add `useTreeSliverController` (thanks to @benthillerkus)
- Added `useOverlayPortalController` (thanks to @offich)

## 0.21.1-pre.4 - 2024-08-01

Expand Down
29 changes: 29 additions & 0 deletions packages/flutter_hooks/lib/src/misc.dart
Original file line number Diff line number Diff line change
Expand Up @@ -229,3 +229,32 @@ class _IsMountedHookState extends HookState<IsMounted, _IsMountedHook> {
"Use BuildContext.mounted instead if you're on Flutter 3.7.0 or greater",
)
typedef IsMounted = bool Function();

/// Creates a [OverlayPortalController] that will be disposed automatically.
///
/// See also:
/// - [OverlayPortalController]
OverlayPortalController useOverlayPortalController({
List<Object?>? keys,
}) {
return use(_OverlayPortalControllerHook(keys: keys));
}

class _OverlayPortalControllerHook extends Hook<OverlayPortalController> {
const _OverlayPortalControllerHook({List<Object?>? keys}) : super(keys: keys);

@override
HookState<OverlayPortalController, Hook<OverlayPortalController>>
createState() => _OverlayPortalControllerHookState();
}

class _OverlayPortalControllerHookState
extends HookState<OverlayPortalController, _OverlayPortalControllerHook> {
final controller = OverlayPortalController();

@override
OverlayPortalController build(BuildContext context) => controller;

@override
String get debugLabel => 'useOverlayPortalController';
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,91 @@
import 'package:flutter/foundation.dart';
import 'package:flutter/material.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) {
useOverlayPortalController();
return const SizedBox();
}),
);

await tester.pump();

final element = tester.element(find.byType(HookBuilder));

expect(
element
.toDiagnosticsNode(style: DiagnosticsTreeStyle.offstage)
.toStringDeep(),
equalsIgnoringHashCodes(
'HookBuilder\n'
' │ useOverlayPortalController: OverlayPortalController DETACHED\n'
' └SizedBox(renderObject: RenderConstrainedBox#00000)\n',
),
);
});

group('useOverlayPortalController', () {
testWidgets('initial values matches with real constructor', (tester) async {
late OverlayPortalController controller;
final controller2 = OverlayPortalController();

await tester.pumpWidget(MaterialApp(
home: Scaffold(
body: HookBuilder(builder: (context) {
controller = useOverlayPortalController();
return Column(
children: [
OverlayPortal(
controller: controller,
overlayChildBuilder: (context) =>
const Text('Overlay Portal'),
),
OverlayPortal(
controller: controller2,
overlayChildBuilder: (context) =>
const Text('Overlay Portal 2'),
),
],
);
}),
),
));
expect(controller, isA<OverlayPortalController>());
expect(controller.isShowing, controller2.isShowing);
});

testWidgets('check show/hide of overlay portal', (tester) async {
late OverlayPortalController controller;
await tester.pumpWidget(MaterialApp(
home: Scaffold(
body: HookBuilder(builder: (context) {
controller = useOverlayPortalController();
return OverlayPortal(
controller: controller,
overlayChildBuilder: (context) => const Text('Overlay Content'),
);
}),
),
));

expect(controller.isShowing, false);
expect(find.text('Overlay Content'), findsNothing);

controller.show();
await tester.pump();
expect(controller.isShowing, true);
expect(find.text('Overlay Content'), findsOneWidget);

controller.hide();
await tester.pump();
expect(controller.isShowing, false);
expect(find.text('Overlay Content'), findsNothing);
});
});
}