Re-trigger Fade Transition - flutter

I am trying to use FadeTransition to fade in a picture and some text after pressing a button. So far this is working fine, however, on subsequent presses of the button I want a new picture and text to Fade in. Right now the first picture and text fades in, but subsequent ones do not.
Here is my code so far:
import 'package:flutter/material.dart';
class SurpriseReveal extends StatefulWidget {
final Widget child;
final int surpriseNumber;
final List<Map<String, String>> surprises;
final Function randomSurprise;
SurpriseReveal(
{#required this.surpriseNumber,
#required this.surprises,
#required this.randomSurprise,
#required this.child});
#override
_SurpriseRevealState createState() => _SurpriseRevealState();
}
class _SurpriseRevealState extends State<SurpriseReveal> with SingleTickerProviderStateMixin{
AnimationController _controller;
Animation _animation;
#override
void initState() {
_controller = AnimationController(
vsync: this,
duration: Duration(seconds: 2),
);
_animation = Tween(
begin: 0.0,
end: 1.0,
).animate(_controller);
}
#override
dispose(){
_controller.dispose();
super.dispose();
}
#override
Widget build(BuildContext context) {
_controller.forward();
return Container(
child: Stack(
children: <Widget>[
Scaffold(
floatingActionButtonLocation: FloatingActionButtonLocation.endFloat,
floatingActionButton: FloatingActionButton(
child: const Icon(Icons.share),
splashColor: Colors.pink[900],
backgroundColor: Colors.pink[300],
onPressed: () {},
),
),
Positioned(
bottom: 300,
top: 40,
right: 40,
left: 40,
child: FadeTransition(
opacity: _animation,
child: Container(
decoration: BoxDecoration(
border: Border.all(
color: Colors.pink[300],
width: 4,
),
borderRadius: BorderRadius.circular(300),
),
child: Padding(
padding: const EdgeInsets.all(40.0),
child: Text(
widget.surprises[widget.surpriseNumber]['Surprises'],
style: TextStyle(fontSize: 20, color: Colors.pink[700]),
),
),
alignment: Alignment.topCenter,
),
),
),
Positioned(
bottom: 380,
top: 120,
right: 70,
left: 70,
child: FadeTransition(
opacity: _animation,
child: Container(
decoration: BoxDecoration(
image: DecorationImage(
image: new AssetImage(
widget.surprises[widget.surpriseNumber]['Image'] ),
fit: BoxFit.fill,
),
border: Border.all(
color: Colors.pink[300],
width: 2,
),
borderRadius: BorderRadius.circular(12),
),
//child: Image.asset(surprises[surpriseNumber]['Image']),
),
),
),
Positioned(
bottom: -60,
top: 400,
right: 120,
left: 120,
child: FloatingActionButton(
onPressed: widget.randomSurprise,
splashColor: Colors.pink[900],
backgroundColor: Colors.pink[300],
child: Container(
// decoration property gives it a color and makes it round like a floating action button
// add your desired padding here
// add the child element
child: Center(
child: Text(
'Surprise me again!',
// style of the text
textAlign: TextAlign.center,
style: TextStyle(
fontSize: 22.0,
color: Colors.pink[900],
),
),
),
),
),
),
],
overflow: Overflow.visible,
),
);
}
}
I would assume I need my FAB that triggers a new picture and text to also trigger the animation again, but I am unsure how to do this?

Try this:
FloatingActionButton(
onPressed: {widget.randomSurprise; _controller.reset();},
......
If you want your animation controller to play the animation again, you need to reset it.

Related

Ink ripple effect when using GestureDetector

I need to use a GestureDetector because it can detect many more types of user interactions than InkWell, but unlike InkWell it doesn't provide any visual response when a user taps or long presses on it.
Is it possible to add a ripple effect for tap/long press while still handling user interactions in the GestureDetector?
Just use this plugin
touch_ripple_effect: ^2.2.4
Touch ripple effect
TouchRippleEffect(
borderRadius: _helloRadius,
rippleColor: Colors.white60,
onTap: (){
print("Anand !");
},
child: Container(
width: 110,
height: 50,
alignment: Alignment.center,
decoration: BoxDecoration(color: Colors.pink, borderRadius: _helloRadius),
child: IconButton(
iconSize: 24.0,
icon: Icon(Icons.search,color: Colors.white, size: 36,),
onPressed: null
),)
),
Touch Feedback effect
TouchFeedback(
onTap: (){
print(" I am Aditya");
},
rippleColor: Colors.blue[200],
child: Container(
width: 120,
height: 40,
alignment: Alignment.center,
decoration: BoxDecoration(color: Colors.yellow, borderRadius: BorderRadius.circular(5),),
child: Text(
"Hit me !",
style: TextStyle(fontSize: 20, fontWeight: FontWeight.bold)
)
),
)
if you want a quick hack, check this:
class Test extends StatelessWidget {
const Test({
Key? key,
this.onTap,
}) : super(key: key);
final void Function()? onTap;
#override
Widget build(BuildContext context) {
final width = MediaQuery.of(context).size.width;
return Scaffold(
body: GestureDetector(
onTap: onTap,
child: SizedBox(
width: width * 0.2,
height: 70,
child: Card(
color: Colors.red,
child: InkWell(
onTap: () {},
),
),
),
),
);
}
}
Try the following code:
InkWell(
onLongPress: () {
// Do what you want to do
},
child: child,
),

Tutorial Coach Mark package flutter - Opacity not showing, screen flashing and skipping not working

I am trying to use the Tutorial Coach Mark package to make the tutorial screen for my app. Though I have 3 problems:
the opacity of the screen color is not working
the text of the tutorial under the highlighted target keeps flashing
skip function is not working when clicking
I have followed each step of this tutorial
https://www.youtube.com/watch?v=nBdPl6jjsoU
but it is not working.
Here is my code. The related part is in the MusicScreen class.
final List<String> Url_list = [
// testa:
'https://www.youtube.com/watch?v=38rQO3dMZrc',
// third eye:
'https://www.youtube.com/watch?v=s2ddLEfSEIA',
//gola
'https://www.youtube.com/watch?v=QJs4iCV-g6I',
// cuore
'https://www.youtube.com/watch?v=q1Ng7FgDsNQ'
// addome
'https://www.youtube.com/watch?v=8s8R8vR53n4',
// sacral
'https://www.youtube.com/watch?v=ueV0WPUUAj0',
// root
'https://www.youtube.com/watch?v=OMPaU5L3zy0'
];
Future<void> _launchURL(String url) async {
if (await canLaunch(url)) {
await launch(
url,
forceSafariVC: false,
);
} else {
throw 'Could not launch $url';
}
print('cant launch ulr');
}
class MusicScreen extends StatefulWidget {
static const routeName = '/music-screen';
#override
_MusicScreenState createState() => _MusicScreenState();
}
class _MusicScreenState extends State<MusicScreen> {
final user = FirebaseAuth.instance.currentUser!;
bool isHover = false;
TutorialCoachMark? tutorialCoachMark;
List<TargetFocus> targets = [];
GlobalKey key1 = GlobalKey();
#override
void initState() {
initTarget();
WidgetsBinding.instance.addTimingsCallback(_afterLayout);
super.initState();
}
void _afterLayout(_) {
Future.delayed(Duration(milliseconds: 100));
showTutorial();
}
void showTutorial() {
tutorialCoachMark = TutorialCoachMark(
textSkip: 'SKIP TUTORIAL',
paddingFocus: 10,
opacityShadow: 0.8,
targets: targets,
)..show(context: context);
}
void initTarget() {
targets.add(
TargetFocus(
color: AppColors.darkPink,
identify: 'target 0',
keyTarget: key1,
contents: [
TargetContent(
child: Container(
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: [
SizedBox(
height: 5,
),
Text(
'Tap any of the Chakra to get your music',
style: TextStyle(
fontSize: 15,
fontWeight: FontWeight.bold,
color: Colors.white),
),
],
),
),
),
],
),
);
}
#override
Widget build(BuildContext context) {
return Scaffold(
endDrawer: MainDrawer(
onTap: () {},
),
backgroundColor: AppColors.lightPink,
appBar: AppBar(
shadowColor: AppColors.lightPink,
backgroundColor: AppColors.midPink,
title: Text(
'MUSICENERGY',
style: Theme.of(context).appBarTheme.textTheme!.headline6,
),
),
body: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: [
Expanded(
child: Stack(
children: [
Container(
padding: EdgeInsets.only(top: 70),
child: Image.asset('images/Meditation.png'),
),
Positioned(
top: -55,
left: 95,
child: Container(
padding: EdgeInsets.all(10),
height: 200,
width: 200,
child: Image.asset('images/testa.png'),
),
),
Positioned(
key: key1,
top: 30,
left: 172,
child: PulsatingCircleIconButton(
() {
_launchURL(Url_list[0]);
},
),
),
Positioned(
top: 30,
left: 90,
child: Container(
padding: EdgeInsets.all(10),
height: 200,
width: 200,
child: Image.asset('images/occhio.png'),
),
),
Positioned(
top: 115,
left: 174,
child: PulsatingCircleIconButton(
() {
_launchURL(Url_list[1]);
},
),
),
Positioned(
top: 110,
left: 105,
child: Container(
padding: EdgeInsets.all(10),
height: 200,
width: 200,
child: Image.asset('images/gola.png'),
),
),
Positioned(
top: 195,
left: 177,
child: PulsatingCircleIconButton(
() {
_launchURL(Url_list[2]);
},
),
),
Positioned(
top: 180,
left: 90,
child: Container(
padding: EdgeInsets.all(30),
height: 200,
width: 200,
child: Image.asset('images/cuore.png'),
),
),
Positioned(
top: 265,
left: 178,
child: PulsatingCircleIconButton(
() {
_launchURL(Url_list[3]);
},
),
),
Positioned(
top: 220,
left: 50,
child: Container(
padding: EdgeInsets.all(30),
height: 250,
width: 250,
child: Image.asset('images/addome.png'),
),
),
Positioned(
top: 330,
left: 177,
child: PulsatingCircleIconButton(
() {
_launchURL(Url_list[4]);
},
),
),
Positioned(
top: 317,
left: 103,
child: Container(
padding: EdgeInsets.all(15),
height: 200,
width: 200,
child: Image.asset('images/sacro.png'),
),
),
Positioned(
top: 402,
left: 177,
child: PulsatingCircleIconButton(
() {
_launchURL(Url_list[5]);
},
),
),
Positioned(
top: 385,
left: 109,
child: Container(
padding: EdgeInsets.all(15),
height: 200,
width: 200,
child: Image.asset('images/root.png'),
),
),
Positioned(
top: 470,
left: 178,
child: PulsatingCircleIconButton(
() {
_launchURL(Url_list[6]);
},
),
),
],
),
),
],
),
);
}
// refresh() {
// setState(() {});
}
class MainDrawer extends StatelessWidget {
final FirebaseServices _auth = FirebaseServices();
final user = FirebaseAuth.instance.currentUser!;
final Function()? onTap;
MainDrawer({required this.onTap});
#override
Widget build(BuildContext context) {
return Drawer(
child: Column(
children: [
Container(
height: 95,
width: double.infinity,
padding: EdgeInsets.all(25),
color: AppColors.darkPink,
alignment: Alignment.centerLeft,
child: Row(children: [
CircleAvatar(
radius: 25,
backgroundImage: NetworkImage(user.photoURL!),
),
SizedBox(
width: 12,
),
Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Text(
user.displayName!,
style: (TextStyle(
fontWeight: FontWeight.bold,
fontSize: 20,
color: Colors.black)),
),
Text(
user.email!,
),
],
),
]),
),
SizedBox(
height: 10,
),
ListTile(
leading: Icon(Icons.star),
title: Text(
'Favourites',
style: TextStyle(
fontSize: 20,
fontWeight: FontWeight.bold,
),
),
onTap: onTap,
),
ListTile(
leading: Icon(
Icons.exit_to_app,
),
title: Text(
'Log Out',
style: TextStyle(
fontSize: 20,
fontWeight: FontWeight.bold,
),
),
onTap: () {
Navigator.pushNamed(context, AuthScreen.routeName);
_auth.signOut();
},
),
],
),
);
}
}
class PulsatingCircleIconButton extends StatefulWidget {
final GestureTapCallback onTap;
PulsatingCircleIconButton(this.onTap);
#override
_PulsatingCircleIconButtonState createState() =>
_PulsatingCircleIconButtonState();
}
class _PulsatingCircleIconButtonState extends State<PulsatingCircleIconButton>
with SingleTickerProviderStateMixin {
AnimationController? _animationController;
Animation? _animation;
#override
void initState() {
_animationController = AnimationController(
vsync: this,
duration: Duration(seconds: 2),
);
_animation = Tween(begin: 0.0, end: 12.0).animate(
CurvedAnimation(parent: _animationController!, curve: Curves.easeOut));
_animationController!.repeat(reverse: true);
super.initState();
}
#override
Widget build(BuildContext context) {
return InkWell(
radius: 100,
borderRadius: BorderRadius.circular(50),
onTap: widget.onTap,
splashColor: AppColors.Violet,
child: AnimatedBuilder(
animation: _animation!,
builder: (context, _) {
return Ink(
padding: EdgeInsets.all(16),
decoration: BoxDecoration(
color: AppColors.midBlue,
shape: BoxShape.circle,
boxShadow: [
for (int i = 1; i <= 2; i++)
BoxShadow(
color: AppColors.lightBlue
.withOpacity(_animationController!.value / 2),
spreadRadius: _animation!.value * i,
),
],
),
);
},
),
);
}
}
Also I am embedding two pictures showing the problem related to the flashing text. It keeps going from solid white to a greyed out white.
enter image description here
enter image description here
I would really appreciate if anyone could help me on this. I have tried literally everything but I can't find where the problem is.
Thanks in advance.

