Repeat animation only once - flutter

There's an interactive example in the flutter docs with slide transition. The animations looks great but I can't stop the animation from constantly repeating. There's a repeat method used on the AnimationController that seems to control the looping of the animation. But I can't see where you can for instance run the animation twice?
Here is the code for connivence ..
/ The following code implements the [SlideTransition] as seen in the video
// above:
import 'package:flutter/material.dart';
void main() => runApp(const MyApp());
/// This is the main application widget.
class MyApp extends StatelessWidget {
const MyApp({Key? key}) : super(key: key);
static const String _title = 'Flutter Code Sample';
#override
Widget build(BuildContext context) {
return MaterialApp(
title: _title,
home: Scaffold(
appBar: AppBar(title: const Text(_title)),
body: const Center(
child: MyStatefulWidget(),
),
),
);
}
}
/// This is the stateful widget that the main application instantiates.
class MyStatefulWidget extends StatefulWidget {
const MyStatefulWidget({Key? key}) : super(key: key);
#override
_MyStatefulWidgetState createState() => _MyStatefulWidgetState();
}
/// This is the private State class that goes with MyStatefulWidget.
class _MyStatefulWidgetState extends State<MyStatefulWidget>
with SingleTickerProviderStateMixin {
late final AnimationController _controller = AnimationController(
duration: const Duration(seconds: 2),
vsync: this,
)..repeat(reverse: true);
late final Animation<Offset> _offsetAnimation = Tween<Offset>(
begin: Offset.zero,
end: const Offset(1.5, 0.0),
).animate(CurvedAnimation(
parent: _controller,
curve: Curves.elasticIn,
));
#override
void dispose() {
super.dispose();
_controller.dispose();
}
#override
Widget build(BuildContext context) {
return SlideTransition(
position: _offsetAnimation,
child: const Padding(
padding: EdgeInsets.all(8.0),
child: FlutterLogo(size: 150.0),
),
);
}
}

Use forward and wait for it to resolve and then call reverse
#override
void initState() {
super.initState();
repeatOnce();
}
void repeatOnce() async {
await _controller.forward();
await _controller.reverse();
}
The Full example
/// Flutter code sample for SlideTransition
// The following code implements the [SlideTransition] as seen in the video
// above:
import 'package:flutter/material.dart';
void main() => runApp(const MyApp());
/// This is the main application widget.
class MyApp extends StatelessWidget {
const MyApp({Key? key}) : super(key: key);
static const String _title = 'Flutter Code Sample';
#override
Widget build(BuildContext context) {
return MaterialApp(
title: _title,
home: Scaffold(
appBar: AppBar(title: const Text(_title)),
body: const Center(
child: MyStatefulWidget(),
),
),
);
}
}
/// This is the stateful widget that the main application instantiates.
class MyStatefulWidget extends StatefulWidget {
const MyStatefulWidget({Key? key}) : super(key: key);
#override
_MyStatefulWidgetState createState() => _MyStatefulWidgetState();
}
/// This is the private State class that goes with MyStatefulWidget.
class _MyStatefulWidgetState extends State<MyStatefulWidget>
with SingleTickerProviderStateMixin {
late final AnimationController _controller = AnimationController(
duration: const Duration(seconds: 2),
vsync: this,
);
late final Animation<Offset> _offsetAnimation = Tween<Offset>(
begin: Offset.zero,
end: const Offset(1.5, 0.0),
).animate(CurvedAnimation(
parent: _controller,
curve: Curves.elasticIn,
));
#override
void initState() {
super.initState();
repeatOnce();
}
void repeatOnce() async {
await _controller.forward();
await _controller.reverse();
}
#override
void dispose() {
super.dispose();
_controller.dispose();
}
#override
Widget build(BuildContext context) {
return SlideTransition(
position: _offsetAnimation,
child: const Padding(
padding: EdgeInsets.all(8.0),
child: FlutterLogo(size: 150.0),
),
);
}
}

Related

In Flutter, how to contain the size of SizeTransition

