How to left untouched Positioned when screen dimension change Flutter - flutter

I have the code below where there is a background image, and then a button that allows to create a drag & drop Container. But my problem is
when the screen size changes, because some containers are lost, how could I do for leave untouch the position of the container depending on the screen size?
class DashboardScreenState extends State<DashboardScreen>
with TickerProviderStateMixin {
ConfigPage config = ConfigPage();
List<SectionContainer> sectionContainers = [];
List<Widget> initialWidgets = [];
#override
void initState() {
config = Project.getProjectPageDetails(widget.id);
super.initState();
}
#override
Widget build(BuildContext context) {
initialWidgets = [
TextButton(
onPressed: () {
setState(() {
sectionContainers.add(SectionContainer());
});
},
child: Text('add'),
),
];
return CustomScaffold(
text: config.name!,
onPageChange: widget.onPageChange,
onLogout: widget.onLogout,
body: Container(
decoration: BoxDecoration(
image: DecorationImage(
image: FileImage(File(config.image!)), fit: BoxFit.cover)),
constraints: BoxConstraints.expand(),
child: Stack(
children: initialWidgets + sectionContainers,
),
));
}
}
here the code of the SectionContainer:
class _SectionContainerState extends State<SectionContainer> {
double xPosition = 0;
double yPosition = 0;
Color color = Colors.red;
#override
void initState() {
super.initState();
}
#override
Widget build(BuildContext context) {
return Positioned(
top: yPosition,
left: xPosition,
child: GestureDetector(
onPanUpdate: (tapInfo) {
setState(() {
xPosition += tapInfo.delta.dx;
yPosition += tapInfo.delta.dy;
});
},
child: Container(
width: 150,
height: 150,
color: color,
),
),
);
}
}
I have also tried with a LayoutBuilder but nothing change,
maybe the problem is in the Container of the background image where there is BoxConstraints.expand()?

This reference should help: MediaQuery Class
Simple look at adding it to code:
Widget build(BuildContext context) {
Size size = MediaQuery.of(context).size;
then down the widget tree you can add size.height and size.width to what you need the screen to adjust to.

Related

how to achieve a functionality like linear loading bar which will load up as user move between various screens

I am using android studio and flutter. I want to build the screen as shown below in the image:screen Image
let's say I have 4 screens. on the first screen, the bar will load up to 25%. the user will move to next screen by clicking on continue, the linearbar will load up to 50% and so on. the user will get back to previous screens by clicking on the back button in the appbar.
I tried stepper but it doesn't serve my purpose.
You can use the widget LinearProgressIndicator(value: 0.25,) for the first screen and with value: 0.5 for the second screen etc.
If you want to change the bar value within a screen, just use StatefullWidget's setState(), or any state management approaches will do.
import 'package:flutter/material.dart';
class ProgressPage extends StatefulWidget {
const ProgressPage({super.key});
#override
State<ProgressPage> createState() => _ProgressPageState();
}
class _ProgressPageState extends State<ProgressPage> {
final _pageController = PageController();
final _pageCount = 3;
int? _currentPage;
double? _screenWidth;
double? _unit;
double? _progress;
#override
void initState() {
super.initState();
_pageController.addListener(() {
_currentPage = _pageController.page?.round();
setState(() {
_progress = (_currentPage! + 1) * _unit!;
});
});
}
#override
void didChangeDependencies() {
super.didChangeDependencies();
_screenWidth = MediaQuery.of(context).size.width;
_unit = _screenWidth! / _pageCount;
_progress ??= _unit;
}
#override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(title: const Text('HOZEROGOLD')),
body: Column(
children: [
Align(
alignment: Alignment.topLeft,
child: Container(
color: Colors.yellow,
height: 10,
width: _progress,
),
),
Expanded(
child: PageView(
controller: _pageController,
children: _createPage(),
),
),
],
),
);
}
List<Widget> _createPage() {
return List<Widget>.generate(
_pageCount,
(index) => Container(
color: Colors.white,
child: Center(
child: ElevatedButton(
onPressed: () => _moveNextPage(),
child: Text('NEXT $index'),
),
),
),
);
}
void _moveNextPage() {
if (_pageController.page!.round() == _pageCount-1) {
_pageController.jumpToPage(0);
} else {
_pageController.nextPage(
curve: Curves.bounceIn,
duration: const Duration(milliseconds: 100));
}
}
}
HAPPY CODING! I hope it will be of help.

Gesture Detector problem when i am using Interactive viewer (FLUTTER)

