Custom widget raise event not working property in flutter - flutter

i need a custom widget for draw vertical and horizontal line on my page whch i can resize widget on runtime. i combine GestureDetector for DrawLine with paint and canvas, so i have a class with name Line and i can paint my lines on this class
so i create a custom widget in flutter for draw vertical line with resize property on runtime
i write this code on flutter
import 'package:flutter/material.dart';
void main() => runApp(MyApp());
class MyApp extends StatelessWidget {
double height = 300;
double top = 0;
double left = 200;
#override
Widget build(BuildContext context) {
return MaterialApp(
title: 'Text Overflow Demo',
theme: ThemeData(
primarySwatch: Colors.blue,
),
home: Scaffold(
body: Stack(
children: <Widget>[
new DrawLineClass(top: 50, left: 100, height: 400),
new DrawLineClass(top: 100, left: 50, height: 300),
new DrawLineClass(top: 150, left: 150, height: 300),
],
),
),
);
}
}
const double ballDiameter = 20.0;
const double strokeWidth = 10.0;
class DrawLineClass extends StatefulWidget {
DrawLineClass({Key key, this.top, this.left, this.height}) : super(key: key);
double height;
double top;
double left;
#override
_DrawLineClassState createState() => _DrawLineClassState();
}
class _DrawLineClassState extends State<DrawLineClass> {
#override
Widget build(BuildContext context) {
return new Stack(
children: <Widget>[
Line(start: {"x": widget.left, "y": widget.top}, end: {"x": widget.left, "y": widget.height}),
// top middle
Positioned(
top: widget.top - ballDiameter / 2,
left: widget.left - ballDiameter / 2,
child: new ManipulatingBall(
onDrag: (dx, dy) {
setState(() {
widget.top = widget.top + dy;
});
},
),
),
// bottom center
Positioned(
top: widget.height - ballDiameter / 2,
left: widget.left - ballDiameter / 2,
child: new ManipulatingBall(
onDrag: (dx, dy) {
var newHeight = widget.height + dy;
setState(() {
widget.height = newHeight > 0 ? newHeight : 0;
});
},
),
),
],
);
}
}
class ManipulatingBall extends StatefulWidget {
ManipulatingBall({Key key, this.onDrag}) : super(key: key);
Function onDrag;
#override
_ManipulatingBallState createState() => _ManipulatingBallState();
}
class _ManipulatingBallState extends State<ManipulatingBall> {
double initX;
double initY;
_handleDrag(details) {
setState(() {
initX = details.globalPosition.dx;
initY = details.globalPosition.dy;
});
}
_handleUpdate(details) {
var dx = details.globalPosition.dx - initX;
var dy = details.globalPosition.dy - initY;
initX = details.globalPosition.dx;
initY = details.globalPosition.dy;
widget.onDrag(dx, dy);
}
#override
Widget build(BuildContext context) {
return new GestureDetector(
behavior: HitTestBehavior.deferToChild,
onPanStart: _handleDrag,
onPanUpdate: _handleUpdate,
child: Container(
width: ballDiameter,
height: ballDiameter,
decoration: BoxDecoration(
color: Colors.blue.withOpacity(0.5),
shape: BoxShape.circle,
),
),
);
}
}
class Line extends StatefulWidget {
final Map<String, double> start;
final Map<String, double> end;
Line({this.start, this.end});
#override
_LineState createState() => _LineState();
}
class _LineState extends State<Line> with SingleTickerProviderStateMixin {
AnimationController _controller;
#override
void initState() {
super.initState();
_controller = AnimationController(vsync: this);
}
#override
void dispose() {
super.dispose();
_controller.dispose();
}
#override
Widget build(BuildContext context) {
return CustomPaint(
size: Size(MediaQuery.of(context).size.width,
MediaQuery.of(context).size.height),
painter: DrawLine(start: widget.start, end: widget.end),
);
}
}
class DrawLine extends CustomPainter {
Map<String, double> start;
Map<String, double> end;
DrawLine({this.start, this.end});
#override
void paint(Canvas canvas, Size size) {
Paint line = new Paint()
..color = Colors.red
..strokeCap = StrokeCap.round
..style = PaintingStyle.fill
..strokeWidth = strokeWidth;
canvas.drawLine(Offset(start["x"], start["y"]), Offset(end["x"], end["y"]), line);
}
#override
bool shouldRepaint(CustomPainter oldDelegate) {
// TODO: implement shouldRepaint
return true;
}
}
i draw 3 lines on my page but only latest line can be resize
whats the problem?

Each line draw on the whole screen, the third line cover the first and second line and both of first line and second line can not receive the events of GestureDetector. Instead, you can use LayoutBuidler to draw line with relative position not global postion.

Related

Animated Curve Line - Flutter

Is it possible to somehow do in flutter what is shown in the GIF?
And so that the starting point always displays a number (in what position this point is at the moment), in the range from 0-100.
EDIT: The animation is now similar to the one in GIF
import 'dart:ui';
import 'package:flutter/material.dart';
void main() => runApp(const MyApp());
class MyApp extends StatefulWidget {
const MyApp({super.key});
#override
State<MyApp> createState() => _MyAppState();
}
class _MyAppState extends State<MyApp> with SingleTickerProviderStateMixin {
late final AnimationController _controller = AnimationController(
vsync: this,
duration: const Duration(seconds: 3)
);
late final Size size = const Size(400,400);
late Path path = Path()
..moveTo(3, size.height-4.5)
..quadraticBezierTo(
size.width, size.height,
size.width-4.5, 3,
);
late PathAnimation pathAnimation = PathAnimation(path: path);
#override
void initState() {
super.initState();
_controller.forward();
}
#override
Widget build(BuildContext context) {
return MaterialApp(
title: 'Material App',
home: Scaffold(
appBar: AppBar(
title: const Text('Material App Bar'),
),
body: Center(
child: ClipRect(
child: AnimatedBuilder(
animation: _controller,
child: Container(
alignment: Alignment.center,
height: size.height,
width: size.width,
decoration: BoxDecoration(
color: Colors.white,
border: Border.all(
color: Colors.black,
width: 3,
)
),
),
builder: (context, child) {
return CustomPaint(
foregroundPainter: PathPainter(
animation: _controller.value,
pathAnimation: pathAnimation,
),
child: child
);
}
),
),
),
),
);
}
}
class PathAnimation {
final Path path;
Path currentPath = Path();
late final List<PathMetric> pathSegments;
late final double totalLength;
double currentLength = 0;
int currentPathIndex = 0;
PathAnimation({
required this.path
}) {
pathSegments = path.computeMetrics().toList();
totalLength = pathSegments.fold(0, (value, element) => value + element.length);
}
Path getCurrentPath(double animation) {
while (animation > (currentLength + pathSegments[currentPathIndex].length) / totalLength) {
double segmentLength = pathSegments[currentPathIndex].length;
currentPath.addPath(
pathSegments[currentPathIndex].extractPath(0, segmentLength),
Offset.zero
);
currentPathIndex++;
currentLength += segmentLength;
}
if (currentPathIndex >= pathSegments.length) return currentPath;
double missingPartLength = animation - currentLength / totalLength;
double newSegmentLength = pathSegments[currentPathIndex].length;
return
currentPath..addPath(
pathSegments[currentPathIndex].extractPath(0, missingPartLength * newSegmentLength),
Offset.zero
);
}
}
class PathPainter extends CustomPainter{
double animation;
PathAnimation pathAnimation;
PathPainter({
required this.animation,
required this.pathAnimation,
});
#override
void paint(Canvas canvas, Size size) {
canvas.drawPath(
pathAnimation.getCurrentPath(animation),
Paint()
..style = PaintingStyle.stroke
..strokeWidth = 3
..color = Colors.red
);
}
#override
bool shouldRepaint(PathPainter oldDelegate) {
return animation != oldDelegate.animation;
}
}

How to set wave effect for a button when taps on it in Flutter

Hi, I was trying to build this ui, but i couldnt implement the wave effect as shown in the image.
i got some code for the wave effect but it does not fit well. I made the ui code very complex. so i made a similar ui for sharing .
///////////////////////////////////////////////////////////////////////////////////////////
//////////////////////////////////////////////////////////////////////////////////////////
/////////////////////////////////////////////////////////////////////////////////////////
class Test extends StatefulWidget {
const Test({Key key}) : super(key: key);
#override
_TestState createState() => _TestState();
}
class _TestState extends State<Test> {
#override
Widget build(BuildContext context) {
return Scaffold(
backgroundColor: AppColors.mainBg3,
body: Center(
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: [
buttonView(0),
buttonView(1),
buttonView(2),
buttonView(4),
],
),
),
);
}
var selectedIndex = 0;
Widget buttonView(int i) {
return Container(
margin: EdgeInsets.only(bottom: 30),
child: InkWell(
onTap: () {
selectedIndex = i;
setState(() {
});
},
child: selectedIndex == i ? WaveAnimation(child: button()) : button(),
),
);
}
Widget button() {
return Row(
mainAxisAlignment: MainAxisAlignment.center,
children: [
CircleAvatar(
radius: 12,
backgroundColor: Colors.white,
child: Container(
height: 13,
width: 13,
decoration: BoxDecoration(
shape: BoxShape.circle,
gradient: LinearGradient(
colors: [Color(0xffD053A3), Color(0xff842990)])),
),
),
],
);
}
}
And heres the code of wave animation
class WaveAnimation extends StatefulWidget {
const WaveAnimation({
this.size = 80.0,
#required this.child,
});
final double size;
final Widget child;
#override
_WaveAnimationState createState() => _WaveAnimationState();
}
class _WaveAnimationState extends State<WaveAnimation>
with TickerProviderStateMixin {
AnimationController _controller;
#override
void initState() {
super.initState();
_controller = AnimationController(
duration: const Duration(milliseconds: 2000),
vsync: this,
);
}
#override
void dispose() {
_controller.dispose();
super.dispose();
}
Color _color = Color(0xffB05CA1);
#override
Widget build(BuildContext context) {
return Center(
child: CustomPaint(
painter: CirclePainter(
_controller,
color: _color.withOpacity(0.1),
),
child: SizedBox(
width: widget.size * 2,
height: widget.size * 2,
child: _button(),
),
),
);
}
Widget _button() {
return Center(
child: ClipRRect(
borderRadius: BorderRadius.circular(widget.size),
child: DecoratedBox(
decoration: BoxDecoration(
gradient: RadialGradient(
colors: <Color>[_color, Color.lerp(_color, Colors.black, 0.05)],
),
),
child: ScaleTransition(
scale: Tween(begin: 0.95, end: 1.0).animate(
CurvedAnimation(
parent: _controller,
curve: CurveWave(),
),
),
),
),
),
);
}
}
class CurveWave extends Curve {
const CurveWave();
#override
double transform(double t) {
if (t == 0 || t == 1) {
return 0.01;
}
return math.sin(t * math.pi);
}
}
class CirclePainter extends CustomPainter {
CirclePainter(
this._animation, {
#required this.color,
}) : super(repaint: _animation);
final Color color;
final Animation<double> _animation;
void circle(Canvas canvas, Rect rect, double value) {
final double opacity = (1.0 - (value / 4.0)).clamp(0.0, 0.2);
final Color _color = color.withOpacity(opacity);
final double size = rect.width / 2;
final double area = size * size;
final double radius = math.sqrt(area * value / 4);
final Paint paint = Paint()..color = _color;
canvas.drawCircle(rect.center, radius, paint);
}
#override
void paint(Canvas canvas, Size size) {
final Rect rect = Rect.fromLTRB(0.0, 0.0, size.width, size.height);
for (int wave = 3; wave >= 0; wave--) {
circle(canvas, rect, wave + _animation.value);
}
}
#override
bool shouldRepaint(CirclePainter oldDelegate) => true;
}
To fit this effect you will use customBorder inside the inkwell.
customBorder:StadiumBorder()

