impelement geckoview intent handling via viewport api; fix lints and ui issues
This commit is contained in:
@@ -171,6 +171,18 @@ GeckoSuggestionsService engineSuggestionsService(Ref ref) {
|
||||
return service;
|
||||
}
|
||||
|
||||
@Riverpod(keepAlive: true)
|
||||
GeckoViewportService viewportService(Ref ref) {
|
||||
final service = GeckoViewportService();
|
||||
service.setUp();
|
||||
|
||||
ref.onDispose(() async {
|
||||
await service.dispose();
|
||||
});
|
||||
|
||||
return service;
|
||||
}
|
||||
|
||||
@Riverpod(keepAlive: true)
|
||||
class EngineReadyState extends _$EngineReadyState {
|
||||
@override
|
||||
|
||||
@@ -246,6 +246,53 @@ final class EngineSuggestionsServiceProvider
|
||||
String _$engineSuggestionsServiceHash() =>
|
||||
r'1ec1192f0c5c86cecc7ad448ee2b039f7a48e32b';
|
||||
|
||||
@ProviderFor(viewportService)
|
||||
final viewportServiceProvider = ViewportServiceProvider._();
|
||||
|
||||
final class ViewportServiceProvider
|
||||
extends
|
||||
$FunctionalProvider<
|
||||
GeckoViewportService,
|
||||
GeckoViewportService,
|
||||
GeckoViewportService
|
||||
>
|
||||
with $Provider<GeckoViewportService> {
|
||||
ViewportServiceProvider._()
|
||||
: super(
|
||||
from: null,
|
||||
argument: null,
|
||||
retry: null,
|
||||
name: r'viewportServiceProvider',
|
||||
isAutoDispose: false,
|
||||
dependencies: null,
|
||||
$allTransitiveDependencies: null,
|
||||
);
|
||||
|
||||
@override
|
||||
String debugGetCreateSourceHash() => _$viewportServiceHash();
|
||||
|
||||
@$internal
|
||||
@override
|
||||
$ProviderElement<GeckoViewportService> $createElement(
|
||||
$ProviderPointer pointer,
|
||||
) => $ProviderElement(pointer);
|
||||
|
||||
@override
|
||||
GeckoViewportService create(Ref ref) {
|
||||
return viewportService(ref);
|
||||
}
|
||||
|
||||
/// {@macro riverpod.override_with_value}
|
||||
Override overrideWithValue(GeckoViewportService value) {
|
||||
return $ProviderOverride(
|
||||
origin: this,
|
||||
providerOverride: $SyncValueProvider<GeckoViewportService>(value),
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
String _$viewportServiceHash() => r'bab39db3180bb6a1cf8c055966b7f5910b41b424';
|
||||
|
||||
@ProviderFor(EngineReadyState)
|
||||
final engineReadyStateProvider = EngineReadyStateProvider._();
|
||||
|
||||
|
||||
@@ -57,6 +57,12 @@ import 'package:weblibre/features/user/data/models/general_settings.dart';
|
||||
import 'package:weblibre/features/user/domain/repositories/general_settings.dart';
|
||||
import 'package:weblibre/utils/ui_helper.dart' as ui_helper;
|
||||
|
||||
/// Callback for toolbar animation progress updates.
|
||||
/// [progress] is 0.0 when hidden, 1.0 when fully visible.
|
||||
/// [heightPx] is the toolbar height in pixels.
|
||||
typedef ToolbarAnimationCallback =
|
||||
void Function(double progress, double heightPx);
|
||||
|
||||
/// Animated toolbar that slides in/out without changing layout constraints.
|
||||
/// Uses SlideTransition to animate visual transform while maintaining
|
||||
/// constant intrinsic size for layout purposes.
|
||||
@@ -64,6 +70,8 @@ class _AnimatedToolbar extends HookWidget {
|
||||
final bool visible;
|
||||
final TabBarPosition position;
|
||||
final Widget child;
|
||||
final double toolbarHeight;
|
||||
final ToolbarAnimationCallback? onAnimationProgress;
|
||||
|
||||
static const _kAnimationDuration = Duration(milliseconds: 250);
|
||||
|
||||
@@ -71,6 +79,9 @@ class _AnimatedToolbar extends HookWidget {
|
||||
required this.visible,
|
||||
required this.position,
|
||||
required this.child,
|
||||
required this.toolbarHeight,
|
||||
// ignore: unused_element_parameter
|
||||
this.onAnimationProgress,
|
||||
});
|
||||
|
||||
@override
|
||||
@@ -80,6 +91,18 @@ class _AnimatedToolbar extends HookWidget {
|
||||
initialValue: visible ? 1.0 : 0.0,
|
||||
);
|
||||
|
||||
// Listen to animation changes and report progress
|
||||
useEffect(() {
|
||||
if (onAnimationProgress == null) return null;
|
||||
|
||||
void listener() {
|
||||
onAnimationProgress?.call(controller.value, toolbarHeight);
|
||||
}
|
||||
|
||||
controller.addListener(listener);
|
||||
return () => controller.removeListener(listener);
|
||||
}, [onAnimationProgress, toolbarHeight]);
|
||||
|
||||
useEffect(() {
|
||||
if (visible) {
|
||||
unawaited(controller.forward());
|
||||
@@ -230,6 +253,7 @@ class BrowserScreen extends HookConsumerWidget {
|
||||
@override
|
||||
Widget build(BuildContext context, WidgetRef ref) {
|
||||
final eventService = ref.watch(eventServiceProvider);
|
||||
final viewportService = ref.watch(viewportServiceProvider);
|
||||
|
||||
final tabInFullScreen = ref.watch(
|
||||
selectedTabStateProvider.select((value) => value?.isFullScreen ?? false),
|
||||
@@ -339,6 +363,38 @@ class BrowserScreen extends HookConsumerWidget {
|
||||
? topAppBarTotalHeight
|
||||
: 0.0;
|
||||
|
||||
// Get pixel ratio for converting logical pixels to physical pixels
|
||||
final pixelRatio = MediaQuery.of(context).devicePixelRatio;
|
||||
|
||||
// Set up GeckoView dynamic toolbar height
|
||||
// This tells GeckoView the maximum toolbar space so it can adjust viewport
|
||||
final bottomToolbarHeightPx = bottomToolbarVisible
|
||||
? (bottomAppBarTotalHeight * pixelRatio).round()
|
||||
: 0;
|
||||
useEffect(() {
|
||||
final lastKeyboardEvent = viewportService.keyboardEvents.valueOrNull;
|
||||
|
||||
if (lastKeyboardEvent == null || !lastKeyboardEvent.isVisible) {
|
||||
unawaited(
|
||||
viewportService.setDynamicToolbarMaxHeight(bottomToolbarHeightPx),
|
||||
);
|
||||
}
|
||||
|
||||
return null;
|
||||
}, [bottomToolbarHeightPx]);
|
||||
|
||||
// Listen to keyboard visibility changes from native
|
||||
useOnStreamChange(
|
||||
viewportService.keyboardEvents,
|
||||
onData: (event) {
|
||||
if (event.isVisible && event.heightPx > 0) {
|
||||
// When keyboard is visible, notify GeckoView to adjust viewport
|
||||
// This uses the native API to handle keyboard without Flutter resize
|
||||
unawaited(viewportService.setDynamicToolbarMaxHeight(event.heightPx));
|
||||
}
|
||||
},
|
||||
);
|
||||
|
||||
// Theme with dynamic snackbar margin to position above bottom toolbar
|
||||
final themeData = Theme.of(context).copyWith(
|
||||
bottomSheetTheme: BottomSheetThemeData(
|
||||
@@ -412,6 +468,7 @@ class BrowserScreen extends HookConsumerWidget {
|
||||
child: _AnimatedToolbar(
|
||||
position: TabBarPosition.bottom,
|
||||
visible: bottomToolbarVisible,
|
||||
toolbarHeight: bottomAppBarTotalHeight,
|
||||
child: _TabBar(
|
||||
tabBarPosition: TabBarPosition.bottom,
|
||||
displayAppBar: displayAppBar,
|
||||
@@ -435,6 +492,7 @@ class BrowserScreen extends HookConsumerWidget {
|
||||
child: _AnimatedToolbar(
|
||||
position: TabBarPosition.top,
|
||||
visible: topToolbarVisible,
|
||||
toolbarHeight: topAppBarTotalHeight,
|
||||
child: _TabBar(
|
||||
tabBarPosition: TabBarPosition.top,
|
||||
showMainToolbar: true,
|
||||
|
||||
Reference in New Issue
Block a user