Following Flutter's example of a SizeTransition,
How can I constrain the width and height of the SizeTransition widget to be the size of the child widget (Flutter's logo in this example)?
Here is the code (copied from the example):
import 'package:flutter/material.dart';
void main() => runApp(const MyApp());
class MyApp extends StatelessWidget {
const MyApp({super.key});
static const String _title = 'Flutter Code Sample';
#override
Widget build(BuildContext context) {
return const MaterialApp(
title: _title,
home: MyStatefulWidget(),
);
}
}
class MyStatefulWidget extends StatefulWidget {
const MyStatefulWidget({super.key});
#override
State<MyStatefulWidget> createState() => _MyStatefulWidgetState();
}
/// AnimationControllers can be created with `vsync: this` because of TickerProviderStateMixin.
class _MyStatefulWidgetState extends State<MyStatefulWidget>
with TickerProviderStateMixin {
late final AnimationController _controller = AnimationController(
duration: const Duration(seconds: 3),
vsync: this,
)..repeat();
late final Animation<double> _animation = CurvedAnimation(
parent: _controller,
curve: Curves.fastOutSlowIn,
);
#override
void dispose() {
_controller.dispose();
super.dispose();
}
#override
Widget build(BuildContext context) {
return Scaffold(
body: SizeTransition(
sizeFactor: _animation,
axis: Axis.horizontal,
axisAlignment: -1,
child: const Center(
child: FlutterLogo(size: 200.0),
),
),
);
}
}

How can use AnimationController in Cubit or Bloc?

I want to use AnimationController in the StatelessWidget class with Cubit or Bloc in a flutter, if anyone can help me with an example link to explain?
I think the general practice is to avoid using presentation layer components in Blocs. I would use Bloc to manage my state value, but run the animation components in a stateful widget.
Blocs
part 'side_bloc.freezed.dart';
typedef StateEmitter = Emitter<SideState>;
class SideBloc extends Bloc<SideEvent, SideState> {
SideBloc() : super(const SideState(20)) {
on<SideIncrement>(onIncrement);
on<SideDecrement>(onDecrement);
}
void onIncrement(SideIncrement event, StateEmitter emit) {
emit(state.copyWith(side: state.side + 10));
}
void onDecrement(SideDecrement event, StateEmitter emit) {
if (state.side <= 10) {
return;
}
emit(state.copyWith(side: state.side - 10));
}
}
#freezed
class SideEvent with _$SideEvent {
const factory SideEvent.increment() = SideIncrement;
const factory SideEvent.decrement() = SideDecrement;
}
#freezed
class SideState with _$SideState {
const factory SideState(int side) = _SideState;
}
Animated component
class HomePage extends StatefulWidget {
const HomePage({Key? key}) : super(key: key);
#override
State<HomePage> createState() => _HomePageState();
}
class _HomePageState extends State<HomePage>
with SingleTickerProviderStateMixin {
late AnimationController _controller;
late Animation<int> _animation;
late CurvedAnimation _curvedAnimation;
#override
void initState() {
_controller = AnimationController(
vsync: this,
duration: const Duration(seconds: 1),
);
_curvedAnimation = CurvedAnimation(
parent: _controller,
curve: Curves.elasticOut,
);
_animation = IntTween(begin: 20, end: 20).animate(_curvedAnimation);
_controller.forward();
super.initState();
}
#override
void dispose() {
_controller.dispose();
super.dispose();
}
void _animateTo(int value) {
int old = _animation.value;
_controller.reset();
_animation = IntTween(begin: old, end: value).animate(
_curvedAnimation,
);
_controller.forward();
}
#override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: const Text('Home'),
),
body: BlocListener<SideBloc, SideState>(
listener: (context, state) {
_animateTo(state.side);
},
child: AnimatedBuilder(
animation: _animation,
builder: (context, child) {
return SizedSquare(side: _animation.value);
},
),
),
floatingActionButton: const SideChangeButtons(),
);
}
}
class SideChangeButtons extends StatelessWidget {
const SideChangeButtons({Key? key}) : super(key: key);
#override
Widget build(BuildContext context) {
return Column(
mainAxisAlignment: MainAxisAlignment.end,
children: [
FloatingActionButton(
onPressed: () =>
context.read<SideBloc>().add(const SideEvent.increment()),
child: const Icon(Icons.add),
),
const SizedBox(height: 8),
FloatingActionButton(
onPressed: () =>
context.read<SideBloc>().add(const SideEvent.decrement()),
child: const Icon(Icons.remove),
),
],
);
}
}
class SizedSquare extends StatelessWidget {
final int side;
const SizedSquare({
Key? key,
required this.side,
}) : super(key: key);
#override
Widget build(BuildContext context) {
return Center(
child: SizedBox.fromSize(
size: Size.square(side.toDouble()),
child: Container(color: Colors.red),
),
);
}
}
AnimationController works with TickerProviderStateMixin which means that you MUST use a statefulWidget somewhere in your widget tree.

