I want to set up the oAuth authentication of the Spotify API in my Flutter app. I chose the flutter_web_auth 0.1.1 package.
So far, I have managed that the user can log in to Spotify. After logging in, the user should be redirected back to my app. That does not work. Spotify always redirects the user to another website and not back to the app. How do I close the WebView after the user logging in and redirect the user to my app?
import 'package:flutter/material.dart';
import 'package:flutter_web_auth/flutter_web_auth.dart';
void main() => runApp(MyApp());
class MyApp extends StatefulWidget {
#override
_MyAppState createState() => _MyAppState();
}
class _MyAppState extends State<MyApp> {
#override
void initState() {
super.initState();
authenticate();
}
void authenticate() async {
// Present the dialog to the user
final result = await FlutterWebAuth.authenticate(
url:
"https://accounts.spotify.com/de/authorize?client_id=78ca499b2577406ba7c364d1682b4a6c&response_type=code&redirect_uri=https://partyai/callback&scope=user-read-private%20user-read-email&state=34fFs29kd09",
callbackUrlScheme: "https://partyai/callback",
);
// Extract token from resulting url
final token = Uri.parse(result).queryParameters['token'];
print('token');
print(token);
}
#override
Widget build(BuildContext context) {
return MaterialApp(
home: Scaffold(
appBar: AppBar(
title: const Text('Web Auth example'),
),
body: Center(
child: Text(
'test',
),
),
),
);
}
}
android/app/src/main/AndroidManifest.xml
<activity android:name="com.linusu.flutter_web_auth.CallbackActivity" >
<intent-filter android:label="flutter_web_auth">
<action android:name="android.intent.action.VIEW" />
<category android:name="android.intent.category.DEFAULT" />
<category android:name="android.intent.category.BROWSABLE" />
<data android:scheme="https://partyai/callback" />
</intent-filter>
</activity>
enter image description here
The callbackUrlScheme should be partyai, the redirect_uri should be partyai:/ and the AndroidManifest.xml android:scheme value should be partyai.
Use this code
void authenticate() async {
// Present the dialog to the user
final result = await FlutterWebAuth.authenticate(
url:
"https://accounts.spotify.com/authorize?client_id=xxxxxxxxxxxxxxxxxxxxxxxxxxxxx&redirect_uri=yourname:/&scope=user-read-currently-playing&response_type=token&state=123",
callbackUrlScheme: "yourname",
);
// Extract token from resulting url
final token = Uri.parse(result);
String at = token.fragment;
at = "http://website/index.html?$at"; // Just for easy persing
var accesstoken = Uri.parse(at).queryParameters['access_token'];
print('token');
print(accesstoken);
}
Add to AndroidManifest.xml android\app\src\main
...
</activity>
<activity android:name="com.linusu.flutter_web_auth.CallbackActivity" >
<intent-filter android:label="flutter_web_auth">
<action android:name="android.intent.action.VIEW" />
<category android:name="android.intent.category.DEFAULT" />
<category android:name="android.intent.category.BROWSABLE" />
<data android:scheme="yourname" />
</intent-filter>
</activity>
</application>
</manifest>
I am also facing issue with navigating back to app from authentication page in flutter am using flutter_web_auth plugin.
As per my understanding every thing working fine but it is not navigating back to flutter application.
Question: what is the .CallbackActivity in the below code
<activity android:name="com.linusu.flutter_web_auth.CallbackActivity" >
<intent-filter android:label="flutter_web_auth">
<action android:name="android.intent.action.VIEW" />
<category android:name="android.intent.category.DEFAULT" />
<category android:name="android.intent.category.BROWSABLE" />
<data android:scheme="yourname" />
</intent-filter>
</activity>
Related
I tried, to authenticate my App at Spotify, via FlutterWebAuth2, but I always, get a "Illegal redirect Uri" Error.
My authentication function
try {
var redirectURI = 'beatrunner:/';
final result = await FlutterWebAuth2.authenticate(
url:
"https://accounts.spotify.com/authorize?client_id=xxxx&redirect_uri= $redirectURI&scope=user-read-currently-playing&response_type=token&state=123",
callbackUrlScheme: "beatrunner",
);
} catch (e) {
print(e);
}
}
My Android Manifest file
<activity android:name="com.linusu.flutter_web_auth2.CallbackActivity" android:exported="true">
<intent-filter android:label="flutter_web_auth2">
<action android:name="android.intent.action.VIEW" />
<category android:name="android.intent.category.DEFAULT" />
<category android:name="android.intent.category.BROWSABLE" />
<data android:scheme="beatrunner" />
</intent-filter>
</activity>
I tried to solve my Problem using this articlehttps://stackoverflow.com/questions/58244209/flutter-oauth2-problems-with-redirect-uri, but I still get a error message.
This is my code
void _launchUrl() async {
userInfo = SpUtil.getMap(Constants.APP_USERINFO);
if(userInfo['permit']['hua_yan'] == '1'){
final Uri _url = Uri.parse('http://www.baidu.com');
if (!await canLaunchUrl(_url)) throw 'Could not launch $_url';
}
}
Will prompt
I/UrlLauncher( 6039): component name for http://www.baidu.com is {com.huawei.browser/com.huawei.browser.Main}
I used some official prompt codes, but still can't, still can't
https://developer.android.com/training/package-visibility/use-cases,in AndroidManifest.xml file
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
package="cn.ahlims.ahlims">
<!-- Flutter needs it to communicate with the running application
to allow setting breakpoints, to provide hot reload, etc.
-->
<uses-permission android:name="android.permission.INTERNET"/>
<queries>
<intent>
<action android:name="android.intent.action.VIEW" />
<category android:name="android.intent.category.BROWSABLE" />
<data android:scheme="https" />
</intent>
</queries>
</manifest>
Hope to get help, thank you
Add below dependency in AndroidManifest.xml just above of application
<uses-permission android:name="android.permission.INTERNET" />
<uses-permission android:name="android.permission.QUERY_ALL_PACKAGES" />
<queries>
<intent>
<action android:name="android.intent.action.VIEW" />
<category android:name="android.intent.category.BROWSABLE" />
<data android:scheme="https" />
</intent>
<!-- If your app opens https URLs -->
<intent>
<action android:name="android.intent.action.VIEW" />
<data android:scheme="http" />
</intent>
<!-- If your app opens https URLs -->
<intent>
<action android:name="android.intent.action.VIEW" />
<data android:scheme="https" />
</intent>
</queries>
MobileUrl Screen
class MobileUrl extends StatelessWidget {
const MobileUrl({Key? key}) : super(key: key);
#override
Widget build(BuildContext context) {
void _launchUrlA() async {
final Uri _url = Uri.parse('https://flutter.dev');
if (!await launchUrl(_url)) throw 'Could not launch $_url';
}
return Scaffold(
body: SafeArea(
child: Center(
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: [
ElevatedButton(onPressed: (){
_launchUrlA();
}, child: Text('Click Button'))
],
),
),
),
);
}
}
This can realize the function I want。
String baseUrl = Get.find<AppConfigService>().baseUrl;
final Uri url = Uri.parse(baseUrl);
if (await canLaunchUrl(url)){
await launchUrl(url, mode: LaunchMode.externalApplication ,webOnlyWindowName:'_blank');
}
Reference links:
https://pub.dev/documentation/url_launcher/latest/url_launcher/launchUrl.html
im opening app from link. but i cant listen my link after opened app. i need to get a token from link.
AndroidManifest.xml:
<meta-data android:name="flutter_deeplinking_enabled" android:value="true" />
<intent-filter>
<action android:name="android.intent.action.VIEW" />
<category android:name="android.intent.category.DEFAULT" />
<category android:name="android.intent.category.BROWSABLE" />
<data
android:scheme="https"
android:host="myappname.page.link" />
</intent-filter>
and my listener uni_links like that:
getLinksStream().listen((link) async {
print("urigirdi");
try {
var _latestUri;
if (link != null) _latestUri = Uri.parse(link);
setState(() {
sourceLink = link!.split("/")[3];
print("link");
linkToken = sourceLink.split("?")[2];
linkPath = sourceLink.split("?")[0];
print(linkPath);
print(linkToken);
});
} on FormatException {
print("--- A link got here but was invalid");
}
});
i need some help. can anybody help me ? thanks for everything...
I am making a mobile browser app. When you download a browser from the play market, then a notification pops up "Which browser do you want to use by default?". How to do it?
Update:
I added this filter to AndroidManifest.xml file. And now, when you click on a link in any application, it asks through which browser to open the link, but now how to intercept this link to send it to the webview? As here
<intent-filter>
<action android:name="android.speech.action.VOICE_SEARCH_RESULTS" />
<category android:name="android.intent.category.DEFAULT" />
</intent-filter>
<intent-filter>
<action android:name="android.intent.action.VIEW" />
<category android:name="android.intent.category.DEFAULT" />
<category android:name="android.intent.category.BROWSABLE" />
<data android:scheme="http" />
<data android:scheme="https" />
<data android:scheme="about" />
<data android:scheme="javascript" />
</intent-filter>
<intent-filter>
<action android:name="android.intent.action.VIEW" />
<category android:name="android.intent.category.BROWSABLE" />
<category android:name="android.intent.category.DEFAULT" />
<data android:scheme="http" />
<data android:scheme="https" />
<data android:scheme="inline" />
<data android:mimeType="text/html" />
<data android:mimeType="text/plain" />
<data android:mimeType="application/xhtml+xml" />
<data android:mimeType="application/vnd.wap.xhtml+xml" />
</intent-filter>
<intent-filter>
<action android:name="android.intent.action.MAIN" />
<category android:name="android.intent.category.DEFAULT" />
<category android:name="android.intent.category.LAUNCHER" />
<category android:name="android.intent.category.BROWSABLE" />
</intent-filter>
<intent-filter>
<action android:name="android.intent.action.WEB_SEARCH" />
<category android:name="android.intent.category.DEFAULT" />
<category android:name="android.intent.category.BROWSABLE" />
<data android:scheme="" />
<data android:scheme="http" />
<data android:scheme="https" />
</intent-filter>
<intent-filter>
<action android:name="android.intent.action.MEDIA_SEARCH" />
<category android:name="android.intent.category.DEFAULT" />
</intent-filter>
<intent-filter>
<action android:name="android.intent.action.SEARCH" />
<category android:name="android.intent.category.DEFAULT" />
</intent-filter>
You can use this package receive_sharing_intent
example code:
import 'package:flutter/material.dart';
import 'dart:async';
import 'package:receive_sharing_intent/receive_sharing_intent.dart';
class MyApp extends StatefulWidget {
const MyApp({super.key});
#override
_MyAppState createState() => _MyAppState();
}
class _MyAppState extends State<MyApp> {
late StreamSubscription _intentDataStreamSubscription;
String? _sharedText;
#override
void initState() {
super.initState();
// For sharing or opening urls/text coming from outside the app while the app is in the memory
_intentDataStreamSubscription =
ReceiveSharingIntent.getTextStream().listen((String value) {
setState(() {
_sharedText = value;
print("Shared: $_sharedText");
});
}, onError: (err) {
print("getLinkStream error: $err");
});
// For sharing or opening urls/text coming from outside the app while the app is closed
ReceiveSharingIntent.getInitialText().then((String? value) {
setState(() {
_sharedText = value;
print("Shared: $_sharedText");
});
});
}
#override
void dispose() {
_intentDataStreamSubscription.cancel();
super.dispose();
}
#override
Widget build(BuildContext context) {
const textStyleBold = TextStyle(fontWeight: FontWeight.bold);
return MaterialApp(
home: Scaffold(
appBar: AppBar(
title: const Text('Plugin example app'),
),
body: Center(
child: Column(
children: <Widget>[
const Text("Shared urls/text:", style: textStyleBold),
Text(_sharedText ?? "")
],
),
),
),
);
}
}
So I know that for android version >= 30 you need to add queries in the manifest, but this didnt do the trick for me.
Im using Android Version 29. I nevertheless added these lines to my manifest.
So my manifest looks like this.
<uses-permission android:name="android.permission.QUERY_ALL_PACKAGES" />
<queries>
<intent>
<action android:name="android.intent.action.VIEW" />
<data android:scheme="https" />
</intent>
<intent>
<action android:name="android.intent.action.DIAL" />
<data android:scheme="tel" />
</intent>
<intent>
<action android:name="android.intent.action.SEND" />
<data android:mimeType="*/*" />
</intent>
</queries>
I managed to reproduce the error for my machine with the code bellow. Nevertheless I get this error:
I/UrlLauncher(10601): component name for mailto:?subject=&body= is null
Down below you find the code for main.dart
import 'package:flutter/material.dart';
import 'package:url_launcher/url_launcher.dart';
void main() {
runApp(const MyApp());
}
class MyApp extends StatelessWidget {
const MyApp({Key? key}) : super(key: key);
// This widget is the root of your application.
#override
Widget build(BuildContext context) {
return MaterialApp(
title: 'Flutter Demo',
theme: ThemeData(
primarySwatch: Colors.blue,
),
home: const MyHomePage(title: 'Flutter Demo Home Page'),
);
}
}
class MyHomePage extends StatefulWidget {
const MyHomePage({Key? key, required this.title}) : super(key: key);
final String title;
#override
State<MyHomePage> createState() => _MyHomePageState();
}
class _MyHomePageState extends State<MyHomePage> {
#override
Widget build(BuildContext context) {return Scaffold(
body: Center(
child: ElevatedButton(
style: ButtonStyle(
shape: MaterialStateProperty.all<RoundedRectangleBorder>(
RoundedRectangleBorder(
borderRadius: BorderRadius.circular(18.0)),
),
),
onPressed: () => launchEmail(
toEmail: "js",
subject: "dm",
message: "jd",
), child: Text("null"),
),
),
);
}
Future launchEmail({
required String toEmail,
required String subject,
required String message,
}) async {
final url =
"mailto:$toEmail?subject=${Uri.encodeFull(subject)}&body=${Uri.encodeFull(message)}";
if (await canLaunch(url)) {
await launch(url);
}
}
}
What I did to resolve the error:
I set my Android API to 30+
Your manifest.xml should contains this:
<uses-permission android:name="android.permission.QUERY_ALL_PACKAGES" />
<queries>
<intent>
<action android:name="android.intent.action.VIEW" />
<data android:scheme="https" />
</intent>
<intent>
<action android:name="android.intent.action.DIAL" />
<data android:scheme="tel" />
</intent>
<intent>
<action android:name="android.intent.action.SEND" />
<data android:mimeType="*/*" />
</intent>
</queries>
If this doesnt fix it for you, Suj suggested to add:
<package android:name="com.app" />
to your manifest
So just paste the lines under the start of your manifest:
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
package="com.nikita.autolab">
<uses-permission android:name="android.permission.QUERY_ALL_PACKAGES"
tools:ignore="QueryAllPackagesPermission" />
<queries>
<intent>
<action android:name="android.intent.action.VIEW" />
<data android:scheme="https" />
</intent>
<intent>
<action android:name="android.intent.action.DIAL" />
<data android:scheme="tel" />
</intent>
<intent>
<action android:name="android.intent.action.SEND" />
<data android:mimeType="*/*" />
</intent>
</queries>
<application
android:label="AutoLab"
android:name="${applicationName}"
android:icon="#mipmap/ic_launcher">
<activity
android:name=".MainActivity"
android:exported="true"
android:launchMode="singleTop"
android:theme="#style/LaunchTheme"
android:configChanges="orientation|keyboardHidden|keyboard|screenSize|smallestScreenSize|locale|layoutDirection|fontScale|screenLayout|density|uiMode"
android:hardwareAccelerated="true"
android:windowSoftInputMode="adjustResize">
<!-- Specifies an Android theme to apply to this Activity as soon as
the Android process has started. This theme is visible to the user
while the Flutter UI initializes. After that, this theme continues
to determine the Window background behind the Flutter UI. -->
<meta-data
android:name="io.flutter.embedding.android.NormalTheme"
android:resource="#style/NormalTheme"
/>
<intent-filter>
<action android:name="android.intent.action.MAIN"/>
<category android:name="android.intent.category.LAUNCHER"/>
</intent-filter>
</activity>
<!-- Don't delete the meta-data below.
This is used by the Flutter tool to generate GeneratedPluginRegistrant.java -->
<meta-data
android:name="flutterEmbedding"
android:value="2" />
</application>
</manifest>
By updating your API to 30 (minSdkVersion), you are restricting the devices and Android versions which can install your app.
Alternative solution: Don't call canLaunch for mailto links - use it only for http and https!
Since I have both http(s) and mailto links in my app, I use the try-catch block.
Here is the full function:
class UrlHandler {
/// Attempts to open the given [url] in in-app browser. Returns `true` after successful opening, `false` otherwise.
static Future<bool> open(String url) async {
try {
await launch(
url,
enableJavaScript: true,
);
return true;
} catch (e) {
log(e.toString());
return false;
}
}
}