mirror of
https://github.com/flutter/flutter.git
synced 2026-02-20 02:29:02 +08:00
WIP Commits separated as follows: - Update lints in analysis_options files - Run `dart fix --apply` - Clean up leftover analysis issues - Run `dart format .` in the right places. Local analysis and testing passes. Checking CI now. Part of https://github.com/flutter/flutter/issues/178827 - Adoption of flutter_lints in examples/api coming in a separate change (cc @loic-sharma) ## Pre-launch Checklist - [ ] I read the [Contributor Guide] and followed the process outlined there for submitting PRs. - [ ] I read the [Tree Hygiene] wiki page, which explains my responsibilities. - [ ] I read and followed the [Flutter Style Guide], including [Features we expect every widget to implement]. - [ ] I signed the [CLA]. - [ ] I listed at least one issue that this PR fixes in the description above. - [ ] I updated/added relevant documentation (doc comments with `///`). - [ ] I added new tests to check the change I am making, or this PR is [test-exempt]. - [ ] I followed the [breaking change policy] and added [Data Driven Fixes] where supported. - [ ] All existing and new tests are passing. If you need help, consider asking for advice on the #hackers-new channel on [Discord]. **Note**: The Flutter team is currently trialing the use of [Gemini Code Assist for GitHub](https://developers.google.com/gemini-code-assist/docs/review-github-code). Comments from the `gemini-code-assist` bot should not be taken as authoritative feedback from the Flutter team. If you find its comments useful you can update your code accordingly, but if you are unsure or disagree with the feedback, please feel free to wait for a Flutter team member's review for guidance on which automated comments should be addressed. <!-- Links --> [Contributor Guide]: https://github.com/flutter/flutter/blob/main/docs/contributing/Tree-hygiene.md#overview [Tree Hygiene]: https://github.com/flutter/flutter/blob/main/docs/contributing/Tree-hygiene.md [test-exempt]: https://github.com/flutter/flutter/blob/main/docs/contributing/Tree-hygiene.md#tests [Flutter Style Guide]: https://github.com/flutter/flutter/blob/main/docs/contributing/Style-guide-for-Flutter-repo.md [Features we expect every widget to implement]: https://github.com/flutter/flutter/blob/main/docs/contributing/Style-guide-for-Flutter-repo.md#features-we-expect-every-widget-to-implement [CLA]: https://cla.developers.google.com/ [flutter/tests]: https://github.com/flutter/tests [breaking change policy]: https://github.com/flutter/flutter/blob/main/docs/contributing/Tree-hygiene.md#handling-breaking-changes [Discord]: https://github.com/flutter/flutter/blob/main/docs/contributing/Chat.md [Data Driven Fixes]: https://github.com/flutter/flutter/blob/main/docs/contributing/Data-driven-Fixes.md
967 lines
29 KiB
Dart
967 lines
29 KiB
Dart
// Copyright 2014 The Flutter Authors. All rights reserved.
|
|
// Use of this source code is governed by a BSD-style license that can be
|
|
// found in the LICENSE file.
|
|
|
|
import 'package:flutter/material.dart';
|
|
import 'package:flutter_test/flutter_test.dart';
|
|
|
|
class MockOnEndFunction {
|
|
int called = 0;
|
|
|
|
void handler() {
|
|
called++;
|
|
}
|
|
}
|
|
|
|
const Duration animationDuration = Duration(milliseconds: 1000);
|
|
const Duration additionalDelay = Duration(milliseconds: 1);
|
|
|
|
void main() {
|
|
late MockOnEndFunction mockOnEndFunction;
|
|
const switchKey = Key('switchKey');
|
|
|
|
setUp(() {
|
|
mockOnEndFunction = MockOnEndFunction();
|
|
});
|
|
|
|
testWidgets('BoxConstraintsTween control test', (WidgetTester tester) async {
|
|
final tween = BoxConstraintsTween(
|
|
begin: BoxConstraints.tight(const Size(20.0, 50.0)),
|
|
end: BoxConstraints.tight(const Size(10.0, 30.0)),
|
|
);
|
|
final BoxConstraints result = tween.lerp(0.25);
|
|
expect(result.minWidth, 17.5);
|
|
expect(result.maxWidth, 17.5);
|
|
expect(result.minHeight, 45.0);
|
|
expect(result.maxHeight, 45.0);
|
|
});
|
|
|
|
testWidgets('DecorationTween control test', (WidgetTester tester) async {
|
|
final tween = DecorationTween(
|
|
begin: const BoxDecoration(color: Color(0xFF00FF00)),
|
|
end: const BoxDecoration(color: Color(0xFFFFFF00)),
|
|
);
|
|
final result = tween.lerp(0.25) as BoxDecoration;
|
|
expect(result.color, isSameColorAs(const Color(0xFF3FFF00)));
|
|
});
|
|
|
|
testWidgets('EdgeInsetsTween control test', (WidgetTester tester) async {
|
|
final tween = EdgeInsetsTween(
|
|
begin: const EdgeInsets.symmetric(vertical: 50.0),
|
|
end: const EdgeInsets.only(top: 10.0, bottom: 30.0),
|
|
);
|
|
final EdgeInsets result = tween.lerp(0.25);
|
|
expect(result.left, 0.0);
|
|
expect(result.right, 0.0);
|
|
expect(result.top, 40.0);
|
|
expect(result.bottom, 45.0);
|
|
});
|
|
|
|
testWidgets('Matrix4Tween control test', (WidgetTester tester) async {
|
|
final tween = Matrix4Tween(
|
|
begin: Matrix4.translationValues(10.0, 20.0, 30.0),
|
|
end: Matrix4.translationValues(14.0, 24.0, 34.0),
|
|
);
|
|
final Matrix4 result = tween.lerp(0.25);
|
|
expect(result, equals(Matrix4.translationValues(11.0, 21.0, 31.0)));
|
|
});
|
|
|
|
testWidgets('AnimatedContainer onEnd callback test', (WidgetTester tester) async {
|
|
await tester.pumpWidget(
|
|
wrap(
|
|
child: TestAnimatedWidget(
|
|
callback: mockOnEndFunction.handler,
|
|
switchKey: switchKey,
|
|
state: _TestAnimatedContainerWidgetState(),
|
|
),
|
|
),
|
|
);
|
|
|
|
final Finder widgetFinder = find.byKey(switchKey);
|
|
|
|
await tester.tap(widgetFinder);
|
|
|
|
await tester.pump();
|
|
expect(mockOnEndFunction.called, 0);
|
|
await tester.pump(animationDuration);
|
|
expect(mockOnEndFunction.called, 0);
|
|
await tester.pump(additionalDelay);
|
|
expect(mockOnEndFunction.called, 1);
|
|
|
|
await tapTest2and3(tester, widgetFinder, mockOnEndFunction);
|
|
});
|
|
|
|
testWidgets('AnimatedPadding onEnd callback test', (WidgetTester tester) async {
|
|
await tester.pumpWidget(
|
|
wrap(
|
|
child: TestAnimatedWidget(
|
|
callback: mockOnEndFunction.handler,
|
|
switchKey: switchKey,
|
|
state: _TestAnimatedPaddingWidgetState(),
|
|
),
|
|
),
|
|
);
|
|
|
|
final Finder widgetFinder = find.byKey(switchKey);
|
|
|
|
await tester.tap(widgetFinder);
|
|
|
|
await tester.pump();
|
|
expect(mockOnEndFunction.called, 0);
|
|
await tester.pump(animationDuration);
|
|
expect(mockOnEndFunction.called, 0);
|
|
await tester.pump(additionalDelay);
|
|
expect(mockOnEndFunction.called, 1);
|
|
|
|
await tapTest2and3(tester, widgetFinder, mockOnEndFunction);
|
|
});
|
|
|
|
testWidgets('AnimatedAlign onEnd callback test', (WidgetTester tester) async {
|
|
await tester.pumpWidget(
|
|
wrap(
|
|
child: TestAnimatedWidget(
|
|
callback: mockOnEndFunction.handler,
|
|
switchKey: switchKey,
|
|
state: _TestAnimatedAlignWidgetState(),
|
|
),
|
|
),
|
|
);
|
|
|
|
final Finder widgetFinder = find.byKey(switchKey);
|
|
|
|
await tester.tap(widgetFinder);
|
|
|
|
await tester.pump();
|
|
expect(mockOnEndFunction.called, 0);
|
|
await tester.pump(animationDuration);
|
|
expect(mockOnEndFunction.called, 0);
|
|
await tester.pump(additionalDelay);
|
|
expect(mockOnEndFunction.called, 1);
|
|
|
|
await tapTest2and3(tester, widgetFinder, mockOnEndFunction);
|
|
});
|
|
|
|
testWidgets('AnimatedPositioned onEnd callback test', (WidgetTester tester) async {
|
|
await tester.pumpWidget(
|
|
wrap(
|
|
child: TestAnimatedWidget(
|
|
callback: mockOnEndFunction.handler,
|
|
switchKey: switchKey,
|
|
state: _TestAnimatedPositionedWidgetState(),
|
|
),
|
|
),
|
|
);
|
|
|
|
final Finder widgetFinder = find.byKey(switchKey);
|
|
|
|
await tester.tap(widgetFinder);
|
|
|
|
await tester.pump();
|
|
expect(mockOnEndFunction.called, 0);
|
|
await tester.pump(animationDuration);
|
|
expect(mockOnEndFunction.called, 0);
|
|
await tester.pump(additionalDelay);
|
|
expect(mockOnEndFunction.called, 1);
|
|
|
|
await tapTest2and3(tester, widgetFinder, mockOnEndFunction);
|
|
});
|
|
|
|
testWidgets('AnimatedPositionedDirectional onEnd callback test', (WidgetTester tester) async {
|
|
await tester.pumpWidget(
|
|
wrap(
|
|
child: TestAnimatedWidget(
|
|
callback: mockOnEndFunction.handler,
|
|
switchKey: switchKey,
|
|
state: _TestAnimatedPositionedDirectionalWidgetState(),
|
|
),
|
|
),
|
|
);
|
|
|
|
final Finder widgetFinder = find.byKey(switchKey);
|
|
|
|
await tester.tap(widgetFinder);
|
|
|
|
await tester.pump();
|
|
expect(mockOnEndFunction.called, 0);
|
|
await tester.pump(animationDuration);
|
|
expect(mockOnEndFunction.called, 0);
|
|
await tester.pump(additionalDelay);
|
|
expect(mockOnEndFunction.called, 1);
|
|
|
|
await tapTest2and3(tester, widgetFinder, mockOnEndFunction);
|
|
});
|
|
|
|
testWidgets('AnimatedSlide onEnd callback test', (WidgetTester tester) async {
|
|
await tester.pumpWidget(
|
|
wrap(
|
|
child: TestAnimatedWidget(
|
|
callback: mockOnEndFunction.handler,
|
|
switchKey: switchKey,
|
|
state: _TestAnimatedSlideWidgetState(),
|
|
),
|
|
),
|
|
);
|
|
|
|
final Finder widgetFinder = find.byKey(switchKey);
|
|
|
|
await tester.tap(widgetFinder);
|
|
await tester.pump();
|
|
expect(mockOnEndFunction.called, 0);
|
|
await tester.pump(animationDuration);
|
|
expect(mockOnEndFunction.called, 0);
|
|
await tester.pump(additionalDelay);
|
|
expect(mockOnEndFunction.called, 1);
|
|
|
|
await tapTest2and3(tester, widgetFinder, mockOnEndFunction);
|
|
});
|
|
|
|
testWidgets('AnimatedSlide transition test', (WidgetTester tester) async {
|
|
await tester.pumpWidget(
|
|
wrap(
|
|
child: TestAnimatedWidget(switchKey: switchKey, state: _TestAnimatedSlideWidgetState()),
|
|
),
|
|
);
|
|
|
|
final RebuildCountingState<StatefulWidget> state = tester
|
|
.widget<TestAnimatedWidget>(find.byType(TestAnimatedWidget))
|
|
.rebuildState!;
|
|
final Finder switchFinder = find.byKey(switchKey);
|
|
final SlideTransition slideWidget = tester.widget<SlideTransition>(
|
|
find.ancestor(of: find.byType(Placeholder), matching: find.byType(SlideTransition)).first,
|
|
);
|
|
|
|
expect(state.builds, equals(1));
|
|
|
|
await tester.tap(switchFinder);
|
|
expect(state.builds, equals(1));
|
|
await tester.pump();
|
|
expect(slideWidget.position.value, equals(Offset.zero));
|
|
expect(state.builds, equals(2));
|
|
|
|
await tester.pump(const Duration(milliseconds: 500));
|
|
expect(slideWidget.position.value, equals(const Offset(0.5, 0.5)));
|
|
expect(state.builds, equals(2));
|
|
await tester.pump(const Duration(milliseconds: 250));
|
|
expect(slideWidget.position.value, equals(const Offset(0.75, 0.75)));
|
|
expect(state.builds, equals(2));
|
|
await tester.pump(const Duration(milliseconds: 250));
|
|
expect(slideWidget.position.value, equals(const Offset(1, 1)));
|
|
expect(state.builds, equals(2));
|
|
});
|
|
|
|
testWidgets('AnimatedScale onEnd callback test', (WidgetTester tester) async {
|
|
await tester.pumpWidget(
|
|
wrap(
|
|
child: TestAnimatedWidget(
|
|
callback: mockOnEndFunction.handler,
|
|
switchKey: switchKey,
|
|
state: _TestAnimatedScaleWidgetState(),
|
|
),
|
|
),
|
|
);
|
|
|
|
final Finder widgetFinder = find.byKey(switchKey);
|
|
|
|
await tester.tap(widgetFinder);
|
|
await tester.pump();
|
|
expect(mockOnEndFunction.called, 0);
|
|
await tester.pump(animationDuration);
|
|
expect(mockOnEndFunction.called, 0);
|
|
await tester.pump(additionalDelay);
|
|
expect(mockOnEndFunction.called, 1);
|
|
|
|
await tapTest2and3(tester, widgetFinder, mockOnEndFunction);
|
|
});
|
|
|
|
testWidgets('AnimatedScale transition test', (WidgetTester tester) async {
|
|
await tester.pumpWidget(
|
|
wrap(
|
|
child: TestAnimatedWidget(switchKey: switchKey, state: _TestAnimatedScaleWidgetState()),
|
|
),
|
|
);
|
|
|
|
final RebuildCountingState<StatefulWidget> state = tester
|
|
.widget<TestAnimatedWidget>(find.byType(TestAnimatedWidget))
|
|
.rebuildState!;
|
|
final Finder switchFinder = find.byKey(switchKey);
|
|
final ScaleTransition scaleWidget = tester.widget<ScaleTransition>(
|
|
find.ancestor(of: find.byType(Placeholder), matching: find.byType(ScaleTransition)).first,
|
|
);
|
|
|
|
expect(state.builds, equals(1));
|
|
|
|
await tester.tap(switchFinder);
|
|
expect(state.builds, equals(1));
|
|
await tester.pump();
|
|
expect(scaleWidget.scale.value, equals(1.0));
|
|
expect(state.builds, equals(2));
|
|
|
|
await tester.pump(const Duration(milliseconds: 500));
|
|
expect(scaleWidget.scale.value, equals(1.5));
|
|
expect(state.builds, equals(2));
|
|
await tester.pump(const Duration(milliseconds: 250));
|
|
expect(scaleWidget.scale.value, equals(1.75));
|
|
expect(state.builds, equals(2));
|
|
await tester.pump(const Duration(milliseconds: 250));
|
|
expect(scaleWidget.scale.value, equals(2.0));
|
|
expect(state.builds, equals(2));
|
|
});
|
|
|
|
testWidgets('AnimatedRotation onEnd callback test', (WidgetTester tester) async {
|
|
await tester.pumpWidget(
|
|
wrap(
|
|
child: TestAnimatedWidget(
|
|
callback: mockOnEndFunction.handler,
|
|
switchKey: switchKey,
|
|
state: _TestAnimatedRotationWidgetState(),
|
|
),
|
|
),
|
|
);
|
|
|
|
final Finder widgetFinder = find.byKey(switchKey);
|
|
|
|
await tester.tap(widgetFinder);
|
|
await tester.pump();
|
|
expect(mockOnEndFunction.called, 0);
|
|
await tester.pump(animationDuration);
|
|
expect(mockOnEndFunction.called, 0);
|
|
await tester.pump(additionalDelay);
|
|
expect(mockOnEndFunction.called, 1);
|
|
|
|
await tapTest2and3(tester, widgetFinder, mockOnEndFunction);
|
|
});
|
|
|
|
testWidgets('AnimatedRotation transition test', (WidgetTester tester) async {
|
|
await tester.pumpWidget(
|
|
wrap(
|
|
child: TestAnimatedWidget(switchKey: switchKey, state: _TestAnimatedRotationWidgetState()),
|
|
),
|
|
);
|
|
|
|
final RebuildCountingState<StatefulWidget> state = tester
|
|
.widget<TestAnimatedWidget>(find.byType(TestAnimatedWidget))
|
|
.rebuildState!;
|
|
final Finder switchFinder = find.byKey(switchKey);
|
|
final RotationTransition rotationWidget = tester.widget<RotationTransition>(
|
|
find.ancestor(of: find.byType(Placeholder), matching: find.byType(RotationTransition)).first,
|
|
);
|
|
|
|
expect(state.builds, equals(1));
|
|
|
|
await tester.tap(switchFinder);
|
|
expect(state.builds, equals(1));
|
|
await tester.pump();
|
|
expect(rotationWidget.turns.value, equals(0.0));
|
|
expect(state.builds, equals(2));
|
|
|
|
await tester.pump(const Duration(milliseconds: 500));
|
|
expect(rotationWidget.turns.value, equals(0.75));
|
|
expect(state.builds, equals(2));
|
|
await tester.pump(const Duration(milliseconds: 250));
|
|
expect(rotationWidget.turns.value, equals(1.125));
|
|
expect(state.builds, equals(2));
|
|
await tester.pump(const Duration(milliseconds: 250));
|
|
expect(rotationWidget.turns.value, equals(1.5));
|
|
expect(state.builds, equals(2));
|
|
});
|
|
|
|
testWidgets('AnimatedOpacity onEnd callback test', (WidgetTester tester) async {
|
|
await tester.pumpWidget(
|
|
wrap(
|
|
child: TestAnimatedWidget(
|
|
callback: mockOnEndFunction.handler,
|
|
switchKey: switchKey,
|
|
state: _TestAnimatedOpacityWidgetState(),
|
|
),
|
|
),
|
|
);
|
|
|
|
final Finder widgetFinder = find.byKey(switchKey);
|
|
|
|
await tester.tap(widgetFinder);
|
|
await tester.pump();
|
|
expect(mockOnEndFunction.called, 0);
|
|
await tester.pump(animationDuration);
|
|
expect(mockOnEndFunction.called, 0);
|
|
await tester.pump(additionalDelay);
|
|
expect(mockOnEndFunction.called, 1);
|
|
|
|
await tapTest2and3(tester, widgetFinder, mockOnEndFunction);
|
|
});
|
|
|
|
testWidgets('AnimatedOpacity transition test', (WidgetTester tester) async {
|
|
await tester.pumpWidget(
|
|
wrap(
|
|
child: TestAnimatedWidget(switchKey: switchKey, state: _TestAnimatedOpacityWidgetState()),
|
|
),
|
|
);
|
|
|
|
final RebuildCountingState<StatefulWidget> state = tester
|
|
.widget<TestAnimatedWidget>(find.byType(TestAnimatedWidget))
|
|
.rebuildState!;
|
|
final Finder switchFinder = find.byKey(switchKey);
|
|
final FadeTransition opacityWidget = tester.widget<FadeTransition>(
|
|
find.ancestor(of: find.byType(Placeholder), matching: find.byType(FadeTransition)).first,
|
|
);
|
|
|
|
expect(state.builds, equals(1));
|
|
|
|
await tester.tap(switchFinder);
|
|
expect(state.builds, equals(1));
|
|
await tester.pump();
|
|
expect(opacityWidget.opacity.value, equals(0.0));
|
|
expect(state.builds, equals(2));
|
|
|
|
await tester.pump(const Duration(milliseconds: 500));
|
|
expect(opacityWidget.opacity.value, equals(0.5));
|
|
expect(state.builds, equals(2));
|
|
await tester.pump(const Duration(milliseconds: 250));
|
|
expect(opacityWidget.opacity.value, equals(0.75));
|
|
expect(state.builds, equals(2));
|
|
await tester.pump(const Duration(milliseconds: 250));
|
|
expect(opacityWidget.opacity.value, equals(1.0));
|
|
expect(state.builds, equals(2));
|
|
});
|
|
|
|
testWidgets('AnimatedFractionallySizedBox onEnd callback test', (WidgetTester tester) async {
|
|
await tester.pumpWidget(
|
|
wrap(
|
|
child: TestAnimatedWidget(
|
|
callback: mockOnEndFunction.handler,
|
|
switchKey: switchKey,
|
|
state: _TestAnimatedFractionallySizedBoxWidgetState(),
|
|
),
|
|
),
|
|
);
|
|
|
|
final Finder widgetFinder = find.byKey(switchKey);
|
|
|
|
await tester.tap(widgetFinder);
|
|
await tester.pump();
|
|
expect(mockOnEndFunction.called, 0);
|
|
await tester.pump(animationDuration);
|
|
expect(mockOnEndFunction.called, 0);
|
|
await tester.pump(additionalDelay);
|
|
expect(mockOnEndFunction.called, 1);
|
|
|
|
await tapTest2and3(tester, widgetFinder, mockOnEndFunction);
|
|
});
|
|
|
|
testWidgets('SliverAnimatedOpacity onEnd callback test', (WidgetTester tester) async {
|
|
await tester.pumpWidget(
|
|
TestAnimatedWidget(
|
|
callback: mockOnEndFunction.handler,
|
|
switchKey: switchKey,
|
|
state: _TestSliverAnimatedOpacityWidgetState(),
|
|
),
|
|
);
|
|
|
|
final Finder widgetFinder = find.byKey(switchKey);
|
|
|
|
await tester.tap(widgetFinder);
|
|
|
|
await tester.pump();
|
|
expect(mockOnEndFunction.called, 0);
|
|
await tester.pump(animationDuration);
|
|
expect(mockOnEndFunction.called, 0);
|
|
await tester.pump(additionalDelay);
|
|
expect(mockOnEndFunction.called, 1);
|
|
|
|
await tapTest2and3(tester, widgetFinder, mockOnEndFunction);
|
|
});
|
|
|
|
testWidgets('SliverAnimatedOpacity transition test', (WidgetTester tester) async {
|
|
await tester.pumpWidget(
|
|
wrap(
|
|
child: TestAnimatedWidget(
|
|
switchKey: switchKey,
|
|
state: _TestSliverAnimatedOpacityWidgetState(),
|
|
),
|
|
),
|
|
);
|
|
|
|
final RebuildCountingState<StatefulWidget> state = tester
|
|
.widget<TestAnimatedWidget>(find.byType(TestAnimatedWidget))
|
|
.rebuildState!;
|
|
final Finder switchFinder = find.byKey(switchKey);
|
|
final SliverFadeTransition opacityWidget = tester.widget<SliverFadeTransition>(
|
|
find
|
|
.ancestor(of: find.byType(Placeholder), matching: find.byType(SliverFadeTransition))
|
|
.first,
|
|
);
|
|
|
|
expect(state.builds, equals(1));
|
|
|
|
await tester.tap(switchFinder);
|
|
expect(state.builds, equals(1));
|
|
await tester.pump();
|
|
expect(opacityWidget.opacity.value, equals(0.0));
|
|
expect(state.builds, equals(2));
|
|
|
|
await tester.pump(const Duration(milliseconds: 500));
|
|
expect(opacityWidget.opacity.value, equals(0.5));
|
|
expect(state.builds, equals(2));
|
|
await tester.pump(const Duration(milliseconds: 250));
|
|
expect(opacityWidget.opacity.value, equals(0.75));
|
|
expect(state.builds, equals(2));
|
|
await tester.pump(const Duration(milliseconds: 250));
|
|
expect(opacityWidget.opacity.value, equals(1.0));
|
|
expect(state.builds, equals(2));
|
|
});
|
|
|
|
testWidgets('AnimatedDefaultTextStyle onEnd callback test', (WidgetTester tester) async {
|
|
await tester.pumpWidget(
|
|
wrap(
|
|
child: TestAnimatedWidget(
|
|
callback: mockOnEndFunction.handler,
|
|
switchKey: switchKey,
|
|
state: _TestAnimatedDefaultTextStyleWidgetState(),
|
|
),
|
|
),
|
|
);
|
|
|
|
final Finder widgetFinder = find.byKey(switchKey);
|
|
|
|
await tester.tap(widgetFinder);
|
|
|
|
await tester.pump();
|
|
expect(mockOnEndFunction.called, 0);
|
|
await tester.pump(animationDuration);
|
|
expect(mockOnEndFunction.called, 0);
|
|
await tester.pump(additionalDelay);
|
|
expect(mockOnEndFunction.called, 1);
|
|
|
|
await tapTest2and3(tester, widgetFinder, mockOnEndFunction);
|
|
});
|
|
|
|
testWidgets('AnimatedPhysicalModel onEnd callback test', (WidgetTester tester) async {
|
|
await tester.pumpWidget(
|
|
wrap(
|
|
child: TestAnimatedWidget(
|
|
callback: mockOnEndFunction.handler,
|
|
switchKey: switchKey,
|
|
state: _TestAnimatedPhysicalModelWidgetState(),
|
|
),
|
|
),
|
|
);
|
|
|
|
final Finder widgetFinder = find.byKey(switchKey);
|
|
|
|
await tester.tap(widgetFinder);
|
|
|
|
await tester.pump();
|
|
expect(mockOnEndFunction.called, 0);
|
|
await tester.pump(animationDuration);
|
|
expect(mockOnEndFunction.called, 0);
|
|
await tester.pump(additionalDelay);
|
|
expect(mockOnEndFunction.called, 1);
|
|
|
|
await tapTest2and3(tester, widgetFinder, mockOnEndFunction);
|
|
});
|
|
|
|
testWidgets('TweenAnimationBuilder onEnd callback test', (WidgetTester tester) async {
|
|
await tester.pumpWidget(
|
|
wrap(
|
|
child: TestAnimatedWidget(
|
|
callback: mockOnEndFunction.handler,
|
|
switchKey: switchKey,
|
|
state: _TestTweenAnimationBuilderWidgetState(),
|
|
),
|
|
),
|
|
);
|
|
|
|
final Finder widgetFinder = find.byKey(switchKey);
|
|
|
|
await tester.tap(widgetFinder);
|
|
|
|
await tester.pump();
|
|
expect(mockOnEndFunction.called, 0);
|
|
await tester.pump(animationDuration);
|
|
expect(mockOnEndFunction.called, 0);
|
|
await tester.pump(additionalDelay);
|
|
expect(mockOnEndFunction.called, 1);
|
|
|
|
await tapTest2and3(tester, widgetFinder, mockOnEndFunction);
|
|
});
|
|
|
|
testWidgets('AnimatedTheme onEnd callback test', (WidgetTester tester) async {
|
|
await tester.pumpWidget(
|
|
wrap(
|
|
child: TestAnimatedWidget(
|
|
callback: mockOnEndFunction.handler,
|
|
switchKey: switchKey,
|
|
state: _TestAnimatedThemeWidgetState(),
|
|
),
|
|
),
|
|
);
|
|
|
|
final Finder widgetFinder = find.byKey(switchKey);
|
|
|
|
await tester.tap(widgetFinder);
|
|
|
|
await tester.pump();
|
|
expect(mockOnEndFunction.called, 0);
|
|
await tester.pump(animationDuration);
|
|
expect(mockOnEndFunction.called, 0);
|
|
await tester.pump(additionalDelay);
|
|
expect(mockOnEndFunction.called, 1);
|
|
|
|
await tapTest2and3(tester, widgetFinder, mockOnEndFunction);
|
|
});
|
|
|
|
testWidgets('Ensure CurvedAnimations are disposed on widget change', (WidgetTester tester) async {
|
|
final key = GlobalKey<ImplicitlyAnimatedWidgetState<AnimatedOpacity>>();
|
|
final curve = ValueNotifier<Curve>(const Interval(0.0, 0.5));
|
|
addTearDown(curve.dispose);
|
|
await tester.pumpWidget(
|
|
wrap(
|
|
child: ValueListenableBuilder<Curve>(
|
|
valueListenable: curve,
|
|
builder: (_, Curve c, _) => AnimatedOpacity(
|
|
key: key,
|
|
opacity: 1.0,
|
|
duration: const Duration(seconds: 1),
|
|
curve: c,
|
|
child: Container(color: Colors.green),
|
|
),
|
|
),
|
|
),
|
|
);
|
|
|
|
final ImplicitlyAnimatedWidgetState<AnimatedOpacity>? firstState = key.currentState;
|
|
final Animation<double>? firstAnimation = firstState?.animation;
|
|
if (firstAnimation == null) {
|
|
fail('animation was null!');
|
|
}
|
|
|
|
final firstCurvedAnimation = firstAnimation as CurvedAnimation;
|
|
|
|
expect(firstCurvedAnimation.isDisposed, isFalse);
|
|
|
|
curve.value = const Interval(0.0, 0.6);
|
|
await tester.pumpAndSettle();
|
|
|
|
final ImplicitlyAnimatedWidgetState<AnimatedOpacity>? secondState = key.currentState;
|
|
final Animation<double>? secondAnimation = secondState?.animation;
|
|
if (secondAnimation == null) {
|
|
fail('animation was null!');
|
|
}
|
|
|
|
final secondCurvedAnimation = secondAnimation as CurvedAnimation;
|
|
|
|
expect(firstState, equals(secondState));
|
|
expect(firstAnimation, isNot(equals(secondAnimation)));
|
|
|
|
expect(firstCurvedAnimation.isDisposed, isTrue);
|
|
expect(secondCurvedAnimation.isDisposed, isFalse);
|
|
|
|
await tester.pumpWidget(wrap(child: const Offstage()));
|
|
await tester.pumpAndSettle();
|
|
|
|
expect(secondCurvedAnimation.isDisposed, isTrue);
|
|
});
|
|
|
|
group('Verify that default args match non-animated variants', () {
|
|
const Widget child = SizedBox.shrink();
|
|
const color = Color(0x00000000);
|
|
|
|
testWidgets('PhysicalModel default args', (WidgetTester tester) async {
|
|
const animatedPhysicalModel = AnimatedPhysicalModel(
|
|
duration: Duration.zero,
|
|
color: color,
|
|
shadowColor: color,
|
|
child: child,
|
|
);
|
|
const physicalModel = PhysicalModel(color: color, shadowColor: color, child: child);
|
|
expect(identical(animatedPhysicalModel.shape, physicalModel.shape), isTrue);
|
|
expect(identical(animatedPhysicalModel.clipBehavior, physicalModel.clipBehavior), isTrue);
|
|
expect(identical(animatedPhysicalModel.borderRadius, physicalModel.borderRadius), isTrue);
|
|
});
|
|
// TODO(nate-thegrate): add every class!
|
|
});
|
|
}
|
|
|
|
Future<void> tapTest2and3(
|
|
WidgetTester tester,
|
|
Finder widgetFinder,
|
|
MockOnEndFunction mockOnEndFunction,
|
|
) async {
|
|
await tester.tap(widgetFinder);
|
|
|
|
await tester.pump();
|
|
await tester.pump(animationDuration + additionalDelay);
|
|
expect(mockOnEndFunction.called, 2);
|
|
|
|
await tester.tap(widgetFinder);
|
|
|
|
await tester.pump();
|
|
await tester.pump(animationDuration + additionalDelay);
|
|
expect(mockOnEndFunction.called, 3);
|
|
}
|
|
|
|
Widget wrap({required Widget child}) {
|
|
return Directionality(
|
|
textDirection: TextDirection.ltr,
|
|
child: Material(child: Center(child: child)),
|
|
);
|
|
}
|
|
|
|
abstract class RebuildCountingState<T extends StatefulWidget> extends State<T> {
|
|
int builds = 0;
|
|
}
|
|
|
|
class TestAnimatedWidget extends StatefulWidget {
|
|
const TestAnimatedWidget({
|
|
super.key,
|
|
this.callback,
|
|
required this.switchKey,
|
|
required this.state,
|
|
});
|
|
final VoidCallback? callback;
|
|
final Key switchKey;
|
|
final State<StatefulWidget> state;
|
|
|
|
RebuildCountingState<StatefulWidget>? get rebuildState =>
|
|
state is RebuildCountingState<StatefulWidget>
|
|
? state as RebuildCountingState<StatefulWidget>
|
|
: null;
|
|
|
|
@override
|
|
State<StatefulWidget> createState() => state; // ignore: no_logic_in_create_state, this test predates the lint
|
|
}
|
|
|
|
abstract class _TestAnimatedWidgetState extends RebuildCountingState<TestAnimatedWidget> {
|
|
bool toggle = false;
|
|
final Widget child = const Placeholder();
|
|
final Duration duration = animationDuration;
|
|
|
|
void onChanged(bool v) {
|
|
setState(() {
|
|
toggle = v;
|
|
});
|
|
}
|
|
|
|
Widget getAnimatedWidget();
|
|
|
|
@override
|
|
Widget build(BuildContext context) {
|
|
builds++;
|
|
final Widget animatedWidget = getAnimatedWidget();
|
|
|
|
return Stack(
|
|
children: <Widget>[
|
|
animatedWidget,
|
|
Switch(key: widget.switchKey, value: toggle, onChanged: onChanged),
|
|
],
|
|
);
|
|
}
|
|
}
|
|
|
|
class _TestAnimatedContainerWidgetState extends _TestAnimatedWidgetState {
|
|
@override
|
|
Widget getAnimatedWidget() {
|
|
return AnimatedContainer(
|
|
duration: duration,
|
|
onEnd: widget.callback,
|
|
width: toggle ? 10 : 20,
|
|
foregroundDecoration: toggle ? const BoxDecoration() : null,
|
|
child: child,
|
|
);
|
|
}
|
|
}
|
|
|
|
class _TestAnimatedPaddingWidgetState extends _TestAnimatedWidgetState {
|
|
@override
|
|
Widget getAnimatedWidget() {
|
|
return AnimatedPadding(
|
|
duration: duration,
|
|
onEnd: widget.callback,
|
|
padding: toggle ? const EdgeInsets.all(8.0) : const EdgeInsets.all(16.0),
|
|
child: child,
|
|
);
|
|
}
|
|
}
|
|
|
|
class _TestAnimatedAlignWidgetState extends _TestAnimatedWidgetState {
|
|
@override
|
|
Widget getAnimatedWidget() {
|
|
return AnimatedAlign(
|
|
duration: duration,
|
|
onEnd: widget.callback,
|
|
alignment: toggle ? Alignment.topLeft : Alignment.bottomRight,
|
|
child: child,
|
|
);
|
|
}
|
|
}
|
|
|
|
class _TestAnimatedPositionedWidgetState extends _TestAnimatedWidgetState {
|
|
@override
|
|
Widget getAnimatedWidget() {
|
|
return AnimatedPositioned(
|
|
duration: duration,
|
|
onEnd: widget.callback,
|
|
left: toggle ? 10 : 20,
|
|
child: child,
|
|
);
|
|
}
|
|
}
|
|
|
|
class _TestAnimatedPositionedDirectionalWidgetState extends _TestAnimatedWidgetState {
|
|
@override
|
|
Widget getAnimatedWidget() {
|
|
return AnimatedPositionedDirectional(
|
|
duration: duration,
|
|
onEnd: widget.callback,
|
|
start: toggle ? 10 : 20,
|
|
child: child,
|
|
);
|
|
}
|
|
}
|
|
|
|
class _TestAnimatedSlideWidgetState extends _TestAnimatedWidgetState {
|
|
@override
|
|
Widget getAnimatedWidget() {
|
|
return AnimatedSlide(
|
|
duration: duration,
|
|
onEnd: widget.callback,
|
|
offset: toggle ? const Offset(1, 1) : Offset.zero,
|
|
child: child,
|
|
);
|
|
}
|
|
}
|
|
|
|
class _TestAnimatedScaleWidgetState extends _TestAnimatedWidgetState {
|
|
@override
|
|
Widget getAnimatedWidget() {
|
|
return AnimatedScale(
|
|
duration: duration,
|
|
onEnd: widget.callback,
|
|
scale: toggle ? 2.0 : 1.0,
|
|
child: child,
|
|
);
|
|
}
|
|
}
|
|
|
|
class _TestAnimatedRotationWidgetState extends _TestAnimatedWidgetState {
|
|
@override
|
|
Widget getAnimatedWidget() {
|
|
return AnimatedRotation(
|
|
duration: duration,
|
|
onEnd: widget.callback,
|
|
turns: toggle ? 1.5 : 0.0,
|
|
child: child,
|
|
);
|
|
}
|
|
}
|
|
|
|
class _TestAnimatedOpacityWidgetState extends _TestAnimatedWidgetState {
|
|
@override
|
|
Widget getAnimatedWidget() {
|
|
return AnimatedOpacity(
|
|
duration: duration,
|
|
onEnd: widget.callback,
|
|
opacity: toggle ? 1.0 : 0.0,
|
|
child: child,
|
|
);
|
|
}
|
|
}
|
|
|
|
class _TestAnimatedFractionallySizedBoxWidgetState extends _TestAnimatedWidgetState {
|
|
@override
|
|
Widget getAnimatedWidget() {
|
|
return AnimatedFractionallySizedBox(
|
|
duration: duration,
|
|
onEnd: widget.callback,
|
|
heightFactor: toggle ? 0.25 : 0.75,
|
|
widthFactor: toggle ? 0.25 : 0.75,
|
|
child: child,
|
|
);
|
|
}
|
|
}
|
|
|
|
class _TestSliverAnimatedOpacityWidgetState extends _TestAnimatedWidgetState {
|
|
@override
|
|
Widget getAnimatedWidget() {
|
|
return SliverAnimatedOpacity(
|
|
sliver: SliverToBoxAdapter(child: child),
|
|
duration: duration,
|
|
onEnd: widget.callback,
|
|
opacity: toggle ? 1.0 : 0.0,
|
|
);
|
|
}
|
|
|
|
@override
|
|
Widget build(BuildContext context) {
|
|
builds++;
|
|
final Widget animatedWidget = getAnimatedWidget();
|
|
|
|
return Material(
|
|
child: Directionality(
|
|
textDirection: TextDirection.ltr,
|
|
child: CustomScrollView(
|
|
slivers: <Widget>[
|
|
animatedWidget,
|
|
SliverToBoxAdapter(
|
|
child: Switch(key: widget.switchKey, value: toggle, onChanged: onChanged),
|
|
),
|
|
],
|
|
),
|
|
),
|
|
);
|
|
}
|
|
}
|
|
|
|
class _TestAnimatedDefaultTextStyleWidgetState extends _TestAnimatedWidgetState {
|
|
@override
|
|
Widget getAnimatedWidget() {
|
|
return AnimatedDefaultTextStyle(
|
|
duration: duration,
|
|
onEnd: widget.callback,
|
|
style: toggle
|
|
? const TextStyle(fontStyle: FontStyle.italic)
|
|
: const TextStyle(fontStyle: FontStyle.normal),
|
|
child: child,
|
|
);
|
|
}
|
|
}
|
|
|
|
class _TestAnimatedPhysicalModelWidgetState extends _TestAnimatedWidgetState {
|
|
@override
|
|
Widget getAnimatedWidget() {
|
|
return AnimatedPhysicalModel(
|
|
duration: duration,
|
|
onEnd: widget.callback,
|
|
color: toggle ? Colors.red : Colors.green,
|
|
shadowColor: Colors.blue,
|
|
child: child,
|
|
);
|
|
}
|
|
}
|
|
|
|
class _TestTweenAnimationBuilderWidgetState extends _TestAnimatedWidgetState {
|
|
@override
|
|
Widget getAnimatedWidget() {
|
|
return TweenAnimationBuilder<double>(
|
|
tween: toggle ? Tween<double>(begin: 1, end: 2) : Tween<double>(begin: 2, end: 1),
|
|
duration: duration,
|
|
onEnd: widget.callback,
|
|
child: child,
|
|
builder: (BuildContext context, double? size, Widget? child) {
|
|
return SizedBox(width: size, height: size, child: child);
|
|
},
|
|
);
|
|
}
|
|
}
|
|
|
|
class _TestAnimatedThemeWidgetState extends _TestAnimatedWidgetState {
|
|
@override
|
|
Widget getAnimatedWidget() {
|
|
return AnimatedTheme(
|
|
data: toggle ? ThemeData.dark() : ThemeData(),
|
|
duration: duration,
|
|
onEnd: widget.callback,
|
|
child: child,
|
|
);
|
|
}
|
|
}
|