From 1d2146b66980c8e063516b33d6084eca665990f0 Mon Sep 17 00:00:00 2001 From: Luigi Rosso Date: Tue, 9 Apr 2019 09:52:09 -0700 Subject: [PATCH] Making sure actor widget is disposed of properly. --- CHANGELOG.md | 4 ++ example/hop/test/hop_test.dart | 29 ++++++++++++++ lib/nima_actor.dart | 72 ++++++++++++++++++++++++---------- pubspec.yaml | 2 +- 4 files changed, 86 insertions(+), 21 deletions(-) create mode 100644 example/hop/test/hop_test.dart diff --git a/CHANGELOG.md b/CHANGELOG.md index 5658f6c..fef2a39 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,3 +1,7 @@ +## [1.0.3] - 2019-04-09 09:50:41 + +* Making sure the Nima Actor widget is disposed of properly when the leaf render widet is unmounted or the render box is detached. + ## [1.0.0] - 5/5/2018 * Initial release with an example NimaActor widget that implements a LeafRenderObjectWidget that can render a Nima actor. Alignment is done based on the setup axis aligned bounding box. diff --git a/example/hop/test/hop_test.dart b/example/hop/test/hop_test.dart new file mode 100644 index 0000000..fbf06a8 --- /dev/null +++ b/example/hop/test/hop_test.dart @@ -0,0 +1,29 @@ +// This is a basic Flutter widget test. +// +// To perform an interaction with a widget in your test, use the WidgetTester +// utility that Flutter provides. For example, you can send tap and scroll +// gestures. You can also use WidgetTester to find child widgets in the widget +// tree, read text, and verify that the values of widget properties are correct. + +import 'package:example/main.dart'; +//import 'package:flutter/material.dart'; +import 'package:flutter_test/flutter_test.dart'; + + +void main() { + testWidgets('Start the game', (WidgetTester tester) async { + // Build our app and trigger a frame. + await tester.pumpWidget(MyApp()); + + // Find the start text + expect(find.text('Attack'), findsOneWidget); + + // // Start the game. + // expect(find.byType(FlatButton), findsNWidgets(2)); + // await tester.tap(find.text('Start')); + // await tester.pumpAndSettle(); + + // // We made it to the game screen. + // expect(find.text('Tasks'), findsOneWidget); + }); +} diff --git a/lib/nima_actor.dart b/lib/nima_actor.dart index 5e5b6d3..f3557d2 100644 --- a/lib/nima_actor.dart +++ b/lib/nima_actor.dart @@ -1,6 +1,7 @@ import 'dart:typed_data'; import 'package:flutter/material.dart'; +import 'package:flutter/rendering.dart'; import 'package:nima/nima.dart'; import 'package:nima/nima/math/aabb.dart'; import 'package:nima/nima/animation/actor_animation.dart'; @@ -66,6 +67,11 @@ class NimaActor extends LeafRenderObjectWidget { ..isPlaying = !paused && animation != null ..clip = clip; } + + @override + void didUnmountRenderObject(covariant NimaActorRenderObject renderObject) { + renderObject.dispose(); + } } class NimaAnimationLayer { @@ -81,10 +87,10 @@ class NimaActorRenderObject extends RenderBox { Alignment _alignment; String _animationName; double _mixSeconds = 0.2; + int _frameCallbackID; double _lastFrameTime = 0.0; NimaAnimationCompleted _completedCallback; NimaController _controller; - bool _isFrameScheduled = false; List _animationLayers = List(); bool _isPlaying; @@ -92,6 +98,37 @@ class NimaActorRenderObject extends RenderBox { FlutterActor _actor; AABB _setupAABB; + void dispose() { + _isPlaying = false; + _updatePlayState(); + _controller = null; + } + + @override + void detach() { + super.detach(); + dispose(); + } + + @override + void attach(PipelineOwner owner) { + super.attach(owner); + _updatePlayState(); + } + + void _updatePlayState() { + if (_isPlaying && attached) { + _frameCallbackID ??= + SchedulerBinding.instance.scheduleFrameCallback(_beginFrame); + } else { + if (_frameCallbackID != null) { + SchedulerBinding.instance.cancelFrameCallbackWithId(_frameCallbackID); + _frameCallbackID = null; + } + _lastFrameTime = 0.0; + } + } + NimaAnimationCompleted get completed => _completedCallback; set completed(NimaAnimationCompleted value) { if (_completedCallback != value) { @@ -99,13 +136,6 @@ class NimaActorRenderObject extends RenderBox { } } - void scheduleFrame() { - if (!_isFrameScheduled) { - _isFrameScheduled = true; - SchedulerBinding.instance.scheduleFrameCallback(beginFrame); - } - } - BoxFit get fit => _fit; set fit(BoxFit value) { if (value == _fit) { @@ -121,9 +151,7 @@ class NimaActorRenderObject extends RenderBox { return; } _isPlaying = value; - if (_isPlaying) { - scheduleFrame(); - } + _updatePlayState(); } bool _clip = true; @@ -233,19 +261,27 @@ class NimaActorRenderObject extends RenderBox { super.performLayout(); } - void beginFrame(Duration timeStamp) { + void _beginFrame(Duration timeStamp) { + _frameCallbackID = null; final double t = timeStamp.inMicroseconds / Duration.microsecondsPerMillisecond / 1000.0; - _isFrameScheduled = false; - if (_lastFrameTime == 0 || _actor == null) { + if (_lastFrameTime == 0) { _lastFrameTime = t; - scheduleFrame(); + _updatePlayState(); return; } double elapsedSeconds = t - _lastFrameTime; _lastFrameTime = t; + if (_advance(elapsedSeconds)) { + _updatePlayState(); + } + + markNeedsPaint(); + } + + bool _advance(double elapsedSeconds) { int lastFullyMixed = -1; double lastMix = 0.0; @@ -295,13 +331,9 @@ class NimaActorRenderObject extends RenderBox { _controller.advance(_actor, elapsedSeconds); } - if (_isPlaying) { - scheduleFrame(); - } - _actor.advance(elapsedSeconds); - markNeedsPaint(); + return _isPlaying; } @override diff --git a/pubspec.yaml b/pubspec.yaml index 95c8ae1..700ee29 100644 --- a/pubspec.yaml +++ b/pubspec.yaml @@ -1,6 +1,6 @@ name: nima description: Nima runtime for flutter. -version: 1.0.2 +version: 1.0.3 author: "2Dimensions Team " homepage: https://github.com/2d-inc/Nima-Flutter environment: