flutter_inappwebview - no html-video - flutter

I'm using flutter_inappwebview in a Flutter-app to display local html (from assets). (I know: not ideal. I would prefer to program the whole thing in Dart, but that is not an option right now). It works great..apart from one thing. Can't get the video in html to work.
Problem
The video works fine when I open the html in any browser (directly from assets folder). So the html should be ok. All other html works fine in the app. So files and pubspec-import should be ok. In the app, the page shows a video-player, but no content on both iOS-sim and iOS device. In the Android-sim it works.
Code
I'm using:
flutter_inappwebview: ^3.2.0
sdk: ">=2.7.0 <3.0.0"
I use the inappview like this, after the imports. Server is already running (flutter reports: flutter: Server running on http://localhost:8080)
InAppWebViewController webViewController;
InAppWebView webView;
class OfflineViewer extends StatefulWidget {
#override
_ViewerState createState() => _ViewerState();
}
class _ViewerState extends State<OfflineViewer> {
#override
Widget build(BuildContext context) {
return InAppWebView(
initialUrl: "http://localhost:8080/assets/source/intro.html",
onWebViewCreated: (InAppWebViewController controller) {
webViewController = controller;
},
);
}
}
The video-tag in the loaded intro.html is:
<video height="600" controls>
<source src="intro.mp4" type="video/mp4">
Your browser does not support the video tag.
</video>
Things I tried:
Different video-formats (webm, ogg) > no difference.
Loading an online video into the html instead of local > works. Video loads and plays.
Different methods of embedding video (video-tag, iframe, video.js) > no difference.
Setting InAppWebViewOptions, like:
cacheEnabled: true,
javaScriptCanOpenWindowsAutomatically: true,
useOnLoadResource: true,
javaScriptEnabled: true,
mediaPlaybackRequiresUserGesture: false,
What makes this hard to solve (for me) is that I can't catch the html-errors once it's embedded in the app, whereas the standalone html doesn't give an error. Any ideas on how to solve or even troubleshoot this are highly appreciated.
Greetings,
Mark

Got it sorted. Just posting this in case anyone else has the same problem. Cause of the problem was: the HTTPServer-class creates a blob-server. It returns a video in one chunk (status 200). iOS doesn't accept that and wants status 206 (parts).
Based on https://stackoverflow.com/a/54004600/6007404 I created a JS-file which converts all the video's in the document. Just add it to any html-file with video:
if(navigator.platform && /iPad|iPhone|iPod|Mac/.test(navigator.platform)){
window.addEventListener("load", streamVideoBlobs);
}
function streamVideoBlobs(){
var allVideos = document.getElementsByTagName("video");
for(var i = 0, max = allVideos.length; i < max; i++)
{
iOSVideoConvert(allVideos[i]);
}
}
async function iOSVideoConvert(video){
let blob = await fetch(video.src).then(r => r.blob());
var videoUrl=createObjectURL(blob);
video.src = videoUrl;
}
function createObjectURL(object) {
return (window.URL) ? window.URL.createObjectURL(object) : window.webkitURL.createObjectURL(object);
}
Apart from that, you'll have to set
<key>NSAppTransportSecurity</key>
<dict>
<key>NSAllowsLocalNetworking</key>
<true/>
</dict>
in your info.plist. (You can also use NSAllowsArbitraryLoads, but that's more allowance then needed and Apple might flag it down when submitting the app).

Not sure but you may be missing the NSAppTransportSecurity setting in your Info.plist. This allows you to load unsafe content which http://localhost:8080 is.
<key>NSAppTransportSecurity</key>
<dict>
<key>NSAllowsArbitraryLoads</key>
<true/>
</dict>

https://stackoverflow.com/a/61999847/15258962
this make me realized ,the problem is not the WebView, its the LocalServer side.
I change the localServer logic to serve basic64 encoded video source when a video tag appeared ,and its worked for me

Related

Flutter-web:url_launcher_web not launching email in browser

I've added a feature of sending feedback through email for my flutter web app. This is my code :
static void launchEmail(String subjectLine) async {
const emailId = Constants.ARUDITO_EMAIL;
print('opening email app for uploading content');
String url = "mailto:$emailId?subject=$subjectLine";
if (kIsWeb) {
if (await webLauncher.UrlLauncherPlugin().canLaunch(url)) {
await webLauncher.UrlLauncherPlugin().launch(
url,
useWebView: true,
enableDomStorage: true,
enableJavaScript: true,
useSafariVC: true,
webOnlyWindowName: 'Arudito',
headers: null,
universalLinksOnly: false,
);
} else {
throw 'Could not open an email app';
}
} else {
if (await canLaunch(url)) {
await launch(url);
} else {
throw 'Could not open an email app';
}
}
}
pubspec.yaml includes :
dependencies:
flutter:
sdk: flutter
url_launcher: ^5.4.2
url_launcher_web: ^0.1.3+1
This code is not launching email in a web browser. It only opens a new tab. However, it is launching on selecting mail app in Windows. Can someone please tell what is the issue here?
I had exactly the same problem. I found a useful answer in another StackOverflow post.
There it is mentioned that if you just omit the canLaunch check and instead wrap the launch in a try-catch block it works on Flutter Web. I just tried, and it works nicely.
Btw, there is also a quite new Link widget in the url_launcher package, which makes it possible to create proper web links (including right-click support).
I had the exact issue. Click on any email link will just opens a new blank tab. That's it.
Took me hours to figure out.
Open Mail app on mac, settings, general, and there's an option for default email reader. It was selecting chrome. Change it to a real email client and it works as expected now.

Flutter open whatsapp with text message

I want to open whatsapp from my Flutter application and send a specific text string. I'll select who I send it to when I'm in whatsapp.
After making some research I came up with this:
_launchWhatsapp() async {
const url = "https://wa.me/?text=Hey buddy, try this super cool new app!";
if (await canLaunch(url)) {
await launch(url);
} else {
throw 'Could not launch $url';
}
}
Which works ok ish, however there are two problems:
As soon as I make the text string into multi words it fails. So if I change it to:
_launchWhatsapp() async {
const url = "https://wa.me/?text=Hey buddy, try this super cool new app!";
if (await canLaunch(url)) {
await launch(url);
} else {
throw 'Could not launch $url';
}
}
Then the Could not launch $url is thrown.
I have whatsapp already installed on my phone, but it doesn't go directly to the app, instead it gives me a webpage first and the option to open the app.
Here is the webpage that I see:
Any help on resolving either of these issues would be greatly appreciated.
Thanks
Carson
P.s. I'm using the Url_launcher package to do this.
From the official Whatsapp FAQ, you can see that using "Universal links are the preferred method of linking to a WhatsApp account".
So in your code, the url string should be:
const url = "https://wa.me/?text=YourTextHere";
If the user has Whatsapp installed in his phone, this link will open it directly. That should solve the problem of opening a webpage first.
For the problem of not being able to send multi-word messages, that's because you need to encode your message as a URL. Thats stated in the documentation aswell:
URL-encodedtext is the URL-encoded pre-filled message.
So, in order to url-encode your message in Dart, you can do it as follows:
const url = "https://wa.me/?text=Your Message here";
var encoded = Uri.encodeFull(url);
As seen in the Dart Language tour.
Please note that in your example-code you have put an extra set of single quotes around the text-message, which you shouldn't.
Edit:
Another option also presented in the Whatsapp FAQ is to directly use the Whatsapp Scheme. If you want to try that, you can use the following url:
const url = "whatsapp://send?text=Hello World!"
Please also note that if you are testing in iOS9 or greater, the Apple Documentation states:
Important
If your app is linked on or after iOS 9.0, you must declare the URL schemes you pass to this method by adding the LSApplicationQueriesSchemes key to your app's Info.plist file. This method always returns false for undeclared schemes, whether or not an appropriate app is installed. To learn more about the key, see LSApplicationQueriesSchemes.
So you need to add the following keys to your info.plist, in case you are using the custom whatsapp scheme:
<key>LSApplicationQueriesSchemes</key>
<array>
<string>whatsapp</string>
</array>
till date: 27-06-2022
using package: https://pub.dev/packages/url_launcher
dependencies - url_launcher: ^6.1.2
TextButton(
onPressed: () {
_launchWhatsapp();
},
)
_launchWhatsapp() async {
var whatsapp = "+91XXXXXXXXXX";
var whatsappAndroid =Uri.parse("whatsapp://send?phone=$whatsapp&text=hello");
if (await canLaunchUrl(whatsappAndroid)) {
await launchUrl(whatsappAndroid);
} else {
ScaffoldMessenger.of(context).showSnackBar(
const SnackBar(
content: Text("WhatsApp is not installed on the device"),
),
);
}
}
Here is new update way...
whatsapp() async{
var contact = "+880123232333";
var androidUrl = "whatsapp://send?phone=$contact&text=Hi, I need some help";
var iosUrl = "https://wa.me/$contact?text=${Uri.parse('Hi, I need some help')}";
try{
if(Platform.isIOS){
await launchUrl(Uri.parse(iosUrl));
}
else{
await launchUrl(Uri.parse(androidUrl));
}
} on Exception{
EasyLoading.showError('WhatsApp is not installed.');
}
}
and call whatsapp function in onpress or ontap function
For using the wa.me domain, make sure to use this format...
https://wa.me/123?text=Your Message here
This will send to the phone number 123. Otherwise, you will get an error message (see? https://wa.me/?text=YourMessageHere ). Or, if you don't want to include the phone number, try this...
https://api.whatsapp.com/send?text=Hello there!
Remember, wa.me requires a phone number, whereas api.whatsapp.com does not. Hope this helps!
I know it is too late for answering this, but for those who want the same functionality, the current way is to do it like this:
launchUrl(Uri.parse('https://wa.me/$countryCode$contactNo?text=Hi'),
mode: LaunchMode.externalApplication);
if you will use URL Launcher then the whatsapp link will be open on web browser. So you need to set parameter - not to open on safari browser. The complete code you can find on this flutter tutorial.
But for your case use below code.
await launch(whatappURL, forceSafariVC: false);
Today i am adding solution its working fine on my desktop and phone
Add 91 if your country code is +91
Remember not add any http or https prefix otherwise wont work.
whatsapp://send?phone=9112345678&text=Hello%20World!

How can I implement video playback in flutter web?

I'm trying to play videos hosted on firebase on my flutter web app.
I can't figure out how this will be possible.
With flutter native, the video-player plugin is used but that only works for ios and android.
Can someone tell me if it is possible to integrate video in a flutter web app?
I have tried using the dart:html package to achieve this. The videoElement class in the package looks relevant. But I'm unable to render the element as a widget.
prefix1.VideoElement element = prefix1.VideoElement();
element.height = 200;
element.width = 200;
)
I want to add a video playback option to my flutter web page.
I've detailed here a solution that uses the Afterglow video player.
You can replace this with any HTML/JS video player of your choice, the approach would be the same.
Basically, you need to alter the index.html template file and interact with the DOM using either dart:html or universal_html packages to get your desired video file playing.
index.html will look like:
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<title>Web Video Player</title>
<script src="https://cdn.jsdelivr.net/npm/afterglowplayer#1.x"></script></head>
<body>
<script src="main.dart.js" type="application/javascript"></script>
<a id="triggerVideoPlayer" class="afterglow" href="#videoPlayer"></a>
<video id="videoPlayer" width="960" height="540" data-skin="dark">
</video>
</body>
</html>
You'll need a playVideo method like the following:
import 'package:flutter/foundation.dart';
import 'package:universal_html/html.dart' as html;
void playVideo(String atUrl) {
if(kIsWeb) {
final v = html.window.document.getElementById('videoPlayer');
if(v != null) {
v.setInnerHtml(
'<source type="video/mp4" src="$atUrl">',
validator: html.NodeValidatorBuilder()
..allowElement('source', attributes: ['src', 'type']));
final a = html.window.document.getElementById( 'triggerVideoPlayer' );
if(a != null) {
a.dispatchEvent(html.MouseEvent('click'));
}
}
} else {
// we're not on the web platform
// and should use the video_player package
}
}
And you'll play the video like this:
playVideo('http://distribution.bbb3d.renderfarming.net/video/mp4/bbb_sunflower_1080p_30fps_normal.mp4');
Before the 10th of December 2019, this method was one of the only ways of playing videos in a Flutter web app. At Flutter Interact 2019 the web support for a couple of packages, including the video_player_web package. The option presented above still work and may serve you better in your use case.
The Flutter video_player plugin now supports web using the video_player_web plugin through federation.
Here is a quote from the Pub page:
To use this plugin in your Flutter Web app, simply add it as a
dependency in your pubspec using a git dependency. This is only
temporary: in the future we hope to make this package an "endorsed"
implementation of video_player, so that it is automatically included
in your Flutter Web app when you depend on package:video_player.
dependencies:
video_player: ^0.10.4
video_player_web:
git:
url: git://github.com/flutter/plugins.git
path: packages/video_player/video_player_web
Once you have the video_player_web dependency in your pubspec, you
should be able to use package:video_player as normal.
the code below worked for me:
Open
[image_picker_for_web / video_player_web] Can't preview PickedFile videos on the web
#58527
void initializeVideo() {
if (_pickedFile != null) {
if (kIsWeb) {
_controller = VideoPlayerController.network(_pickedFile!.path);
} else {
_controller = VideoPlayerController.file(File(_pickedFile!.path));
}
_controller.initialize().then((value) => setState(() {}));
} else {
_controller = VideoPlayerController.file(File(''));
}
}
based on code example above, i updated my code to:
#override
void initState() {
super.initState();
setState(() {
if (kIsWeb) {
controller = VideoPlayerController.network(widget.videoFile.path);
;
} else {
controller = VideoPlayerController.file(widget.videoFile);
}
});
controller.initialize();
controller.play();
controller.setVolume(1);
controller.setLooping(true);
}

FILE_URI path for Camera not working on IONIC 4

When using the Cameara to take a picture with destinationType: this.camera.DestinationType.FILE_URI, the resulting URL will not work to display the image. For example, when attempting to take a photo like this:
this.camera.getPicture(options).then((url) => {
// Load Image
this.imagePath = url;
}, (err) => {
console.log(err);
});
Attempting to display it as <img [src]="imagePath" > will result in an error (file not found).
The problem here is that the URL is in the file:///storage... path instead of the correct one based on localhost.
In previous versions of Ionic, this would be solved by using normalizeURL. This will not work on Ionic 4 (or at least I could not make it work).
To solve this issue, you will need to use convertFileSrc():
import {WebView} from '#ionic-native/ionic-webview/ngx';
...
this.camera.getPicture(options).then((url) => {
// Load Image
this.imagePath = this.webview.convertFileSrc(url);
}, (err) => {
console.log(err);
});
Now the image URL will be in the appropriate http://localhost:8080/_file_/storage... format and will load correctly.
See WKWebView - Ionic Docs for more information.
In my case, the following code works with me
const downloadFileURL = 'file:///...';
// Convert a `file://` URL to a URL that is compatible with the local web server in the Web View plugin.
const displayedImg = (<any>window).Ionic.WebView.convertFileSrc(downloadFileURL);
In case some gots here looking for the answer on ionic4, check this out
"Not allowed to load local resource" for Local image from Remote page in PhoneGap Build
and look for the answer from #Alok Singh that's how I got it working on ionic4 and even works with livereload
UPDATE december 2021:
You have to install the new Ionic Webview
RUN:
ionic cordova plugin add cordova-plugin-ionic-webview
npm install #awesome-cordova-plugins/ionic-webview
Import it in app.module and your page where you wanna use it:
import { WebView } from '#awesome-cordova-plugins/ionic-webview/ngx';
image = "";
constructor(private webview: WebView){}
Then this will work:
this.camera.getPicture(options).then((imageData) => {
this.image = this.webview.convertFileSrc(imageData)
}, (err) => {
// Handle error
});
And show it in the HTML page:
<img [src]="image" alt="">

Ionic Photo Viewer doesn't work on ios

I'm using Ionic Photo Viewer to show images in full screen. My HTML is:-
<ion-slides>
<ion-slide col-12 *ngFor="let image of businessImages | async">
<div class="main-slider-image" [defaultImage]="'assets/imgs/default_image_slider.png'" [lazyLoad]="image.thumb400Url" [offset]="100" (click)="openImage(image.originalUrl)">
</div>
</ion-slide>
</ion-slides>
On TypeScript:-
openImage(url) {
this.photoViewer.show(url, "", { share: false });
}
On Android is working like this:-
Click here to see Android version
On the other hand, on the iPhone is working like this:-
Click here to see iPhone version
On the iPhone, the photo viewer doesn't open the photo. I've tried:-
openImage(url) {
this.photoViewer.show(url);
}
But this also didn't work. If you've any idea how to solve this issue please share. Thank you
This issue is really crazy and had to spend lots of time to figure out the solutions. The solutions is all the parameters in the 'options' variable are required.
Follow this:
const options = {
share: true, // default is false
closeButton: true, // default is true
copyToReference: true, // default is false
headers: "", // If it is not provided, it will trigger an exception
piccasoOptions: { } // If it is not provided, it will trigger an exception
};
this.photoViewer.show(url, "", options);
At least I had to revert to 1.1.11 (found from NPM) to show IOS properly. For Android, latest version seemed to work.
Share did not seem to work for IOS in 1.1.11. In Android latest photo-viewer plugin it seemed to work. So now I have:
private viewPhoto(url: string): void {
if (url && url != 'assets/images/profile.png') {
this.photoViewer.show(url, '', { share: this.platform.is('android') });
}
}
And.. I think the correct place to discuss these is https://github.com/sarriaroman/photoviewer/issues.
And another thing, I'm considering to use another plugin, https://github.com/Riron/ionic-img-viewer. Some of the photoviewer issues had a link to this but haven't tried it yet.
I have the same error, and im solve with this
ionic cordova plugin rm com-sarriaroman-photoviewer
ionic cordova plugin add com-sarriaroman-photoviewer#1.1.18
npm install --save #ionic-native/photo-viewer
on your function, if device using ios, decodeURIComponent was the answer
showImage(url,title) {
var options = {
share: true, // default is false
closeButton: true, // iOS only: default is true
copyToReference: true // iOS only: default is false
};
if (this.platform.is("ios")) {
url = decodeURIComponent(url);
}
this.photoViewer.show(url, title, options);
}