Flutter web: Launch page in target="_self" with url_launcher - hyperlink

I am using url_launcher package.
When in Flutter web, I would like the url to be opened in the current page, and not in target="_blank"
I tried adding forceWebView: true,
if (await canLaunch(url)) {
await launch(
url,
forceSafariVC: true,
forceWebView: true,
headers: <String, String>{'target': '_self'},
);
} else {
throw 'Could not launch $url';
}
And also added headers thinking they might have something to do, but they don't.
Is there a way to do this? Thank you
Any other solution to open a url in mobile and in web, and that enables the possiblity to open the web link in self is accepted also

In flutter_web if you want to achieve this you can use the webOnlyWindowName property and pass _self or _blank depending on your choice.
_self - opens in the same tab.
_blank - opens in a new tab.
I am not sure if its documented properly. But you can find the piece of code responsible for this here.
Following is a working solution which you can test.
import 'package:flutter/material.dart';
import 'package:url_launcher/url_launcher.dart';
void main() {
runApp(UrlLauchDemo());
}
class UrlLauchDemo extends StatelessWidget {
String url = 'https://www.google.com';
#override
Widget build(BuildContext context) {
return MaterialApp(
home: Scaffold(
body: Center(
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: [
MaterialButton(
color: Colors.greenAccent,
child: Text('Launch Google in this page'),
onPressed: () async {
if (await canLaunch(url)) {
await launch(
url,
forceSafariVC: true,
forceWebView: true,
webOnlyWindowName: '_self',
);
} else {
throw 'Could not launch $url';
}
},
),
SizedBox(
height: 100,
),
MaterialButton(
color: Colors.blueAccent,
child: Text('Launch Google in new Tab'),
onPressed: () async {
if (await canLaunch(url)) {
await launch(
url,
forceSafariVC: true,
forceWebView: true,
webOnlyWindowName: '_blank',
);
} else {
throw 'Could not launch $url';
}
},
),
],
),
),
),
);
}
}
Update: 12.01.2021
This information is documented in the api documentation here

Related

Flutter webview navigationDelegate called twice

I am trying to download files which contains a certain url pattern with Flutter web view. This works but in this case the browser is opened twice, as the navigationDelegate is called twice. NavigationRequest Object is same except isForMainFrame property. It is false for first time, and true for second time.
CupertinoPageScaffold(
navigationBar: CupertinoNavigationBar(middle: Text(_appTitle)),
child: Container(
child: SafeArea(
child: IndexedStack(
index: _stackToView,
children: <Widget>[
WebView(
key: _key,
javascriptMode: JavascriptMode.unrestricted,
initialUrl: this._connectionString,
onPageStarted: (value) => setState(() {
if (shouldChangeStack) {
_stackToView = 1;
} else {
_stackToView = 0;
}
}),
onPageFinished: (value) => setState(() {
_stackToView = 0;
}),
navigationDelegate: (NavigationRequest request) async {
print(request.url);
if (request.url.contains("download")) {
setState(() {
shouldChangeStack = false;
});
if (await canLaunch(request.url)) {
await launch(request.url);
}
return NavigationDecision.prevent;
} else {
setState(() {
shouldChangeStack = true;
});
return NavigationDecision.navigate;
}
},
),
Container(
child: Center(
child: CircularProgressIndicator(),
),
)
],
),
top: true,
),
),
);
The reason why the delegate method is called twice was because setState() is called. This causes the entire Widget build() to be rebuilt. As previously mentioned in the comments, a workaround for this issue is to set a checker before launching the page and define if the page needs to be opened.

Flutter app crashes on firebase phone auth in iOS platform

