MouseRegion doesn't cover positioned element in stack - flutter

I struggle to get a positioned object that I have in a stack to keep the mouse region that I have defined on an element that is bigger than the initial stacksize. The content gets shown, but it doesn't keep the mouse region. Meaning, even I'm still on top of the positioned object, it disappears when the mouse leaves the defined area.
I also can't switch the size of the element because that would cause my content to get shifted.
Do you have any suggestions on how to solve that?
In HTML, this behaviour is standard. So it puzzles me that Flutter struggles so much with it.
Here a code example
MouseRegion(
onEnter: (_) {
isButtonHovered = true;
setState(() {});
},
onExit: (_) async {
isButtonHovered = false;
setState(() {});
},
child: SizedBox(
width: 200,
height: 100,
child: Stack(
clipBehavior: Clip.none,
alignment: Alignment.bottomLeft,
children: [
Container(
color: Colors.blue,
width: 200,
height: 100,
),
isButtonHovered
? Positioned(
bottom: 0,
left: 0,
child: Container(
color: Colors.red,
width: 200,
height: 500,
))
: Container(),
],
)));

The mouse region is constrained within the 200 of the sized box.. You can also change the height of the sized box based on the mouse hover like this
import 'package:flutter/material.dart';
void main() {
runApp(MyApp());
}
class MyApp extends StatelessWidget {
#override
Widget build(BuildContext context) {
return MaterialApp(
home: Scaffold(
body: Center(
child: MyWidget(),
),
),
);
}
}
class MyWidget extends StatefulWidget {
#override
State<MyWidget> createState() => _MyWidgetState();
}
class _MyWidgetState extends State<MyWidget> {
bool isButtonHovered = false;
#override
Widget build(BuildContext context) {
return MouseRegion(
onEnter: (_) {
isButtonHovered = true;
setState(() {});
},
onExit: (_) async {
isButtonHovered = false;
setState(() {});
},
child: SizedBox(
width: 200,
height: isButtonHovered?500:100,//<---Here
child: Stack(
clipBehavior: Clip.none,
alignment: Alignment.bottomLeft,
children: [
Container(
color: Colors.blue,
width: 200,
height: 100,
),
isButtonHovered
? Positioned(
bottom: 0,
left: 0,
child: Container(
color: Colors.red,
width: 200,
height: 500,
))
: Container(),
],
)));
}
}
Edit
I think the layout you created is wrong. I created a working demo for you. Please check this
import 'package:flutter/material.dart';
const Color darkBlue = Color.fromARGB(255, 18, 32, 47);
void main() {
runApp(MyApp());
}
class MyApp extends StatelessWidget {
#override
Widget build(BuildContext context) {
return MaterialApp(
theme: ThemeData.dark().copyWith(
scaffoldBackgroundColor: darkBlue,
),
debugShowCheckedModeBanner: false,
home: Scaffold(
body: Center(
child: MyWidget(),
),
),
);
}
}
class MyWidget extends StatefulWidget {
#override
State<MyWidget> createState() => _MyWidgetState();
}
class _MyWidgetState extends State<MyWidget> {
bool isButtonHovered = false;
#override
Widget build(BuildContext context) {
return Stack(children: [
Column(children: [
Text("Content"),
MouseRegion(
onEnter: (_) {
isButtonHovered = true;
setState(() {});
},
child: Container(height: 200, width: 200, color: Colors.blue)),
Text("Content"),
]),
if (isButtonHovered)
MouseRegion(
onExit: (_) {
isButtonHovered = false;
setState(() {});
},
child: Container(height: 500, width: 200, color: Colors.red.withOpacity(0.5)))
]);
}
}
The mouse region will not work outside a SizedBox with specific size even if we set the clip to none in a stack that's inside the sized box..

I think the structure will get initially 500 height. And rather than using onEnter it is better to use onHover. Also, I think enabling the max container will be based on hover position.
Text('Content'),
SizedBox(
width: 200,
height: 500,
child: MouseRegion(
onEnter: (details) {},
onHover: (event) {
final dx = event.localPosition.dy;
// enter position > max height - min blue Box
if (dx > 500 - 100) {
isButtonHovered = true;
setState(() {});
}
},
onExit: (details) async {
isButtonHovered = false;
setState(() {});
},
child: Stack(
clipBehavior: Clip.none,
alignment: Alignment.bottomLeft,
children: [
Positioned(
bottom: 0,
left: 0,
child: Container(
color: Colors.blue,
width: 200,
height: 100,
),
),
isButtonHovered
? Positioned(
bottom: 0,
left: 0,
child: Container(
color: Colors.red.withOpacity(0.8),
width: 200,
height: 500,
))
: Container(),
],
),
),
),
Text('Content'),

