Skip to content

Commit b989428

Browse files
Add SizeTransition.fixedCrossAxisSizeFactor (#134659)
Fixes flutter/flutter#134654
1 parent f38244f commit b989428

File tree

2 files changed

+140
-3
lines changed

2 files changed

+140
-3
lines changed

packages/flutter/lib/src/widgets/transitions.dart

Lines changed: 13 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -441,8 +441,10 @@ class SizeTransition extends AnimatedWidget {
441441
this.axis = Axis.vertical,
442442
required Animation<double> sizeFactor,
443443
this.axisAlignment = 0.0,
444+
this.fixedCrossAxisSizeFactor,
444445
this.child,
445-
}) : super(listenable: sizeFactor);
446+
}) : assert(fixedCrossAxisSizeFactor == null || fixedCrossAxisSizeFactor >= 0.0),
447+
super(listenable: sizeFactor);
446448

447449
/// [Axis.horizontal] if [sizeFactor] modifies the width, otherwise
448450
/// [Axis.vertical].
@@ -471,6 +473,14 @@ class SizeTransition extends AnimatedWidget {
471473
/// A value of 0.0 (the default) indicates the center for either [axis] value.
472474
final double axisAlignment;
473475

476+
/// The factor by which to multiply the cross axis size of the child.
477+
///
478+
/// If the value of [fixedCrossAxisSizeFactor] is less than one, the child
479+
/// will be clipped along the appropriate axis.
480+
///
481+
/// If `null` (the default), the cross axis size is as large as the parent.
482+
final double? fixedCrossAxisSizeFactor;
483+
474484
/// The widget below this widget in the tree.
475485
///
476486
/// {@macro flutter.widgets.ProxyWidget.child}
@@ -487,8 +497,8 @@ class SizeTransition extends AnimatedWidget {
487497
return ClipRect(
488498
child: Align(
489499
alignment: alignment,
490-
heightFactor: axis == Axis.vertical ? math.max(sizeFactor.value, 0.0) : null,
491-
widthFactor: axis == Axis.horizontal ? math.max(sizeFactor.value, 0.0) : null,
500+
heightFactor: axis == Axis.vertical ? math.max(sizeFactor.value, 0.0) : fixedCrossAxisSizeFactor,
501+
widthFactor: axis == Axis.horizontal ? math.max(sizeFactor.value, 0.0) : fixedCrossAxisSizeFactor,
492502
child: child,
493503
),
494504
);

packages/flutter/test/widgets/transitions_test.dart

Lines changed: 127 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -244,6 +244,7 @@ void main() {
244244
textDirection: TextDirection.ltr,
245245
child: SizeTransition(
246246
sizeFactor: animation,
247+
fixedCrossAxisSizeFactor: 2.0,
247248
child: const Text('Ready'),
248249
),
249250
);
@@ -252,18 +253,22 @@ void main() {
252253

253254
final RenderPositionedBox actualPositionedBox = tester.renderObject(find.byType(Align));
254255
expect(actualPositionedBox.heightFactor, 0.0);
256+
expect(actualPositionedBox.widthFactor, 2.0);
255257

256258
controller.value = 0.0;
257259
await tester.pump();
258260
expect(actualPositionedBox.heightFactor, 0.0);
261+
expect(actualPositionedBox.widthFactor, 2.0);
259262

260263
controller.value = 0.75;
261264
await tester.pump();
262265
expect(actualPositionedBox.heightFactor, 0.5);
266+
expect(actualPositionedBox.widthFactor, 2.0);
263267

264268
controller.value = 1.0;
265269
await tester.pump();
266270
expect(actualPositionedBox.heightFactor, 1.0);
271+
expect(actualPositionedBox.widthFactor, 2.0);
267272
});
268273

269274
testWidgetsWithLeakTracking('SizeTransition clamps negative size factors - horizontal axis', (WidgetTester tester) async {
@@ -275,6 +280,7 @@ void main() {
275280
child: SizeTransition(
276281
axis: Axis.horizontal,
277282
sizeFactor: animation,
283+
fixedCrossAxisSizeFactor: 1.0,
278284
child: const Text('Ready'),
279285
),
280286
);
@@ -283,18 +289,139 @@ void main() {
283289

284290
final RenderPositionedBox actualPositionedBox = tester.renderObject(find.byType(Align));
285291
expect(actualPositionedBox.widthFactor, 0.0);
292+
expect(actualPositionedBox.heightFactor, 1.0);
286293

287294
controller.value = 0.0;
288295
await tester.pump();
289296
expect(actualPositionedBox.widthFactor, 0.0);
297+
expect(actualPositionedBox.heightFactor, 1.0);
290298

291299
controller.value = 0.75;
292300
await tester.pump();
293301
expect(actualPositionedBox.widthFactor, 0.5);
302+
expect(actualPositionedBox.heightFactor, 1.0);
303+
304+
controller.value = 1.0;
305+
await tester.pump();
306+
expect(actualPositionedBox.widthFactor, 1.0);
307+
expect(actualPositionedBox.heightFactor, 1.0);
308+
});
309+
310+
testWidgetsWithLeakTracking('SizeTransition with fixedCrossAxisSizeFactor should size its cross axis from its children - vertical axis', (WidgetTester tester) async {
311+
final AnimationController controller = AnimationController(vsync: const TestVSync());
312+
final Animation<double> animation = Tween<double>(begin: 0, end: 1.0).animate(controller);
313+
314+
const Key key = Key('key');
315+
316+
final Widget widget = Directionality(
317+
textDirection: TextDirection.ltr,
318+
child: Center(
319+
child: SizedBox(
320+
key: key,
321+
child: SizeTransition(
322+
sizeFactor: animation,
323+
fixedCrossAxisSizeFactor: 1.0,
324+
child: const SizedBox.square(dimension: 100),
325+
),
326+
),
327+
),
328+
);
329+
330+
await tester.pumpWidget(widget);
331+
332+
final RenderPositionedBox actualPositionedBox = tester.renderObject(find.byType(Align));
333+
expect(actualPositionedBox.heightFactor, 0.0);
334+
expect(actualPositionedBox.widthFactor, 1.0);
335+
expect(tester.getSize(find.byKey(key)), const Size(100, 0));
336+
337+
controller.value = 0.0;
338+
await tester.pump();
339+
expect(actualPositionedBox.heightFactor, 0.0);
340+
expect(actualPositionedBox.widthFactor, 1.0);
341+
expect(tester.getSize(find.byKey(key)), const Size(100, 0));
342+
343+
controller.value = 0.5;
344+
await tester.pump();
345+
expect(actualPositionedBox.heightFactor, 0.5);
346+
expect(actualPositionedBox.widthFactor, 1.0);
347+
expect(tester.getSize(find.byKey(key)), const Size(100, 50));
348+
349+
controller.value = 1.0;
350+
await tester.pump();
351+
expect(actualPositionedBox.heightFactor, 1.0);
352+
expect(actualPositionedBox.widthFactor, 1.0);
353+
expect(tester.getSize(find.byKey(key)), const Size.square(100));
354+
355+
controller.value = 0.5;
356+
await tester.pump();
357+
expect(actualPositionedBox.heightFactor, 0.5);
358+
expect(actualPositionedBox.widthFactor, 1.0);
359+
expect(tester.getSize(find.byKey(key)), const Size(100, 50));
360+
361+
controller.value = 0.0;
362+
await tester.pump();
363+
expect(actualPositionedBox.heightFactor, 0.0);
364+
expect(actualPositionedBox.widthFactor, 1.0);
365+
expect(tester.getSize(find.byKey(key)), const Size(100, 0));
366+
});
367+
368+
testWidgetsWithLeakTracking('SizeTransition with fixedCrossAxisSizeFactor should size its cross axis from its children - horizontal axis', (WidgetTester tester) async {
369+
final AnimationController controller = AnimationController(vsync: const TestVSync());
370+
final Animation<double> animation = Tween<double>(begin: 0.0, end: 1.0).animate(controller);
371+
372+
const Key key = Key('key');
373+
374+
final Widget widget = Directionality(
375+
textDirection: TextDirection.ltr,
376+
child: Center(
377+
child: SizedBox(
378+
key: key,
379+
child: SizeTransition(
380+
axis: Axis.horizontal,
381+
sizeFactor: animation,
382+
fixedCrossAxisSizeFactor: 1.0,
383+
child: const SizedBox.square(dimension: 100),
384+
),
385+
),
386+
),
387+
);
388+
389+
await tester.pumpWidget(widget);
390+
391+
final RenderPositionedBox actualPositionedBox = tester.renderObject(find.byType(Align));
392+
expect(actualPositionedBox.heightFactor, 1.0);
393+
expect(actualPositionedBox.widthFactor, 0.0);
394+
expect(tester.getSize(find.byKey(key)), const Size(0, 100));
395+
396+
controller.value = 0.0;
397+
await tester.pump();
398+
expect(actualPositionedBox.heightFactor, 1.0);
399+
expect(actualPositionedBox.widthFactor, 0.0);
400+
expect(tester.getSize(find.byKey(key)), const Size(0, 100));
401+
402+
controller.value = 0.5;
403+
await tester.pump();
404+
expect(actualPositionedBox.heightFactor, 1.0);
405+
expect(actualPositionedBox.widthFactor, 0.5);
406+
expect(tester.getSize(find.byKey(key)), const Size(50, 100));
294407

295408
controller.value = 1.0;
296409
await tester.pump();
410+
expect(actualPositionedBox.heightFactor, 1.0);
297411
expect(actualPositionedBox.widthFactor, 1.0);
412+
expect(tester.getSize(find.byKey(key)), const Size.square(100));
413+
414+
controller.value = 0.5;
415+
await tester.pump();
416+
expect(actualPositionedBox.heightFactor, 1.0);
417+
expect(actualPositionedBox.widthFactor, 0.5);
418+
expect(tester.getSize(find.byKey(key)), const Size(50, 100));
419+
420+
controller.value = 0.0;
421+
await tester.pump();
422+
expect(actualPositionedBox.heightFactor, 1.0);
423+
expect(actualPositionedBox.widthFactor, 0.0);
424+
expect(tester.getSize(find.byKey(key)), const Size(0, 100));
298425
});
299426

300427
testWidgetsWithLeakTracking('MatrixTransition animates', (WidgetTester tester) async {

0 commit comments

Comments
 (0)