Flutter Animation Basic Example
Flutter animations let a widget move smoothly from one visual state to another. In this example, a Text widget grows from a small font size to a larger font size when the user presses a button.
The tutorial first shows an immediate font-size change and then replaces that abrupt update with an AnimationController and a Tween<double>.
Changing Flutter Text Size Without Animation
Consider the following Flutter application. It displays text with a font size of 20 and changes the font size to 40 when the button is pressed.
main.dart
import 'package:flutter/material.dart';
void main() {
runApp(MyApp());
}
class MyApp extends StatefulWidget {
@override
_MyAppState createState() => _MyAppState();
}
class _MyAppState extends State<MyApp> {
double _fontSize = 20;
void increaseFontSize() {
setState(() {
_fontSize = 40;
});
}
@override
Widget build(BuildContext context) {
return MaterialApp(
home: Scaffold(
appBar: AppBar(
title: Center(child: Text('Flutter - tutorialkart.com')),
),
body: ListView(children: <Widget>[
Container(
margin: EdgeInsets.all(20),
child: Text(
'Hello! Welcome to TutorialKart. We shall zoom this text when you long press on it.',
style: TextStyle(fontSize: _fontSize),
)),
RaisedButton(
onPressed: () => {increaseFontSize()},
child: Text('Bigger Font'),
)
]),
));
}
}
Calling setState() rebuilds the widget with the new value of _fontSize. The text jumps directly from 20 to 40 because no intermediate values are produced.
How the Flutter Font-Size Animation Works
To make the transition smooth, Flutter needs a sequence of values between the starting and ending font sizes. The animation in this example uses these parts:
AnimationControllercontrols the animation duration and progress.Tween<double>defines the range from the beginning font size to the ending font size.SingleTickerProviderStateMixinsupplies the ticker used by the controller.- An animation listener calls
setState()as the animation value changes.
The result is a gradual increase in the text size instead of an immediate jump.

Steps to Animate Text Font Size in Flutter
1. Import Flutter Animation Classes
Import the animation library so the application can use Animation, AnimationController, and Tween.
import 'package:flutter/animation.dart';
The Material library also exports many animation classes, but the explicit import makes the example’s dependency clear.
2. Add SingleTickerProviderStateMixin to the State Class
Declare the state class with SingleTickerProviderStateMixin. The mixin provides a ticker that advances the animation frame by frame while it is active.
class _MyAppState extends State<MyApp> with SingleTickerProviderStateMixin {
...
}
Use SingleTickerProviderStateMixin when the state owns one animation controller. A state that manages multiple controllers commonly uses TickerProviderStateMixin.
3. Declare the Animation and Controller
Declare an animation that produces double values and a controller that manages its timing.
Animation<double> animation;
AnimationController controller;
4. Configure the Font-Size Tween in initState()
Create the controller in initState(), set its duration to one second, and connect it to a tween that generates values from 12.0 to 50.0.
void initState() {
super.initState();
controller =
AnimationController(duration: const Duration(seconds: 1), vsync: this);
animation = Tween<double>(begin: 12.0, end: 50.0).animate(controller)
..addListener(() {
setState(() {
// The state that has changed here is the animation object’s value.
});
});
}
During the animation, animation.value changes repeatedly. The listener rebuilds the widget so the current value can be applied to TextStyle.fontSize.
5. Start the Flutter Animation
Call forward() when the user presses the button. The controller then progresses from its lower bound to its upper bound.
controller.forward();
After the animation has completed, calling forward() again does not restart it automatically. Call controller.reset() before forward(), or use controller.forward(from: 0), when the animation should replay from the beginning.
6. Dispose the AnimationController
An AnimationController uses a ticker and must be disposed when the state is removed. Calling controller.dispose() releases those resources and prevents the controller from continuing after the widget is gone.
Complete Flutter Animation Example
The following original example combines the controller, tween, listener, button callback, and cleanup logic in one file.
main.dart
import 'package:flutter/animation.dart';
import 'package:flutter/material.dart';
void main() {
runApp(MyApp());
}
class MyApp extends StatefulWidget {
@override
_MyAppState createState() => _MyAppState();
}
class _MyAppState extends State<MyApp> with SingleTickerProviderStateMixin {
Animation<double> animation;
AnimationController controller;
@override
void initState() {
super.initState();
controller =
AnimationController(duration: const Duration(seconds: 1), vsync: this);
animation = Tween<double>(begin: 12.0, end: 50.0).animate(controller)
..addListener(() {
setState(() {
// The state that has changed here is the animation object’s value.
});
});
}
void increaseFontSize() {
controller.forward();
}
@override
Widget build(BuildContext context) {
return MaterialApp(
home: Scaffold(
appBar: AppBar(
title: Center(child: Text('Flutter - tutorialkart.com')),
),
body: ListView(children: <Widget>[
Container(
margin: EdgeInsets.all(20),
child: Text(
'Hello! Welcome to TutorialKart. This is a basic demonstration of animation in Flutter.',
style: TextStyle(fontSize: animation.value),
)),
RaisedButton(
onPressed: () => {increaseFontSize()},
child: Text('Bigger Font'),
)
]),
));
}
@override
void dispose() {
controller.dispose();
super.dispose();
}
}
This code reflects the Flutter API style used when the tutorial was originally written. In current null-safe Flutter projects, fields should be initialized safely, and ElevatedButton is used instead of the deprecated RaisedButton.
Null-Safe Flutter Version with AnimatedBuilder
The following updated example uses null safety, ElevatedButton, a curved animation, and AnimatedBuilder. AnimatedBuilder rebuilds only the part of the interface that depends on the animation, so a manual animation listener is not required.
import 'package:flutter/material.dart';
void main() {
runApp(const MyApp());
}
class MyApp extends StatelessWidget {
const MyApp({super.key});
@override
Widget build(BuildContext context) {
return const MaterialApp(
home: FontSizeAnimationPage(),
);
}
}
class FontSizeAnimationPage extends StatefulWidget {
const FontSizeAnimationPage({super.key});
@override
State<FontSizeAnimationPage> createState() =>
_FontSizeAnimationPageState();
}
class _FontSizeAnimationPageState extends State<FontSizeAnimationPage>
with SingleTickerProviderStateMixin {
late final AnimationController _controller;
late final Animation<double> _fontSizeAnimation;
@override
void initState() {
super.initState();
_controller = AnimationController(
duration: const Duration(seconds: 1),
vsync: this,
);
_fontSizeAnimation = Tween<double>(
begin: 20,
end: 40,
).animate(
CurvedAnimation(
parent: _controller,
curve: Curves.easeInOut,
),
);
}
void _toggleFontSize() {
if (_controller.status == AnimationStatus.completed) {
_controller.reverse();
} else {
_controller.forward();
}
}
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: const Text('Flutter Font Size Animation'),
),
body: Padding(
padding: const EdgeInsets.all(20),
child: Column(
children: [
AnimatedBuilder(
animation: _fontSizeAnimation,
builder: (context, child) {
return Text(
'Press the button to animate this text.',
style: TextStyle(
fontSize: _fontSizeAnimation.value,
),
);
},
),
const SizedBox(height: 24),
ElevatedButton(
onPressed: _toggleFontSize,
child: const Text('Toggle Font Size'),
),
],
),
),
);
}
@override
void dispose() {
_controller.dispose();
super.dispose();
}
}
The button alternates between forward() and reverse(), so the text can grow and shrink repeatedly.
Using AnimatedDefaultTextStyle for a Simpler Text Animation
For a straightforward transition between two text styles, an implicit animation is often shorter. AnimatedDefaultTextStyle automatically interpolates the style whenever its style property changes.
class SimpleTextAnimation extends StatefulWidget {
const SimpleTextAnimation({super.key});
@override
State<SimpleTextAnimation> createState() => _SimpleTextAnimationState();
}
class _SimpleTextAnimationState extends State<SimpleTextAnimation> {
bool _isLarge = false;
@override
Widget build(BuildContext context) {
return Column(
children: [
AnimatedDefaultTextStyle(
duration: const Duration(milliseconds: 600),
curve: Curves.easeInOut,
style: TextStyle(
fontSize: _isLarge ? 40 : 20,
color: Colors.black,
),
child: const Text('Animated Flutter text'),
),
ElevatedButton(
onPressed: () {
setState(() {
_isLarge = !_isLarge;
});
},
child: const Text('Change Text Size'),
),
],
);
}
}
Use this implicit approach when you only need Flutter to animate between old and new style values. Use an AnimationController when you need direct control over starting, reversing, repeating, pausing, or coordinating the animation.
Common Flutter Animation Problems in This Example
- The animation runs only once: restart it with
forward(from: 0), reset the controller first, or reverse it after completion. - The text does not update: make sure the animated value is used by the widget and that the UI rebuilds through a listener,
AnimatedBuilder, or another animated widget. - A ticker warning appears: dispose the controller in
dispose(). - The app reports a late initialization error: initialize each
latecontroller and animation ininitState()before the build method uses them. - The old button class fails in a current project: replace
RaisedButtonwithElevatedButton.
Flutter Font-Size Animation FAQs
What does vsync do in AnimationController?
The vsync argument connects the controller to a ticker provider. This allows Flutter to schedule animation frames efficiently and stop unnecessary ticking when the widget is not visible.
Why is SingleTickerProviderStateMixin used?
It provides one ticker for one AnimationController. It is suitable when the state object manages a single controller.
How can the text animation run again?
Call controller.forward(from: 0) to replay it from the start. To alternate between larger and smaller text, call reverse() after the forward animation completes.
Should I use AnimatedDefaultTextStyle or AnimationController?
Use AnimatedDefaultTextStyle for a simple automatic transition between text styles. Use AnimationController when the animation needs precise timing, direction, repetition, status handling, or coordination with other animations.
Flutter Animation Example Summary
In this Flutter Tutorial, we learned how to animate the font size of a Text widget. The explicit example uses an AnimationController and Tween<double>, while the implicit example uses AnimatedDefaultTextStyle for a shorter implementation.
TutorialKart.com