Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
15 changes: 10 additions & 5 deletions example/lib/examples/everything_view.dart
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@
import 'dart:math';
import 'dart:ui' as ui;

import 'package:flutter/cupertino.dart';
import 'package:flutter/material.dart';
import 'package:flutter_animate/flutter_animate.dart';

Expand All @@ -20,8 +21,8 @@ class EverythingView extends StatelessWidget {
childAspectRatio: 0.85,
children: [
/***
A few fun / interesting examples
***/
A few fun / interesting examples
***/
tile(
'fade+tint+blur+scale',
a
Expand Down Expand Up @@ -103,9 +104,9 @@ class EverythingView extends StatelessWidget {
),

/***
Catalog of minimal examples for all visual effects.
In alphabetic order of the effect's class name.
***/
Catalog of minimal examples for all visual effects.
In alphabetic order of the effect's class name.
***/

tile('align', a.align()),

Expand Down Expand Up @@ -175,6 +176,10 @@ class EverythingView extends StatelessWidget {
tile('scaleY', a.scaleY()),
tile('scaleXY', a.scaleXY()),

tile('sizeX', a.sizeX()),
tile('sizeY', a.sizeY()),
tile('sizeXY', a.size()),

tile('shake', a.shake()),
tile('shakeX', a.shakeX()),
tile('shakeY', a.shakeY()),
Expand Down
1 change: 1 addition & 0 deletions lib/src/effects/effects.dart
Original file line number Diff line number Diff line change
Expand Up @@ -26,3 +26,4 @@ export 'then_effect.dart';
export 'tint_effect.dart';
export 'toggle_effect.dart';
export 'visibility_effect.dart';
export 'size_effect.dart';
130 changes: 130 additions & 0 deletions lib/src/effects/size_effect.dart
Original file line number Diff line number Diff line change
@@ -0,0 +1,130 @@
import 'dart:math' as math;
import 'package:flutter/widgets.dart';
import 'package:flutter_animate/flutter_animate.dart';

/// An effect that adjust the size of target between the specified [begin] and [end]
/// offset values. unlike [ScaleEffect], this effect does affect the real size
/// of the widget.
/// Defaults to `begin=0.0, end=1.0`.
@immutable
class SizeEffect extends Effect<double> {
static const double neutralValue = 1.0;
static const double defaultValue = 0.0;
static const double defaultAxisAlignment = 0.0;

const SizeEffect({
super.delay,
super.duration,
super.curve,
double? begin,
double? end,
this.fixedWidthFactor,
this.fixedHeightFactor,
this.alignment,
}) : super(
begin: begin ?? (end == null ? defaultValue : neutralValue),
end: end ?? neutralValue,
);

final AlignmentGeometry? alignment;
final double? fixedWidthFactor;
final double? fixedHeightFactor;

@override
Widget build(
BuildContext context,
Widget child,
AnimationController controller,
EffectEntry entry,
) {
final animation = buildAnimation(controller, entry);
return getOptimizedBuilder<double>(
animation: animation,
builder: (_, __) {
return ClipRect(
child: Align(
alignment: alignment ?? Alignment.center,
widthFactor: fixedWidthFactor ?? math.max(animation.value, 0.0),
heightFactor: fixedHeightFactor ?? math.max(animation.value, 0.0),
child: child,
),
);
},
);
}
}

/// Adds [SizeEffect] related extensions to [AnimateManager].
extension SizeEffectExtensions<T extends AnimateManager<T>> on T {
/// Adds a [SizeEffect] that adjust the size of target between
/// the specified [begin] and [end] offset values.
///
T size({
Duration? delay,
Duration? duration,
Curve? curve,
double? begin,
double? end,
double? fixedWidthFactor,
double? fixedHeightFactor,
AlignmentGeometry? alignment,
}) =>
addEffect(SizeEffect(
delay: delay,
duration: duration,
curve: curve,
begin: begin,
end: end,
alignment: alignment,
fixedWidthFactor: fixedWidthFactor,
fixedHeightFactor: fixedHeightFactor,
));

/// Adds a [SizeEffect] that adjust the size of target horizontally between
/// the specified [begin] and [end] values.
///
/// [axisAlignment] describes how to align the child along the horizontal axis
/// that [sizeFactor] is modifying.
T sizeX({
Duration? delay,
Duration? duration,
Curve? curve,
double? begin,
double? end,
double? axisAlignment,
}) =>
addEffect(SizeEffect(
delay: delay,
duration: duration,
curve: curve,
begin: begin,
end: end,
fixedHeightFactor: 1.0,
alignment: AlignmentDirectional(
axisAlignment ?? SizeEffect.defaultAxisAlignment, -1.0)));

/// Adds a [SizeEffect] that adjust the size of target vertically between
/// the specified [begin] and [end] values.
///
/// [axisAlignment] describes how to align the child along the vertical axis
/// that [sizeFactor] is modifying.
T sizeY({
Duration? delay,
Duration? duration,
Curve? curve,
double? begin,
double? end,
double? axisAlignment,
}) =>
addEffect(SizeEffect(
delay: delay,
duration: duration,
curve: curve,
begin: begin,
end: end,
fixedWidthFactor: 1.0,
alignment: AlignmentDirectional(
-1.0,
axisAlignment ?? SizeEffect.defaultAxisAlignment,
)));
}
61 changes: 61 additions & 0 deletions test/effects/size_test.dart
Original file line number Diff line number Diff line change
@@ -0,0 +1,61 @@
import 'package:flutter/material.dart';
import 'package:flutter_test/flutter_test.dart';
import 'package:flutter_animate/flutter_animate.dart';

import '../tester_extensions.dart';

void main() {
testWidgets('SizeEffect: size', (tester) async {
final animation = const FlutterLogo().animate().size(
duration: 1000.ms,
begin: 1.0,
end: 0.0,
);

await tester.pumpAnimation(MaterialApp(home: animation),
initialDelay: 500.ms);
_verifySize(tester, 0.5, 0.5);
});

testWidgets('SizeEffect: size', (tester) async {
final animation =
const FlutterLogo().animate().size(duration: 1000.ms, end: 2);

// Check halfway,
await tester.pumpAnimation(MaterialApp(home: animation),
initialDelay: 500.ms);
_verifySize(tester, 1.5, 1.5);
});

testWidgets('SizeEffect: sizeX', (tester) async {
final animation =
const FlutterLogo().animate().sizeX(duration: 1000.ms, end: 2);

// Check halfway,
await tester.pumpAnimation(MaterialApp(home: animation),
initialDelay: 500.ms);
_verifySize(tester, 1.5, 1);
});

testWidgets('SizeEffect: sizeY', (tester) async {
final animation =
const FlutterLogo().animate().sizeY(duration: 1000.ms, end: 2);

// Check halfway,
await tester.pumpAnimation(MaterialApp(home: animation),
initialDelay: 500.ms);
_verifySize(tester, 1, 1.5);
});
}

_verifySize(
WidgetTester tester, double widthFactor, double heightFactor) async {
tester.widget(find.byType(Align));
expect(
tester.widget(find.byType(Align)),
isA<Align>()
.having(
(Align align) => align.widthFactor, 'widthFactor', widthFactor)
.having((Align align) => align.heightFactor, 'heightFactor',
heightFactor));
}