How to pass animation value to a child widget - flutter

I have a widget that defines an animation. This animation progresses from 0.0 to 1.0. This widget also has a child widget, whose opacity I'd like to control in sync with the animation progress. However, I don't see how I can have the child widget's state "track" the state of the parent's animation progress. Whatever I give from the parent to the child ends up being final and, thus, immutable.
I tried passing the Animation directly, but then the app crashes with error "The getter value was called on null".
Edit: with code. My problem is that the MenuItem class can't be made aware of the states of the Animation values.
import 'package:flutter/material.dart';
class MenuItem extends StatefulWidget {
final Function() onPressed;
final String tooltip;
final String helper;
final IconData icon;
MenuItem({this.onPressed, this.tooltip, this.helper, this.icon});
#override
_MenuItemState createState() => _MenuItemState();
}
class _MenuItemState extends State<MenuItem> {
bool isOpened = true;
double _elevateButtonValue = 0.0;
#override
Widget build(BuildContext context) {
return Container(
child: Row(
mainAxisAlignment: MainAxisAlignment.end,
children: [
AnimatedOpacity(
opacity: isOpened ? 1.0 : 0.0,
duration: Duration(milliseconds: 500),
child: new Container(
child: Text(widget.helper),
decoration: new BoxDecoration (
borderRadius: new BorderRadius.all(const Radius.circular(8.0)),
color: Colors.white,
boxShadow: [new BoxShadow(
color: Colors.black.withOpacity(0.3),
offset: Offset(0.0, 6.0),
blurRadius: 16.0,
),
],
),
padding: new EdgeInsets.fromLTRB(8.0, 8.0, 8.0, 8.0),
),
),
SizedBox(width: 12.0),
FloatingActionButton(
onPressed: () {},
tooltip: widget.tooltip,
child: Icon(widget.icon),
backgroundColor: Colors.deepOrange,
elevation: _elevateButtonValue,
),
],
),
);
}
}
class MenuFabs extends StatefulWidget {
#override
_MenuFabsState createState() => _MenuFabsState();
}
class _MenuFabsState extends State<MenuFabs>
with SingleTickerProviderStateMixin {
bool isOpened = false;
AnimationController _animationController;
Animation<Color> _buttonColor;
Animation<double> _animateIcon;
Animation<double> _translateButton;
Animation<double> _elevateButton;
Curve _curve = Curves.easeOut;
double _fabHeight = 56.0;
#override
initState() {
_animationController =
AnimationController(vsync: this, duration: Duration(milliseconds: 500))
..addListener(() {
setState(() {});
});
_animateIcon =
Tween<double>(begin: 0.0, end: 1.0).animate(_animationController);
_buttonColor = ColorTween(
begin: Colors.deepOrange,
end: Colors.black45,
).animate(CurvedAnimation(
parent: _animationController,
curve: Interval(
0.00,
1.00,
curve: Curves.linear,
),
));
_translateButton = Tween<double>(
begin: _fabHeight,
end: -14.0,
).animate(CurvedAnimation(
parent: _animationController,
curve: Interval(
0.0,
0.75,
curve: _curve,
),
));
_elevateButton = Tween<double>(
begin: 0.0,
end: 6.0,
).animate(CurvedAnimation(
parent: _animationController,
curve: Interval(
0.25,
1.0,
curve: _curve,
),
));
super.initState();
}
#override
dispose() {
_animationController.dispose();
super.dispose();
}
animate() {
if (!isOpened) {
_animationController.forward();
} else {
_animationController.reverse();
}
isOpened = !isOpened;
}
Widget goal() {
return Container(
child: new MenuItem(
onPressed: () {},
tooltip: 'Geolocate',
helper: 'Geolocate',
icon: Icons.radio_button_checked,
),
);
}
Widget invite() {
return Container(
child: new MenuItem(
onPressed: () {},
tooltip: 'Invite friends',
helper: 'Invite friends',
icon: Icons.person_add,
),
);
}
Widget toggle() {
return Container(
child: FloatingActionButton(
backgroundColor: _buttonColor.value,
onPressed: animate,
tooltip: 'Toggle',
child: AnimatedIcon(
icon: AnimatedIcons.menu_close,
progress: _animateIcon,
),
),
);
}
#override
Widget build(BuildContext context) {
return Column(
mainAxisAlignment: MainAxisAlignment.end,
children: <Widget>[
Transform(
transform: Matrix4.translationValues(
0.0,
_translateButton.value * 2.0,
0.0,
),
child: goal(),
),
Transform(
transform: Matrix4.translationValues(
0.0,
_translateButton.value,
0.0,
),
child: invite(),
),
Transform(
transform: Matrix4.translationValues(
168.0,
0.0,
0.0,
),
child: toggle(),
),
],
);
}
}