I have implemented Firebase Phone auth in my project,
On android side everything works fine, but for iOS side, when i try to send verification code from my end, app crashes and lost connection.
I have submitted my code below.
main.dart
class MyApp extends StatelessWidget {
#override
Widget build(BuildContext context) {
return new MaterialApp(
home: new MyHomePage(),
routes: <String, WidgetBuilder> {
'/homepage': (BuildContext context) => MyApp(),
'/landingpage': (BuildContext context) => MyHomePage()
}
);
}
}
class MyHomePage extends StatefulWidget {
#override
_MyHomePageState createState() => new _MyHomePageState();
}
class _MyHomePageState extends State<MyHomePage> {
String phoneNo;
String smsCode;
String verificationId;
Future<void> verifyPhone() async {
print("main");
final PhoneCodeAutoRetrievalTimeout autoRetrieve = (String verId) {
print("varification id");
this.verificationId = verId;
};
final PhoneCodeSent smsCodeSent = (String verId, [int forceCodeResend]) {
print("send code dilog");
this.verificationId = verId;
smsCodeDialog(context).then((value) {
print('Signed in');
});
};
final PhoneVerificationCompleted verifiedSuccess = (AuthCredential user){
print('Phone Verification Completed');
};
final PhoneVerificationFailed veriFailed = (AuthException exception) {
print('${exception.message}');
};
await FirebaseAuth.instance.verifyPhoneNumber(
phoneNumber: "+919700000000",
codeAutoRetrievalTimeout: autoRetrieve,
codeSent: smsCodeSent,
timeout: const Duration(seconds: 5),
verificationCompleted: verifiedSuccess,
verificationFailed: veriFailed);
}
Future<bool> smsCodeDialog(BuildContext context) {
return showDialog(
context: context,
barrierDismissible: false,
builder: (BuildContext context) {
return new AlertDialog(
title: Text('Enter sms Code'),
content: TextField(
onChanged: (value) {
this.smsCode = value;
},
),
contentPadding: EdgeInsets.all(10.0),
actions: <Widget>[
new FlatButton(
child: Text('Done'),
onPressed: () {
FirebaseAuth.instance.currentUser().then((user) {
if (user != null) {
Navigator.of(context).pop();
Navigator.of(context).pushReplacementNamed('/homepage');
} else {
Navigator.of(context).pop();
signIn();
}
});
},
)
],
);
});
}
signIn(){
final AuthCredential credential = PhoneAuthProvider.getCredential(
verificationId: verificationId,
smsCode: smsCode);
FirebaseAuth.instance.signInWithCredential(credential).then((user){
Navigator.of(context).pushReplacementNamed('/homepage');
}).catchError((e){
print('Auth Credential Error : $e');
});
}
#override
Widget build(BuildContext context) {
return new Scaffold(
appBar: new AppBar(
title: new Text('PhoneAuth'),
),
body: new Center(
child: Container(
padding: EdgeInsets.all(25.0),
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: <Widget>[
TextField(
decoration: InputDecoration(hintText: 'Enter Phone number'),
onChanged: (value) {
this.phoneNo = value;
},
),
SizedBox(height: 10.0),
RaisedButton(
onPressed: verifyPhone,
child: Text('Verify'),
textColor: Colors.white,
elevation: 7.0,
color: Colors.blue)
],
)),
),
);
}
}
this is error while submit the button
Launching lib/main.dart on Dhruvin’s iPhone in debug mode...
Automatically signing iOS for device deployment using specified development team in Xcode project: ******
Running Xcode build...
Xcode build done. 99.4s
Installing and launching...
Syncing files to device Dhruvin’s iPhone...
Lost connection to device.
However i am not able to see stacktrace or log as app lost connection from debugger.
i Solved the Problem,
thank you so much #DevarshRanpura for Helping me,
1) replace
signIn(){
final AuthCredential credential = PhoneAuthProvider.getCredential(
verificationId: verificationId,
smsCode: smsCode);
FirebaseAuth.instance.signInWithCredential(credential).then((user){
Navigator.of(context).pushReplacementNamed('/homepage');
}).catchError((e){
print('Auth Credential Error : $e');
});
}
to this
signIn() async {
final AuthCredential credential = PhoneAuthProvider.getCredential(
verificationId: verificationId,
smsCode: smsCode,
);
final AuthResult authResult = await _auth.signInWithCredential(credential);
final FirebaseUser user = authResult.user;
print('User Id : ' + user.uid);
}
also Define _auth on your dart file
final FirebaseAuth _auth = FirebaseAuth.instance;
2) adding reverse client id in to your project
open your project in xcode
double click on your project name
go to the info tab
In URL Types->URL schemes add the reverse client Id

how to download/create pdf through webview in flutter

