I have this code:
AnimatedOpacity(
duration: Duration(seconds: 1),
opacity: _text == 'hide text'? 0 : 1,
child: Text(_text),
)
What is the easiest way to show old value of text (eg not 'hide text', but whatever is there before the text changes to this) for the duration of fading the text out?
You can create your own Widget to do what you need. I created a sample for you:
Result
Explanation
First, I created this code, where the main value is "show text", when the user press the button, the value will be updated to "hide text" and the animation will start.
There is a function fadeBuilder
that you can use to validate when you want to hide the text, in this sample the condition to hide the text is value == 'hide text'
class _SampleViewState extends State<SampleView> {
String _myText = 'show text';
@override
Widget build(BuildContext context) {
return Column(
mainAxisAlignment: MainAxisAlignment.center,
children: [
MyAnimatedTextOpacity(
value: _myText,
fadeBuilder: (value) => value == 'hide text',
),
ElevatedButton(
onPressed: () {
setState(() {
_myText = 'hide text';
});
},
child: const Text('change text'),
)
],
);
}
}
Now this is the custom widget:
typedef FadeBuilder = bool Function(String value);
class MyAnimatedTextOpacity extends StatefulWidget {
const MyAnimatedTextOpacity({
required this.value,
required this.fadeBuilder,
super.key,
});
final String value;
final FadeBuilder fadeBuilder;
@override
State<MyAnimatedTextOpacity> createState() => _MyAnimatedTextOpacityState();
}
class _MyAnimatedTextOpacityState extends State<MyAnimatedTextOpacity> {
late final String _value = widget.value;
@override
Widget build(BuildContext context) {
return AnimatedOpacity(
duration: const Duration(seconds: 1),
opacity: widget.fadeBuilder(widget.value) ? 0 : 1,
child: Text(_value),
);
}
}
A new sample based on your requirements:
Result
Code:
class BadgesDemo extends StatefulWidget {
const BadgesDemo({super.key});
@override
State<BadgesDemo> createState() => _BadgesDemoState();
}
class _BadgesDemoState extends State<BadgesDemo> {
int? badgeValue;
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: const Text('Badges Demo'),
),
body: Center(
child: BadgeWidget(
value: badgeValue?.toString(),
child: const Icon(Icons.phone),
),
),
floatingActionButton: FloatingActionButton.extended(
onPressed: () {
// Assign the badge value
setState(() {
badgeValue = Random().nextInt(100);
});
},
label: const Text('Generate random number'),
),
);
}
}
class BadgeWidget extends StatefulWidget {
const BadgeWidget({
required this.child,
this.value,
super.key,
});
final Widget child;
final String? value;
@override
State<BadgeWidget> createState() => _BadgeWidgetState();
}
class _BadgeWidgetState extends State<BadgeWidget>
with SingleTickerProviderStateMixin {
late AnimationController _controller;
@override
void didUpdateWidget(covariant BadgeWidget oldWidget) {
if (widget.value != oldWidget.value) {
_controller.forward(from: 0);
}
super.didUpdateWidget(oldWidget);
}
@override
void initState() {
super.initState();
_controller = AnimationController(
vsync: this,
duration: const Duration(seconds: 1),
);
}
@override
void dispose() {
_controller.dispose();
super.dispose();
}
@override
Widget build(BuildContext context) {
const dotSize = 20.0;
return Stack(
clipBehavior: Clip.none,
children: [
ClipRRect(
borderRadius: BorderRadius.circular(10),
child: Material(
color: Colors.green[400],
child: InkWell(
onTap: () {
_controller.reverse();
},
child: Container(
padding: const EdgeInsets.all(10.0),
child: widget.child,
),
),
),
),
if (widget.value != null)
Positioned(
right: -dotSize / 3,
top: -dotSize / 3,
child: FadeTransition(
opacity: _controller,
child: Container(
decoration: BoxDecoration(
color: Colors.red,
borderRadius: BorderRadius.circular(10),
),
width: dotSize,
height: dotSize,
child: Center(
child: Text(
widget.value!,
style: const TextStyle(
color: Colors.white,
fontSize: 12,
),
),
),
),
),
),
],
);
}
}