My app has a draggable floating action button that could move everywhere on the screen and stay at that position when being dropped. The code for the button is from a blogspot and not mine. The problem is, since everything is set in portrait mode, and there is no detection when rotation occurs, the button could be unreachable when device rotates from portrait mode to landscape mode (if the button was previously on the bottom half of the screen in portrait mode).
Is there anyway to detect previous orientation in order to set the offset for the button again when it rotates?
Here is the code for the floating button class/widget
import 'package:flutter/material.dart';
class DraggableFloatingActionButton extends StatefulWidget {
final Widget child;
final double deviceWidth;
final double deviceHeight;
final Function onPressed;
final GlobalKey parentKey;
DraggableFloatingActionButton({
required this.child,
required this.deviceWidth,
required this.deviceHeight,
required this.onPressed,
required this.parentKey,
});
@override
State<StatefulWidget> createState() => _DraggableFloatingActionButtonState();
}
class _DraggableFloatingActionButtonState extends State<DraggableFloatingActionButton> {
final GlobalKey _key = GlobalKey();
bool _isDragging = false;
late Offset _offset;
late Offset _minOffset;
late Offset _maxOffset;
@override
void initState() {
_offset = Offset(widget.deviceWidth * 0.8, widget.deviceHeight * 0.75);
WidgetsBinding.instance.addPostFrameCallback(_setBoundary);
super.initState();
}
void _setBoundary(_) {
final RenderBox parentRenderBox =
widget.parentKey.currentContext?.findRenderObject() as RenderBox;
final RenderBox renderBox = _key.currentContext?.findRenderObject() as RenderBox;
try {
final Size parentSize = parentRenderBox.size;
final Size size = renderBox.size;
setState(() {
_minOffset = const Offset(0, 0);
_maxOffset = Offset(parentSize.width - size.width, parentSize.height - size.height);
});
} catch (e) {
print('catch: $e');
}
}
void _updatePosition(PointerMoveEvent pointerMoveEvent) {
double newOffsetX = _offset.dx pointerMoveEvent.delta.dx;
double newOffsetY = _offset.dy pointerMoveEvent.delta.dy;
if (newOffsetX < _minOffset.dx) {
newOffsetX = _minOffset.dx;
} else if (newOffsetX > _maxOffset.dx) {
newOffsetX = _maxOffset.dx;
}
if (newOffsetY < _minOffset.dy) {
newOffsetY = _minOffset.dy;
} else if (newOffsetY > _maxOffset.dy) {
newOffsetY = _maxOffset.dy;
}
setState(() {
_offset = Offset(newOffsetX, newOffsetY);
});
}
@override
Widget build(BuildContext context) {
return Positioned(
left: _offset.dx,
top: _offset.dy,
child: Listener(
onPointerMove: (PointerMoveEvent pointerMoveEvent) {
_updatePosition(pointerMoveEvent);
setState(() {
_isDragging = true;
});
},
onPointerUp: (PointerUpEvent pointerUpEvent) {
print('onPointerUp');
if (_isDragging) {
setState(() {
_isDragging = false;
});
} else {
widget.onPressed();
}
},
child: Container(
key: _key,
child: widget.child,
),
),
);
}
}
As you can see, it only sets the button's offset based on portrait mode once in initState function, and doesn't deal with rotation.
A walk around that I could think of at this moment is just having another floating button specifically set for landscape mode.
Thank you in advance for any answer.
CodePudding user response:
Ok, so I still don't know how to detect rotation, but my walk around works.
All I do is create another class(widget) for a landscape floating button.
In main, check device orientation to use either the floating button portrait class or landscape class.
The only thing that I change in the landscape button class is
from
_offset = Offset(widget.deviceWidth * 0.8, widget.deviceHeight * 0.75);
to
_offset = Offset(widget.deviceHeight * 1.75, widget.deviceWidth * 0.3);
CodePudding user response:
You shouldn't care about orientation. You should use something like LayoutBuilder
to determine your space available, and make breakpoint decisions based on that. LayoutBuilder will update immediately if the device space changes, and you should react to that by doing another layout.