Skip to content

Commit 49c314f

Browse files
authored
feat: Add useCarouselController (#447)
1 parent a63df87 commit 49c314f

File tree

4 files changed

+166
-0
lines changed

4 files changed

+166
-0
lines changed

README.md

+1
Original file line numberDiff line numberDiff line change
@@ -358,6 +358,7 @@ A series of hooks with no particular theme.
358358
| [useExpansionTileController](https://api.flutter.dev/flutter/material/ExpansionTileController-class.html) | Creates a `ExpansionTileController`. |
359359
| [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 |
360360
| [useDraggableScrollableController](https://api.flutter.dev/flutter/widgets/DraggableScrollableController-class.html) | Creates a `DraggableScrollableController`. |
361+
| [useCarouselController](https://pub.dev/documentation/flutter_hooks/latest/flutter_hooks/useCarouselController.html) | Creates and disposes a `CarouselController`. |
361362

362363
## Contributions
363364

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

packages/flutter_hooks/lib/src/hooks.dart

+2
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@ import 'package:flutter/foundation.dart';
44
import 'package:flutter/material.dart'
55
show
66
Brightness,
7+
CarouselController,
78
DraggableScrollableController,
89
ExpansionTileController,
910
WidgetStatesController,
@@ -17,6 +18,7 @@ import 'framework.dart';
1718

1819
part 'animation.dart';
1920
part 'async.dart';
21+
part 'carousel_controller.dart';
2022
part 'draggable_scrollable_controller.dart';
2123
part 'expansion_tile_controller.dart';
2224
part 'fixed_extent_scroll_controller.dart';
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,117 @@
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+
import 'package:flutter_test/flutter_test.dart';
6+
7+
import 'mock.dart';
8+
9+
void main() {
10+
testWidgets('debugFillProperties', (tester) async {
11+
await tester.pumpWidget(
12+
HookBuilder(builder: (context) {
13+
useCarouselController();
14+
return const SizedBox();
15+
}),
16+
);
17+
18+
final element = tester.element(find.byType(HookBuilder));
19+
20+
expect(
21+
element
22+
.toDiagnosticsNode(style: DiagnosticsTreeStyle.offstage)
23+
.toStringDeep(),
24+
equalsIgnoringHashCodes(
25+
'HookBuilder\n'
26+
' │ useCarouselController: CarouselController#00000(no clients)\n'
27+
' └SizedBox(renderObject: RenderConstrainedBox#00000)\n',
28+
),
29+
);
30+
});
31+
32+
group('useCarouselController', () {
33+
testWidgets('initial values matches with real constructor', (tester) async {
34+
late CarouselController controller;
35+
late CarouselController controller2;
36+
37+
await tester.pumpWidget(
38+
HookBuilder(builder: (context) {
39+
controller2 = CarouselController();
40+
controller = useCarouselController();
41+
return Container();
42+
}),
43+
);
44+
45+
expect(controller.initialItem, controller2.initialItem);
46+
expect(controller.initialScrollOffset, controller2.initialScrollOffset);
47+
expect(controller.keepScrollOffset, controller2.keepScrollOffset);
48+
expect(controller.onAttach, controller2.onAttach);
49+
expect(controller.onDetach, controller2.onDetach);
50+
});
51+
52+
testWidgets("returns a CarouselController that doesn't change",
53+
(tester) async {
54+
late CarouselController controller;
55+
late CarouselController controller2;
56+
57+
await tester.pumpWidget(
58+
HookBuilder(builder: (context) {
59+
controller = useCarouselController();
60+
return Container();
61+
}),
62+
);
63+
64+
expect(controller, isA<CarouselController>());
65+
66+
await tester.pumpWidget(
67+
HookBuilder(builder: (context) {
68+
controller2 = useCarouselController();
69+
return Container();
70+
}),
71+
);
72+
73+
expect(identical(controller, controller2), isTrue);
74+
});
75+
76+
testWidgets('passes hook parameters to the CarouselController',
77+
(tester) async {
78+
late CarouselController controller;
79+
80+
await tester.pumpWidget(
81+
HookBuilder(
82+
builder: (context) {
83+
controller = useCarouselController(
84+
initialItem: 42,
85+
);
86+
87+
return Container();
88+
},
89+
),
90+
);
91+
92+
expect(controller.initialItem, 42);
93+
});
94+
95+
testWidgets('disposes the CarouselController on unmount', (tester) async {
96+
late CarouselController controller;
97+
98+
await tester.pumpWidget(
99+
HookBuilder(
100+
builder: (context) {
101+
controller = useCarouselController();
102+
return Container();
103+
},
104+
),
105+
);
106+
107+
// pump another widget so that the old one gets disposed
108+
await tester.pumpWidget(Container());
109+
110+
expect(
111+
() => controller.addListener(() {}),
112+
throwsA(isFlutterError.having(
113+
(e) => e.message, 'message', contains('disposed'))),
114+
);
115+
});
116+
});
117+
}

0 commit comments

Comments
 (0)