I want to shake the alert dialog like this: (not only the text, but also the entire pop up dialog)
https://www.youtube.com/watch?v=IaHMoifUBSw
How can I shake the whole alert dialog when user clicks a button?
This can be done with AnimatedBuilder
and Transform
widgets. Use sin
function from dart:math
to map the AnimationController
value between 0.0
to 1.0
into a smooth sinusoidal wave with desired amplitude. The period can be specified directly using duration
in the AnimationController
itself.
To start the animation, you can either call controller.repeat()
to make it run indefinitely, until you call controller.stop()
, or you can use controller.forward()
to run it once.
To let it shake 3 times then stop, for example, you can do this:
onPressed: () async {
await _controller.forward(from: 0.0);
await _controller.forward(from: 0.0);
await _controller.forward(from: 0.0);
},
This is how it looks in action (please bare with the frame rate limit of GIF):
Full source code for you to use as a starting point is attached below. You can adjust duration
and distance
to change the intensity of the shaking animation:
import 'dart:math';
import 'package:flutter/material.dart';
void main() {
runApp(MyApp());
}
class MyApp extends StatelessWidget {
@override
Widget build(BuildContext context) {
return MaterialApp(
home: Test(),
);
}
}
class Test extends StatelessWidget {
const Test({Key? key}) : super(key: key);
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(title: Text('Shaking Dialog Demo')),
body: Center(
child: ElevatedButton(
child: Text('Show Dialog'),
onPressed: () {
showDialog(
context: context,
builder: (_) => ShakeableDialog(),
);
},
),
),
);
}
}
class ShakeableDialog extends StatefulWidget {
final Duration duration; // how fast to shake
final double distance; // how far to shake
const ShakeableDialog({
Key? key,
this.duration = const Duration(milliseconds: 300),
this.distance = 24.0,
}) : super(key: key);
@override
_ShakeableDialogState createState() => _ShakeableDialogState();
}
class _ShakeableDialogState extends State<ShakeableDialog>
with SingleTickerProviderStateMixin {
late final _controller = AnimationController(
vsync: this,
duration: widget.duration,
);
@override
void dispose() {
_controller.dispose();
super.dispose();
}
@override
Widget build(BuildContext context) {
return AnimatedBuilder(
animation: _controller,
builder: (BuildContext context, Widget? child) {
final dx = sin(_controller.value * 2 * pi) * widget.distance;
return Transform.translate(
offset: Offset(dx, 0),
child: child,
);
},
child: AlertDialog(
title: Text('Alert Dialog Title'),
content: Text('Try these buttons!'),
actions: [
TextButton(
child: Text('SHAKE 3 TIMES'),
onPressed: () async {
await _controller.forward(from: 0.0);
await _controller.forward(from: 0.0);
await _controller.forward(from: 0.0);
},
),
TextButton(
child: Text('KEEP SHAKING'),
onPressed: () => _controller.repeat(),
),
TextButton(
child: Text('STOP SHAKING'),
onPressed: () => _controller.stop(),
),
TextButton(
child: Text('CLOSE'),
onPressed: () => Navigator.of(context).pop(),
),
],
),
);
}
}