How can I use Animated switcher without a button in Flutter - dart

I have multiple widgets I want to fade in one after another but without needing a button. Every tutorial I have seen has required the use of a button in order to transition to the next widget. The widgets I want to transition take up the whole screen .

How do you want to trigger the transition?
If time is a suitable trigger.
class MyStatefulWidget extends StatefulWidget {
const MyStatefulWidget({Key? key}) : super(key: key);
#override
State<MyStatefulWidget> createState() => _MyStatefulWidgetState();
}
class _MyStatefulWidgetState extends State<MyStatefulWidget> {
late Timer timer;
int count = 0;
#override
void initState() {
super.initState();
timer = Timer.periodic(Duration(seconds: 1), (_) => setState(() => count += 1));
}
#override
void dispose() {
super.dispose();
timer.cancel();
}
#override
Widget build(BuildContext context) {
return Container(
color: Colors.white,
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: <Widget>[
AnimatedSwitcher(
duration: const Duration(milliseconds: 500),
transitionBuilder: (Widget child, Animation<double> animation) {
return ScaleTransition(child: child, scale: animation);
},
child: Text(
'$count',
key: ValueKey<int>(count),
style: Theme.of(context).textTheme.headline4,
),
),
],
),
);
}
}

Related

How to show a Flutter widget as a result of action, and fade it out with no action

I am trying to have some text show (or quickly fade in) as a result of user action (e.g. button click), and after a second fade out without user action. I understand that fading is accomplished by AnimatedOpacity class with opacity being set by a state variable, however it's not clear to me how to accomplish this particular scenario. Many thanks.
first, text its show it self while not fade its can be considered animated too, or create some async function
check this code :
import 'package:flutter/material.dart';
class FadeTextAuto extends StatefulWidget {
const FadeTextAuto({Key? key}) : super(key: key);
#override
_FadeTextAutoState createState() => _FadeTextAutoState();
}
class _FadeTextAutoState extends State<FadeTextAuto> with SingleTickerProviderStateMixin{
late Animation _animationFadeInOut;
late AnimationController _animationController;
bool _textShouldPlay = false;
late Animation _animationText;
String info = "This text appear in";
#override
void initState() {
super.initState();
_animationController = AnimationController(vsync: this, duration: const Duration(milliseconds: 2000));
_animationFadeInOut = Tween<double>(
begin: 0.0, end: 1.0).animate(CurvedAnimation(parent: _animationController, curve: Curves.linear));
_animationText = StepTween(
begin: 1, end: 3
).animate(CurvedAnimation(parent: _animationController, curve: Curves.linear));
}
#override
Widget build(BuildContext context) {
return Material(
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: [
TextButton(onPressed: (){
setState(() {
info = "This text appear in";
_textShouldPlay = false;
});
print(_animationText.value);
_animationController.forward().then((value){
if(mounted){
setState(() {
_textShouldPlay = true;
info = "Hey read this Information";
});
Future.delayed(const Duration(milliseconds: 2000),(){
//wait a little for text can be read
if(mounted){
setState(() {
_textShouldPlay = false;
info = "this Text will be disappear in";
});
}
}).then((value){
if(mounted){
_animationController.reverse();
}
});
}
});
}, child: const Text("User Click")),
AnimatedBuilder(
animation: _animationController,
builder: (context, child){
return Opacity(
opacity: _animationFadeInOut.value,
child: Text(
"$info ${_textShouldPlay?"":_animationText.value} ", style: const TextStyle(color: Colors.cyan, fontSize: 20),
),
);
},
),
],
),
);
}
#override
void dispose() {
_animationController.dispose();
super.dispose();
}
}
result :

Flutter - My animation is only playing once

