webview_flutter package - Unable to establish connection on channel. error - flutter

I am trying to do WebView action with flutter_webview package. However, I am getting an error like this:
E/flutter (21358): [ERROR:flutter/runtime/dart_vm_initializer.cc(41)] Unhandled Exception: PlatformException(error, java.lang.IllegalStateException: Trying to create a platform view of unregistered type: plugins.flutter.io/webview
Codes:
import 'package:flutter/material.dart';
import 'package:webview_flutter/webview_flutter.dart';
class ShelterMapPage extends StatefulWidget {
const ShelterMapPage({super.key});
#override
State<ShelterMapPage> createState() => _ShelterMapPageState();
}
final controller = WebViewController()
..setJavaScriptMode(JavaScriptMode.unrestricted)
..setBackgroundColor(const Color(0x00000000))
..setNavigationDelegate(
NavigationDelegate(
onProgress: (int progress) {
// Update loading bar.
},
onPageStarted: (String url) {},
onPageFinished: (String url) {},
onWebResourceError: (WebResourceError error) {},
onNavigationRequest: (NavigationRequest request) {
if (request.url.startsWith('https://www.youtube.com/')) {
return NavigationDecision.prevent;
}
return NavigationDecision.navigate;
},
),
)
..loadRequest(Uri.parse('https://flutter.dev'));
class _ShelterMapPageState extends State<ShelterMapPage> {
#override
Widget build(BuildContext context) {
return Scaffold(body: WebViewWidget(controller: controller));
}
}
Why am I getting an error and how can I solve it? Thanks for help.

Related

Why Flutter hybrid app does not start? I have used webview and google mobad that should show an ad when the app started

I am about to create an app using webview in flutter language and I want to show open app in mobad when the app opened or started.
The code you provided is written in Dart programming language. It initializes an instance of the "AppOpenAdManager" class and calls the "loadAd" method on it. Then, it initializes an instance of the "AppLifecycleReactor" class and passes the "appOpenAdManager" instance to its constructor.
The purpose of this code is to load and manage an advertisement using the "AppOpenAdManager" class, and to monitor the lifecycle events of the app using the "AppLifecycleReactor" class.
I am very new to flutter, thank you for helping me.
here's main.dart
import 'package:flutter/material.dart';
import 'package:webview_flutter/webview_flutter.dart';
import 'package:google_mobile_ads/google_mobile_ads.dart';
import 'package:factorial/app_open_ad_manager.dart';
import 'app_lifecycle_reactor.dart';
void main() {
WidgetsFlutterBinding.ensureInitialized();
MobileAds.instance.initialize();
late AppLifecycleReactor _appLifecycleReactor;
AppOpenAdManager appOpenAdManager = AppOpenAdManager()..loadAd();
_appLifecycleReactor =
AppLifecycleReactor(appOpenAdManager: appOpenAdManager);
runApp(
const MaterialApp(
debugShowCheckedModeBanner: false,
home: WebViewApp(),
),
);
}
class WebViewApp extends StatefulWidget {
const WebViewApp({super.key});
#override
State<WebViewApp> createState() => _WebViewAppState();
}
class _WebViewAppState extends State<WebViewApp> with WidgetsBindingObserver {
late final WebViewController controller;
#override
void initState() {
super.initState();
WidgetsBinding.instance.addObserver(this);
controller = WebViewController()
..setJavaScriptMode(JavaScriptMode.unrestricted)
..loadRequest(
Uri.parse('https://example.com'),
);
}
#override
Widget build(BuildContext context) {
return Scaffold(
body: WebViewWidget(
controller: controller,
),
);
}
}
here's app_open_ad_manager.dart
import 'package:google_mobile_ads/google_mobile_ads.dart';
import 'dart:io' show Platform;
class AppOpenAdManager {
String adUnitId = Platform.isAndroid
? 'ca-app-pub-3940256099942544/3419835294'
: 'ca-app-pub-3940256099942544/5662855259';
AppOpenAd? _appOpenAd;
bool _isShowingAd = false;
void loadAd() {
AppOpenAd.load(
adUnitId: adUnitId,
orientation: AppOpenAd.orientationPortrait,
request: AdRequest(),
adLoadCallback: AppOpenAdLoadCallback(
onAdLoaded: (ad) {
_appOpenAd = ad;
},
onAdFailedToLoad: (error) {
print('AppOpenAd failed to load: $error');
// Handle the error.
},
),
);
}
void showAdIfAvailable() {
if (!isAdAvailable) {
print('Tried to show ad before available.');
loadAd();
return;
}
if (_isShowingAd) {
print('Tried to show ad while already showing an ad.');
return;
}
// Set the fullScreenContentCallback and show the ad.
_appOpenAd!.fullScreenContentCallback = FullScreenContentCallback(
onAdShowedFullScreenContent: (ad) {
_isShowingAd = true;
print('$ad onAdShowedFullScreenContent');
},
onAdFailedToShowFullScreenContent: (ad, error) {
print('$ad onAdFailedToShowFullScreenContent: $error');
_isShowingAd = false;
ad.dispose();
_appOpenAd = null;
},
onAdDismissedFullScreenContent: (ad) {
print('$ad onAdDismissedFullScreenContent');
_isShowingAd = false;
ad.dispose();
_appOpenAd = null;
loadAd();
},
);
}
/// Whether an ad is available to be shown.
bool get isAdAvailable {
return _appOpenAd != null;
}
}
and here's
import 'app_open_ad_manager.dart';
import 'package:google_mobile_ads/google_mobile_ads.dart';
/// Listens for app foreground events and shows app open ads.
class AppLifecycleReactor {
final AppOpenAdManager appOpenAdManager;
AppLifecycleReactor({required this.appOpenAdManager});
void listenToAppStateChanges() {
AppStateEventNotifier.startListening();
AppStateEventNotifier.appStateStream
.forEach((state) => _onAppStateChanged(state));
}
void _onAppStateChanged(AppState appState) {
// Try to show an app open ad if the app is being resumed and
// we're not already showing an app open ad.
if (appState == AppState.foreground) {
appOpenAdManager.showAdIfAvailable();
}
}
}
I was following this Tutorial and I got all the codes there.

How to handle deeplinking in Flutter using routes

I'm attempting to build deeplink functionality and so far the initial start of the app and retrieving parameters from the deeplink is going fine.
However I am having issues navigating to a screen after I deeplink into the app. How should I do this?
My code looks like this:
void main() {
runApp(MyApp());
}
class MyApp extends StatefulWidget {
#override
_MyAppState createState() => _MyAppState();
}
class _MyAppState extends State<MyApp> with SingleTickerProviderStateMixin {
Uri _latestUri;
Object _err;
StreamSubscription _sub;
#override void initState() {
super.initState();
_handleIncomingLinks();
}
#override void dispose() {
_sub?.cancel();
super.dispose();
}
void _handleIncomingLinks() {
_sub = uriLinkStream.listen((Uri uri) {
if (!mounted) return;
print('got uri: $uri'); // printed: got uri: myapp://?key1=test
setState(() {
_latestUri = uri;
_err = null;
Navigator.pushNamed(context, 'login'); // This doesn't work because the context does not include navigator
});
}, onError: (Object err) {
if (!mounted) return;
print('got err: $err');
setState(() {
_latestUri = null;
if (err is FormatException) {
_err = err;
} else {
_err = null;
}
});
});
}
#override Widget build(BuildContext context) {
return MaterialApp(
initialRoute: 'splash-screen',
onGenerateRoute: (settings) {
switch (settings.name) {
case 'splash-screen':
return
PageTransition(
child: BlocProvider(
create: (context) => SplashScreenCubit(APIRepository(
apiClient: APIClient(httpClient: http.Client()))),
child: SplashScreen(),
),
type: PageTransitionType.rightToLeft,
settings: settings);
break;
case 'create-account':
return PageTransition(
child: BlocProvider(
create: (context) => CreateAccountScreenCubit(
APIRepository(
apiClient: APIClient(httpClient: http.Client()))),
child: CreateAccountScreen(),
),
type: PageTransitionType.rightToLeft,
settings: settings);
break;
case 'login':
return PageTransition(
child: BlocProvider(
create: (context) => LoginScreenCubit(APIRepository(
apiClient: APIClient(httpClient: http.Client()))),
child: LoginScreen(),
),
type: PageTransitionType.rightToLeft,
settings: settings);
break;
default:
return null;
},
);
}
}
If what you needed is to be able to navigate without getting the context from Navigtor.of as you want to handling deeplink, you need to use navigatorKey property, you can read the details here.
then your code will be look like this. [EDITED, I add where to add the navigator key on the material app]
void main() { ... }
class MyApp extends StatefulWidget { ... }
class _MyAppState extends State<MyApp> with SingleTickerProviderStateMixin {
Uri _latestUri;
Object _err;
GlobalKey<NavigatorState> navigatorKey = GlobalKey();
StreamSubscription _sub;
#override void initState() { ... }
#override void dispose() { ... }
void _handleIncomingLinks() {
_sub = uriLinkStream.listen((Uri uri) {
if (!mounted) return;
print('got uri: $uri'); // printed: got uri: myapp://?key1=test
setState(() {
_latestUri = uri;
_err = null;
});
// use the navigatorkey currentstate to navigate to the page you are intended to visit
navigatorKey.currentState.pushNamedAndRemoveUntil('login', (route) => false);
}, onError: (Object err) { ... });
#override Widget build(BuildContext context) {
return MaterialApp(
...
navigatorKey: navigatorKey,
...
);
}
}
Your deep link stream can be triggered before the build method, but you are not allowed to call Navigator at the time. So, you can fix it using addPostFrameCallback provided by SchedulerBinding:
addPostFrameCallback
Schedule a callback for the end of this frame.
Does not request a new frame.
This callback is run during a frame, just after the persistent frame
callbacks (which is when the main rendering pipeline has been
flushed). If a frame is in progress and post-frame callbacks haven't
been executed yet, then the registered callback is still executed
during the frame. Otherwise, the registered callback is executed
during the next frame.
The callbacks are executed in the order in which they have been added.
Post-frame callbacks cannot be unregistered. They are called exactly
once.
...
void _handleIncomingLinks() {
_sub = uriLinkStream.listen((Uri uri) {
if (!mounted) return;
print('got uri: $uri'); // printed: got uri: myapp://?key1=test
setState(() {
_latestUri = uri;
_err = null;
// Call your navigator inside addPostFrameCallback
WidgetsBinding.instance?.addPostFrameCallback((_) {
Navigator.pushNamed(context, 'login');
});
});
}, onError: (Object err) {
if (!mounted) return;
print('got err: $err');
setState(() {
_latestUri = null;
if (err is FormatException) {
_err = err;
} else {
_err = null;
}
});
});
}
...

