Unverified Commit d916806a authored by matthew-carroll's avatar matthew-carroll Committed by GitHub

Use RenderAnimatedOpacity within AnimatedOpacity widget (#15466) (#18121)

* Use RenderAnimatedOpacity within AnimatedOpacity widget (#15466)

* Fixed minor bug in RenderAnimatedOpacity

* Updated protected API for ImplicitlyAnimatedWidget
parent 2828a459
...@@ -837,7 +837,7 @@ class RenderAnimatedOpacity extends RenderProxyBox { ...@@ -837,7 +837,7 @@ class RenderAnimatedOpacity extends RenderProxyBox {
_alpha = _getAlphaFromOpacity(_opacity.value.clamp(0.0, 1.0)); _alpha = _getAlphaFromOpacity(_opacity.value.clamp(0.0, 1.0));
if (oldAlpha != _alpha) { if (oldAlpha != _alpha) {
final bool didNeedCompositing = _currentlyNeedsCompositing; final bool didNeedCompositing = _currentlyNeedsCompositing;
_currentlyNeedsCompositing = _alpha > 0 || _alpha < 255; _currentlyNeedsCompositing = _alpha > 0 && _alpha < 255;
if (child != null && didNeedCompositing != _currentlyNeedsCompositing) if (child != null && didNeedCompositing != _currentlyNeedsCompositing)
markNeedsCompositingBitsUpdate(); markNeedsCompositingBitsUpdate();
markNeedsPaint(); markNeedsPaint();
......
...@@ -4,6 +4,7 @@ ...@@ -4,6 +4,7 @@
import 'package:flutter/animation.dart'; import 'package:flutter/animation.dart';
import 'package:flutter/foundation.dart'; import 'package:flutter/foundation.dart';
import 'package:flutter/rendering.dart';
import 'package:vector_math/vector_math_64.dart'; import 'package:vector_math/vector_math_64.dart';
import 'basic.dart'; import 'basic.dart';
...@@ -12,6 +13,7 @@ import 'debug.dart'; ...@@ -12,6 +13,7 @@ import 'debug.dart';
import 'framework.dart'; import 'framework.dart';
import 'text.dart'; import 'text.dart';
import 'ticker_provider.dart'; import 'ticker_provider.dart';
import 'transitions.dart';
/// An interpolation between two [BoxConstraints]. /// An interpolation between two [BoxConstraints].
/// ///
...@@ -257,6 +259,7 @@ abstract class ImplicitlyAnimatedWidgetState<T extends ImplicitlyAnimatedWidget> ...@@ -257,6 +259,7 @@ abstract class ImplicitlyAnimatedWidgetState<T extends ImplicitlyAnimatedWidget>
); );
_updateCurve(); _updateCurve();
_constructTweens(); _constructTweens();
didUpdateTweens();
} }
@override @override
...@@ -273,6 +276,7 @@ abstract class ImplicitlyAnimatedWidgetState<T extends ImplicitlyAnimatedWidget> ...@@ -273,6 +276,7 @@ abstract class ImplicitlyAnimatedWidgetState<T extends ImplicitlyAnimatedWidget>
_controller _controller
..value = 0.0 ..value = 0.0
..forward(); ..forward();
didUpdateTweens();
} }
} }
...@@ -329,9 +333,23 @@ abstract class ImplicitlyAnimatedWidgetState<T extends ImplicitlyAnimatedWidget> ...@@ -329,9 +333,23 @@ abstract class ImplicitlyAnimatedWidgetState<T extends ImplicitlyAnimatedWidget>
/// as the begin value. /// as the begin value.
/// ///
/// 2. Take the value returned from the callback, and store it. This is the /// 2. Take the value returned from the callback, and store it. This is the
/// value to use as the current value the next time that the forEachTween() /// value to use as the current value the next time that the [forEachTween]
/// method is called. /// method is called.
///
/// Subclasses that contain properties based on tweens created by
/// [forEachTween] should override [didUpdateTweens] to update those
/// properties. Dependent properties should not be updated within
/// [forEachTween].
@protected
void forEachTween(TweenVisitor<dynamic> visitor); void forEachTween(TweenVisitor<dynamic> visitor);
/// Optional hook for subclasses that runs after all tweens have been updated
/// via [forEachTween].
///
/// Any properties that depend upon tweens created by [forEachTween] should be
/// updated within [didUpdateTweens], not within [forEachTween].
@protected
void didUpdateTweens() {}
} }
/// A base class for widgets with implicit animations that need to rebuild their /// A base class for widgets with implicit animations that need to rebuild their
...@@ -1003,18 +1021,24 @@ class AnimatedOpacity extends ImplicitlyAnimatedWidget { ...@@ -1003,18 +1021,24 @@ class AnimatedOpacity extends ImplicitlyAnimatedWidget {
} }
} }
class _AnimatedOpacityState extends AnimatedWidgetBaseState<AnimatedOpacity> { class _AnimatedOpacityState extends ImplicitlyAnimatedWidgetState<AnimatedOpacity> {
Tween<double> _opacity; Tween<double> _opacity;
Animation<double> _opacityAnimation;
@override @override
void forEachTween(TweenVisitor<dynamic> visitor) { void forEachTween(TweenVisitor<dynamic> visitor) {
_opacity = visitor(_opacity, widget.opacity, (dynamic value) => new Tween<double>(begin: value)); _opacity = visitor(_opacity, widget.opacity, (dynamic value) => new Tween<double>(begin: value));
} }
@override
void didUpdateTweens() {
_opacityAnimation = _opacity.animate(controller);
}
@override @override
Widget build(BuildContext context) { Widget build(BuildContext context) {
return new Opacity( return new FadeTransition(
opacity: _opacity.evaluate(animation), opacity: _opacityAnimation,
child: widget.child child: widget.child
); );
} }
......
...@@ -221,8 +221,8 @@ void main() { ...@@ -221,8 +221,8 @@ void main() {
}); });
Iterable<double> opacities = titles.map((Element element) { Iterable<double> opacities = titles.map((Element element) {
final RenderOpacity renderOpacity = element.ancestorRenderObjectOfType(const TypeMatcher<RenderOpacity>()); final RenderAnimatedOpacity renderOpacity = element.ancestorRenderObjectOfType(const TypeMatcher<RenderAnimatedOpacity>());
return renderOpacity.opacity; return renderOpacity.opacity.value;
}); });
expect(opacities, <double> [ expect(opacities, <double> [
...@@ -246,8 +246,8 @@ void main() { ...@@ -246,8 +246,8 @@ void main() {
}); });
opacities = titles.map((Element element) { opacities = titles.map((Element element) {
final RenderOpacity renderOpacity = element.ancestorRenderObjectOfType(const TypeMatcher<RenderOpacity>()); final RenderAnimatedOpacity renderOpacity = element.ancestorRenderObjectOfType(const TypeMatcher<RenderAnimatedOpacity>());
return renderOpacity.opacity; return renderOpacity.opacity.value;
}); });
expect(opacities, <double> [ expect(opacities, <double> [
...@@ -302,11 +302,11 @@ void main() { ...@@ -302,11 +302,11 @@ void main() {
expect(find.text('Title'), findsOneWidget); expect(find.text('Title'), findsOneWidget);
expect(find.text('Different title'), findsOneWidget); expect(find.text('Different title'), findsOneWidget);
RenderOpacity largeTitleOpacity = RenderAnimatedOpacity largeTitleOpacity =
tester.element(find.text('Title')).ancestorRenderObjectOfType(const TypeMatcher<RenderOpacity>()); tester.element(find.text('Title')).ancestorRenderObjectOfType(const TypeMatcher<RenderAnimatedOpacity>());
// Large title initially visible. // Large title initially visible.
expect( expect(
largeTitleOpacity.opacity, largeTitleOpacity.opacity.value,
1.0 1.0
); );
// Middle widget not even wrapped with RenderOpacity, i.e. is always visible. // Middle widget not even wrapped with RenderOpacity, i.e. is always visible.
...@@ -322,10 +322,10 @@ void main() { ...@@ -322,10 +322,10 @@ void main() {
await tester.pump(const Duration(milliseconds: 300)); await tester.pump(const Duration(milliseconds: 300));
largeTitleOpacity = largeTitleOpacity =
tester.element(find.text('Title')).ancestorRenderObjectOfType(const TypeMatcher<RenderOpacity>()); tester.element(find.text('Title')).ancestorRenderObjectOfType(const TypeMatcher<RenderAnimatedOpacity>());
// Large title no longer visible. // Large title no longer visible.
expect( expect(
largeTitleOpacity.opacity, largeTitleOpacity.opacity.value,
0.0 0.0
); );
......
...@@ -78,13 +78,13 @@ double getBorderWeight(WidgetTester tester) => getBorderSide(tester)?.width; ...@@ -78,13 +78,13 @@ double getBorderWeight(WidgetTester tester) => getBorderSide(tester)?.width;
Color getBorderColor(WidgetTester tester) => getBorderSide(tester)?.color; Color getBorderColor(WidgetTester tester) => getBorderSide(tester)?.color;
double getHintOpacity(WidgetTester tester) { double getHintOpacity(WidgetTester tester) {
final Opacity opacityWidget = tester.widget<Opacity>( final FadeTransition opacityWidget = tester.widget<FadeTransition>(
find.ancestor( find.ancestor(
of: find.text('hint'), of: find.text('hint'),
matching: find.byType(Opacity), matching: find.byType(FadeTransition),
).last ).last
); );
return opacityWidget.opacity; return opacityWidget.opacity.value;
} }
void main() { void main() {
......
...@@ -105,12 +105,12 @@ Future<Null> skipPastScrollingAnimation(WidgetTester tester) async { ...@@ -105,12 +105,12 @@ Future<Null> skipPastScrollingAnimation(WidgetTester tester) async {
} }
double getOpacity(WidgetTester tester, Finder finder) { double getOpacity(WidgetTester tester, Finder finder) {
return tester.widget<Opacity>( return tester.widget<FadeTransition>(
find.ancestor( find.ancestor(
of: finder, of: finder,
matching: find.byType(Opacity), matching: find.byType(FadeTransition),
) )
).opacity; ).opacity.value;
} }
void main() { void main() {
......
...@@ -3,11 +3,13 @@ ...@@ -3,11 +3,13 @@
// found in the LICENSE file. // found in the LICENSE file.
import 'dart:typed_data'; import 'dart:typed_data';
import 'dart:ui' as ui show Image; import 'dart:ui' as ui show Image;
import 'package:flutter/animation.dart';
import 'package:flutter/foundation.dart'; import 'package:flutter/foundation.dart';
import 'package:flutter/gestures.dart'; import 'package:flutter/gestures.dart';
import 'package:flutter/rendering.dart'; import 'package:flutter/rendering.dart';
import 'package:flutter/src/scheduler/ticker.dart';
import 'package:test/test.dart'; import 'package:test/test.dart';
import 'rendering_tester.dart'; import 'rendering_tester.dart';
...@@ -190,4 +192,99 @@ void main() { ...@@ -190,4 +192,99 @@ void main() {
expect(data.getUint32(0), equals(0x00000080)); expect(data.getUint32(0), equals(0x00000080));
expect(data.getUint32(stride - 4), equals(0xffffffff)); expect(data.getUint32(stride - 4), equals(0xffffffff));
}); });
test('RenderOpacity does not composite if it is transparent', () {
final RenderOpacity renderOpacity = new RenderOpacity(
opacity: 0.0,
child: new RenderSizedBox(const Size(1.0, 1.0)), // size doesn't matter
);
layout(renderOpacity, phase: EnginePhase.composite);
expect(renderOpacity.needsCompositing, false);
});
test('RenderOpacity does not composite if it is opaque', () {
final RenderOpacity renderOpacity = new RenderOpacity(
opacity: 1.0,
child: new RenderSizedBox(const Size(1.0, 1.0)), // size doesn't matter
);
layout(renderOpacity, phase: EnginePhase.composite);
expect(renderOpacity.needsCompositing, false);
});
test('RenderAnimatedOpacity does not composite if it is transparent', () async {
final Animation<double> opacityAnimation = new AnimationController(
vsync: new _FakeTickerProvider(),
)..value = 0.0;
final RenderAnimatedOpacity renderAnimatedOpacity = new RenderAnimatedOpacity(
opacity: opacityAnimation,
child: new RenderSizedBox(const Size(1.0, 1.0)), // size doesn't matter
);
layout(renderAnimatedOpacity, phase: EnginePhase.composite);
expect(renderAnimatedOpacity.needsCompositing, false);
});
test('RenderAnimatedOpacity does not composite if it is opaque', () {
final Animation<double> opacityAnimation = new AnimationController(
vsync: new _FakeTickerProvider(),
)..value = 1.0;
final RenderAnimatedOpacity renderAnimatedOpacity = new RenderAnimatedOpacity(
opacity: opacityAnimation,
child: new RenderSizedBox(const Size(1.0, 1.0)), // size doesn't matter
);
layout(renderAnimatedOpacity, phase: EnginePhase.composite);
expect(renderAnimatedOpacity.needsCompositing, false);
});
}
class _FakeTickerProvider implements TickerProvider {
@override
Ticker createTicker(TickerCallback onTick) {
return new _FakeTicker();
}
} }
class _FakeTicker implements Ticker {
@override
bool muted;
@override
void absorbTicker(Ticker originalTicker) {}
@override
String get debugLabel => null;
@override
bool get isActive => null;
@override
bool get isTicking => null;
@override
bool get scheduled => null;
@override
bool get shouldScheduleTick => null;
@override
void dispose() {}
@override
void scheduleTick({bool rescheduling = false}) {}
@override
TickerFuture start() {
return null;
}
@override
void stop({bool canceled = false}) {}
@override
void unscheduleTick() {}
}
\ No newline at end of file
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment