Load html content in flutter web? - flutter

I am trying to show a webpage in webview via html content. in android and ios flutter_html working good, but in flutter web this library not working.
I also tried this code but got no answer :
IFrameElement iframeElement = IFrameElement()
..src = 'url'
..style.border = 'none'
..onLoad.listen((event) {
// perform you logic here.
});
ui.platformViewRegistry.registerViewFactory(
'webpage',
(int viewId) => iframeElement,
);
return Directionality(
textDirection: TextDirection.ltr,
child: Center(
child: SizedBox(
width: double.infinity,
height: double.infinity,
child: HtmlElementView(viewType: 'webpage'),
),
),
);
Is there any another way to show the html content in the flutter web application?

I am now using flutter html to render the html content page in my app, this is the library address: https://pub.dev/packages/flutter_html. The code I am using look like this:
if (item.content != "")
Html(
data: item.content,
style: {
"body": Style(
fontSize: FontSize(19.0),
),
},
onLinkTap: (String? url, RenderContext context, Map<String, String> attributes, dom.Element? element){
CommonUtils.launchUrl(url);
}),
the item.content is my html web page source code, hope this help for you.

Related

PlatformException(multiple_request, Cancelled by a second request, null, null) in imagePicker

I am using a riverpod provider class to handle picking of image from gallery. However, once an image is picked, I get the error: PlatformException(multiple_request, Cancelled by a second request null, null). Not sure where a second request is coming from. More importantly, no image is applied to my placeholder (CircleAvartar) due to this unknown cancellation.
Here are the two dart files in question and thanks for the help.
imageProvider file:
final myImageProvider =
ChangeNotifierProvider<ImageNotifier>((ref) => ImageNotifier());
class ImageNotifier extends ChangeNotifier {
ImageNotifier() : super();
final file = useState<File?>(null);
final imageFile = useState<XFile?>(null);
final imagePicker = ImagePicker();
Future<void> _pickImage(int type) async {
try {
XFile? userImage = await imagePicker.pickImage(
source: type == 1 ? ImageSource.gallery : ImageSource.camera,
imageQuality: 50,
);
imageFile.value = userImage;
// imageFile.value = XFile(userImage!.path);
} catch (e) {
print(e);
}
notifyListeners();
}
void showPicker(context) {
showModalBottomSheet(
backgroundColor: Theme.of(context).primaryColor,
context: context,
builder: (BuildContext bc) {
return SafeArea(
child: Wrap(
children: [
ListTile(
leading: const Icon(
Icons.photo_library,
color: Colors.white,
),
title: const Text(
'Photo Gallery',
style: TextStyle(fontSize: 22),
),
onTap: () => _pickImage(1),
),
ListTile(
leading: const Icon(
Icons.photo_camera,
color: Colors.white,
),
title: const Text(
'Camera',
style: TextStyle(fontSize: 22),
),
onTap: () => _pickImage(2),
),
ListTile(
leading: const Icon(
Icons.close,
color: Colors.white,
),
title: const Text(
'Cancel',
style: TextStyle(fontSize: 22),
),
onTap: () {
imageFile.value = null;
Navigator.of(context).pop();
},
),
],
),
);
},
);
notifyListeners();
}
AuthScreen dart file:
Widget build(BuildContext context, WidgetRef ref) {
final _passwordController = useTextEditingController();
final _passwordFocusScope = useFocusNode();
final _emailFocusScope = useFocusNode();
final _phoneFocusScope = useFocusNode();
final _confirmFocusScope = useFocusNode();
final _isVisible = useState<bool>(true);
var _authMode = useState<AuthMode>(AuthMode.login);
final imageProviderState = ref.watch(myImageProvider.notifier);
final deviceSize = MediaQuery.of(context).size;
final authMode = ModalRoute.of(context)?.settings.arguments as String;
switch (authMode) {
case 'login':
_authMode.value = AuthMode.login;
break;
case 'register':
_authMode.value = AuthMode.register;
break;
case 'google':
_authMode.value = AuthMode.google;
break;
case 'guest':
_authMode.value = AuthMode.guest;
break;
}
return Scaffold(
body: Stack(
children: [
Padding(
padding: const EdgeInsets.all(8.0),
child: Form(
key: _formKey,
child: SingleChildScrollView(
child: Column(
mainAxisAlignment: MainAxisAlignment.start,
children: [
const SizedBox(
height: 80,
),
Center(
child: _authMode.value == AuthMode.login
? const Text(
'Access Your Account',
style: TextStyle(
fontSize: 25,
),
)
: Row(
children: [
InkWell(
onTap: () =>
imageProviderState.showPicker(context),
// () => ref
// .read(myImageProvider.notifier)
// .showPicker(context),
child: CircleAvatar(
radius: 50,
backgroundImage:
imageProviderState.imageFile.value !=
null
? FileImage(
// File(ref
// .read(imageProvider.notifier)
// .imageFile
// .value!
// .path),
// )
File(imageProviderState
.imageFile.value!.path),
)
: null,
child: imageProviderState.imageFile.value ==
null
? const Icon(
Icons.camera,
// Icons.add_photo_alternate,
size: 30,
color: Colors.white,
)
: null,
),
),
After testing the code on a real device (iPhone and Android) I was able to select and attach a photo from gallery and camera to my form. The issue is with trying to do this task on a simulator even though one was able to do so once upon a time. Don't even bother anymore until Apple fixes this trouble. My advice is that you debug on a real device to ensure things are working as coded and you can return to the simulator/emulator afterwards. I lost a lot of time trying to make tis work on a simulator to no avail.
I have the latest Flutter 3.3.9 and Xcode 14.1 and this is still a problem. The workaround is very simple though after reading this issue. When using the image_picker, DO NOT pick the first image (with pink flowers):
In addition to my earlier answer and further tweaking with the dev in simulator environment, I just discovered that the uploaded image does show up upon a reload/restart. Weird but works if you must test in simulation mode. Simply restart and the uploaded image will show up. It is still a simulator issue though, IMHO.
It can help to double-click on the image you are selecting from the gallery instead of clicking only once.
For whatever reason, if I clicked only once, it would not show up and the same error as yours appeared.
If I clicked twice there was a short lag, but the image showed up.
Tested on iOS simulator - don't get this issue personally on my Android emulator.
I had this issue picking one of the default album images on my iOS simulator.
I was able to get around this by going to Safari, saving a png to Photos and then selecting that downloaded png in my Flutter app.
Thanks to Marc's post which pointed me in the right direction regarding HEIC support
Hi please have a look at this discussion:
https://github.com/flutter/flutter/issues/70436
on on the image picker package site we can see that it is a well known apple simulator issue. I would say that it should work for you on real devices (or try to test it only with particular pictures from iOS simulator photos)
Make sure ALLOW PHOTO ACCESS permission is set to either Selected Photos or All Photos. In my case, I had denied the permission so there was no error log on the console and the image picker was not opening.
PS I know it's not directly related to the SO's question but might be helpful if someone comes across this.
Don't bother about this issue much. This is just a simulator issue(mostly on iOS). Testing this on a real device is advisable.
I think it because it using 'pickimage' instead of 'pickMultiImage', so u are only allow to pick 1 image at a time, try to make ur 'imageFile' to null first before you pick another image.

Unable to display html file from local storage in webview

I have a Flutter project in which I am:
Downloading the zip file (full of html files)
Extracting the html files to a new directory (ebooks/02)
Saving the local file urls in a List
Displaying the urls in Webview & iterate through List for back & forth.
However, in the web view all I get is "Unable to load asset..."
Though any standard http url works fine in webview.
I tried from these two answers but no result: Answer1 & Answer2
The exception I get is :
E/flutter (10963): [ERROR:flutter/lib/ui/ui_dart_state.cc(186)] Unhandled Exception: Unable to load asset: /data/user/0/com.pts.school_ebook_reader_app_prag/app_flutter/ebooks/04/00.html
I need to understand how to make the local html at the given path display in webview.
Any help would be appreciated.
Edit:
The webview code (currently trying to display only 1st url in list):
class _BookReaderState extends State<BookReader> {
List<String> urls = UserData.ebook;
WebViewController web;
final _key = UniqueKey();
String _url;
#override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
automaticallyImplyLeading: false,
title: Text(
"Book Title Here",
style: GoogleFonts.roboto(
fontWeight: FontWeight.w900,
fontSize: 25.0,
color: Colors.white),
textAlign: TextAlign.center,
),
actions: [
Padding(
padding: EdgeInsets.only(right: 50),
child: IconButton(
icon: Image.asset('images/04_mobile-menu.png'),
color: Colors.red,
alignment: Alignment.centerLeft,
onPressed: () {
Navigator.push(
context,
MaterialPageRoute(
builder: (context) => MyLibrary_Screen()));
}),
),
Padding(
padding: const EdgeInsets.only(left: 1.0),
child: IconButton(
icon: Image.asset('images/05_mobile-close.png'),
color: Colors.red,
alignment: Alignment.centerRight,
onPressed: () {
Navigator.push(
context,
MaterialPageRoute(
builder: (context) => MyLibrary_Screen()));
}),
),
],
),
body: Column(
children: [
Padding(
padding: EdgeInsets.symmetric(horizontal: 10, vertical: 10),
child: Container(
width: 700,
height: 490,
child: FutureBuilder<String>(
future: _loadHtmlFromAssets(0),
builder: (context, snapshot) {
if (snapshot.hasData) {
return WebView(
initialUrl: new Uri.dataFromString(snapshot.data,
mimeType: 'text/html')
.toString(),
javascriptMode: JavascriptMode.unrestricted,
);
} else if (snapshot.hasError) {
return Text("${snapshot.error}");
}
return CircularProgressIndicator();
})),
),
Padding(
padding: EdgeInsets.only(top: 85),
child: Container(
height: 70,
color: Colors.blue,
child: RowSuper(
innerDistance: 50,
children: [
InkWell(
child: Image.asset(
"images/05_mobile-arrow-left.png",
alignment: Alignment.bottomLeft,
height: 170,
width: 90,
),
onTap: () => pageIncDec(1),
),
Text('Page ${urls.indexOf(_url) + 1} of ${urls.length}',
style: GoogleFonts.roboto(
fontWeight: FontWeight.w900,
fontSize: 33.0,
color: Colors.white)),
InkWell(
child: Image.asset(
"images/05_mobile-arrow-right.png",
alignment: Alignment.bottomRight,
height: 270,
width: 90,
),
onTap: () => pageIncDec(2),
),
],
),
),
),
],
));
}
pageIncDec(int i) async {
int n;
if (i == 1) {
setState(() {
urls.indexOf(_url) > 0 ? n = urls.indexOf(_url) - 1 : n = 0;
});
} else {
setState(() {
urls.indexOf(_url) < urls.length
? n = urls.indexOf(_url) + 1
: n = urls.length - 1;
});
}
_url = await _loadHtmlFromAssets(n);
web.loadUrl(_url);
print(_url);
}
Future<String> _loadHtmlFromAssets(int n) async {
String fileText = await rootBundle.loadString(urls[n]);
print(fileText);
String r = (Uri.dataFromString(fileText,
mimeType: 'text/html', encoding: Encoding.getByName('utf-8'))
.toString());
print(r);
return r;
}
Code to add files :
Directory dir =
Directory('${_appDocDir.path}/$folderName/${item.key_name}');
List<FileSystemEntity> listOfAllFolderAndFiles =
await dir.list(recursive: false).toList();
if (UserData.ebook != null) UserData.ebook.clear();
listOfAllFolderAndFiles.forEach((element) {
if (element.toString().contains("html")) {
String url = element.toString().replaceAll("File: ", "");
url = url.replaceAll("'", "");
UserData.ebook.add(url.toString());
}
UserData.eBookTitle = item.title;
});
print(UserData.ebook);
And result of printing UserData.ebook :
I/flutter ( 3465): [/data/user/0/com.pts.school_ebook_reader_app_prag/app_flutter/ebooks/02/00.html, /data/user/0/com.pts.school_ebook_reader_app_prag/app_flutter/ebooks/02/01.html, /data/user/0/com.pts.school_ebook_reader_app_prag/app_flutter/ebooks/02/02.html, /data/user/0/com.pts.school_ebook_reader_app_prag/app_flutter/ebooks/02/03.html, /data/user/0/com.pts.school_ebook_reader_app_prag/app_flutter/ebooks/02/04.html, /data/user/0/com.pts.school_ebook_reader_app_prag/app_flutter/ebooks/02/05.html, /data/user/0/com.pts.school_ebook_reader_app_prag/app_flutter/ebooks/02/06.html]
Checking:
//Checking if file exists
print("File ${UserData.ebook[0]} exists ? " +
File(UserData.ebook[0]).existsSync().toString());
Result:
I/flutter ( 3465): File /data/user/0/com.pts.school_ebook_reader_app_prag/app_flutter/ebooks/02/00.html exists ? true
Finally after trying all possible plugins realized that Flutter webview as of now cannot display local html files that are heavy on css & javascript side.
The same webview can only display external urls or basic html files(minus css & js).
I switched over to native android for this.
I think you should load html as normal file, not like asset, because it's not located in Assets directory and convert it to base64:
Future<String> _loadHtmlFromAssets(int n) async {
final file = File(urls[n]);
String fileText = await file.readAsString();
final base64 = base64Encode(utf8.encode(fileText));
return "data:text/html;base64,$base64";
}
Then show it like:
return WebView(
initialUrl: snapshot.data.toString(),
javascriptMode: JavascriptMode.unrestricted,
);
I know this may be a little late, but it's possible to add an HTML view with complex js and css, it can be done in two methods. The first and really bad looking way is to put all in one file, it will be visible both in iOS and Android and to load it via the WebView, the other method (I'm using this one to load an Angular local web component in an app) is to use the plugin webview_flutter_plus which is an extension of the normal WebView in flutter. This plugin requires to add in the pubspec.yaml all the files needed in the WebComponent, so you can add multiple complex css files and js files.
The tutorial in the plugin is pretty complete.
The only problem I'm facing is with iOS, which doesn't find the files, but that should be caused by a native problem, iOS try to load the files runtime and those are in a different location, so you need to find the correct path and replace it runtime in the html file (that was the solution I've implemented in a native project in swift).
Hope this helped for future projects.

Flutter Local Notifications Plugin: createNotificationChannel function not working

I am trying to create an android notification channel with flutter_local_notifications: ^5.0.0+4
Put it looks like it does not work as should be.
I wrote a code using getNotificationChannels to verify if the channel was created successfully
Please take a look to my code
Future<void> _getNotificationChannels() async {
await flutterLocalNotificationsPlugin.resolvePlatformSpecificImplementation<
AndroidFlutterLocalNotificationsPlugin>()?.createNotificationChannel(AndroidNotificationChannel(
'high_importance_channel', // id
'High Importance Notifications', // title
'This channel is used for important notifications', // description
importance: Importance.max,
));
final Widget notificationChannelsDialogContent =
await _getNotificationChannelsDialogContent();
await showDialog<void>(
context: context,
builder: (BuildContext context) => AlertDialog(
content: notificationChannelsDialogContent,
actions: <Widget>[
TextButton(
onPressed: () {
Navigator.of(context).pop();
},
child: const Text('OK'),
),
],
),
);
}
Future<Widget> _getNotificationChannelsDialogContent() async {
try {
final List<AndroidNotificationChannel>? channels =
await flutterLocalNotificationsPlugin
.resolvePlatformSpecificImplementation<
AndroidFlutterLocalNotificationsPlugin>()!
.getNotificationChannels();
return Container(
width: double.maxFinite,
child: ListView(
children: <Widget>[
const Text(
'Notifications Channels',
style: TextStyle(fontWeight: FontWeight.bold),
),
const Divider(color: Colors.black),
if (channels?.isEmpty ?? true)
const Text('No notification channels')
else
for (AndroidNotificationChannel channel in channels!)
Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: <Widget>[
Text('id: ${channel.id}\n'
'name: ${channel.name}\n'
'description: ${channel.description}\n'
'groupId: ${channel.groupId}\n'
'importance: ${channel.importance.value}\n'
'playSound: ${channel.playSound}\n'
'sound: ${channel.sound?.sound}\n'
'enableVibration: ${channel.enableVibration}\n'
'vibrationPattern: ${channel.vibrationPattern}\n'
'showBadge: ${channel.showBadge}\n'
'enableLights: ${channel.enableLights}\n'
'ledColor: ${channel.ledColor}\n'),
const Divider(color: Colors.black),
],
),
],
),
);
} on PlatformException catch (error) {
return Text(
'Error calling "getNotificationChannels"\n'
'code: ${error.code}\n'
'message: ${error.message}',
);
}
}
As you can see I am using createNotificationChannel to create a notifications channel then I am using getNotificationChannels to see if the channel was created successfully.
I am getting always "No notification channels" as a result in the dialog.
I do not know what is wrong with my code,
Thanks for your help
The problem was the Android version that I am using for testing,
Notification channels is a concept that is specific Android 8 or newer, which is why the API docs state that the method to create channels is only for those versions of Android

I'd like to add a like button to this flutter blog app

This project can be found here:https://github.com/Santos-Enoque/flutter_blog_app
So far I have this connected to firebase realtime database and it works well. I'm trying to add a like button to the Home page(lib/screens/home.dart) where all the posts are listed.
The Homepage displays the blog results using a Card with a ListTile. The trailing property of the ListTile card is already used so I'd like to use the leading property of the ListTile card to display a favourite icon which would increment a counter++ when tapped and also save the results to Firebase. Just like Facebook's like button.
Here's the code below:
child: Card(
child: ListTile(
title: ListTile(
onTap: (){
_incrementCounter();
},
leading: FittedBox(
fit: BoxFit.fitWidth,
child: Row(
children: <Widget>[
Icon(Icons.favorite),
Text(postsList[index].counter.toString()
),
],
),
),
title: Text(
postsList[index].title,
style: TextStyle(
fontSize: 16.0, fontWeight: FontWeight.bold),
),
trailing: Text(
timeago.format(DateTime.fromMillisecondsSinceEpoch(postsList[index].date)),
style: TextStyle(fontSize: 12.0, color: Colors.grey),
),
),
subtitle: Padding(
padding: const EdgeInsets.only(bottom: 14.0),
child: Text(postsList[index].body, style: TextStyle(fontSize: 14.0),),
),
),
),
Here's the _increment counter code:
try {
var ref = FirebaseDatabase.instance.reference().child('posts/{postId}/counter');
await ref.once().then((data) async => {
await ref.set(data.value + 1),
});
} catch (e) {
print(e.message);
}
}
![Home page of blog]https://photos.google.com/share/AF1QipMK6C-Wx2vZHHbE2jDMQsfYNnwl9OWK_5W8OKOfiIChcXt-gnWnCH7ba_EpyRnRAA?key=cGxkRkVSSk9PQTdtTXB0MzZBRDNHNUVzSGxlcDVB
The blog posts are displayed as cards as in the image ... I'm trying to add an icon on the left side of the card(leading) plus an incrementing value everytime someone taps the icon. Something like the like button on facebook. And also save the data to firebase realtime database.
Any help is much appreciated ... Thank you all!
I think what you may want to do is add this function to your onPressed. You will also need to set the text of the next to the icon equal to the new value read.
void like() async {
try {
var ref = FirebaseDatabase.instance.reference().child('path to likes for a post');
await ref.once().then((data) async => {
await ref.set(data.value + 1);
});
} catch (e) {
print(e.message);
}
}
Hope this helps.
P.S.- You may find this video of use: https://www.youtube.com/watch?v=l8_7RTRRmHo
Thanks to everyone who helped with this:
Code for UI:
leading: FittedBox(
fit: BoxFit.fitWidth,
child: Row(
children: <Widget>[
Icon(Icons.favorite),
Text(postsList[index].counter.toString()
),
],
),
),
Code for Function:
onTap: (){
_incrementCounter(postsList[index].key);
}
...
void _incrementCounter(key) async {
try {
var ref = FirebaseDatabase.instance.reference().child('posts/'+ key +'/counter');
await ref.once().then((data) async => {
await ref.set(data.value + 1),
});
} catch (e) {
print(e.message);
}
}
}

Flutter add clickable link/url to a CheckboxListTile

I have a CheckboxListTile and a widget in the title tag. How can I create a html text (text + url) to this widget?
My text is: 'Hello Click here!'
I tried this: flutter_html: ^0.10.4
with no luck
Thanks
Edit:
You can try link: ^1.1.0
import 'package:link/link.dart';
...
Link(
child: Text('This is a link to Flutter', style: TextStyle(
decoration: TextDecoration.underline, // add add underline in text
),),
url: 'https://google.com',
onError: _showErrorSnackBar,
),
void _showErrorSnackBar() {
Scaffold.of(context).showSnackBar(
SnackBar(
content: Text('Oops... the URL couldn\'t be opened!'),
),
);
}
...
Output:
Have you tried url_launcher,
RaisedButton(
onPressed: _launchURL,
child: Text('Text'),
),
_launchURL() async {
const url = 'https://flutter.dev';
if (await canLaunch(url)) {
await launch(url);
} else {
throw 'Could not launch $url';
}
You can also try linkify
linkify("Made by https://cretezy.com");
You could try flutter_linkify.
You need to replace Text with Linkify widget.
With this plugin you code might look like this:
Linkify(
onOpen: (link) => print("Clicked ${link.url}!"),
text: "Made by https://cretezy.com",
);