Draggable feedback widget gets an offset when InteractiveViewer is zoomed in

I'm trying to Drag a widget on top of an InteractiveViewer.
The code works fine when the scale is 1.0.
However, if zoomed in, when I drag the circle:
the feedback widget is offset by a few pixels to the bottom right
when I let go, the circle moves up
Why does that happen?
Here's a demo illustrating what I'm talking about:
Below is the code of this app
import 'package:flutter/material.dart';
void main() {
runApp(MyApp());
}
class MyApp extends StatelessWidget {
// This widget is the root of your application.
#override
Widget build(BuildContext context) {
return MaterialApp(
title: 'Flutter Demo',
theme: ThemeData(
primarySwatch: Colors.blue,
),
home: MyHomePage(title: 'Flutter Demo Home Page'),
);
}
}
enum _Action { scale, pan }
class MyHomePage extends StatefulWidget {
MyHomePage({Key key, this.title}) : super(key: key);
final String title;
#override
_MyHomePageState createState() => _MyHomePageState();
}
const _defaultMarkerSize = 48.0;
class _MyHomePageState extends State<MyHomePage> {
Offset _pos = Offset.zero; // Position could go from -1 to 1 in both directions
_Action action; // pan or pinch, useful to know if we need to scale down pin
final _transformationController = TransformationController();
#override
Widget build(BuildContext context) {
final scale = _transformationController.value.getMaxScaleOnAxis();
final size = _defaultMarkerSize / scale;
return Scaffold(
appBar: AppBar(title: Text(widget.title)),
body: InteractiveViewer(
transformationController: _transformationController,
maxScale: 5,
minScale: 1,
child: Stack(
children: [
Center(child: Image.asset('image/board.png')),
DraggablePin(
pos: _pos,
size: size,
onDragEnd: (details) {
final matrix = Matrix4.inverted(_transformationController.value);
final height = AppBar().preferredSize.height;
final sceneY = details.offset.dy - height;
final viewportPoint = MatrixUtils.transformPoint(
matrix,
Offset(details.offset.dx, sceneY) + Offset(_defaultMarkerSize / 2, _defaultMarkerSize / 2),
);
final screenSize = MediaQuery.of(context).size;
final x = viewportPoint.dx * 2 / screenSize.width - 1;
final y = viewportPoint.dy * 2 / screenSize.height - 1;
setState(() {
_pos = Offset(x, y);
});
},
),
],
),
onInteractionStart: (details) {
// No need to call setState as we don't need to rebuild
action = null;
},
onInteractionUpdate: (details) {
if (action == null) {
if (details.scale == 1)
action = _Action.pan;
else
action = _Action.scale;
}
if (action == _Action.scale) {
// Need to resize the pins so that they keep the same size
setState(() {});
}
},
),
floatingActionButton: FloatingActionButton(
child: Icon(Icons.restore),
onPressed: () {
setState(() {
_pos = Offset.zero;
});
},
),
);
}
}
class DraggablePin extends StatelessWidget {
final Offset pos;
final double size;
final void Function(DraggableDetails) onDragEnd;
const DraggablePin({this.pos, this.size, this.onDragEnd, Key key}) : super(key: key);
#override
Widget build(BuildContext context) {
final offset = size / 2;
Widget pinWidget = Pin(size);
final screenSize = MediaQuery.of(context).size;
final height = screenSize.height - AppBar().preferredSize.height;
pinWidget = Draggable(
child: pinWidget,
feedback: Pin(_defaultMarkerSize),
childWhenDragging: Container(),
onDragEnd: onDragEnd,
);
return Positioned(
top: pos.dy * height / 2 + height / 2 - offset,
left: pos.dx * screenSize.width / 2 + screenSize.width / 2 - offset,
child: pinWidget,
);
}
}
class Pin extends StatelessWidget {
const Pin(this.size, {Key key}) : super(key: key);
final double size;
#override
Widget build(BuildContext context) {
return Material(
color: Colors.transparent,
child: Center(
child: Ink(
decoration: ShapeDecoration(
color: Colors.green,
shape: const CircleBorder(),
),
child: IconButton(
constraints: BoxConstraints.tightFor(width: size, height: size),
padding: const EdgeInsets.all(0),
iconSize: size,
splashRadius: size / 2,
splashColor: Colors.white,
icon: Container(),
onPressed: () {},
),
),
),
);
}
}

flutter design Curves layout as single widget

Here solved question about design this layout.
I have a problem to using that, because of this curve on right of screen is not widget and when I want to have some other widgets in green side, I can't, because designed curve is not widget its clipped from green layout.
Suppose I want to have this curve on right of screen and some widget in below of that.
Source code
import 'package:flutter/material.dart';
void main() {
runApp(MaterialApp(home: Scaffold(body: Test())));
}
class Test extends StatefulWidget {
#override
_TestState createState() => _TestState();
}
class _TestState extends State<Test> {
double _height = 0.0;
double _width = 0.0;
double _rightPadding = 2.0;
double _btnSize = 25.0;
double _btnY = 0.0;
#override
Widget build(BuildContext context) {
if (_height == 0.0)
setState(() {
_height = MediaQuery.of(context).size.height;
_width = MediaQuery.of(context).size.width;
_btnY = _height / 3 * 2;
});
return _height == 0.0
? Container()
: Stack(
children: <Widget>[
Container(
color: Colors.white,
),
CustomPaint(
size: Size(_width - _rightPadding, _height),
painter: CurvedPainter(_btnSize, _btnY),
),
],
);
}
}
class CurvedPainter extends CustomPainter {
CurvedPainter(this.btnSize, this.btnY);
final double btnSize;
final double btnY;
#override
void paint(Canvas canvas, Size size) {
Path path = Path();
path.moveTo(0.0, 0.0);
path.lineTo(size.width, 0.0);
path.lineTo(size.width, btnY - btnSize * 2);
path.cubicTo(size.width, btnY - btnSize * 0.3, size.width - btnSize * 0.95, btnY - btnSize * 0.9, size.width - btnSize, btnY);
path.cubicTo(size.width - btnSize * 0.95, btnY + btnSize * 0.9, size.width, btnY + btnSize * 0.3, size.width, btnY + btnSize * 2);
path.lineTo(size.width, size.height);
path.lineTo(0.0, size.height);
path.lineTo(0.0, 0.0);
canvas.drawPath(
path,
Paint()
..color = Colors.transparent
..style = PaintingStyle.fill);
}
#override
bool shouldRepaint(CurvedPainter oldDelegate) => oldDelegate.btnY != btnY;
}
I want to use right curve as an widget on top of all widgets, without having green side, you suppose green side is as ListView.
It seems that you need something like this given in the image below.
Update: Dragging control added
import 'dart:math' as math;
import 'package:flutter/material.dart';
void main() => runApp(MyApp());
class MyApp extends StatelessWidget {
#override
Widget build(BuildContext context) {
return MaterialApp(
debugShowCheckedModeBanner: false,
title: 'Flutter Demo',
theme: ThemeData(
primarySwatch: Colors.blue,
),
home: Scaffold(
body: Test(),
),
);
}
}
class Test extends StatefulWidget {
#override
_TestState createState() => _TestState();
}
enum dragType { vertical, horizontal }
class _TestState extends State<Test> {
final double _btnSize = 48.0;
final double _rightPadding = 0;
double _extraOffcet = 0;
double _btnY;
double _currentX;
double _currentY;
double _height;
double _width;
bool _isHorizontalActive = true;
bool _isVerticalActive = true;
#override
void initState() {
WidgetsBinding.instance.addPostFrameCallback(_afterLayout);
super.initState();
}
_afterLayout(_) {
_height = MediaQuery.of(context).size.height;
_width = MediaQuery.of(context).size.width;
_btnY = _height / 2;
setState(() {});
}
_onDrag(details) {
_updateCoordinates(
details.globalPosition.dx,
details.globalPosition.dy,
);
}
_updateCoordinates(double x, double y) {
setState(() {
if (_isHorizontalActive) {
_updateX(x);
}
if (_isVerticalActive) {
_updateY(y);
}
});
}
_updateX(x) {
var dx = _currentX - x;
_currentX = x;
_extraOffcet = _extraOffcet + dx;
_extraOffcet = math.max(_extraOffcet, _rightPadding);
_extraOffcet = math.min(_extraOffcet, _width - _btnSize);
}
_updateY(y) {
var dy = _currentY - y;
_currentY = y;
_btnY = _btnY - dy;
_btnY = math.max(_btnY, _btnSize);
_btnY = math.min(_btnY, _height - _btnSize);
}
_listItem(String text, double height) {
return Container(
height: height,
child: Text(text, style: TextStyle(fontSize: 20.0)),
);
}
#override
Widget build(BuildContext context) {
return _height == null
? Container()
: Stack(
children: <Widget>[
Container(
padding: EdgeInsets.only(right: 30),
color: Colors.blue,
height: double.infinity,
width: double.infinity,
child: Column(mainAxisAlignment: MainAxisAlignment.center, children: [
_buildSwitch(type: dragType.vertical),
_buildSwitch(type: dragType.horizontal),
...List.generate(
4,
(index) => _listItem('inside', 80),
),
]),
),
Positioned(
right: _extraOffcet + _rightPadding,
child: CustomPaint(
size: Size(_btnSize, _height),
painter: CurvedPainter(_btnSize, _btnY),
),
),
Positioned(
top: _btnY - _btnSize / 2 + 5,
right: _extraOffcet + _rightPadding + 5,
child: GestureDetector(
onPanDown: (details) {
_currentX = details.globalPosition.dx;
_currentY = details.globalPosition.dy;
},
onPanStart: _onDrag,
onPanUpdate: _onDrag,
child: Material(
type: MaterialType.circle,
color: Colors.white,
elevation: 8.0,
child: Container(
width: _btnSize - 10,
height: _btnSize - 10,
child: Icon(Icons.add),
),
),
),
),
Positioned(
top: 0,
left: _width - _extraOffcet,
child: Container(
color: Colors.white,
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
crossAxisAlignment: CrossAxisAlignment.start,
children: List.generate(8, (index) => _listItem('from right', 20)),
),
height: _height,
width: _width,
),
),
],
);
}
SwitchListTile _buildSwitch({dragType type}) {
Function onChange;
String titleText, sutitleText;
bool value;
if (type == dragType.horizontal) {
value = _isHorizontalActive;
titleText = 'Horizontal dragging';
onChange = (newValue) => setState(() => _isHorizontalActive = newValue);
} else {
value = _isVerticalActive;
titleText = 'Vertical dragging';
onChange = (newValue) => setState(() => _isVerticalActive = newValue);
}
sutitleText = value ? 'active' : 'disabled';
return SwitchListTile(
value: value,
onChanged: onChange,
title: Text(titleText),
subtitle: Text(sutitleText),
);
}
}
class CurvedPainter extends CustomPainter {
CurvedPainter(this.btnSize, this.btnY);
final double btnSize;
final double btnY;
#override
void paint(Canvas canvas, Size size) {
var halfBtnSize = btnSize / 2;
var xMax = size.width;
var yMax = size.height;
var path = Path()
..moveTo(halfBtnSize, yMax)
..lineTo(halfBtnSize, btnY + halfBtnSize * 2)
..cubicTo(halfBtnSize, btnY + halfBtnSize, 0, btnY + halfBtnSize, 0, btnY)
..cubicTo(0, btnY - halfBtnSize, halfBtnSize, btnY - halfBtnSize, halfBtnSize,
btnY - halfBtnSize * 2)
..lineTo(halfBtnSize, 0)
..lineTo(xMax, 0)
..lineTo(xMax, yMax);
canvas.drawPath(
path,
Paint()
..color = Colors.white
..style = PaintingStyle.fill);
}
#override
bool shouldRepaint(CurvedPainter oldDelegate) => oldDelegate.btnY != btnY;
}

