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;
});
},
),
),
),
),
);
}
}
Related
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,
);
},
),
),
),
),
);
}
}
I am trying to animate between widgets as follows:
AnimatedSwitcher(
duration: const Duration(seconds: 1),
transitionBuilder: (Widget child, Animation<double> animation) {
return SlideTransition(
position: Tween(
begin: Offset(1.0, 0.0),
end: Offset(0.0, 0.0),)
.animate(animation),
child: child,
);
},
child: Provider.of<UserWidgets>(context, listen: false).renderWidget(context),
),
This works fine but for two different sized widgets its not smooth because of OffSet.
Try wrapping both your child widgets inside an Align widget like this,
child: Align(
alignment: Alignment.topCenter,
child: Provider.of<UserWidgets>(context, listen: false).renderWidget(context),
)
This should ensure that both your old and new children are always aligned to the topCenter while animating.
Here is the full working example.
class Switcher extends StatefulWidget {
State<StatefulWidget> createState() => SwitcherS();
}
class SwitcherS extends State<Switcher> {
bool state = false;
buildChild (index) => Align(
alignment: Alignment.topCenter,
child: Container(
width: index == 0 ? 100 : 150,
height: index == 0 ? 200 : 150,
color:index == 0 ? Colors.deepPurple : Colors.deepOrange,
),
key: ValueKey('$index'));
#override
Widget build(BuildContext context) {
return GestureDetector(
onTap: () => setState(() { state = !state; }),
child: Padding(
padding: EdgeInsets.all(24),
child: AnimatedSwitcher(
duration: const Duration(seconds: 1),
transitionBuilder: (Widget child, Animation<double> animation) {
return SlideTransition(
position: Tween(
begin: Offset(1.0, 1.0),
end: Offset(0.0, 0.0),
).animate(animation),
child: child,
);
},
child: buildChild(state ? 0 : 1),
),
);
}
}
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
i am developing a chatbot and i want to add a typing indicator before the bot reply to the user, i have tried switching between widgets after the user inputs a message using future delayed, but it is not working at all.
the following code demonstrates how i tried using the future delayed:
bool _nextWidget = false;
#override
void initState() {
super.initState();
}
void myMethod() {
Future.delayed(
const Duration(
seconds: 5,
milliseconds: 500,
),
() {
if (this.mounted) {
setState(() {
_nextWidget = true;
});
}
});
}
Widget bot(String message) {
myMethod();
return _nextWidget ? botMessage(message) : botInd();
}
Widget botInd() {
return Container(
alignment: Alignment.bottomLeft,
margin: EdgeInsets.only(top: 20),
child: Container(
constraints: BoxConstraints(maxWidth: 75, maxHeight: 100),
child: JumpingDotsProgressIndicator(fontSize: 50.0, color: Colors.white)
)
);
}
Widget botMessage(String message) {
return ChatBubble(
clipper: ChatBubbleClipper2(type: BubbleType.receiverBubble),
alignment: Alignment.bottomLeft,
margin: EdgeInsets.only(top: 20),
backGroundColor: Colors.white,
child: Container(
constraints: BoxConstraints(maxWidth: MediaQuery.of(context).size.width * 0.7),
child: Text(
message,
style: TextStyle(color: Colors.black, fontWeight: FontWeight.bold)
)
)
);
}
Have a look at https://flutter.dev/docs/cookbook/effects/typing-indicator it explains all necessary steps how to create a typing indicator with code examples.
import 'dart:math';
import 'package:flutter/cupertino.dart';
import 'package:flutter/material.dart';
import 'package:flutter/rendering.dart';
void main() {
runApp(
const MaterialApp(
home: ExampleIsTyping(),
debugShowCheckedModeBanner: false,
),
);
}
const _backgroundColor = Color(0xFF333333);
class ExampleIsTyping extends StatefulWidget {
const ExampleIsTyping({
Key? key,
}) : super(key: key);
#override
_ExampleIsTypingState createState() => _ExampleIsTypingState();
}
class _ExampleIsTypingState extends State<ExampleIsTyping> {
bool _isSomeoneTyping = false;
#override
Widget build(BuildContext context) {
return Scaffold(
backgroundColor: _backgroundColor,
appBar: AppBar(
title: const Text('Typing Indicator'),
),
body: Column(
children: [
Expanded(
child: _buildMessages(),
),
Align(
alignment: Alignment.bottomLeft,
child: TypingIndicator(
showIndicator: _isSomeoneTyping,
),
),
_buildIsTypingSimulator(),
],
),
);
}
Widget _buildMessages() {
return ListView.builder(
padding: const EdgeInsets.symmetric(vertical: 8.0),
itemCount: 25,
reverse: true,
itemBuilder: (context, index) {
return Padding(
padding: const EdgeInsets.only(left: 100.0),
child: FakeMessage(isBig: index.isOdd),
);
},
);
}
Widget _buildIsTypingSimulator() {
return Container(
color: Colors.grey,
padding: const EdgeInsets.all(16),
child: Center(
child: CupertinoSwitch(
onChanged: (newValue) {
setState(() {
_isSomeoneTyping = newValue;
});
},
value: _isSomeoneTyping,
),
),
);
}
}
class TypingIndicator extends StatefulWidget {
const TypingIndicator({
Key? key,
this.showIndicator = false,
this.bubbleColor = const Color(0xFF646b7f),
this.flashingCircleDarkColor = const Color(0xFF333333),
this.flashingCircleBrightColor = const Color(0xFFaec1dd),
}) : super(key: key);
final bool showIndicator;
final Color bubbleColor;
final Color flashingCircleDarkColor;
final Color flashingCircleBrightColor;
#override
_TypingIndicatorState createState() => _TypingIndicatorState();
}
class _TypingIndicatorState extends State<TypingIndicator>
with TickerProviderStateMixin {
late AnimationController _appearanceController;
late Animation<double> _indicatorSpaceAnimation;
late Animation<double> _smallBubbleAnimation;
late Animation<double> _mediumBubbleAnimation;
late Animation<double> _largeBubbleAnimation;
late AnimationController _repeatingController;
final List<Interval> _dotIntervals = const [
Interval(0.25, 0.8),
Interval(0.35, 0.9),
Interval(0.45, 1.0),
];
#override
void initState() {
super.initState();
_appearanceController = AnimationController(
vsync: this,
)..addListener(() {
setState(() {});
});
_indicatorSpaceAnimation = CurvedAnimation(
parent: _appearanceController,
curve: const Interval(0.0, 0.4, curve: Curves.easeOut),
reverseCurve: const Interval(0.0, 1.0, curve: Curves.easeOut),
).drive(Tween<double>(
begin: 0.0,
end: 60.0,
));
_smallBubbleAnimation = CurvedAnimation(
parent: _appearanceController,
curve: const Interval(0.0, 0.5, curve: Curves.elasticOut),
reverseCurve: const Interval(0.0, 0.3, curve: Curves.easeOut),
);
_mediumBubbleAnimation = CurvedAnimation(
parent: _appearanceController,
curve: const Interval(0.2, 0.7, curve: Curves.elasticOut),
reverseCurve: const Interval(0.2, 0.6, curve: Curves.easeOut),
);
_largeBubbleAnimation = CurvedAnimation(
parent: _appearanceController,
curve: const Interval(0.3, 1.0, curve: Curves.elasticOut),
reverseCurve: const Interval(0.5, 1.0, curve: Curves.easeOut),
);
_repeatingController = AnimationController(
vsync: this,
duration: const Duration(milliseconds: 1500),
);
if (widget.showIndicator) {
_showIndicator();
}
}
#override
void didUpdateWidget(TypingIndicator oldWidget) {
super.didUpdateWidget(oldWidget);
if (widget.showIndicator != oldWidget.showIndicator) {
if (widget.showIndicator) {
_showIndicator();
} else {
_hideIndicator();
}
}
}
#override
void dispose() {
_appearanceController.dispose();
_repeatingController.dispose();
super.dispose();
}
void _showIndicator() {
_appearanceController
..duration = const Duration(milliseconds: 750)
..forward();
_repeatingController.repeat();
}
void _hideIndicator() {
_appearanceController
..duration = const Duration(milliseconds: 150)
..reverse();
_repeatingController.stop();
}
#override
Widget build(BuildContext context) {
return AnimatedBuilder(
animation: _indicatorSpaceAnimation,
builder: (context, child) {
return SizedBox(
height: _indicatorSpaceAnimation.value,
child: child,
);
},
child: Stack(
children: [
_buildAnimatedBubble(
animation: _smallBubbleAnimation,
left: 8,
bottom: 8,
bubble: _buildCircleBubble(8),
),
_buildAnimatedBubble(
animation: _mediumBubbleAnimation,
left: 10,
bottom: 10,
bubble: _buildCircleBubble(16),
),
_buildAnimatedBubble(
animation: _largeBubbleAnimation,
left: 12,
bottom: 12,
bubble: _buildStatusBubble(),
),
],
),
);
}
Widget _buildAnimatedBubble({
required Animation<double> animation,
required double left,
required double bottom,
required Widget bubble,
}) {
return Positioned(
left: left,
bottom: bottom,
child: AnimatedBuilder(
animation: animation,
builder: (context, child) {
return Transform.scale(
scale: animation.value,
alignment: Alignment.bottomLeft,
child: child,
);
},
child: bubble,
),
);
}
Widget _buildCircleBubble(double size) {
return Container(
width: size,
height: size,
decoration: BoxDecoration(
shape: BoxShape.circle,
color: widget.bubbleColor,
),
);
}
Widget _buildStatusBubble() {
return Container(
width: 85,
height: 44,
padding: const EdgeInsets.symmetric(horizontal: 8),
decoration: BoxDecoration(
borderRadius: BorderRadius.circular(27),
color: widget.bubbleColor,
),
child: Row(
mainAxisAlignment: MainAxisAlignment.spaceEvenly,
children: [
_buildFlashingCircle(0),
_buildFlashingCircle(1),
_buildFlashingCircle(2),
],
),
);
}
Widget _buildFlashingCircle(int index) {
return AnimatedBuilder(
animation: _repeatingController,
builder: (context, child) {
final circleFlashPercent =
_dotIntervals[index].transform(_repeatingController.value);
final circleColorPercent = sin(pi * circleFlashPercent);
return Container(
width: 12,
height: 12,
decoration: BoxDecoration(
shape: BoxShape.circle,
color: Color.lerp(widget.flashingCircleDarkColor,
widget.flashingCircleBrightColor, circleColorPercent),
),
);
},
);
}
}
#immutable
class FakeMessage extends StatelessWidget {
const FakeMessage({
Key? key,
required this.isBig,
}) : super(key: key);
final bool isBig;
#override
Widget build(BuildContext context) {
return Container(
margin: const EdgeInsets.symmetric(vertical: 8.0, horizontal: 24.0),
height: isBig ? 128.0 : 36.0,
decoration: BoxDecoration(
borderRadius: const BorderRadius.all(Radius.circular(8.0)),
color: Colors.grey.shade300,
),
);
}
}
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!