I was using youtube_player_flutter and implemented everything correctly as it was written in it's README.
But still I was facing one issue that whenever I open that page where I want the youtube player to open, it keeps loading and never loads the video.
I've searched about this issue everywhere but didn't get any solution. One of those solutions was that to include internet permission in AndroidManifest.xml:
<uses-permission android:name="android.permission.INTERNET"/>
I did this, nothing changed. I also downgraded the package to v6.1.1, someone asked me to do this in github issue, but that also did nothing.
How can I resolve this issue?
I'm answering my own question as I didn't find anything that can resolve this problem when I was searching about this issue.
So, I tried to define the controller in initState() and it worked, and now it's working in v7.0.0+7. This is my code:
class AboutTopic extends StatefulWidget {
final String videoLink;
AboutTopic({this.videoLink});
#override
_AboutTopicState createState() => _AboutTopicState();
}
class _AboutTopicState extends State<AboutTopic> {
YoutubePlayerController _controller;
#override
void initState() {
_controller = YoutubePlayerController(
initialVideoId:
YoutubePlayer.convertUrlToId(widget.videoLink),
flags: YoutubePlayerFlags(
mute: false,
autoPlay: true,
disableDragSeek: true,
loop: false,
enableCaption: false),
);
super.initState();
}
#override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: Text('About'),
centerTitle: true,
leading: IconButton(
icon: Icon(Icons.arrow_back),
onPressed: () {
Navigator.of(context).pop();
},
)
),
body: YoutubePlayer(
controller: _controller,
showVideoProgressIndicator: true,
bottomActions: <Widget>[
const SizedBox(width: 14.0),
CurrentPosition(),
const SizedBox(width: 8.0),
ProgressBar(isExpanded: true),
RemainingDuration(),
],
aspectRatio: 4 / 3,
progressIndicatorColor: Colors.white,
onReady: () {
print('Player is ready.');
},
),
);
}
}
Related
I want to add a Youtube video in my flutter web app , I tried Youtube_player_iframe and its not showing anything ..
here is my code :
void initState() {
super.initState();
_controller = YoutubePlayerController(
params: const YoutubePlayerParams(
showControls: true,
mute: false,
showFullscreenButton: true,
loop: false,
),
)..onInit = (){
_controller.loadVideoByUrl(mediaContentUrl: 'https://www.youtube.com/watch?v=5oZXNRQ0_Uo');
};
}
in the scaffold :
YoutubePlayerControllerProvider(controller: _controller, child: YoutubePlayer(
aspectRatio: 4/3,
controller: _controller,
)),
i found the solution just replace the
_controller.loadVideoByUrl(...);
to
_controller.loadVideo(...);
I have a DataTable in which some cells have links. Ideally, I would like to fetch a preview about the link's content whenever hovering over the link, which I was able to achieve using the Stack widget. However, since the stacked preview is inside the DataCell, it seems like I'm not able to raise its "z-index" to be on top of the rest of the table.
Is this not possible with Flutter, or is there a way around it?
The only way I imagine this working, without something to update a global z-index, would be for the cell to update a global state and then have the thumbnail preview appear on a Stack above the DataTable level. But I wish there was a less clunkier way to do it...
3 widgets I've tried but to no avail — they might work, I don't know —:
Tooltip
Overlay
FloatingActionButton
My whole app is here, and the precise commit is 0303732. The relevant code is this ClickableLink widget:
import 'package:flutter/material.dart';
import 'package:flutter/gestures.dart';
import 'package:url_launcher/url_launcher.dart';
import '../schema/links.dart';
#immutable
class ClickableLink extends StatefulWidget {
const ClickableLink({
Key? key,
required this.link,
this.linkText,
this.color = Colors.blue,
}) : super(key: key);
final Link link;
final String? linkText;
final Color color;
#override
State<ClickableLink> createState() => _ClickableLinkState();
}
class _ClickableLinkState extends State<ClickableLink> {
Widget hoverWidget = const SizedBox.shrink();
void _fetchPreview(PointerEvent pointerEvent) {
setState(() {
if (widget.link.host == 'online-go.com' && widget.link.prePath == 'game') {
hoverWidget = Positioned(
top: 25,
child: Image.network('https://online-go.com/api/v1/games/${widget.link.id}/png'),
);
}
});
}
void _onExit(PointerEvent pointerEvent) {
setState(() {
hoverWidget = const SizedBox.shrink();
});
}
#override
Widget build(BuildContext context) {
return MouseRegion(
onHover: _fetchPreview,
onExit: _onExit,
child: Stack(
clipBehavior: Clip.none,
children: [
SelectableText.rich(
TextSpan(
text: widget.linkText ?? widget.link.id,
style: TextStyle(color: widget.color),
recognizer: TapGestureRecognizer()
..onTap = () async => launch(widget.link.completeLink),
),
),
hoverWidget,
],
),
);
}
}
The problem here is due to the fact that your Stack widget, defined inside ClickableLink, will be at a "lower" point (inside your app widget tree) than every other GameResultCell.
So even the higher z-index will still be behind the other GameResultCells.
To fix this I would reccomend changing your structure and define an higher point in your structure to show the preview.
Another way could be using a library to nest your preview inside a tooltip. Take a look at this one for example:
just_the_tooltip: ^0.0.11+2. With this package, you could even use a StatelessWidget.
The result here is more similar to what I suppose you were expecting.
class ClickableLink extends StatelessWidget {
#override
Widget build(BuildContext context) {
return JustTheTooltip(
content: Image.network(
'https://online-go.com/api/v1/games/${widget.link.id}/png',
),
child: SelectableText.rich(
TextSpan(
text: widget.linkText ?? widget.link.id,
style: TextStyle(
color: widget.color ??
(DogempTheme.currentThemeIsLight(context)
? const Color(0xff1158c7)
: Colors.orange.withOpacity(0.85)),
),
recognizer: TapGestureRecognizer()
..onTap = () async => launch(widget.link.completeLink),
),
),
);
}
}
Lastly you could use a Dialog, but the resulting behaviour is a bit different.
Take a look at this code if you want to try:
class _ClickableLinkState extends State<ClickableLink> {
Widget hoverWidget = const SizedBox.shrink();
void _fetchPreview(PointerEvent pointerEvent) {
showDialog(
context: context,
builder: (context) {
return Dialog(
backgroundColor: Colors.transparent,
child: Column(
mainAxisSize: MainAxisSize.min,
children: [
Image.network(
'https://online-go.com/api/v1/games/${widget.link.id}/png'),
const SizedBox(
height: 16.0,
),
TextButton(
onPressed: () async => launch(widget.link.completeLink),
child: const Text('Go to complete link'))
],
),
);
},
);
}
#override
Widget build(BuildContext context) {
return MouseRegion(
onHover: _fetchPreview,
child: Stack(
clipBehavior: Clip.none,
children: [
SelectableText.rich(
TextSpan(
text: widget.linkText ?? widget.link.id,
style: TextStyle(
color: widget.color ??
(DogempTheme.currentThemeIsLight(context)
? const Color(0xff1158c7)
: Colors.orange.withOpacity(0.85)),
),
recognizer: TapGestureRecognizer()
..onTap = () async => launch(widget.link.completeLink),
),
),
],
),
);
}
}
I'm having a weird issue with the Flutter InAppWebView plugin version 4.0.0+4 here https://pub.dev/packages/flutter_inappwebview where I try to load the simple contact us form into the plugin and realize that I can't enter the content into html input text field if I use non English keyboard, in my case I use Vietnamese keyboard. If I switch the keyboard to English one then its working. I double checked the contact us form and made sure its working 100% on Chrome browser outside of the Flutter app using even non English keyboard. I don't use any special code or settings for the plugin, just same as the one mentioned in the pub.dev. I'm using Flutter channel stable v. 1.22.6
This is my code in case you need it:
class WebViewerWidget extends StatefulWidget {
final Map<String, String> metaData;
WebViewerWidget({this.metaData});
#override
_WebViewerWidgetState createState() => _WebViewerWidgetState();
}
class _WebViewerWidgetState extends State<WebViewerWidget> {
#override
void initState() {
super.initState();
}
#override
Widget build(BuildContext context) {
InAppWebViewController _webviewCtrl;
double progressIndicator = 0;
return Scaffold(
backgroundColor: ColorPalette.white,
appBar: PreferredSize(
child: TopNavWidget(
title: widget.metaData['title'] ?? '',
),
preferredSize: Size.fromHeight(50.0),
),
body: Builder(
builder: (BuildContext context) {
return Container(
child: Column(
children: [
Container(
child: progressIndicator < 1
? LinearProgressIndicator(
value: progressIndicator,
backgroundColor: Colors.black12,
valueColor:
AlwaysStoppedAnimation<Color>(Colors.blue),
)
: Container()),
Expanded(
child: InAppWebView(
initialUrl: widget.metaData['url'] ?? 'about:blank',
initialOptions: InAppWebViewGroupOptions(
crossPlatform: InAppWebViewOptions(
debuggingEnabled: true,
javaScriptEnabled: true,
useShouldInterceptAjaxRequest: true,
useShouldInterceptFetchRequest: true,
),
ios: IOSInAppWebViewOptions(),
android: AndroidInAppWebViewOptions(),
),
onWebViewCreated:
(InAppWebViewController webviewController) {
_webviewCtrl = webviewController;
},
onProgressChanged:
(InAppWebViewController controller, int progress) {
setState(() {
progressIndicator = progress / 100;
});
},
),
),
],
),
);
},
));
}
}
Thanks.
Ok, after spending a couple days fixing the issue, I had to give up on this one. Its definitely a bug from the plugin, found someone had the similar issue here https://github.com/pichillilorenzo/flutter_inappwebview/issues/560. I then tried another plugin called WebView https://pub.dev/packages/webview_flutter and it worked perfectly.
This has been fixed with new version 5.0.0 (the latest version now is 5.0.5+3): use useHybridComposition: true Android-specific webview option, all the issues related to the Android keyboard are fixed.
I am learning Flutter currently and was making a personal finance app. I have the option to bookmark my guides and then view them on the bookmark tab. Right now, I am using a list to simply store names of guides and display them as list tiles.
The issue I am having is that whenever the bookmark list is updated WHILE the app is running, the Bookmarks page loads the right info but then when I close and restart the app, it goes back to it's initial state of being empty. How can I fix it so that the app saves bookmarked tabs?
main.dart
List<String> bookmarked = [];
String introInfo = """ <h1>Introduction!</h1>
<p><strong><em>NOTE: The guides are U.S. specific but most information can be applied in most countries outside the U.S.</em></strong></p>
<p>The guides in this app will teach you the basics of personal finance.</p>
<p>Financial knowledge is something that is invaluable but the U.S. education system does not put much emphasis on it. If you are looking to get into personal finance, you're at the right place.</p>""";
void main() {
runApp(MaterialApp(
initialRoute: '/',
routes: {
'/': (context) => MyApp(),
'/finTable': (context) => FinNav(),
'/disclaimer': (context) => Disclaimer(),
'/intro': (context) => GuideStyle(guideName: 'introduction',guideInfo: introInfo, isFav: bookmarked.contains('introduction'),),
'/budget': (context) => GuideStyle(guideName: 'budget',guideInfo: introInfo, isFav: bookmarked.contains('budget'),),
'/bookmark': (context) => Bookmarks(),
},
theme: ThemeData(fontFamily: 'Raleway'),
));
}
/* I have a stateless widget that shows all pages and navigates to one the user selects */
guidestyle.dart
class GuideStyle extends StatelessWidget {
String guideName;
String guideInfo;
Widget previous;
Widget next;
bool isFav;
GuideStyle({this.guideName,this.guideInfo, this.isFav });//this.next, this.previous});
#override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
backgroundColor: Color.fromRGBO(220, 20, 60, 1.0),
title: Text('Introduction'),
centerTitle: true,
elevation: 10.0,
actions: <Widget>[
Padding(
padding: const EdgeInsets.fromLTRB(0.0,2.0,50.0,0.0),
child: MyStatefulWidget(isFav: isFav,name: guideName,),
),
],
),
body: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: <Widget>[
Padding(
padding: const EdgeInsets.all(10.0),
child: RaisedButton(
onPressed: () {
Navigator.pop(context);
},
child: Text('Back'),
textColor: Colors.white,
color: Color.fromRGBO(220, 20, 60, 0.8),
),
),
Expanded(
child: SingleChildScrollView(
child: Padding(
padding: const EdgeInsets.all(8.0),
child: HtmlWidget(
guideInfo,
)
),
),
),
],
));
}
}
class MyStatefulWidget extends StatefulWidget {
bool isFav;
String name;
MyStatefulWidget({Key key, this.isFav, this.name}) : super(key: key);
#override
_MyStatefulWidgetState createState() => _MyStatefulWidgetState();
}
class _MyStatefulWidgetState extends State<MyStatefulWidget> {
Widget build(BuildContext context) {
return Column(
mainAxisSize: MainAxisSize.min,
children: <Widget>[
IconButton(
icon: widget.isFav ? Icon(Icons.bookmark, color: Colors.black) : Icon(Icons.bookmark_border),
onPressed: () {
setState(() {
widget.isFav = !widget.isFav;
if(widget.isFav){
bookmarked.add(widget.name);
bookmarked = bookmarked;
}else{
bookmarked.remove(widget.name);
bookmarked = bookmarked;
}
});
},
),
],
);
}
}
As mentioned, the guidestyle.dart updates the list while the app is running but the list is reset when the app is restarted.
I was looking into using sqflite but it seems overkill so I am unsure about my other options. Any help would be appreciated!
You can use the SharedPreferences package or any other method that is able to persist data between app launches. See this for options to persist data.
Options:
Persist data with SQLite (Though you don't want to use it, it is still an option)
Read and write files
Store key-value data on disk(SharedPreferences) - This is the simplest and will probably suit your needs just fine
If you are using SharedPreferences the setStringList method will suit your needs perfectly.
As a side note, the line bookmarked = bookmarked; is useless.
List<String> bookmarked = []; this always init your data empty
First, You need store package shared_preferences or sqflite or etc..
You can find here => https://pub.dev/
and then check data exist.
After, if(hasData) bookmarked = "loaded data" else bookmarked = [];
Yesterday I spent over ten hours trying to learn a bit of MobX and applying a simple SnackBar if there is an error coming from the API. My question is if the solution I found can be considered good and appropriate or there is a better one to be implemented.
class _LoginPageState extends State<LoginPage> {
final _scaffoldKey = GlobalKey<ScaffoldState>();
final _controller = Modular.get<LoginController>();
#override
Widget build(BuildContext context) {
return Scaffold(
key: _scaffoldKey,
appBar: AppBar(
title: Text(widget.title),
),
body: Observer(
builder: (context) {
if (_controller.token?.error != null) {
WidgetsBinding.instance.addPostFrameCallback((_) {
_scaffoldKey.currentState.showSnackBar(SnackBar(
content: Text(_controller.token?.error),
duration: Duration(seconds: 2),
));
});
}
return Center(
child: PrimaryButton(
onPressed: () => _controller.authenticate(),
text: 'Enviar',
icon: Icons.send,
),
);
},
),
);
}
}
In case you're curious about it, I'm using flutter_modular, hence the Modular.get<>()
I like this approach, that is as long as you make sure your snackbar does NOT cover the content of the page, as you know errors from API's could be complex and well documented, therefore you may come across a situation where the snackbar would cover your content.
I usually would use showDialog instead, as errors should not usually accur. when they do I would push a popup displaying and explaining the situation using the error details.
This is my customized version of popups:
class ButtonsAndAction extends FlatButton{
///Providing a func is "optional", just pass null if you want the defualt action to pop the navigator.
ButtonsAndAction(BuildContext context, String text, Function func ) : super(child: new Text(text, textDirection: Helper.textDirection(),style: TextStyle(color: ConstantValues.mainBackgroundColor)
,), onPressed: func == null ? () {Navigator.of(context).pop();} : func);
}
class Helper{
static TextDirection textDirection() => AppConfig.rtl ? TextDirection.rtl : TextDirection.ltr;
/// Used to push alerts of texts with a set of actions(buttons and actions) if wanted
static Future pushDialog(BuildContext context, String title, String body, {List<ButtonsAndAction> actions, bool dismissable = true}) {
return showDialog(
context: context,
builder: (BuildContext context) {
return new WillPopScope(
onWillPop: () async => dismissable,
child:
new AlertDialog(
shape: new RoundedRectangleBorder(borderRadius: BorderRadius.all(Radius.circular(ConstantValues.roundRadius)),
side: BorderSide(color: ConstantValues.mainBackgroundColor, width: ConstantValues.roundBorderWidthForPopup)),
title: new Container(child: new Text(title, textDirection: textDirection(), style: TextStyle(color: ConstantValues.mainBackgroundColor),), width: double.infinity,),
content: new Container(child: SingleChildScrollView(child:
new Text(body, textDirection: textDirection(), style: TextStyle(color: ConstantValues.mainBackgroundColor))),
width: double.infinity),
actions: actions
));
},
);
}
}
Good luck!