Adding with SingleTickerProviderStateMixin to the child Widget made the error go away!

Related

How to remove right margin from floating action button?

I am implementing FAB as Expandable with little dark overlay when FAB is clicked.
My problem is there is right margin in Scaffold and my overlay is not filling whole view.
How to remove that right margin?
Here is how it's looking:
Here is my Scaffold code:
return Scaffold(
resizeToAvoidBottomInset: true,
floatingActionButton: !isSearchBarVisible ? SizedBox.expand(
child: ExpandableFab(
//key: _key,
distance: size.height * 0.09,
children: [
/* not needed to show problem */
],
),
) : null,
body: /* some body */
and here is ExpandableFab class
class ExpandableFab extends StatefulWidget {
const ExpandableFab({
Key? key,
this.initialOpen,
required this.distance,
required this.children,
}) : super(key: key);
final bool? initialOpen;
final double distance;
final List<Widget> children;
#override
_ExpandableFabState createState() => _ExpandableFabState();
}
class _ExpandableFabState extends State<ExpandableFab>
with SingleTickerProviderStateMixin {
late AnimationController _controller;
Animation<double>? _expandAnimation;
bool _open = false;
#override
void initState() {
super.initState();
_open = widget.initialOpen ?? false;
_controller = AnimationController(
value: _open ? 1.0 : 0.0,
duration: const Duration(milliseconds: 300),
vsync: this,
);
_expandAnimation = CurvedAnimation(
curve: Curves.fastOutSlowIn,
reverseCurve: Curves.easeOutQuad,
parent: _controller,
);
}
#override
void dispose() {
_controller.dispose();
super.dispose();
}
void _toggle() {
setState(() {
_open = !_open;
if (_open) {
_controller.forward();
} else {
_controller.reverse();
}
});
}
#override
Widget build(BuildContext context) {
final size = MediaQuery.of(context).size;
return GestureDetector(
onTap: () => _toggle(),
child: Stack(
alignment: Alignment.bottomRight,
clipBehavior: Clip.none,
children: [
IgnorePointer(
ignoring: !_open,
child: TweenAnimationBuilder<double>(
tween: Tween<double>(begin: 0.0, end: _open ? 1.0 : 0.0),
duration: Duration(milliseconds: 500),
curve: Curves.easeInOut,
builder: (_, value, child) {
if (value < 0.001) {
return child!;
}
return BackdropFilter(
filter: ImageFilter.blur(sigmaX: value, sigmaY: value),
child: child,
);
},
child: Container(color: Colors.transparent),
),
),
IgnorePointer(
ignoring: !_open,
child: AnimatedOpacity(
duration: Duration(milliseconds: 500),
opacity: _open ? 1 : 0,
curve: Curves.easeInOut,
child: Container(
color: Colors.black12,
),
),
),
Transform.translate(
offset: Offset(0, 0),
child: Stack(
alignment: Alignment.bottomRight,
children: [
Positioned(
bottom: size.height * 0.14,
child: _buildTapToCloseFab(size)
),
Positioned(
bottom: size.height * 0.14,
child: _buildTapToOpenFab(size)
),
..._buildExpandingActionButtons(),
],
),
),
],
),
);
}
Widget _buildTapToCloseFab(Size size) {
return SizedBox(
width: 56.0,
height: 56.0,
child: Center(
child: Material(
shape: const CircleBorder(),
clipBehavior: Clip.antiAlias,
elevation: 4.0,
child: InkWell(
onTap: _toggle,
child: Padding(
padding: const EdgeInsets.all(8.0),
child: Icon(
Icons.close,
color: Theme.of(context).primaryColor,
),
),
),
),
),
);
}
Widget _buildTapToOpenFab(Size size) {
return IgnorePointer(
ignoring: _open,
child: AnimatedContainer(
transformAlignment: Alignment.center,
transform: Matrix4.diagonal3Values(
_open ? 0.7 : 1.0,
_open ? 0.7 : 1.0,
1.0,
),
duration: const Duration(milliseconds: 250),
curve: const Interval(0.0, 0.5, curve: Curves.easeOut),
child: AnimatedOpacity(
opacity: _open ? 0.0 : 1.0,
curve: const Interval(0.25, 1.0, curve: Curves.easeInOut),
duration: const Duration(milliseconds: 250),
child: FloatingActionButton(
onPressed: _toggle,
child: Icon(
Icons.add,
),
),
),
),
);
}
List<Widget> _buildExpandingActionButtons() {
final children = <Widget>[];
final count = widget.children.length;
final step = 90.0 / (count - 1);
var dist;
for (var i = 0, angleInDegrees = 0.0;
i < count;
i++, angleInDegrees += step) {
if (i == 0) {
dist = (widget.distance) * (i + 1);
}
else {
dist = (widget.distance) * (i + 1);
}
children.add(
_ExpandingActionButton(
directionInDegrees: 90,
maxDistance: dist,
progress: _expandAnimation,
child: widget.children[i],
),
);
}
return children;
}
}
class _ExpandingActionButton extends StatelessWidget {
const _ExpandingActionButton({
Key? key,
required this.directionInDegrees,
required this.maxDistance,
required this.progress,
required this.child,
}) : super(key: key);
final double directionInDegrees;
final double maxDistance;
final Animation<double>? progress;
final Widget child;
#override
Widget build(BuildContext context) {
final size = MediaQuery.of(context).size;
return AnimatedBuilder(
animation: progress!,
builder: (BuildContext context, Widget? child) {
final offset = Offset.fromDirection(
directionInDegrees * (math.pi / 180.0),
progress!.value * maxDistance,
);
return Positioned(
right: 4.0 + offset.dx,
bottom: (size.height * 0.14) + 4.0 + offset.dy,
child: Transform.rotate(
angle: (1.0 - progress!.value) * math.pi / 2,
child: child,
),
);
},
child: FadeTransition(
opacity: progress!,
child: child,
),
);
}
}
It's mostly code from Flutter tutorial: https://docs.flutter.dev/cookbook/effects/expandable-fab with some little changes like vertical expand or overlay when FAB is expanded.
Use this structure:
Scaffold(
resizeToAvoidBottomInset: true,
floatingActionButton: !isSearchBarVisible ? Padding(
padding: const EdgeInsets.all(8.0),
child: SizedBox.expand(
child: ExpandableFab(
//key: _key,
distance: size.height * 0.09,
children: [
/* not needed to show problem */
],
),
),
) : null,
body:

lift and flip card animation in flutter

I want to lift my card up, flip it and then place it back to its original position. Currently i have achieved to flip the card by the following code. Can anyone tell how can i lift the card as well.
animation = TweenSequence(
<TweenSequenceItem<double>>[
TweenSequenceItem<double>(
tween: Tween(begin: 0.0, end: 2*pi).chain(CurveTween(curve: Curves.linear)),
weight: 50.0,
),
TweenSequenceItem<double>(
tween: ConstantTween<double>(pi*2),
weight: 50.0,
),
],
).animate(animationController);
And my widget is:-
AnimatedBuilder(
animation: animationController,
child: Container(
height: 100,
width: 100,
color: Colors.red,
),
builder: (context,child){
Matrix4 transform = Matrix4.identity();
transform.setEntry(3, 2, 0.001);
transform.rotateY(animation.value);
return Transform(
transform: transform,
alignment: Alignment.center,
child: child,
);
},
),
You can lift the container up and put it back using AnimatedContainer. Here is a sample using your snippets.
class FlipAndLiftAnimation extends StatefulWidget {
const FlipAndLiftAnimation({Key? key}) : super(key: key);
#override
_FlipAndLiftAnimationState createState() => _FlipAndLiftAnimationState();
}
class _FlipAndLiftAnimationState extends State<FlipAndLiftAnimation>
with TickerProviderStateMixin {
late AnimationController animationController;
late Animation<double> animation;
double boxSize = 100;
double initialSize = 100;
double expandedSize = 300;
#override
void initState() {
animationController = AnimationController(
vsync: this,
duration: const Duration(milliseconds: 350),
);
animation = TweenSequence(
<TweenSequenceItem<double>>[
TweenSequenceItem<double>(
tween: Tween(begin: 0.0, end: 2 * pi)
.chain(CurveTween(curve: Curves.linear)),
weight: 50.0,
),
TweenSequenceItem<double>(
tween: ConstantTween<double>(pi * 2),
weight: 50.0,
),
],
).animate(animationController);
super.initState();
}
#override
Widget build(BuildContext context) {
return Scaffold(
body: Container(
color: const Color.fromARGB(164, 117, 81, 1),
child: Center(
child: GestureDetector(
onTap: () async {
animationController.isCompleted
? animationController.reverse()
: animationController.forward();
setState(() {
boxSize = expandedSize;
});
await Future.delayed(const Duration(milliseconds: 500), () {
setState(() {
boxSize = initialSize;
});
});
},
child: AnimatedBuilder(
animation: animationController,
child: AnimatedContainer(
margin: EdgeInsets.all(50),
height: boxSize,
width: boxSize,
color: Colors.red,
duration: Duration(milliseconds: 300),
),
builder: (context, child) {
Matrix4 transform = Matrix4.identity();
transform.setEntry(3, 2, 0.001);
transform.rotateY(animation.value);
transform.scale(1.0, 1.0, 10);
return Transform(
transform: transform,
alignment: Alignment.center,
child: child,
);
},
),
),
),
),
);
}
}

How to hide actions in floatingButton

I am trying to have a FloatingButton with multiple options and each option triggers an action. I achieve that so far, and also an animated transition when pressed. What I need is to achieve that when the Button is not pressed, only one icon is showing.
This is the code so far:
class _FloatingButtonMultipleOptionState extends State<FloatingButtonMultipleOption> with SingleTickerProviderStateMixin {
bool isOpened = false;
AnimationController _animationController;
Animation<Color> _buttonColor;
Animation<double> _animateIcon;
Animation<double> _translateButton;
Curve _curve = Curves.easeOut;
double _fabHeight = 56.0;
#override
initState() {
_animationController = AnimationController(vsync: this, duration: Duration(milliseconds: 500))
..addListener(() {
setState(() {});
});
_animateIcon = Tween<double>(begin: 0.0, end: 1.0).animate(_animationController);
_buttonColor = ColorTween(
begin: TheBaseColors.lightBlue,
end: TheBaseColors.lightRed,
).animate(CurvedAnimation(
parent: _animationController,
curve: Interval(
0.00,
1.00,
curve: Curves.linear,
),
));
_translateButton = Tween<double>(
begin: _fabHeight,
end: -14.0,
).animate(CurvedAnimation(
parent: _animationController,
curve: Interval(
0.0,
0.75,
curve: _curve,
),
));
super.initState();
}
#override
dispose() {
_animationController.dispose();
super.dispose();
}
animate() {
if (!isOpened) {
_animationController.forward();
} else {
_animationController.reverse();
}
isOpened = !isOpened;
}
Widget post() {
return FloatingActionButton.extended(
heroTag: null,
onPressed: (){
Navigator.push(context, MaterialPageRoute(builder: (context) => CreatePost()));
},
tooltip: 'Post',
label: Row(
children: [
Padding(
padding: const EdgeInsets.all(8.0),
child: Text('Send a Post'),
),
Icon(
Icons.message,
),
],
),
);
}
Widget activity() {
return FloatingActionButton.extended(
heroTag: null,
onPressed: (){
Navigator.push(context, MaterialPageRoute(builder: (context) => CreateActivity()));
},
tooltip: 'Activiy',
label: Row(
children: [
Padding(
padding: const EdgeInsets.all(8.0),
child: Text('Create activity'),
),
Icon(Icons.event),
],
),
);
}
Widget close() {
return FloatingActionButton(
backgroundColor: _buttonColor.value,
onPressed: animate,
tooltip: 'Close',
child: AnimatedIcon(
icon: AnimatedIcons.menu_close,
progress: _animateIcon,
),
);
}
#override
Widget build(BuildContext context) {
return Column(
children: <Widget>[
Transform(
transform: Matrix4.translationValues(
0.0,
_translateButton.value * 3.0,
0.0,
),
child: post(),
),
Transform(
transform: Matrix4.translationValues(
0.0,
_translateButton.value * 2.0,
0.0,
),
child: activity(),
),
close(),
],
);
}
}
In the photo attached you can see how when the button is not pressed, the user can see the option 'Create activity' and this should only be seen when the button is pressed.
You could set a boolean "isButtonPressed" that changes state every time you press the FloatingActionButton, and then wrap the other buttons into the Visibility widget like this
Visibility(
visible: isButtonPressed,
child: activity(),
)
This will make your floatingActionButton "Create activity" visible when the button is pressed, and hide it when the button is pressed a second time.

Animating the front card to the back in a stack view of cards in flutter

I have got a stack of cards and scroll wheel. I am trying to animate the front card, move it to the right, then bring it back in a way it goes to the back of the cards.
I have used a future function to specify witch part of code should be done first. But what I get is; it changes the index of the card first the animation takes place. Here is my code:
class AnimationsPractice extends StatefulWidget {
static const String id = 'test_screen';
#override
_AnimationsPracticeState createState() => _AnimationsPracticeState();
}
class _AnimationsPracticeState extends State<AnimationsPractice>
with SingleTickerProviderStateMixin {
FixedExtentScrollController _scrollController =
FixedExtentScrollController(initialItem: 0);
AnimationController _controller;
Animation<Offset> _offsetAnimation;
int selected;
List<Widget> sampleCard;
Animation<Offset> _offsetAnimation2;
bool halfWayAnimation;
#override
void initState() {
super.initState();
_controller =
AnimationController(duration: const Duration(seconds: 1), vsync: this)
..repeat();
_offsetAnimation = Tween<Offset>(
begin: Offset.zero,
end: const Offset(1.5, 0.0),
).animate(
CurvedAnimation(
parent: _controller,
curve: Interval(
0.0,
0.5,
curve: Curves.elasticIn,
),
),
);
_offsetAnimation2 = Tween<Offset>(
begin: const Offset(1.5, 0.0),
end: Offset.zero,
).animate(
CurvedAnimation(
parent: _controller,
curve: Interval(
0.5,
1.0,
curve: Curves.elasticIn,
),
),
);
halfWayAnimation = false;
_controller.stop();
sampleCard = [
Container(
height: 60,
width: 40,
color: Colors.red,
),
Transform.rotate(
angle: 10 * (pi / 180),
child: Container(
height: 60,
width: 40,
color: Colors.blueGrey,
)),
SlideTransition(
position: halfWayAnimation ? _offsetAnimation2 : _offsetAnimation,
child: Container(
height: 60,
width: 40,
color: Colors.yellow,
),
),
];
}
#override
void dispose() {
super.dispose();
_controller.dispose();
}
Future<void> _playAnimation() async {
try {
await _controller.forward().orCancel;
await siftingIndex();
await _controller.reverse().orCancel;
} on TickerCanceled {
// the animation got canceled, probably because it was disposed of
}
}
Future<void> siftingIndex() {
return Future.delayed(const Duration(microseconds: 200), () {
sampleCard.insert(0, sampleCard[sampleCard.length - 1]);
sampleCard.removeLast();
});
}
#override
Widget build(BuildContext context) {
return Scaffold(
appBar: PreferredSize(
preferredSize: const Size.fromHeight(180.0),
child: SafeArea(
child: TextButton(
child: Text('back to login'),
onPressed: () {
Navigator.pushNamed(context, LoginScreen.id);
},
),
),
),
body: Column(
crossAxisAlignment: CrossAxisAlignment.center,
children: [
Stack(children: sampleCard),
CustomScrollWheel(
onItemChange: (x) {
setState(() {
_playAnimation();
halfWayAnimation = true;
});
},
scrollController: _scrollController,
),
],
),
);
}
}
enter image description here

How to animate a Container/Button vertically up and down?

I'm trying to animate the center button vertically up and down like a lift after a button click but can't figure out how to go about it. I am using sequenceAnimations elsewhere on this button so I tried wrapping it in a Positioned widget and change the bottom offset partnered with a Tween to cycle between values, unfortuntley this results in LayoutErrors and crashes...
Tried Position wrapping.
class LiftPage extends StatefulWidget {
#override
State<StatefulWidget> createState() {
return _LiftPageState();
}
}
class _LiftPageState extends State<LiftPage> with TickerProviderStateMixin {
AnimationController _pageLoadController;
AnimationController _liftImageController;
SequenceAnimation _liftPageLoadAnimation;
SequenceAnimation _liftImageAnimation;
double _screenWidth = 2000.0;
double _screenHeight = 5000.0;
#override
void initState() {
super.initState();
initPlayer();
_pageLoadController = AnimationController(vsync: this);
_liftImageController = AnimationController(vsync: this);
_liftImageAnimation = SequenceAnimationBuilder()
.addAnimatable(
animatable: Tween<double>(
begin: 0,
end: _screenHeight,
),
from: Duration(seconds: 0),
to: Duration(seconds: _timeForLiftUp),
tag: "liftGoingUp",
)
.animate(_liftImageController);
_liftPageLoadAnimation = SequenceAnimationBuilder()
.addAnimatable(
animatable: ColorTween(
begin: Color(0xfff665c6),
end: Color(0xffF599E9),
),
from: Duration(seconds: 0),
to: Duration(seconds: 4),
tag: "color",
)
.addAnimatable(
animatable: Tween<double>(
begin: 125,
end: 0,
),
from: Duration(seconds: 0),
to: Duration(seconds: 2),
tag: "border",
)
.addAnimatable(
animatable: Tween<double>(
begin: 100,
end: _screenHeight,
),
from: Duration(seconds: 0),
to: Duration(seconds: 4),
tag: "height",
)
.addAnimatable(
animatable: Tween<double>(
begin: 100,
end: _screenWidth,
),
from: Duration(seconds: 0),
to: Duration(seconds: 4),
tag: "width",
)
.addAnimatable(
animatable: ColorTween(
begin: Colors.white,
end: Color(0xffF599E9),
),
from: Duration(seconds: 0),
to: Duration(seconds: 1),
tag: "iconFade",
)
.addAnimatable(
animatable: ColorTween(
begin: Color(0x00000000),
end: Color(0xfff665c6),
),
from: Duration(milliseconds: 2000),
to: Duration(milliseconds: 2300),
tag: "circleFadeIn",
)
.addAnimatable(
animatable: ColorTween(
begin: Color(0x00ffffff),
end: Color(0xfff665c6),
),
from: Duration(milliseconds: 2500),
to: Duration(milliseconds: 3500),
tag: "audioControlsFadeIn",
)
.animate(_pageLoadController);
}
#override
Widget build(BuildContext context) {
List<Widget> childrenStack = <Widget>[
Scaffold(
body: AnimatedBuilder(
animation: _pageLoadController,
builder: (BuildContext context, Widget child) {
return Stack(
children: <Widget>[
Center(child: _placeButton()),
Center(
child: _placeCircle(),
),
Align(
alignment: Alignment.topCenter,
child: _playBackControls(),
),
],
);
}),
),
];
return MaterialApp(
home: Scaffold(
appBar: AppBar(
title: Center(
child: Text('Lift Page'),
),
backgroundColor: Color(0xfff665c6),
),
body: Center(child: Stack(children: childrenStack)),
));
}
GestureDetector _placeCircle() {
return GestureDetector(
onTap: () {
final _status = _pageLoadController.status;
if (_status == AnimationStatus.completed) {
_pageLoadController.reverse();
_liftPage_liftButtonTapped();
} else {
_pageLoadController.forward();
_liftPage_liftButtonTapped();
}
},
child: Container(
height: 100,
width: 100,
decoration: BoxDecoration(
color: _liftPageLoadAnimation["circleFadeIn"].value,
borderRadius: BorderRadius.circular(125.0),
),
),
);
}
I'm trying to animate the pink circle/button up and down.
https://imgur.com/a/Iu7i5uw
You can use AnimatedContainer, here is an example:
class UpDown extends StatefulWidget {
#override
UpDownState createState() {
return UpDownState();
}
}
class UpDownState extends State<UpDown> {
bool up = false;
#override
Widget build(BuildContext context) {
return Scaffold(
body: Center(
child: AnimatedContainer(
padding: EdgeInsets.all(10.0),
duration: Duration(milliseconds: 250), // Animation speed
transform: Transform.translate(
offset: Offset(0, up ? -100 : 0), // Change -100 for the y offset
).transform,
child: Container(
height: 50.0,
child: FloatingActionButton(
backgroundColor: Colors.red,
child: Icon(Icons.ac_unit),
onPressed: () {
setState(() {
up = !up;
});
},
),
),
),
),
);
}
}