Files
MrbWebLibre/packages/flutter_mozilla_components/lib/src/geckoview_widget.dart
T
2025-06-29 11:05:25 +02:00

125 lines
3.6 KiB
Dart

/*
* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at https://mozilla.org/MPL/2.0/.
*/
import 'dart:async';
import 'package:flutter/foundation.dart';
import 'package:flutter/gestures.dart';
import 'package:flutter/material.dart';
import 'package:flutter/rendering.dart';
import 'package:flutter/scheduler.dart';
import 'package:flutter/services.dart';
import 'package:flutter_mozilla_components/src/domain/services/gecko_browser.dart';
class GeckoView extends StatefulWidget {
final Future<void> Function()? preInitializationStep;
final Future<void> Function()? postInitializationStep;
const GeckoView({
super.key,
this.preInitializationStep,
this.postInitializationStep,
});
@override
State<GeckoView> createState() => _GeckoViewState();
}
class _GeckoViewState extends State<GeckoView> {
static const platform = MethodChannel(
'eu.weblibre.flutter_mozilla_components/trim_memory',
);
final browserService = GeckoBrowserService();
late final AppLifecycleListener _listener;
@override
void initState() {
super.initState();
_setupMethodCallHandler();
_listener = AppLifecycleListener(
onResume: () async {
//Make sure fragment visible after rsuming the app in case native resources have been disposed
await _showNativeFragment();
},
);
}
void _setupMethodCallHandler() {
platform.setMethodCallHandler((MethodCall call) async {
if (call.method == 'onTrimMemory') {
await browserService.onTrimMemory(call.arguments as int);
}
});
}
Future<bool> _showNativeFragment({
int maxRetries = 100,
/// Default ist about one frame
Duration retryDelay = const Duration(milliseconds: 1000 ~/ 60),
}) async {
for (int attempt = 0; attempt < maxRetries; attempt++) {
final result = await browserService.showNativeFragment();
if (result) {
debugPrint('Fragment ATTACHED after $attempt tries');
return true;
}
if (attempt < maxRetries - 1) {
await Future.delayed(retryDelay);
}
}
debugPrint('Fragment FAILED after $maxRetries tries');
return false;
}
@override
void dispose() {
platform.setMethodCallHandler(null);
_listener.dispose();
super.dispose();
}
@override
Widget build(BuildContext context) {
return PlatformViewLink(
viewType: 'eu.weblibre/gecko',
surfaceFactory: (context, controller) {
return AndroidViewSurface(
controller: controller as AndroidViewController,
gestureRecognizers: const <Factory<OneSequenceGestureRecognizer>>{},
hitTestBehavior: PlatformViewHitTestBehavior.opaque,
);
},
onCreatePlatformView: (PlatformViewCreationParams params) {
return PlatformViewsService.initExpensiveAndroidView(
id: params.id,
viewType: 'eu.weblibre/gecko',
layoutDirection: TextDirection.ltr,
creationParams: {},
creationParamsCodec: const StandardMessageCodec(),
)
..addOnPlatformViewCreatedListener((value) {
params.onPlatformViewCreated(value);
SchedulerBinding.instance.addPostFrameCallback((_) async {
await widget.preInitializationStep?.call();
await _showNativeFragment();
await widget.postInitializationStep?.call();
});
})
// ignore: discarded_futures that hos it is done in docs
..create();
},
);
}
}