Change the fill color of the container from left to right in Flutter

I am trying to change the color of a container from say white to red. But I want to do it from left to right that is, from the left of the container the color red starts filling up and expands all the way to the right in some duration. I know animated container will change the color but it seems to change the color of the entire container and not how I want it to
Like the 'Next Episode' of Netflix
You could use the Stack Widget and use an AnimatedContainer in between the background container (grey) and the foreground container (displaying icon & text):
class NetflixCustomButton extends StatefulWidget {
final Duration animationDuration;
final double height;
final double width;
final double borderRadius;
const NetflixCustomButton({
this.animationDuration = const Duration(milliseconds: 800),
this.height = 30,
this.width = 130.0,
this.borderRadius = 10.0,
});
#override
_NetflixCustomButtonState createState() =>
_NetflixCustomButtonState();
}
class _NetflixCustomButtonState
extends State<NetflixCustomButton> {
double _animatedWidth = 0.0;
#override
Widget build(BuildContext context) {
return Stack(
children: [
Container(
height: widget.height,
width: widget.width,
decoration: BoxDecoration(
borderRadius: BorderRadius.circular(
widget.borderRadius,
),
color: Colors.grey,
),
),
AnimatedContainer(
duration: widget.animationDuration,
height: widget.height,
width: _animatedWidth,
decoration: BoxDecoration(
borderRadius: BorderRadius.circular(
widget.borderRadius,
),
color: Colors.white,
),
),
InkWell(
child: Container(
height: widget.height,
width: widget.width,
decoration: BoxDecoration(
borderRadius: BorderRadius.circular(
widget.borderRadius,
),
color: Colors.transparent,
),
child: Row(
mainAxisAlignment: MainAxisAlignment.center,
children: const [
Icon(
Icons.play_arrow,
color: Colors.black87,
),
Text(
'Next Episode',
style: TextStyle(
color: Colors.black87,
),
),
],
),
),
onTap: () {
setState(() {
_animatedWidth = widget.width;
});
},
),
],
);
}
}

