How to start a Lottie Animation on tap in Flutter? - flutter

I want to use a Lottie Animation as a micro interaction in my Flutter App. I want this animation to play when the user taps on it. After reading the documentation of the Lottie Package I know how to add the Animation and how to display it. But I don't understand yet how I can start the animation with a Gesture Detectors onTap property and play it only once.
I tried this by using the forward() method of the Animation Controller but it seems not to work:
GestureDetector(
onTap: () {
_controller.forward();
},
child: Center(
child: Lottie.asset('assets/test.json', repeat: false, controller: _controller),
),
),

Would this work?
GestureDetector(
onTap: () {
_controller.forward();
},
child: Center(
child: Lottie.asset('assets/test.json',
repeat: false,
controller: _controller,
onLoaded: (composition) {
_controller
..duration = composition.duration;
},
),
),
)

Related

Flutter Button inside a Wheel ListView Widget

I am using a ListWheelScrollView more specific a clickable one.
(clickable_list_wheel_view)
I am trying to have a button inside my itemWidget but I can not click it. This is my list:
return ClickableListWheelScrollView(
scrollController: _scrollController,
itemHeight: _itemHeight,
itemCount: months.length,
scrollOnTap: true,
onItemTapCallback: (index) {
print(index)
},
child: ListWheelScrollView.useDelegate(
controller: _scrollController,
itemExtent: _itemHeight,
physics: FixedExtentScrollPhysics(),
diameterRatio: 3,
squeeze: 0.95,
onSelectedItemChanged: (index) {
// print(index);
},
childDelegate: ListWheelChildBuilderDelegate(
builder: (context, index) => MonthCardWidget(
month: months[index],
),
childCount: months.length,
),
),
);
Inside my MonthCardWidget I have this simple button:
Container(
height: 45,
width: 45,
color: Colors.transparent,
child: IconButton(
splashColor: Colors.transparent,
highlightColor: Colors.transparent,
onPressed: () {
print('flip');
},
icon: SvgPicture.asset('assets/images/flip.svg',
height: 20, width: 20),
),
),
Everything is getting displayed just fine but onPressed is never called on my button. I guess the GestureDetector is overriding the button? Is there any workaround for this? I couldn't find anything on this.
The GestureDetector is simply on top of the listView:
#override
Widget build(BuildContext context) {
if (_listHeight == .0) {
return MeasureSize(
child: Container(
child: widget.child,
),
onChange: (value) {
setState(() {
_listHeight = value.height;
});
},
);
}
return GestureDetector(
onTap: _onTap,
onTapUp: (tapUpDetails) {
_tapUpDetails = tapUpDetails?.localPosition;
},
child: widget.child,
);
}
The click will never reach your GestureDetector, you need to specify the click in the ClickableListWheelScrollView here:
onItemTapCallback: (index) {
print(index)
},
If the print you have there isn't printing, then get with the ClickableListWheelScrollView plugin publisher. You'll need logic to reach the changes you want with the button based on the index number passed from the ClickableListWheelScrollView I think....so a map of setState functions might do it.
Think of the button just being a view, and you're just running the function logic in parallel to each button based on its position in the list. The button is buried under the list, you need a layer above the ListWheel, which is the Clickable plugin, but obviously it becomes disconnected from the button, so you need to create logic to link them based on the index value, which is the position of the object in the list.
What I typically do:
onSelectedItemChanged: (index) {
// print(index);
},
This Will return the index position of the item in your list. Use this to change a variable.
Wrap your scroller in a GestureDetector.
In the Gesture Detector, write or call a function that takes your index position variable and performs your function as you desire.
If you need to control where in your list position the scroll begins, or remember its position when you rebuild etc, use a FixedExtentScrollController with an initialItem.
Everything slaves off the index of the onSelectedItemChanged in this way, and you use the FixedExtentScrollController to set a starting position at build. Remember that the list begins at 0.

Pick Value onTap in CupertinoPicker and CupertinoDatePicker