Flutter - Custom Animation Using CustomPaint

I created a CustomPaint widget and I want to change the heigth from zero to end of screen with smooth animation as shown in the image.
You can use Flutter's AnimationController to drive explicit CustomPaint animation as follows (DartPad):
class AnimatedHeightCustomPaint extends StatefulWidget {
final AnimationController controller;
final Size size;
final Color color;
final Curve curve;
const AnimatedHeightCustomPaint({Key? key,
required this.controller,
required this.size,
required this.color,
this.curve = Curves.linear}) : super(key: key);
#override
State<StatefulWidget> createState() => AnimatedHeightCustomPaintState();
}
class AnimatedHeightCustomPaintState extends State<AnimatedHeightCustomPaint> {
#override
void initState() {
super.initState();
// Listen to the animation progress and update the custom paint (height in this case)
widget.controller.addListener(() => setState(() { }));
}
#override
Widget build(BuildContext context) => CustomPaint(
painter: AnimatedHeightPainter(
// Here we can apply some fancy animation progress like bounce in
heightProps: widget.curve.transform(widget.controller.value),
color: widget.color,
),
// Since you want to change the height, you need to provide a size
size: widget.size,
);
}
class AnimatedHeightPainter extends CustomPainter
{
// Progress of the animation, i.e. between 0.0 and 1.0
final double heightProps;
final Color color;
AnimatedHeightPainter({required this.heightProps, required this.color});
#override
void paint(Canvas canvas, Size size) {
canvas.drawRect(Offset(0.0, size.height * (1 - heightProps)) & Size(size.width, size.height * heightProps),
Paint()..color = color,
);
}
#override
bool shouldRepaint(covariant CustomPainter oldDelegate) => true;
}
As a complete sample,
import 'package:flutter/material.dart';
void main() => runApp(MyApp());
class MyApp extends StatelessWidget {
#override
Widget build(BuildContext context) {
return MaterialApp(
title: 'Flutter Demo',
debugShowCheckedModeBanner: false,
theme: ThemeData(
primarySwatch: Colors.blue,
),
home: const MyHomePage(title: 'Flutter Demo Home Page'),
);
}
}
class MyHomePage extends StatefulWidget {
final String title;
const MyHomePage({
Key? key,
required this.title,
}) : super(key: key);
#override
_MyHomePageState createState() => _MyHomePageState();
}
class _MyHomePageState extends State<MyHomePage> with SingleTickerProviderStateMixin {
// Flutter's AnimationController
late AnimationController _animationController;
#override
void initState() {
super.initState();
_animationController = AnimationController(
vsync: this, // the SingleTickerProviderStateMixin
duration: const Duration(seconds: 2),
);
}
#override
void dispose() {
_animationController.dispose();
super.dispose();
}
#override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: Text(widget.title),
),
body: Center(
child: AnimatedHeightCustomPaint(
controller: _animationController,
size: MediaQuery.of(context).size,
color: Colors.red,
curve: Curves.bounceInOut,
),
),
floatingActionButton: FloatingActionButton(
onPressed: () {
// In Parent widget, you can control the animation (back and forth here)
if(_animationController.isCompleted) {
_animationController.reverse();
}else if(_animationController.isDismissed) {
_animationController.forward();
}
},
tooltip: 'Increment',
child: const Icon(Icons.add),
),
);
}
}
class AnimatedHeightCustomPaint extends StatefulWidget {
final AnimationController controller;
final Size size;
final Color color;
final Curve curve;
const AnimatedHeightCustomPaint({Key? key,
required this.controller,
required this.size,
required this.color,
this.curve = Curves.linear}) : super(key: key);
#override
State<StatefulWidget> createState() => AnimatedHeightCustomPaintState();
}
class AnimatedHeightCustomPaintState extends State<AnimatedHeightCustomPaint> {
#override
void initState() {
super.initState();
// Listen to the animation progress and update the custom paint (height in this case)
widget.controller.addListener(() => setState(() { }));
}
#override
Widget build(BuildContext context) => CustomPaint(
painter: AnimatedHeightPainter(
// Here we can apply some fancy animation progress like bounce in
heightProps: widget.curve.transform(widget.controller.value),
color: widget.color,
),
// Since you want to change the height, you need to provide a size
size: widget.size,
);
}
class AnimatedHeightPainter extends CustomPainter
{
// Progress of the animation, i.e. between 0.0 and 1.0
final double heightProps;
final Color color;
AnimatedHeightPainter({required this.heightProps, required this.color});
#override
void paint(Canvas canvas, Size size) {
canvas.drawRect(Offset(0.0, size.height * (1 - heightProps)) & Size(size.width, size.height * heightProps),
Paint()..color = color,
);
}
#override
bool shouldRepaint(covariant CustomPainter oldDelegate) => true;
}
Perhaps You can even use AnimatedContainer if there is no specific reason to use CustomPaint widget (DartPad):
import 'package:flutter/material.dart';
void main() => runApp(MyApp());
class MyApp extends StatelessWidget {
#override
Widget build(BuildContext context) {
return MaterialApp(
title: 'Flutter Demo',
debugShowCheckedModeBanner: false,
theme: ThemeData(
primarySwatch: Colors.blue,
),
home: const MyHomePage(title: 'Flutter Demo Home Page'),
);
}
}
class MyHomePage extends StatefulWidget {
final String title;
const MyHomePage({
Key? key,
required this.title,
}) : super(key: key);
#override
_MyHomePageState createState() => _MyHomePageState();
}
class _MyHomePageState extends State<MyHomePage> with SingleTickerProviderStateMixin {
// Flutter's AnimationController
bool isExpanded = true;
#override
Widget build(BuildContext context) {
final screenSize = MediaQuery.of(context).size;
return Scaffold(
appBar: AppBar(
title: Text(widget.title),
),
body: Stack(
children: [
Align(
alignment: Alignment.bottomCenter,
child: AnimatedContainer(
curve: Curves.bounceInOut,
width: screenSize.width,
height: screenSize.height * (isExpanded ? 1: 0),
duration: const Duration(seconds: 2),
color: Colors.red,
),
),
],
),
floatingActionButton: FloatingActionButton(
onPressed: () {
setState(() => isExpanded = !isExpanded);
},
tooltip: 'Increment',
child: const Icon(Icons.add),
),
);
}
}

