Flutter: How to make a custom animated Drop Down Menu? - flutter

I'm trying to make a custom drop-down menu in Flutter that is just a simple button that on tap shows a scrollable row of products. I've attached some images that better show what I'm referring to. I tried to use an IconButton and an AnimatedContainer but I can't seem to get it working. I was hoping someone would have a better idea on how to do something like this.
Here is my code so far by the way:
class ModelsDropdown extends StatefulWidget {
final List<Phone> phones;
ModelsDropdown({#required this.phones});
#override
_ModelsDropdownState createState() => _ModelsDropdownState();
}
class _ModelsDropdownState extends State<ModelsDropdown> {
bool _droppedDown;
#override
void initState() {
_droppedDown = false;
super.initState();
}
#override
Widget build(BuildContext context) {
return AnimatedContainer(
duration: Duration(milliseconds: 300),
decoration: BoxDecoration(
borderRadius: BorderRadius.circular(100.0),
border: Border.all(width: 2.0)
),
width: 32.0,
height: 32.0,
child: Column(
children: [
IconButton(
padding: const EdgeInsets.only(right: 0.0),
icon: Icon(
_droppedDown ? Icons.expand_more : Icons.expand_less
),
color: Colors.black,
onPressed: () {
setState(() {
_droppedDown = !_droppedDown;
});
}
),
_droppedDown ?
Container(
width: MediaQuery.of(context).size.width,
height: 300.0,
color: Colors.orangeAccent,
) :
SizedBox.shrink()
],
),
);
}
}
With the container at the bottom, it doesn't even work. I get an overflow error. Any help is very much appreciated. Thanks a lot, everyone.

I Think you should Change The Below Container with an AnimatedCrossFade.
Animated Cross Fade Has Two Child First and Second ...
https://api.flutter.dev/flutter/widgets/AnimatedCrossFade-class.html
Dont set Height for Your Child ... its Works Perfectly...
AnimatedCrossFade(
duration: const Duration(seconds: 3),
firstChild: Container(), // When you don't want to show menu..
secondChild: menu,
crossFadeState: _first ? CrossFadeState.showFirst : CrossFadeState.showSecond,
)
with this method you don't need Animated Container at top ... remove that.(return Column)...
with Animated cross Fade You don't need to Know The Height of widget and its for works dynamic Heights
-------------- if You Want to use Your Code and Use Fixed Height ----------------
width: _droppedDown ? YourWidth : 32.0,
height: _droppedDown ? 300 : 32.0,

Related

Flutter - Detect when finger enter in a container

In my interface I have a row of containers like this
.
The idea is that when I pass my finger on these containers, the one under my finger gets bigger (and other changes but that's not the point).
I know how to use GestureDetector and get it bigger when I tap on the container with "onTap". But if you keep your finger down and drag it to another container nothing change. Idealy I'd like to be able to detect when the user pass his finger hover a container while touching the screen.
Appreciate if someone can advise. Thank you in advance!
You can use onVerticalDragUpdate on GestureDetector.
class DraUILien extends StatefulWidget {
const DraUILien({super.key});
#override
State<DraUILien> createState() => _DraUILienState();
}
class _DraUILienState extends State<DraUILien> {
int? activeIndex;
final double containerWidth = 30;
#override
Widget build(BuildContext context) {
return Scaffold(
body: Center(
child: GestureDetector(
onVerticalDragUpdate: (details) {
activeIndex =
details.localPosition.dx ~/ (containerWidth + 16); //16 padding
setState(() {});
},
child: SizedBox(
height: 200,
child: Row(
children: List.generate(
10,
(index) => Padding(
padding: const EdgeInsets.all(8.0),
child: AnimatedContainer(
duration: Duration(milliseconds: 300),
color: index == activeIndex ? Colors.blue : Colors.grey,
width: containerWidth,
height: index == activeIndex ? 200 : 100,
),
),
),
),
),
)),
);
}
}
Play with the logic for more customization. If you need onTap functionality try including onPanDown

Why isn't my Flutter animated checkmark working?

I'm trying to replicate the following animation from dribble.com:
I've gotten the ScaleTransition to work but the SizeTransition does not. What am I doing wrong or what don't I understand?
When I only swap out the SizeTransition with a FadeTransition (and keep the same controllers & animations), the animations both run. When I move the Center widget from being a child of the SizeTransition to the parent the animation runs.
However it is not properly centered.
import 'package:flutter/material.dart';
class AnimatedCheck extends StatefulWidget {
#override
_AnimatedCheckState createState() => _AnimatedCheckState();
}
class _AnimatedCheckState extends State<AnimatedCheck> with TickerProviderStateMixin {
late AnimationController scaleController = AnimationController(duration: const Duration(milliseconds: 800), vsync: this);
late Animation<double> scaleAnimation = CurvedAnimation(parent: scaleController, curve: Curves.elasticOut);
late AnimationController checkController = AnimationController(duration: const Duration(milliseconds: 400), vsync: this);
late Animation<double> checkAnimation = CurvedAnimation(parent: checkController, curve: Curves.linear);
#override
void initState() {
super.initState();
scaleController.addStatusListener((status) {
if (status == AnimationStatus.completed) {
checkController.forward();
}
});
scaleController.forward();
}
#override
void dispose() {
scaleController.dispose();
checkController.dispose();
super.dispose();
}
#override
Widget build(BuildContext context) {
double circleSize = 140;
double iconSize = 108;
return ScaleTransition(
scale: scaleAnimation,
child: Container(
height: circleSize,
width: circleSize,
decoration: BoxDecoration(
color: Colors.green,
shape: BoxShape.circle,
),
child: SizeTransition(
sizeFactor: checkAnimation,
axis: Axis.horizontal,
axisAlignment: -1,
child: Center(
child: Icon(Icons.check, color: Colors.white, size: iconSize)
)
),
),
);
}
}
When you move the Center to the outside, it doesn't center the children of it's children, just puts it's child in the center of the area it occupies. Try setting the Container alignment using alignment: Alignment.center, inside of the Container. Please let me know if it doesn't work and I'll replicate the code to figure out the problem is.
Edit: This is because there is space around the visual check you see, the SizeTransition doesn't know this so it animates from the edge of the Icon which causes the visual bug you see. I'd recommend you use a tool like Rive (rive.app) to accomplish what you want, alternatively, you can use another animation or use some clunky workaround like animating the position while the size transition occurs so that it appears to be centered.
Hi if this is an issue for you, I figured out how both animations will be triggerd.
See code below.
Stack(
children: [
Center(
child: ScaleTransition(
scale: scaleAnimation,
child: Container(
height: circleSize,
width: circleSize,
decoration: BoxDecoration(
color: kApprovedColor,
shape: BoxShape.circle,
),
),
),
),
SizeTransition(
sizeFactor: checkAnimation,
axis: Axis.horizontal,
axisAlignment: -1,
child: Center(
child: Icon(Icons.check, color: Colors.white, size: iconSize),
),
),
],
),
Flutter's documentation has an explanation:
Like most widgets, SizeTransition will conform to the constraints it
is given, so be sure to put it in a context where it can change size.
For instance, if you place it into a Container with a fixed size, then
the SizeTransition will not be able to change size, and will appear to
do nothing.
Thank you. Now I know what I'm doing wrong.

How do I animate a container when the page is initializing;

class _MaintenanceState extends State<Maintenance> {
Function below is supposed to initialise the animation
#override
void initState() {
super.initState();
_animate();
}
var loginWidth = 0.0;
Curve _curve = Curves.fastOutSlowIn;
_animate() {
setState(() {
loginWidth == 0.0 ? loginWidth = 130 : loginWidth = 0.0;
});
}
I tried placing the width of the animated container in the
initState but it still doesn't animate.
AnimatedContainer(
duration: Duration(seconds: 1),
width: loginWidth,
height: 60.0,
decoration: BoxDecoration(
color: Colors.green[600],
borderRadius: BorderRadius.circular(20.0),
),
child: Padding(
padding: const EdgeInsets.all(8.0),
child: Row(
children: [
Text("Add \nProject"),
)),
Spacer(),
Image(
image: AssetImage(
"assets/images/add_icon.png",
),
),
],
),
),
),
Any suggestions would be much appreciated
There are several things you have to consider to animate.
use AnimationController to track the currentstate of animation, to set the duration till which you want to animate and other several properties if you want to add in animation.
if you are using animation you have to use SingleTickerProviderStateMixin or MultiTickerProviderStateMixin mixin in your class.
I am sharing my GitHub project link in which I used a simple animation you can refer to it.

Animate two containers width using Animated container

I have two widgets,
Widget FirstWidget()
{
return Container(
height: 40,
child: new Text('Food Products',style: TextStyle(color: Colors.gray))
decoration: new BoxDecoration(
borderRadius: new BorderRadius.all(new Radius.circular(4.0)),
border: Border.all(width: 2, color:Colors.gray)),
padding: new EdgeInsets.all(12.0),
)
}
Widget SecondWidget()
{
return Container(
height: 40,
child: Row(
crossAxisAlignment: CrossAxisAlignment.end,
mainAxisAlignment: MainAxisAlignment.center,
mainAxisSize: MainAxisSize.min,
children: <Widget>[
Text('Food Products',
TextStyle(color: Colors.blue),
SizedBox(width:8),
Icon(Icons.check,color:Colors.blue ,size: 24,)
],),
decoration: new BoxDecoration(
borderRadius: new BorderRadius.all(new Radius.circular(4.0)),
border: Border.all(width: 2, color:Colors.red)),
padding: new EdgeInsets.all(12.0),
)
}
Now, i need to animate the two widgets based on selection,
class BasicProduct extends StatefulWidget {
final String name;
final bool isSelected;
BasicProduct({Key key, #required this.name, this.isSelected = false});
#override
State<StatefulWidget> createState() => _BasicProduct(name, isSelected);
}
class _BasicProduct extends State<BasicProduct> {
final String name;
bool _isSelected;
_BasicProduct(this.name, this._isSelected);
#override
Widget build(BuildContext context) {
Widget _myAnimatedWidget = _isSelected ? SecondWidget() : FirstWidget();
return new GestureDetector(
onTap: () {
setState(() {
_isSelected = !_isSelected;
_myAnimatedWidget =
_isSelected
? SecondWidget()
: FirstWidget();
});
},
child: AnimatedContainer(
key : _animatedContainerKey
duration: const Duration(seconds: 1),
width: boxWidth
child: _myAnimatedWidget
);
}
}
How to get the width for the two widgets to animate?
Tried Calling WidgetsBinding.instance.addPostFrameCallback((_)=> getSize) in the initState to get the Size
getSize()
{
var boxWidth = _animatedContainerKey.currentContext.findRenderObject().size.width;
}
This Size returning for the SecondWidget only, when firstWidget is changed, the Size returning for SecondWidget only.
Can anyone help how to resolve the issue?
I would take a different approach to this. For one, if you can, you should try using the AnimatedCrossFade widget - it automatically handles both the fade and the size change between two different widgets. If you can make it so that instead of having to specify an exact size in your widgets (always a bad idea) they simply fit to the available space, then this should do what you want. However, without more details of what you're actually trying to accomplish with the animation & size of the widgets, it's a bit difficult to help.
If this doesn't work, then you should consider using an AnimationController and a widget inheriting AnimatedWidget. This way, you have complete control over the build process of your two widgets - you can size, position, and fade them however you want with a stack + opacity or whatever else works best for you.
I would normally write out an example but I think it's a bit beyond the scope of this answer, however you can find out an example of an animated widget here: https://api.flutter.dev/flutter/widgets/AnimatedWidget-class.html. If you do need to go down this route and can't figure out, it would help if you could add more details about what it is you're trying to accomplish instead of what you've attempted to do to accomplish it.

How to animate an image moving across the screen?

I have a fixed size image (of a playing card) and I'd like to write code so users see the image slide from one part of the screen to another (like a card being dealt and moving across the surface). If possible, it would be best to do so in a way that's moderately responsive for different screen sizes.
Most of what I've seen or learned about involves Hero widgets or animation where a widget changes size but stays in the same location. I'm asking about something different.
What you want is the animatedPosition widget, it will move any widget from one point of the screen to another, you can even morph the size of the widget.
https://api.flutter.dev/flutter/widgets/AnimatedPositioned-class.html
AnimatedPositioned(
width: selected ? 200.0 : 50.0,
height: selected ? 50.0 : 200.0,
top: selected ? 50.0 : 150.0,
duration: const Duration(seconds: 2),
curve: Curves.fastOutSlowIn,
child: GestureDetector(
onTap: () {
setState(() {
selected = !selected;
});
},
child: Container(
color: Colors.blue,
child: const Center(child: Text('Tap me')),
),
),
),
You can either use Flutter's built in animations Animation<double>. In Flutter, an Animation object knows nothing about what is onscreen. An Animation is an abstract class that understands its current value and its state (completed or dismissed). One of the more commonly used animation types is Animation<double>.
For example:
// lib/main.dart (AnimatedLogo)
class AnimatedLogo extends AnimatedWidget {
AnimatedLogo({Key key, Animation<double> animation})
: super(key: key, listenable: animation);
Widget build(BuildContext context) {
final animation = listenable as Animation<double>;
return Center(
child: Container(
margin: EdgeInsets.symmetric(vertical: 10),
height: animation.value,
width: animation.value,
child: FlutterLogo(),
),
);
}
}
Or you can import and use one of these packages into your project:
https://pub.dev/packages/simple_animations
https://pub.dev/packages/animator