WORK :
I am Creating App for book and I am linking images and swipe to next page
I am using Gesture Detector for swipe next page
and I am using InteractiveViewer for zoom page
PROBLEM :
The problem is when I use pinch to zoom-in its successfully work but when Drag the page for seeing more words . It detect gesture Detector and It goes to other page ..
WHAT I WANT :
I want to disable the gestureDetector when I am using InteractiveViewer,
Like when I am in Zoom-in mode so the gestureDetector Disable and when I Zoom out
the GestureDetector enable.
import 'package:flutter/material.dart';
class Screen2 extends StatefulWidget {
const Screen2({ Key key }) : super(key: key);
#override
_Screen2State createState() => _Screen2State();
}
class _Screen2State extends State<Screen2> {
#override
Widget build(BuildContext context) {
return Scaffold(
appBar: appbarr(),
body: GestureDetector(
onHorizontalDragUpdate: (details) {
// Note: Sensitivity is integer used when you don't want to mess up vertical drag
int sensitivity = 8;
int senElse = -8;
if (details.delta.dx > sensitivity) {
Navigator.pop(context);
}
else if (details.delta.dx < senElse )
{
Navigator.push(context, MaterialPageRoute(builder: (context) => Screen3()));
}
},
child: InteractiveViewer(
panEnabled: true,
minScale: 0.5,
maxScale: 5,
child: Container(
width: MediaQuery.of(context).size.width,
height: MediaQuery.of(context).size.height,
decoration: BoxDecoration(
image: DecorationImage(
image: AssetImage("assets/2.jpg"),
fit: BoxFit.fill,
),
),
),
),
)
);
}
}
You need to check if the is the user has zoomed in or not. If the user has zoomed in we not render a GestureDetector. We only make use of the GestureDetector is the user hasn't zoomed. To check whether the user has zoomed in or not, we use a TranformationController and compare it's current value with the identity matrix.
Here is the working code:
class Screen2 extends StatefulWidget {
const Screen2({ Key? key }) : super(key: key);
#override
_Screen2State createState() => _Screen2State();
}
class _Screen2State extends State<Screen2> {
final transformationController = TransformationController();
bool get userHasZoomedIn => (Matrix4.identity() - transformationController.value).infinityNorm() > 0.000001;
#override
Widget build(BuildContext context) {
final interactiveImage = InteractiveViewer(
panEnabled: true,
minScale: 0.5,
maxScale: 5,
transformationController: transformationController,
onInteractionEnd: (details) => setState((){}),
child: Container(
width: MediaQuery.of(context).size.width,
height: MediaQuery.of(context).size.height,
decoration: BoxDecoration(
image: DecorationImage(
image: AssetImage("assets/2.jpg"),
fit: BoxFit.fill,
),
),
),
);
final appBar = AppBar(title: Text('Title'));
if(userHasZoomedIn){
return Scaffold(
appBar: appBar,
body: interactiveImage,
);
}
return Scaffold(
appBar: appBar,
body: GestureDetector(
onHorizontalDragUpdate: (details) {
// Note: Sensitivity is integer used when you don't want to mess up vertical drag
int sensitivity = 8;
int senElse = -8;
if (details.delta.dx > sensitivity) {
Navigator.pop(context);
}
else if (details.delta.dx < senElse )
{
Navigator.push(context, MaterialPageRoute(builder: (context) => Screen3()));
}
},
child: interactiveImage,
)
);
}
}
Note that we call setState in the InteractionViewer.onInteractionEnd. Another approach would be to add a listener to the TransformationController:
transformationController.addListener(() {
setState(() {});
});
You should not do that because that will fire setState() way too often which causes a bad user experience and high CPU and GPU load.

How to zoom a GestureDetector with onPanUpdate call?