Flutter manage dx of Offset by swiping to right or left

in Flutter application i want to divide width of screen to 10 part and when user swipe to right or left i could detect each part of this swipe, for example
after divide screen to 10 part i have a variable named screenParts as double which that has 0.0 by default, when user swipe to right the variable value should be plus 1 part and swipe to left should be minus the variable value minus
this variable value should be between 0.0 and 1, you could consider Tween<double>
double screenParts = 0.0;
final double screenWidth = MediaQuery.of(context).size.width / 10;
i want to use this value inside into this part of code:
return GestureDetector(
onPanUpdate: (details) {
if (details.delta.dx > 0) {
if (screenParts / screenWidth == 0) screenParts = screenParts += 0.1;
} else if (details.delta.dx < 0) {
if (screenParts / screenWidth == 0) screenParts = screenParts -= 0.1;
}
setState(() {});
},
child: SafeArea(
child: FadeTransition(
opacity: CurvedAnimation(parent: animation, curve: Curves.fastLinearToSlowEaseIn),
child: SlideTransition(
position: Tween<Offset>(
begin: animateDirection,
end: Offset(screenParts, 0), //<---- this part
).animate(CurvedAnimation(
parent: animation,
curve: Curves.fastLinearToSlowEaseIn,
)),
// ignore: void_checks
child: Material(elevation: 8.0, child: child)),
),
),
);
it means i try to manage dx of Offset by screenParts value with swiping to right or left
is this what you are looking for?
import 'package:flutter/material.dart';
void main() => runApp(MyApp());
class MyApp extends StatelessWidget {
#override
Widget build(BuildContext context) {
return MaterialApp(
title: 'Flutter Demo',
theme: ThemeData(
primarySwatch: Colors.blue,
),
home: Scaffold(
body: SafeArea(
child: MyHomePage(),
),
),
);
}
}
class MyHomePage extends StatefulWidget {
#override
_MyHomePageState createState() => _MyHomePageState();
}
class _MyHomePageState extends State<MyHomePage>
with SingleTickerProviderStateMixin {
double position = 0.0;
#override
void initState() {
super.initState();
}
void handleXChange(double deltaX) {
setState(() {
position = deltaX / MediaQuery.of(context).size.width;
});
}
#override
Widget build(BuildContext context) {
return Stack(
children: <Widget>[
Text(position.toString()),
DragArea(
handleXChange: handleXChange,
),
],
);
}
}
class DragArea extends StatefulWidget {
const DragArea({Key key, #required this.handleXChange}) : super(key: key);
final void Function(double newX) handleXChange;
#override
_DragAreaState createState() => _DragAreaState();
}
class _DragAreaState extends State<DragArea> {
double initX;
void onPanStart(DragStartDetails details) {
initX = details.globalPosition.dx;
}
void onPanUpdate(DragUpdateDetails details) {
var x = details.globalPosition.dx;
var deltaX = x - initX;
widget.handleXChange(deltaX);
}
#override
Widget build(BuildContext context) {
return GestureDetector(
onPanStart: onPanStart,
onPanUpdate: onPanUpdate,
child: Container(
decoration: BoxDecoration(
border: Border.all(
color: Colors.blueAccent,
),
),
),
);
}
}