I'm trying to use the url_launcher
plugin to open youtube videos by link but the canLaunch
function keeps throwing an error. I'm able to bypass this error only by completely removing the canLaunch
function but can't figure out what is wrong.
Code not working:
_goToVideo(YoutubeVideoData video) async {
if (await canLaunch(video.url)) {
await launch(video.url);
} else {
throw 'Could not launch ${video.url}';
}
}
Code working:
_goToVideo(YoutubeVideoData video) async {
await launch(video.url);
}
I'm not quite sure why I can't use the canLaunch
method as written in the README Example
Error:
E/flutter (12574): [ERROR:flutter/lib/ui/ui_dart_state.cc(166)] Unhandled Exception: Could not launch https://www.youtube.com/watch?v=-3g5WlqJtIo
E/flutter (12574): #0 _goToVideo (package:esfandapp/widgets/newsList/videoCard.dart:71:5)
E/flutter (12574): <asynchronous suspension>
E/flutter (12574): #1 VideoCard.build.<anonymous closure> (package:esfandapp/widgets/newsList/videoCard.dart:13:20)
E/flutter (12574): #2 _InkResponseState._handleTap (package:flutter/src/material/ink_well.dart:992:19)
E/flutter (12574): #3 _InkResponseState.build.<anonymous closure> (package:flutter/src/material/ink_well.dart:1098:38)
E/flutter (12574): #4 GestureRecognizer.invokeCallback (package:flutter/src/gestures/recognizer.dart:184:24)
E/flutter (12574): #5 TapGestureRecognizer.handleTapUp (package:flutter/src/gestures/tap.dart:524:11)
E/flutter (12574): #6 BaseTapGestureRecognizer._checkUp (package:flutter/src/gestures/tap.dart:284:5)
E/flutter (12574): #7 BaseTapGestureRecognizer.acceptGesture (package:flutter/src/gestures/tap.dart:256:7)
E/flutter (12574): #8 GestureArenaManager.sweep (package:flutter/src/gestures/arena.dart:158:27)
E/flutter (12574): #9 GestureBinding.handleEvent (package:flutter/src/gestures/binding.dart:224:20)
E/flutter (12574): #10 GestureBinding.dispatchEvent (package:flutter/src/gestures/binding.dart:200:22)
E/flutter (12574): #11 GestureBinding._handlePointerEvent (package:flutter/src/gestures/binding.dart:158:7)
E/flutter (12574): #12 GestureBinding._flushPointerEventQueue (package:flutter/src/gestures/binding.dart:104:7)
E/flutter (12574): #13 GestureBinding._handlePointerDataPacket (package:flutter/src/gestures/binding.dart:88:7)
E/flutter (12574): #14 _rootRunUnary (dart:async/zone.dart:1206:13)
E/flutter (12574): #15 _CustomZone.runUnary (dart:async/zone.dart:1100:19)
E/flutter (12574): #16 _CustomZone.runUnaryGuarded (dart:async/zone.dart:1005:7)
E/flutter (12574): #17 _invoke1 (dart:ui/hooks.dart:267:10)
E/flutter (12574): #18 _dispatchPointerDataPacket (dart:ui/hooks.dart:176:5)
Widget using the function:
class VideoCard extends StatelessWidget {
final YoutubeVideoData video;
VideoCard({this.video});
@override
Widget build(BuildContext context) {
return InkWell(
onTap: () => _goToVideo(video),
child: Container(
child: Card(
child: Container(
child: Column(
children: [
Align(
child: Padding(
child: Text(
video.title,
style: TextStyle(
fontFamily: 'Roboto Condensed',
fontSize: 16,
),
),
padding: EdgeInsets.fromLTRB(15, 0, 15, 10),
),
alignment: Alignment.centerLeft,
),
Container(
child: Image.network(video.thumbnails[1], fit: BoxFit.cover,),
width: MediaQuery.of(context).size.width,
),
Align(
child: Container(
child: Text(
video.date.toString() + "",
style: TextStyle(
fontFamily: 'Roboto Condensed',
fontSize: 14,
fontWeight: FontWeight.w300,
),
),
padding: EdgeInsets.fromLTRB(15, 5, 15, 0),
),
alignment: Alignment.centerLeft,
),
],
),
width: MediaQuery.of(context).size.width - 32,
padding: EdgeInsets.symmetric(
horizontal: 0,
vertical: 10,
),
alignment: Alignment.center,
),
shape: RoundedRectangleBorder(
borderRadius: BorderRadius.all(Radius.circular(25))),
),
),
);
}
}
Starting from API30 (Android 11), your Android app has to list all apps it interacts with.
You can add:
<uses-permission android:name="android.permission.QUERY_ALL_PACKAGES"/>
in your android manifest to bypass it or specifically list them.
For more info: https://developer.android.com/about/versions/11/privacy/package-visibility
Personally, I don't like the uncertainty that seems to come with using the QUERY_ALL_PACKAGES permission (because Google could stop letting people use it in the future). For that reason, I did some investigation and found that adding the following to my AndroidManifest.xml allows my app to open the browser, phone app and email app on API 30:
<manifest>
<!-- Nest within the manifest element, not the application element-->
<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>
....
</application>
</manifest>
To get the same functionality working on iOS, it may be necessary to add the following to your info.plist file:
<key>LSApplicationQueriesSchemes</key>
<array>
<string>https</string>
<string>http</string>
<string>tel</string>
<string>mailto</string>
</array>
Just wanted to share in case it helps someone else out.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With