UNIT-4: ANIMATION
1. Animated Container
2. App’s Navigation
3. Scrolling Lists and Effects
4. List View
5. Grid View
6. Making the UI responsive for web, Android and iOS
1. Introduction to Animations in Flutter
Animations in Flutter improve the user experience by adding motion and visual
appeal. Flutter provides built-in tools to create smooth and dynamic animations.
Animation is the process of creating the illusion of movement by displaying a
sequence of images or frames in rapid succession.
Types of Animations in Flutter
Implicit Animations: Automatically transition between values when the
widget’s state changes.
Explicit Animations: Require an AnimationController for fine-grained
control.
2. Implicit Animations
Implicit animations in Flutter simplify the process of adding animations by
automatically handling value transitions. These animations are easy to use because
they do not require an explicit AnimationController.
Key Features of Implicit Animations:
Minimal code, easy to implement.
Automatic interpolation of values.
Built-in widgets available for different animation effects.
Implicit Animation Widgets:
1. AnimatedContainer – Animates changes in size, color, padding, etc.
2. AnimatedOpacity – Smoothly transitions between different opacity levels.
3. AnimatedAlign – Animates movement within a parent container.
4. AnimatedCrossFade – Cross-fades between two widgets.
5. AnimatedPositioned – Animates position changes inside a Stack.
Example:
AnimatedContainer(
duration: Duration(seconds: 1),
width: _isExpanded ? 200 : 100,
height: _isExpanded ? 200 : 100,
color: _isExpanded ? Colors.blue : Colors.red,
child: GestureDetector(
onTap: () {
setState(() {
_isExpanded = !_isExpanded;
});
},
child: Center(child: Text("Tap Me!")),
),
)
3. Explicit Animations
Explicit animations require an AnimationController for fine control over animation
timing. It requires full control using an AnimationController to define custom animations.
Key Components of Explicit Animations:
AnimationController – Controls the animation duration and state.
Tween – Defines value ranges for animation (e.g., size, opacity).
AnimatedBuilder – Rebuilds widgets when animation values change.
Example: Using AnimationController
class ExplicitAnimation extends StatefulWidget {
@override
_ExplicitAnimationState createState() => _ExplicitAnimationState();
class _ExplicitAnimationState extends State<ExplicitAnimation> with
SingleTickerProviderStateMixin {
late AnimationController _controller;
late Animation<double> _animation;
@override
void initState() {
super.initState();
_controller = AnimationController(
duration: Duration(seconds: 2),
vsync: this,
)..repeat(reverse: true);
_animation = Tween<double>(begin: 50, end: 200).animate(_controller);
@override
void dispose() {
_controller.dispose();
super.dispose();
@override
Widget build(BuildContext context) {
return Center(
child: AnimatedBuilder(
animation: _animation,
builder: (context, child) {
return Container(
width: _animation.value,
height: _animation.value,
color: Colors.green,
);
},
),
);
4. Advanced Animations
Hero Animation (Page Transitions)
The Hero widget enables smooth transitions between screens.
Hero(
tag: 'hero-tag',
child: Image.asset('assets/sample.jpg'),
Custom Painter Animation
Used for drawing animations (e.g., charts, progress bars).
Lottie & Rive Animations
These allow integrating pre-built animations from design tools like Adobe After
Effects.
Example Program:
Implicit Animation using AnimatedContainer
import 'package:flutter/material.dart';
void main() => runApp(MyApp());
class MyApp extends StatelessWidget {
@override
Widget build(BuildContext context) {
return MaterialApp(
home: AnimatedContainerExample(),
);
class AnimatedContainerExample extends StatefulWidget {
@override
_AnimatedContainerExampleState createState() =>
_AnimatedContainerExampleState();
class _AnimatedContainerExampleState extends
State<AnimatedContainerExample> {
bool _isExpanded = false;
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(title: Text("Implicit Animation Example")),
body: Center(
child: GestureDetector(
onTap: () {
setState(() {
_isExpanded = !_isExpanded;
});
},
child: AnimatedContainer(
duration: Duration(seconds: 1),
width: _isExpanded ? 200 : 100,
height: _isExpanded ? 200 : 100,
color: _isExpanded ? Colors.blue : Colors.red,
alignment: Alignment.center,
child: Text("Tap Me!", style: TextStyle(color: Colors.white)),
),
),
),
);
}
Explicit Animation using AnimationController
Explicit animations provide more control and flexibility by using an
AnimationController. These animations allow developers to manage animations'
duration, curves, and states explicitly.
import 'package:flutter/material.dart';
void main() => runApp(MyApp());
class MyApp extends StatelessWidget {
@override
Widget build(BuildContext context) {
return MaterialApp(
home: ExplicitAnimationExample(),
);
}
}
class ExplicitAnimationExample extends StatefulWidget {
@override
_ExplicitAnimationExampleState createState() =>
_ExplicitAnimationExampleState();
}
class _ExplicitAnimationExampleState extends State<ExplicitAnimationExample>
with SingleTickerProviderStateMixin {
late AnimationController _controller;
late Animation<double> _animation;
@override
void initState() {
super.initState();
_controller = AnimationController(
duration: Duration(seconds: 2),
vsync: this,
)..repeat(reverse: true);
_animation = Tween<double>(begin: 50, end: 200).animate(_controller);
}
@override
void dispose() {
_controller.dispose();
super.dispose();
}
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(title: Text("Explicit Animation Example")),
body: Center(
child: AnimatedBuilder(
animation: _animation,
builder: (context, child) {
return Container(
width: _animation.value,
height: _animation.value,
color: Colors.green,
);
},
),
),
);
}
}
Hero Animation Between Screens:
import 'package:flutter/material.dart';
void main() => runApp(MyApp());
class MyApp extends StatelessWidget {
@override
Widget build(BuildContext context) {
return MaterialApp(home: FirstScreen());
}
}
class FirstScreen extends StatelessWidget {
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(title: Text("Hero Animation")),
body: Center(
child: GestureDetector(
onTap: () {
Navigator.push(
context,
MaterialPageRoute(builder: (context) => SecondScreen()),
);
},
child: Hero(
tag: 'hero-tag',
child: Container(width: 100, height: 100, color: Colors.orange),
),
),
),
);
}
}
class SecondScreen extends StatelessWidget {
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(title: Text("Hero Destination")),
body: Center(
child: Hero(
tag: 'hero-tag',
child: Container(width: 200, height: 200, color: Colors.orange),
),
),
);
}
}
Rotation Animation Using AnimatedBuilder:
import 'package:flutter/material.dart';
void main() => runApp(MyApp());
class MyApp extends StatelessWidget {
@override
Widget build(BuildContext context) {
return MaterialApp(home: RotationExample());
}
}
class RotationExample extends StatefulWidget {
@override
_RotationExampleState createState() => _RotationExampleState();
}
class _RotationExampleState extends State<RotationExample> with
SingleTickerProviderStateMixin {
late AnimationController _controller;
@override
void initState() {
super.initState();
_controller = AnimationController(
duration: Duration(seconds: 2),
vsync: this,
)..repeat(); // Continuous rotation
}
@override
void dispose() {
_controller.dispose();
super.dispose();
}
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(title: Text("Rotation Animation")),
body: Center(
child: AnimatedBuilder(
animation: _controller,
child: Container(width: 100, height: 100, color: Colors.blue),
builder: (context, child) {
return Transform.rotate(
angle: _controller.value * 6.28, // 2 * pi (Full rotation)
child: child,
);
},
),
),
);
}
App Navigation in Flutter
1. Introduction to Navigation in Flutter
Navigation in Flutter allows users to move between different screens (pages).
Flutter provides various methods for navigation, including:
Push and Pop Navigation (using Navigator)
Named Routes (using onGenerateRoute or routes map)
Bottom Navigation Bar (for switching between multiple tabs)
Drawer Navigation (for sidebar menu-based navigation)
Basic Navigation using Navigator
Flutter uses a stack-based approach for navigation. The Navigator widget manages
the stack of routes.
Navigating to a New Screen (push)
Navigator.push(
context,
MaterialPageRoute(builder: (context) => SecondScreen()),
);
Going Back to the Previous Screen (pop)
Navigator.pop(context);
Example: Basic Navigation
import 'package:flutter/material.dart';
void main() {
runApp(MyApp());
}
class MyApp extends StatelessWidget {
@override
Widget build(BuildContext context) {
return MaterialApp(
home: FirstScreen(),
);
class FirstScreen extends StatelessWidget {
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(title: Text("First Screen")),
body: Center(
child: ElevatedButton(
onPressed: () {
Navigator.push(
context,
MaterialPageRoute(builder: (context) => SecondScreen()),
);
},
child: Text("Go to Second Screen"),
),
),
);
class SecondScreen extends StatelessWidget {
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(title: Text("Second Screen")),
body: Center(
child: ElevatedButton(
onPressed: () {
Navigator.pop(context);
},
child: Text("Go Back"),
),
),
);
Named Routes Navigation
Defining Named Routes
void main() {
runApp(MaterialApp(
initialRoute: '/',
routes: {
'/': (context) => HomeScreen(),
'/second': (context) => SecondScreen(),
},
));
Navigating with Named Routes
Navigator.pushNamed(context, '/second');
Going Back
Navigator.pop(context);
Passing Data Between Screens
Passing Data to Another Screen
Navigator.push(
context,
MaterialPageRoute(
builder: (context) => SecondScreen(data: "Hello from First Screen"),
),
);
Receiving Data in the Second Screen
class SecondScreen extends StatelessWidget {
final String data;
SecondScreen({required this.data});
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(title: Text("Second Screen")),
body: Center(child: Text(data)),
);
Bottom Navigation Bar
import 'package:flutter/material.dart';
void main() => runApp(MyApp());
class MyApp extends StatelessWidget {
@override
Widget build(BuildContext context) {
return MaterialApp(home: HomeScreen());
}
class HomeScreen extends StatefulWidget {
@override
_HomeScreenState createState() => _HomeScreenState();
class _HomeScreenState extends State<HomeScreen> {
int _selectedIndex = 0;
final List<Widget> _screens = [ScreenOne(), ScreenTwo()];
void _onItemTapped(int index) {
setState(() {
_selectedIndex = index;
});
@override
Widget build(BuildContext context) {
return Scaffold(
body: _screens[_selectedIndex],
bottomNavigationBar: BottomNavigationBar(
currentIndex: _selectedIndex,
onTap: _onItemTapped,
items: [
BottomNavigationBarItem(icon: Icon(Icons.home), label: "Home"),
BottomNavigationBarItem(icon: Icon(Icons.settings), label: "Settings"),
],
),
);
class ScreenOne extends StatelessWidget {
@override
Widget build(BuildContext context) {
return Center(child: Text("Home Screen"));
class ScreenTwo extends StatelessWidget {
@override
Widget build(BuildContext context) {
return Center(child: Text("Settings Screen"));
Drawer Navigation (Sidebar Menu)
Drawer(
child: ListView(
children: [
DrawerHeader(
child: Text("Menu"),
decoration: BoxDecoration(color: Colors.blue),
),
ListTile(
title: Text("Home"),
onTap: () {
Navigator.pushNamed(context, '/');
},
),
ListTile(
title: Text("Settings"),
onTap: () {
Navigator.pushNamed(context, '/settings');
},
),
],
),
Basic Navigation
import 'package:flutter/material.dart';
void main() {
runApp(MyApp());
class MyApp extends StatelessWidget {
@override
Widget build(BuildContext context) {
return MaterialApp(
home: FirstScreen(),
);
class FirstScreen extends StatelessWidget {
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(title: Text("First Screen")),
body: Center(
child: ElevatedButton(
onPressed: () {
Navigator.push(
context,
MaterialPageRoute(builder: (context) => SecondScreen()),
);
},
child: Text("Go to Second Screen"),
),
),
);
}
class SecondScreen extends StatelessWidget {
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(title: Text("Second Screen")),
body: Center(
child: ElevatedButton(
onPressed: () {
Navigator.pop(context);
},
child: Text("Back to First Screen"),
),
),
);
Named Routes Navigation
import 'package:flutter/material.dart';
void main() {
runApp(MyApp());
class MyApp extends StatelessWidget {
@override
Widget build(BuildContext context) {
return MaterialApp(
initialRoute: '/',
routes: {
'/': (context) => FirstScreen(),
'/second': (context) => SecondScreen(),
},
);
class FirstScreen extends StatelessWidget {
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(title: Text("First Screen")),
body: Center(
child: ElevatedButton(
onPressed: () {
Navigator.pushNamed(context, '/second');
},
child: Text("Go to Second Screen"),
),
),
);
class SecondScreen extends StatelessWidget {
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(title: Text("Second Screen")),
body: Center(
child: ElevatedButton(
onPressed: () {
Navigator.pop(context);
},
child: Text("Back to First Screen"),
),
),
);
Drawer Navigation
import 'package:flutter/material.dart';
void main() {
runApp(MyApp());
}
class MyApp extends StatelessWidget {
@override
Widget build(BuildContext context) {
return MaterialApp(
home: HomeScreen(),
);
class HomeScreen extends StatelessWidget {
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(title: Text("Drawer Example")),
drawer: Drawer(
child: ListView(
padding: EdgeInsets.zero,
children: <Widget>[
DrawerHeader(
decoration: BoxDecoration(
color: Colors.blue,
),
child: Text(
"Drawer Header",
style: TextStyle(
color: Colors.white,
fontSize: 24,
),
),
),
ListTile(
leading: Icon(Icons.home),
title: Text("Home"),
onTap: () {
Navigator.pop(context);
},
),
ListTile(
leading: Icon(Icons.settings),
title: Text("Settings"),
onTap: () {
Navigator.pop(context);
},
),
ListTile(
leading: Icon(Icons.contact_mail),
title: Text("Contact"),
onTap: () {
Navigator.pop(context);
},
),
],
),
),
body: Center(child: Text("Swipe from left or tap the menu icon!")),
);