flutter design Curves layout as single widget - flutter

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;
}

Related

How to create a Sine Wave Clipper in Flutter?

I want to create a Custom Clipper like the image below.
Finally figured it out.
Clip Widget
ClipPath(
child: Container(
height: 8,
width: double.infinity,
color: Colors.white,
),
clipper: WaveClipper(),
),
Wave Clipper
class WaveClipper extends CustomClipper<Path> {
#override
Path getClip(Size size) {
var path = Path();
path.lineTo(0, size.height);
var x = 0.0;
var numberOfWaves = 30;
var increment = size.width / numberOfWaves;
bool startFromTop = false;
while (x < size.width) {
if (startFromTop) {
path.lineTo(x, 0);
path.cubicTo(x + increment / 2, 0, x + increment / 2, size.height,
x + increment, size.height);
} else {
path.lineTo(x, size.height);
path.cubicTo(x + increment / 2, size.height, x + increment / 2, 0,
x + increment, 0);
}
x += increment;
startFromTop = !startFromTop;
}
path.lineTo(size.width, 0);
path.lineTo(0, 0);
path.close();
return path;
}
#override
bool shouldReclip(CustomClipper<Path> oldClipper) => false;
}
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',
home: MyHomePage(title: 'Flutter'),
);
}
}
class MyHomePage extends StatefulWidget {
MyHomePage({Key? key, required this.title}) : super(key: key);
final String title;
#override
_MyHomePageState createState() => _MyHomePageState();
}
class _MyHomePageState extends State<MyHomePage> {
#override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: Text(
widget.title,
style: TextStyle(color: Colors.white),
),
),
body: Padding(
padding: const EdgeInsets.all(30.0),
child: ClipPath(
child: Container(
width: MediaQuery.of(context).size.width,
height: 200,
color: Colors.grey,
),
clipper: CustomClipPath(),
),
),
);
}
}
class CustomClipPath extends CustomClipper<Path> {
Path getClip(Size size) {
var path = new Path();
path.lineTo(0.0, 40.0);
path.lineTo(0.0, size.height);
path.lineTo(size.width, size.height);
path.lineTo(size.width, 40.0);
for (int i = 0; i < 10; i++) {
if (i % 2 == 0) {
path.quadraticBezierTo(
size.width - (size.width / 16) - (i * size.width / 8),
0.0,
size.width - ((i + 1) * size.width / 8),
size.height - 160);
} else {
path.quadraticBezierTo(
size.width - (size.width / 16) - (i * size.width / 8),
size.height - 120,
size.width - ((i + 1) * size.width / 8),
size.height - 160);
}
}
path.lineTo(0.0, 40.0);
path.close();
return path;
}
#override
bool shouldReclip(CustomClipper<Path> oldClipper) => false;
}

How to fit any curved to screen? - Flutter

