Flutter Web, HtmlElementView, "Removing disallowed attribute" - flutter

I am trying to Create HtmlElementView for Playstore Application Badge in Flutter Web?
I tried the following as mentioned here
Widget _getAppStoreBadgeView() {
final NodeValidatorBuilder _htmlValidator =
new NodeValidatorBuilder.common()
..allowElement('a', attributes: ['data-target', 'data-toggle'])
..allowElement('button', attributes: ['data-target', 'data-toggle']);
ui.platformViewRegistry.registerViewFactory("idid", (int viewId) {
final element = DivElement()
..setInnerHtml(r"""
<a href='https://play.google.com/store/apps/details?id=com.shahxad.evendor&pcampaignid=pcampaignidMKT-Other-global-all-co-prtnr-py-PartBadge-Mar2515-1'><img alt='Get it on Google Play' src='https://play.google.com/intl/en_us/badges/static/images/badges/en_badge_web_generic.png'/></a>
""", validator: _htmlValidator)
..style.width = "100%"
..style.height = "100%"
..style.border = 'none';
return element;
});
return Container(
child: HtmlElementView(viewType: "idid"),
height: 150,
width: 400,
);
}
But it's not working as I am getting like this.
I am getting this message in the Chrome console.
Removing disallowed attribute
html_dart2js.dart:39901 Removing disallowed attribute <A href="https://play.google.com/store/apps/details?id=com.shahxad.flutter_tex_example&pcampaignid=pcampaignidMKT-Other-global-all-co-prtnr-py-PartBadge-Mar2515-1">
html_dart2js.dart:39901 Removing disallowed attribute <IMG src="https://play.google.com/intl/en_us/badges/static/images/badges/en_badge_web_generic.png">

Use NodeValidatorBuilder from the dart:html library with predefined rules allowHtml5, allowNavigation, allowImages and define class ItemUrlPolicy to allow navigation from generated html-code.
import 'dart:html' as html;
import 'dart:ui' as ui;
import 'package:flutter/material.dart';
class ItemUrlPolicy implements html.UriPolicy {
RegExp regex = RegExp(r'(?:http://|https://)?.*');
bool allowsUri(String uri) {
return regex.hasMatch(uri);
}
}
class MyWidget extends StatefulWidget {
#override
MyWidgetState createState() => MyWidgetState();
}
class MyWidgetState extends State<MyWidget> {
html.DivElement _element;
#override
void initState() {
super.initState();
_element = html.DivElement()
..appendHtml("""
<a href='https://play.google.com/store/apps/details?id=com.shahxad.evendor&pcampaignid=pcampaignidMKT-Other-global-all-co-prtnr-py-PartBadge-Mar2515-1'>
<img alt='Get it on Google Play' src='https://play.google.com/intl/en_us/badges/static/images/badges/en_badge_web_generic.png'/>
</a>
""",
validator: html.NodeValidatorBuilder()
..allowHtml5(uriPolicy: ItemUrlPolicy())
..allowNavigation(ItemUrlPolicy())
..allowImages(ItemUrlPolicy()));
// ignore: undefined_prefixed_name
ui.platformViewRegistry.registerViewFactory('demo-view', (int viewId) => _element);
}
#override
Widget build(BuildContext context) {
return HtmlElementView(key: UniqueKey(), viewType: "demo-view");
}
}

Related

Trying to implement AdSense in Flutter Web - TagError: adsbygoogle.push() error: No slot size for availableWidth=0

