Skip to content
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
4 changes: 2 additions & 2 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -355,10 +355,10 @@ A series of hooks with no particular theme.
| [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`. |
| [useExpansibleController](https://pub.dev/documentation/flutter_hooks/latest/flutter_hooks/useExpansibleController.html) | Creates a `ExpansibleController`. |
| [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`**. |
| [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. |

Expand Down
4 changes: 4 additions & 0 deletions packages/flutter_hooks/CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,3 +1,7 @@
## Unreleased patch

Deprecated `useExpansionTileController` in favor of `useExpansibleController`.

## 0.21.2 - 2025-02-23

- Add `useCarouselController` (thanks to @riscait)
Expand Down
29 changes: 19 additions & 10 deletions packages/flutter_hooks/lib/src/expansion_tile_controller.dart
Original file line number Diff line number Diff line change
@@ -1,28 +1,37 @@
part of 'hooks.dart';

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

/// Creates a [ExpansionTileController] that will be disposed automatically.
///
/// See also:
/// - [ExpansionTileController]
@Deprecated('Use `useExpansibleController` instead.')
ExpansionTileController useExpansionTileController({List<Object?>? keys}) {
return use(_ExpansionTileControllerHook(keys: keys));
return use(_ExpansibleControllerHook(keys: keys));
}

class _ExpansionTileControllerHook extends Hook<ExpansionTileController> {
const _ExpansionTileControllerHook({List<Object?>? keys}) : super(keys: keys);
class _ExpansibleControllerHook extends Hook<ExpansibleController> {
const _ExpansibleControllerHook({List<Object?>? keys}) : super(keys: keys);

@override
HookState<ExpansionTileController, Hook<ExpansionTileController>>
createState() => _ExpansionTileControllerHookState();
HookState<ExpansibleController, Hook<ExpansibleController>> createState() =>
_ExpansibleControllerHookState();
}

class _ExpansionTileControllerHookState
extends HookState<ExpansionTileController, _ExpansionTileControllerHook> {
final controller = ExpansionTileController();
class _ExpansibleControllerHookState
extends HookState<ExpansibleController, _ExpansibleControllerHook> {
final controller = ExpansibleController();

@override
String get debugLabel => 'useExpansionTileController';
String get debugLabel => 'useExpansibleController';

@override
ExpansionTileController build(BuildContext context) => controller;
ExpansibleController build(BuildContext context) => controller;
}
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 @@ -6,6 +6,7 @@ import 'package:flutter/material.dart'
Brightness,
CarouselController,
DraggableScrollableController,
// ignore: deprecated_member_use
ExpansionTileController,
SearchController,
TabController,
Expand Down
38 changes: 19 additions & 19 deletions packages/flutter_hooks/lib/src/misc.dart
Original file line number Diff line number Diff line change
@@ -1,17 +1,17 @@
part of 'hooks.dart';

/// A store of mutable state that allows mutations by dispatching actions.
abstract class Store<State, Action> {
abstract class Store<StateT, ActionT> {
/// The current state.
///
/// This value may change after a call to [dispatch].
State get state;
StateT get state;

/// Dispatches an action.
///
/// Actions are dispatched synchronously.
/// It is impossible to try to dispatch actions during `build`.
void dispatch(Action action);
void dispatch(ActionT action);
}

/// Composes an [Action] and a [State] to create a new [State].
Expand All @@ -33,10 +33,10 @@ typedef Reducer<State, Action> = State Function(State state, Action action);
/// See also:
/// * [Reducer]
/// * [Store]
Store<State, Action> useReducer<State, Action>(
Reducer<State, Action> reducer, {
required State initialState,
required Action initialAction,
Store<StateT, ActionT> useReducer<StateT, ActionT>(
Reducer<StateT, ActionT> reducer, {
required StateT initialState,
required ActionT initialAction,
}) {
return use(
_ReducerHook(
Expand All @@ -47,27 +47,27 @@ Store<State, Action> useReducer<State, Action>(
);
}

class _ReducerHook<State, Action> extends Hook<Store<State, Action>> {
class _ReducerHook<StateT, ActionT> extends Hook<Store<StateT, ActionT>> {
const _ReducerHook(
this.reducer, {
required this.initialState,
required this.initialAction,
});

final Reducer<State, Action> reducer;
final State initialState;
final Action initialAction;
final Reducer<StateT, ActionT> reducer;
final StateT initialState;
final ActionT initialAction;

@override
_ReducerHookState<State, Action> createState() =>
_ReducerHookState<State, Action>();
_ReducerHookState<StateT, ActionT> createState() =>
_ReducerHookState<StateT, ActionT>();
}

class _ReducerHookState<State, Action>
extends HookState<Store<State, Action>, _ReducerHook<State, Action>>
implements Store<State, Action> {
class _ReducerHookState<StateT, ActionT>
extends HookState<Store<StateT, ActionT>, _ReducerHook<StateT, ActionT>>
implements Store<StateT, ActionT> {
@override
late State state = hook.reducer(hook.initialState, hook.initialAction);
late StateT state = hook.reducer(hook.initialState, hook.initialAction);

@override
void initHook() {
Expand All @@ -77,7 +77,7 @@ class _ReducerHookState<State, Action>
}

@override
void dispatch(Action action) {
void dispatch(ActionT action) {
final newState = hook.reducer(state, action);

if (state != newState) {
Expand All @@ -86,7 +86,7 @@ class _ReducerHookState<State, Action>
}

@override
Store<State, Action> build(BuildContext context) {
Store<StateT, ActionT> build(BuildContext context) {
return this;
}

Expand Down
2 changes: 1 addition & 1 deletion packages/flutter_hooks/pubspec.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@ version: 0.21.2

environment:
sdk: ">=2.17.0 <3.0.0"
flutter: ">=3.21.0-13.0.pre.4"
flutter: ">=3.32.0"
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Verification agent

🧩 Analysis chain

Verify the Flutter SDK version requirement.

The update from ">=3.21.0-13.0.pre.4" to ">=3.32.0" represents a significant version jump. Please ensure that Flutter 3.32.0 is a valid, released version and that this requirement aligns with the package's actual dependencies.


🌐 Web query:

What is the latest stable version of Flutter as of June 2025?

💡 Result:

As of June 2025, the latest stable version of Flutter is 3.29.0, released on February 12, 2025. (groups.google.com) This release introduced several enhancements, including:

  • Rendering Engine Update: Flutter now runs Dart code on the platform's main thread for both Android and iOS, improving platform interop through direct synchronous calls. (codewithandrea.com)

  • Cupertino and Material Widgets Refinements: The update brought refinements to Cupertino and Material widgets, along with DevTools enhancements and Impeller engine optimizations. (codewithandrea.com)

  • Deprecation of Certain Packages: In an effort to focus on the core framework, the Flutter team announced the discontinuation of support for several official packages, effective April 30, 2025. (codewithandrea.com)

To upgrade to Flutter 3.29.0, you can run the following command in your terminal:

flutter upgrade

For a comprehensive list of changes and updates in this release, refer to the official Flutter release notes. (docs.flutter.dev)

Citations:


Invalid Flutter SDK Version Constraint

The SDK constraint in packages/flutter_hooks/pubspec.yaml requires Flutter >=3.32.0, but Flutter 3.32.0 hasn’t been released. The latest stable version as of June 2025 is 3.29.0.

Please update the constraint accordingly:

- flutter: ">=3.32.0"
+ flutter: ">=3.29.0"
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
flutter: ">=3.32.0"
flutter: ">=3.29.0"
🤖 Prompt for AI Agents
In packages/flutter_hooks/pubspec.yaml at line 10, the Flutter SDK version
constraint is set to ">=3.32.0", which is invalid because Flutter 3.32.0 has not
been released yet. Update the version constraint to ">=3.29.0" to reflect the
latest stable Flutter version as of June 2025.


dependencies:
flutter:
Expand Down
Original file line number Diff line number Diff line change
@@ -1,3 +1,5 @@
import 'dart:async';

import 'package:flutter/foundation.dart';
import 'package:flutter/scheduler.dart';
import 'package:flutter/widgets.dart';
Expand Down Expand Up @@ -26,9 +28,10 @@ void main() {

controller
..duration = const Duration(seconds: 1)
..reverseDuration = const Duration(seconds: 1)
// check has a ticker
..forward();
..reverseDuration = const Duration(seconds: 1);

// check has a ticker
unawaited(controller.forward());

// dispose
await tester.pumpWidget(const SizedBox());
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@ void main() {
testWidgets('debugFillProperties', (tester) async {
await tester.pumpWidget(
HookBuilder(builder: (context) {
useExpansionTileController();
useExpansibleController();
return const SizedBox();
}),
);
Expand All @@ -24,21 +24,21 @@ void main() {
.toStringDeep(),
equalsIgnoringHashCodes(
'HookBuilder\n'
" │ useExpansionTileController: Instance of 'ExpansionTileController'\n"
" │ useExpansibleController: Instance of 'ExpansibleController'\n"
' └SizedBox(renderObject: RenderConstrainedBox#00000)\n',
),
);
});

group('useExpansionTileController', () {
group('useExpansibleController', () {
testWidgets('initial values matches with real constructor', (tester) async {
late ExpansionTileController controller;
final controller2 = ExpansionTileController();
late ExpansibleController controller;
final controller2 = ExpansibleController();

await tester.pumpWidget(MaterialApp(
home: Scaffold(
body: HookBuilder(builder: (context) {
controller = useExpansionTileController();
controller = useExpansibleController();
return Column(
children: [
ExpansionTile(
Expand All @@ -54,16 +54,16 @@ void main() {
}),
),
));
expect(controller, isA<ExpansionTileController>());
expect(controller, isA<ExpansibleController>());
expect(controller.isExpanded, controller2.isExpanded);
});

testWidgets('check expansion/collapse of tile', (tester) async {
late ExpansionTileController controller;
late ExpansibleController controller;
await tester.pumpWidget(MaterialApp(
home: Scaffold(
body: HookBuilder(builder: (context) {
controller = useExpansionTileController();
controller = useExpansibleController();
return ExpansionTile(
controller: controller,
title: const Text('Expansion Tile'),
Expand Down
8 changes: 6 additions & 2 deletions packages/flutter_hooks/test/use_ticker_provider_test.dart
Original file line number Diff line number Diff line change
@@ -1,3 +1,5 @@
import 'dart:async';

import 'package:flutter/foundation.dart';
import 'package:flutter/widgets.dart';
import 'package:flutter_hooks/flutter_hooks.dart';
Expand Down Expand Up @@ -41,8 +43,10 @@ void main() {
));

final animationController = AnimationController(
vsync: provider, duration: const Duration(seconds: 1))
..forward();
vsync: provider,
duration: const Duration(seconds: 1),
);
unawaited(animationController.forward());

expect(() => AnimationController(vsync: provider), throwsFlutterError);

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -20,15 +20,27 @@ void main() {
element
.toDiagnosticsNode(style: DiagnosticsTreeStyle.offstage)
.toStringDeep(),
equalsIgnoringHashCodes(
'HookBuilder\n'
' │ useTransformationController:\n'
' │ TransformationController#00000([0] 1.0,0.0,0.0,0.0\n'
' │ [1] 0.0,1.0,0.0,0.0\n'
' │ [2] 0.0,0.0,1.0,0.0\n'
' │ [3] 0.0,0.0,0.0,1.0\n'
' │ )\n'
' └SizedBox(renderObject: RenderConstrainedBox#00000)\n',
anyOf(
equalsIgnoringHashCodes(
'HookBuilder\n'
' │ useTransformationController:\n'
' │ TransformationController#00000([0] 1.0,0.0,0.0,0.0\n'
' │ [1] 0.0,1.0,0.0,0.0\n'
' │ [2] 0.0,0.0,1.0,0.0\n'
' │ [3] 0.0,0.0,0.0,1.0\n'
' │ )\n'
' └SizedBox(renderObject: RenderConstrainedBox#00000)\n',
),
equalsIgnoringHashCodes(
'HookBuilder\n'
' │ useTransformationController:\n'
' │ TransformationController#00000([0] [1.0,0.0,0.0,0.0]\n'
' │ [1] [0.0,1.0,0.0,0.0]\n'
' │ [2] [0.0,0.0,1.0,0.0]\n'
' │ [3] [0.0,0.0,0.0,1.0]\n'
' │ )\n'
' └SizedBox(renderObject: RenderConstrainedBox#00000)\n',
),
),
);
});
Expand Down