Reputation: 1
I'm designing a resizable and repositionable widget. I can keep this widget within the upper and left screen boundaries, but I have not yet managed to keep it within the right and lower screen boundaries. The widget was created with GestureDetector and defined by taking a position within the Stack structure. I'm trying update widget position in onPanUpdate method but right and bottom screen boundaries not working. How can I define the right and bottom frame boundaries and ensure that the widget stays within these boundaries? top_and_left_frame_part | bottom_and_right_frame_part
Here's my code blocks, first important issue :
,onPanUpdate: (details) {
setState(() {
if (!isResizing) {
posx += details.delta.dx / 2;
posy += details.delta.dy / 2;
posx = posx.clamp(0, areaW);
posy = posy.clamp(0, areaH);
right = posx + width;
if (right > areaW) {
right = areaW;
}
}
in this block, onPanUpdate methods working but 'if' statement and 'right = areaW' not working.
import 'dart:math';
import 'package:flutter/material.dart';
class Note2Widget extends StatefulWidget {
const Note2Widget({super.key});
@override
State<Note2Widget> createState() => _Note2WidgetState();
}
class _Note2WidgetState extends State<Note2Widget> {
double width = 100;
double height = 100;
double posx = 0;
double posy = 0;
late double areaW;
late double areaH;
bool isResizing = false;
Offset startPosition = const Offset(0, 0);
double right = 0;
@override
Widget build(BuildContext context) {
areaW = MediaQuery.sizeOf(context).width;
areaH = MediaQuery.sizeOf(context).height;
return Positioned(
top: posy,
left: posx,
width: areaW,
height: areaH,
child: GestureDetector(
onPanStart: (details) {
if (details.localPosition.dx >= width - 15 &&
details.localPosition.dy >= height - 15) {
setState(() {
isResizing = true;
startPosition = details.localPosition;
});
}
},
onPanUpdate: (details) {
setState(() {
if (!isResizing) {
posx += details.delta.dx / 2;
posy += details.delta.dy / 2;
posx = posx.clamp(0, areaW);
posy = posy.clamp(0, areaH);
right = posx + width;
if (right > areaW) {
right = areaW;
}
}
if (isResizing) {
double dx = details.localPosition.dx - startPosition.dx;
double dy = details.localPosition.dy - startPosition.dy;
width += dx;
height += dy;
startPosition = details.localPosition;
}
if (height < 50) {
height = 50;
}
if (width < 50) {
width = 50;
}
});
},
onPanEnd: (details) {
setState(() {
isResizing = false;
});
},
child: Stack(children: [
Positioned(
top: posy,
left: posx,
width: width,
height: height,
child: Container(
decoration: BoxDecoration(
border: Border.all(width: 2, color: Colors.black)),
child: Center(
child: Padding(
padding: const EdgeInsets.all(8),
child: Container(
width: width - 10,
height: height - 10,
color: Colors.orangeAccent,
child: const TextField(
textAlign: TextAlign.center,
maxLines: null,
decoration: InputDecoration(
border: InputBorder.none,
),
style: TextStyle(
color: Colors.black,
fontSize: 20,
decoration: TextDecoration.none),
),
),
)),
))
]),
));
}
}
This is widget button on screen
class ExampleWidgetButton extends StatefulWidget {
const ExampleWidgetButton({super.key});
@override
State<ExampleWidgetButton> createState() => _ExampleWidgetButtonState();
}
class _ExampleWidgetButtonState extends State<ExampleWidgetButton> {
@override
Widget build(BuildContext context) {
return Positioned(
bottom: 30,
left: 60,
child: CircleAvatar(
backgroundColor: Colors.amberAccent,
radius: 35,
child: IconButton(
onPressed: () {
context
.read<PageDraggable>()
.addWidget(const Note2Widget());
},
color: Colors.black,
icon: const Icon(Icons.pix)),
));
}
}
This is main page in project
import 'package:flutter/material.dart';
import 'package:flutter_colorpicker/flutter_colorpicker.dart';
import 'package:provider/provider.dart';
import 'package:star_menu/star_menu.dart';
import 'widgets/interfaceButtons.dart';
double globalPosX = 0;
double globalPosY = 0;
double containerLeft = 0;
double containerTop = 0;
bool isResizing = false;
Offset startPosition = Offset.zero;
String tempText = "";
class PageDraggable extends StatefulWidget with ChangeNotifier {
PageDraggable({super.key});
static List<Widget> widgets = [];
void addWidget(Widget widget) {
PageDraggable.widgets.add(widget);
notifyListeners();
print("${widgets.toList()}");
}
void removeWidget(Widget widget) {
PageDraggable.widgets.remove(widget);
notifyListeners();
}
void allRemoveWidget() {
PageDraggable.widgets.clear();
notifyListeners();
}
@override
State<PageDraggable> createState() => _PageDraggableState();
}
class _PageDraggableState extends State<PageDraggable> with ChangeNotifier {
int count = 0;
Color pickerColor = const Color(0xff443a49);
Color currentColor = const Color(0xff443a49);
void changeColor(Color color) {
setState(() => pickerColor = color);
}
@override
Widget build(BuildContext context) {
return Scaffold(
body: SafeArea(
child: Stack(children: <Widget>[
// AreaControlWidget(),
Consumer<PageDraggable>(
builder: (context, value, child) {
return Positioned(
top: 5,
left: 250,
child: Text("| x: $globalPosX \n| y: $globalPosY "));
},
),
Consumer<PageDraggable>(
builder: (context, value, child) {
return Stack(
children: [...PageDraggable.widgets],
);
},
),
///Delete Button
const DeleteWidget(),
///
Positioned(
bottom: 40,
left: 120,
child: ElevatedButton(
onPressed: () {
showDialog(
builder: (context) => AlertDialog(
title: const Text('Pick a Color'),
content: SingleChildScrollView(
child: MaterialPicker(
pickerColor: pickerColor,
onColorChanged: changeColor,
),
),
actions: <Widget>[
ElevatedButton(
child: const Text('Got it'),
onPressed: () {
setState(() => currentColor = pickerColor);
Navigator.of(context).pop();
},
),
],
),
context: context);
},
child: const Text("Click"))),
///
Positioned(
bottom: 60,
right: 30,
child: StarMenu(
params: const StarMenuParameters(
shape: MenuShape.circle,
circleShapeParams:
CircleShapeParams(startAngle: 90, endAngle: 180),
animationCurve: Curves.bounceInOut,
),
onStateChanged: (state) => print("state changed"),
onItemTapped: (index, controller) {
controller.closeMenu!();
print("Menu item $index tapped");
},
items: const [
///Create Widget Button
CreateWidget(),
///Create Text Widget Button
CreateTextWidget(),
///Resizeable Widget Button
ResizeableWidget(),
///StickyNote Widget Button
StickyNoteWidgetButton(),
///Example Widget Button
ExampleWidgetButton(),
],
child: FloatingActionButton(
onPressed: () {},
child: const Icon(Icons.looks_one),
),
),
)
]),
),
);
}
}
This is main.dart file
import 'package:draggable_example/ui/pageDraggable.dart';
import 'package:flutter/material.dart';
import 'package:provider/provider.dart';
void main() => runApp(MultiProvider(
providers: [
ChangeNotifierProvider(
create: (context) => PageDraggable(),
),
],
child: MaterialApp(
home: PageDraggable(),
)));
All necessary libraries are installed and running, and flutter is running on the latest version.
-flutter doctor
Doctor summary (to see all details, run flutter doctor -v):
[√] Flutter (Channel stable, 3.13.9, on Microsoft Windows [Version 10.0.19045.3570], locale tr-TR)
[√] Windows Version (Installed version of Windows is version 10 or higher)
[√] Android toolchain - develop for Android devices (Android SDK version 34.0.0)
[X] Chrome - develop for the web (Cannot find Chrome executable at .\Google\Chrome\Application\chrome.exe)
! Cannot find Chrome. Try setting CHROME_EXECUTABLE to a Chrome executable.
[√] Visual Studio - develop Windows apps (Visual Studio Community 2022 17.6.5)
[√] Android Studio (version 2022.3)
[√] VS Code, 64-bit edition (version 1.81.1)
[√] Connected device (3 available)
[√] Network resources
Upvotes: 0
Views: 225
Reputation: 7292
Your problem is that MediaQuery.sizeOf(context) gives you the size of the screen but you have your PageDraggable widget wrapped in a SafeArea which adds padding sufficient to avoid notches and rounded corners.
What you need is to take your screen size and subtract the amount cut off by the SafeArea.
This library on pub.dev may help: https://pub.dev/packages/responsive_sizer. It has an object called Adaptive that is aware of the SafeArea. To get the usable width, you'll go
double usableHeight = Adaptive.sh();
double usableWidth = Adaptive.sw();
Then just draw beginning at 0,0
as you'd normally do but now the width and height of your drawing area will fit the screen.
Upvotes: 0