I'm working on a drawing app. I want to enable both drawing on the canvas and zooming the drawings. So far, I tried achieving it by wrapping GestureDetector in InteractiveViewer and using AbsorbPointer to turn the zoom mode on and off. See the minimum demo code below.
import 'package:flutter/cupertino.dart';
import 'package:flutter/material.dart';
void main() => runApp(new MaterialApp(
home: new IssueExamplePage(),
debugShowCheckedModeBanner: false,
));
class IssueExamplePage extends StatefulWidget {
#override
_IssueExamplePageState createState() => _IssueExamplePageState();
}
class _IssueExamplePageState extends State<IssueExamplePage> {
bool drawingBlocked = false;
List<Offset> points = [];
#override
Widget build(BuildContext context) {
return Scaffold(
body: SafeArea(
child: Container(
color: Colors.grey,
padding: EdgeInsets.all(5),
child: Container(
color: Colors.white,
child: InteractiveViewer(
child: AbsorbPointer(
absorbing: drawingBlocked,
child: GestureDetector(
behavior: HitTestBehavior.translucent,
onPanUpdate: (details) {
RenderBox renderBox = context.findRenderObject();
Offset cursorLocation = renderBox.globalToLocal(details.localPosition);
setState(() {
points = List.of(points)..add(cursorLocation);
});
},
onPanEnd: (details) {
setState(() {
points = List.of(points)..add(null);
});
},
child: CustomPaint(
painter: MyPainter(points),
size: Size.infinite
),
),
),
),
),
),
),
floatingActionButton: Column(
mainAxisAlignment: MainAxisAlignment.end,
children: [
FloatingActionButton(
child: Icon(drawingBlocked? CupertinoIcons.hand_raised_fill : CupertinoIcons.hand_raised),
onPressed: () {
setState(() {
drawingBlocked = !drawingBlocked;
});
}
),
SizedBox(
height: 10
),
FloatingActionButton(
child: Icon(Icons.clear),
onPressed: () {
setState(() {
points = [];
});
}
),
SizedBox(
height: 20
),
],
),
);
}
}
class MyPainter extends CustomPainter {
MyPainter(this.points);
List<Offset> points;
Paint paintBrush = Paint()
..color = Colors.blue
..strokeWidth = 5
..strokeJoin = StrokeJoin.round
..strokeCap = StrokeCap.round;
#override
void paint(Canvas canvas, Size size) {
for (int i = 0; i < points.length - 1; i++) {
if (points[i] != null && points[i + 1] != null) {
canvas.drawLine(points[i], points[i + 1], paintBrush);
}
}
}
#override
bool shouldRepaint(MyPainter oldDelegate) {
return points != oldDelegate.points;
}
}
However, with this realization OnPanUpdate is working with a delay (see Flutter onPanStart invokes late when the widget is wrapped inside InteractiveViewer). Is there any other way to achieve the expected result (both zooming and drawing) or is it possible to fix the OnPanUpdate delay?
UPDATE: I found some sort of solution. You can use Listener instead of GestureDetector (it has substitues for the most of the parameters: onPanStart -> onPointerDown, onPanUpdate -> onPointerMove, etc.). But it seems like there's no fix for GestureDetector.

Why is the origin of the widget constantly at top-left when I use Transform.translate for the widget in my code?

I'm writing a flutter program in which you can move a widget with your finger by using GestureDetector instead of Draggable Widget.
Here is my code. I use Transform.translate to move.
import 'package:flutter/material.dart';
class AAA extends StatefulWidget {
#override
AAAState createState() => AAAState();
}
class AAAState extends State<AAA> {
double x = 0.0;
double y = 0.0;
#override
Widget build(BuildContext context) {
return Transform.translate(
offset: Offset(x, y),
child: GestureDetector(
onPanUpdate: (DragUpdateDetails details) {
RenderBox getBox = context.findRenderObject();
final localOffset = getBox.globalToLocal(details.globalPosition);
setState(() {
x = localOffset.dx;
y = localOffset.dy;
});
},
child: ElevatedButton(
onPressed: null,
child: Text('Button'),
),
),
);
}
}
//Actually I'm using AAA widget in a List like this.
Container(
width: double.infinity,
height: double.infinity,
child: Column(
children: widgetList,
),
),
But in my code, when I move the widget with my finger, the origin of the widget is constantly fixed at top-left.
I tried using Transform instead of Transform.translate and changing origin: property, but it wasn't going well.
Like this:
gif image
I want the origin to be at the position where I touched first.
Sorry for my poor English, but give me some advice!

Flutter Custom Painter drawing is laggy

