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.
CodePudding user response:
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),
),
);
}
}