Related

How to run widget over the container screen in flutter

I want to show some widget onTap() event over screen like below image .
Here is my code
In this the build method is return Container().
Container has one child named SingleChildScrollView and also it has some children.
So I don't want to change on this all children when new widget will create.
In simple, saw widget onTap() over the running screen without disturb another widget.
class _SettingScreenState extends State<SettingScreen> {
List<Widget> _iconList=[];
List<Widget> _titleList=[];
List<Widget> _settingLIst=[];
#override
void initState() {
super.initState();
for(int i=0;i<5;i++){
_iconList.add(_addInIcon(i));
}
for(int i=0;i<5;i++){
_titleList.add(_addInTitle(i));
}
for(int i=0;i<24;i++){
_settingLIst.add(_addInSetting(i));
}
}
#override
Widget build(BuildContext context) {
return Container(
decoration: BoxDecoration(
color: Colors.white
),
child: SingleChildScrollView(
child: Column(
children: <Widget>[
_titleList[0],
_addInSetting(0),
_addInSetting(1),
_titleList[1],
_settingLIst[2],
_settingLIst[3],
_titleList[2],
_settingLIst[4],
_settingLIst[5],
_settingLIst[6],
_settingLIst[7],
_settingLIst[8],
_settingLIst[9],
GestureDetector(
onTap: (){
//open widget over the this screen
},
child: Button(
image: _coverImage(),
width: double.infinity,
height: 50,
alignment: Alignment.center,
fit: BoxFit.fitHeight,
),
),
],
),
),
);
}
}
Ans , is setState() is necessary while create new widget over the previous Container() widget ?
Because we cant change anything on previous Container()
In Android-java development, what i did
onClick()
{
ImageView imageView = new ImageView(this);
Glide.with(getApplicationContext()).load(position).into(imageView);
RelativeLayout.LayoutParams layoutParams = new RelativeLayout.LayoutParams( Math.round((float) 35 * density), Math.round((float) 35 * density));
imageView.setX(reactionButton.getX());
imageView.setY(reactionButton.getY());
imageView.setLayoutParams(layoutParams);
messageRelativeLayout.addView(imageView);
imageView.bringToFront();
animateReaction(imageView);
}
It means every time new ImageView will added on tree, no matter if previous ImageView is appear or not.
Simple click button and create new ImageView and show front of screen.
this feature i want to apply in flutter
Child of OverlayWidget
class OverlayChild extends StatefulWidget {
final Function clearCallBack;
final double maxWidth;
final double maxHeight;
final int itemIndex;
const OverlayChild({
Key? key,
required this.clearCallBack,
required this.maxWidth,
required this.maxHeight,
required this.itemIndex,
}) : super(key: key);
#override
_OverlayChildState createState() => _OverlayChildState();
}
class _OverlayChildState extends State<OverlayChild> {
late Timer timer;
final Random random = Random();
#override
void initState() {
super.initState();
print("int ${widget.itemIndex}");
timer = Timer.periodic(Duration(seconds: 5), (timer) {
setState(() {
widget.clearCallBack(widget.itemIndex);
timer.cancel();
print("after delay");
});
});
}
#override
void dispose() {
timer.cancel();
super.dispose();
}
#override
Widget build(BuildContext context) {
return Positioned(
top: random.nextDouble() * widget.maxHeight,
left: random.nextDouble() * widget.maxWidth,
child: Container(
height: 20,
width: 20,
padding: EdgeInsets.all(3),
decoration: BoxDecoration(
shape: BoxShape.circle,
color:
widget.itemIndex.isEven ? Colors.deepPurple : Colors.cyanAccent,
),
child: Center(child: Text("${widget.itemIndex}")),
),
);
}
}
Main Widget
class HomeOverLay extends StatefulWidget {
#override
_HomeState createState() => _HomeState();
}
class _HomeState extends State<HomeOverLay> {
List<OverlayChild> overlayItems = [];
int itemId = 0;
#override
Widget build(BuildContext context) {
return LayoutBuilder(
builder: (context, constraints) => Scaffold(
floatingActionButton: FloatingActionButton(
onPressed: () async {
setState(() {
overlayItems.add(
OverlayChild(
key: UniqueKey(),
clearCallBack: (id) {
setState(() {
overlayItems
.removeWhere((element) => element.itemIndex == id);
});
},
itemIndex: itemId,
//same as container height
maxHeight: constraints.maxHeight * .1,
maxWidth: constraints.maxWidth,
),
);
itemId++;
});
},
),
body: Stack(
children: [
Align(
alignment: Alignment.topCenter, // you may want some changes here
child: SizedBox(
height: constraints.maxHeight,
width: constraints.maxWidth,
child: SingleChildScrollView(
child: Column(
children: [
...List.generate(
22,
(index) => Container(
height: 100,
width: double.infinity,
color: index.isEven
? Colors.deepPurple
: Colors.orangeAccent,
),
)
],
),
),
),
),
Positioned(
top: constraints.maxHeight * .2,
left: 0,
right: 0,
child: Container(
width: overlayItems.length > 0 ? constraints.maxWidth : 0,
height:
overlayItems.length > 0 ? constraints.maxHeight * .1 : 0,
color: Colors.pinkAccent,
child: Stack(
children: [
...overlayItems.toList(),
],
),
),
),
],
),
),
);
}
}