Flutter pageview navigate

How can I use pageController.jumptopage(0) method on button in Page3.dart ? Is it possible? Full example code:
main.dart file
import 'package:flutter/material.dart';
import 'package:test_app/page1.dart';
import 'package:test_app/page2.dart';
import 'package:test_app/page3.dart';
void main() {
runApp(const MyApp());
}
class MyApp extends StatelessWidget {
const MyApp({Key? key}) : super(key: key);
#override
Widget build(BuildContext context) {
return const MaterialApp(
title: 'Flutter Demo',
home: MyHomePage(title: 'Flutter Demo Home Page'),
);
}
}
class MyHomePage extends StatefulWidget {
const MyHomePage({Key? key, required this.title}) : super(key: key);
final String title;
#override
State<MyHomePage> createState() => _MyHomePageState();
}
class _MyHomePageState extends State<MyHomePage> {
late PageController pageController;
int selectedPage = 0;
final List<Widget> pagesList = [const Page1(), const Page2(), const Page3()];
#override
void initState() {
super.initState();
pageController = PageController();
}
#override
void dispose() {
pageController.dispose();
super.dispose();
}
#override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: Text(widget.title),
),
body: PageView(
controller: pageController,
children: pagesList,
onPageChanged: (index) {
setState(() => selectedPage = index);
},
),
);
}
}
Page3.dart file
import 'package:flutter/material.dart';
class Page3 extends StatelessWidget {
const Page3({Key? key}) : super(key: key);
#override
Widget build(BuildContext context) {
return Container(
alignment: Alignment.center,
decoration: const BoxDecoration(
color: Colors.red,
),
child: ElevatedButton(
child: const Text('Button'),
onPressed: () {
// pageController.jumptopage(0); <<< use this
}),
);
}
}

