我正在尝试制作dart
中的自定义对话框的动画,以便当它弹出时创建一些动画。 Android
中有一个带有动画对话框的库,Flutter
Sweet Alert Dialog中有任何类似的库
如何在颤振中实现相同的功能?
答案 0 :(得分:8)
要创建对话框,可以使用Overlay 或 Dialog类。如果要像在给定框架中那样添加动画,则可以像以下示例中那样使用AnimationController。 CurvedAnimation类用于在动画上创建弹跳效果。
更新:通常,最好使用showDialog功能显示对话框,因为关闭和手势由Flutter处理。我已经更新了该示例,该示例现在与showDialog
一起运行,您可以通过点击背景关闭该对话框。
您可以将以下代码复制并粘贴到新项目中并进行调整。它本身可以运行。
import 'package:flutter/material.dart';
void main() => runApp(new MyApp());
class MyApp extends StatelessWidget {
@override
Widget build(BuildContext context) {
return MaterialApp(title: 'Flutter Demo', theme: ThemeData(), home: Page());
}
}
class Page extends StatelessWidget {
@override
Widget build(BuildContext context) {
return Scaffold(
body: Center(
child: RaisedButton.icon(
onPressed: () {
showDialog(
context: context,
builder: (_) => FunkyOverlay(),
);
},
icon: Icon(Icons.message),
label: Text("PopUp!")),
),
);
}
}
class FunkyOverlay extends StatefulWidget {
@override
State<StatefulWidget> createState() => FunkyOverlayState();
}
class FunkyOverlayState extends State<FunkyOverlay>
with SingleTickerProviderStateMixin {
AnimationController controller;
Animation<double> scaleAnimation;
@override
void initState() {
super.initState();
controller =
AnimationController(vsync: this, duration: Duration(milliseconds: 450));
scaleAnimation =
CurvedAnimation(parent: controller, curve: Curves.elasticInOut);
controller.addListener(() {
setState(() {});
});
controller.forward();
}
@override
Widget build(BuildContext context) {
return Center(
child: Material(
color: Colors.transparent,
child: ScaleTransition(
scale: scaleAnimation,
child: Container(
decoration: ShapeDecoration(
color: Colors.white,
shape: RoundedRectangleBorder(
borderRadius: BorderRadius.circular(15.0))),
child: Padding(
padding: const EdgeInsets.all(50.0),
child: Text("Well hello there!"),
),
),
),
),
);
}
}
答案 1 :(得分:2)
每当您要显示带有某些动画的Dialog
时,最好的方法是使用showGeneralDialog()
注意:必须提供所有参数,否则会发生某些错误。
showGeneralDialog(
barrierColor: Colors.black.withOpacity(0.5), //SHADOW EFFECT
transitionBuilder: (context, a1, a2, widget) {
return Center(
child: Container(
height: 100.0 * a1.value, // USE PROVIDED ANIMATION
width: 100.0 * a1.value,
color: Colors.blue,
),
);
},
transitionDuration: Duration(milliseconds: 200), // DURATION FOR ANIMATION
barrierDismissible: true,
barrierLabel: 'LABEL',
context: context,
pageBuilder: (context, animation1, animation2) {
return Text('PAGE BUILDER');
});
}, child: Text('Show Dialog'),),
如果需要更多自定义,请扩展PopupRoute并创建自己的_DialogRoute<T>
和showGeneralDialog()
编辑
已编辑Niklas的答案,具有关闭Overlay
的功能:)
void main() => runApp(new MyApp());
class MyApp extends StatelessWidget {
@override
Widget build(BuildContext context) {
return MaterialApp(title: 'Flutter Demo', theme: ThemeData(), home: Page());
}
}
class Page extends StatelessWidget {
@override
Widget build(BuildContext context) {
return Scaffold(
body: Center(
child: RaisedButton.icon(
onPressed: () {
OverlayEntry overlayEntry;
overlayEntry = OverlayEntry(builder: (c) {
return FunkyOverlay(onClose: () => overlayEntry.remove());
});
Overlay.of(context).insert(overlayEntry);
},
icon: Icon(Icons.message),
label: Text("PopUp!")),
),
);
}
}
class FunkyOverlay extends StatefulWidget {
final VoidCallback onClose;
const FunkyOverlay({Key key, this.onClose}) : super(key: key);
@override
State<StatefulWidget> createState() => FunkyOverlayState();
}
class FunkyOverlayState extends State<FunkyOverlay>
with SingleTickerProviderStateMixin {
AnimationController controller;
Animation<double> opacityAnimation;
Animation<double> scaleAnimatoin;
@override
void initState() {
super.initState();
controller =
AnimationController(vsync: this, duration: Duration(milliseconds: 450));
opacityAnimation = Tween<double>(begin: 0.0, end: 0.4).animate(
CurvedAnimation(parent: controller, curve: Curves.fastOutSlowIn));
scaleAnimatoin =
CurvedAnimation(parent: controller, curve: Curves.elasticInOut);
controller.addListener(() {
setState(() {});
});
controller.forward();
}
@override
Widget build(BuildContext context) {
return Material(
color: Colors.black.withOpacity(opacityAnimation.value),
child: Center(
child: ScaleTransition(
scale: scaleAnimatoin,
child: Container(
decoration: ShapeDecoration(
color: Colors.white,
shape: RoundedRectangleBorder(
borderRadius: BorderRadius.circular(15.0))),
child: Padding(
padding: const EdgeInsets.all(50.0),
child: OutlineButton(onPressed: widget.onClose, child: Text('Close!'),),
),
),
),
),
);
}
}
答案 2 :(得分:1)
我尝试制作您gif中显示的动画。会发布代码来帮助需要它的人,它并不完美,所以如果有人想帮助改进它,那就去吧。
外观:
代码:
import 'package:flutter/material.dart';
import 'package:angles/angles.dart';
import 'dart:math';
import 'dart:core';
class CheckAnimation extends StatefulWidget {
final double size;
final VoidCallback onComplete;
CheckAnimation({this.size, this.onComplete});
@override
_CheckAnimationState createState() => _CheckAnimationState();
}
class _CheckAnimationState extends State<CheckAnimation>
with SingleTickerProviderStateMixin {
AnimationController _controller;
Animation<double> curve;
@override
void initState() {
// TODO: implement initState
super.initState();
_controller =
AnimationController(duration: Duration(seconds: 2), vsync: this);
curve = CurvedAnimation(parent: _controller, curve: Curves.bounceInOut);
_controller.addListener(() {
setState(() {});
if(_controller.status == AnimationStatus.completed && widget.onComplete != null){
widget.onComplete();
}
});
_controller.forward();
}
@override
Widget build(BuildContext context) {
return Container(
height: widget.size ?? 100,
width: widget.size ?? 100,
color: Colors.transparent,
child: CustomPaint(
painter: CheckPainter(value: curve.value),
),
);
}
@override
void dispose() {
// TODO: implement dispose
_controller.dispose();
super.dispose();
}
}
class CheckPainter extends CustomPainter {
Paint _paint;
double value;
double _length;
double _offset;
double _secondOffset;
double _startingAngle;
CheckPainter({this.value}) {
_paint = Paint()
..color = Colors.greenAccent
..strokeWidth = 5.0
..strokeCap = StrokeCap.round
..style = PaintingStyle.stroke;
assert(value != null);
_length = 60;
_offset = 0;
_startingAngle = 205;
}
@override
void paint(Canvas canvas, Size size) {
// Background canvas
var rect = Offset(0, 0) & size;
_paint.color = Colors.greenAccent.withOpacity(.05);
double line1x1 = size.width / 2 +
size.width * cos(Angle.fromDegrees(_startingAngle).radians) * .5;
double line1y1 = size.height / 2 +
size.height * sin(Angle.fromDegrees(_startingAngle).radians) * .5;
double line1x2 = size.width * .45;
double line1y2 = size.height * .65;
double line2x1 =
size.width / 2 + size.width * cos(Angle.fromDegrees(320).radians) * .35;
double line2y1 = size.height / 2 +
size.height * sin(Angle.fromDegrees(320).radians) * .35;
canvas.drawArc(rect, Angle.fromDegrees(_startingAngle).radians,
Angle.fromDegrees(360).radians, false, _paint);
canvas.drawLine(Offset(line1x1, line1y1), Offset(line1x2, line1y2), _paint);
canvas.drawLine(Offset(line2x1, line2y1), Offset(line1x2, line1y2), _paint);
// animation painter
double circleValue, checkValue;
if (value < .5) {
checkValue = 0;
circleValue = value / .5;
} else {
checkValue = (value - .5) / .5;
circleValue = 1;
}
_paint.color = const Color(0xff72d0c3);
double firstAngle = _startingAngle + 360 * circleValue;
canvas.drawArc(
rect,
Angle.fromDegrees(firstAngle).radians,
Angle.fromDegrees(
getSecondAngle(firstAngle, _length, _startingAngle + 360))
.radians,
false,
_paint);
double line1Value = 0, line2Value = 0;
if (circleValue >= 1) {
if (checkValue < .5) {
line2Value = 0;
line1Value = checkValue / .5;
} else {
line2Value = (checkValue - .5) / .5;
line1Value = 1;
}
}
double auxLine1x1 = (line1x2 - line1x1) * getMin(line1Value, .8);
double auxLine1y1 =
(((auxLine1x1) - line1x1) / (line1x2 - line1x1)) * (line1y2 - line1y1) +
line1y1;
if (_offset < 60) {
auxLine1x1 = line1x1;
auxLine1y1 = line1y1;
}
double auxLine1x2 = auxLine1x1 + _offset / 2;
double auxLine1y2 =
(((auxLine1x1 + _offset / 2) - line1x1) / (line1x2 - line1x1)) *
(line1y2 - line1y1) +
line1y1;
if (checkIfPointHasCrossedLine(Offset(line1x2, line1y2),
Offset(line2x1, line2y1), Offset(auxLine1x2, auxLine1y2))) {
auxLine1x2 = line1x2;
auxLine1y2 = line1y2;
}
if (_offset > 0) {
canvas.drawLine(Offset(auxLine1x1, auxLine1y1),
Offset(auxLine1x2, auxLine1y2), _paint);
}
// SECOND LINE
double auxLine2x1 = (line2x1 - line1x2) * line2Value;
double auxLine2y1 =
((((line2x1 - line1x2) * line2Value) - line1x2) / (line2x1 - line1x2)) *
(line2y1 - line1y2) +
line1y2;
if (checkIfPointHasCrossedLine(Offset(line1x1, line1y1),
Offset(line1x2, line1y2), Offset(auxLine2x1, auxLine2y1))) {
auxLine2x1 = line1x2;
auxLine2y1 = line1y2;
}
if (line2Value > 0) {
canvas.drawLine(
Offset(auxLine2x1, auxLine2y1),
Offset(
(line2x1 - line1x2) * line2Value + _offset * .75,
((((line2x1 - line1x2) * line2Value + _offset * .75) - line1x2) /
(line2x1 - line1x2)) *
(line2y1 - line1y2) +
line1y2),
_paint);
}
}
double getMax(double x, double y) {
return (x > y) ? x : y;
}
double getMin(double x, double y) {
return (x > y) ? y : x;
}
bool checkIfPointHasCrossedLine(Offset a, Offset b, Offset point) {
return ((b.dx - a.dx) * (point.dy - a.dy) -
(b.dy - a.dy) * (point.dx - a.dx)) >
0;
}
double getSecondAngle(double angle, double plus, double max) {
if (angle + plus > max) {
_offset = angle + plus - max;
return max - angle;
} else {
_offset = 0;
return plus;
}
}
@override
bool shouldRepaint(CheckPainter old) {
return old.value != value;
}
}
我使用了angles package
答案 3 :(得分:1)
showGeneralDialog()
,无需使用额外的库或窗口小部件。您可以从This Link
获取更多动画对话框参考。void _openCustomDialog() {
showGeneralDialog(barrierColor: Colors.black.withOpacity(0.5),
transitionBuilder: (context, a1, a2, widget) {
return Transform.scale(
scale: a1.value,
child: Opacity(
opacity: a1.value,
child: AlertDialog(
shape: OutlineInputBorder(
borderRadius: BorderRadius.circular(16.0)),
title: Text('Hello!!'),
content: Text('How are you?'),
),
),
);
},
transitionDuration: Duration(milliseconds: 200),
barrierDismissible: true,
barrierLabel: '',
context: context,
pageBuilder: (context, animation1, animation2) {});
}