I wanted to animate text and I put a nice animation. So I've made a statefull widget called AnimatedText() and I use it in an other stateful widget that use this AnimatedText and refresh the text every time you clic the screen. However, when I change this text with a setState(), the animation doesn't play again if it had already played...
Here is my code, the AnimatedText() :
class AnimatedText extends StatefulWidget {
double textSize;
String text;
AnimatedText(this.text, {this.textSize = 20});
#override
_AnimatedTextState createState() => _AnimatedTextState();
}
class _AnimatedTextState extends State<AnimatedText> with SingleTickerProviderStateMixin {
Animation _fontSizeAnimation;
AnimationController _controller;
#override
void initState() {
_controller = AnimationController(duration: Duration(milliseconds: 250), vsync: this);
_fontSizeAnimation = CurvedAnimation(parent: _controller, curve: Curves.bounceOut);
_fontSizeAnimation.addListener(() => setState(() {}));
_controller.forward();
super.initState();
}
#override
void dispose() {
_controller.dispose();
super.dispose();
}
#override
Widget build(BuildContext context) {
return Text(widget.text, textAlign: TextAlign.center, style: GoogleFonts.rubik(color: Colors.white, fontSize: _fontSizeAnimation.value*ResponsiveSize().responsiveSizeSmall(widget.textSize)));
}
}
And here is the class that use the AnimatedText():
class BasicDisplay extends StatefulWidget {
Question question;
VoidCallback onTap;
VoidCallback onPressed;
BasicDisplay(this.question, this.onTap, this.onPressed);
#override
_BasicDisplayState createState() => _BasicDisplayState();
}
class _BasicDisplayState extends State<BasicDisplay> with SingleTickerProviderStateMixin {
#override
Widget build(BuildContext context) {
return GamePageBackground(
onTap: widget.onTap,
question: widget.question,
children: <Widget>[
Expanded(
child: Container(
alignment: Alignment.topRight,
child: IconButton(
onPressed: widget.onPressed,
icon: Icon(Icons.clear, color: Color(0xaaffffff), size: ResponsiveSize().responsiveSize(quitIconButtonSize),),
),
),
),
AnimatedText(widget.question.category, textSize: questionCategoryTextSize,),
AnimatedText(widget.question.question, textSize: questionTextSize,),
Expanded(
child: Container(
padding: EdgeInsets.only(bottom: ResponsiveSize().responsiveSize(10)),
alignment: Alignment.bottomCenter,
child: Text(widget.question.explanation, textAlign: TextAlign.center ,style: GoogleFonts.rubik(color: Colors.white, fontSize: ResponsiveSize().responsiveSize(bottomExplanationTextSize))),
),
),
],
);
}
}
The onPressed call back call the Game class that will refresh the text.
Any one know how to solve this problem ?
I found a solution based on #HBS idea !
I took his function and changed it a little bit :
#override
void didUpdateWidget(AnimatedText oldWidget) {
if(oldWidget.text != widget.text) {
_controller.reset();
_controller.forward();
}
super.didUpdateWidget(oldWidget);
}
You can override the didUpdateWidget in your AnimatedText class:
#override
void didUpdateWidget(AnimatedText oldWidget) {
if(oldWidget.text != widget.text) {
_controller.reset();
_controller.forward();
}
super.didUpdateWidget(oldWidget);
}

How to pass data from child widget to its parent

I've the below custom widget that make a Switch and reads its status (true/false)
Then I add this one to my main app widget (parent), how can I make the parent knows the value of the switch!
import 'package:flutter/material.dart';
class Switchy extends StatefulWidget{
Switchy({Key key}) : super(key: key);
#override
State<StatefulWidget> createState() => new _SwitchyState();
}
class _SwitchyState extends State<Switchy> {
var myvalue = true;
void onchange(bool value) {
setState(() {
this.myvalue = value; // I need the parent to receive this one!
print('value is: $value');
});
}
#override
Widget build(BuildContext context) {
return
new Card(
child: new Container(
child: new Row(
mainAxisAlignment: MainAxisAlignment.end,
children: <Widget>[
new Text("Enable/Disable the app in the background",
textAlign: TextAlign.left,
textDirection: TextDirection.ltr,),
new Switch(value: myvalue, onChanged: (bool value) => onchange(value)),
],
),
),
);
}
}
In the main.dart (parent) file, I started with this:
import 'widgets.dart';
import 'package:flutter/material.dart';
void main() => runApp(new MyApp());
class MyApp extends StatelessWidget {
#override
Widget build(BuildContext context) {
return new MaterialApp(
title: 'Flutter Demo',
theme: new ThemeData(
primarySwatch: Colors.deepOrange,
),
home: new MyHomePage(title: 'My App settup'),
);
}
}
class MyHomePage extends StatefulWidget {
MyHomePage({Key key, this.title}) : super(key: key);
final String title;
#override
_MyHomePageState createState() => new _MyHomePageState();
}
class _MyHomePageState extends State<MyHomePage> {
Widget e = new Switchy();
//...
}
The first possibility is to pass a callback into your child, and the second is to use the of pattern for your stateful widget. See below.
import 'package:flutter/material.dart';
class MyStatefulWidget extends StatefulWidget {
#override
State<StatefulWidget> createState() => new MyStatefulWidgetState();
// note: updated as context.ancestorStateOfType is now deprecated
static MyStatefulWidgetState of(BuildContext context) =>
context.findAncestorStateOfType<MyStatefulWidgetState>();
}
class MyStatefulWidgetState extends State<MyStatefulWidget> {
String _string = "Not set yet";
set string(String value) => setState(() => _string = value);
#override
Widget build(BuildContext context) {
return new Column(
children: <Widget>[
new Text(_string),
new MyChildClass(callback: (val) => setState(() => _string = val))
],
);
}
}
typedef void StringCallback(String val);
class MyChildClass extends StatelessWidget {
final StringCallback callback;
MyChildClass({this.callback});
#override
Widget build(BuildContext context) {
return new Column(
children: <Widget>[
new FlatButton(
onPressed: () {
callback("String from method 1");
},
child: new Text("Method 1"),
),
new FlatButton(
onPressed: () {
MyStatefulWidget.of(context).string = "String from method 2";
},
child: new Text("Method 2"),
)
],
);
}
}
void main() => runApp(
new MaterialApp(
builder: (context, child) => new SafeArea(child: new Material(color: Colors.white, child: child)),
home: new MyStatefulWidget(),
),
);
There is also the alternative of using an InheritedWidget instead of a StatefulWidget; this is particularly useful if you want your child widgets to rebuild if the parent widget's data changes and the parent isn't a direct parent. See the inherited widget documentation
In 2020, the function in the highest voted answer is marked deprecated. So here is the modified solution based on that answer.
import 'package:flutter/material.dart';
class MyStatefulWidget extends StatefulWidget {
#override
State<StatefulWidget> createState() => new MyStatefulWidgetState();
// --> NOTE this! <--
static MyStatefulWidgetState of(BuildContext context) =>
context.findAncestorStateOfType<MyStatefulWidgetState>();
}
class MyStatefulWidgetState extends State<MyStatefulWidget> {
String _string = "Not set yet";
set string(String value) => setState(() => _string = value);
#override
Widget build(BuildContext context) {
return new Column(
children: <Widget>[
new Text(_string),
new MyChildClass(callback: (val) => setState(() => _string = val))
],
);
}
}
typedef void StringCallback(String val);
class MyChildClass extends StatelessWidget {
final StringCallback callback;
MyChildClass({this.callback});
#override
Widget build(BuildContext context) {
return new Column(
children: <Widget>[
new FlatButton(
onPressed: () {
callback("String from method 1");
},
child: new Text("Method 1"),
),
new FlatButton(
onPressed: () {
MyStatefulWidget.of(context).string = "String from method 2";
},
child: new Text("Method 2"),
)
],
);
}
}
void main() => runApp(
new MaterialApp(
builder: (context, child) => new SafeArea(child: new Material(color: Colors.white, child: child)),
home: new MyStatefulWidget(),
),
);
However, the methods mentioned in the answers of this question has a drawback. From doc:
In general, though, consider using a callback that triggers a stateful change in the ancestor rather than using the imperative style implied by this method. This will usually lead to more maintainable and reusable code since it decouples widgets from each other.
Calling this method is relatively expensive (O(N) in the depth of the tree). Only call this method if the distance from this widget to the desired ancestor is known to be small and bounded.
I think notifications are quite a civilized solution and they allow for a very clean communication without variable juggling and they bubble up if you need them to:
Define a notification:
class SwitchChanged extends Notification {
final bool val
SwitchChanged(this.val);
}
Raise notification in your child's event handler:
onPressed: () {
SwitchChanged(true).dispatch(context);
}
Finally, wrap your parent with notification listener:
NotificationListener<SwitchChanged>(
child: YourParent(...),
onNotification: (n) {
setState(() {
// Trigger action on parent via setState or do whatever you like.
});
return true;
}
)
You can pass a callback defined in the parent widget to the child widget and as soon as an action is performed in the child widget, the callback gets invoked.
class ParentWidget extends StatelessWidget {
// This gets called when the button is pressed in the ChildWidget.
void _onData(String data) {
print(data); // Hello World
}
#override
Widget build(BuildContext context) {
return Scaffold(
body: ChildWidget(onData: _onData),
);
}
}
class ChildWidget extends StatelessWidget {
final void Function(String) onData;
ChildWidget({
super.key,
required this.onData,
});
#override
Widget build(BuildContext context) {
return ElevatedButton(
onPressed: () {
// Pass 'Hello World' to parent widget.
onData('Hello World');
},
child: Text('Button'),
);
}
}
Use InheritedWidget - https://api.flutter.dev/flutter/widgets/InheritedWidget-class.html
This lets you access data of the parent in all the children
I found a way to do this which was fairly simple, I'm a flutter noob so maybe it isn't the best way. If someone sees something wrong with it, feel free to leave a comment. Basically state is set in parent widget, child widget updates the state of the parent, and any child widgets of the parents which use the state values are redrawn when the value is updated.
Parent widget:
class MyWidget extends StatefulWidget {
const MyWidget({Key? key}) : super(key: key);
#override
_MyWidgetState createState() => _MyWidgetState();
}
class _MyWidgetState extends State<MyWidget> {
String _stringToChange = ""; // the string you want to update in child
// function to update state with changes to term
_updateStringToChange(String stringToChange) {
setState(() {
_stringToChange = stringToChange;
// Other logic you might want to do as string value changes
});
}
#override
Widget build(BuildContext context) {
return MaterialApp(
title: 'title',
theme: ThemeData(
primarySwatch: Colors.blue,
visualDensity: VisualDensity.adaptivePlatformDensity,
),
home: Scaffold(
appBar: AppBar(
title: const Center(
child: Text("app bar title"),
),
),
body: Column(children: <Widget>[
ChildWhichMakesChanges(
updateStringToChange: _updateStringToChange,
),
Expanded(
child: Container(
padding: const EdgeInsets.fromLTRB(20, 10, 0, 10),
child: ChildWhichUsesChanges(
stringToChange: _stringToChange,
)))
]),
));
}
}
ChildWhichMakesChanges (this example uses a text box to enter input):
class ChildWhichMakesChanges extends StatefulWidget {
final ValueChanged<String> updateStringToChange;
const ChildWhichMakesChanges({Key? key, required this.updateStringToChange}) : super(key: key);
#override
_TextInputState createState() => _TextInputState();
}
class _TextInputState extends State<ChildWhichMakesChanges> {
#override
Widget build(BuildContext context) {
return Column(
crossAxisAlignment: CrossAxisAlignment.start,
mainAxisSize: MainAxisSize.min,
children: [
Padding(
padding: const EdgeInsets.symmetric(horizontal: 8, vertical: 25),
child: TextField(
decoration: const InputDecoration(
border: OutlineInputBorder(),
hintText: 'Enter text',
),
onChanged: (String stringToChange) {
widget.updateStringToChange(stringToChange);
})),
]);
}
}
Using the changed string value in ChildWhichUsesChanges:
class ChildWhichUsesChanges extends StatelessWidget {
final String stringToChange;
const ChildWhichUsesChanges(
{Key? key,
required this.stringToChange})
: super(key: key);
#override
Widget build(BuildContext context) {
return Text(stringToChange)
}
}
2022 Solution:
A simple one.
Make it work like interface.
You can make your own custom CallBack Function just by defining typedef. It will just work as an interface between child to parent widget.
This is an IMP function:
typedef void GetColor(Color? color, String? string);
Following is Parent Widget:
import 'package:flutter/material.dart';
typedef void GetColor(Color? color, String? string);
class NavigationDialog extends StatefulWidget {
const NavigationDialog({Key? key}) : super(key: key);
#override
_NavigationDialogState createState() => _NavigationDialogState();
}
class _NavigationDialogState extends State<NavigationDialog> {
Color? color = Colors.blue[700];
#override
Widget build(BuildContext context) {
return Scaffold(
backgroundColor: color,
appBar: AppBar(
title: const Text('Navigation Dialog Screen'),
),
body: Center(
child: ElevatedButton(
child: const Text('Change Color'),
onPressed: () {
_showColorDialog(context, (value, string) {
setState(() {
color = value;
print(string);
});
});
}),
),
);
}
And Following is a child Widget Code:
_showColorDialog(BuildContext context, Function getColor) async {
color = null;
await showDialog(
barrierDismissible: false,
context: context,
builder: (_) {
return AlertDialog(
title: const Text('Very important question'),
content: const Text('Please choose a color'),
actions: <Widget>[
TextButton(
child: const Text('Red'),
onPressed: () {
color = Colors.red[700];
getColor(color, 'Red');// This line of action wil send your data back to parent
Navigator.pop(context, color);
}),
TextButton(
child: const Text('Green'),
onPressed: () {
color = Colors.green[700];
getColor(color, 'Green');// This line of action wil send your data back to parent
Navigator.pop(context, color);
}),
TextButton(
child: const Text('Blue'),
onPressed: () {
color = Colors.blue[700];
getColor(color, 'Blue');// This line of action wil send your data back to parent
Navigator.pop(context, color);
}),
],
);
},
);
}
}
In this example, We are selecting a color from Child Alert Dialog widget and pass to Parent widget.
Store the value in that child widget in shared preference, then access that shared preference value in the parent widget.

Add blinking to button

I would like to draw a user's attention to a submit button anytime a radio button is selected and I would like to know if there's anyway to implement this in flutter. I have looked at the RaisedButton docs but there doesn't seem to be any property that flashes or shakes the button. The code below for instance initially has no radio button selected so the button is grayed out, once a choice is made amongst multiple radio buttons, the submit RaisedButton onPressed property value is no longer null but replaced with the action required; however I also want a situation where if a different radio button is selected, there is some way to add some motion (flashing/shaking button) to the submit button but not change the onPressed property
new Radio<int>(
value: 1,
groupValue: 0,
onChanged: handleRadioValue
)
new RaisedButton(
child: const Text('SUBMIT')
onPressed: selected
)
handleRadioValue(){
selected = groupValue == 0 ? null : submitButton();
//change Raised button to attract attention}
You can attract attention by animating the color of the RaisedButton. This example draws attention to the RaisedButton when the radio button selection changes by changing its color to the current theme's disabledColor and back.
import 'dart:math';
import 'package:flutter/material.dart';
void main() {
runApp(new MyApp());
}
class MyApp extends StatelessWidget {
#override
Widget build(BuildContext context) {
return new MaterialApp(
title: 'Flutter Demo',
home: new MyHomePage(),
);
}
}
class MyHomePage extends StatefulWidget {
MyHomePage({Key key}) : super(key: key);
#override
createState() => new MyHomePageState();
}
class MyHomePageState extends State<MyHomePage> with TickerProviderStateMixin {
AnimationController _controller;
int _radioValue;
#override initState() {
_controller = new AnimationController(
vsync: this,
duration: const Duration(milliseconds: 100),
)..addStatusListener((AnimationStatus status) {
if (status == AnimationStatus.completed)
_controller.reverse();
});
super.initState();
}
void _handleRadioValue(int value) {
// Don't animate the first time that the radio value is set
if (_radioValue != null)
_controller.forward();
setState(() {
_radioValue = value;
});
}
#override
Widget build(BuildContext context) {
ThemeData theme = Theme.of(context);
return new Scaffold(
body: new Center(
child: new Column(
mainAxisSize: MainAxisSize.max,
mainAxisAlignment: MainAxisAlignment.center,
children: [
new Radio<int>(
value: 0,
groupValue: _radioValue,
onChanged: _handleRadioValue
),
new Radio<int>(
value: 1,
groupValue: _radioValue,
onChanged: _handleRadioValue
),
new AnimatedBuilder(
child: const Text('SUBMIT'),
animation: _controller,
builder: (BuildContext context, Widget child) {
return new RaisedButton(
color: new ColorTween(
begin: theme.primaryColor,
end: theme.disabledColor,
).animate(_controller).value,
colorBrightness: Brightness.dark,
child: child,
onPressed: _radioValue == null ?
null :
() => print('submit')
);
}
)
],
),
),
);
}
#override
void dispose() {
_controller.dispose();
super.dispose();
}
}

