fix key usage issue after long inactivity

This commit is contained in:
Fabian Freund
2025-09-15 06:04:45 +02:00
parent 31621f580f
commit 2b667e3f11
@@ -57,8 +57,6 @@ class BrowserScreen extends HookConsumerWidget {
@override @override
Widget build(BuildContext context, WidgetRef ref) { Widget build(BuildContext context, WidgetRef ref) {
final scaffoldKey = useMemoized(() => GlobalKey<ScaffoldState>());
final eventService = ref.watch(eventServiceProvider); final eventService = ref.watch(eventServiceProvider);
final sheetDisplayed = ref.watch( final sheetDisplayed = ref.watch(
@@ -106,56 +104,6 @@ class BrowserScreen extends HookConsumerWidget {
); );
final sheetController = useRef<PersistentBottomSheetController?>(null); final sheetController = useRef<PersistentBottomSheetController?>(null);
ref.listen(bottomSheetControllerProvider, (previous, next) {
final state = scaffoldKey.currentState;
if (state != null) {
if (sheetController.value != null) {
sheetController.value!.close();
sheetController.value = null;
}
if (next != null) {
final relativeSafeArea = MediaQuery.of(context).relativeSafeArea();
sheetController.value = state.showBottomSheet((context) {
bool dismissOnThreshold(
DraggableScrollableNotification notification,
) {
if (notification.extent <= 0.1) {
ref.read(bottomSheetControllerProvider.notifier).dismiss();
return true;
} else {
ref
.read(bottomSheetExtendProvider.notifier)
.add(notification.extent);
}
return false;
}
final sheet = switch (next) {
ViewTabsSheet() =>
NotificationListener<DraggableScrollableNotification>(
key: ValueKey(next),
onNotification: dismissOnThreshold,
child: _ViewTabsSheet(maxChildSize: relativeSafeArea),
),
final EditUrlSheet parameter =>
NotificationListener<DraggableScrollableNotification>(
key: ValueKey(parameter),
onNotification: dismissOnThreshold,
child: _ViewUrlSheet(
initialTabState: parameter.tabState,
maxChildSize: relativeSafeArea,
),
),
};
return sheet;
});
}
}
});
return PopScope( return PopScope(
//We need this for BackButtonListener to work downstream //We need this for BackButtonListener to work downstream
@@ -164,7 +112,6 @@ class BrowserScreen extends HookConsumerWidget {
child: Theme( child: Theme(
data: themeData, data: themeData,
child: Scaffold( child: Scaffold(
key: scaffoldKey,
extendBodyBehindAppBar: tabInFullScreen, extendBodyBehindAppBar: tabInFullScreen,
bottomNavigationBar: HookConsumer( bottomNavigationBar: HookConsumer(
builder: (context, ref, child) { builder: (context, ref, child) {
@@ -265,161 +212,221 @@ class BrowserScreen extends HookConsumerWidget {
); );
}, },
), ),
body: DragTarget<TabDragData>( body: Consumer(
onMove: (details) { child: DragTarget<TabDragData>(
ref onMove: (details) {
.read(willAcceptDropProvider.notifier) ref
.setData(DeleteDropData(details.data.tabId)); .read(willAcceptDropProvider.notifier)
}, .setData(DeleteDropData(details.data.tabId));
onLeave: (data) { },
ref.read(willAcceptDropProvider.notifier).clear(); onLeave: (data) {
}, ref.read(willAcceptDropProvider.notifier).clear();
onAcceptWithDetails: (details) async { },
ref.read(willAcceptDropProvider.notifier).clear(); onAcceptWithDetails: (details) async {
await ref ref.read(willAcceptDropProvider.notifier).clear();
.read(tabRepositoryProvider.notifier) await ref
.closeTab(details.data.tabId); .read(tabRepositoryProvider.notifier)
.closeTab(details.data.tabId);
if (context.mounted) { if (context.mounted) {
ui_helper.showTabUndoClose( ui_helper.showTabUndoClose(
context, context,
ref.read(tabRepositoryProvider.notifier).undoClose, ref.read(tabRepositoryProvider.notifier).undoClose,
); );
} }
}, },
builder: (context, _, _) { builder: (context, _, _) {
return OverlayPortal( return OverlayPortal(
controller: overlayController, controller: overlayController,
overlayChildBuilder: (context) { overlayChildBuilder: (context) {
return overlayBuilder!.call(context); return overlayBuilder!.call(context);
}, },
child: Listener( child: Listener(
onPointerDown: sheetDisplayed onPointerDown: sheetDisplayed
? (_) { ? (_) {
ref
.read(bottomSheetControllerProvider.notifier)
.dismiss();
}
: null,
child: BackButtonListener(
onBackButtonPressed: () async {
final tabState = ref.read(selectedTabStateProvider);
final tabCount = ref.read(
tabListProvider.select((tabs) => tabs.value.length),
);
//Don't do anything if a child route is active
if (GoRouterState.of(context).topRoute?.name !=
BrowserRoute.name) {
return false;
}
if (sheetDisplayed) {
ref ref
.read(bottomSheetControllerProvider.notifier) .read(bottomSheetControllerProvider.notifier)
.dismiss(); .dismiss();
return true;
} }
: null,
child: BackButtonListener(
onBackButtonPressed: () async {
final tabState = ref.read(selectedTabStateProvider);
final tabCount = ref.read( if (overlayBuilder != null) {
tabListProvider.select((tabs) => tabs.value.length), ref
); .read(overlayControllerProvider.notifier)
.dismiss();
return true;
}
//Don't do anything if a child route is active if (tabState?.isFullScreen == true) {
if (GoRouterState.of(context).topRoute?.name !=
BrowserRoute.name) {
return false;
}
if (sheetDisplayed) {
ref
.read(bottomSheetControllerProvider.notifier)
.dismiss();
return true;
}
if (overlayBuilder != null) {
ref.read(overlayControllerProvider.notifier).dismiss();
return true;
}
if (tabState?.isFullScreen == true) {
await ref
.read(selectedTabSessionNotifierProvider)
.exitFullscreen();
return true;
}
if (tabState?.isLoading == true) {
lastBackButtonPress.value = null;
final controller = ref.read(
selectedTabSessionNotifierProvider,
);
await controller.stopLoading();
return true;
} else if (tabState?.readerableState.active == true) {
lastBackButtonPress.value = null;
await ref
.read(readerableScreenControllerProvider.notifier)
.toggleReaderView(false);
return true;
} else if (tabState?.historyState.canGoBack == true) {
lastBackButtonPress.value = null;
final controller = ref.read(
selectedTabSessionNotifierProvider,
);
await controller.goBack();
return true;
}
//Go router has routes to go back to
if (context.canPop()) {
return true;
}
if (ref
.read(tabRepositoryProvider.notifier)
.hasLaunchedFromIntent(tabState?.id)) {
//Mark back as unhandled and navigator will pop
await SystemNavigator.pop();
return false;
}
if (lastBackButtonPress.value != null &&
DateTime.now().difference(
lastBackButtonPress.value!,
) <
_backButtonPressTimeout) {
lastBackButtonPress.value = null;
if (tabState != null && tabCount > 1) {
await ref await ref
.read(tabRepositoryProvider.notifier) .read(selectedTabSessionNotifierProvider)
.closeTab(tabState.id); .exitFullscreen();
return true;
}
if (context.mounted) { if (tabState?.isLoading == true) {
ui_helper.showTabUndoClose( lastBackButtonPress.value = null;
context,
ref final controller = ref.read(
.read(tabRepositoryProvider.notifier) selectedTabSessionNotifierProvider,
.undoClose, );
);
} await controller.stopLoading();
return true;
} else if (tabState?.readerableState.active == true) {
lastBackButtonPress.value = null;
await ref
.read(readerableScreenControllerProvider.notifier)
.toggleReaderView(false);
return true; return true;
} else { } else if (tabState?.historyState.canGoBack == true) {
lastBackButtonPress.value = null;
final controller = ref.read(
selectedTabSessionNotifierProvider,
);
await controller.goBack();
return true;
}
//Go router has routes to go back to
if (context.canPop()) {
return true;
}
if (ref
.read(tabRepositoryProvider.notifier)
.hasLaunchedFromIntent(tabState?.id)) {
//Mark back as unhandled and navigator will pop //Mark back as unhandled and navigator will pop
await SystemNavigator.pop(); await SystemNavigator.pop();
return false; return false;
} }
} else {
lastBackButtonPress.value = DateTime.now();
ui_helper.showTabBackButtonMessage(
context,
tabCount,
_backButtonPressTimeout,
);
return true; if (lastBackButtonPress.value != null &&
} DateTime.now().difference(
}, lastBackButtonPress.value!,
child: _BrowserView( ) <
sheetDisplayed: sheetDisplayed, _backButtonPressTimeout) {
isFullscreen: tabInFullScreen, lastBackButtonPress.value = null;
if (tabState != null && tabCount > 1) {
await ref
.read(tabRepositoryProvider.notifier)
.closeTab(tabState.id);
if (context.mounted) {
ui_helper.showTabUndoClose(
context,
ref
.read(tabRepositoryProvider.notifier)
.undoClose,
);
}
return true;
} else {
//Mark back as unhandled and navigator will pop
await SystemNavigator.pop();
return false;
}
} else {
lastBackButtonPress.value = DateTime.now();
ui_helper.showTabBackButtonMessage(
context,
tabCount,
_backButtonPressTimeout,
);
return true;
}
},
child: _BrowserView(
sheetDisplayed: sheetDisplayed,
isFullscreen: tabInFullScreen,
),
), ),
), ),
), );
); },
),
builder: (context, ref, child) {
ref.listen(bottomSheetControllerProvider, (previous, next) {
if (sheetController.value != null) {
sheetController.value!.close();
sheetController.value = null;
}
if (next != null) {
final relativeSafeArea = MediaQuery.of(
context,
).relativeSafeArea();
sheetController.value = Scaffold.of(context).showBottomSheet((
context,
) {
bool dismissOnThreshold(
DraggableScrollableNotification notification,
) {
if (notification.extent <= 0.1) {
ref
.read(bottomSheetControllerProvider.notifier)
.dismiss();
return true;
} else {
ref
.read(bottomSheetExtendProvider.notifier)
.add(notification.extent);
}
return false;
}
final sheet = switch (next) {
ViewTabsSheet() =>
NotificationListener<DraggableScrollableNotification>(
key: ValueKey(next),
onNotification: dismissOnThreshold,
child: _ViewTabsSheet(maxChildSize: relativeSafeArea),
),
final EditUrlSheet parameter =>
NotificationListener<DraggableScrollableNotification>(
key: ValueKey(parameter),
onNotification: dismissOnThreshold,
child: _ViewUrlSheet(
initialTabState: parameter.tabState,
maxChildSize: relativeSafeArea,
),
),
};
return sheet;
});
}
});
return child!;
}, },
), ),
floatingActionButton: ReaderAppearanceButton(), floatingActionButton: ReaderAppearanceButton(),