use keyed state hook
This commit is contained in:
+6
-9
@@ -72,6 +72,7 @@ import 'package:weblibre/features/user/data/models/general_settings.dart';
|
||||
import 'package:weblibre/features/user/data/models/proxy_routing_settings.dart';
|
||||
import 'package:weblibre/features/user/domain/repositories/general_settings.dart';
|
||||
import 'package:weblibre/features/user/domain/repositories/proxy_routing_settings.dart';
|
||||
import 'package:weblibre/presentation/hooks/keyed_state.dart';
|
||||
import 'package:weblibre/utils/move_to_background.dart';
|
||||
import 'package:weblibre/utils/ui_helper.dart' as ui_helper;
|
||||
|
||||
@@ -687,15 +688,11 @@ class BrowserScreen extends HookConsumerWidget {
|
||||
|
||||
// Track dismissed state without triggering full rebuild on every
|
||||
// hide/show. Updated via ref.listen below and synced on tab switch.
|
||||
final toolbarDismissed = useState(false);
|
||||
|
||||
// Sync dismissed state when selected tab changes
|
||||
useEffect(() {
|
||||
toolbarDismissed.value =
|
||||
ref.read(toolbarVisibilityControllerProvider(selectedTabId)) ==
|
||||
ToolbarVisibility.dismissed;
|
||||
return null;
|
||||
}, [selectedTabId]);
|
||||
final toolbarDismissed = useKeyedState(
|
||||
ref.read(toolbarVisibilityControllerProvider(selectedTabId)) ==
|
||||
ToolbarVisibility.dismissed,
|
||||
[selectedTabId],
|
||||
);
|
||||
|
||||
final stableToolbarHeight =
|
||||
autoHideTabBar && !toolbarDismissed.value && !tabInFullScreen
|
||||
|
||||
+2
-6
@@ -32,6 +32,7 @@ import 'package:weblibre/features/geckoview/features/top_sites/domain/entities/t
|
||||
import 'package:weblibre/features/geckoview/features/top_sites/domain/entities/top_site_source.dart';
|
||||
import 'package:weblibre/features/geckoview/features/top_sites/domain/providers.dart';
|
||||
import 'package:weblibre/features/geckoview/features/top_sites/domain/repositories/top_site_repository.dart';
|
||||
import 'package:weblibre/presentation/hooks/keyed_state.dart';
|
||||
import 'package:weblibre/presentation/widgets/url_icon.dart';
|
||||
import 'package:weblibre/utils/ui_helper.dart' as ui_helper;
|
||||
|
||||
@@ -228,12 +229,7 @@ class _ReorderableTopSitesGrid extends HookConsumerWidget {
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context, WidgetRef ref) {
|
||||
final localItems = useState(persistedItems);
|
||||
|
||||
useEffect(() {
|
||||
localItems.value = persistedItems;
|
||||
return null;
|
||||
}, [persistedItems]);
|
||||
final localItems = useKeyedState(persistedItems, [persistedItems]);
|
||||
|
||||
return SliverToBoxAdapter(
|
||||
child: Padding(
|
||||
|
||||
@@ -28,6 +28,7 @@ import 'package:weblibre/features/settings/presentation/widgets/settings_detail.
|
||||
import 'package:weblibre/features/user/data/models/general_settings.dart';
|
||||
import 'package:weblibre/features/user/domain/repositories/general_settings.dart';
|
||||
import 'package:weblibre/presentation/hooks/cached_future.dart';
|
||||
import 'package:weblibre/presentation/hooks/keyed_state.dart';
|
||||
|
||||
const List<SettingsSectionDefinition> generalSettingsSections = [
|
||||
SettingsSectionDefinition(
|
||||
@@ -198,12 +199,7 @@ class _UiZoomSection extends HookConsumerWidget {
|
||||
final uiScaleFactor = ref.watch(
|
||||
generalSettingsWithDefaultsProvider.select((s) => s.uiScaleFactor),
|
||||
);
|
||||
final sliderValue = useState(uiScaleFactor);
|
||||
|
||||
useEffect(() {
|
||||
sliderValue.value = uiScaleFactor;
|
||||
return null;
|
||||
}, [uiScaleFactor]);
|
||||
final sliderValue = useKeyedState(uiScaleFactor, [uiScaleFactor]);
|
||||
|
||||
final sliderLabel = '${(sliderValue.value * 100).round()}%';
|
||||
|
||||
|
||||
@@ -18,7 +18,6 @@
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:flutter_hooks/flutter_hooks.dart';
|
||||
import 'package:flutter_material_design_icons/flutter_material_design_icons.dart';
|
||||
import 'package:hooks_riverpod/hooks_riverpod.dart';
|
||||
import 'package:weblibre/features/settings/presentation/controllers/save_settings.dart';
|
||||
@@ -27,6 +26,7 @@ import 'package:weblibre/features/user/data/models/engine_settings.dart';
|
||||
import 'package:weblibre/features/user/data/models/general_settings.dart';
|
||||
import 'package:weblibre/features/user/domain/repositories/engine_settings.dart';
|
||||
import 'package:weblibre/features/user/domain/repositories/general_settings.dart';
|
||||
import 'package:weblibre/presentation/hooks/keyed_state.dart';
|
||||
|
||||
const List<SettingsSectionDefinition> webContentSettingsSections = [
|
||||
SettingsSectionDefinition(
|
||||
@@ -178,12 +178,7 @@ class _FontSizeFactorSlider extends HookConsumerWidget {
|
||||
final fontSizeFactor = ref.watch(
|
||||
engineSettingsWithDefaultsProvider.select((s) => s.fontSizeFactor),
|
||||
);
|
||||
final sliderValue = useState(fontSizeFactor);
|
||||
|
||||
useEffect(() {
|
||||
sliderValue.value = fontSizeFactor;
|
||||
return null;
|
||||
}, [fontSizeFactor]);
|
||||
final sliderValue = useKeyedState(fontSizeFactor, [fontSizeFactor]);
|
||||
|
||||
final sliderLabel = '${(sliderValue.value * 100).round()}%';
|
||||
|
||||
|
||||
+5
-11
@@ -18,7 +18,6 @@
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:flutter_hooks/flutter_hooks.dart';
|
||||
import 'package:flutter_material_design_icons/flutter_material_design_icons.dart';
|
||||
import 'package:hooks_riverpod/hooks_riverpod.dart';
|
||||
import 'package:weblibre/core/routing/routes.dart';
|
||||
@@ -26,6 +25,7 @@ import 'package:weblibre/features/settings/presentation/controllers/save_setting
|
||||
import 'package:weblibre/features/settings/presentation/widgets/settings_detail.dart';
|
||||
import 'package:weblibre/features/user/data/models/general_settings.dart';
|
||||
import 'package:weblibre/features/user/domain/repositories/general_settings.dart';
|
||||
import 'package:weblibre/presentation/hooks/keyed_state.dart';
|
||||
|
||||
const List<SettingsSectionDefinition> toolbarLayoutSettingsSections = [
|
||||
SettingsSectionDefinition(
|
||||
@@ -460,11 +460,7 @@ class _QuickTabSwitcherTitleWidthTile extends HookConsumerWidget {
|
||||
),
|
||||
);
|
||||
|
||||
final sliderValue = useState(titleWidth);
|
||||
useEffect(() {
|
||||
sliderValue.value = titleWidth;
|
||||
return null;
|
||||
}, [titleWidth]);
|
||||
final sliderValue = useKeyedState(titleWidth, [titleWidth]);
|
||||
|
||||
final enabled = switcherEnabled && showTitles;
|
||||
|
||||
@@ -626,11 +622,9 @@ class _QuickTabSwitcherHierarchyGlyphsTile extends HookConsumerWidget {
|
||||
),
|
||||
);
|
||||
|
||||
final sliderValue = useState(hierarchyGlyphs.toDouble());
|
||||
useEffect(() {
|
||||
sliderValue.value = hierarchyGlyphs.toDouble();
|
||||
return null;
|
||||
}, [hierarchyGlyphs]);
|
||||
final sliderValue = useKeyedState(hierarchyGlyphs.toDouble(), [
|
||||
hierarchyGlyphs,
|
||||
]);
|
||||
|
||||
final currentGlyphs = sliderValue.value.round();
|
||||
final enabled = switcherEnabled;
|
||||
|
||||
+3
-7
@@ -7,6 +7,7 @@ import 'package:hooks_riverpod/hooks_riverpod.dart';
|
||||
import 'package:search_protocol/search_protocol.dart';
|
||||
import 'package:weblibre/core/providers/persisted_bool.dart';
|
||||
import 'package:weblibre/features/web_search/domain/controllers/search_controller.dart';
|
||||
import 'package:weblibre/presentation/hooks/keyed_state.dart';
|
||||
import 'package:weblibre/presentation/widgets/uri_breadcrumb.dart';
|
||||
import 'package:weblibre/presentation/widgets/url_icon.dart';
|
||||
|
||||
@@ -541,16 +542,11 @@ class WebSearchInfoboxCarousel extends HookConsumerWidget {
|
||||
|
||||
// Natural (content) height of each page, measured off-stage. `null` until
|
||||
// a page has been laid out at least once.
|
||||
final heights = useState<List<double?>>(
|
||||
final heights = useKeyedState<List<double?>>(
|
||||
List<double?>.filled(infos.length, null),
|
||||
[infos.length],
|
||||
);
|
||||
|
||||
// Reset the measurement cache when the page set changes.
|
||||
useEffect(() {
|
||||
heights.value = List<double?>.filled(infos.length, null);
|
||||
return null;
|
||||
}, [infos.length]);
|
||||
|
||||
useEffect(() {
|
||||
void listener() {
|
||||
final page = controller.page?.round() ?? 0;
|
||||
|
||||
@@ -0,0 +1,63 @@
|
||||
/*
|
||||
* Copyright (c) 2024-2026 Fabian Freund.
|
||||
*
|
||||
* This file is part of WebLibre
|
||||
* (see https://weblibre.eu).
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU Affero General Public License as
|
||||
* published by the Free Software Foundation, either version 3 of the
|
||||
* License, or (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU Affero General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Affero General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
import 'package:flutter/widgets.dart';
|
||||
import 'package:flutter_hooks/flutter_hooks.dart';
|
||||
|
||||
/// Creates a [ValueNotifier] that is re-created when [keys] change.
|
||||
ValueNotifier<T> useKeyedState<T>(T initialData, List<Object?> keys) {
|
||||
return use(_KeyedStateHook<T>(initialData: initialData, keys: keys));
|
||||
}
|
||||
|
||||
class _KeyedStateHook<T> extends Hook<ValueNotifier<T>> {
|
||||
const _KeyedStateHook({
|
||||
required this.initialData,
|
||||
required List<Object?> keys,
|
||||
}) : super(keys: keys);
|
||||
|
||||
final T initialData;
|
||||
|
||||
@override
|
||||
_KeyedStateHookState<T> createState() => _KeyedStateHookState<T>();
|
||||
}
|
||||
|
||||
class _KeyedStateHookState<T>
|
||||
extends HookState<ValueNotifier<T>, _KeyedStateHook<T>> {
|
||||
late final _state = ValueNotifier<T>(hook.initialData)
|
||||
..addListener(_listener);
|
||||
|
||||
@override
|
||||
void dispose() {
|
||||
_state.dispose();
|
||||
super.dispose();
|
||||
}
|
||||
|
||||
@override
|
||||
ValueNotifier<T> build(BuildContext context) => _state;
|
||||
|
||||
void _listener() {
|
||||
setState(() {});
|
||||
}
|
||||
|
||||
@override
|
||||
Object? get debugValue => _state.value;
|
||||
|
||||
@override
|
||||
String get debugLabel => 'useKeyedState<$T>';
|
||||
}
|
||||
Reference in New Issue
Block a user