Skip to content

Commit 835aea0

Browse files
authored
Add useFixedExtentScrollController (rrousselGit#437)
1 parent a10ec1e commit 835aea0

7 files changed

+170
-33
lines changed

README.md

+1
Original file line numberDiff line numberDiff line change
@@ -346,6 +346,7 @@ A series of hooks with no particular theme.
346346
| [useTabController](https://pub.dev/documentation/flutter_hooks/latest/flutter_hooks/useTabController.html) | Creates and disposes a `TabController`. |
347347
| [useScrollController](https://pub.dev/documentation/flutter_hooks/latest/flutter_hooks/useScrollController.html) | Creates and disposes a `ScrollController`. |
348348
| [usePageController](https://pub.dev/documentation/flutter_hooks/latest/flutter_hooks/usePageController.html) | Creates and disposes a `PageController`. |
349+
| [useFixedExtentScrollController](https://pub.dev/documentation/flutter_hooks/latest/flutter_hooks/useFixedExtentScrollController.html) | Creates and disposes a `FixedExtentScrollController`. |
349350
| [useAppLifecycleState](https://pub.dev/documentation/flutter_hooks/latest/flutter_hooks/useAppLifecycleState.html) | Returns the current `AppLifecycleState` and rebuilds the widget on change. |
350351
| [useOnAppLifecycleStateChange](https://pub.dev/documentation/flutter_hooks/latest/flutter_hooks/useOnAppLifecycleStateChange.html) | Listens to `AppLifecycleState` changes and triggers a callback on change. |
351352
| [useTransformationController](https://pub.dev/documentation/flutter_hooks/latest/flutter_hooks/useTransformationController.html) | Creates and disposes a `TransformationController`. |

packages/flutter_hooks/CHANGELOG.md

+5
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,9 @@
1+
## Unreleased build
2+
3+
- Added `useFixedExtentScrollController` (thanks to @whynotmake-it)
4+
15
## 0.21.1-pre.3 - 2024-07-30
6+
27
- Added `useOnListenableChange` (thanks to @whynotmake-it)
38

49
## 0.21.1-pre.2 - 2024-07-22
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,57 @@
1+
part of 'hooks.dart';
2+
3+
/// Creates [FixedExtentScrollController] that will be disposed automatically.
4+
///
5+
/// See also:
6+
/// - [FixedExtentScrollController]
7+
FixedExtentScrollController useFixedExtentScrollController({
8+
int initialItem = 0,
9+
ScrollControllerCallback? onAttach,
10+
ScrollControllerCallback? onDetach,
11+
List<Object?>? keys,
12+
}) {
13+
return use(
14+
_FixedExtentScrollControllerHook(
15+
initialItem: initialItem,
16+
onAttach: onAttach,
17+
onDetach: onDetach,
18+
keys: keys,
19+
),
20+
);
21+
}
22+
23+
class _FixedExtentScrollControllerHook
24+
extends Hook<FixedExtentScrollController> {
25+
const _FixedExtentScrollControllerHook({
26+
required this.initialItem,
27+
this.onAttach,
28+
this.onDetach,
29+
super.keys,
30+
});
31+
32+
final int initialItem;
33+
final ScrollControllerCallback? onAttach;
34+
final ScrollControllerCallback? onDetach;
35+
36+
@override
37+
HookState<FixedExtentScrollController, Hook<FixedExtentScrollController>>
38+
createState() => _FixedExtentScrollControllerHookState();
39+
}
40+
41+
class _FixedExtentScrollControllerHookState extends HookState<
42+
FixedExtentScrollController, _FixedExtentScrollControllerHook> {
43+
late final controller = FixedExtentScrollController(
44+
initialItem: hook.initialItem,
45+
onAttach: hook.onAttach,
46+
onDetach: hook.onDetach,
47+
);
48+
49+
@override
50+
FixedExtentScrollController build(BuildContext context) => controller;
51+
52+
@override
53+
void dispose() => controller.dispose();
54+
55+
@override
56+
String get debugLabel => 'useFixedExtentScrollController';
57+
}

packages/flutter_hooks/lib/src/hooks.dart

+1
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,7 @@ part 'animation.dart';
1919
part 'async.dart';
2020
part 'draggable_scrollable_controller.dart';
2121
part 'expansion_tile_controller.dart';
22+
part 'fixed_extent_scroll_controller.dart';
2223
part 'focus_node.dart';
2324
part 'focus_scope_node.dart';
2425
part 'keep_alive.dart';
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,102 @@
1+
import 'package:flutter/foundation.dart';
2+
import 'package:flutter/material.dart';
3+
import 'package:flutter_hooks/src/framework.dart';
4+
import 'package:flutter_hooks/src/hooks.dart';
5+
6+
import 'mock.dart';
7+
8+
void main() {
9+
testWidgets('debugFillProperties', (tester) async {
10+
await tester.pumpWidget(
11+
HookBuilder(builder: (context) {
12+
useFixedExtentScrollController();
13+
return const SizedBox();
14+
}),
15+
);
16+
17+
final element = tester.element(find.byType(HookBuilder));
18+
19+
expect(
20+
element
21+
.toDiagnosticsNode(style: DiagnosticsTreeStyle.offstage)
22+
.toStringDeep(),
23+
equalsIgnoringHashCodes(
24+
'HookBuilder\n'
25+
' │ useFixedExtentScrollController:\n'
26+
' │ FixedExtentScrollController#00000(no clients)\n'
27+
' └SizedBox(renderObject: RenderConstrainedBox#00000)\n',
28+
),
29+
);
30+
});
31+
32+
group('useFixedExtentScrollController', () {
33+
testWidgets('initial values matches with real constructor', (tester) async {
34+
late FixedExtentScrollController controller;
35+
late FixedExtentScrollController controller2;
36+
37+
await tester.pumpWidget(
38+
HookBuilder(builder: (context) {
39+
controller2 = FixedExtentScrollController();
40+
controller = useFixedExtentScrollController();
41+
return Container();
42+
}),
43+
);
44+
45+
expect(controller.debugLabel, controller2.debugLabel);
46+
expect(controller.initialItem, controller2.initialItem);
47+
expect(controller.onAttach, controller2.onAttach);
48+
expect(controller.onDetach, controller2.onDetach);
49+
});
50+
testWidgets("returns a FixedExtentScrollController that doesn't change",
51+
(tester) async {
52+
late FixedExtentScrollController controller;
53+
late FixedExtentScrollController controller2;
54+
55+
await tester.pumpWidget(
56+
HookBuilder(builder: (context) {
57+
controller2 = FixedExtentScrollController();
58+
controller = useFixedExtentScrollController();
59+
return Container();
60+
}),
61+
);
62+
expect(controller, isA<FixedExtentScrollController>());
63+
64+
await tester.pumpWidget(
65+
HookBuilder(builder: (context) {
66+
controller2 = useFixedExtentScrollController();
67+
return Container();
68+
}),
69+
);
70+
71+
expect(identical(controller, controller2), isTrue);
72+
});
73+
74+
testWidgets('passes hook parameters to the FixedExtentScrollController',
75+
(tester) async {
76+
late FixedExtentScrollController controller;
77+
78+
void onAttach(ScrollPosition position) {}
79+
void onDetach(ScrollPosition position) {}
80+
81+
await tester.pumpWidget(
82+
HookBuilder(
83+
builder: (context) {
84+
controller = useFixedExtentScrollController(
85+
initialItem: 42,
86+
onAttach: onAttach,
87+
onDetach: onDetach,
88+
);
89+
90+
return Container();
91+
},
92+
),
93+
);
94+
95+
expect(controller.initialItem, 42);
96+
expect(controller.onAttach, onAttach);
97+
expect(controller.onDetach, onDetach);
98+
});
99+
});
100+
}
101+
102+
class TickerProviderMock extends Mock implements TickerProvider {}

packages/flutter_hooks/test/use_page_controller_test.dart

+2-17
Original file line numberDiff line numberDiff line change
@@ -45,6 +45,8 @@ void main() {
4545
expect(controller.initialPage, controller2.initialPage);
4646
expect(controller.keepPage, controller2.keepPage);
4747
expect(controller.viewportFraction, controller2.viewportFraction);
48+
expect(controller.onAttach, controller2.onAttach);
49+
expect(controller.onDetach, controller2.onDetach);
4850
});
4951
testWidgets("returns a PageController that doesn't change", (tester) async {
5052
late PageController controller;
@@ -98,23 +100,6 @@ void main() {
98100
expect(controller.onDetach, onDetach);
99101
});
100102

101-
testWidgets('onAttach and onDetach are null by default', (tester) async {
102-
late PageController controller;
103-
104-
await tester.pumpWidget(
105-
HookBuilder(
106-
builder: (context) {
107-
controller = usePageController();
108-
109-
return Container();
110-
},
111-
),
112-
);
113-
114-
expect(controller.onAttach, isNull);
115-
expect(controller.onDetach, isNull);
116-
});
117-
118103
testWidgets('disposes the PageController on unmount', (tester) async {
119104
late PageController controller;
120105

packages/flutter_hooks/test/use_scroll_controller_test.dart

+2-16
Original file line numberDiff line numberDiff line change
@@ -44,6 +44,8 @@ void main() {
4444
expect(controller.debugLabel, controller2.debugLabel);
4545
expect(controller.initialScrollOffset, controller2.initialScrollOffset);
4646
expect(controller.keepScrollOffset, controller2.keepScrollOffset);
47+
expect(controller.onAttach, controller2.onAttach);
48+
expect(controller.onDetach, controller2.onDetach);
4749
});
4850
testWidgets("returns a ScrollController that doesn't change",
4951
(tester) async {
@@ -98,22 +100,6 @@ void main() {
98100
expect(controller.onAttach, onAttach);
99101
expect(controller.onDetach, onDetach);
100102
});
101-
102-
testWidgets('onAttach and onDetach are null by default', (tester) async {
103-
late ScrollController controller;
104-
105-
await tester.pumpWidget(
106-
HookBuilder(
107-
builder: (context) {
108-
controller = useScrollController();
109-
110-
return Container();
111-
},
112-
),
113-
);
114-
expect(controller.onAttach, isNull);
115-
expect(controller.onDetach, isNull);
116-
});
117103
});
118104
}
119105

0 commit comments

Comments
 (0)