Rotation Transition to rotate anticlockwise flutter

I have this code that makes the icon rotate clockwise. I want to make it rotate anticlockwise. how can I do that?
animationController =
AnimationController(vsync: this, duration: Duration(seconds: 3))
..repeat(reverse: true);
RotationTransition(
turns: animationController,
child: Icon(Icons.settings_sharp),
alignment: Alignment.center,
)
Concerning RotationTransition widget rotation direction is manipulated by turns property.
When widget rotates clockwise controllers value goes from 0.0 to 1.0.
To rotate in opposite direction you need value to go from 1.0. to 0.0.
To achieve that, you can set up Tween:
final Tween<double> turnsTween = Tween<double>(
begin: 1,
end: 0,
);
And animate this tween in turns property with any AnimationController you need:
child: RotationTransition(
turns: turnsTween.animate(_controller),
...
Sample result and code to reproduce:
import 'package:flutter/material.dart';
void main() => runApp(const MyApp());
/// This is the main application widget.
class MyApp extends StatelessWidget {
const MyApp({Key? key}) : super(key: key);
static const String _title = 'Flutter Code Sample';
#override
Widget build(BuildContext context) {
return const MaterialApp(
title: _title,
home: MyStatefulWidget(),
);
}
}
/// This is the stateful widget that the main application instantiates.
class MyStatefulWidget extends StatefulWidget {
const MyStatefulWidget({Key? key}) : super(key: key);
#override
State<MyStatefulWidget> createState() => _MyStatefulWidgetState();
}
/// This is the private State class that goes with MyStatefulWidget.
/// AnimationControllers can be created with `vsync: this` because of TickerProviderStateMixin.
class _MyStatefulWidgetState extends State<MyStatefulWidget>
with TickerProviderStateMixin {
late final AnimationController _controller = AnimationController(
duration: const Duration(seconds: 4),
vsync: this,
)..repeat();
final Tween<double> turnsTween = Tween<double>(
begin: 1,
end: 0,
);
#override
void dispose() {
_controller.dispose();
super.dispose();
}
#override
Widget build(BuildContext context) {
return Scaffold(
body: Center(
child: RotationTransition(
turns: turnsTween.animate(_controller),
child: const Padding(
padding: EdgeInsets.all(8.0),
child: FlutterLogo(size: 150.0),
),
),
),
);
}
}
You can add in an AnimationBuilder and Animation object and set the end Tween property to negative.
Also, It looked like you were attempting to have the widget reverse rotation upon completion in a cycle? If so, I added in the code for that too if it helps :)
For example:
#override
void initState() {
_controller =
AnimationController(vsync: this, duration: Duration(seconds: 3))
//To have the widget change rotation direction upon completion
..addStatusListener((status) {
if(status == AnimationStatus.completed)
_controller.reverse();
if(status == AnimationStatus.dismissed){
_controller.forward();
}
});
_animTurn = Tween<double>(begin: 0.0, end: -1.0).animate(_controller);
_controller.forward();
super.initState();
}
#override
void dispose() {
_controller.dispose();
super.dispose();
}
#override
Widget build(BuildContext context) {
return AnimatedBuilder(
animation: _controller,
builder: (context, child) {
return
RotationTransition(
turns: _animTurn,
child: Icon(Icons.settings_sharp,size: 100,),
alignment: Alignment.center,
);
});
}
}