Skip to content

Commit

Permalink
Add useFixedExtentScrollController (#437)
Browse files Browse the repository at this point in the history
  • Loading branch information
timcreatedit authored Aug 1, 2024
1 parent a10ec1e commit 835aea0
Show file tree
Hide file tree
Showing 7 changed files with 170 additions and 33 deletions.
1 change: 1 addition & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -346,6 +346,7 @@ A series of hooks with no particular theme.
| [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`. |
Expand Down
5 changes: 5 additions & 0 deletions packages/flutter_hooks/CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,4 +1,9 @@
## Unreleased build

- Added `useFixedExtentScrollController` (thanks to @whynotmake-it)

## 0.21.1-pre.3 - 2024-07-30

- Added `useOnListenableChange` (thanks to @whynotmake-it)

## 0.21.1-pre.2 - 2024-07-22
Expand Down
57 changes: 57 additions & 0 deletions packages/flutter_hooks/lib/src/fixed_extent_scroll_controller.dart
Original file line number Diff line number Diff line change
@@ -0,0 +1,57 @@
part of 'hooks.dart';

/// Creates [FixedExtentScrollController] that will be disposed automatically.
///
/// See also:
/// - [FixedExtentScrollController]
FixedExtentScrollController useFixedExtentScrollController({
int initialItem = 0,
ScrollControllerCallback? onAttach,
ScrollControllerCallback? onDetach,
List<Object?>? keys,
}) {
return use(
_FixedExtentScrollControllerHook(
initialItem: initialItem,
onAttach: onAttach,
onDetach: onDetach,
keys: keys,
),
);
}

class _FixedExtentScrollControllerHook
extends Hook<FixedExtentScrollController> {
const _FixedExtentScrollControllerHook({
required this.initialItem,
this.onAttach,
this.onDetach,
super.keys,
});

final int initialItem;
final ScrollControllerCallback? onAttach;
final ScrollControllerCallback? onDetach;

@override
HookState<FixedExtentScrollController, Hook<FixedExtentScrollController>>
createState() => _FixedExtentScrollControllerHookState();
}

class _FixedExtentScrollControllerHookState extends HookState<
FixedExtentScrollController, _FixedExtentScrollControllerHook> {
late final controller = FixedExtentScrollController(
initialItem: hook.initialItem,
onAttach: hook.onAttach,
onDetach: hook.onDetach,
);

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

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

@override
String get debugLabel => 'useFixedExtentScrollController';
}
1 change: 1 addition & 0 deletions packages/flutter_hooks/lib/src/hooks.dart
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@ part 'animation.dart';
part 'async.dart';
part 'draggable_scrollable_controller.dart';
part 'expansion_tile_controller.dart';
part 'fixed_extent_scroll_controller.dart';
part 'focus_node.dart';
part 'focus_scope_node.dart';
part 'keep_alive.dart';
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,102 @@
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) {
useFixedExtentScrollController();
return const SizedBox();
}),
);

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

expect(
element
.toDiagnosticsNode(style: DiagnosticsTreeStyle.offstage)
.toStringDeep(),
equalsIgnoringHashCodes(
'HookBuilder\n'
' │ useFixedExtentScrollController:\n'
' │ FixedExtentScrollController#00000(no clients)\n'
' └SizedBox(renderObject: RenderConstrainedBox#00000)\n',
),
);
});

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

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

expect(controller.debugLabel, controller2.debugLabel);
expect(controller.initialItem, controller2.initialItem);
expect(controller.onAttach, controller2.onAttach);
expect(controller.onDetach, controller2.onDetach);
});
testWidgets("returns a FixedExtentScrollController that doesn't change",
(tester) async {
late FixedExtentScrollController controller;
late FixedExtentScrollController controller2;

await tester.pumpWidget(
HookBuilder(builder: (context) {
controller2 = FixedExtentScrollController();
controller = useFixedExtentScrollController();
return Container();
}),
);
expect(controller, isA<FixedExtentScrollController>());

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

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

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

void onAttach(ScrollPosition position) {}
void onDetach(ScrollPosition position) {}

await tester.pumpWidget(
HookBuilder(
builder: (context) {
controller = useFixedExtentScrollController(
initialItem: 42,
onAttach: onAttach,
onDetach: onDetach,
);

return Container();
},
),
);

expect(controller.initialItem, 42);
expect(controller.onAttach, onAttach);
expect(controller.onDetach, onDetach);
});
});
}

class TickerProviderMock extends Mock implements TickerProvider {}
19 changes: 2 additions & 17 deletions packages/flutter_hooks/test/use_page_controller_test.dart
Original file line number Diff line number Diff line change
Expand Up @@ -45,6 +45,8 @@ void main() {
expect(controller.initialPage, controller2.initialPage);
expect(controller.keepPage, controller2.keepPage);
expect(controller.viewportFraction, controller2.viewportFraction);
expect(controller.onAttach, controller2.onAttach);
expect(controller.onDetach, controller2.onDetach);
});
testWidgets("returns a PageController that doesn't change", (tester) async {
late PageController controller;
Expand Down Expand Up @@ -98,23 +100,6 @@ void main() {
expect(controller.onDetach, onDetach);
});

testWidgets('onAttach and onDetach are null by default', (tester) async {
late PageController controller;

await tester.pumpWidget(
HookBuilder(
builder: (context) {
controller = usePageController();

return Container();
},
),
);

expect(controller.onAttach, isNull);
expect(controller.onDetach, isNull);
});

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

Expand Down
18 changes: 2 additions & 16 deletions packages/flutter_hooks/test/use_scroll_controller_test.dart
Original file line number Diff line number Diff line change
Expand Up @@ -44,6 +44,8 @@ void main() {
expect(controller.debugLabel, controller2.debugLabel);
expect(controller.initialScrollOffset, controller2.initialScrollOffset);
expect(controller.keepScrollOffset, controller2.keepScrollOffset);
expect(controller.onAttach, controller2.onAttach);
expect(controller.onDetach, controller2.onDetach);
});
testWidgets("returns a ScrollController that doesn't change",
(tester) async {
Expand Down Expand Up @@ -98,22 +100,6 @@ void main() {
expect(controller.onAttach, onAttach);
expect(controller.onDetach, onDetach);
});

testWidgets('onAttach and onDetach are null by default', (tester) async {
late ScrollController controller;

await tester.pumpWidget(
HookBuilder(
builder: (context) {
controller = useScrollController();

return Container();
},
),
);
expect(controller.onAttach, isNull);
expect(controller.onDetach, isNull);
});
});
}

Expand Down

0 comments on commit 835aea0

Please sign in to comment.