onSelectedItemChanged is working perfectly in CupertinoPicker/CupertinoDatePicker
But I also want to pick value when user clicked the value.
Currently, user must scroll in order to pick the value, and as far as I know CupertinoPicker/CupertinoDatePicker doesn't have onTap, onPressed functions
How can I solve this issue
Unfortunately, the gesture detection inside CupertinoPicker/CupertinoDatePicker is not supported for now. When you trace the code inside CupertinoPicker, it leads to use ListWheelScrollView at the end and it does not respond to the tap event.
Discussion thread on GitHub:
Tapping items is not working in CupertinoPicker
ListWheelScrollView children do not respond on gesture (onTap) events
There is a workaround solution by using package clickable_list_wheel_view (fixed height for the child widget, mentioned here)
I found a workaround by using flutter_portal.
final ScrollController scrollController = FixedExtentScrollController();
#override
Widget build(BuildContext context) {
return Container(
height: 100,
child: Portal(
child: CupertinoPicker.builder(
scrollController: scrollController,
childCount: itemCount,
itemBuilder: (context, index) {
return PortalTarget(
anchor: Aligned(
follower: Alignment.center,
target: Alignment.center,
),
portalFollower: GestureDetector(
behavior: HitTestBehavior.translucent,
onTap: () {
scrollController.animateToItem(index,
duration: Duration(milliseconds: 250),
curve: Curves.decelerate);
},
),
child: Text(index.toString()),
);
},
),
)
);
}
GestureDetector(
behavior: HitTestBehavior.translucent,
onTap: () => // write what you want to do,
child: CupertinoPicker(),
),

How to add a double tap gesture to video player without loosing the focus of controls?

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.

How to animate widgets automatically in Flutter?

I have a Future which returns a List and using FutureBuilder and inside it a ListView.builder I show data that I got from Future.
But the problem is, whenever Future returns data my list of widgets just pops directly into the screen. I want to give it a animation (fade or curve) which I am not supposed to trigger manually, instead I want that when I got the data from Future my list to pop to screen not direcly but with some latency/fade/curve or any other animation.
How can I achieve that?
You can use a opacity animation, here you can see an example.
Then you can set
bool _visible = true; //Put this inside your initState
AnimatedOpacity(
opacity: _visible ? 1.0 : 0.0,
duration: Duration(milliseconds: 500),
child:ListView.builder(
itemCount: count,
padding: EdgeInsets.all(10.0),
itemBuilder: (BuildContext context, int position) {
return Card(
color: Colors.white,
elevation: 2.0,
child: ListTile(
title: Text(this.list[position].name),
subtitle: Text(this.list[position].type),
trailing: GestureDetector(
child: Icon(Icons.delete, color: Colors.grey,),
onTap: () {
_delete(context, list[position]);
},
),
onTap: () {
debugPrint("ListTile Tapped");
navigateToDetail(this.list[position]);
},
),
);
},
)
),

Using a textfield as a button

I'm trying to use a textfield as a button so when someone taps it, it takes him to a different page.
I've tried disabling the textfield but then I cannot use the onTap property. But if the textfield is not disabled, when I click it, it gets editing focus and this is something I don't want.
Can anyone help what's the easiest way this could be done?
Thanks
TextField(
readOnly: true,
onTap: () {
// Do something
},
);
You can use a Flatbutton instead. Just put this in your code:
FlatButton(
onPressed: () {
/*...*/
},
child: Text(
"Clickable text!",
),
)
Or else use a GestureDetector:
GestureDetector(
OnTap: () {...},
child: TextField(
// Text...
),
);
you can wrap it with an InkWell, see below:
InkWell(
onTap: () {
},
child: Container(
padding: EdgeInsets.all(4.0),
child: Text('Click me'),
),
);
you can try the following.
GestureDetector(
onTap: () {
// redirect // nav to different screen
},
child: TextField(
enabled: false
),
)
References
TextField
GestureDetector