I'm trying to make a radio app, using the assets_audio_player library, it does fetch the radio stream from the link, and all is working, but when I try to make the Play/Pause button change icon accordingly, it just doesn't work, I tried two approaches:
1- Using the library builder:
assetsAudioPlayer.builderCurrent(
builder: (context, isPlaying){
return PlayerBuilder.isPlaying(
player: assetsAudioPlayer,
builder: (context, isPlaying) {
return RawMaterialButton(
constraints: BoxConstraints.expand(width: 70, height: 70),
fillColor: Constants.WHITE,
shape: CircleBorder(),
child: RadiantGradientMask(
child: Icon(isPlaying ? Icons.pause : Icons.play_arrow ,
size: 42,
color: Constants.WHITE,
),
gradient: gradient,
),
onPressed: (){
assetsAudioPlayer.playOrPause();
}
);
});
}
),
2- Using a normal button, and a bool value and setState:
RawMaterialButton(
constraints: BoxConstraints.expand(width: 70, height: 70),
fillColor: Constants.WHITE,
shape: CircleBorder(),
child: RadiantGradientMask(
child: Icon(isPlaying ? Icons.pause : Icons.play_arrow ,
size: 42,
color: Constants.WHITE,
),
gradient: gradient,
),
onPressed: (){
assetsAudioPlayer.playOrPause();
setState(() {
isPlaying = assetsAudioPlayer.isPlaying.value;
});
},
In the 1st method, the library starts streaming, but the icon doesn't change at all (pause/play), in the 2nd method, it does change, but after pressing the button several times, it glitches, and the 'play' becomes 'pause', and 'pause' becomes 'play' (reversed).
Any idea about how to get this to work properly?
The method call assetsAudioPlayer.playOrPause(); is Async, and the setState({}); is Sync.
You are checking if the status changed synchronously, but changing it async, you need to await for the the playOrPause() method.
Related
I would like an image with shimmer effect applied while loading, similar to the fancy_shimmer_image package, to also receive ripple effect from an InkWell. Is there a way to do this?
The only way i found to do that is this "trick". Use a Stack for set a widget on top of your image. And then the inkwell effect works:
Stack(
children: [
// image with shimmer effect
FancyShimmerImage(
imageUrl:
'https://cdn.pixabay.com/photo/2014/06/03/19/38/board-361516_960_720.jpg',
shimmerBaseColor: Colors.amberAccent,
shimmerHighlightColor: Colors.redAccent,
shimmerBackColor: Colors.greenAccent,
),
//use this widget on top to do the effect
Positioned.fill(
child: Material(
color: Colors.transparent,
child: InkWell(
// splashColor: Colors.lightGreenAccent,
onTap: () {
print("on tap");
},
),
),
),
],
),
use the shimmer package and cached network image
and use it like that
CachedNetworkImage(
imageUrl: prov
.cart
.cartItems[index]
.image,
placeholder:
(context, url) =>
Shimmer.fromColors(
baseColor: Colors.grey.shade300,
highlightColor: Colors.white,
enabled: true, child: Image.asset(
'assets/images/product_placeholder.png',
),
)
),
Is there a way to close the flutter speedDial when tap on a label widget?. I did not use SpeedDial's child property, but it has that feature. Currently when I tap on a label widget it stays until I manually close the widget. Or even a way to change the child property of SpeedDial widget would be enough, while I want a custom shape like in the picture.
Navigator.pop() did not work
SpeedDial(
buttonSize: const Size(45, 45),
animatedIcon: AnimatedIcons.menu_close,
children: [
SpeedDialChild(
labelWidget: GestureDetector(
onTap: () async {
Feedback.forTap(context);
await _crudStorage.deleteAllTask();
},
child: Container(
height: 50.0,
decoration: BoxDecoration(
color:
Theme.of(context).cardColor,
border: Border.all(width: 2.0),
borderRadius:
BorderRadius.circular(30.0),
),
child: Row(
children: [
const Padding(
padding: EdgeInsets.only(
left: 12.0, right: 8.0),
child:
Text('Clear all tasks'),
),
Padding(
padding:
const EdgeInsets.only(
right: 8.0),
child: SvgPicture.asset(
'assets/svg/all.svg',
),
),
],
),
),
),
),
],
)
don't use async & await on tap use separate function to Call
https://tutorialmeta.com/question/how-to-call-async-function-with-ontap-flutter
refer the above link
It was so simple. Also the development team, provided how to implement this on their official extension page on pub.dev go to pub. I will mention the same documentation for your reference:
first you need to create a value notifier:
ValueNotifier<bool> isDialOpen = ValueNotifier(false);
Then set openCloseDial to isDialOpen in your SpeedDial:
SpeedDial(
...
openCloseDial: isDialOpen,
...
)
Now you can change the state of dial open:
isDialOpen.value = false;
Example: Just set the value to false inside the onTap callback :)
onTap: () async {
Feedback.forTap(context);
await _crudStorage.deleteAllTask();
isDialOpen.value = false;
},
use closeManually: false in SpeedDial Contructor. The widget will close automatically on select any function.
I tried it by wrapping the
return ClipRRect(
borderRadius: BorderRadius.circular(30.0),
child: Chewie(
controller: _chewieController,
)
with
return Stack(
children: [
ClipRRect(
borderRadius: BorderRadius.circular(30.0),
child: Chewie(
controller: _chewieController,
),
),
Positioned.fill(child: GestureDetector(
onDoubleTap: (){
print('its double tapped ');
},
child: Container(
color: Colors.transparent,
height: double.infinity,
width: double.infinity,
),))
],
);
Now I am able to doubleTap, but with a single tap, controllers don't appear, Is there any way I can achieve both things.
With a doubleTap, I can call the like function and with onTap to show the controllers.
the package used above chewie 0.12.0
I don't know how Chewie is built but if there is GestureDetector you could change it to something like this:
GestureDetector(
behavior: HitTestBehavior.opaque,
onTap: () => setState(() => // show_controls,),
onDoubleTap:() => setState(() => // like behaviour,),
child: VideoWidget // or whatever is there
}
So you can have both listeners on one widget. I think that way onTap will be with some slight delay to check if it is not onDoubleTap. That way you don't have to put overlay on top of your widget.
If for some reason you want to have this overlay... then the interesting attribute here is behaviour as it decides if elements behind will receive events.
https://api.flutter.dev/flutter/rendering/PlatformViewHitTestBehavior-class.html
Update
Try changing into
GestureDetector(
behavior: HitTestBehavior.translucent,
onDoubleTap: (){
print('its double tapped ');
}
OR
In the github project, this line:
https://github.com/flutter/plugins/blob/master/packages/video_player/video_player/example/lib/main.dart#L290
It seems that the controls only show when video is in pause state. Its about this value controller.value.isPlaying.
Why not to control it in your own GestureDetector ?
GestureDetector(
behavior: HitTestBehavior.opaque,
onTap: () => setState(() => _controller.pause() ,), //changing here
onDoubleTap:() => setState(() => // like behaviour,),
child: VideoWidget // or whatever is there
}
Anyway, hope it you will find answer
return GestureDetector(
behavior: HitTestBehavior.opaque,
onTapDown: (_) {
setState(() {
isMute = !isMute;
_chewieController.setVolume(isMute ? 0 : 1);
});
},
onDoubleTap: (){
print('liked');
},
child: ClipRRect(
borderRadius: BorderRadius.circular(30.0),
child: Chewie(
controller: _chewieController,
),
),
);
on simple onTap on video, it shows the controller, onTap here is not getting override hence
onTapDown is used as an alternative to onTap to mute the video.
In my application I use inkwell/flat button/ink response/Gesture Detector/RaisedButton, etc for my tap component. The application already in release Alpha channel (Android) and Test Flight (IOS). However the user is not satisfied with the result, because all the button is less responsive/sensitive compare to native button component (Android/iOS). Is there any alternative or another way in Flutter to create tapable widget or button that have responsiveness similar with native application?
Example code:
InkWell(
onTap: () => Navigator.of(context).pushNamed(
Routes.MEMBERSHIP,
arguments: membershipPlanModel),
child: Card(
margin: EdgeInsets.only(
right: 16,
),
shape: RoundedRectangleBorder(
borderRadius: BorderRadius.all(Radius.circular(10))),
child: Container(...
Another code:
FlatButton(
onPressed: () async {
_cancelTransaction();
Navigator.of(context).pop(true);
Navigator.of(context).pop(true);
},
child: Text(
Labels.YES,
style: Fonts.GHOST,
),
),
Can I access the data in Bloc streams from outside a build method?
For example, in the build method, I am able to access the data using snapShot.data. Like this:
return StreamBuilder(
//initialData: Colors.blue,
stream: colorBloc.colorStream,
builder: (BuildContext context, snapShot) => Container(
width: menuButtonSize,
height: menuButtonSize,
decoration: BoxDecoration(
shape: BoxShape.circle,
color: snapShot.data, //This returns the correct colour previously stored in this stream.
boxShadow: [
BoxShadow(
color: Colors.black,
blurRadius: 15,
),
],
),
),
);
But for troubleshooting another Bloc instance that I am trying to get working, I would like to be able to print out the current value of snapShot.data somehow, so I can see what it is doing and if it is updating properly, because currently it isn't working.
Current snippet of non-working Bloc:
Widget customTheme() {
return StreamBuilder(
initialData: true,
stream: customToggleBloc.customToggleStream,
builder: (BuildContext context, snapShot) => GestureDetector(
onTap: (){
snapShot.data == true ? widget?._callback('custom') : widget?._callback('clearcustom'); //Section A
},
child: Container(
width: menuButtonSize + 8,
height: menuButtonSize + 8,
decoration: BoxDecoration(
shape: BoxShape.circle,
color: AppState.brownTheme,
boxShadow: [
BoxShadow(
color: Colors.black,
blurRadius: 25,
),
],
),
child: new IconTheme(
data: new IconThemeData(
color: Colors.white,
size: 35,
),
child: snapShot.data == true ? new Icon(Icons.add_photo_alternate) : new Icon(Icons.cancel), // Section B
),
),
),
);
}
So what I would like it to do is have a button that is in one of two states at all times. 1) It displays Icons.add_photo_alternate and allows you to pick an image from the gallery; 2) It displays Icons.cancel and removes the previously selected image. Section A handles the onPress event options, and Section B handles the displayed icon.
What I actually get is Icons.add_photo_alternate at all times, and on press actually triggers BOTH of the alternative code blocks.
So I would really like to be able to access this data to see where I might be going wrong!
I am trying variations of things similar to:
print(customToggleBloc.customToggleSink.toString());
Which returns:
Instance of '_StreamSinkWrapper<bool>'
And not the value inside. Is it even possible to access this information?
I think this behavior is caused by your stream not returning any data, and thus relying on the initialData you provide to build its children.To be sure, you can remove the initialDate: true from your StreamBuilder and wrap the builder body with if-else to check for data existence: if data is available your GestureDetector will be built, if not a CircularProgressIndicator will be displayed in the middle. This way you will know what is going on with your stream:
Widget customTheme() {
return StreamBuilder(
//initialData: true,
stream: customToggleBloc.customToggleStream,
builder: (BuildContext context, snapShot) {
if(snapShot.hasData){
print(snapShot.data.toString());
return GestureDetector(
onTap: (){
widget?._callback(snapShot.data == true ? 'custom' : 'clearcustom') ; //Section A
},
child: Container(
width: menuButtonSize + 8,
height: menuButtonSize + 8,
decoration: BoxDecoration(
shape: BoxShape.circle,
color: AppState.brownTheme,
boxShadow: [
BoxShadow(
color: Colors.black,
blurRadius: 25,
),
],
),
child: new IconTheme(
data: new IconThemeData(
color: Colors.white,
size: 35,
),
child: Icon(snapShot.data == true ? Icons.add_photo_alternate : Icons.cancel), // Section B
),
),
);
}else{
return Center(
child: CircularProgressIndicator(),
);
}
}
);
}