The method 'load' was called on null. error while trying to show banner add with flutter

I was trying to put the banner ad on my homepage everything works fine but before the app starts I get a:
The method 'load' was called on null.
Receiver: null
Tried calling: load()
The app opens and it shows the banner with the test ad but I want to know the reason behind this error.
here is my code:
class Acilis extends StatefulWidget {
#override
_AcilisState createState() => _AcilisState();
}
class _AcilisState extends State<Acilis> {
BannerAd banner;
#override
void didChangeDependencies() {
// TODO: implement didChangeDependencies
super.didChangeDependencies();
final adState = Provider.of<AdState>(context);
adState.initialization.then((status) {
setState(() {
banner = BannerAd(
size: AdSize.banner,
adUnitId: adState.bannerAdUnitId,
listener: adState.adlistener,
request: AdRequest())
..load();
});
});
}
#override
Widget build(BuildContext context) {
banner.load();
return Material(
Container(
height: 50,
child: AdWidget(ad: banner),
),
I skim the no relevant code here. this is the only two place that has the code of the banner. there is also an Admob_Service.dart file with the Ad State Class:
import 'dart:io';
import 'package:google_mobile_ads/google_mobile_ads.dart';
class AdState {
Future<InitializationStatus> initialization;
AdState(this.initialization);
String get bannerAdUnitId => Platform.isAndroid
? 'ca-app-pub-3940256099942544/6300978111'
: 'ca-app-pub-3940256099942544/6300978111'; //test id
BannerAdListener get adlistener => _adListener;
BannerAdListener _adListener = BannerAdListener(
onAdLoaded: (ad) => print('Ad Loaded'),
onAdClosed: (ad) => print('Ad Closed'),
onAdFailedToLoad: (ad, error) => print('Ad failed to load'),
onAdOpened: (ad) => print('Ad Opened'));
}

flutter) Bluetooth provider error =>setState() or markNeedsBuild() called during build

I'm trying to develop "BLE Control App" with using flutter_Blue.
I added a tab bar so I want to Maintain Bluetooth State "Connect".
so I'm trying to use Provider, To set connection state but I have an error like this.
**======== Exception caught by foundation library ====================================================
The following assertion was thrown while dispatching notifications for BluetoothProvider:
setState() or markNeedsBuild() called during build.
This _InheritedProviderScope<BluetoothProvider> widget cannot be marked as needing to build because the framework is already in the process of building widgets. A widget can be marked as needing to be built during the build phase only if one of its ancestors is currently building. This exception is allowed because the framework builds parent widgets before children, which means a dirty descendant will always be built. Otherwise, the framework might not visit this widget during this build phase.
The widget on which setState() or markNeedsBuild() was called was: _InheritedProviderScope<BluetoothProvider>
value: Instance of 'BluetoothProvider'
listening to value
The widget which was currently being built when the offending call was made was: Consumer<BluetoothProvider>
dirty
dependencies: [_InheritedProviderScope<BluetoothProvider>]
When the exception was thrown, this was the stack:
#0 Element.markNeedsBuild.<anonymous closure> (package:flutter/src/widgets/framework.dart:4138:11)
#1 Element.markNeedsBuild (package:flutter/src/widgets/framework.dart:4153:6)
#2 _InheritedProviderScopeElement.markNeedsNotifyDependents (package:provider/src/inherited_provider.dart:531:5)
#3 ChangeNotifier.notifyListeners (package:flutter/src/foundation/change_notifier.dart:243:25)
#4 BluetoothProvider.startScan (package:flutter_joystick/provider/bluetooth_provider.dart:46:5)
...
The BluetoothProvider sending notification was: Instance of 'BluetoothProvider'**
this is my bluetooth provider code
class BluetoothProvider with ChangeNotifier{
final String SERVICE_UUID = "0000ffe0-0000-1000-8000-00805f9b34fb";
final String CHARACTERISTIC_UUID="0000ffe1-0000-1000-8000-00805f9b34fb";
final String TARGET_DEVICE_NAME="HMSoft";
FlutterBlue flutterBlue = FlutterBlue.instance;
StreamSubscription<ScanResult> scanSubScription;
BluetoothDevice targetDevice;
BluetoothCharacteristic targetCharacteristic;
BluetoothState bluetoothState;
String connectionText="";
String joystick="";
startScan(){
connectionText="Start Scanning";
scanSubScription = flutterBlue.scan().listen((scanResult){
if(scanResult.device.name==TARGET_DEVICE_NAME){
print("Device Found");
stopScan();
connectionText="Found Target Device";
targetDevice = scanResult.device;
}
}, onDone: () => stopScan());
notifyListeners();
}
stopScan(){
scanSubScription?.cancel();
scanSubScription=null;
notifyListeners();
}
connectToDevice() async{
if(targetDevice==null) return;
connectionText = "Device Connecting";
await targetDevice.connect();
print("Device Connected");
connectionText="Device Connected";
discoverServices();
notifyListeners();
}
disconnectFromDevice(){
if(targetDevice==null) return;
targetDevice.disconnect();
connectionText="Device Disconnected";
notifyListeners();
}
discoverServices() async{
if(targetDevice==null) return;
List<BluetoothService> services = await targetDevice.discoverServices();
services.forEach((service) {
if(service.uuid.toString() == SERVICE_UUID){
service.characteristics.forEach((characteristc) {
if (characteristc.uuid.toString() == CHARACTERISTIC_UUID) {
targetCharacteristic = characteristc;
writeData("Connect Complete!\r\n");
connectionText = "All Ready with ${targetDevice.name}";
}
});
}
}
);
notifyListeners();
}
writeData(String data) async{
if(targetCharacteristic==null) return;
List<int> bytes = utf8.encode(data);
await targetCharacteristic.write(bytes);
notifyListeners();
}
}
Funny, the Bluetooth connection is progressing, but the error written above keeps coming up through the console window.
The first page of the Tab Bar is the joystick page, and Bluetooth is connected due to an error, but the joystick is not working.
Here is Joystick code
class JoyPad extends StatefulWidget {
#override
_JoyPadState createState() => _JoyPadState();
}
class _JoyPadState extends State<JoyPad> {
BluetoothProvider _bluetoothProvider;
#override
Widget build(BuildContext context) {
_bluetoothProvider = Provider.of<BluetoothProvider>(context,listen:false);
return Consumer<BluetoothProvider>(
builder:(context,provider,child) {
_bluetoothProvider.startScan();
return Scaffold(
appBar: AppBar(
title: Text(_bluetoothProvider.connectionText),
backgroundColor: Colors.indigoAccent,
actions: <Widget>[
IconButton(
icon: Icon(Icons.bluetooth), iconSize: 30,
onPressed: () {
_bluetoothProvider.connectToDevice();
print(_bluetoothProvider.bluetoothState.toString());
},
),
IconButton(
icon: Icon(Icons.bluetooth_disabled), iconSize: 30,
onPressed: () {
_bluetoothProvider.disconnectFromDevice();
print(_bluetoothProvider.bluetoothState.toString());
}),
],
),
body: joystickWidget(),
);
});
}
}
Additionally, the provider does not "setState" so I try to display connection text according to the status change on the App Bar, but it is not possible.
I would also appreciate it if you could tell me how to solve it.
You are actually encountering this error because you try to rebuild the widget tree while it's being build.
Your call on _bluetoothProvider.startScan();in your Consumer's builder method will call the notifyListeners method which actually tries to rebuild the tree while it's being build, thus that exception will be thrown.
WHY?
The Consumer widget is actually listening to changes on your BluetoothProvider; so when you call the notifyListeners on the BluetothProvider class, the Consumer tries to rebuild itself, which is not authorized.
A solution would be to first build the tree, and then call the startScan method.
You could try this:
Provider Code
class BluetoothProvider with ChangeNotifier{
final String SERVICE_UUID = "0000ffe0-0000-1000-8000-00805f9b34fb";
final String CHARACTERISTIC_UUID="0000ffe1-0000-1000-8000-00805f9b34fb";
final String TARGET_DEVICE_NAME="HMSoft";
FlutterBlue flutterBlue = FlutterBlue.instance;
StreamSubscription<ScanResult> scanSubScription;
BluetoothDevice targetDevice;
BluetoothCharacteristic targetCharacteristic;
BluetoothState bluetoothState;
String connectionText="";
String joystick="";
startScan() {
connectionText="Start Scanning";
scanSubScription = flutterBlue.scan().listen((scanResult){
if(scanResult.device.name==TARGET_DEVICE_NAME){
print("Device Found");
stopScan();
connectionText="Found Target Device";
targetDevice = scanResult.device;
}
}, onDone: () => stopScan());
notifyListeners();
}
stopScan() {
scanSubScription?.cancel();
scanSubScription=null;
notifyListeners();
}
connectToDevice() async{
if(targetDevice==null) return;
connectionText = "Device Connecting";
await targetDevice.connect();
print("Device Connected");
connectionText="Device Connected";
discoverServices();
notifyListeners();
}
disconnectFromDevice(){
if(targetDevice==null) return;
targetDevice.disconnect();
connectionText="Device Disconnected";
notifyListeners();
}
discoverServices() async {
if(targetDevice==null) return;
List<BluetoothService> services = await targetDevice.discoverServices();
services.forEach((service) {
if(service.uuid.toString() == SERVICE_UUID){
service.characteristics.forEach((characteristc) {
if (characteristc.uuid.toString() == CHARACTERISTIC_UUID) {
targetCharacteristic = characteristc;
writeData("Connect Complete!\r\n");
connectionText = "All Ready with ${targetDevice.name}";
}
});
}
});
notifyListeners();
}
writeData(String data) async{
if(targetCharacteristic==null) return;
List<int> bytes = utf8.encode(data);
await targetCharacteristic.write(bytes);
notifyListeners();
}
}
Widget code
class JoyPad extends StatefulWidget {
#override
_JoyPadState createState() => _JoyPadState();
}
class _JoyPadState extends State<JoyPad> {
#override
void initState() {
super.initState();
WidgetsBinding.instance.addPostFrameCallback((_) {
// The code in this block will be executed after the build method
context.read<BluetoothProvider>().startScan();
});
}
#override
Widget build(BuildContext context) {
return Consumer<BluetoothProvider>(
builder:(context,provider,child) {
return Scaffold(
appBar: AppBar(
title: Text(_bluetoothProvider.connectionText),
backgroundColor: Colors.indigoAccent,
actions: <Widget>[
IconButton(
icon: Icon(Icons.bluetooth), iconSize: 30,
onPressed: () {
_bluetoothProvider.connectToDevice();
print(_bluetoothProvider.bluetoothState.toString());
},
),
IconButton(
icon: Icon(Icons.bluetooth_disabled), iconSize: 30,
onPressed: () {
_bluetoothProvider.disconnectFromDevice();
print(_bluetoothProvider.bluetoothState.toString());
},
),
],
),
body: joystickWidget(),
);
});
}
}
}
context.read<BluetoothProvider>().startScan(); is a shortcut for Provider.of<BluetoothProvider>(context, listen: false).startScan() : it basically does the same thing.

Where to handle Firebase Dynamic Links in Flutter?

I use Firebase dynamic links and also named routes. What I want is to install a global listener for the dynamic link events and forward to register page if a token is provided. In the code below I got the exception The context used to push or pop routes from the Navigator must be that of a widget that is a descendant of a Navigator widget. which means I have to put navigation code below the home: property of MaterialApp. But when doing this I had to implement the dynamic links event handler for earch route.
class MyApp extends StatelessWidget {
String title = "Framr";
#override
Widget build(BuildContext context) {
FirebaseDynamicLinks.instance.onLink(
onSuccess: (linkData) {
if (linkData != null) {
try {
Navigator.pushNamed(context, '/register', arguments: linkData);
// throws: The context used to push or pop routes from the Navigator must be that of a widget that is a descendant of a Navigator widget.
} catch(e) {
print(e);
}
}
return null;
}
);
return MaterialApp(
title: "...",
home: LoginPage(),
routes: {
'/createEvent': (context) => CreateEventPage(),
'/showEvent': (context) => ShowEventPage(),
'/register': (context) => RegisterPage(),
},
);
}
}
I was able to get this work by following the example provided from the dynamic link README with the use of the no_context_navigation package or GlobalKey to workaround around the lack of context to call Navigator.pushNamed(...). Note: You don't have to use no_context_navigation. You can implement the no context routing yourself. Here's an example.
// Add this
import 'package:no_context_navigation/no_context_navigation.dart';
void main() {
runApp(MaterialApp(
title: 'Dynamic Links Example',
// Add this
navigatorKey: NavigationService.navigationKey,
routes: <String, WidgetBuilder>{
'/': (BuildContext context) => MyHomeWidget(), // Default home route
'/helloworld': (BuildContext context) => MyHelloWorldWidget(),
},
));
}
class MyHomeWidgetState extends State<MyHomeWidget> {
.
.
.
#override
void initState() {
super.initState();
this.initDynamicLinks();
}
void initDynamicLinks() async {
FirebaseDynamicLinks.instance.onLink(
onSuccess: (PendingDynamicLinkData dynamicLink) async {
// Add this.
final NavigationService navService = NavigationService();
final Uri deepLink = dynamicLink?.link;
if (deepLink != null) {
// This doesn't work due to lack of context
// Navigator.pushNamed(context, deepLink.path);
// Use this instead
navService.pushNamed('/helloworld', args: dynamicLink);
}
},
onError: (OnLinkErrorException e) async {
print('onLinkError');
print(e.message);
}
);
final PendingDynamicLinkData data = await FirebaseDynamicLinks.instance.getInitialLink();
final Uri deepLink = data?.link;
if (deepLink != null) {
// This doesn't work due to lack of context
// Navigator.pushNamed(context, deepLink.path);
// Use this instead
navService.pushNamed('/helloworld', args: dynamicLink);
}
}
.
.
.
}
// pubspec.yaml
no_context_navigation: ^1.0.4