I have a Flutter app that uses AdMob but that doesn't work for web. As suggested by some solutions, I am trying to use an IFrame to display an AdSense ad. I have almost the same code as one example that worked for others, but for me, it does not show the ad and I get an error TagError: adsbygoogle.push() error: No slot size for availableWidth=0. Is there a better way to go about this? If not, what part of my code is causing the error?
adview.html:
<script async src="https://pagead2.googlesyndication.com/pagead/js/adsbygoogle.js?client=ca-pub-<replace>"
crossorigin="anonymous"></script>
<!-- Web Flutter Globe -->
<ins class="adsbygoogle"
style="display:inline-block;width:320px;height:80px"
data-ad-client="ca-pub-<replace>"
data-ad-slot="<replace>"
data-ad-format="auto"
data-full-width-responsive="true"></ins>
<script>
(adsbygoogle = window.adsbygoogle || []).push({});
</script>
adview.dart:
import 'mobile_implementation.dart'
if (dart.library.html) 'web_implementation.dart'
as package;
import 'package:flutter/foundation.dart';
import 'package:flutter/widgets.dart';
class AdsenseWeb extends StatelessWidget {
const AdsenseWeb({super.key, required this.height, required this.width});
final double height;
final double width;
#override
Widget build(BuildContext context) {
return kIsWeb
? package.AdsenseWeb.adsenseAdsView(width, height)
: const SizedBox();
}
}
web_implementation.dart:
import 'dart:html';
import 'dart:ui' as ui;
import 'package:flutter/material.dart';
class AdsenseWeb {
static Widget adsenseAdsView(double width, double height) {
// ignore: undefined_prefixed_name
ui.platformViewRegistry.registerViewFactory(
'adViewType',
(int viewID) => IFrameElement()
..width = '$width'
..height = '$height'
..src = 'adview.html'
..style.border = 'none');
return SizedBox(
height: height,
width: width,
child: const HtmlElementView(
viewType: 'adViewType',
),
);
}
}
mobile_implementation.dart:
import 'package:flutter/material.dart';
class AdsenseWeb {
static Widget adsenseAdsView(double width, double height) {
return const SizedBox();
}
}

Could not find the correct Provider<Movies> above this MyApp Widget

