Committed by
GitHub
Merge pull request #2711 from jonataslaw/animations
The easiest way of create animations with Flutter.
Showing
7 changed files
with
1472 additions
and
0 deletions
| @@ -3,6 +3,7 @@ | @@ -3,6 +3,7 @@ | ||
| 3 | /// injection, and route management in a quick and practical way. | 3 | /// injection, and route management in a quick and practical way. |
| 4 | library get; | 4 | library get; |
| 5 | 5 | ||
| 6 | +export 'get_animations/index.dart'; | ||
| 6 | export 'get_common/get_reset.dart'; | 7 | export 'get_common/get_reset.dart'; |
| 7 | export 'get_connect/connect.dart'; | 8 | export 'get_connect/connect.dart'; |
| 8 | export 'get_core/get_core.dart'; | 9 | export 'get_core/get_core.dart'; |
lib/get_animations/animations.dart
0 → 100644
| 1 | +import 'dart:math'; | ||
| 2 | +import 'dart:ui'; | ||
| 3 | + | ||
| 4 | +import 'package:flutter/widgets.dart'; | ||
| 5 | + | ||
| 6 | +import 'get_animated_builder.dart'; | ||
| 7 | + | ||
| 8 | +typedef OffsetBuilder = Offset Function(BuildContext, double); | ||
| 9 | + | ||
| 10 | +class FadeInAnimation extends OpacityAnimation { | ||
| 11 | + FadeInAnimation({ | ||
| 12 | + super.key, | ||
| 13 | + required super.duration, | ||
| 14 | + required super.delay, | ||
| 15 | + required super.child, | ||
| 16 | + super.onComplete, | ||
| 17 | + super.begin = 0, | ||
| 18 | + super.end = 1, | ||
| 19 | + super.idleValue = 0, | ||
| 20 | + }); | ||
| 21 | +} | ||
| 22 | + | ||
| 23 | +class FadeOutAnimation extends OpacityAnimation { | ||
| 24 | + FadeOutAnimation({ | ||
| 25 | + super.key, | ||
| 26 | + required super.duration, | ||
| 27 | + required super.delay, | ||
| 28 | + required super.child, | ||
| 29 | + super.onComplete, | ||
| 30 | + super.begin = 1, | ||
| 31 | + super.end = 0, | ||
| 32 | + super.idleValue = 1, | ||
| 33 | + }); | ||
| 34 | +} | ||
| 35 | + | ||
| 36 | +class OpacityAnimation extends GetAnimatedBuilder<double> { | ||
| 37 | + OpacityAnimation({ | ||
| 38 | + super.key, | ||
| 39 | + required super.duration, | ||
| 40 | + required super.delay, | ||
| 41 | + required super.child, | ||
| 42 | + required super.onComplete, | ||
| 43 | + required double begin, | ||
| 44 | + required double end, | ||
| 45 | + required super.idleValue, | ||
| 46 | + }) : super( | ||
| 47 | + tween: Tween<double>(begin: begin, end: end), | ||
| 48 | + builder: (context, value, child) { | ||
| 49 | + return Opacity( | ||
| 50 | + opacity: value, | ||
| 51 | + child: child!, | ||
| 52 | + ); | ||
| 53 | + }, | ||
| 54 | + ); | ||
| 55 | +} | ||
| 56 | + | ||
| 57 | +class RotateAnimation extends GetAnimatedBuilder<double> { | ||
| 58 | + RotateAnimation({ | ||
| 59 | + super.key, | ||
| 60 | + required super.duration, | ||
| 61 | + required super.delay, | ||
| 62 | + required super.child, | ||
| 63 | + super.onComplete, | ||
| 64 | + required double begin, | ||
| 65 | + required double end, | ||
| 66 | + super.idleValue = 0, | ||
| 67 | + }) : super( | ||
| 68 | + builder: (context, value, child) => Transform.rotate( | ||
| 69 | + angle: value, | ||
| 70 | + child: child, | ||
| 71 | + ), | ||
| 72 | + tween: Tween<double>(begin: begin, end: end), | ||
| 73 | + ); | ||
| 74 | +} | ||
| 75 | + | ||
| 76 | +class ScaleAnimation extends GetAnimatedBuilder<double> { | ||
| 77 | + ScaleAnimation({ | ||
| 78 | + super.key, | ||
| 79 | + required super.duration, | ||
| 80 | + required super.delay, | ||
| 81 | + required super.child, | ||
| 82 | + super.onComplete, | ||
| 83 | + required double begin, | ||
| 84 | + required double end, | ||
| 85 | + super.idleValue = 0, | ||
| 86 | + }) : super( | ||
| 87 | + builder: (context, value, child) => Transform.scale( | ||
| 88 | + scale: value, | ||
| 89 | + child: child, | ||
| 90 | + ), | ||
| 91 | + tween: Tween<double>(begin: begin, end: end), | ||
| 92 | + ); | ||
| 93 | +} | ||
| 94 | + | ||
| 95 | +// class SlideAnimation extends GetAnimatedBuilder<Offset> { | ||
| 96 | +// SlideAnimation({ | ||
| 97 | +// super.key, | ||
| 98 | +// required super.duration, | ||
| 99 | +// required super.delay, | ||
| 100 | +// required super.child, | ||
| 101 | +// super.onComplete, | ||
| 102 | +// required Offset begin, | ||
| 103 | +// required Offset end, | ||
| 104 | +// super.idleValue = const Offset(0, 0), | ||
| 105 | +// }) : super( | ||
| 106 | +// builder: (context, value, child) => Transform.translate( | ||
| 107 | +// offset: value, | ||
| 108 | +// child: child, | ||
| 109 | +// ), | ||
| 110 | +// tween: Tween(begin: begin, end: end), | ||
| 111 | +// ); | ||
| 112 | +// } | ||
| 113 | + | ||
| 114 | +class BounceAnimation extends GetAnimatedBuilder<double> { | ||
| 115 | + BounceAnimation({ | ||
| 116 | + super.key, | ||
| 117 | + required super.duration, | ||
| 118 | + required super.delay, | ||
| 119 | + required super.child, | ||
| 120 | + super.onComplete, | ||
| 121 | + super.curve = Curves.bounceOut, | ||
| 122 | + required double begin, | ||
| 123 | + required double end, | ||
| 124 | + super.idleValue = 0, | ||
| 125 | + }) : super( | ||
| 126 | + builder: (context, value, child) => Transform.scale( | ||
| 127 | + scale: 1 + value.abs(), | ||
| 128 | + child: child, | ||
| 129 | + ), | ||
| 130 | + tween: Tween<double>(begin: begin, end: end), | ||
| 131 | + ); | ||
| 132 | +} | ||
| 133 | + | ||
| 134 | +class SpinAnimation extends GetAnimatedBuilder<double> { | ||
| 135 | + SpinAnimation({ | ||
| 136 | + super.key, | ||
| 137 | + required super.duration, | ||
| 138 | + required super.delay, | ||
| 139 | + required super.child, | ||
| 140 | + super.onComplete, | ||
| 141 | + super.idleValue = 0, | ||
| 142 | + }) : super( | ||
| 143 | + builder: (context, value, child) => Transform.rotate( | ||
| 144 | + angle: value * pi / 180.0, | ||
| 145 | + child: child, | ||
| 146 | + ), | ||
| 147 | + tween: Tween<double>(begin: 0, end: 360), | ||
| 148 | + ); | ||
| 149 | +} | ||
| 150 | + | ||
| 151 | +class SizeAnimation extends GetAnimatedBuilder<double> { | ||
| 152 | + SizeAnimation({ | ||
| 153 | + super.key, | ||
| 154 | + required super.duration, | ||
| 155 | + required super.delay, | ||
| 156 | + required super.child, | ||
| 157 | + super.onComplete, | ||
| 158 | + super.idleValue = 0, | ||
| 159 | + required double begin, | ||
| 160 | + required double end, | ||
| 161 | + }) : super( | ||
| 162 | + builder: (context, value, child) => Transform.scale( | ||
| 163 | + scale: value, | ||
| 164 | + child: child, | ||
| 165 | + ), | ||
| 166 | + tween: Tween<double>(begin: begin, end: end), | ||
| 167 | + ); | ||
| 168 | +} | ||
| 169 | + | ||
| 170 | +class BlurAnimation extends GetAnimatedBuilder<double> { | ||
| 171 | + BlurAnimation({ | ||
| 172 | + super.key, | ||
| 173 | + required super.duration, | ||
| 174 | + required super.delay, | ||
| 175 | + required super.child, | ||
| 176 | + super.onComplete, | ||
| 177 | + required double begin, | ||
| 178 | + required double end, | ||
| 179 | + super.idleValue = 0, | ||
| 180 | + }) : super( | ||
| 181 | + builder: (context, value, child) => BackdropFilter( | ||
| 182 | + filter: ImageFilter.blur( | ||
| 183 | + sigmaX: value, | ||
| 184 | + sigmaY: value, | ||
| 185 | + ), | ||
| 186 | + child: child, | ||
| 187 | + ), | ||
| 188 | + tween: Tween<double>(begin: begin, end: end), | ||
| 189 | + ); | ||
| 190 | +} | ||
| 191 | + | ||
| 192 | +class FlipAnimation extends GetAnimatedBuilder<double> { | ||
| 193 | + FlipAnimation({ | ||
| 194 | + super.key, | ||
| 195 | + required super.duration, | ||
| 196 | + required super.delay, | ||
| 197 | + required super.child, | ||
| 198 | + super.onComplete, | ||
| 199 | + required double begin, | ||
| 200 | + required double end, | ||
| 201 | + super.idleValue = 0, | ||
| 202 | + }) : super( | ||
| 203 | + builder: (context, value, child) { | ||
| 204 | + final radians = value * pi; | ||
| 205 | + return Transform( | ||
| 206 | + transform: Matrix4.rotationY(radians), | ||
| 207 | + alignment: Alignment.center, | ||
| 208 | + child: child, | ||
| 209 | + ); | ||
| 210 | + }, | ||
| 211 | + tween: Tween<double>(begin: begin, end: end), | ||
| 212 | + ); | ||
| 213 | +} | ||
| 214 | + | ||
| 215 | +class WaveAnimation extends GetAnimatedBuilder<double> { | ||
| 216 | + WaveAnimation({ | ||
| 217 | + super.key, | ||
| 218 | + required super.duration, | ||
| 219 | + required super.delay, | ||
| 220 | + required super.child, | ||
| 221 | + super.onComplete, | ||
| 222 | + required double begin, | ||
| 223 | + required double end, | ||
| 224 | + super.idleValue = 0, | ||
| 225 | + }) : super( | ||
| 226 | + builder: (context, value, child) => Transform( | ||
| 227 | + transform: Matrix4.translationValues( | ||
| 228 | + 0.0, | ||
| 229 | + 20.0 * sin(value * pi * 2), | ||
| 230 | + 0.0, | ||
| 231 | + ), | ||
| 232 | + child: child, | ||
| 233 | + ), | ||
| 234 | + tween: Tween<double>(begin: begin, end: end), | ||
| 235 | + ); | ||
| 236 | +} | ||
| 237 | + | ||
| 238 | +class WobbleAnimation extends GetAnimatedBuilder<double> { | ||
| 239 | + WobbleAnimation({ | ||
| 240 | + super.key, | ||
| 241 | + required super.duration, | ||
| 242 | + required super.delay, | ||
| 243 | + required super.child, | ||
| 244 | + super.onComplete, | ||
| 245 | + required double begin, | ||
| 246 | + required double end, | ||
| 247 | + super.idleValue = 0, | ||
| 248 | + }) : super( | ||
| 249 | + builder: (context, value, child) => Transform( | ||
| 250 | + transform: Matrix4.identity() | ||
| 251 | + ..setEntry(3, 2, 0.001) | ||
| 252 | + ..rotateZ(sin(value * pi * 2) * 0.1), | ||
| 253 | + alignment: Alignment.center, | ||
| 254 | + child: child, | ||
| 255 | + ), | ||
| 256 | + tween: Tween<double>(begin: begin, end: end), | ||
| 257 | + ); | ||
| 258 | +} | ||
| 259 | + | ||
| 260 | +class SlideInLeftAnimation extends SlideAnimation { | ||
| 261 | + SlideInLeftAnimation({ | ||
| 262 | + super.key, | ||
| 263 | + required super.duration, | ||
| 264 | + required super.delay, | ||
| 265 | + required super.child, | ||
| 266 | + super.onComplete, | ||
| 267 | + required super.begin, | ||
| 268 | + required super.end, | ||
| 269 | + super.idleValue = 0, | ||
| 270 | + }) : super( | ||
| 271 | + offsetBuild: (context, value) => | ||
| 272 | + Offset(value * MediaQuery.of(context).size.width, 0), | ||
| 273 | + ); | ||
| 274 | +} | ||
| 275 | + | ||
| 276 | +class SlideInRightAnimation extends SlideAnimation { | ||
| 277 | + SlideInRightAnimation({ | ||
| 278 | + super.key, | ||
| 279 | + required super.duration, | ||
| 280 | + required super.delay, | ||
| 281 | + required super.child, | ||
| 282 | + super.onComplete, | ||
| 283 | + required super.begin, | ||
| 284 | + required super.end, | ||
| 285 | + super.idleValue = 0, | ||
| 286 | + }) : super( | ||
| 287 | + offsetBuild: (context, value) => | ||
| 288 | + Offset((1 - value) * MediaQuery.of(context).size.width, 0), | ||
| 289 | + ); | ||
| 290 | +} | ||
| 291 | + | ||
| 292 | +class SlideInUpAnimation extends SlideAnimation { | ||
| 293 | + SlideInUpAnimation({ | ||
| 294 | + super.key, | ||
| 295 | + required super.duration, | ||
| 296 | + required super.delay, | ||
| 297 | + required super.child, | ||
| 298 | + super.onComplete, | ||
| 299 | + required super.begin, | ||
| 300 | + required super.end, | ||
| 301 | + super.idleValue = 0, | ||
| 302 | + }) : super( | ||
| 303 | + offsetBuild: (context, value) => | ||
| 304 | + Offset(0, value * MediaQuery.of(context).size.height), | ||
| 305 | + ); | ||
| 306 | +} | ||
| 307 | + | ||
| 308 | +class SlideInDownAnimation extends SlideAnimation { | ||
| 309 | + SlideInDownAnimation({ | ||
| 310 | + super.key, | ||
| 311 | + required super.duration, | ||
| 312 | + required super.delay, | ||
| 313 | + required super.child, | ||
| 314 | + super.onComplete, | ||
| 315 | + required super.begin, | ||
| 316 | + required super.end, | ||
| 317 | + super.idleValue = 0, | ||
| 318 | + }) : super( | ||
| 319 | + offsetBuild: (context, value) => | ||
| 320 | + Offset(0, (1 - value) * MediaQuery.of(context).size.height), | ||
| 321 | + ); | ||
| 322 | +} | ||
| 323 | + | ||
| 324 | +class SlideAnimation extends GetAnimatedBuilder<double> { | ||
| 325 | + SlideAnimation({ | ||
| 326 | + super.key, | ||
| 327 | + required super.duration, | ||
| 328 | + required super.delay, | ||
| 329 | + required super.child, | ||
| 330 | + required double begin, | ||
| 331 | + required double end, | ||
| 332 | + required OffsetBuilder offsetBuild, | ||
| 333 | + super.onComplete, | ||
| 334 | + super.idleValue = 0, | ||
| 335 | + }) : super( | ||
| 336 | + builder: (context, value, child) => Transform.translate( | ||
| 337 | + offset: offsetBuild(context, value), | ||
| 338 | + child: child, | ||
| 339 | + ), | ||
| 340 | + tween: Tween<double>(begin: begin, end: end), | ||
| 341 | + ); | ||
| 342 | +} | ||
| 343 | + | ||
| 344 | +// class ZoomAnimation extends GetAnimatedBuilder<double> { | ||
| 345 | +// ZoomAnimation({ | ||
| 346 | +// super.key, | ||
| 347 | +// required super.duration, | ||
| 348 | +// required super.delay, | ||
| 349 | +// required super.child, | ||
| 350 | +// super.onComplete, | ||
| 351 | +// required double begin, | ||
| 352 | +// required double end, | ||
| 353 | +// super.idleValue = 0, | ||
| 354 | +// }) : super( | ||
| 355 | +// builder: (context, value, child) => Transform.scale( | ||
| 356 | +// scale: lerpDouble(1, end, value)!, | ||
| 357 | +// child: child, | ||
| 358 | +// ), | ||
| 359 | +// tween: Tween<double>(begin: begin, end: end), | ||
| 360 | +// ); | ||
| 361 | +// } | ||
| 362 | + | ||
| 363 | +class ColorAnimation extends GetAnimatedBuilder<Color?> { | ||
| 364 | + ColorAnimation({ | ||
| 365 | + super.key, | ||
| 366 | + required super.duration, | ||
| 367 | + required super.delay, | ||
| 368 | + required super.child, | ||
| 369 | + super.onComplete, | ||
| 370 | + required Color begin, | ||
| 371 | + required Color end, | ||
| 372 | + Color? idleColor, | ||
| 373 | + }) : super( | ||
| 374 | + builder: (context, value, child) => ColorFiltered( | ||
| 375 | + colorFilter: ColorFilter.mode( | ||
| 376 | + Color.lerp(begin, end, value!.value.toDouble())!, | ||
| 377 | + BlendMode.srcIn, | ||
| 378 | + ), | ||
| 379 | + child: child, | ||
| 380 | + ), | ||
| 381 | + idleValue: idleColor ?? begin, | ||
| 382 | + tween: ColorTween(begin: begin, end: end), | ||
| 383 | + ); | ||
| 384 | +} |
lib/get_animations/extensions.dart
0 → 100644
| 1 | +import 'package:flutter/material.dart'; | ||
| 2 | + | ||
| 3 | +import 'animations.dart'; | ||
| 4 | +import 'get_animated_builder.dart'; | ||
| 5 | + | ||
| 6 | +const _defaultDuration = Duration(seconds: 2); | ||
| 7 | +const _defaultDelay = Duration.zero; | ||
| 8 | + | ||
| 9 | +extension AnimationExtension on Widget { | ||
| 10 | + GetAnimatedBuilder? get _currentAnimation => | ||
| 11 | + (this is GetAnimatedBuilder) ? this as GetAnimatedBuilder : null; | ||
| 12 | + | ||
| 13 | + GetAnimatedBuilder fadeIn({ | ||
| 14 | + Duration duration = _defaultDuration, | ||
| 15 | + Duration delay = _defaultDelay, | ||
| 16 | + ValueSetter<AnimationController>? onComplete, | ||
| 17 | + bool isSequential = false, | ||
| 18 | + }) { | ||
| 19 | + assert(isSequential || this is! FadeOutAnimation, | ||
| 20 | + 'Can not use fadeOut + fadeIn when isSequential is false'); | ||
| 21 | + | ||
| 22 | + return FadeInAnimation( | ||
| 23 | + duration: duration, | ||
| 24 | + delay: _getDelay(isSequential, delay), | ||
| 25 | + onComplete: onComplete, | ||
| 26 | + child: this, | ||
| 27 | + ); | ||
| 28 | + } | ||
| 29 | + | ||
| 30 | + GetAnimatedBuilder fadeOut({ | ||
| 31 | + Duration duration = _defaultDuration, | ||
| 32 | + Duration delay = _defaultDelay, | ||
| 33 | + ValueSetter<AnimationController>? onComplete, | ||
| 34 | + bool isSequential = false, | ||
| 35 | + }) { | ||
| 36 | + assert(isSequential || this is! FadeInAnimation, | ||
| 37 | + 'Can not use fadeOut() + fadeIn when isSequential is false'); | ||
| 38 | + | ||
| 39 | + return FadeOutAnimation( | ||
| 40 | + duration: duration, | ||
| 41 | + delay: _getDelay(isSequential, delay), | ||
| 42 | + onComplete: onComplete, | ||
| 43 | + child: this, | ||
| 44 | + ); | ||
| 45 | + } | ||
| 46 | + | ||
| 47 | + GetAnimatedBuilder rotate({ | ||
| 48 | + required double begin, | ||
| 49 | + required double end, | ||
| 50 | + Duration duration = _defaultDuration, | ||
| 51 | + Duration delay = _defaultDelay, | ||
| 52 | + ValueSetter<AnimationController>? onComplete, | ||
| 53 | + bool isSequential = false, | ||
| 54 | + }) { | ||
| 55 | + return RotateAnimation( | ||
| 56 | + duration: duration, | ||
| 57 | + delay: _getDelay(isSequential, delay), | ||
| 58 | + begin: begin, | ||
| 59 | + end: end, | ||
| 60 | + onComplete: onComplete, | ||
| 61 | + child: this, | ||
| 62 | + ); | ||
| 63 | + } | ||
| 64 | + | ||
| 65 | + GetAnimatedBuilder scale({ | ||
| 66 | + required double begin, | ||
| 67 | + required double end, | ||
| 68 | + Duration duration = _defaultDuration, | ||
| 69 | + Duration delay = _defaultDelay, | ||
| 70 | + ValueSetter<AnimationController>? onComplete, | ||
| 71 | + bool isSequential = false, | ||
| 72 | + }) { | ||
| 73 | + return ScaleAnimation( | ||
| 74 | + duration: duration, | ||
| 75 | + delay: _getDelay(isSequential, delay), | ||
| 76 | + begin: begin, | ||
| 77 | + end: end, | ||
| 78 | + onComplete: onComplete, | ||
| 79 | + child: this, | ||
| 80 | + ); | ||
| 81 | + } | ||
| 82 | + | ||
| 83 | + GetAnimatedBuilder slide({ | ||
| 84 | + required OffsetBuilder offset, | ||
| 85 | + double begin = 0, | ||
| 86 | + double end = 1, | ||
| 87 | + Duration duration = _defaultDuration, | ||
| 88 | + Duration delay = _defaultDelay, | ||
| 89 | + ValueSetter<AnimationController>? onComplete, | ||
| 90 | + bool isSequential = false, | ||
| 91 | + }) { | ||
| 92 | + return SlideAnimation( | ||
| 93 | + duration: duration, | ||
| 94 | + delay: _getDelay(isSequential, delay), | ||
| 95 | + begin: begin, | ||
| 96 | + end: end, | ||
| 97 | + onComplete: onComplete, | ||
| 98 | + offsetBuild: offset, | ||
| 99 | + child: this, | ||
| 100 | + ); | ||
| 101 | + } | ||
| 102 | + | ||
| 103 | + GetAnimatedBuilder bounce({ | ||
| 104 | + required double begin, | ||
| 105 | + required double end, | ||
| 106 | + Duration duration = _defaultDuration, | ||
| 107 | + Duration delay = _defaultDelay, | ||
| 108 | + ValueSetter<AnimationController>? onComplete, | ||
| 109 | + bool isSequential = false, | ||
| 110 | + }) { | ||
| 111 | + return BounceAnimation( | ||
| 112 | + duration: duration, | ||
| 113 | + delay: _getDelay(isSequential, delay), | ||
| 114 | + begin: begin, | ||
| 115 | + end: end, | ||
| 116 | + onComplete: onComplete, | ||
| 117 | + child: this, | ||
| 118 | + ); | ||
| 119 | + } | ||
| 120 | + | ||
| 121 | + GetAnimatedBuilder spin({ | ||
| 122 | + Duration duration = _defaultDuration, | ||
| 123 | + Duration delay = _defaultDelay, | ||
| 124 | + ValueSetter<AnimationController>? onComplete, | ||
| 125 | + bool isSequential = false, | ||
| 126 | + }) { | ||
| 127 | + return SpinAnimation( | ||
| 128 | + duration: duration, | ||
| 129 | + delay: _getDelay(isSequential, delay), | ||
| 130 | + onComplete: onComplete, | ||
| 131 | + child: this, | ||
| 132 | + ); | ||
| 133 | + } | ||
| 134 | + | ||
| 135 | + GetAnimatedBuilder size({ | ||
| 136 | + required double begin, | ||
| 137 | + required double end, | ||
| 138 | + Duration duration = _defaultDuration, | ||
| 139 | + Duration delay = _defaultDelay, | ||
| 140 | + ValueSetter<AnimationController>? onComplete, | ||
| 141 | + bool isSequential = false, | ||
| 142 | + }) { | ||
| 143 | + return SizeAnimation( | ||
| 144 | + duration: duration, | ||
| 145 | + delay: _getDelay(isSequential, delay), | ||
| 146 | + begin: begin, | ||
| 147 | + end: end, | ||
| 148 | + onComplete: onComplete, | ||
| 149 | + child: this, | ||
| 150 | + ); | ||
| 151 | + } | ||
| 152 | + | ||
| 153 | + GetAnimatedBuilder blur({ | ||
| 154 | + double begin = 0, | ||
| 155 | + double end = 15, | ||
| 156 | + Duration duration = _defaultDuration, | ||
| 157 | + Duration delay = _defaultDelay, | ||
| 158 | + ValueSetter<AnimationController>? onComplete, | ||
| 159 | + bool isSequential = false, | ||
| 160 | + }) { | ||
| 161 | + return BlurAnimation( | ||
| 162 | + duration: duration, | ||
| 163 | + delay: _getDelay(isSequential, delay), | ||
| 164 | + begin: begin, | ||
| 165 | + end: end, | ||
| 166 | + onComplete: onComplete, | ||
| 167 | + child: this, | ||
| 168 | + ); | ||
| 169 | + } | ||
| 170 | + | ||
| 171 | + GetAnimatedBuilder flip({ | ||
| 172 | + double begin = 0, | ||
| 173 | + double end = 1, | ||
| 174 | + Duration duration = _defaultDuration, | ||
| 175 | + Duration delay = _defaultDelay, | ||
| 176 | + ValueSetter<AnimationController>? onComplete, | ||
| 177 | + bool isSequential = false, | ||
| 178 | + }) { | ||
| 179 | + return FlipAnimation( | ||
| 180 | + duration: duration, | ||
| 181 | + delay: _getDelay(isSequential, delay), | ||
| 182 | + begin: begin, | ||
| 183 | + end: end, | ||
| 184 | + onComplete: onComplete, | ||
| 185 | + child: this, | ||
| 186 | + ); | ||
| 187 | + } | ||
| 188 | + | ||
| 189 | + GetAnimatedBuilder wave({ | ||
| 190 | + double begin = 0, | ||
| 191 | + double end = 1, | ||
| 192 | + Duration duration = _defaultDuration, | ||
| 193 | + Duration delay = _defaultDelay, | ||
| 194 | + ValueSetter<AnimationController>? onComplete, | ||
| 195 | + bool isSequential = false, | ||
| 196 | + }) { | ||
| 197 | + return WaveAnimation( | ||
| 198 | + duration: duration, | ||
| 199 | + delay: _getDelay(isSequential, delay), | ||
| 200 | + begin: begin, | ||
| 201 | + end: end, | ||
| 202 | + onComplete: onComplete, | ||
| 203 | + child: this, | ||
| 204 | + ); | ||
| 205 | + } | ||
| 206 | + | ||
| 207 | + Duration _getDelay(bool isSequential, Duration delay) { | ||
| 208 | + assert(!(isSequential && delay != Duration.zero), | ||
| 209 | + "Error: When isSequential is true, delay must be non-zero. Context: isSequential: $isSequential delay: $delay"); | ||
| 210 | + | ||
| 211 | + return isSequential | ||
| 212 | + ? (_currentAnimation?.totalDuration ?? Duration.zero) | ||
| 213 | + : delay; | ||
| 214 | + } | ||
| 215 | +} |
lib/get_animations/get_animated_builder.dart
0 → 100644
| 1 | +import 'package:flutter/material.dart'; | ||
| 2 | + | ||
| 3 | +import 'animations.dart'; | ||
| 4 | + | ||
| 5 | +class GetAnimatedBuilder<T> extends StatefulWidget { | ||
| 6 | + final Duration duration; | ||
| 7 | + final Duration delay; | ||
| 8 | + final Widget child; | ||
| 9 | + final ValueSetter<AnimationController>? onComplete; | ||
| 10 | + final ValueSetter<AnimationController>? onStart; | ||
| 11 | + final Tween<T> tween; | ||
| 12 | + final T idleValue; | ||
| 13 | + final ValueWidgetBuilder<T> builder; | ||
| 14 | + final Curve curve; | ||
| 15 | + | ||
| 16 | + Duration get totalDuration => duration + delay; | ||
| 17 | + | ||
| 18 | + const GetAnimatedBuilder({ | ||
| 19 | + super.key, | ||
| 20 | + this.curve = Curves.linear, | ||
| 21 | + this.onComplete, | ||
| 22 | + this.onStart, | ||
| 23 | + required this.duration, | ||
| 24 | + required this.tween, | ||
| 25 | + required this.idleValue, | ||
| 26 | + required this.builder, | ||
| 27 | + required this.child, | ||
| 28 | + required this.delay, | ||
| 29 | + }); | ||
| 30 | + @override | ||
| 31 | + GetAnimatedBuilderState<T> createState() => GetAnimatedBuilderState<T>(); | ||
| 32 | +} | ||
| 33 | + | ||
| 34 | +class GetAnimatedBuilderState<T> extends State<GetAnimatedBuilder<T>> | ||
| 35 | + with SingleTickerProviderStateMixin { | ||
| 36 | + late final AnimationController _controller; | ||
| 37 | + late final Animation<T> _animation; | ||
| 38 | + | ||
| 39 | + // AnimationController get controller => _controller; | ||
| 40 | + // Animation<T> get animation => _animation; | ||
| 41 | + | ||
| 42 | + bool _wasStarted = false; | ||
| 43 | + // bool get wasStarted => _wasStarted; | ||
| 44 | + | ||
| 45 | + late T _idleValue; | ||
| 46 | + | ||
| 47 | + bool _willResetOnDispose = false; | ||
| 48 | + | ||
| 49 | + bool get willResetOnDispose => _willResetOnDispose; | ||
| 50 | + | ||
| 51 | + void _listener(AnimationStatus status) { | ||
| 52 | + switch (status) { | ||
| 53 | + case AnimationStatus.completed: | ||
| 54 | + widget.onComplete?.call(_controller); | ||
| 55 | + if (_willResetOnDispose) { | ||
| 56 | + _controller.reset(); | ||
| 57 | + } | ||
| 58 | + break; | ||
| 59 | + // case AnimationStatus.dismissed: | ||
| 60 | + case AnimationStatus.forward: | ||
| 61 | + widget.onStart?.call(_controller); | ||
| 62 | + break; | ||
| 63 | + // case AnimationStatus.reverse: | ||
| 64 | + default: | ||
| 65 | + break; | ||
| 66 | + } | ||
| 67 | + } | ||
| 68 | + | ||
| 69 | + @override | ||
| 70 | + void initState() { | ||
| 71 | + super.initState(); | ||
| 72 | + | ||
| 73 | + if (widget is OpacityAnimation) { | ||
| 74 | + final current = | ||
| 75 | + context.findRootAncestorStateOfType<GetAnimatedBuilderState>(); | ||
| 76 | + final isLast = current == null; | ||
| 77 | + | ||
| 78 | + if (widget is FadeInAnimation) { | ||
| 79 | + _idleValue = 1.0 as dynamic; | ||
| 80 | + } else { | ||
| 81 | + if (isLast) { | ||
| 82 | + _willResetOnDispose = false; | ||
| 83 | + } else { | ||
| 84 | + _willResetOnDispose = true; | ||
| 85 | + } | ||
| 86 | + _idleValue = widget.idleValue; | ||
| 87 | + } | ||
| 88 | + } else { | ||
| 89 | + _idleValue = widget.idleValue; | ||
| 90 | + } | ||
| 91 | + | ||
| 92 | + _controller = AnimationController( | ||
| 93 | + vsync: this, | ||
| 94 | + duration: widget.duration, | ||
| 95 | + ); | ||
| 96 | + | ||
| 97 | + _controller.addStatusListener(_listener); | ||
| 98 | + | ||
| 99 | + _animation = widget.tween.animate( | ||
| 100 | + CurvedAnimation( | ||
| 101 | + parent: _controller, | ||
| 102 | + curve: widget.curve, | ||
| 103 | + ), | ||
| 104 | + ); | ||
| 105 | + | ||
| 106 | + Future.delayed(widget.delay, () { | ||
| 107 | + if (mounted) { | ||
| 108 | + setState(() { | ||
| 109 | + _wasStarted = true; | ||
| 110 | + _controller.forward(); | ||
| 111 | + }); | ||
| 112 | + } | ||
| 113 | + }); | ||
| 114 | + } | ||
| 115 | + | ||
| 116 | + @override | ||
| 117 | + void dispose() { | ||
| 118 | + _controller.removeStatusListener(_listener); | ||
| 119 | + _controller.dispose(); | ||
| 120 | + super.dispose(); | ||
| 121 | + } | ||
| 122 | + | ||
| 123 | + @override | ||
| 124 | + Widget build(BuildContext context) { | ||
| 125 | + return AnimatedBuilder( | ||
| 126 | + animation: _animation, | ||
| 127 | + builder: (context, child) { | ||
| 128 | + final value = _wasStarted ? _animation.value : _idleValue; | ||
| 129 | + return widget.builder(context, value, child); | ||
| 130 | + }, | ||
| 131 | + child: widget.child, | ||
| 132 | + ); | ||
| 133 | + } | ||
| 134 | +} |
lib/get_animations/index.dart
0 → 100644
test/animations/extensions_test.dart
0 → 100644
| 1 | +import 'package:flutter/material.dart'; | ||
| 2 | +import 'package:flutter_test/flutter_test.dart'; | ||
| 3 | +import 'package:get/get.dart'; | ||
| 4 | + | ||
| 5 | +void main() { | ||
| 6 | + group('Animation Extension', () { | ||
| 7 | + Widget buildWidget() { | ||
| 8 | + return Container( | ||
| 9 | + width: 100, | ||
| 10 | + height: 100, | ||
| 11 | + color: Colors.red, | ||
| 12 | + ); | ||
| 13 | + } | ||
| 14 | + | ||
| 15 | + testWidgets('fadeIn() and fadeOut() can not be used sequentially', | ||
| 16 | + (WidgetTester tester) async { | ||
| 17 | + final widget = buildWidget(); | ||
| 18 | + | ||
| 19 | + expect(() => widget.fadeIn().fadeOut(), throwsAssertionError); | ||
| 20 | + expect(() => widget.fadeOut().fadeIn(), throwsAssertionError); | ||
| 21 | + | ||
| 22 | + expect(() => widget.fadeIn(isSequential: true).fadeOut(), | ||
| 23 | + throwsAssertionError); | ||
| 24 | + expect(() => widget.fadeOut(isSequential: true).fadeIn(), | ||
| 25 | + throwsAssertionError); | ||
| 26 | + }); | ||
| 27 | + | ||
| 28 | + testWidgets('can not use delay when isSequential is true', | ||
| 29 | + (WidgetTester tester) async { | ||
| 30 | + final widget = buildWidget(); | ||
| 31 | + | ||
| 32 | + expect( | ||
| 33 | + () => widget.fadeIn( | ||
| 34 | + isSequential: true, delay: const Duration(seconds: 1)), | ||
| 35 | + throwsAssertionError); | ||
| 36 | + }); | ||
| 37 | + | ||
| 38 | + testWidgets( | ||
| 39 | + 'fadeIn() and fadeOut() can be used together when isSequential is true', | ||
| 40 | + (WidgetTester tester) async { | ||
| 41 | + final widget = buildWidget(); | ||
| 42 | + | ||
| 43 | + expect( | ||
| 44 | + () => widget.fadeIn(isSequential: true).fadeOut(isSequential: true), | ||
| 45 | + isNot(throwsException)); | ||
| 46 | + | ||
| 47 | + expect(() => widget.fadeIn().fadeOut(isSequential: true), | ||
| 48 | + isNot(throwsException)); | ||
| 49 | + }); | ||
| 50 | + | ||
| 51 | + testWidgets('fadeIn() returns a FadeInAnimation', | ||
| 52 | + (WidgetTester tester) async { | ||
| 53 | + final widget = buildWidget(); | ||
| 54 | + const begin = 0.0; | ||
| 55 | + const end = 1.0; | ||
| 56 | + final animation = widget.fadeIn(); | ||
| 57 | + | ||
| 58 | + expect(animation, isA<FadeInAnimation>()); | ||
| 59 | + | ||
| 60 | + _testDefaultValues( | ||
| 61 | + animation: animation, widget: widget, begin: begin, end: end); | ||
| 62 | + }); | ||
| 63 | + | ||
| 64 | + testWidgets('fadeOut() returns a animation', (WidgetTester tester) async { | ||
| 65 | + final widget = buildWidget(); | ||
| 66 | + const begin = 1.0; | ||
| 67 | + const end = 0.0; | ||
| 68 | + final animation = widget.fadeOut(); | ||
| 69 | + | ||
| 70 | + expect(animation, isA<FadeOutAnimation>()); | ||
| 71 | + | ||
| 72 | + _testDefaultValues( | ||
| 73 | + animation: animation, widget: widget, begin: begin, end: end); | ||
| 74 | + }); | ||
| 75 | + | ||
| 76 | + testWidgets('rotate() returns a RotateAnimation', | ||
| 77 | + (WidgetTester tester) async { | ||
| 78 | + const begin = 0.9; | ||
| 79 | + const end = 1.1; | ||
| 80 | + final widget = buildWidget(); | ||
| 81 | + final animation = widget.rotate(begin: begin, end: end); | ||
| 82 | + | ||
| 83 | + expect(animation, isA<RotateAnimation>()); | ||
| 84 | + | ||
| 85 | + _testDefaultValues( | ||
| 86 | + animation: animation, widget: widget, begin: begin, end: end); | ||
| 87 | + }); | ||
| 88 | + | ||
| 89 | + testWidgets('scale() returns a ScaleAnimation', | ||
| 90 | + (WidgetTester tester) async { | ||
| 91 | + const begin = 0.9; | ||
| 92 | + const end = 1.1; | ||
| 93 | + final widget = buildWidget(); | ||
| 94 | + final animation = widget.scale(begin: begin, end: end); | ||
| 95 | + | ||
| 96 | + expect(animation, isA<ScaleAnimation>()); | ||
| 97 | + | ||
| 98 | + _testDefaultValues( | ||
| 99 | + animation: animation, widget: widget, begin: begin, end: end); | ||
| 100 | + }); | ||
| 101 | + | ||
| 102 | + testWidgets('slide() returns a SlideAnimation', | ||
| 103 | + (WidgetTester tester) async { | ||
| 104 | + const begin = 0; | ||
| 105 | + const end = 1; | ||
| 106 | + final widget = buildWidget(); | ||
| 107 | + final animation = widget.slide(offset: (_, __) => const Offset(0, 0)); | ||
| 108 | + | ||
| 109 | + expect(animation, isA<SlideAnimation>()); | ||
| 110 | + | ||
| 111 | + _testDefaultValues( | ||
| 112 | + animation: animation, widget: widget, begin: begin, end: end); | ||
| 113 | + }); | ||
| 114 | + | ||
| 115 | + testWidgets('bounce() returns a BounceAnimation', | ||
| 116 | + (WidgetTester tester) async { | ||
| 117 | + const begin = 0.9; | ||
| 118 | + const end = 1.1; | ||
| 119 | + final widget = buildWidget(); | ||
| 120 | + final animation = widget.bounce(begin: begin, end: end); | ||
| 121 | + | ||
| 122 | + expect(animation, isA<BounceAnimation>()); | ||
| 123 | + | ||
| 124 | + _testDefaultValues( | ||
| 125 | + animation: animation, | ||
| 126 | + widget: widget, | ||
| 127 | + begin: begin, | ||
| 128 | + end: end, | ||
| 129 | + curve: Curves.bounceOut, | ||
| 130 | + ); | ||
| 131 | + }); | ||
| 132 | + | ||
| 133 | + testWidgets('spin() returns a SpinAnimation', (WidgetTester tester) async { | ||
| 134 | + final widget = buildWidget(); | ||
| 135 | + const begin = 0.0; | ||
| 136 | + const end = 360; | ||
| 137 | + final animation = widget.spin(); | ||
| 138 | + | ||
| 139 | + expect(animation, isA<SpinAnimation>()); | ||
| 140 | + | ||
| 141 | + _testDefaultValues( | ||
| 142 | + animation: animation, widget: widget, begin: begin, end: end); | ||
| 143 | + }); | ||
| 144 | + | ||
| 145 | + testWidgets('size() returns a SizeAnimation', (WidgetTester tester) async { | ||
| 146 | + final widget = buildWidget(); | ||
| 147 | + | ||
| 148 | + const begin = 0.9; | ||
| 149 | + const end = 1.1; | ||
| 150 | + final animation = widget.size(begin: begin, end: end); | ||
| 151 | + | ||
| 152 | + expect(animation, isA<SizeAnimation>()); | ||
| 153 | + | ||
| 154 | + _testDefaultValues( | ||
| 155 | + animation: animation, widget: widget, begin: begin, end: end); | ||
| 156 | + }); | ||
| 157 | + | ||
| 158 | + testWidgets('blur() returns a BlurAnimation', (WidgetTester tester) async { | ||
| 159 | + final widget = buildWidget(); | ||
| 160 | + | ||
| 161 | + const begin = 0.9; | ||
| 162 | + const end = 1.1; | ||
| 163 | + final animation = widget.blur(begin: begin, end: end); | ||
| 164 | + | ||
| 165 | + expect(animation, isA<BlurAnimation>()); | ||
| 166 | + | ||
| 167 | + _testDefaultValues( | ||
| 168 | + animation: animation, widget: widget, begin: begin, end: end); | ||
| 169 | + }); | ||
| 170 | + | ||
| 171 | + testWidgets('flip() returns a FlipAnimation', (WidgetTester tester) async { | ||
| 172 | + final widget = buildWidget(); | ||
| 173 | + | ||
| 174 | + const begin = 0.9; | ||
| 175 | + const end = 1.1; | ||
| 176 | + final animation = widget.flip(begin: begin, end: end); | ||
| 177 | + | ||
| 178 | + expect(animation, isA<FlipAnimation>()); | ||
| 179 | + | ||
| 180 | + _testDefaultValues( | ||
| 181 | + animation: animation, widget: widget, begin: begin, end: end); | ||
| 182 | + }); | ||
| 183 | + | ||
| 184 | + testWidgets('wave() returns a FlipAnimation', (WidgetTester tester) async { | ||
| 185 | + final widget = buildWidget(); | ||
| 186 | + | ||
| 187 | + const begin = 0.9; | ||
| 188 | + const end = 1.1; | ||
| 189 | + final animation = widget.wave(begin: begin, end: end); | ||
| 190 | + | ||
| 191 | + expect(animation, isA<WaveAnimation>()); | ||
| 192 | + | ||
| 193 | + _testDefaultValues( | ||
| 194 | + animation: animation, widget: widget, begin: begin, end: end); | ||
| 195 | + }); | ||
| 196 | + }); | ||
| 197 | +} | ||
| 198 | + | ||
| 199 | +void _testDefaultValues<T>({ | ||
| 200 | + required GetAnimatedBuilder animation, | ||
| 201 | + required Widget widget, | ||
| 202 | + required T begin, | ||
| 203 | + required T end, | ||
| 204 | + Curve curve = Curves.linear, | ||
| 205 | +}) { | ||
| 206 | + expect(animation.tween.begin, begin); | ||
| 207 | + expect(animation.tween.end, end); | ||
| 208 | + if (animation.idleValue is Offset) { | ||
| 209 | + expect(animation.idleValue, Offset.zero); | ||
| 210 | + } else if (animation is FadeOutAnimation) { | ||
| 211 | + expect(animation.idleValue, 1); | ||
| 212 | + } else { | ||
| 213 | + expect(animation.idleValue, 0); | ||
| 214 | + } | ||
| 215 | + | ||
| 216 | + expect(animation.delay, Duration.zero); | ||
| 217 | + expect(animation.child, widget); | ||
| 218 | + expect(animation.curve, curve); | ||
| 219 | +} |
| 1 | +import 'package:flutter/material.dart'; | ||
| 2 | +import 'package:flutter_test/flutter_test.dart'; | ||
| 3 | +import 'package:get/get.dart'; | ||
| 4 | + | ||
| 5 | +class _Wrapper extends StatelessWidget { | ||
| 6 | + const _Wrapper({required this.child}); | ||
| 7 | + final Widget child; | ||
| 8 | + | ||
| 9 | + @override | ||
| 10 | + Widget build(BuildContext context) { | ||
| 11 | + return MaterialApp( | ||
| 12 | + home: Scaffold(body: child), | ||
| 13 | + ); | ||
| 14 | + } | ||
| 15 | +} | ||
| 16 | + | ||
| 17 | +void main() { | ||
| 18 | + testWidgets('GetAnimatedBuilder defaults', (WidgetTester tester) async { | ||
| 19 | + await tester.pumpWidget( | ||
| 20 | + _Wrapper( | ||
| 21 | + child: GetAnimatedBuilder<int>( | ||
| 22 | + duration: const Duration(milliseconds: 500), | ||
| 23 | + tween: Tween(begin: 0, end: 10), | ||
| 24 | + idleValue: 0, | ||
| 25 | + builder: (_, value, __) => Text(value.toString()), | ||
| 26 | + delay: Duration.zero, | ||
| 27 | + child: Container(), | ||
| 28 | + ), | ||
| 29 | + ), | ||
| 30 | + ); | ||
| 31 | + | ||
| 32 | + // Verify that the widget starts with the idle value. | ||
| 33 | + expect(find.text('0'), findsOneWidget); | ||
| 34 | + | ||
| 35 | + // Wait for the animation to complete. | ||
| 36 | + await tester.pumpAndSettle(const Duration(milliseconds: 500)); | ||
| 37 | + | ||
| 38 | + // Verify that the widget ends with the final value. | ||
| 39 | + expect(find.text('10'), findsOneWidget); | ||
| 40 | + }); | ||
| 41 | + | ||
| 42 | + testWidgets('GetAnimatedBuilder changes value over time', (tester) async { | ||
| 43 | + await tester.pumpWidget( | ||
| 44 | + _Wrapper( | ||
| 45 | + child: GetAnimatedBuilder<double>( | ||
| 46 | + duration: const Duration(milliseconds: 500), | ||
| 47 | + tween: Tween<double>(begin: 0.0, end: 1.0), | ||
| 48 | + idleValue: 0.0, | ||
| 49 | + builder: (context, value, child) { | ||
| 50 | + return Opacity(opacity: value); | ||
| 51 | + }, | ||
| 52 | + delay: const Duration(milliseconds: 500), | ||
| 53 | + child: Container( | ||
| 54 | + width: 100, | ||
| 55 | + height: 100, | ||
| 56 | + color: Colors.red, | ||
| 57 | + ), | ||
| 58 | + ), | ||
| 59 | + ), | ||
| 60 | + ); | ||
| 61 | + | ||
| 62 | + // Initial state is idleValue | ||
| 63 | + expect(tester.widget<Opacity>(find.byType(Opacity)).opacity, 0.0); | ||
| 64 | + | ||
| 65 | + // Wait for the delay to finish | ||
| 66 | + await tester.pump(const Duration(milliseconds: 500)); | ||
| 67 | + | ||
| 68 | + // Verify that the value changes over time | ||
| 69 | + await tester.pump(const Duration(milliseconds: 100)); | ||
| 70 | + expect(tester.widget<Opacity>(find.byType(Opacity)).opacity, | ||
| 71 | + closeTo(0.2, 0.01)); | ||
| 72 | + | ||
| 73 | + await tester.pump(const Duration(milliseconds: 100)); | ||
| 74 | + expect(tester.widget<Opacity>(find.byType(Opacity)).opacity, | ||
| 75 | + closeTo(0.4, 0.01)); | ||
| 76 | + | ||
| 77 | + await tester.pump(const Duration(milliseconds: 100)); | ||
| 78 | + expect(tester.widget<Opacity>(find.byType(Opacity)).opacity, | ||
| 79 | + closeTo(0.6, 0.01)); | ||
| 80 | + | ||
| 81 | + await tester.pump(const Duration(milliseconds: 100)); | ||
| 82 | + expect(tester.widget<Opacity>(find.byType(Opacity)).opacity, | ||
| 83 | + closeTo(0.8, 0.01)); | ||
| 84 | + | ||
| 85 | + await tester.pump(const Duration(milliseconds: 100)); | ||
| 86 | + expect(tester.widget<Opacity>(find.byType(Opacity)).opacity, | ||
| 87 | + closeTo(1.0, 0.01)); | ||
| 88 | + }); | ||
| 89 | + | ||
| 90 | + testWidgets('onComplete callback is called when animation finishes', | ||
| 91 | + (WidgetTester tester) async { | ||
| 92 | + AnimationController? controller; | ||
| 93 | + var onCompleteCalled = false; | ||
| 94 | + | ||
| 95 | + await tester.pumpWidget( | ||
| 96 | + _Wrapper( | ||
| 97 | + child: GetAnimatedBuilder<int>( | ||
| 98 | + duration: const Duration(milliseconds: 500), | ||
| 99 | + tween: Tween(begin: 0, end: 10), | ||
| 100 | + idleValue: 0, | ||
| 101 | + builder: (_, value, __) => Text(value.toString()), | ||
| 102 | + delay: Duration.zero, | ||
| 103 | + onComplete: (c) { | ||
| 104 | + onCompleteCalled = true; | ||
| 105 | + controller = c; | ||
| 106 | + }, | ||
| 107 | + child: Container(), | ||
| 108 | + ), | ||
| 109 | + ), | ||
| 110 | + ); | ||
| 111 | + | ||
| 112 | + expect(onCompleteCalled, isFalse); | ||
| 113 | + | ||
| 114 | + // Wait for the animation to complete. | ||
| 115 | + await tester.pumpAndSettle(const Duration(milliseconds: 500)); | ||
| 116 | + | ||
| 117 | + // Verify that the onComplete callback was called. | ||
| 118 | + expect(controller, isNotNull); | ||
| 119 | + | ||
| 120 | + expect(onCompleteCalled, isTrue); | ||
| 121 | + }); | ||
| 122 | + | ||
| 123 | + testWidgets('onStart callback is called when animation starts', | ||
| 124 | + (WidgetTester tester) async { | ||
| 125 | + var onStartCalled = false; | ||
| 126 | + | ||
| 127 | + await tester.pumpWidget( | ||
| 128 | + _Wrapper( | ||
| 129 | + child: GetAnimatedBuilder( | ||
| 130 | + duration: const Duration(seconds: 1), | ||
| 131 | + delay: Duration.zero, | ||
| 132 | + tween: Tween<double>(begin: 0, end: 1), | ||
| 133 | + idleValue: 0, | ||
| 134 | + builder: (context, value, child) => Container(), | ||
| 135 | + child: Container(), | ||
| 136 | + onStart: (_) { | ||
| 137 | + onStartCalled = true; | ||
| 138 | + }, | ||
| 139 | + ), | ||
| 140 | + ), | ||
| 141 | + ); | ||
| 142 | + | ||
| 143 | + expect(onStartCalled, isFalse); | ||
| 144 | + | ||
| 145 | + await tester.pump(const Duration(milliseconds: 500)); | ||
| 146 | + expect(onStartCalled, isTrue); | ||
| 147 | + }); | ||
| 148 | + | ||
| 149 | + testWidgets('GetAnimatedBuilder delay', (WidgetTester tester) async { | ||
| 150 | + await tester.pumpWidget( | ||
| 151 | + _Wrapper( | ||
| 152 | + child: GetAnimatedBuilder<int>( | ||
| 153 | + duration: const Duration(milliseconds: 500), | ||
| 154 | + tween: Tween(begin: 0, end: 10), | ||
| 155 | + idleValue: 0, | ||
| 156 | + builder: (_, value, __) => Text(value.toString()), | ||
| 157 | + delay: const Duration(milliseconds: 500), | ||
| 158 | + child: Container(), | ||
| 159 | + ), | ||
| 160 | + ), | ||
| 161 | + ); | ||
| 162 | + | ||
| 163 | + // Verify that the widget starts with the idle value. | ||
| 164 | + expect(find.text('0'), findsOneWidget); | ||
| 165 | + | ||
| 166 | + // Wait for the delay to pass. | ||
| 167 | + await tester.pump(const Duration(milliseconds: 500)); | ||
| 168 | + | ||
| 169 | + // Verify that the animation has started. | ||
| 170 | + expect(find.text('0'), findsOneWidget); | ||
| 171 | + | ||
| 172 | + // Wait for the animation to complete. | ||
| 173 | + await tester.pumpAndSettle(const Duration(milliseconds: 500)); | ||
| 174 | + | ||
| 175 | + // Verify that the widget ends with the final value. | ||
| 176 | + expect(find.text('10'), findsOneWidget); | ||
| 177 | + }); | ||
| 178 | + | ||
| 179 | + testWidgets( | ||
| 180 | + 'FadeInAnimation in idle should be visible, but not visible when the animation starts', | ||
| 181 | + (WidgetTester tester) async { | ||
| 182 | + await tester.pumpWidget( | ||
| 183 | + _Wrapper( | ||
| 184 | + child: FadeInAnimation( | ||
| 185 | + delay: const Duration(milliseconds: 500), | ||
| 186 | + duration: const Duration(milliseconds: 500), | ||
| 187 | + idleValue: 0, | ||
| 188 | + child: const Text('Hello'), | ||
| 189 | + ), | ||
| 190 | + ), | ||
| 191 | + ); | ||
| 192 | + | ||
| 193 | + // in idle, the opacity should be 1.0 | ||
| 194 | + expect(tester.widget<Opacity>(find.byType(Opacity)).opacity, 1.0); | ||
| 195 | + | ||
| 196 | + // Wait for the delay to finish | ||
| 197 | + await tester.pump(const Duration(milliseconds: 500)); | ||
| 198 | + | ||
| 199 | + // When the animation starts the opacity should not be visible | ||
| 200 | + expect(tester.widget<Opacity>(find.byType(Opacity)).opacity, 0.0); | ||
| 201 | + | ||
| 202 | + // Verify that the value changes over time | ||
| 203 | + await tester.pump(const Duration(milliseconds: 100)); | ||
| 204 | + | ||
| 205 | + // The value should be updated | ||
| 206 | + expect(tester.widget<Opacity>(find.byType(Opacity)).opacity, | ||
| 207 | + closeTo(0.2, 0.01)); | ||
| 208 | + | ||
| 209 | + await tester.pump(const Duration(milliseconds: 100)); | ||
| 210 | + expect(tester.widget<Opacity>(find.byType(Opacity)).opacity, | ||
| 211 | + closeTo(0.4, 0.01)); | ||
| 212 | + | ||
| 213 | + await tester.pump(const Duration(milliseconds: 100)); | ||
| 214 | + expect(tester.widget<Opacity>(find.byType(Opacity)).opacity, | ||
| 215 | + closeTo(0.6, 0.01)); | ||
| 216 | + | ||
| 217 | + await tester.pump(const Duration(milliseconds: 100)); | ||
| 218 | + expect(tester.widget<Opacity>(find.byType(Opacity)).opacity, | ||
| 219 | + closeTo(0.8, 0.01)); | ||
| 220 | + | ||
| 221 | + await tester.pump(const Duration(milliseconds: 100)); | ||
| 222 | + expect(tester.widget<Opacity>(find.byType(Opacity)).opacity, | ||
| 223 | + closeTo(1.0, 0.01)); | ||
| 224 | + }); | ||
| 225 | + | ||
| 226 | + testWidgets( | ||
| 227 | + 'willResetOnDispose should false when fadeOut is the last animation in a sequential animation', | ||
| 228 | + (WidgetTester tester) async { | ||
| 229 | + await tester.pumpWidget( | ||
| 230 | + _Wrapper( | ||
| 231 | + child: const Text('Hello') | ||
| 232 | + .fadeIn( | ||
| 233 | + isSequential: true, | ||
| 234 | + duration: const Duration(milliseconds: 500), | ||
| 235 | + ) | ||
| 236 | + .fadeOut( | ||
| 237 | + isSequential: true, | ||
| 238 | + duration: const Duration(milliseconds: 500), | ||
| 239 | + ), | ||
| 240 | + ), | ||
| 241 | + ); | ||
| 242 | + | ||
| 243 | + // The variable starts as false | ||
| 244 | + expect( | ||
| 245 | + tester | ||
| 246 | + .state<GetAnimatedBuilderState>(find.byType(FadeOutAnimation)) | ||
| 247 | + .willResetOnDispose, | ||
| 248 | + false); | ||
| 249 | + | ||
| 250 | + // Jump to middle of next animation | ||
| 251 | + await tester.pump(const Duration(milliseconds: 500)); | ||
| 252 | + | ||
| 253 | + // The value should be false | ||
| 254 | + expect( | ||
| 255 | + tester | ||
| 256 | + .state<GetAnimatedBuilderState>(find.byType(FadeOutAnimation)) | ||
| 257 | + .willResetOnDispose, | ||
| 258 | + false); | ||
| 259 | + | ||
| 260 | + await tester.pumpAndSettle(); | ||
| 261 | + }); | ||
| 262 | + | ||
| 263 | + testWidgets( | ||
| 264 | + 'willResetOnDispose should true when fadeOut is not last animation in a sequential animation', | ||
| 265 | + (WidgetTester tester) async { | ||
| 266 | + await tester.pumpWidget( | ||
| 267 | + _Wrapper( | ||
| 268 | + child: const Text('Hello') | ||
| 269 | + .fadeOut( | ||
| 270 | + isSequential: true, | ||
| 271 | + duration: const Duration(milliseconds: 500), | ||
| 272 | + ) | ||
| 273 | + .fadeIn( | ||
| 274 | + isSequential: true, | ||
| 275 | + duration: const Duration(milliseconds: 500), | ||
| 276 | + ), | ||
| 277 | + ), | ||
| 278 | + ); | ||
| 279 | + | ||
| 280 | + // The variable starts as true | ||
| 281 | + expect( | ||
| 282 | + tester | ||
| 283 | + .state<GetAnimatedBuilderState>(find.byType(FadeOutAnimation)) | ||
| 284 | + .willResetOnDispose, | ||
| 285 | + true); | ||
| 286 | + | ||
| 287 | + // Jump to middle of next animation | ||
| 288 | + await tester.pump(const Duration(milliseconds: 500)); | ||
| 289 | + | ||
| 290 | + // The value should be true | ||
| 291 | + expect( | ||
| 292 | + tester | ||
| 293 | + .state<GetAnimatedBuilderState>(find.byType(FadeOutAnimation)) | ||
| 294 | + .willResetOnDispose, | ||
| 295 | + true); | ||
| 296 | + | ||
| 297 | + await tester.pumpAndSettle(); | ||
| 298 | + }); | ||
| 299 | + | ||
| 300 | + testWidgets('RotateAnimation', (WidgetTester tester) async { | ||
| 301 | + await tester.pumpWidget( | ||
| 302 | + RotateAnimation( | ||
| 303 | + duration: const Duration(seconds: 1), | ||
| 304 | + delay: Duration.zero, | ||
| 305 | + begin: 0.0, | ||
| 306 | + end: 360.0, | ||
| 307 | + child: Container(), | ||
| 308 | + ), | ||
| 309 | + ); | ||
| 310 | + expect(find.byType(RotateAnimation), findsOneWidget); | ||
| 311 | + await tester.pumpAndSettle(); | ||
| 312 | + }); | ||
| 313 | + | ||
| 314 | + testWidgets('ScaleAnimation', (WidgetTester tester) async { | ||
| 315 | + await tester.pumpWidget( | ||
| 316 | + ScaleAnimation( | ||
| 317 | + duration: const Duration(seconds: 1), | ||
| 318 | + delay: Duration.zero, | ||
| 319 | + begin: 1.0, | ||
| 320 | + end: 2.0, | ||
| 321 | + child: Container(), | ||
| 322 | + ), | ||
| 323 | + ); | ||
| 324 | + expect(find.byType(ScaleAnimation), findsOneWidget); | ||
| 325 | + await tester.pumpAndSettle(); | ||
| 326 | + }); | ||
| 327 | + | ||
| 328 | + testWidgets('WaveAnimation', (WidgetTester tester) async { | ||
| 329 | + await tester.pumpWidget( | ||
| 330 | + WaveAnimation( | ||
| 331 | + duration: const Duration(seconds: 1), | ||
| 332 | + delay: Duration.zero, | ||
| 333 | + begin: 1.0, | ||
| 334 | + end: 2.0, | ||
| 335 | + child: Container(), | ||
| 336 | + ), | ||
| 337 | + ); | ||
| 338 | + expect(find.byType(WaveAnimation), findsOneWidget); | ||
| 339 | + await tester.pumpAndSettle(); | ||
| 340 | + }); | ||
| 341 | + | ||
| 342 | + testWidgets('WobbleAnimation', (WidgetTester tester) async { | ||
| 343 | + await tester.pumpWidget( | ||
| 344 | + WobbleAnimation( | ||
| 345 | + duration: const Duration(seconds: 1), | ||
| 346 | + delay: Duration.zero, | ||
| 347 | + begin: 1.0, | ||
| 348 | + end: 2.0, | ||
| 349 | + child: Container(), | ||
| 350 | + ), | ||
| 351 | + ); | ||
| 352 | + expect(find.byType(WobbleAnimation), findsOneWidget); | ||
| 353 | + await tester.pumpAndSettle(); | ||
| 354 | + }); | ||
| 355 | + | ||
| 356 | + testWidgets('SlideAnimation', (WidgetTester tester) async { | ||
| 357 | + await tester.pumpWidget( | ||
| 358 | + SlideAnimation( | ||
| 359 | + offsetBuild: (p0, p1) => const Offset(1.0, 1.0), | ||
| 360 | + duration: const Duration(seconds: 1), | ||
| 361 | + delay: Duration.zero, | ||
| 362 | + begin: 0, | ||
| 363 | + end: 1, | ||
| 364 | + child: Container(), | ||
| 365 | + ), | ||
| 366 | + ); | ||
| 367 | + expect(find.byType(SlideAnimation), findsOneWidget); | ||
| 368 | + await tester.pumpAndSettle(); | ||
| 369 | + }); | ||
| 370 | + | ||
| 371 | + testWidgets('SlideInLeftAnimation', (WidgetTester tester) async { | ||
| 372 | + await tester.pumpWidget( | ||
| 373 | + _Wrapper( | ||
| 374 | + child: SlideInLeftAnimation( | ||
| 375 | + duration: const Duration(seconds: 1), | ||
| 376 | + delay: Duration.zero, | ||
| 377 | + begin: 1.0, | ||
| 378 | + end: 2.0, | ||
| 379 | + child: Container(), | ||
| 380 | + ), | ||
| 381 | + ), | ||
| 382 | + ); | ||
| 383 | + expect(find.byType(SlideInLeftAnimation), findsOneWidget); | ||
| 384 | + await tester.pumpAndSettle(); | ||
| 385 | + }); | ||
| 386 | + | ||
| 387 | + testWidgets('SlideInRightAnimation', (WidgetTester tester) async { | ||
| 388 | + await tester.pumpWidget( | ||
| 389 | + _Wrapper( | ||
| 390 | + child: SlideInRightAnimation( | ||
| 391 | + duration: const Duration(seconds: 1), | ||
| 392 | + delay: Duration.zero, | ||
| 393 | + begin: 1.0, | ||
| 394 | + end: 2.0, | ||
| 395 | + child: Container(), | ||
| 396 | + ), | ||
| 397 | + ), | ||
| 398 | + ); | ||
| 399 | + expect(find.byType(SlideInRightAnimation), findsOneWidget); | ||
| 400 | + await tester.pumpAndSettle(); | ||
| 401 | + }); | ||
| 402 | + | ||
| 403 | + testWidgets('SlideInUpAnimation', (WidgetTester tester) async { | ||
| 404 | + await tester.pumpWidget( | ||
| 405 | + _Wrapper( | ||
| 406 | + child: SlideInUpAnimation( | ||
| 407 | + duration: const Duration(seconds: 1), | ||
| 408 | + delay: Duration.zero, | ||
| 409 | + begin: 1.0, | ||
| 410 | + end: 2.0, | ||
| 411 | + child: Container(), | ||
| 412 | + ), | ||
| 413 | + ), | ||
| 414 | + ); | ||
| 415 | + expect(find.byType(SlideInUpAnimation), findsOneWidget); | ||
| 416 | + await tester.pumpAndSettle(); | ||
| 417 | + }); | ||
| 418 | + | ||
| 419 | + testWidgets('SlideInDownAnimation', (WidgetTester tester) async { | ||
| 420 | + await tester.pumpWidget( | ||
| 421 | + _Wrapper( | ||
| 422 | + child: SlideInDownAnimation( | ||
| 423 | + duration: const Duration(seconds: 1), | ||
| 424 | + delay: Duration.zero, | ||
| 425 | + begin: 1.0, | ||
| 426 | + end: 2.0, | ||
| 427 | + child: Container(), | ||
| 428 | + ), | ||
| 429 | + ), | ||
| 430 | + ); | ||
| 431 | + expect(find.byType(SlideInDownAnimation), findsOneWidget); | ||
| 432 | + await tester.pumpAndSettle(); | ||
| 433 | + }); | ||
| 434 | + | ||
| 435 | + testWidgets('BounceAnimation', (WidgetTester tester) async { | ||
| 436 | + await tester.pumpWidget( | ||
| 437 | + BounceAnimation( | ||
| 438 | + duration: const Duration(seconds: 1), | ||
| 439 | + delay: Duration.zero, | ||
| 440 | + begin: 0.0, | ||
| 441 | + end: 1.0, | ||
| 442 | + child: Container(), | ||
| 443 | + ), | ||
| 444 | + ); | ||
| 445 | + expect(find.byType(BounceAnimation), findsOneWidget); | ||
| 446 | + await tester.pumpAndSettle(); | ||
| 447 | + }); | ||
| 448 | + | ||
| 449 | + testWidgets('SpinAnimation', (WidgetTester tester) async { | ||
| 450 | + await tester.pumpWidget( | ||
| 451 | + SpinAnimation( | ||
| 452 | + duration: const Duration(seconds: 1), | ||
| 453 | + delay: Duration.zero, | ||
| 454 | + child: Container(), | ||
| 455 | + ), | ||
| 456 | + ); | ||
| 457 | + expect(find.byType(SpinAnimation), findsOneWidget); | ||
| 458 | + await tester.pumpAndSettle(); | ||
| 459 | + }); | ||
| 460 | + | ||
| 461 | + testWidgets('ColorAnimation', (WidgetTester tester) async { | ||
| 462 | + await tester.pumpWidget( | ||
| 463 | + ColorAnimation( | ||
| 464 | + duration: const Duration(seconds: 1), | ||
| 465 | + delay: Duration.zero, | ||
| 466 | + begin: Colors.blue, | ||
| 467 | + end: Colors.red, | ||
| 468 | + child: Container(), | ||
| 469 | + ), | ||
| 470 | + ); | ||
| 471 | + expect(find.byType(ColorAnimation), findsOneWidget); | ||
| 472 | + await tester.pumpAndSettle(); | ||
| 473 | + }); | ||
| 474 | + | ||
| 475 | + testWidgets('SizeAnimation', (WidgetTester tester) async { | ||
| 476 | + await tester.pumpWidget( | ||
| 477 | + SizeAnimation( | ||
| 478 | + duration: const Duration(seconds: 1), | ||
| 479 | + delay: Duration.zero, | ||
| 480 | + begin: 1.0, | ||
| 481 | + end: 2.0, | ||
| 482 | + child: Container(), | ||
| 483 | + ), | ||
| 484 | + ); | ||
| 485 | + expect(find.byType(SizeAnimation), findsOneWidget); | ||
| 486 | + await tester.pumpAndSettle(); | ||
| 487 | + }); | ||
| 488 | + | ||
| 489 | + testWidgets('BlurAnimation', (WidgetTester tester) async { | ||
| 490 | + await tester.pumpWidget( | ||
| 491 | + BlurAnimation( | ||
| 492 | + duration: const Duration(seconds: 1), | ||
| 493 | + delay: Duration.zero, | ||
| 494 | + begin: 1.0, | ||
| 495 | + end: 2.0, | ||
| 496 | + child: Container(), | ||
| 497 | + ), | ||
| 498 | + ); | ||
| 499 | + expect(find.byType(BlurAnimation), findsOneWidget); | ||
| 500 | + await tester.pumpAndSettle(); | ||
| 501 | + }); | ||
| 502 | + | ||
| 503 | + testWidgets('FlipAnimation', (WidgetTester tester) async { | ||
| 504 | + await tester.pumpWidget( | ||
| 505 | + FlipAnimation( | ||
| 506 | + duration: const Duration(seconds: 1), | ||
| 507 | + delay: Duration.zero, | ||
| 508 | + begin: 1.0, | ||
| 509 | + end: 2.0, | ||
| 510 | + child: Container(), | ||
| 511 | + ), | ||
| 512 | + ); | ||
| 513 | + expect(find.byType(FlipAnimation), findsOneWidget); | ||
| 514 | + await tester.pumpAndSettle(); | ||
| 515 | + }); | ||
| 516 | +} |
-
Please register or login to post a comment