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

feat(hook): implemented hook for MaterialStateController #383

Merged
merged 5 commits into from
Oct 10, 2023
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
35 changes: 18 additions & 17 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -338,23 +338,24 @@ 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`. |
| [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`. |
| [useExpansionTileController](https://api.flutter.dev/flutter/material/ExpansionTileController-class.html) | Creates a `ExpansionTileController`. |
| 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`. |
| [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`. |
| [useMaterialStatesController](https://pub.dev/documentation/flutter_hooks/latest/flutter_hooks/useMaterialStatesController.html) | Creates and disposes a `MaterialStatesController`. |
| [useExpansionTileController](https://api.flutter.dev/flutter/material/ExpansionTileController-class.html) | Creates a `ExpansionTileController`. |

## Contributions

Expand Down
9 changes: 8 additions & 1 deletion packages/flutter_hooks/lib/src/hooks.dart
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,13 @@ import 'dart:async';

import 'package:flutter/foundation.dart';
import 'package:flutter/material.dart'
show Brightness, ExpansionTileController, SearchController, TabController;
show
Brightness,
ExpansionTileController,
MaterialState,
MaterialStatesController,
SearchController,
TabController;
import 'package:flutter/scheduler.dart';
import 'package:flutter/widgets.dart';

Expand All @@ -26,3 +32,4 @@ part 'tab_controller.dart';
part 'text_controller.dart';
part 'transformation_controller.dart';
part 'widgets_binding_observer.dart';
part 'material_states_controller.dart';
44 changes: 44 additions & 0 deletions packages/flutter_hooks/lib/src/material_states_controller.dart
Original file line number Diff line number Diff line change
@@ -0,0 +1,44 @@
part of 'hooks.dart';

/// Creates a [MaterialStatesController] that will be disposed automatically.
///
/// See also:
/// - [MaterialStatesController]
MaterialStatesController useMaterialStatesController({
Set<MaterialState>? values,
List<Object?>? keys,
}) {
return use(
_MaterialStatesControllerHook(
values: values,
keys: keys,
),
);
}

class _MaterialStatesControllerHook extends Hook<MaterialStatesController> {
const _MaterialStatesControllerHook({
required this.values,
super.keys,
});

final Set<MaterialState>? values;

@override
HookState<MaterialStatesController, Hook<MaterialStatesController>>
createState() => _MaterialStateControllerHookState();
}

class _MaterialStateControllerHookState
extends HookState<MaterialStatesController, _MaterialStatesControllerHook> {
late final controller = MaterialStatesController(hook.values);

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

@override
void dispose() => controller.dispose();

@override
String get debugLabel => 'useMaterialStatesController';
}
113 changes: 113 additions & 0 deletions packages/flutter_hooks/test/use_material_states_controller_test.dart
Original file line number Diff line number Diff line change
@@ -0,0 +1,113 @@
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 'package:flutter_test/flutter_test.dart';

import 'mock.dart';

void main() {
testWidgets('debugFillProperties', (tester) async {
await tester.pumpWidget(
HookBuilder(builder: (context) {
useMaterialStatesController();
return const SizedBox();
}),
);

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

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

group('useMaterialStatesController', () {
testWidgets('initial values matches with real constructor', (tester) async {
late MaterialStatesController controller;
late MaterialStatesController controller2;

await tester.pumpWidget(
HookBuilder(builder: (context) {
controller2 = MaterialStatesController();
controller = useMaterialStatesController();
return Container();
}),
);

expect(controller.value, controller2.value);
});
testWidgets("returns a MaterialStatesController that doesn't change",
(tester) async {
late MaterialStatesController controller;
late MaterialStatesController controller2;

await tester.pumpWidget(
HookBuilder(builder: (context) {
controller = useMaterialStatesController();
return Container();
}),
);

expect(controller, isA<MaterialStatesController>());

await tester.pumpWidget(
HookBuilder(builder: (context) {
controller2 = useMaterialStatesController();
return Container();
}),
);

expect(identical(controller, controller2), isTrue);
});

testWidgets('passes hook parameters to the MaterialStatesController',
(tester) async {
late MaterialStatesController controller;

await tester.pumpWidget(
HookBuilder(
builder: (context) {
controller = useMaterialStatesController(
values: {MaterialState.selected},
);

return Container();
},
),
);

expect(controller.value, {MaterialState.selected});
});

testWidgets('disposes the MaterialStatesController on unmount',
(tester) async {
late MaterialStatesController controller;

await tester.pumpWidget(
HookBuilder(
builder: (context) {
controller = useMaterialStatesController();
return Container();
},
),
);

// pump another widget so that the old one gets disposed
await tester.pumpWidget(Container());

expect(
() => controller.addListener(() {}),
throwsA(isFlutterError.having(
(e) => e.message, 'message', contains('disposed'))),
);
});
});
}
Loading