I am working on Blockly(Blockly is a client-side library for the programming language JavaScript for creating block-based visual programming languages and editors) for the flutter desktop windows.
I am using the webview_windows
package to display 'https://developers.google.com/blockly
' in a webview.
below is my code
import 'package:flutter/material.dart';
import 'package:flutter/services.dart';
import 'dart:async';
import 'package:webview_windows/webview_windows.dart';
final navigatorKey = GlobalKey<NavigatorState>();
void main() {
runApp(MyApp());
}
class MyApp extends StatelessWidget {
@override
Widget build(BuildContext context) {
return MaterialApp(navigatorKey: navigatorKey, home: ExampleBrowser());
}
}
class ExampleBrowser extends StatefulWidget {
@override
State<ExampleBrowser> createState() => _ExampleBrowser();
}
class _ExampleBrowser extends State<ExampleBrowser> {
final _controller = WebviewController();
final _textController = TextEditingController();
bool _isWebviewSuspended = false;
@override
void initState() {
super.initState();
initPlatformState();
}
Future<void> initPlatformState() async {
try {
await _controller.initialize();
_controller.url.listen((url) {
_textController.text = URL;
});
await _controller.setBackgroundColor(Colors.transparent);
await _controller.setPopupWindowPolicy(WebviewPopupWindowPolicy.deny);
await _controller.loadUrl('https://developers.google.com/blockly');
if (!mounted) return;
setState(() {});
} on PlatformException catch (e) {
WidgetsBinding.instance?.addPostFrameCallback((_) {
showDialog(
context: context,
builder: (_) => AlertDialog(
title: Text('Error'),
content: Column(
mainAxisSize: MainAxisSize.min,
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Text('Code: ${e.code}'),
Text('Message: ${e.message}'),
],
),
actions: [
TextButton(
child: Text('Continue'),
onPressed: () {
Navigator.of(context).pop();
},
)
],
));
});
}
}
Widget compositeView() {
if (!_controller.value.isInitialized) {
return const Text(
'Not Initialized',
style: TextStyle(
fontSize: 24.0,
fontWeight: FontWeight.w900,
),
);
} else {
return Padding(
padding: EdgeInsets.all(20),
child: Column(
children: [
Card(
elevation: 0,
child: TextField(
decoration: InputDecoration(
hintText: 'URL',
contentPadding: EdgeInsets.all(10.0),
suffixIcon: IconButton(
icon: Icon(Icons.refresh),
onPressed: () {
_controller.reload();
},
)),
textAlignVertical: TextAlignVertical.center,
controller: _textController,
onSubmitted: (val) {
_controller.loadUrl(val);
},
),
),
Expanded(
child: Card(
color: Colors.transparent,
elevation: 0,
clipBehavior: Clip.antiAliasWithSaveLayer,
child: Stack(
children: [
Webview(
_controller,
permissionRequested: _onPermissionRequested,
),
StreamBuilder<LoadingState>(
stream: _controller.loadingState,
builder: (context, snapshot) {
if (snapshot.hasData &&
snapshot.data == LoadingState.loading) {
return LinearProgressIndicator();
} else {
return SizedBox();
}
}),
],
))),
],
),
);
}
}
@override
Widget build(BuildContext context) {
return Scaffold(
floatingActionButton: FloatingActionButton(
tooltip: _isWebviewSuspended ? 'Resume webview' : 'Suspend webview',
onPressed: () async {
if (_isWebviewSuspended) {
await _controller.resume();
} else {
await _controller.suspend();
}
setState(() {
_isWebviewSuspended = !_isWebviewSuspended;
});
},
child: Icon(_isWebviewSuspended ? Icons.play_arrow : Icons.pause),
),
appBar: AppBar(
title: StreamBuilder<String>(
stream: _controller.title,
builder: (context, snapshot) {
return Text(
snapshot.hasData ? snapshot.data! : 'WebView (Windows) Example');
},
)),
body: Center(
child: compositeView(),
),
);
}
Future<WebviewPermissionDecision> _onPermissionRequested(
String url, WebviewPermissionKind kind, bool isUserInitiated) async {
final decision = await showDialog<WebviewPermissionDecision>(
context: navigatorKey.currentContext!,
builder: (BuildContext context) => AlertDialog(
title: const Text('WebView permission requested'),
content: Text('WebView has requested permission \'$kind\''),
actions: <Widget>[
TextButton(
onPressed: () =>
Navigator.pop(context, WebviewPermissionDecision.deny),
child: const Text('Deny'),
),
TextButton(
onPressed: () =>
Navigator.pop(context, WebviewPermissionDecision.allow),
child: const Text('Allow'),
),
],
),
);
return decision ?? WebviewPermissionDecision.none;
}
}
I had the same issue in a diffetent scenario, what I did was to do a
Future.delayed
of 1 second to the other functions inside the controller, in my case the exception is thrown inside setBackgroundColor (which was called right afterinitialize()
) sayingvalue.isInitialized
is not true after controller is initialized, here an example:Here the solution:
For some reason even though we initialize the controller, the rest of the methods inside of it do not get notify about the value inside the controller when it changes, even though we use await to complete the initialization.