mirror of
https://github.com/rive-app/rive-flutter.git
synced 2025-07-03 14:18:37 +08:00
feat: add isTouchScrollEnabled
Resolves https://github.com/rive-app/rive-flutter/issues/436, and - https://rive.app/community/forums/support/fsnLLkXUaA62/flutter-scrolling-not-working-as-expected-on-mobile/ft3UcorwBVAf Our web runtime has a `isTouchScrollEnabled`. This reproduces that behaviour. ``` /// For Rive Listeners, allows scrolling behavior to still occur on Rive /// widgets when a touch/drag action is performed on touch-enabled devices. /// Otherwise, scroll behavior may be prevented on touch/drag actions on the /// widget by default. /// /// Default `false`. ``` I made some TODOs in the code for future considerations. We might want to opt in to give users more control over which gestures should be registered, and we can potentially be smart about doing this conditionally, depending on what the Rive graphic allows you to do. But this requires more investigation. https://github.com/user-attachments/assets/81119bed-cb8a-4672-9559-d1c85832bad9 Diffs= 8d1fdd16ad feat: add isTouchScrollEnabled (#8651) Co-authored-by: Gordon <pggordonhayes@gmail.com>
This commit is contained in:
@ -1 +1 @@
|
||||
7986d64d8371531716ea3f038dcbec5da187e6cd
|
||||
8d1fdd16ad196c3acca18faa89db2e1f4ef94365
|
||||
|
@ -1,3 +1,7 @@
|
||||
## Upcoming
|
||||
|
||||
- Adds the `isTouchScrollEnabled` property to `RiveAnimation` and `Rive` widgets. When `true` allows scrolling behavior to occur on Rive widgets when a touch/drag action is performed on touch-enabled devices. Defauls to `false`, which means Rive will "absorb" the pointer down event and a scroll cannot be triggered if the touch occured within a Rive Listener area. Setting to `true` will impact Rive's capability to handle multiple gestures simultaneously.
|
||||
|
||||
## 0.13.18
|
||||
|
||||
- Bump to latest `rive_common`, v0.4.13. Resolves [issues building rive_common downstream](https://github.com/rive-app/rive-flutter/issues/354#issuecomment-2491004291).
|
||||
|
@ -4,6 +4,8 @@ import 'package:flutter/services.dart';
|
||||
import 'package:flutter/widgets.dart';
|
||||
import 'package:rive/src/controllers/state_machine_controller.dart';
|
||||
import 'package:rive/src/rive_core/artboard.dart';
|
||||
import 'package:rive/src/rive_core/state_machine_controller.dart'
|
||||
show HitResult;
|
||||
import 'package:rive/src/rive_render_box.dart';
|
||||
import 'package:rive/src/runtime_artboard.dart';
|
||||
import 'package:rive_common/math.dart';
|
||||
@ -99,6 +101,14 @@ class Rive extends LeafRenderObjectWidget {
|
||||
/// Default `1.0`.
|
||||
final double speedMultiplier;
|
||||
|
||||
/// For Rive Listeners, allows scrolling behavior to still occur on Rive
|
||||
/// widgets when a touch/drag action is performed on touch-enabled devices.
|
||||
/// Otherwise, scroll behavior may be prevented on touch/drag actions on the
|
||||
/// widget by default.
|
||||
///
|
||||
/// Default `false`.
|
||||
final bool isTouchScrollEnabled;
|
||||
|
||||
const Rive({
|
||||
required this.artboard,
|
||||
super.key,
|
||||
@ -111,6 +121,7 @@ class Rive extends LeafRenderObjectWidget {
|
||||
this.alignment = Alignment.center,
|
||||
this.clipRect,
|
||||
this.speedMultiplier = 1.0,
|
||||
this.isTouchScrollEnabled = false,
|
||||
});
|
||||
|
||||
@override
|
||||
@ -128,7 +139,8 @@ class Rive extends LeafRenderObjectWidget {
|
||||
..enableHitTests = enablePointerEvents
|
||||
..cursor = cursor
|
||||
..behavior = behavior
|
||||
..speedMultiplier = speedMultiplier;
|
||||
..speedMultiplier = speedMultiplier
|
||||
..isTouchScrollEnabled = isTouchScrollEnabled;
|
||||
}
|
||||
|
||||
@override
|
||||
@ -147,7 +159,8 @@ class Rive extends LeafRenderObjectWidget {
|
||||
..enableHitTests = enablePointerEvents
|
||||
..cursor = cursor
|
||||
..behavior = behavior
|
||||
..speedMultiplier = speedMultiplier;
|
||||
..speedMultiplier = speedMultiplier
|
||||
..isTouchScrollEnabled = isTouchScrollEnabled;
|
||||
}
|
||||
}
|
||||
|
||||
@ -245,6 +258,20 @@ class RiveRenderObject extends RiveRenderBox implements MouseTrackerAnnotation {
|
||||
return false;
|
||||
}
|
||||
|
||||
// TODO: A possible alternative to [isTouchScrollEnabled] is to allow
|
||||
// users to set custom recognizers. Or for us to provide
|
||||
// heuristics on whether a Rive graphic has certain gestures:
|
||||
// - Pointer down/up
|
||||
// - Drag
|
||||
// - Scroll
|
||||
// - etc.
|
||||
// With this information we can better decide which recognizers to use,
|
||||
// while optionally allowing end users to override it, or provide custom ones.
|
||||
// Can be considered for `rive_native`.
|
||||
//
|
||||
// https://api.flutter.dev/flutter/gestures/GestureArenaManager-class.html
|
||||
final _recognizer = ImmediateMultiDragGestureRecognizer();
|
||||
|
||||
@override
|
||||
void handleEvent(PointerEvent event, HitTestEntry entry) {
|
||||
assert(debugHandleEvent(event, entry));
|
||||
@ -252,11 +279,13 @@ class RiveRenderObject extends RiveRenderBox implements MouseTrackerAnnotation {
|
||||
return;
|
||||
}
|
||||
if (event is PointerDownEvent) {
|
||||
_hitHelper(
|
||||
event,
|
||||
(controller, artboardPosition) =>
|
||||
controller.pointerDown(artboardPosition, event),
|
||||
);
|
||||
_hitHelper(event, (controller, artboardPosition) {
|
||||
final hitResult = controller.pointerDown(artboardPosition, event);
|
||||
|
||||
if (hitResult != HitResult.none && !isTouchScrollEnabled) {
|
||||
_recognizer.addPointer(event);
|
||||
}
|
||||
});
|
||||
}
|
||||
if (event is PointerUpEvent) {
|
||||
_hitHelper(
|
||||
@ -338,6 +367,7 @@ class RiveRenderObject extends RiveRenderBox implements MouseTrackerAnnotation {
|
||||
@override
|
||||
void dispose() {
|
||||
_artboard.redraw.removeListener(scheduleRepaint);
|
||||
_recognizer.dispose();
|
||||
super.dispose();
|
||||
}
|
||||
|
||||
|
@ -466,8 +466,6 @@ class StateMachineController extends RiveAnimationController<CoreContext>
|
||||
|
||||
late CoreContext core;
|
||||
|
||||
final _recognizer = ImmediateMultiDragGestureRecognizer();
|
||||
|
||||
@override
|
||||
bool init(CoreContext core) {
|
||||
this.core = core;
|
||||
@ -734,9 +732,6 @@ class StateMachineController extends RiveAnimationController<CoreContext>
|
||||
hitEvent: ListenerType.down,
|
||||
pointerEvent: event,
|
||||
);
|
||||
if (hitResult != HitResult.none) {
|
||||
_recognizer.addPointer(event);
|
||||
}
|
||||
return hitResult;
|
||||
}
|
||||
|
||||
|
@ -13,6 +13,7 @@ abstract class RiveRenderBox extends RenderBox {
|
||||
Rect? _clipRect;
|
||||
bool _tickerModeEnabled = true;
|
||||
bool _enableHitTests = false;
|
||||
bool _isTouchScrollEnabled = false;
|
||||
|
||||
bool get useArtboardSize => _useArtboardSize;
|
||||
|
||||
@ -89,6 +90,14 @@ abstract class RiveRenderBox extends RenderBox {
|
||||
}
|
||||
}
|
||||
|
||||
bool get isTouchScrollEnabled => _isTouchScrollEnabled;
|
||||
|
||||
set isTouchScrollEnabled(bool value) {
|
||||
if (value != _isTouchScrollEnabled) {
|
||||
_isTouchScrollEnabled = value;
|
||||
}
|
||||
}
|
||||
|
||||
bool _paintedLastFrame = false;
|
||||
|
||||
@override
|
||||
|
@ -76,6 +76,14 @@ class RiveAnimation extends StatefulWidget {
|
||||
/// Default `1.0`.
|
||||
final double speedMultiplier;
|
||||
|
||||
/// For Rive Listeners, allows scrolling behavior to still occur on Rive
|
||||
/// widgets when a touch/drag action is performed on touch-enabled devices.
|
||||
/// Otherwise, scroll behavior may be prevented on touch/drag actions on the
|
||||
/// widget by default.
|
||||
///
|
||||
/// Default `false`.
|
||||
final bool isTouchScrollEnabled;
|
||||
|
||||
/// Creates a new [RiveAnimation] from an asset bundle.
|
||||
///
|
||||
/// *Example:*
|
||||
@ -98,6 +106,7 @@ class RiveAnimation extends StatefulWidget {
|
||||
this.behavior = RiveHitTestBehavior.opaque,
|
||||
this.objectGenerator,
|
||||
this.speedMultiplier = 1,
|
||||
this.isTouchScrollEnabled = false,
|
||||
Key? key,
|
||||
}) : name = asset,
|
||||
file = null,
|
||||
@ -128,6 +137,7 @@ class RiveAnimation extends StatefulWidget {
|
||||
this.behavior = RiveHitTestBehavior.opaque,
|
||||
this.objectGenerator,
|
||||
this.speedMultiplier = 1,
|
||||
this.isTouchScrollEnabled = false,
|
||||
Key? key,
|
||||
}) : name = url,
|
||||
file = null,
|
||||
@ -156,6 +166,7 @@ class RiveAnimation extends StatefulWidget {
|
||||
this.behavior = RiveHitTestBehavior.opaque,
|
||||
this.objectGenerator,
|
||||
this.speedMultiplier = 1,
|
||||
this.isTouchScrollEnabled = false,
|
||||
Key? key,
|
||||
}) : name = path,
|
||||
file = null,
|
||||
@ -185,6 +196,7 @@ class RiveAnimation extends StatefulWidget {
|
||||
this.controllers = const [],
|
||||
this.onInit,
|
||||
this.speedMultiplier = 1,
|
||||
this.isTouchScrollEnabled = false,
|
||||
Key? key,
|
||||
this.behavior = RiveHitTestBehavior.opaque,
|
||||
}) : name = null,
|
||||
@ -356,6 +368,7 @@ class RiveAnimationState extends State<RiveAnimation> {
|
||||
enablePointerEvents: _shouldAddHitTesting,
|
||||
behavior: widget.behavior,
|
||||
speedMultiplier: widget.speedMultiplier,
|
||||
isTouchScrollEnabled: widget.isTouchScrollEnabled,
|
||||
)
|
||||
: widget.placeHolder ?? const SizedBox();
|
||||
}
|
||||
|
Reference in New Issue
Block a user