Widget width not updating after value change

I am trying to build a side menu that will open and close when the arrow icon at the bottom of the vertical app bar seen in the screenshots below is pressed. I am currently doing this by using a global bool value named isleftWidgetCollapsed which will change the width of the side menu to 0 if isleftWidgetCollapsed is set to true.
It seems to be working correctly but only when I resize the app window as seen in the screenshots below. How can I get it to work on the press of the IconButton and without having to resize the app window every time?
Section of code from buildLeftMenu.dart:
Widget buildLeft(context, HomeViewModel model) {
final _scrollbar = ScrollController();
return Material(
textStyle: TextStyle(
color: Colors.white70,
fontFamily: 'Lato',
),
child: Row(
children: [
Container(
width: (gb.isleftWidgetCollapsed==true)
? 0
: MediaQuery.of(context).size.width * .20, //21.width,
height: 100.height,
decoration: BoxDecoration(
color: MainTheme.primary[50], //Colors.blueAccent[400],
),
Section of code from the mainHomeView.dart:
body: Container(
child: Row(
children: [
VerticalAppBar(),
buildLeft(context, HomeViewModel()),
...
onPressed section from VerticalAppBar.dart:
leading: RotatedBox(
quarterTurns: 1,
child: IconButton(
icon: Icon(gb.isleftWidgetCollapsed
? Icons.arrow_right
: Icons.arrow_left),
onPressed: () {
setState(() {
gb.isleftWidgetCollapsed = !gb.isleftWidgetCollapsed;
buildLeft(context, HomeViewModel());
});
},
)),
),
);
}
}
globals.dart :
library my_prj.globals;
bool isLoggedIn = false;
bool isleftWidgetCollapsed = false;
The call to buildLeft inside onPressed is doing nothing:
onPressed: () {
setState(() {
gb.isleftWidgetCollapsed = !gb.isleftWidgetCollapsed;
buildLeft(context, HomeViewModel());
});
},
Here is a solution using hooks_riverpod package. You will find all the info about this package here: http://riverpod.dev/
import 'package:flutter/material.dart';
import 'package:flutter_hooks/flutter_hooks.dart';
import 'package:hooks_riverpod/hooks_riverpod.dart';
void main() {
runApp(
ProviderScope(
child: MaterialApp(
debugShowCheckedModeBanner: false,
title: 'Flutter Demo',
home: HomePage(),
),
),
);
}
class HomePage extends HookWidget {
#override
Widget build(BuildContext context) {
final menuOpened = useProvider(menuOpenedProvider).state;
return Scaffold(
body: Stack(
children: [
AnimatedPositioned(
duration: Duration(milliseconds: 300),
top: 0,
right: 0,
bottom: 0,
left: menuOpened
? kVerticalBarWidth + kLeftMenuWidth
: kVerticalBarWidth,
child: Content(),
),
AnimatedPositioned(
duration: Duration(milliseconds: 300),
top: 0,
bottom: 0,
left: menuOpened
? kVerticalBarWidth
: kVerticalBarWidth - kLeftMenuWidth,
child: LeftMenu(),
),
Align(alignment: Alignment.centerLeft, child: VerticalAppBar()),
],
),
);
}
}
class VerticalAppBar extends HookWidget {
#override
Widget build(BuildContext context) {
final menuOpened = useProvider(menuOpenedProvider).state;
return Container(
width: kVerticalBarWidth,
color: kVerticalBarColor,
child: Align(
alignment: Alignment.bottomCenter,
child: IconButton(
onPressed: () =>
context.read(menuOpenedProvider).state = !menuOpened,
icon: Icon(menuOpened ? Icons.arrow_left : Icons.arrow_right)),
),
);
}
}
class LeftMenu extends HookWidget {
#override
Widget build(BuildContext context) {
return Container(
color: kLeftMenuColor,
width: 200.0,
padding: EdgeInsets.all(8.0),
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
...List.generate(20, (index) => Text('Menu Item $index')),
],
),
);
}
}
class Content extends StatelessWidget {
#override
Widget build(BuildContext context) {
return Container(
color: kContentColor,
child: Center(
child: Text('CONTENT'),
),
);
}
}
// Provider for the Menu State
final menuOpenedProvider = StateProvider((ref) => true);
// Some constants
const double kVerticalBarWidth = 48.0;
const double kLeftMenuWidth = 200.0;
const Color kVerticalBarColor = Color(0xffc19277);
const Color kLeftMenuColor = Color(0xffe1bc91);
const Color kContentColor = Color(0xff62959c);

Rotating image based on drag handle in flutter

My end goal is to achieve somethinng like this:
As you can see there's the drag handle is required to rotate this image.
I have a following code:
import 'package:flutter/material.dart';
double ballRadius = 7.5;
void main() {
runApp(MyApp());
}
class MyApp extends StatefulWidget {
#override
_MyAppState createState() => _MyAppState();
}
class _MyAppState extends State<MyApp> {
double _angle = 0.0;
#override
Widget build(BuildContext context) {
return MaterialApp(
debugShowCheckedModeBanner: false,
home: Scaffold(
body: SafeArea(
child: Stack(
children: [
Positioned(
top: 100,
left: 100,
child: Transform.rotate(
angle: _angle,
child: Column(
children: [
Container(
width: 30,
height: 30,
decoration: BoxDecoration(
color: Colors.black,
borderRadius: BorderRadius.circular(30),
),
child: LayoutBuilder(
builder: (context, constraints) {
return GestureDetector(
behavior: HitTestBehavior.translucent,
onPanUpdate: (DragUpdateDetails details) {
Offset centerOfGestureDetector = Offset(
constraints.maxWidth / 2,
constraints.maxHeight / 2,
);
final touchPositionFromCenter =
details.localPosition -
centerOfGestureDetector;
print(touchPositionFromCenter.direction);
setState(() {
_angle = touchPositionFromCenter.direction;
});
},
);
},
),
),
Container(
height: 30,
width: 5,
color: Colors.black,
),
Container(
height: 200,
width: 200,
color: Colors.red,
),
],
),
),
)
],
),
),
),
);
}
}
It is working. But sometimes it's too fast or too slow. Please help me fix this issue.
I made a few modifications to the code, notably
Treating the "real" centerOfGestureDetector as the center of all the items you would like to rotate
Determining and tracking the change in angle with the onPanStart,onPanEnd and onPanUpdate methods
import 'package:flutter/material.dart';
double ballRadius = 7.5;
void main() {
runApp(MyApp());
}
class MyApp extends StatefulWidget {
#override
_MyAppState createState() => _MyAppState();
}
class _MyAppState extends State<MyApp> {
double _angle = 0.0;
double _oldAngle = 0.0;
double _angleDelta = 0.0;
#override
Widget build(BuildContext context) {
return MaterialApp(
debugShowCheckedModeBanner: false,
home: Scaffold(
body: SafeArea(
child: Stack(
children: [
Positioned(
top: 100,
left: 100,
child: Transform.rotate(
angle: _angle,
child: Column(
children: [
Container(
width: 30,
height: 30,
decoration: BoxDecoration(
color: Colors.black,
borderRadius: BorderRadius.circular(30),
),
child: LayoutBuilder(
builder: (context, constraints) {
// Offset centerOfGestureDetector = Offset(
// constraints.maxWidth / 2, constraints.maxHeight / 2);
/**
* using center of positioned element instead to better fit the
* mental map of the user rotating object.
* (height = container height (30) + container height (30) + container height (200)) / 2
*/
Offset centerOfGestureDetector =
Offset(constraints.maxWidth / 2, 130);
return GestureDetector(
behavior: HitTestBehavior.translucent,
onPanStart: (details) {
final touchPositionFromCenter =
details.localPosition -
centerOfGestureDetector;
_angleDelta = _oldAngle -
touchPositionFromCenter.direction;
},
onPanEnd: (details) {
setState(
() {
_oldAngle = _angle;
},
);
},
onPanUpdate: (details) {
final touchPositionFromCenter =
details.localPosition -
centerOfGestureDetector;
setState(
() {
_angle = touchPositionFromCenter.direction +
_angleDelta;
},
);
},
);
},
),
),
Container(
height: 30,
width: 5,
color: Colors.black,
),
Container(
height: 200,
width: 200,
color: Colors.red,
),
],
),
),
)
],
),
),
),
);
}
}

RefreshIndicator not working with SingleChildScrollView as child

I am trying to use RefreshIndicator to reload a list I show on my home screen. The code looks similar to this:
class Home extends StatefulWidget {
#override
_StartHomeState createState() => _StartHomeState();
}
class _StartHomeState extends State<Home> {
EventsList events;
#override
void initState() {
super.initState();
events = EventsList();
}
#override
Widget build(BuildContext context) {
return Scaffold(
resizeToAvoidBottomPadding: false,
body: RefreshIndicator(
onRefresh: resfreshEventList,
child: SingleChildScrollView(
physics: AlwaysScrollableScrollPhysics(),
child: Column(
children: [
HomeTopBar(),
events,
],
),
),
),
);
}
Future<Null> resfreshEventList() async {
await Future.delayed(Duration(seconds: 2));
setState(() {
events = EventsList();
});
return null;
}
}
EventsList is another stateful widget that will call an API and map the response to a list of widgets. I have tried setting the physics property of the SingleChildScrollView as mentioned here: https://github.com/flutter/flutter/issues/22180 but no luck. Using ListView instead of the SingleChildScrollView doesn't work either.
It seems to be working fine in this example When I pull to refresh then resfreshEventList gets fired and also setState is working without any problem.
Here is the code which I am using:
import 'package:flutter/material.dart';
class Home extends StatefulWidget {
#override
_StartHomeState createState() => _StartHomeState();
}
class _StartHomeState extends State<Home> {
// EventsList events;
int number = 0;
#override
// void initState() {
// super.initState();
// events = EventsList();
// }
#override
Widget build(BuildContext context) {
return MaterialApp(
home: Scaffold(
appBar: AppBar(
title: Text("RefreshIndicator Example"),
),
resizeToAvoidBottomPadding: false,
body: RefreshIndicator(
onRefresh: resfreshEventList,
child: SingleChildScrollView(
physics: AlwaysScrollableScrollPhysics(),
child: Column(
children: [
// HomeTopBar(),
// events,
Container(
height: 200,
width: 200,
color: Colors.red,
child: Center(
child: Text(number.toString()),
),
),
Divider(),
Container(
height: 200,
width: 200,
color: Colors.red,
child: Center(
child: Text(number.toString()),
),
),
Divider(),
Container(
height: 200,
width: 200,
color: Colors.red,
child: Center(
child: Text(number.toString()),
),
),
Divider(),
Container(
height: 200,
width: 200,
color: Colors.red,
child: Center(
child: Text(number.toString()),
),
),
Divider(),
Container(
height: 200,
width: 200,
color: Colors.red,
child: Center(
child: Text(number.toString()),
),
)
],
),
),
),
));
}
Future<Null> resfreshEventList() async {
// await Future.delayed(Duration(seconds: 2));
// setState(() {
// events = EventsList();
// });
setState(() {
number = number + 1;
});
print("Refresh Pressed");
return null;
}
}
Output:

flutter - create an elastic effect on animation

I have a container in a Stack widget of height 100.0. It is positioned in the center using a Positioned widget as follows
Container(
width:100.0,
height:100.0,
child: Stack(
fit: StackFit.expand,
children: <Widget>[
Positioned(
top: 40.0,
child: Container(
width: 20.0,
height: 20.0,
color: Colors.red,
),
)
],
)
)
I want to animate the red container in such a way that when clicked it goes to bottom of the parent container and when clicked angain bounces back to top then back to center.
I tried using Curves.elasticOut but that is not enough bounce for me.
How do I acheive this effect
Try this code and let me know if I understand your animation correctly
Edited:
import 'package:flutter/material.dart';
import 'package:flutter/physics.dart';
import 'dart:math';
const BOX_COLOR = Colors.cyan;
void main() => runApp(new MyApp());
class MyApp extends StatelessWidget {
#override
Widget build(BuildContext context) {
return MaterialApp(
title: "Spring Box",
theme: ThemeData(
primaryColor: Colors.red,
),
home: HomePage(),
);
}
}
class HomePage extends StatelessWidget {
#override
Widget build(BuildContext context) {
return Scaffold(
body: Container(
child: Padding(
child: PhysicsBox(),
padding: EdgeInsets.only(
left: 20.0,
right: 20.0,
top: 20.0,
bottom: 20.0,
),
),
));
}
}
class PhysicsBox extends StatefulWidget {
#override
BoxState createState() => BoxState();
}
class BoxState extends State<PhysicsBox> with TickerProviderStateMixin {
AnimationController controller;
AnimationController controller2;
Animation<double> animation;
SpringSimulation simulation;
double _position;
#override
void initState() {
super.initState();
simulation = SpringSimulation(
SpringDescription(
mass: 1.0,
stiffness: 100.0,
damping: 5.0,
),
400.0,
208.0,
-4000.0,
);
controller2 = AnimationController(vsync: this,duration: Duration(milliseconds: 70));
animation = Tween(begin: 200.0, end: 400.0).animate(controller2)
..addListener((){
if(controller2.status == AnimationStatus.completed){controller.reset();}
setState(() {
_position = animation.value;
});
});
controller = AnimationController(vsync: this,duration: Duration(milliseconds: 700))..forward()
..addListener(() {
if(controller.status == AnimationStatus.completed){controller2.reset();}
setState(() {
_position = simulation.x(controller.value);
});
print('${simulation.x(controller.value)}');
});
}
#override
Widget build(BuildContext context) {
return Container(
color: Colors.yellow,
width:500.0,
height:500.0,
child: Stack(
fit: StackFit.expand,
children: <Widget>[
Positioned(
top: _position,
child: GestureDetector(
onTap: (){
if (controller.status == AnimationStatus.completed) {
controller2.forward();//controller.reset();
}else{
controller.forward();}
},
child: Container(
width: 100.0,
height: 100.0,
color: Colors.red,
),
),
)
],
)
);
}
}
Use TweenMax for Flutter: https://pub.dartlang.org/packages/tweenmax
Wrap your container by a GestureDetector to use Tap Gesture, then replace your red container with TweenContainer:
import 'package:flutter/material.dart';
import 'package:flutter/physics.dart';
import 'package:tweenmax/tweenmax.dart';
void main() => runApp(new MyApp());
class MyApp extends StatelessWidget {
#override
Widget build(BuildContext context) {
return MaterialApp(
title: "Spring Box",
theme: ThemeData(
primaryColor: Colors.red,
),
home: HomePage(),
);
}
}
class HomePage extends StatefulWidget {
#override
HomePageState createState() => HomePageState();
}
class HomePageState extends State<HomePage> {
bool isClicked = false;
#override
Widget build(BuildContext context) {
TweenContainer redContainer = TweenContainer(
data: TweenData(
top: 40,
width: 20.0,
height: 20.0,
color: Colors.red,
),
);
return Scaffold(
body: GestureDetector(
child: Container(
width: 100.0,
height: 100.0,
margin: EdgeInsets.only(top: 100, left: 100),
color: Colors.yellow,
child: Stack(
fit: StackFit.expand,
children: <Widget>[
redContainer
],
)
),
onTap: (){
// click first time, animate "redContainer" to bottom:
if(!isClicked){
TweenMax.to(
redContainer,
duration: 0.3,
ease: Curves.ease,
data: TweenData(
top: 80
)
);
} else { // click second time, animate "redContainer" to top:
TweenMax.to(
redContainer,
duration: 0.2,
ease: Curves.easeIn,
data: TweenData(
top: 0
),
onComplete: (redContainer){
// animate it back to center position:
TweenMax.to(
redContainer,
duration: 0.8,
ease: ElasticOutCurve(0.3),
data: TweenData(
top: 40
)
);
}
);
}
isClicked = !isClicked;
},
)
);
}
}
See this video: https://drive.google.com/file/d/1i3BgxWiVna6kQMRKVgUEQDTW2QTlSXWo/view?usp=sharing