Why calling setState in a callback inside child widget, which have another setState, breaks program?

Here is a custom Switch widget I implemented using Animation.
enum SwitchType {
LockToggle, EnableToggle
}
class DiamondSwitch extends StatefulWidget {
final double width, height;
final SwitchType switchType;
final double switchThumbSize;
final VoidCallback onTapCallback;
DiamondSwitch({
key, this.width, this.height,
this.switchType, this.switchThumbSize,
#required this.onTapCallback
}) : super(key: key);
#override
_DiamondSwitchState createState() => _DiamondSwitchState(
width: width, height: height,
switchType: switchType, switchThumbSize: switchThumbSize,
onTapCallback: onTapCallback
);
}
class _DiamondSwitchState extends State<DiamondSwitch> {
final double width, height;
final int _toggleAnimationDuration = 1000;
bool _isOn = false;
final List<Color>
_darkGradientShades = <Color>[
Colors.black, Color.fromRGBO(10, 10, 10, 1.0)
],
_lightGradientShades = <Color>[
Colors.white, Color.fromRGBO(150, 150, 150, 1.0)
];
final SwitchType switchType;
final double switchThumbSize;
List<Icon> _switchIcons = new List<Icon>();
final double _switchIconSize = 35.0;
final VoidCallback onTapCallback;
_DiamondSwitchState({
this.width = 100.0, this.height = 40.0,
this.switchThumbSize = 40.0, #required this.switchType,
#required this.onTapCallback
});
#override
void initState() {
_switchIcons.addAll(
(switchType == SwitchType.LockToggle)?
<Icon>[
Icon(
Icons.lock,
color: Colors.black,
size: _switchIconSize,
),
Icon(
Icons.lock_open,
color: Colors.white,
size: _switchIconSize,
)
]
:
<Icon>[
Icon(
Icons.done,
color: Colors.black,
size: _switchIconSize,
),
Icon(
Icons.close,
color: Colors.white,
size: _switchIconSize,
)
]
);
super.initState();
}
#override
Widget build(BuildContext context) {
return AnimatedContainer(
duration: Duration(milliseconds: _toggleAnimationDuration),
width: width, height: height,
decoration: ShapeDecoration(
shape: BeveledRectangleBorder(
borderRadius: BorderRadius.circular(28.0),
side: (_isOn)?
BorderSide(
color: Color.fromRGBO(45, 45, 45, 1.0),
width: 0.5,
)
:
BorderSide.none,
),
gradient: LinearGradient(
colors: <Color>[
...((_isOn)? _darkGradientShades : _lightGradientShades)
],
begin: Alignment(1.0, -0.8), end: Alignment(-0.7, 1.0),
stops: <double>[0.4, 1.0]
),
),
alignment: Alignment(0.0, 0.0),
child: Stack(
alignment: Alignment(0.0, 0.0),
children: <Widget>[
AnimatedPositioned(
duration: Duration(milliseconds: _toggleAnimationDuration),
curve: Curves.easeIn,
left: (_isOn)? 0.0 : ((width * 70) / 100),
right: (_isOn)? ((width * 70) / 100) : 0.0,
child: GestureDetector(
onTap: () {
onTapCallback();
setState(() {
_isOn = !_isOn;
});
},
child: AnimatedSwitcher(
duration: Duration(milliseconds: _toggleAnimationDuration),
transitionBuilder: (Widget child, Animation<double> animation) {
return FadeTransition(
opacity: animation,
child: child,
);
},
child: Transform.rotate(
alignment: Alignment.center,
angle: (math.pi / 4),
child: Container(
width: switchThumbSize, height: switchThumbSize,
alignment: Alignment.center,
decoration: BoxDecoration(
borderRadius: BorderRadius.circular(3.0),
color: (_isOn)? Colors.white : Colors.black,
border: (_isOn)?
null
:
Border.all(
color: Color.fromRGBO(87, 87, 87, 1.0),
width: 1.0,
),
),
child: Transform.rotate(
alignment: Alignment.center,
angle: -(math.pi / 4),
child: (_isOn)?
_switchIcons[0]
:
_switchIcons[1],
),
),
),
),
),
)
],
),
);
}
}
I added a onTapCallback because I need to set another flag in the parent widget to trigger a Image change. Here is related code that belongs to parent widget;
DiamondSwitch(
switchType: SwitchType.LockToggle,
width: 186.0,
height: 60.0,
switchThumbSize: 41.0,
onTapCallback: () {
this.setState(() {
this._isLockOn = !this._isLockOn;
});
},
key: UniqueKey(),
),
When I run this code, animation doesn't work. It detects tap and executes onTap callback, and all code in onTap Works, (I tested with print methods), but as I said, animation isn't happening.
I want to learn why does this happen, is this about how Flutter work? If yes, can you explain?
TY for taking time ^.^!
EDIT
I want to know why does getting a method with setState breaks the animation I'm sharing the current parent widget with the approach of the answerer #pulyaevskiy implemented.
class _SettingsState extends State<Settings> {
bool
_isLockOn = false,
_isPassiconOn = false;
#override
Widget build(BuildContext context) {
return Scaffold(
backgroundColor: Colors.black,
body: SafeArea(
child: Column(
children: <Widget>[
/*Lock Toggle Graphic*/
Align(
alignment: Alignment(-0.1, 0.0),
child: Row(
mainAxisAlignment: MainAxisAlignment.end,
mainAxisSize: MainAxisSize.min,
children: <Widget>[
/*Lock Toggle Text*/
RotatedBox(
quarterTurns: 3,
child: Column(
children: <Widget>[
Text(
(_isLockOn)? "locked" : "unlocked",
style: TextStyle(
color: Colors.white,
fontFamily: "Philosopher",
fontSize: 25.0,
shadows: <Shadow>[
Shadow(
color: Color.fromRGBO(184, 184, 184, 0.68),
offset: Offset(2.0, 2.0),
blurRadius: 4.0,
),
],
),
),
Padding(
padding: const EdgeInsets.only(top: 5.5),
child: Container(
color: Color.fromRGBO(204, 204, 204, 1.0),
width: 30.0, height: 1.0,
),
),
],
),
),
/*Lock Toggle Image*/
Image.asset(
"assets/images/settings_screen/crystal_"
"${(_isLockOn)? "white_light_up" : "black_outline"}.png",
scale: 5.0,
alignment: Alignment.center,
),
],
),
),
/*Lock Toggle Switch*/
Padding(
padding: const EdgeInsets.only(top: 12.5),
child: DiamondSwitch(
switchType: SwitchType.LockToggle,
width: 186.0,
height: 60.0,
switchThumbSize: 41.0,
flagToControl: this._isLockOn,
onTapCallback: () {
this.setState(() {
this._isLockOn = !this._isLockOn;
});
},
key: UniqueKey(),
),
),
/*Separator*/
WhiteDiamondSeparator(paddingAmount: 36.5),
/*Passicon Toggle Graphic*/
Align(
alignment: Alignment(-0.32, 0.0),
child: Row(
mainAxisAlignment: MainAxisAlignment.end,
mainAxisSize: MainAxisSize.min,
children: <Widget>[
/*Lock Toggle Text*/
RotatedBox(
quarterTurns: 3,
child: Column(
children: <Widget>[
Text(
"passicon",
style: TextStyle(
color: Colors.white,
fontFamily: "Philosopher",
fontSize: 25.0,
shadows: <Shadow>[
Shadow(
color: Color.fromRGBO(184, 184, 184, 0.68),
offset: Offset(2.0, 2.0),
blurRadius: 4.0,
),
],
),
),
Padding(
padding: const EdgeInsets.only(top: 5.5),
child: Container(
color: Color.fromRGBO(204, 204, 204, 1.0),
width: 30.0, height: 1.0,
),
),
],
),
),
/*Passicon Toggle Image*/
Padding(
padding: const EdgeInsets.only(left: 40),
child: Image.asset(
"assets/images/settings_screen/emote_"
"${(_isPassiconOn)? "winking" : "nervous"}.png",
scale: 3.25,
alignment: Alignment.center,
),
),
],
),
),
/*Passicon Toggle Switch*/
Padding(
padding: const EdgeInsets.only(top: 42.5),
child: DiamondSwitch(
switchType: SwitchType.PassiconToggle,
width: 186.0,
height: 60.0,
switchThumbSize: 41.0,
flagToControl: this._isPassiconOn,
onTapCallback: () {
this.setState(() {
this._isPassiconOn = !this._isPassiconOn;
});
},
key: UniqueKey(),
),
),
/*Separator*/
WhiteDiamondSeparator(paddingAmount: 36.5),
],
)
),
);
}
}
And I added flagToControl in DiamondSwitch and using it in _DiamondSwitchState as bool get _isOn => widget.flagToControl;.
In the old approach, if I don't execute and other thing than
setState() { _isOn = !_isOn; }
animation happens as it should. What am I missing?
In your onTapCallback you are changing state of your parent widget, which works indeed. However it does not affect state of the DiamondSwitch widget itself
(I see that in GestureDetector you also setState of the switch widget, but there are a few issues with this approach).
To fix this you can pass the value of this._isLockOn from your parent widget's state to the DiamondSwitch child. This means you need another property on your switch widget. E.g.
class DiamondSwitch extends StatefulWidget {
final bool isOn;
// ... remaining fields go here
DiamondSwitch({this.isOn, ...});
}
Then change _DiamondSwitchState as well. Could simply proxy _isOn to the widget's value:
class _DiamondSwitchState extends State<DiamondSwitch> {
bool get _isOn => widget.isOn;
}
This is much better than keeping state of isOn in two places as you have now (in parent widget AND in the switch itself). With this change your isLockOn state is only kept on the parent widget and you just pass it down to the switch child to use.
This means that for GestureDetector's onTap property you'll simply pass the onTapCallback of the parent widget as well, no need to wrap it with another function.
The last part: in your parent widget's build method:
DiamondSwitch(
isOn: this._isLockOn, // <-- new line here to pass the value down
switchType: SwitchType.LockToggle,
width: 186.0,
height: 60.0,
switchThumbSize: 41.0,
onTapCallback: () {
this.setState(() {
this._isLockOn = !this._isLockOn;
});
},
key: UniqueKey(),
),
Another benefit of doing it this way is that now you can initialize your switch with a different default value if needed (right now it's hardcoded to always start off as false). So if you load value of isLockOn from a database and it's set to true you can immediately pass this value to the switch child and represent your state correctly.

LinearPercentIndicator not displayed when defined in function Flutter

I'm rather new to Flutter/Dart so I could have made a foolish mistake. I have defined a LinearPercentIndicator in what I believe is a function inside my class _FourthRouteState. I call this function inside Widget build(BuildContext context) with 3 parameters: context, var1, var2 where var1 is a double between 0.0 and 1.0 and var2 is a text object.
The default PercentIndicator should display 75% whereas if the user presses the Confirm button, the percentage should change to 100%. It would be ideal if the animation of the LinearPercentIndicator starts over, since the value changes from 75 to 100.
The problem is that the LinearPercentIndicator is not displayed on my page and I don't receive any errors so I'm not sure what I'm doing wrong. If you could both help me with my code (I only included the relevant parts) and provide me with some relevant/useful documentation, that would be great!
import 'package:flutter/material.dart';
import 'package:percent_indicator/percent_indicator.dart';
import 'package:page_transition/page_transition.dart';
void main() {
runApp(MaterialApp(
theme:
ThemeData(accentColor: Colors.black87),
home: FourthRoute(),
));
}
class FourthRoute extends StatefulWidget {
final value;
FourthRoute({Key key, this.value}) : super(key: key);
#override
_FourthRouteState createState() => new _FourthRouteState();
}
class _FourthRouteState extends State<FourthRoute> {
var _textController = new TextEditingController();
var _formKey = GlobalKey<FormFieldState>();
getPercentageIndicator(context, var1,var2) {
setState(() {
LinearPercentIndicator(
width: MediaQuery
.of(context)
.size
.width - 50,
animation: true,
lineHeight: 20.0,
animationDuration: 2000,
percent: var1,
center: Text(var2),
linearStrokeCap: LinearStrokeCap.roundAll,
progressColor: Colors.green,
backgroundColor: Colors.green.withOpacity(0.2),
);
});
}
#override
Widget build(BuildContext context) {
Color foreground = Colors.green;
Color background = foreground.withOpacity(0.2);
return Scaffold(
body: Stack(
fit: StackFit.expand,
children: <Widget>[
Container(
decoration: BoxDecoration(
color: Color.fromRGBO(53, 73, 94, 0.9)),
),
Positioned(
top: 0.0,
left: 0.0,
right: 0.0,
child: AppBar(
backgroundColor: Colors.transparent,
elevation: 0.0,
),
),
Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: <Widget>[
Padding(
padding: EdgeInsets.symmetric(vertical: 50.0),
),
Padding(
padding: EdgeInsets.fromLTRB(20.0,16.0,0.0,0.0), // left, top, right, bottom
child: getPercentageIndicator(context,0.75,"75%"),
),
],
),
Positioned(
bottom: 150.0,
right: 20.0,
child: ButtonTheme(
minWidth: 150.0,
child: RaisedButton(
padding: EdgeInsets.all(8.0),
child: Text('Confirm',
style: TextStyle(
fontSize: 24,
color: Colors.black87,
fontWeight: FontWeight.bold
),
),
shape: RoundedRectangleBorder(
borderRadius: BorderRadius.circular(18.0)
),
color: Colors.white,
splashColor: Colors.blueGrey,
onPressed: () {
if (_formKey.currentState.validate()) {
getPercentageIndicator(context,1.0,"100%");
// Navigator.push(
// context,
// PageTransition(
// type: PageTransitionType.fade,
// child:HomePage(),
// duration: Duration(
// seconds: 1
// ),
// ),
// );
}
}
),
),
),
],
),
);
}
}
Why do you make instance of LinearPercentIndicator widget inside of setState in getPercentageIndicator? It makes no sense. You probably wanted just to return it:
getPercentageIndicator(context, var1,var2) {
return LinearPercentIndicator(
width: MediaQuery
.of(context)
.size
.width - 50,
animation: true,
lineHeight: 20.0,
animationDuration: 2000,
percent: var1,
center: Text(var2),
linearStrokeCap: LinearStrokeCap.roundAll,
progressColor: Colors.green,
backgroundColor: Colors.green.withOpacity(0.2),
);
}
By the way, you shouldn't use mediaquery size here. Use LayoutBuilder to change width depending on how much space your widget has:
getPercentageIndicator(context, var1,var2) {
return LayoutBuilder(
builder: (context, constraints) {
return LinearPercentIndicator(
width: constraints.maxWidth - 50,
animation: true,
lineHeight: 20.0,
animationDuration: 2000,
percent: var1,
center: Text(var2),
linearStrokeCap: LinearStrokeCap.roundAll,
progressColor: Colors.green,
backgroundColor: Colors.green.withOpacity(0.2),
);
}
);
}