I am new to flutter and would like to learn how to fit any screen curved from photo.
I'am not able to fit curved to the left, how to fit this shape to every possible screen?
I don't know why but the left side won't fit
I needs help or hints.
class StartAfterRegister extends StatelessWidget {
static Route route() {
return MaterialPageRoute<void>(builder: (_) => StartAfterRegister());
}
#override
Widget build(BuildContext context) {
return Scaffold(body: _profilePage(context));
}
}
Widget _profilePage(BuildContext context) {
return SafeArea(
child: Center(
child: Column(
children: [
const SizedBox(height: 452),
_curved(),
],
),
),
);
// });
}
Widget _curved() {
return Container(
// padding: const EdgeInsets.all(8.0),
// child: Padding(
// margin: const EdgeInsets.only(left: 0.0, right: 28.0),
// const EdgeInsets.fromLTRB(0, 10, 25, 10),
child: CustomPaint(
size: Size(600, (320* 1.1617600000000001).toDouble()),
//You can Replace [WIDTH] with your desired width for
// Custom Paint and height will be calculated automatically
painter: RPSCustomPainter(),
),
);
}
Code was generated with this page :https://fluttershapemaker.com/
I don't know where I made a mistake, wrong portions?
import 'dart:ui' as ui;
import 'package:flutter/cupertino.dart';
import 'package:flutter_login/components/theme/colors.dart';
class RPSCustomPainter extends CustomPainter {
#override
void paint(Canvas canvas, Size size) {
Path path_0 = Path();
path_0.moveTo(size.width*1.071853,size.height*1.035738);
path_0.lineTo(size.width*0.07250032,size.height*1.035738);
path_0.lineTo(size.width*0.07250032,size.height*0.3868996);
path_0.cubicTo(size.width*0.07241850,size.height*0.3325917,size.width*0.09748933,size.height*0.2804976,size.width*0.1421620,size.height*0.2421464);
path_0.cubicTo(size.width*0.1969324,size.height*0.1950013,size.width*0.2692701,size.height*0.1829006,size.width*0.3079311,size.height*0.1816967);
path_0.cubicTo(size.width*0.5894098,size.height*0.1729377,size.width*0.8344350,size.height*0.1919213,size.width*0.9302565,size.height*0.1649115);
path_0.cubicTo(size.width*0.9696772,size.height*0.1538054,size.width*1.040485,size.height*0.1192928,size.width*1.071848,size.height*0.03573744);
path_0.cubicTo(size.width*1.069587,size.height*0.2428419,size.width*1.074102,size.height*0.8286341,size.width*1.071853,size.height*1.035738);
path_0.close();
Paint paint_0_fill = Paint()..style=PaintingStyle.fill;
paint_0_fill.color = teal ;
canvas.drawPath(path_0,paint_0_fill);
}
#override
bool shouldRepaint(covariant CustomPainter oldDelegate) {
return true;
}
**There is a problem in your custom shape if you want to create more shape be sure you set in widht and size **
like this
simply add this code to custom painter
class RPSCustomPainter extends CustomPainter {
#override
void paint(Canvas canvas, Size size) {
Paint paint_0 = new Paint()
..color = Color.fromARGB(255, 33, 150, 243)
..style = PaintingStyle.fill
..strokeWidth = 1;
Path path_0 = Path();
path_0.moveTo(size.width, size.height);
path_0.lineTo(size.width * 0.0012500, size.height * 0.9942857);
path_0.lineTo(size.width * 0.0012500, size.height * 0.4271429);
path_0.quadraticBezierTo(size.width * 0.0740625, size.height * 0.2807143,
size.width * 0.1950000, size.height * 0.2800000);
path_0.quadraticBezierTo(size.width * 0.8515625, size.height * 0.3560714,
size.width * 0.9962500, size.height * 0.2828571);
path_0.quadraticBezierTo(size.width * 0.9990625, size.height * 0.2850000,
size.width, size.height * 0.2857143);
path_0.quadraticBezierTo(size.width * 1.0084375, size.height * 0.4992857,
size.width, size.height);
path_0.close();
canvas.drawPath(path_0, paint_0);
}
#override
bool shouldRepaint(covariant CustomPainter oldDelegate) {
return true;
}
}
**add this in your child **
import 'package:flutter/material.dart';
class Demo extends StatefulWidget {
const Demo({Key? key}) : super(key: key);
#override
_DemoState createState() => _DemoState();
}
class _DemoState extends State<Demo> {
#override
Widget build(BuildContext context) {
/// this is the width of screen
var width = MediaQuery.of(context).size.width;
return Scaffold(
appBar: AppBar(),
body: Stack(
children: [
Align(
alignment: Alignment.bottomLeft,
child: Container(
color: Colors.red,
width: width,
child: CustomPaint(
size: Size((width).toDouble(), (width * 0.875).toDouble()),
painter: RPSCustomPainter(),
),
))
],
),
);
}
}

How to keep clipped area constant while resizing a container dynamically in Flutter?

I have a following code which has an image that is cropped to 150px by default. What I want is to make this cropped area remain constant while resizing the container using the resize handle.
I want the resize happen like so: (See how the cropped area is constant i.e. it doesn't move. It's as if I'm resizing an image of cropped width & height)
But in my code, it works like this:
See how the cropped area also gets cropped when resizing. I want to keep the cropped area constant(not move) while resizing. Any help would be greatly appreciated.
Here's my code:
import 'package:flutter/material.dart';
import 'package:flutter/rendering.dart';
void main() {
debugPaintSizeEnabled = true;
runApp(MyApp());
}
class MyApp extends StatelessWidget {
#override
Widget build(BuildContext context) {
return MaterialApp(
title: 'Flutter Demo',
home: Test(),
);
}
}
class Test extends StatefulWidget {
#override
_TestState createState() => _TestState();
}
class _TestState extends State<Test> {
double _cropWidth = 150;
double _width = 300;
#override
Widget build(BuildContext context) {
return Scaffold(
body: Stack(
children: [
Positioned(
top: 0,
left: 0,
child: ClipRect(
clipper: Cropper(
height: 200,
width: _cropWidth,
top: 0,
left: 0,
),
child: Image.network(
"https://picresize.com/images/t1rsz_pexels-photo-1108099.jpg",
height: 200,
width: _width,
fit: BoxFit.fill,
),
),
),
Positioned(
top: 0,
left: 0,
child: Container(
height: 200,
width: _cropWidth,
child: Stack(
children: <Widget>[
Positioned(
top: 80,
left: _cropWidth - 20,
child: GestureDetector(
onPanUpdate: (d) {
_cropWidth = _cropWidth + d.delta.dx;
_width = _width + d.delta.dx;
setState(() {});
},
child:
Container(height: 20, width: 20, color: Colors.blue),
),
)
],
),
),
),
],
),
);
}
}
class Cropper extends CustomClipper<Rect> {
final double left;
final double top;
final double width;
final double height;
const Cropper({
this.left = 0.0,
this.top = 0.0,
this.width = 0.0,
this.height = 0.0,
});
#override
Rect getClip(Size a) {
return Rect.fromLTWH(left, top, width, height);
}
#override
bool shouldReclip(CustomClipper<Rect> old) => true;
}
There is no problem in cropWidth. The problem is in the original image width.
You may notice the area cropWidth and width should be related in some ratio. You should calculate crop width from width (or reverse).
In your problem, the ratio seems to be a fixed number:
double _ratio = 0.5;
double _cropWidth = 150;
double _width = 300;
...
onPanUpdate: (d) {
_cropWidth = _cropWidth + d.delta.dx;
_width = _cropWidth / _ratio;
setState(() {});
}
...
If you want, you can change the crop ratio by modifying it
Positioned(
top: 120,
left: _cropWidth - 20,
child: GestureDetector(
onPanUpdate: (d) {
_cropWidth = _cropWidth + d.delta.dx;
_ratio = _cropWidth / _width;
setState(() {});
},
child:
Container(height: 20, width: 20, color: Colors.green),
),
),
Is this what you want?
import 'package:flutter/material.dart';
import 'package:flutter/rendering.dart';
void main() {
debugPaintSizeEnabled = true;
runApp(MyApp());
}
class MyApp extends StatelessWidget {
#override
Widget build(BuildContext context) {
return MaterialApp(
title: 'Flutter Demo',
home: Test(),
);
}
}
class Test extends StatefulWidget {
#override
_TestState createState() => _TestState();
}
class _TestState extends State<Test> {
double _cropWidth = 300;
double _width = 300;
#override
Widget build(BuildContext context) {
return Scaffold(
body: Stack(
children: [
Positioned(
top: 0,
left: 0,
child: ClipRect(
clipper: Cropper(
height: 200,
width: _cropWidth,
top: 0,
left: 0,
),
child: Image.network(
"https://hips.hearstapps.com/hmg-prod.s3.amazonaws.com/images/dog-puppy-on-garden-royalty-free-image-1586966191.jpg",
height: 200,
width: _width,
fit: BoxFit.fill,
),
),
),
Positioned(
top: 0,
left: 0,
child: Container(
height: 200,
width: _cropWidth,
child: Stack(
children: <Widget>[
Positioned(
top: 80,
left: _cropWidth - 20,
child: GestureDetector(
onPanUpdate: (d) {
_cropWidth = _cropWidth + d.delta.dx;
_width = _width + d.delta.dx;
setState(() {});
},
child:
Container(height: 20, width: 20, color: Colors.blue),
),
)
],
),
),
),
],
),
);
}
}
class Cropper extends CustomClipper<Rect> {
final double left;
final double top;
final double width;
final double height;
const Cropper({
this.left = 0.0,
this.top = 0.0,
this.width = 0.0,
this.height = 0.0,
});
#override
Rect getClip(Size a) {
return Rect.fromLTWH(left, top, width, height);
}
#override
bool shouldReclip(CustomClipper<Rect> old) => true;
}
i am also try this to do thing, may be its helpful for you
import 'package:flutter/material.dart';
import 'package:flutter/rendering.dart';
void main() {
debugPaintSizeEnabled = true;
runApp(MyApp());
}
class MyApp extends StatelessWidget {
#override
Widget build(BuildContext context) {
return MaterialApp(
title: 'Flutter Demo',
home: Test(),
);
}
}
class Test extends StatefulWidget {
#override
_TestState createState() => _TestState();
}
class _TestState extends State<Test> {
double _cropWidth = 350;
double _width = 350;
#override
Widget build(BuildContext context) {
return Scaffold(
body: Stack(
children: [
ClipRect(
clipper: Cropper(
height: 200,
width: _cropWidth,
),
child: Image.network(
"https://images.pexels.com/photos/1108099/pexels-photo-1108099.jpeg?auto=compress&cs=tinysrgb&dpr=1&w=500",
height: 200,
width: _width,
fit: BoxFit.fill,
),
),
Container(
height: 200,
width: _cropWidth,
child: Stack(
children: <Widget>[
Positioned(
top: 80,
left: _cropWidth - 20,
child: GestureDetector(
onPanUpdate: (d) {
_cropWidth = _cropWidth + d.delta.dx;
_width = _width + d.delta.dx;
setState(() {});
},
child:
Container(height: 20, width: 20, color: Colors.blue),
),
)
],
),
),
],
),
);
}
}
class Cropper extends CustomClipper<Rect> {
final double left;
final double top;
final double width;
final double height;
const Cropper({
this.left = 0.0,
this.top = 0.0,
this.width = 0.0,
this.height = 0.0,
});
#override
Rect getClip(Size a) {
return Rect.fromLTWH(left, top, width, height);
}
#override
bool shouldReclip(CustomClipper<Rect> old) => true;
}

Custom widget raise event not working property in 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.

Flutter: How to move, rotate and zoom the container?

I want to make a container which can be dragged around, zoom and rotate. I am able to achieve a zoom. Below is my code:
//variable declaration
double _scale = 1.0;
double _previousScale;
var yOffset = 400.0;
var xOffset = 50.0;
var rotation = 0.0;
var lastRotation = 0.0;
//build method
#override
Widget build(BuildContext context) {
return Scaffold(
body: Center(
child: Center(
child: GestureDetector(
onScaleStart: (scaleDetails) {
_previousScale = _scale;
print(' scaleStarts = ${scaleDetails.focalPoint}');
},
onScaleUpdate: (scaleUpdates){
//ScaleUpdateDetails
rotation += lastRotation - scaleUpdates.rotation;
lastRotation = scaleUpdates.rotation;
print("lastRotation = $lastRotation");
print(' scaleUpdates = ${scaleUpdates.scale} rotation = ${scaleUpdates.rotation}');
setState(() => _scale = _previousScale * scaleUpdates.scale);
},
onScaleEnd: (scaleEndDetails) {
_previousScale = null;
print(' scaleEnds = ${scaleEndDetails.velocity}');
},
child:
Transform(
transform: Matrix4.diagonal3( Vector3(_scale, _scale, _scale))..rotateZ(rotation * math.pi/180.0),
alignment: FractionalOffset.center,
child: Container(
height: 200.0,
width: 200.0,
color: Colors.red,
),
)
,
),
),
),
);
}
Currently, there is no rotation and I can't move the container around.
use Matrix Gesture Detector package1 package, here you have the basic sample:
MatrixGestureDetector(
onMatrixUpdate: (m, tm, sm, rm) {
setState(() {
matrix = n;
});
},
child: Transform(
transform: matrix,
child: ....
),
),
for more sample code refer to example folder that contains 6 demos
You can use RotatedBox widget (for rotation) along with InteractiveViewer widget(for zoom in and zoom out).
panEnabled property in InteractiveViewer widget used for moving the container
Scaffold(
backgroundColor: Colors.black,
body: Center(
child: RotatedBox(
quarterTurns: 1,
child: InteractiveViewer(
boundaryMargin: EdgeInsets.zero,
minScale: 1,
maxScale: 4,
child: Container(
height: 200,
width: 200,
color: Colors.blue,
),
),
),
),
),
You can also use photo_viewer instead of matrix_gesture_detector. More info in this answer: https://stackoverflow.com/a/62426232/2942294
i had same issue. the problem is solved by Matrix gesture detetctor package. but i have solved this by another case:- the whole code is here.
// #dart=2.9
import 'package:flutter/material.dart';
void main() {
runApp(MyApp());
}
class MyApp extends StatefulWidget {
const MyApp({Key key}) : super(key: key);
#override
_MyAppState createState() => _MyAppState();
}
class _MyAppState extends State<MyApp> {
#override
Widget build(BuildContext context) {
return MaterialApp(
home: HomePage(),
);
}
}
class ContainerList {
double height;
double width;
double scale;
double rotation;
double xPosition;
double yPosition;
ContainerList({
this.height,
this.rotation,
this.scale,
this.width,
this.xPosition,
this.yPosition,
});
}
class HomePage extends StatefulWidget {
const HomePage({Key key}) : super(key: key);
#override
_HomePageState createState() => _HomePageState();
}
class _HomePageState extends State<HomePage> {
List<ContainerList> list = [];
Offset _initPos;
Offset _currentPos = Offset(0, 0);
double _currentScale;
double _currentRotation;
Size screen;
#override
void initState() {
screen = Size(400, 500);
list.add(ContainerList(
height: 200.0,
width: 200.0,
rotation: 0.0,
scale: 1.0,
xPosition: 0.1,
yPosition: 0.1,
));
super.initState();
}
#override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(),
body: Container(
padding: EdgeInsets.symmetric(horizontal: 10.0, vertical: 10.0),
height: 500.0,
color: Colors.blue.withOpacity(0.8),
width: double.infinity,
child: Stack(
children: list.map((value) {
return GestureDetector(
onScaleStart: (details) {
if (value == null) return;
_initPos = details.focalPoint;
_currentPos = Offset(value.xPosition, value.yPosition);
_currentScale = value.scale;
_currentRotation = value.rotation;
},
onScaleUpdate: (details) {
if (value == null) return;
final delta = details.focalPoint - _initPos;
final left = (delta.dx / screen.width) + _currentPos.dx;
final top = (delta.dy / screen.height) + _currentPos.dy;
setState(() {
value.xPosition = Offset(left, top).dx;
value.yPosition = Offset(left, top).dy;
value.rotation = details.rotation + _currentRotation;
value.scale = details.scale * _currentScale;
});
},
child: Stack(
children: [
Positioned(
left: value.xPosition * screen.width,
top: value.yPosition * screen.height,
child: Transform.scale(
scale: value.scale,
child: Transform.rotate(
angle: value.rotation,
child: Container(
height: value.height,
width: value.width,
child: FittedBox(
fit: BoxFit.fill,
child: Listener(
onPointerDown: (details) {
// if (_inAction) return;
// _inAction = true;
// _activeItem = val;
_initPos = details.position;
_currentPos =
Offset(value.xPosition, value.yPosition);
_currentScale = value.scale;
_currentRotation = value.rotation;
},
onPointerUp: (details) {
// _inAction = false;
},
child: Container(
height: value.height,
width: value.width,
color: Colors.red,
),
// child: Image.network(value.name),
),
),
),
),
),
)
],
),
);
}).toList(),
),
),
);
}
}
so now i can khow the rotation angle value and scale value also. in matrix gesture detector package you can not khow about this values.
Had the same problem, here is my solution:
https://stackoverflow.com/a/74165192/12098106
import 'package:flutter/material.dart';
// -------------------------------------------------------------------
// THE ITEM TO BE DRAWN
// -------------------------------------------------------------------
class DrawContainer {
Color color;
Offset offset;
double width;
double height;
double scale;
double angle;
late double _baseScaleFactor;
late double _baseAngleFactor;
DrawContainer(this.color, this.offset, this.width, this.height, this.scale,
this.angle) {
onScaleStart();
}
onScaleStart() {
_baseScaleFactor = scale;
_baseAngleFactor = angle;
}
onScaleUpdate(double scaleNew) =>
scale = (_baseScaleFactor * scaleNew).clamp(0.5, 5);
onRotateUpdate(double angleNew) => angle = _baseAngleFactor + angleNew;
}
// -------------------------------------------------------------------
// APP
// -------------------------------------------------------------------
void main() {
runApp(const MaterialApp(home: GestureTest()));
}
class GestureTest extends StatefulWidget {
const GestureTest({Key? key}) : super(key: key);
#override
// ignore: library_private_types_in_public_api
_GestureTestState createState() => _GestureTestState();
}
// -------------------------------------------------------------------
// APP STATE
// -------------------------------------------------------------------
class _GestureTestState extends State<GestureTest> {
final List<DrawContainer> containers = [
DrawContainer(Colors.red, const Offset(50, 50), 100, 100, 1.0, 0.0),
DrawContainer(Colors.yellow, const Offset(100, 100), 200, 100, 1.0, 0.0),
DrawContainer(Colors.green, const Offset(150, 150), 50, 100, 1.0, 0.0),
];
void onGestureStart(DrawContainer e) => e.onScaleStart();
onGestureUpdate(DrawContainer e, ScaleUpdateDetails d) {
e.offset = e.offset + d.focalPointDelta;
if (d.rotation != 0.0) e.onRotateUpdate(d.rotation);
if (d.scale != 1.0) e.onScaleUpdate(d.scale);
setState(() {}); // redraw
}
#override
Widget build(BuildContext context) {
return SafeArea(
child: Scaffold(
body: SizedBox(
height: double.infinity,
width: double.infinity,
child: Stack(
children: [
...containers.map((e) {
return GestureDetector(
onScaleStart: (details) {
// detect two fingers to reset internal factors
if (details.pointerCount == 2) {
onGestureStart(e);
}
},
onScaleUpdate: (details) => onGestureUpdate(e, details),
child: DrawWidget(e));
}).toList(),
],
),
),
));
}
}
// -------------------------------------------------------------------
// POSITION, ROTATE AND SCALE THE WIDGET
// -------------------------------------------------------------------
class DrawWidget extends StatelessWidget {
final DrawContainer e;
const DrawWidget(this.e, {Key? key}) : super(key: key);
#override
Widget build(BuildContext context) {
return Stack(
children: [
Positioned(
left: e.offset.dx,
top: e.offset.dy,
child: Transform.rotate(
angle: e.angle,
child: Transform.scale(
scale: e.scale,
child: Container(
height: e.width,
width: e.height,
color: e.color,
),
),
),
),
],
);
}
}