We will do flutter 3d animation of card image using AnimationController, Tween and AnimationStatus.
We will do that using stateful class. Inside the class we will override initState() method and declare
@override
void initState() {
super.initState();
_controller =
AnimationController(vsync: this, duration: const Duration(seconds: 1));
_animation = Tween(end: 1.0, begin: 0.0).animate(_controller)
..addListener(() {
setState(() {
});
})
..addStatusListener((status) {
_status = status;
});
}
Here addListener() and addStatusListener() plays very important roles. Without them animation is not dynamic and not updated immediately. Because of this status listener, we can flip image inside the card.
See setState() inside addListener(). Because of this, UI gets the latest value and rebuild the UI.
We animate value between 0 and 1 and then use value inside Transform() widget do rotation using the rotate() function.
Of course we need to assign rotate() inside the transform property of Transform widget. We also need to make sure we are calling identity() and setEntry() in certain order.
setEntry() plays roles for perspective of viewer. If you change the value of setEntry() you will see the perspective is different.
void main() {
runApp(const MyApp());
}
class MyApp extends StatelessWidget {
const MyApp({Key? key}) : super(key: key);
@override
Widget build(BuildContext context) {
return MaterialApp(
// Remove the debug banner
debugShowCheckedModeBanner: false,
theme: ThemeData(
primarySwatch: Colors.blue,
),
home: const HomePage());
}
}
class HomePage extends StatefulWidget {
const HomePage({Key? key}) : super(key: key);
@override
State<HomePage> createState() => _HomePageState();
}
// don't forget "with SingleTickerProviderStateMixin"
class _HomePageState extends State<HomePage> with TickerProviderStateMixin {
late AnimationController _controller;
late Animation _animation;
AnimationStatus _status = AnimationStatus.dismissed;
@override
void initState() {
super.initState();
_controller =
AnimationController(vsync: this, duration: const Duration(seconds: 1));
_animation = Tween(end: 1.0, begin: 0.0).animate(_controller)
..addListener(() {
setState(() {
});
})
..addStatusListener((status) {
_status = status;
});
}
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: const Text('dbestech'),
),
body: Center(
child: Column(
crossAxisAlignment: CrossAxisAlignment.center,
children: [
const SizedBox(
height: 30,
),
Transform(
alignment: FractionalOffset.center,
transform: Matrix4.identity()
..setEntry(3, 2, 0.0015)
..rotateY(pi * _animation.value),
child: Card(
child: _animation.value <= 0.5
? Container(
color: Colors.blue,
width: 240,
height: 300,
child: const Center(
child: Text(
'?',
style: TextStyle(fontSize: 100, color: Colors.white),
)))
: Container(
width: 240,
height: 300,
color: Colors.grey,
child: Image.network(
'https://www.dbestech.com/img/mobile.png',
fit: BoxFit.cover,
)),
),
),
// Vertical Flipping
const SizedBox(
height: 30,
),
ElevatedButton(
onPressed: () {
if (_status == AnimationStatus.dismissed) {
_controller.forward();
} else {
_controller.reverse();
}
},
child: const Text('See inside'))
],
),
),
);
}
}
We also check the _animation.value, if this less than 0.5, we show another Container() widget with question mark in it.
We used this _animation.value with pi in rotate() function to change the value between 0 and pi.
flip the image,
child: Transform.scale(
scaleX: -1,
child: Image.network(
‘https://www.dbestech.com/img/mobile.png’,
fit: BoxFit.cover,
)),
),
)
ok. thanks