So, I'm using BLoC and Provider packages in one app.
In my 'moviesprovider.dart' I am fetching some data from my API which returns a json, when app is opening first time. How can I get access to Provider.of(context) from main.dart in MultiProvider? Basically, I want to get access to the same instance of List movies, but don't know how.
The error I'm getting:
Error: Could not find the correct Provider above this MyApp Widget
This happens because you used a BuildContext that does not include the provider
of your choice.
Code:
Main.dart
void main() {
runApp(MyApp());
}
class MyApp extends StatelessWidget {
MyApp({Key key}) : super(key: key);
#override
Widget build(BuildContext context) {
return MultiProvider(
providers: [
ChangeNotifierProvider.value(
value: Movies(),
),
Provider<SwipeBloc>(create: (_) {
SwipeBloc()
..add(
LoadMoviesEvent(
movies: context.read<Movies>().movies,
),
);
}),
ChangeNotifierProvider.value(
value: User(),
),
ChangeNotifierProvider.value(
value: Auth(),
),
],
child: ...
}
}
movies_provider.dart
import 'dart:convert';
import 'package:flutter/material.dart';
import 'package:movies_recomendations/constants.dart';
import 'package:http/http.dart' as http;
import './single_movie_provider.dart';
class Movies with ChangeNotifier {
String plotText = "";
List<Movie> _movies = [];
List<Movie> get movies {
return <Movie>[..._movies];
}
.....
Future<void> fetchAndSetMovies() async {
const url = 'http://192.168.1.142:8000/Desktop/textData.json';
try {
final response = await http.get(
Uri.parse(url),
);
String source = Utf8Decoder().convert(response.bodyBytes);
final extractedData =
List<Map<String, dynamic>>.from(json.decode(source));
final List<Movie> loadedMovies = [];
extractedData.forEach(
((movieInfo) => {
loadedMovies.add(Movie(
id: movieInfo['id'],
age: 12,
countries: List<String>.from(movieInfo['country']),
description: movieInfo['descriprion'],
frames: movieInfo['frames'],
genre: movieInfo['genre'],
poster: movieInfo['poster'],
premiereWorld: movieInfo['date'].toString(),
ratingIMDb: movieInfo['ratingIMDb'],
ratingKinopoisk: movieInfo['ratingKinopoisk'],
title: movieInfo['title'][1],
ifSeries: movieInfo['ifSeries'],
dateTo: movieInfo['dateTo'].toString(),
isFavourite: true,
seasons: movieInfo['seasons'],
)),
}),
);
_movies = loadedMovies;
notifyListeners();
} on Exception catch (e) {
print('error');
print(e.toString());
}
}
}
Swipe_event.dart
part of 'swipe_block.dart';
abstract class SwipeEvent extends Equatable {
const SwipeEvent();
#override
List<Object> get props => [];
}
class LoadMoviesEvent extends SwipeEvent {
final List<Movie> movies ;
LoadMoviesEvent({
required this.movies,
});
#override
List<Object> get props => [movies];
}
class SwipeLeftEvent extends SwipeEvent {
final Movie movie;
SwipeLeftEvent({
required this.movie,
});
#override
List<Object> get props => [movie];
}
class SwipeRightEvent extends SwipeEvent {
final Movie movie;
SwipeRightEvent({
required this.movie,
});
#override
List<Object> get props => [movie];
}
You probably need to move the code calling Provider.of(context) into its own widget. As the error implies you can't use Provider to retrieve dependencies within the same BuildContext you used to set the Provider scope. Creating a new widget will also generate a new BuildContext.
If you really need to use Provider.of(context) in the same class you define MultiProvider you could use the Builder widget to generate a new context.
So, to solve this problem you should NOT use BlocProvider in main.dart. You should use it in that direct widget where BLoC Provider will be implemented. So I use it in one screen - recomendations, so I write it there like this
class RecomendationsScreen extends StatelessWidget {
static const routeName = '/recomendations';
#override
Widget build(BuildContext context) {
return Scaffold(
backgroundColor: kBackgroundColor,
body: BlocProvider(
create: (_) => SwipeBloc()
..add(
LoadMoviesEvent(
movies: Provider.of<Movies>(context).movies,
),
),
child: RecomendationsBody(),
),
);
}
}

Flutter web - Geolocator not working when uploaded to server

everyone.
I'm trying to develop a PWA with flutter 2.2.1 that shows a map using Mapbox_gl and displays the user current location using Geolocator.
So far everything works as expected while debuging the app, but when I run:
flutter build
or
flutter build --release
and then run
firebase deploy
the site gets uploaded, the map shows as intended and it asks for permissions but the user's location is never shown and Google Chrome's Console throws this error:
Uncaught TypeError: m.gfR is not a function
at Object.avh (main.dart.js:20405)
at main.dart.js:65755
at aiD.a (main.dart.js:5853)
at aiD.$2 (main.dart.js:34394)
at ahm.$1 (main.dart.js:34386)
at Rx.o1 (main.dart.js:35356)
at adi.$0 (main.dart.js:34770)
at Object.tQ (main.dart.js:5975)
at a5.mn (main.dart.js:34687)
at ada.$0 (main.dart.js:34731)
Here's the code I'm using on flutter:
mapbox.dart
import 'dart:async';
import 'dart:io';
import 'dart:math';
import 'package:flutter/foundation.dart';
import 'package:flutter/material.dart';
import 'package:flutter/widgets.dart';
import 'package:geolocator/geolocator.dart';
import 'package:kkc/main.dart';
import 'package:mapbox_gl/mapbox_gl.dart';
import 'package:kkc/services/location_service.dart';
class Mapbox extends StatefulWidget {
const Mapbox();
#override
State createState() => MapboxState();
}
class MapboxState extends State<Mapbox> {
final Random _rnd = new Random();
Position? _currentLocation;
LatLng _currentCoordinates = new LatLng(0,0);
final List<_PositionItem> _positionItems = <_PositionItem>[];
StreamSubscription<Position>? _positionStreamSubscription;
late MapboxMapController _mapController;
List<Marker> _markers = [];
List<_MarkerState> _markerStates = [];
CameraPosition _kInitialPosition = CameraPosition(
target: LatLng(19.4274418, -99.1682147),
zoom: 18.0,
tilt: 70,
);
void _addMarkerStates(_MarkerState markerState) {
_markerStates.add(markerState);
}
void _onMapCreated(MapboxMapController controller) {
_mapController = controller;
controller.addListener(() {
if (controller.isCameraMoving) {
_updateMarkerPosition();
}
});
}
void _onStyleLoadedCallback() {
_updateMarkerPosition();
}
void _onCameraIdleCallback() {
_updateMarkerPosition();
}
void _updateMarkerPosition() {
final coordinates = <LatLng>[];
for (final markerState in _markerStates) {
coordinates.add(markerState.getCoordinate());
}
_mapController.toScreenLocationBatch(coordinates).then((points) {
_markerStates.asMap().forEach((i, value) {
_markerStates[i].updatePosition(points[i]);
});
});
}
void _addMarker(Point<double> point, LatLng coordinates) {
setState(() {
_markers.add(Marker(_rnd.nextInt(100000).toString(), coordinates, point, _addMarkerStates));
});
}
#override
void initState() {
super.initState();
_getCurrentLocation();
}
#override
void dispose() {
super.dispose();
}
#override
Widget build(BuildContext context) {
return new Scaffold(
body: Stack(children: [
MapboxMap(
accessToken: Kukulcan.MAPBOX_ACCESS_TOKEN,
trackCameraPosition: true,
onMapCreated: _onMapCreated,
onCameraIdle: _onCameraIdleCallback,
onStyleLoadedCallback: _onStyleLoadedCallback,
initialCameraPosition: _kInitialPosition,
),
IgnorePointer(
ignoring: true,
child: Stack(
children: _markers,
))
]),
);
}
void _getCurrentLocation() async {
_currentLocation = await LocationService.startLocationService();
_currentCoordinates = new LatLng(_currentLocation!.latitude,_currentLocation!.longitude);
await _mapController.animateCamera(CameraUpdate.newLatLng(_currentCoordinates));
_addMarker(new Point(1, 1), _currentCoordinates);
if (_positionStreamSubscription == null) {
final positionStream = Geolocator.getPositionStream();
_positionStreamSubscription = positionStream.handleError((error) {
_positionStreamSubscription?.cancel();
_positionStreamSubscription = null;
}).listen((position) => setState(() => _positionItems.add(
_PositionItem(_PositionItemType.position, position.toString()))));
_positionStreamSubscription?.pause();
}
}
}
class Marker extends StatefulWidget {
final Point _initialPosition;
LatLng _coordinate;
final void Function(_MarkerState) _addMarkerState;
Marker(
String key, this._coordinate, this._initialPosition, this._addMarkerState)
: super(key: Key(key));
#override
State<StatefulWidget> createState() {
final state = _MarkerState(_initialPosition);
_addMarkerState(state);
return state;
}
}
class _MarkerState extends State with TickerProviderStateMixin {
final _iconSize = 80.0;
Point _position;
_MarkerState(this._position);
#override
Widget build(BuildContext context) {
var ratio = 1.0;
//web does not support Platform._operatingSystem
if (!kIsWeb) {
// iOS returns logical pixel while Android returns screen pixel
ratio = Platform.isIOS ? 1.0 : MediaQuery.of(context).devicePixelRatio;
}
return Positioned(
left: _position.x / ratio - _iconSize / 2,
top: _position.y / ratio - _iconSize / 2,
child: Image.asset('assets/img/pin.png', height: _iconSize));
}
void updatePosition(Point<num> point) {
setState(() {
_position = point;
});
}
LatLng getCoordinate() {
return (widget as Marker)._coordinate;
}
}
enum _PositionItemType {
permission,
position,
}
class _PositionItem {
_PositionItem(this.type, this.displayValue);
final _PositionItemType type;
final String displayValue;
}
Does anyone have an idea on what's the problem?
Cheers!
Anyway the solution i found is to use --no-sound-null-safety argument as stated by geolocat documentation
I quote:
NOTE: due to a bug in the dart:html library the web version of the Geolocator plugin does not work with sound null safety enabled and compiled in release mode. Running the App in release mode with sound null safety enabled results in a Uncaught TypeError (see issue #693). The current workaround would be to build your App with sound null safety disabled in release mode:

Error: The getter 'title' isn't defined for the type 'RSSParser'

I wanted to implements a simple tutorial of how parsing an Rss Feed with Flutter, here is my code:
import 'package:flutter/material.dart';
import 'package:webfeed/webfeed.dart';
import 'package:http/http.dart' as http;
import 'package:url_launcher/url_launcher.dart';
import 'package:cached_network_image/cached_network_image.dart';
class RSSParser extends StatefulWidget {
#override
_RSSParserState createState() => _RSSParserState();
}
class _RSSParserState extends State<RSSParser> {
final String url = "https://www.90min.com/posts.rss";
RssFeed _feed;
String _title;
static const String loadingFeedMsg = 'Loading Feed...';
static const String feedLoadErrorMsg = 'Error Loading Feed.';
static const String feedOpenErrorMsg = 'Error Opening Feed.';
Future<RssFeed> loadFeed() async{
try{
final client = http.Client();
final response = await client.get(url);
return RssFeed.parse(response.body);
}
catch(e){
}
return null;
}
updateTitle(title){
setState(() {
_title = title;
});
}
#override
void initState() {
// TODO: implement initState
super.initState();
updateTitle(widget.title);
}
updateFeed(feed){
setState(() {
_feed = feed;
});
}
#override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: Text(_title),
),
);
}
}
The problem is that i got a compilation error in that instruction
updateTitle(widget.title);
with the following error message:
The getter 'title' isn't defined for the type 'RSSParser'
In the tutorial, it works fine!!
Do you have an idea how to solve this?
Thank you
You haven't declared title for you RSS widget. It should look something like ths:
class RSSParser extends StatefulWidget {
final String title;
const RSSParser({required this.title});
This should solve your error.
This is not working because there is not title in RSS class.
I think you are not clear with use of widget.something. It means that in the class which extends StatefulWidget there is a something parameter which i need to get in stateObject.
See the code to understand.
class YellowBird extends StatefulWidget {
const YellowBird({ Key? key }) : super(key: key);
String someData = 'SomeData'; // Some data
#override
_YellowBirdState createState() => _YellowBirdState();
}
//This is the state object
class _YellowBirdState extends State<YellowBird> {
// Now that if you need some data from the above class. You use use this widget.someData to get it here
String getHere = widget.someData ;
#override
Widget build(BuildContext context) {
return Container(color: const Color(0xFFFFE306));
}
}

How to prevent error of platformViewRegistry [flutter-web]

I am trying to add pdf view in my web page (flutter web).. here is part of the code
ui.platformViewRegistry.registerViewFactory(
'hello-world-html',
(int viewId) => html.IFrameElement()
..width = '700'
..height = '4000'
..src = 'http:xxx.pdf'
..style.border = 'none');
the code runs like what I want, but I get error like this
The name 'platformViewRegistry' is being referenced through the prefix 'ui', but it isn't defined in any of the libraries imported using that prefix.
Try correcting the prefix or importing the library that defines 'platformViewRegistry'.
is there a way to prevent that error happen?
Edit use analysis_options.yaml
analyzer:
errors:
undefined_prefixed_name: ignore
You can copy paste run full code below
You can use // ignore: undefined_prefixed_name
code snippet
// ignore: undefined_prefixed_name
ui.platformViewRegistry.registerViewFactory(
'hello-world-html',
(int viewId) => html.IFrameElement()
..width = '700'
..height = '4000'
..src = 'http:xxx.pdf'
..style.border = 'none');
working demo
full simulate code
import 'package:flutter/material.dart';
// import 'dart:io' if (dart.library.html) 'dart:ui' as ui;
import 'dart:ui' as ui;
import 'dart:html' as html;
void main() {
runApp(MyApp());
}
class MyApp extends StatelessWidget {
#override
Widget build(BuildContext context) {
return MaterialApp(
title: 'Flutter Demo',
home: MyHomePage(),
);
}
}
class MyHomePage extends StatelessWidget {
#override
Widget build(BuildContext context) {
return Scaffold(
body: Center(child: Iframe()),
floatingActionButton: FloatingActionButton(
onPressed: () {},
tooltip: 'Increment',
child: Icon(Icons.add),
),
);
}
}
class Iframe extends StatelessWidget {
Iframe() {
// ignore: undefined_prefixed_name
ui.platformViewRegistry.registerViewFactory('iframe', (int viewId) {
var iframe = html.IFrameElement();
iframe.src = 'http://www.africau.edu/images/default/sample.pdf';
return iframe;
});
}
#override
Widget build(BuildContext context) {
return Container(
width: 400, height: 300, child: HtmlElementView(viewType: 'iframe'));
}
}
chunhunghan's proposed fix of ignoring undefined_prefix_name is overkill and risky. If you do this, all errors of the type prefix.wrongValue will be suppressed in your IDE. If you're on a small/personal project, this is fine, but if you're working on a larger scale project I would advise against this.
A better solution is to create a shim file for not-web contexts like so:
platform_view_registry.dart
export 'package:my_package/src/fake_platform_view_registry.dart'
if (dart.library.html) 'dart:ui' show platformViewRegistry;
fake_platform_view_registry.dart
// add either a dynamically typed variable
dynamic platformViewRegistry;
// or a more thorough shim like this one
class FakePlatformViewRegistry {
void registerViewFactory(
String viewTypeId, dynamic Function(int) viewFactory) {
throw UnsupportedError("platform view registry in non-web context");
}
}
final platformViewRegistry = FakePlatformViewRegistry();
Then just import package:my_package/src/platform_view_registry.dart instead of dart:ui (and probably drop the ui prefix too).
import "package:my_package/src/platform_view_registry.dart";
...
platformViewRegistry.registerViewFactory(
'hello-world-html',
(int viewId) => html.IFrameElement()
..width = '700'
..height = '4000'
..src = 'http:xxx.pdf'
..style.border = 'none');