How do I create an animated number counter?

I'd like to create a number counter that animates from a starting value to an end value. I've looked into using a Timer but can't seem to animate/update state properly. Including the decimal value would be great, but a simple integer animation is fine.
Number counter that needs to animate
double _mileCounter = 643.6;
_animateMileCounter() {
Duration duration = new Duration(milliseconds: 300);
return new Timer(duration, _updateMileCounter);
}
_updateMileCounter() {
setState(() {
_mileCounter += 1;
});
}
How would I increment the counter X number of times (with animation)? Similar to how a car's odometer increments.
For anyone still looking, you can use ImplicitlyAnimatedWidget.
Here is an example of an int counter. Works analogously for doubles.
class AnimatedCount extends ImplicitlyAnimatedWidget {
final int count;
AnimatedCount({
Key key,
#required this.count,
#required Duration duration,
Curve curve = Curves.linear
}) : super(duration: duration, curve: curve, key: key);
#override
ImplicitlyAnimatedWidgetState<ImplicitlyAnimatedWidget> createState() => _AnimatedCountState();
}
class _AnimatedCountState extends AnimatedWidgetBaseState<AnimatedCount> {
IntTween _count;
#override
Widget build(BuildContext context) {
return new Text(_count.evaluate(animation).toString());
}
#override
void forEachTween(TweenVisitor visitor) {
_count = visitor(_count, widget.count, (dynamic value) => new IntTween(begin: value));
}
}
Just rebuild the widget with a new value and it automatically animates there.
You should use an AnimationController with an AnimatedBuilder to rebuild your text when the controller changes. Here's an example that increments the miles when the floating action button is pressed (double.toStringAsFixed to get the decimal to show), with a curve on the animation speed:
import 'dart:math';
import 'package:flutter/material.dart';
void main() {
runApp(new MyApp());
}
class MyApp extends StatelessWidget {
#override
Widget build(BuildContext context) {
return new MaterialApp(
title: 'Flutter Demo',
theme: new ThemeData(primarySwatch: Colors.purple),
home: new MyHomePage(),
);
}
}
class MyHomePage extends StatefulWidget {
MyHomePage({Key key}) : super(key: key);
#override
createState() => new MyHomePageState();
}
class MyHomePageState extends State<MyHomePage> with TickerProviderStateMixin {
AnimationController _controller;
Animation<double> _animation;
double _miles = 0.0;
#override initState() {
_controller = new AnimationController(
vsync: this,
duration: const Duration(milliseconds: 1500),
);
_animation = _controller;
super.initState();
}
#override
Widget build(BuildContext context) {
TextTheme textTheme = Theme.of(context).textTheme;
return new Scaffold(
body: new Material(
color: const Color.fromRGBO(246, 251, 8, 1.0),
child: new Center(
child: new Column(
mainAxisAlignment: MainAxisAlignment.center,
children: [
new AnimatedBuilder(
animation: _animation,
builder: (BuildContext context, Widget child) {
return new Text(
_animation.value.toStringAsFixed(1),
style: textTheme.display4.copyWith(fontStyle: FontStyle.italic),
);
},
),
new Text(
"MILES",
style: textTheme.display1.copyWith(fontStyle: FontStyle.italic),
)
],
),
),
),
floatingActionButton: new FloatingActionButton(
child: new Icon(Icons.directions_run),
onPressed: () {
Random rng = new Random();
setState(() {
_miles += rng.nextInt(20) + 0.3;
_animation = new Tween<double>(
begin: _animation.value,
end: _miles,
).animate(new CurvedAnimation(
curve: Curves.fastOutSlowIn,
parent: _controller,
));
});
_controller.forward(from: 0.0);
}
),
);
}
#override
void dispose() {
_controller.dispose();
super.dispose();
}
}
You can use Countup package.
Countup(
begin: 0,
end: 7500,
duration: Duration(seconds: 3),
separator: ',',
style: TextStyle(
fontSize: 36,
),
)
https://pub.dev/packages/countup
You can simply use this plugin countup: ^0.1.3
import 'package:countup/countup.dart';
Countup(
begin: 100,
end: 8000,
duration: Duration(seconds: 3),
separator: ',',
style: TextStyle(
fontSize: 36,
fontweight : Fontweight.bold,
),
),

Resources