in this class as an widget which i use animation to show and hide, when i multiple show or hiding by click, sometimes i get this error:
BoxConstraints has a negative minimum width.The offending constraints were: BoxConstraints(w=-0.8, 0.0<=h<=Infinity; NOT NORMALIZED)
this part of code has problem
child: Row( //<--- problem cause on this line of code
children: <Widget>[
...
],
),
My class code:
class FollowingsStoryList extends StatefulWidget {
final List<Stories> _stories;
FollowingsStoryList({#required List<Stories> stories}) : _stories = stories;
#override
_FollowingsStoryListState createState() => _FollowingsStoryListState();
}
class _FollowingsStoryListState extends State<FollowingsStoryList> with TickerProviderStateMixin {
List<Stories> get stories => widget._stories;
bool _isExpanded = false;
AnimationController _barrierController;
#override
void initState() {
super.initState();
_barrierController = AnimationController(duration: const Duration(milliseconds: 400), vsync: this);
}
#override
Widget build(BuildContext context) {
return AnimatedContainer(
curve: _isExpanded ? Curves.elasticOut : Curves.elasticIn,
duration: Duration(milliseconds: 800),
child: Row(
children: <Widget>[
AnimatedContainer(
curve: _isExpanded ? Curves.elasticOut : Curves.elasticIn,
duration: Duration(milliseconds: 800),
width: _isExpanded ? 90 : 1,
color: Colors.white,
padding: EdgeInsets.only(bottom: 65.0),
child: Column(
children: <Widget>[
],
),
),
GestureDetector(
onTap: _toggleExpanded,
child: StoriesShapeBorder(
alignment: Alignment(1, 0.60),
icon: Icons.adjust,
color: Colors.white,
barWidth: 4,
),
),
],
),
);
}
_toggleExpanded() {
setState(() {
_isExpanded = !_isExpanded;
if (_isExpanded) {
_barrierController.forward();
} else {
_barrierController.reverse();
}
});
}
}
Because Curves.elasticOut and Curves.elasticIn are
oscillating curves that grows/shrinks in magnitude while overshooting
its bounds.
When _isExpanded is false, you are expecting the width to go from 90 to 1 however Curves.elasticOut overshoots and becomes much less than 1 (hence a negative width) for a short period of time.
This is what is causing the error imho. Plus for what reason do you have nested AnimatedContainers? Top AnimatedContainer seems unnecessary.
Try getting rid of the top AnimatedContainer and changing all 4 of the Curves to Curves.linear and I think your error will be gone. Actually you can use any Curves which don't overshoot.
If you really need Elastic Curve, then maybe this would work too:
#override
Widget build(BuildContext context) {
return Row(
children: <Widget>[
AnimatedContainer(
curve: _isExpanded ? Curves.elasticOut : Curves.easeInSine,
duration: Duration(milliseconds: 800),
width: _isExpanded ? 90 : 1,
color: Colors.white,
padding: EdgeInsets.only(bottom: 65.0),
child: Column(
children: <Widget>[
],
),
),
GestureDetector(
onTap: _toggleExpanded,
child: StoriesShapeBorder(
alignment: Alignment(1, 0.60),
icon: Icons.adjust,
color: Colors.white,
barWidth: 4,
),
),
],
);
}
Try it and let me know.
For example, you can use AnimatedController, to (oddly enough) control your animation value and set minimum value "0".
import 'dart:math' as math;
double getValue(...) {
// some logic / curves
// ....
return math.max(0, value);
}
Related
I need to divide the range of animation value to a smaller ranges like this simple example:
class TextAnimation extends StatefulWidget {
const TextAnimation({Key? key}) : super(key: key);
#override
State<TextAnimation> createState() => _TextAnimationState();
}
class _TextAnimationState extends State<TextAnimation>
with SingleTickerProviderStateMixin {
late AnimationController anController;
#override
void initState() {
// TODO: implement initState
super.initState();
anController = AnimationController(
vsync: this,
duration: Duration(seconds: 5),
reverseDuration: Duration(seconds: 5));
}
#override
void dispose() {
// TODO: implement dispose
super.dispose();
anController.dispose();
}
#override
Widget build(BuildContext context) {
return Scaffold(
body: AnimatedBuilder(
animation: anController,
builder: (context, child) {
return Column(
mainAxisAlignment: MainAxisAlignment.spaceEvenly,
crossAxisAlignment: CrossAxisAlignment.center,
children: [
SizedBox(
width: double.infinity,
),
Container(
width: 300 *
Tween(begin: 0.0, end: 1.0)
.animate(CurvedAnimation(
parent: anController,
curve:
Curves.fastOutSlowIn))
.value,
height: 100,
color: Colors.red,
),
Container(
width: 300 *
(Tween(begin: 0.0, end: 1.0) .animate(CurvedAnimation(
parent: anController, curve: Curves.fastOutSlowIn))
.value.clamp(0.5, 1)),
height: 100,
color: Colors.blue),
InkWell(
onTap: () {
anController.forward();
},
child: Text(
'Start',
style: TextStyle(
color: Colors.black,
fontSize: 20,
),
))
],
);
}),
);
}
}
the second container need to start the animation from the half of the range to the end...
the problem is, the curve effect is lost in this range ,I need to give this range (0.5-1.0) its own curve ,
start fast in 0.5 and end with slow motion.
is there a way to do it without using another animation controller to drive this range?
note : without AnimatedContainer the code above is just a simple example for the problem.
expected behavior
i tried this code but it give me completely difference result from left side and strange animated
double val = 0;
#override
Widget build(BuildContext context) {
return Stack(
children: [
Container(
height: 400,
color: Colors.red,
),
TweenAnimationBuilder(
duration: const Duration(milliseconds: 150),
tween: Tween<double>(begin: 0 , end: val) ,
builder: (BuildContext context, double? value, Widget? child) {
return (
Transform(
alignment: Alignment.center,
transform: Matrix4.identity()
..setEntry(3, 2, 0.001)
..setEntry(0, 3, 200 * value!)
..rotateY((pi/6)*value),
child: DefaultTabController(
length: 5,
child: Scaffold(
body: Center(
child: Container(
color: Colors.yellowAccent,
child: IconButton(
onPressed: () {
setState(() {
setState(() {
val == 0 ? val = 1 : val = 0 ;
});
});
},
icon: Text('tab me'),
),
),
)
)
)
)
);
}
)
],
);
}
also i need only the red Container the one who animated from down to up , but i don't know why the main screen is also animate .. i need it never animated ..
any suggestion most welcome guys .. thanks
Instead of custom animation, you can use AnimatedContainer().
Create a boolean like selected which will tell the animated container when to close and when to open the container. And using setState you can toggle the animation.
Align your AnimatedContainer() with Align() and give alignment: Alignment.bottomCenter. And give height:0 is not selected and when selected give height the half of screen using MediaQuery.of(context)
class MyStatefulWidget extends StatefulWidget {
const MyStatefulWidget({Key? key}) : super(key: key);
#override
State<MyStatefulWidget> createState() => _MyStatefulWidgetState();
}
class _MyStatefulWidgetState extends State<MyStatefulWidget> {
bool selected = false;
#override
Widget build(BuildContext context) {
return Column(children: [
ElevatedButton(
onPressed: () {
setState(() {
selected = !selected;
});
},
child: Text("Tap Me!!"),
),
Spacer(),
GestureDetector(
onTap: () {
setState(() {
selected = !selected;
});
},
child: Align(
alignment: Alignment.bottomCenter,
child: AnimatedContainer(
width: double.infinity,
height: selected ? MediaQuery.of(context).size.height / 2 : 0,
color: selected ? Colors.red : Colors.blue,
alignment:
selected ? Alignment.center : AlignmentDirectional.topCenter,
duration: const Duration(seconds: 2),
curve: Curves.fastOutSlowIn,
child: const FlutterLogo(size: 75),
),
),
)
]);
}
}
You can try the same code in dartpad here
In my app, I have a container that I want to start rotating with a slow-curve on a click, then keep rotation, and then the next click will make it stop with a slow-curve.
How do I make a curve-animation in flutter?
Somthing like this:
https://miro.medium.com/max/960/1*ZLekwO4QthfAWlBgM-9vpA.gif
Make an animation controller and an animation.
AnimationController _animController;
Animation<double> _animation;
#override
void initState() {
super.initState();
_animController = AnimationController(
duration: Duration(seconds: 2),
vsync: this,
);
_animation =
Tween<double>(begin: 0, end: 2 * math.pi).animate(_animController)
..addListener(() {
setState(() {});
});
}
#override
void dispose() {
_animController.dispose();
super.dispose();
}
Define a boolean variable. This variable indicates whether the object is animating or not.
var _animating = false;
End the animation on Stop and repeat the animation on Start.
Scaffold(
backgroundColor: Colors.blueGrey,
body: Center(
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: <Widget>[
Transform.rotate(
angle: _animation.value,
child: Container(
color: Colors.green,
height: 80,
width: 80,
padding: EdgeInsets.all(30),
),
),
Padding(
padding: const EdgeInsets.only(top: 20.0),
child: RaisedButton(
color: Colors.white,
child: Text(_animating ? "Stop" : "Start"),
onPressed: () {
if (_animating) {
_animController.animateTo(1,
duration: Duration(seconds: 3), curve: Curves.ease);
} else {
_animController.repeat();
}
setState(() => _animating = !_animating);
},
),
),
],
),
),
)
Result:
I have a simple Card with an "Expand" button that toggles visibility of an _expandedCardBody method that returns a Column.
bool expandedCard = false;
Widget _expandedCardBody() {
return Column(
children: <Widget>[
Row(...),
Row(...),
Row(...),
]);
}
#override
Widget build(BuildContext context) {
return Scaffold(
body: Container(
padding: EdgeInsets.all(10),
child: ListView(
children: <Widget>[
Text("Setup my budget", style: kTitleStyle),
Card(
color: Colors.white,
child: Column(
children: <Widget>[
ListTile(
leading: SvgPicture.asset(
"assets/images/icon-eating-out.svg",
width: 65),
title: Text('Eating out', style: kNormalStyle),
subtitle:
Text('AED 1,245 this month', style: kSmallStyle),
trailing: SizedBox(
width: 75,
child: TextFormField(
keyboardType: TextInputType.number,
decoration: InputDecoration(hintText: "AED.."),
style: kNormalStyle,
),
),
),
expandedCard ? _expandedCardBody() : SizedBox(),
Divider(),
Container(
alignment: Alignment.topLeft,
child: FlatButton(
child: Text(expandedCard ? 'COLLAPSE' : 'EXPAND'),
onPressed: () {
setState(() {
expandedCard = !expandedCard;
});
},
),
),
],
),
)
],
)));
}
It works, but this is what it looks like:
Instead of simply showing/hiding _expandedCardBody, I'd like to animate it's height.
I've tried using AnimatedContainer like so, but it requires knowing the height of the _expandedCardBody (which I do not).
AnimatedContainer(
child: _expandedCardBody(),
height: expandedCard ? 200 : 0, // 🤔 don't know what the height is.
duration: Duration(milliseconds: 250),
),
How can I animate the height of the Card body?
Use SingleTickerProvider with your state class
for eg like this :
class YourClass extends State<YourClass>
with SingleTickerProviderStateMixin {
AnimationController _animationController;
Animation<double> animation;
var isDetailOpened = false;
#override
void initState() {
_animationController =
AnimationController(duration: Duration(milliseconds: 200), vsync: this);
_animationController.addListener(() {
setState(() {});
});
seeMoreEnabled = false;
animation =
CurvedAnimation(parent: _animationController, curve: Curves.easeInOut);
super.initState();
}
#override
dispose() {
_animationController.dispose();
super.dispose();
}
then Replace your Text (Expand or collapse) with flatButton or CupertinoButton
after doing that onPress or onClick method
onPressed: () {
if (isDetailOpened) {
_animationController.reverse();
} else {
_animationController.forward();
}
isDetailOpened = !isDetailOpened;
},
after that Put your widgets in SizeTransition
SizeTransition(
axisAlignment: 1.0,
sizeFactor: animation,
(your other widgets)
This is an example for an ideal purpose (in simply way)
I have been struggling with Flutter with something that in my head seems to simple. I want a container that spins when pressing a button and this should be animated. I have a container in the center of my screen. I have 2 buttons, 1 with "+" and with a "-". When I press the "+" I want the container to rotate 180 degrees in a clockwise rotation, If I press the "+" again I want it to perform another clockwise rotation of 180 degrees. When pressing "-" I want it to spin counter clockwise for 180 degrees.
Currently I have it build so that it will rotate container however the axis is on the top left point of the container instead of the center. I have tried to tackle this but nothing seems to change this behaviour and I found this issue but it's since been closed
https://github.com/flutter/flutter/issues/27419
It's mind boggling to me that I cannot perform such a simple operation and was wondering if someone knows where I'm going wrong.
Some code:
import 'package:flutter/material.dart';
import 'dart:math';
void main() => runApp(Spinner());
class Spinner extends StatefulWidget {
#override
_SpinnerState createState() => _SpinnerState();
}
class _SpinnerState extends State<Spinner> {
double _angle = 0;
#override
Widget build(BuildContext context) {
return MaterialApp(
home: Scaffold(
body: Center(
child: AnimatedContainer(
alignment: FractionalOffset.center,
height: 200,
width: 200,
transform: Matrix4.rotationZ(_angle),
decoration: BoxDecoration(
color: Colors.blue
),
duration: Duration(seconds: 2),
),
),
floatingActionButton: Column(
mainAxisAlignment: MainAxisAlignment.center,
crossAxisAlignment: CrossAxisAlignment.end,
children: <Widget>[
FloatingActionButton(onPressed: () {
setState(() {
_angle += 180 * pi / 180;
});
},
child: const Icon(Icons.add),
),
Container(
height: 20,
),
FloatingActionButton(onPressed: () {
setState(() {
_angle -= 180 * pi / 180;
});
},
child: const Icon(Icons.remove),
)
],
)
)
);
}
}
Edit:
I did find this post however when using it the container instantly snaps to the new position and I want this to be animted.
How can I rotate a Container widget in 2D around a specified anchor point?
I found a solution to tackle this problem, instead of using the animatedContained I decided to use an Animation Controller.
my SpinnerState class now looks like this:
class _SpinnerState extends State<RadialMenu> with SingleTickerProviderStateMixin {
AnimationController controller;
#override
void initState() {
super.initState();
controller = AnimationController(duration: Duration(milliseconds: 900), vsync: this);
}
#override
Widget build(BuildContext context) {
return RadialAnimation(controller: controller);
}
}
I then created a stateless widget which contains the animations etc.
class SpinnerAnimation extends StatelessWidget {
SpinnerAnimation({Key key, this.controller})
: rotation = Tween<double>(
begin: 0.0,
end: 180.0,
).animate(
CurvedAnimation(
parent: controller,
curve: Interval(
0.0,
0.75,
curve: Curves.linear,
),
),
),
super(key: key);
final AnimationController controller;
final Animation<double> rotation;
build(context) {
return AnimatedBuilder(
animation: controller,
builder: (context, builder) {
return Column(
mainAxisAlignment: MainAxisAlignment.center,
children: [
FloatingActionButton(
child: Icon(Icons.add),
onPressed: _open,
backgroundColor: Colors.green),
FloatingActionButton(
child: Icon(Icons.remove),
onPressed: _close,
backgroundColor: Colors.red),
Transform.rotate(
angle: radians(rotation.value),
child: Column(
children: [
Container(
width: 200,
height: 200,
decoration: new BoxDecoration(
image: DecorationImage(
image: AssetImage('images/CRLogo.png'))),
)
],
))
],
);
});
}
_open() {
controller.forward(from: 0);
}
_close() {
controller.reverse(from: 1);
}
}
This has at least solved this problem for me and I hope it helps someone else in the future.