I have created a webviewscaffold but can't download anything from it while browsing from the webview, I made but when I click the button it does nothing. I don't know where to start, like in other browsers that can download and preview, I'm trying to achieve the same thing in here.
class AppState extends State<App> {
#override
Widget build(BuildContext context) {
return Scaffold(
body: Stack(
children: <Widget>[
Align(
alignment: Alignment(1, 1),
child: Container(
child: Web(), // it is a statefulwidget that have WebviewScaffold, i have created it on a new page and imported/used here
),
),
Column(
mainAxisAlignment: MainAxisAlignment.end,
children: <Widget>[
Padding(
padding: EdgeInsets.only(top: 20.0),
)
],
),
Align(
alignment: Alignment(0.7, -0.93),
child: FloatingActionButton(
tooltip: "Share",
child: Icon(
Icons.share,
color: Colors.amberAccent,
),
onPressed: () {
_onShareTap();
}),
),
],
),
);
}
I expect, when I click the print or download button within the webview it should work like any other browser.
You can use my plugin flutter_inappwebview, which is a Flutter plugin that allows you to add inline WebViews or open an in-app browser window and has a lot of events, methods, and options to control WebViews.
To be able to recognize downloadable files, you need to set the useOnDownloadStart: true option, and then you can listen the onDownloadStart event!
Also, for example, on Android you need to add write permission inside your AndroidManifest.xml file:
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE"/>
Then, you need to ask permission using the permission_handler plugin. Instead, to effectively download your file, you can use the flutter_downloader plugin.
Here is a complete example using http://ovh.net/files/ (in particular, the http://ovh.net/files/1Mio.dat as URL) to test the download:
import 'dart:async';
import 'package:flutter/material.dart';
import 'package:flutter_inappwebview/flutter_inappwebview.dart';
import 'package:flutter_downloader/flutter_downloader.dart';
import 'package:path_provider/path_provider.dart';
import 'package:permission_handler/permission_handler.dart';
Future main() async {
WidgetsFlutterBinding.ensureInitialized();
await FlutterDownloader.initialize(
debug: true // optional: set false to disable printing logs to console
);
await Permission.storage.request();
runApp(new MyApp());
}
class MyApp extends StatefulWidget {
#override
_MyAppState createState() => new _MyAppState();
}
class _MyAppState extends State<MyApp> {
InAppWebViewController webView;
#override
void initState() {
super.initState();
}
#override
void dispose() {
super.dispose();
}
#override
Widget build(BuildContext context) {
return MaterialApp(
home: Scaffold(
appBar: AppBar(
title: const Text('InAppWebView Example'),
),
body: Container(
child: Column(children: <Widget>[
Expanded(
child: InAppWebView(
initialUrl: "http://ovh.net/files/1Mio.dat",
initialHeaders: {},
initialOptions: InAppWebViewGroupOptions(
crossPlatform: InAppWebViewOptions(
debuggingEnabled: true,
useOnDownloadStart: true
),
),
onWebViewCreated: (InAppWebViewController controller) {
webView = controller;
},
onLoadStart: (InAppWebViewController controller, String url) {
},
onLoadStop: (InAppWebViewController controller, String url) {
},
onDownloadStart: (controller, url) async {
print("onDownloadStart $url");
final taskId = await FlutterDownloader.enqueue(
url: url,
savedDir: (await getExternalStorageDirectory()).path,
showNotification: true, // show download progress in status bar (for Android)
openFileFromNotification: true, // click on notification to open downloaded file (for Android)
);
},
))
])),
),
);
}
}
Here, as you can see, I'm using also the path_provider plugin to get the folder where I want to save the file.
If you are the owner of html page, you can check workaround that works for me. In source of your html page add onclick function for specific resource links:
function downloadpdf() {
var currentHref = window.location.href;
window.history.pushState(null, null, '/app/somepdf.pdf');
setTimeout(() => window.location.replace(currentHref), 1000);
}
In Flutter code add listener:
StreamSubscription<String> _onWebViewUrlChanged;
_onWebViewUrlChanged =
FlutterWebviewPlugin().onUrlChanged.listen((String url) {
if (url.contains('.pdf')) {
launchURL(url);
}
});
launchURL will open predefined url in external browser window. So you can download pdf/etc from flutter_webview_plugin.
You should add some your app-specific js/flutter magic

Show CircularProgressIndicator while loading URL in Web View

I want to show CircularProgressIndicator when ever the webview loads an URL. Below is code but it only shows loading element while initializing the webview.
Widget build(BuildContext context) {
return new MaterialApp(
theme
: new ThemeData(
primaryColor: Color.fromRGBO(58, 66, 86, 1.0), fontFamily: 'Raleway'),
routes: {
"/": (_) => new WebviewScaffold(
url: url,
appBar: new AppBar(
title: Text(title),
actions: <Widget>[
IconButton(
icon: Icon(Icons.close),
onPressed: () => Navigator.of(context).pop(null),
)
],
),
withJavascript: true,
withLocalStorage: true,
appCacheEnabled: true,
hidden: true,
)
},
);
}
I want it to show loading element when user clicks on any link within webview.
its should work for first time, I know that is not exactly what's your looking for but it may help.
WebviewScaffold(
url: "https://www.google.com/",
appBar: new AppBar(
title: const Text('Widget webview'),
),
withZoom: true,
withLocalStorage: true,
hidden: true,
initialChild: Container(
child: const Center(
child: CircularProgressIndicator(),
),
),
);
This doesn't seem to be supported currently.
There is a pull request that seems to provide such a feature
https://github.com/fluttercommunity/flutter_webview_plugin/pull/255
Several related issues/feature requests
https://github.com/fluttercommunity/flutter_webview_plugin/issues/177
https://github.com/fluttercommunity/flutter_webview_plugin/issues/284
https://github.com/fluttercommunity/flutter_webview_plugin/issues/232
https://github.com/fluttercommunity/flutter_webview_plugin/issues/159
This is how I implemented using IndexedStack
class WebViewWidget extends StatefulWidget {
#override
_WebViewWidgetState createState() => _WebViewWidgetState();
}
class _WebViewWidgetState extends State<WebViewWidget> {
var stackToShow = 1;
#override
Widget build(BuildContext context) {
return IndexedStack(
index: stackToShow,
children: [
WebView(
initialUrl: "https://www.google.com/",
onPageFinished: (String url) {
// when page loaded
setState(() {
stackToShow = 0;
});
},
),
Container(child: Center(child: CircularProgressIndicator())),
],
);
}
}
Enjoy coding!
This will work with WebviewScaffold
Just paste it in your Class.
#override
void initState() {
super.initState();
_onPageProgress =
flutterWebViewPlugin.onProgressChanged.listen(progessChange);
}
progessChange(double event) {
print("Page loading " + event.toString());
if (event == 1.0) {
flutterWebViewPlugin.show();
} else {
flutterWebViewPlugin.hide();
}
}
final flutterWebViewPlugin = FlutterWebviewPlugin();
late StreamSubscription<double> _onPageProgress;
Widget build(BuildContext context) {
return WebviewScaffold(
initialChild: Container(
color: Colors.white,
child: Center(
child: CircularProgressIndicator(
color: Colors.blue,
)),
),
hidden: true,
clearCache: true,
withJavascript: true,
url: "https://www.google.com/",
);
}

Flutter / flutter_webview_plugin => hide/show AppBar + BottomNavigationBar on Scroll down/up

I am using a WebviewScaffold. It works well, but now I want to hide the BottomNavigationBar and the AppBar on Scroll up. OnScroll down it should show the AppBar and BottomNavigationBar. Like it works in Chrome on iOS.
As I know I cant use a Sliver because
"Warning: The webview is not integrated in the widget tree, it is a native view on top of the flutter view. you won't be able to use snackbars, dialogs ..."
It would be Nice if somebody could help me.
Thanks in advance!
I found how to get ScrollParams.... But now I have to hide/show the AppBar and the BottomNavigationBar.
#override
void initState() {
// TODO: implement initState
super.initState();
_onScrollYChanged =
flutterWebViewPlugin.onScrollYChanged.listen((double x) {
if (mounted) {
print(_onScrollYChanged.toString());
setState(() {
if (_posY < x) {
_showBar = false;
print("DOWN");
} else {
_showBar = true;
print("UP");
}
_posY = x;
print("Scroll in Y Direction: $x");
});
}
});
}
My Build in my APP looks like this:
#override
Widget build(BuildContext context) {
return WebviewScaffold(
url: "https://www.domain.de",
appBar: AppBar(
toolbarOpacity: _showBar ? 1.0 : 0.0,
title: const Text('Widget WebView'),
leading: IconButton(
icon: Icon(Icons.apps),
onPressed: () {
flutterWebViewPlugin.goBack();
},
),
),
bottomNavigationBar: BottomAppBar(
color: Colors.blue,
child: Row(
children: <Widget>[
IconButton(
icon: const Icon(Icons.arrow_back_ios),
onPressed: () {
flutterWebViewPlugin.goBack();
},
),
],
),
),
withZoom: true,
withLocalStorage: true,
hidden: true,
headers: _HTMLheaders,
);
}
}

Resources