I am trying to code a drawing app, in which users can choose different pen color and draw colorful drawings. I have created a class PointsGroup which stores list of offsets and associated color. In GestureDetector's onPanUpdate, the PointsGroup is appended to list of PointsGroup and passed to SignaturePainter.
But the drawing is bit laggy, it is not drawn as soon as pen moves.
You can see the video https://free.hubcap.video/v/LtOqoEj9H0dY9F9xC_jSst9HT3tSOJlTi
import 'package:flutter/material.dart';
List<Color> colorList = [
Colors.indigo,
Colors.blue,
Colors.green,
Colors.yellow,
Colors.orange,
Colors.red
];
void main() => runApp(MaterialApp(
home: HomePage(),
debugShowCheckedModeBanner: false,
));
class HomePage extends StatefulWidget {
#override
_HomePageState createState() => _HomePageState();
}
class _HomePageState extends State<HomePage> {
List<Offset> _points = <Offset>[];
List<Offset> _setPoints = <Offset>[];
List<PointsGroup> _ptsGroupList = <PointsGroup>[];
int startIndex;
int endIndex;
#override
void initState() {
ColorChoser.penColor = Colors.black;
super.initState();
}
#override
Widget build(BuildContext context) {
return Scaffold(
body: Stack(
children: <Widget>[
GestureDetector(
onPanStart: (details) {
setState(() {
_points.clear();
startIndex = _ptsGroupList.length;
ColorChoser.showColorSelector = false;
});
},
onPanUpdate: (DragUpdateDetails details) {
setState(() {
RenderBox object = context.findRenderObject();
Offset _localPosition =
object.globalToLocal(details.globalPosition);
_points = new List.from(_points)..add(_localPosition);
_setPoints = new List.from(_points);
_ptsGroupList.add(new PointsGroup(
setPoints: _setPoints, setColor: ColorChoser.penColor));
});
},
onPanEnd: (DragEndDetails details) {
setState(() {
_points.add(null);
ColorChoser.showColorSelector = true;
endIndex = _ptsGroupList.length;
if (startIndex < endIndex) {
_ptsGroupList.replaceRange(
startIndex, endIndex - 1, [_ptsGroupList.removeLast()]);
}
});
},
child: CustomPaint(
painter: SignaturePainter(grpPointsList: _ptsGroupList),
size: Size.infinite,
),
),
ColorChoser(),
],
),
floatingActionButton: FloatingActionButton(
child: Icon(Icons.undo),
onPressed: () {
setState(() {
if (_ptsGroupList.length > 0) {
_ptsGroupList.removeLast();
}
});
}),
);
}
}
class ColorChoser extends StatefulWidget {
const ColorChoser({
Key key,
}) : super(key: key);
static Color backgroundColor = Colors.white;
static Color penColor = Colors.blue;
static bool showColorSelector = true;
#override
_ColorChoserState createState() => _ColorChoserState();
}
class _ColorChoserState extends State<ColorChoser> {
#override
Widget build(BuildContext context) {
return Visibility(
visible: ColorChoser.showColorSelector,
child: Positioned(
bottom: 0,
left: 0,
width: MediaQuery.of(context).size.width,
child: Container(
height: 60,
child: ListView.builder(
scrollDirection: Axis.horizontal,
itemCount: colorList.length,
itemBuilder: (context, index) {
return InkWell(
onTap: () {
setState(() {
ColorChoser.penColor = colorList[index];
});
},
child: Padding(
padding: const EdgeInsets.symmetric(
horizontal: 4.0, vertical: 5.0),
child: Container(
color: colorList[index],
// height: 30,
width: 45,
),
),
);
}),
),
),
);
}
}
class SignaturePainter extends CustomPainter {
List<Offset> points;
List<PointsGroup> grpPointsList = <PointsGroup>[];
var paintObj;
SignaturePainter({
this.grpPointsList = const [],
});
#override
void paint(Canvas canvas, Size size) {
for (PointsGroup pts in grpPointsList) {
points = pts.setPoints;
paintObj = Paint()
..color = pts.setColor
..strokeCap = StrokeCap.round
..strokeWidth = 5.0;
for (int i = 0; i < points.length - 1; i++) {
if (points[i] != null && points[i + 1] != null) {
canvas.drawLine(points[i], points[i + 1], paintObj);
}
}
}
}
#override
bool shouldRepaint(SignaturePainter oldDelegate) =>
oldDelegate.points != points;
}
class PointsGroup {
List<Offset> setPoints = <Offset>[];
Color setColor;
PointsGroup({this.setPoints, this.setColor});
}
Also the drawing is not shown for the very first draw. As soon as pen
is lifted it starts showing.
P.S. If there is any alternate way is to achieve the desired multi-colored drawing, it will be okay.
You are clearing all the points whenever onPanStart is triggered (when the user places their finger on the screen). If you remove _points.clear() from onPanStart: (details) {} you will retain all the points that the user draws.
The application begins to lag and framerate is impacted after many points are drawn. You'll notice this when the user has drawn a decent amount on the canvas. To prevent lag from occurring, one strategy is to reduce the number of points being drawn. You can halve the number of points and still give the user the autonomy to draw what they desire by doing this:
final int THRESHOLD = 2;
if (totalPoints % THRESHOLD == 0){
_points = new List.from(_points)..add(_localPosition);
}
totalPoints is a counter you increment by one in onPanUpdate: (details) {}
Another technique is to wrap the subclass widget that extends CustomPainter, in this case CustomPaint, with a RepaintBoundary widget https://api.flutter.dev/flutter/widgets/RepaintBoundary-class.html. This widget will ensure that only the regions of the canvas where painting occurs is redrawn when needed. By limiting refresh rendering to one widget, you will speed up the process and deliver better results.
RepaintBoundary(
child: CustomPaint(
isComplex: true,
willChange: false,
painter: Painter(
points: _points,
),
),
),