I am creating custom navigation drawer. I want add curved animation onItemSelected.
But I have no idea how to create curved animation effect. I have tried many plugins but could not find plugin which related with this design. Here is my example code
import 'package:flutter/material.dart';
import 'package:flutter_vector_icons/flutter_vector_icons.dart';
void main() {
runApp(
MaterialApp(
home: MyApp(),
),
);
}
class MyApp extends StatefulWidget {
#override
_MyAppState createState() => _MyAppState();
}
List<bool> selected = [true, false, false, false, false];
class _MyAppState extends State<MyApp> {
List<IconData> icon = [
Feather.wind,
Feather.folder,
Feather.monitor,
Feather.lock,
Feather.mail,
];
void select(int n) {
for (int i = 0; i < 5; i++) {
if (i == n) {
selected[i] = true;
} else {
selected[i] = false;
}
}
}
#override
Widget build(BuildContext context) {
return Scaffold(
body: Stack(
children: [
Container(
color: Colors.white,
),
Container(
margin: EdgeInsets.all(8.0),
height: MediaQuery.of(context).size.height,
width: 101.0,
decoration: BoxDecoration(
color: Color(0xff332A7C),
borderRadius: BorderRadius.circular(20.0),
),
child: Stack(
children: [
Positioned(
top: 110,
child: Column(
children: icon
.map(
(e) => NavBarItem(
icon: e,
selected: selected[icon.indexOf(e)],
onTap: () {
setState(() {
select(icon.indexOf(e));
});
},
),
)
.toList(),
),
),
],
),
),
],
),
);
}
}
class NavBarItem extends StatefulWidget {
final IconData icon;
final Function onTap;
final bool selected;
NavBarItem({
this.icon,
this.onTap,
this.selected,
});
#override
_NavBarItemState createState() => _NavBarItemState();
}
class _NavBarItemState extends State<NavBarItem> with TickerProviderStateMixin {
AnimationController _controller1;
AnimationController _controller2;
Animation<double> _anim1;
Animation<double> _anim2;
Animation<double> _anim3;
Animation<Color> _color;
bool hovered = false;
#override
void initState() {
super.initState();
_controller1 = AnimationController(
vsync: this,
duration: Duration(milliseconds: 250),
);
_controller2 = AnimationController(
vsync: this,
duration: Duration(milliseconds: 275),
);
_anim1 = Tween(begin: 101.0, end: 75.0).animate(_controller1);
_anim2 = Tween(begin: 101.0, end: 25.0).animate(_controller2);
_anim3 = Tween(begin: 101.0, end: 50.0).animate(_controller2);
_color = ColorTween(end: Color(0xff332a7c), begin: Colors.white)
.animate(_controller2);
_controller1.addListener(() {
setState(() {});
});
_controller2.addListener(() {
setState(() {});
});
}
#override
void didUpdateWidget(NavBarItem oldWidget) {
super.didUpdateWidget(oldWidget);
if (!widget.selected) {
Future.delayed(Duration(milliseconds: 10), () {
//_controller1.reverse();
});
_controller1.reverse();
_controller2.reverse();
} else {
_controller1.forward();
_controller2.forward();
Future.delayed(Duration(milliseconds: 10), () {
//_controller2.forward();
});
}
}
#override
Widget build(BuildContext context) {
return GestureDetector(
onTap: () {
widget.onTap();
},
child: MouseRegion(
onEnter: (value) {
setState(() {
hovered = true;
});
},
onExit: (value) {
setState(() {
hovered = false;
});
},
child: Container(
width: 101.0,
color:
hovered && !widget.selected ? Colors.white12 : Colors.transparent,
child: Stack(
children: [
Container(
height: 80.0,
width: 101.0,
child: Center(
child: Icon(
widget.icon,
color: _color.value,
size: 18.0,
),
),
),
],
),
),
),
);
}
}
I want create Navigation Drawer like this image given in above example. How can I achieve this please help me for it Thank you in advance.
Related
it has three container when i tap on it it change to next flip I tried with flip_card package and it has only front and back but I need extra one
flip_board (https://pub.dev/packages/flip_board#middle-flip) package does exactly what you want.
Sample Code : -
FlipWidget({
flipType: FlipType.spinFlip, // Change flip effect
itemStream: _stream, // it is basically the length of Stacked Widgets.
itemBuilder: _itemBuilder, // Body of your Widget
flipDirection: AxisDirection.right, // flip up, down, left, right.
});
This package has many more methods and Widget types, to learn more about it vist the official page : - https://pub.dev/packages/flip_board#middle-flip
Complete Code : -
import 'dart:async';
import 'package:flip_board/flip_widget.dart';
import 'package:flutter/material.dart';
void main() => runApp(const MaterialApp(home: FlipPage()));
class FlipPage extends StatefulWidget {
const FlipPage({super.key});
#override
State<FlipPage> createState() => _FlipPageState();
}
class _FlipPageState extends State<FlipPage> {
#override
Widget build(BuildContext context) {
var spinController = StreamController<int>.broadcast();
bool containerClicked = false;
int nextSpinValue = 0;
int? widgetIndex = 0;
void spin() => spinController.add(++nextSpinValue);
return Scaffold(
body: Center(
child: FlipWidget(
initialValue: nextSpinValue,
itemStream: spinController.stream,
flipType: FlipType.spinFlip,
itemBuilder: (_, index) {
return GestureDetector(
onTap: (() async {
if (!containerClicked) {
containerClicked = true;
widgetIndex = index as int?;
if (widgetIndex! < 2) {
spin();
} else {
nextSpinValue = 0;
spinController.add(nextSpinValue);
}
await Future.delayed(const Duration(milliseconds: 500));
containerClicked = false;
}
}),
child: Container(
color: index == 0
? Colors.red[100]
: index == 1
? Colors.amber[100]
: Colors.blue[100],
height: 200,
width: 200,
alignment: Alignment.center,
child: Text(
'Widget $index',
style: const TextStyle(fontSize: 18),
)));
},
flipDirection: AxisDirection.up,
),
),
);
}
}
Output : -
As my opinion you should use animationOpacity like this :
i made one demo for you please check it.
class MyHomePage extends StatefulWidget {
const MyHomePage({Key? key, this.title}) : super(key: key);
final String? title;
#override
_MyHomePageState createState() => _MyHomePageState();
}
class _MyHomePageState extends State<MyHomePage> {
bool _showFirstSide = false;
bool _showSecondSide = false;
bool _showThirdSide = false;
#override
void initState() {
super.initState();
_showFirstSide = true;
_showSecondSide = false;
_showThirdSide = false;
}
#override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: const Text(""),
centerTitle: true,
),
body: Center(
child: SizedBox(
height: 100,
width: 100,
// constraints: BoxConstraints.tight(size),
child: _buildFlipAnimation(),
),
),
);
}
void _switchCard() {
setState(() {
if (_showFirstSide) {
_showFirstSide = !_showFirstSide;
_showSecondSide = true;
_showFirstSide = false;
} else if (_showSecondSide) {
_showSecondSide = !_showSecondSide;
_showThirdSide = true;
_showFirstSide = false;
} else if (_showThirdSide) {
_showThirdSide = !_showThirdSide;
_showFirstSide = true;
_showSecondSide = false;
}
});
}
Widget _buildFlipAnimation() {
return GestureDetector(
onTap: _switchCard,
child: Stack(
children: [
AnimatedOpacity(
curve: Curves.easeInBack,
duration: const Duration(milliseconds: 700),
opacity: _showFirstSide ? 1 : 0,
child: Container(
color: Colors.black,
height: MediaQuery.of(context).size.height / 2,
width: MediaQuery.of(context).size.width / 2,
),
),
AnimatedOpacity(
curve: Curves.easeIn.flipped,
duration: const Duration(milliseconds: 700),
opacity: _showSecondSide ? 1 : 0,
child: Container(
color: Colors.blue,
height: MediaQuery.of(context).size.height / 2,
width: MediaQuery.of(context).size.width / 2,
),
),
AnimatedOpacity(
curve: Curves.easeOut,
duration: const Duration(milliseconds: 400),
opacity: _showThirdSide ? 1 : 0,
child: Container(
color: Colors.pink,
height: MediaQuery.of(context).size.height / 2,
width: MediaQuery.of(context).size.width / 2,
),
),
],
),
);
}
}
Stackoverflowers!
I'm using an BottomAppBar inside the bottomNavigationBar section of the Scaffold. The problem is that it doesn't persists while I'm navigating. I used the persistent_bottom_nav_bar plugin, but it doesn't work with my custom navigation bar because it has a ripple animation in one button and a bottomSheet that is over the keyboard.
home_page.dart
This file has the CustomNavigationBar and the main pages for each item on it.
class HomePage extends StatefulWidget {
const HomePage({super.key});
#override
State<HomePage> createState() => _HomePageState();
}
class _HomePageState extends State<HomePage> with TickerProviderStateMixin {
NavigationProvider? navigationProvider;
AnimationController? rippleController;
AnimationController? scaleController;
Animation<double>? rippleAnimation;
Animation<double>? scaleAnimation;
#override
void initState() {
super.initState();
rippleController = AnimationController(
vsync: this, duration: const Duration(milliseconds: 500));
scaleController = AnimationController(
vsync: this, duration: const Duration(milliseconds: 500))
..addStatusListener((status) {
if (status == AnimationStatus.completed) {
scaleController!.reverse();
Navigator.push(
context,
PageTransition(
type: PageTransitionType.bottomToTop,
child: pages.elementAt(2),
childCurrent: widget,
fullscreenDialog: true,
)).whenComplete(() => setState(() {
buttonColor = Colors.black;
}));
}
});
rippleAnimation =
Tween<double>(begin: 80.0, end: 90.0).animate(rippleController!)
..addStatusListener((status) {
if (status == AnimationStatus.completed) {
rippleController!.reverse();
} else if (status == AnimationStatus.dismissed) {
rippleController!.forward();
}
});
scaleAnimation =
Tween<double>(begin: 1.0, end: 30.0).animate(scaleController!);
rippleController!.forward();
}
#override
void dispose() {
rippleController!.dispose();
scaleController!.dispose();
super.dispose();
}
#override
Widget build(BuildContext context) {
navigationProvider = Provider.of<NavigationProvider>(context);
return Scaffold(
body:
pages.elementAt(navigationProvider!.bottomNavigationBarSelectedIndex),
bottomNavigationBar: CustomNavigationBar(
rippleController: rippleController,
scaleController: scaleController,
rippleAnimation: rippleAnimation,
scaleAnimation: scaleAnimation),
);
}
}
custom_navigation_bar.dart
This file contains the properties of the CustomNavigationBar.
class CustomNavigationBar extends StatefulWidget {
const CustomNavigationBar({
super.key,
this.rippleController,
this.scaleController,
this.rippleAnimation,
this.scaleAnimation,
});
final AnimationController? rippleController;
final AnimationController? scaleController;
final Animation<double>? rippleAnimation;
final Animation<double>? scaleAnimation;
#override
State<CustomNavigationBar> createState() => _CustomNavigationBarState();
}
class _CustomNavigationBarState extends State<CustomNavigationBar> {
#override
Widget build(BuildContext context) {
final navigationProvider = Provider.of<NavigationProvider>(context);
return BottomAppBar(
child: IconTheme(
data: const IconThemeData(color: Colors.black, size: 36),
child: Padding(
padding: const EdgeInsets.fromLTRB(10, 5, 10, 5),
child: Row(
mainAxisSize: MainAxisSize.max,
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: [
Wrap(
crossAxisAlignment: WrapCrossAlignment.center,
direction: Axis.vertical,
children: [
IconButton(
icon: ...,
padding: ...,
constraints: ...,
onPressed: () {
//Here I change the selected index with Provider.
...
},
),
Text(
title,
style: ...,
),
],
),
const Spacer(),
Wrap(...),
const Spacer(),
InkWell(
onTap: () {
setState(
() {
//Executes the ripple animation.
widget.scaleController!.forward();
},
);
},
child: AnimatedBuilder(
animation: widget.scaleAnimation!,
builder: (context, child) => Transform.scale(
scale: widget.scaleAnimation!.value,
child: Container(
width: 50,
height: 50,
margin: const EdgeInsets.all(10),
decoration: const BoxDecoration(
shape: BoxShape.circle, color: Colors.blue),
child: Icon(Icons.add,
color: widget.scaleAnimation!.value == 1.0
? Colors.white
: Colors.blue),
),
),
),
),
const Spacer(),
Wrap(...),
const Spacer(),
Wrap(...),
],
),
),
),
);
}
}
As you can see, I use Provider to manage the state of the CustomNavigationBar when it changes the index.
Example of what I want:
This app is Splitwise and it has some pages with the navigation bar and others without it. That ripple animation is similar to mine. Also the bottom sheet has the same effect in my app.
I'll wait for all your suggestions, thanks!
Trying to get desired output of the Animation above. Tried with AnimatedBuilder along with Transform of Matrix of z-axis for this animation. But failed to do so. Here's my code. I couldn't get the angle, shrinking or the shaking part right.
MyHomePage
class MyHomePage extends StatefulWidget {
const MyHomePage({Key? key}) : super(key: key);
#override
_MyHomePageState createState() => _MyHomePageState();
}
class _MyHomePageState extends State<MyHomePage> with TickerProviderStateMixin {
late TabController _tabController;
late void Function() _memberCaller;
late void Function() _voucherCaller;
late void Function() _rewardCaller;
late void Function() _drinksCaller;
double degree = 0;
#override
void initState() {
super.initState();
_tabController = TabController(length: 4, vsync: this);
}
#override
void dispose() {
_tabController.dispose();
super.dispose();
}
void _callMethoCaller(int index) {
switch (index) {
case 0:
_memberCaller.call();
break;
case 1:
_voucherCaller.call();
break;
case 2:
_rewardCaller.call();
break;
case 3:
_drinksCaller.call();
break;
}
}
#override
Widget build(BuildContext context) {
List<Widget> _tabs(int index) => [
AnimationTab(
iconName: Icons.card_giftcard,
label: 'Membership',
functionCaller: (void Function() method) {
_memberCaller = method;
}),
AnimationTab(
iconName: Icons.confirmation_num_outlined,
label: 'Voucher',
functionCaller: (void Function() method) {
_voucherCaller = method;
}),
AnimationTab(
iconName: Icons.emoji_events_outlined,
label: 'Rewards',
functionCaller: (void Function() method) {
_rewardCaller = method;
}),
AnimationTab(
iconName: Icons.wine_bar_outlined,
label: 'Drinks',
functionCaller: (void Function() method) {
_drinksCaller = method;
}),
];
return Scaffold(
appBar: AppBar(
centerTitle: true,
bottom: PreferredSize(
child: Container(
child: TabBar(
onTap: _callMethoCaller,
controller: _tabController,
labelPadding: EdgeInsets.only(top: 5.0, bottom: 2.0),
indicatorColor: Colors.black,
tabs: List.generate(4, (index) => _tabs(index)[index]),
),
decoration: BoxDecoration(
color: Colors.white,
boxShadow: const [
BoxShadow(
color: Colors.white,
spreadRadius: 5.0,
offset: Offset(0, 3))
],
),
),
preferredSize: Size.fromHeight(30),
),
),
body: TabBarView(
controller: _tabController,
children: const [
Center(child: Text('1')),
Center(child: Text('2')),
Center(child: Text('3')),
Center(child: Text('4')),
],
),
);
}
}
AnimationTab
class AnimationTab extends StatefulWidget {
final String label;
final IconData iconName;
final FunctionCaller functionCaller;
const AnimationTab({
Key? key,
required this.iconName,
required this.label,
required this.functionCaller,
}) : super(key: key);
#override
_AnimationTabState createState() => _AnimationTabState();
}
class _AnimationTabState extends State<AnimationTab>
with TickerProviderStateMixin {
late AnimationController _animationController;
late Animation<double> _animation;
#override
void initState() {
super.initState();
_animationController = AnimationController(
vsync: this,
duration: Duration(milliseconds: 500),
);
final _curvedAnimation = CurvedAnimation(
parent: _animationController,
curve: Curves.bounceIn,
reverseCurve: Curves.bounceOut);
_animation = TweenSequence<double>([
TweenSequenceItem<double>(tween: Tween(begin: 0, end: 12.5), weight: 1),
TweenSequenceItem<double>(tween: Tween(begin: 12.5, end: 0), weight: 1),
TweenSequenceItem<double>(tween: Tween(begin: 0, end: -12.5), weight: 1),
]).animate(_curvedAnimation)
..addListener(() {
setState(() {});
})
..addStatusListener((status) {
if (status == AnimationStatus.completed) {
_animationController.reverse();
}
});
}
void _animationExecution() {
_animationController.forward();
}
#override
Widget build(BuildContext context) {
const _tabTextStyle = TextStyle(
fontWeight: FontWeight.w300, fontSize: 12, color: Colors.black);
widget.functionCaller.call(_animationExecution);
return AnimatedBuilder(
animation: _animationController,
builder: (ctx, _) {
return Transform(
transform: Matrix4.rotationZ(math.pi * _animation.value / 180),
alignment: Alignment.center,
child: Tab(
icon: Icon(widget.iconName, color: Colors.black),
child: Text(widget.label, style: _tabTextStyle),
),
);
},
);
}
}
you dont need those TweenSequences, "status listeners" etc, also instead of Matrix4.rotationZ use ordinary Transform.rotate, check this:
class TabTest extends StatefulWidget {
#override
_TabTestState createState() => _TabTestState();
}
class _TabTestState extends State<TabTest> with TickerProviderStateMixin {
late TabController tabController;
late List<AnimationController> animationControllers;
#override
void initState() {
super.initState();
tabController = TabController(length: 4, vsync: this)
..addListener(_listener);
animationControllers = List.generate(4, (i) => AnimationController(
vsync: this,
duration: Duration(milliseconds: 750),
reverseDuration: Duration(milliseconds: 350),
));
}
#override
Widget build(BuildContext context) {
// timeDilation = 5;
return Scaffold(
appBar: AppBar(
bottom: TabBar(
tabs: List.generate(4, (i) => AnimatedBuilder(
animation: animationControllers[i],
builder: (context, child) {
final child = Tab(
icon: Icon(Icons.cast),
child: Text('tab $i'),
);
final value = animationControllers[i].value;
if (animationControllers[i].status == AnimationStatus.forward) {
final angle = sin(4 * pi * value) * pi * 0.3;
return Transform.rotate(angle: angle, child: child);
} else {
final dy = sin(2 * pi * value) * 0.2;
return FractionalTranslation(translation: Offset(0, dy), child: child);
}
},
),
),
controller: tabController,
),
),
body: TabBarView(
children: List.generate(4, (i) =>
FittedBox(
child: Text('tab $i'),
),
),
controller: tabController,
),
);
}
void _listener() {
if (tabController.indexIsChanging) {
animationControllers[tabController.previousIndex].reverse();
} else {
animationControllers[tabController.index].forward();
}
}
#override
void dispose() {
super.dispose();
tabController
..removeListener(_listener)
..dispose();
animationControllers.forEach((ac) => ac.dispose());
}
}
This is a widget for my quiz app. I am currently working on the quiz option button.
I want it to blink green or red whether the answer is true or not.
But it's not working. I tried different things but I couldn't succeed.
import 'package:flutter/material.dart';
class TestButton extends StatefulWidget {
TestButton({this.text, this.color, this.onPressed});
final Function onPressed;
final Color color;
final String text;
#override
_TestButtonState createState() => _TestButtonState();
}
class _TestButtonState extends State<TestButton> with TickerProviderStateMixin {
AnimationController _animationController;
Animation _animation;
void initState() {
super.initState();
_animationController =
AnimationController(vsync: this, duration: Duration(milliseconds: 400));
_animation = ColorTween(begin: Colors.transparent, end: widget.color)
.animate(CurvedAnimation(curve: Curves.decelerate, parent: _animationController))
..addListener(() {
setState(() {
});
});
_animationController.addStatusListener((status) {
if (status == AnimationStatus.completed) {
_animationController.reverse();
} else if (status == AnimationStatus.dismissed) {
_animationController.forward();
}
});
_animationController.forward();
}
#override
void dispose() {
_animationController.dispose();
super.dispose();
}
#override
Widget build(BuildContext context) {
double width = MediaQuery.of(context).size.width;
return SizedBox(
width: width / 1.3,
child: InkWell(
borderRadius: BorderRadius.circular(30),
onTap: widget.onPressed,
child: Container(
child: Container(
alignment: Alignment.center,
padding: EdgeInsets.all(width / 20),
decoration: BoxDecoration(
color: _animation.value,
border: Border.all(color: Colors.white38),
borderRadius: BorderRadius.circular(20)),
child: Text(
widget.text,
style: TextStyle(fontSize: 16, fontFamily: "Fondamento"),
),
),
),
),
);
}
}
And this is the usage of the button. I am applying a setState function but it doesn't make sense. The problem is that the animation works but the setState function doesn't change the color value in the ColorTween
TestButton(
text: question.optionA,
color: colorA,//Colors.transparent
onPressed: () {
if (canTap) {
canTap = false;
if (question.answer == 1) {
setState(() {
colorA = Colors.green;
});
} else {
setState(() {
colorA = Colors.red;
});
}
}
},
),
Remove this part:
..addListener(() {
setState(() {
});
});
And use AnimatedBuilder like this:
return SizedBox(
width: width / 1.3,
child: InkWell(
borderRadius: BorderRadius.circular(30),
onTap: widget.onPressed,
child: Container(
child: AnimatedBuilder(
animation: _animationController,
builder: (_, __) => Container(
alignment: Alignment.center,
padding: EdgeInsets.all(width / 20),
decoration: BoxDecoration(
color: _animationController.value,
border: Border.all(color: Colors.white38),
borderRadius: BorderRadius.circular(20)),
child: Text(
widget.text,
style: TextStyle(fontSize: 16, fontFamily: "Fondamento"),
),
)
),
),
),
);
You can copy paste run full code below
You can use didUpdateWidget to reset _animation
In working demo, when click button, color changes from red to green
#override
void didUpdateWidget(covariant TestButton oldWidget) {
if (oldWidget.color != widget.color) {
_animation = ColorTween(begin: Colors.transparent, end: widget.color)
.animate(CurvedAnimation(
curve: Curves.decelerate, parent: _animationController))
..addListener(() {
setState(() {});
});
}
super.didUpdateWidget(oldWidget);
}
working demo
full code
import 'package:flutter/material.dart';
class TestButton extends StatefulWidget {
TestButton({this.text, this.color, this.onPressed});
final Function onPressed;
final Color color;
final String text;
#override
_TestButtonState createState() => _TestButtonState();
}
class _TestButtonState extends State<TestButton> with TickerProviderStateMixin {
AnimationController _animationController;
Animation _animation;
void initState() {
super.initState();
_animationController =
AnimationController(vsync: this, duration: Duration(milliseconds: 400));
_animation = ColorTween(begin: Colors.transparent, end: widget.color)
.animate(CurvedAnimation(
curve: Curves.decelerate, parent: _animationController))
..addListener(() {
setState(() {});
});
_animationController.addStatusListener((status) {
if (status == AnimationStatus.completed) {
_animationController.reverse();
} else if (status == AnimationStatus.dismissed) {
_animationController.forward();
}
});
_animationController.forward();
}
#override
void didUpdateWidget(covariant TestButton oldWidget) {
if (oldWidget.color != widget.color) {
_animation = ColorTween(begin: Colors.transparent, end: widget.color)
.animate(CurvedAnimation(
curve: Curves.decelerate, parent: _animationController))
..addListener(() {
setState(() {});
});
}
super.didUpdateWidget(oldWidget);
}
#override
void dispose() {
_animationController.dispose();
super.dispose();
}
#override
Widget build(BuildContext context) {
double width = MediaQuery.of(context).size.width;
return SizedBox(
width: width / 1.3,
child: InkWell(
borderRadius: BorderRadius.circular(30),
onTap: widget.onPressed,
child: Container(
child: Container(
alignment: Alignment.center,
padding: EdgeInsets.all(width / 20),
decoration: BoxDecoration(
color: _animation.value,
border: Border.all(color: Colors.white38),
borderRadius: BorderRadius.circular(20)),
child: Text(
widget.text,
style: TextStyle(fontSize: 16, fontFamily: "Fondamento"),
),
),
),
),
);
}
}
void main() {
runApp(MyApp());
}
class MyApp extends StatelessWidget {
#override
Widget build(BuildContext context) {
return MaterialApp(
title: 'Flutter Demo',
theme: ThemeData(
primarySwatch: Colors.blue,
),
home: MyHomePage(title: 'Flutter Demo Home Page'),
);
}
}
class MyHomePage extends StatefulWidget {
MyHomePage({Key key, this.title}) : super(key: key);
final String title;
#override
_MyHomePageState createState() => _MyHomePageState();
}
class _MyHomePageState extends State<MyHomePage> {
Color colorA = Colors.red;
bool canTap = true;
int answer = 1;
#override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: Text(widget.title),
),
body: Center(
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: <Widget>[
TestButton(
text: "question.optionA",
color: colorA, //Colors.transparent
onPressed: () {
if (canTap) {
canTap = false;
if (answer == 1) {
setState(() {
colorA = Colors.green;
});
} else {
setState(() {
colorA = Colors.red;
});
}
}
},
),
],
),
),
);
}
}
in this simple code, i try to show and hide barrier on top of widgets,showing this barrier can be have with animation, but when i try to close and hide that, controller.reverse() doesn't have any animation to hide
import 'package:flutter/material.dart';
void main() => runApp(MaterialApp(
home: BarrierEffect(),
));
class BarrierEffect extends StatefulWidget {
#override
State<BarrierEffect> createState() => _BarrierEffect();
}
class _BarrierEffect extends State<BarrierEffect> with TickerProviderStateMixin {
var isShownBarrier = false;
AnimationController controller;
Animation<double> animation;
#override
void initState() {
super.initState();
controller = AnimationController(duration: const Duration(milliseconds: 1000), vsync: this);
animation = CurvedAnimation(parent: controller, curve: Curves.easeIn);
animation.addStatusListener((status) {
if (status == AnimationStatus.completed) {
controller.reverse();
setState(() {
isShownBarrier = false;
});
} else if (status == AnimationStatus.dismissed) {
controller.forward();
setState(() {
isShownBarrier = true;
});
}
});
}
#override
Widget build(BuildContext context) {
return Scaffold(
body: Container(
child: Stack(
children: <Widget>[
Center(
child: RaisedButton(
color: Colors.white,
child: Text('show barrier'),
onPressed: () => controller.forward(),
shape: RoundedRectangleBorder(borderRadius: BorderRadius.circular(50.0))),
),
Visibility(
visible: isShownBarrier ? true : false,
child: FadeTransition(
opacity: animation,
child: Container(
color: Colors.black.withOpacity(0.5),
child: Center(child: Text('test')),
),
),
)
],
),
),
);
}
}
Is this what you are looking for?
Full code:
void main() => runApp(MaterialApp(home: BarrierEffect()));
class BarrierEffect extends StatefulWidget {
#override
State<BarrierEffect> createState() => _BarrierEffect();
}
class _BarrierEffect extends State<BarrierEffect> with TickerProviderStateMixin {
AnimationController controller;
#override
void initState() {
super.initState();
controller = AnimationController(duration: const Duration(milliseconds: 2000), vsync: this);
}
#override
Widget build(BuildContext context) {
return Scaffold(
body: AnimatedBuilder(
animation: controller,
builder: (_, child) {
return Stack(
children: <Widget>[
Center(
child: RaisedButton(
child: Text('Show Barrier'),
onPressed: () => controller.repeat(reverse: true),
shape: RoundedRectangleBorder(borderRadius: BorderRadius.circular(50.0)),
),
),
Visibility(
visible: controller.value != 0,
child: Opacity(
opacity: controller.value,
child: Container(
color: Colors.black.withOpacity(0.9),
child: Center(child: Text('My Barrier', style: TextStyle(color: Colors.white))),
),
),
)
],
);
},
),
);
}
}