Draw lines with flutter - dart

Is there any way to display skew borders at the top and bottom?
I came up with the solution below by using two images (top_layout and bottom_layout.png). Is there any other way to make those color bars with shadows without using static images?
return Container(
color: const Color.fromARGB(255, 236, 0, 140),
child: Container(
padding: const EdgeInsets.all(8.0),
child: Container(
color: Colors.white,
margin:
EdgeInsets.only(top: 60.0, bottom: 20.0, left: 15.0, right: 15.0),
child: Stack(
children: <Widget>[
Positioned.fill(
child: Image.asset(
"assets/imgs/top_layout.png",
fit: BoxFit.fitWidth,
alignment: Alignment.topCenter,
),
),
Positioned.fill(
child: Image.asset(
"assets/imgs/xbottom_layout.png",
fit: BoxFit.fitWidth,
alignment: Alignment.bottomLeft,
),
),
],
),
),
),
);
}

How do draw lines in Flutter using the CustomPaint widget
To paint in Flutter you use the CustomPaint widget. The CustomPaint widget takes a CustomPainter object as a parameter. In that class you have to override the paint method, which gives you a canvas that you can paint on. Here is the code to draw the line in the image above.
#override
void paint(Canvas canvas, Size size) {
final p1 = Offset(50, 50);
final p2 = Offset(250, 150);
final paint = Paint()
..color = Colors.black
..strokeWidth = 4;
canvas.drawLine(p1, p2, paint);
}
Notes:
The drawLine method draws a line connecting the two points you give it.
An Offset is a pair of (dx, dy) doubles, offset from the top left corner of the CustomPaint widget.
Another option
You could do something similar with the drawPoints method using the PointMode.polygon option.
#override
void paint(Canvas canvas, Size size) {
final pointMode = ui.PointMode.polygon;
final points = [
Offset(50, 100),
Offset(150, 75),
Offset(250, 250),
Offset(130, 200),
Offset(270, 100),
];
final paint = Paint()
..color = Colors.black
..strokeWidth = 4
..strokeCap = StrokeCap.round;
canvas.drawPoints(pointMode, points, paint);
}
Context
Here is the main.dart code so that you can see it in context.
import 'dart:ui' as ui;
import 'package:flutter/material.dart';
void main() => runApp(MyApp());
class MyApp extends StatelessWidget {
#override
Widget build(BuildContext context) {
return MaterialApp(
home: Scaffold(
body: HomeWidget(),
),
);
}
}
class HomeWidget extends StatelessWidget {
#override
Widget build(BuildContext context) {
return Center(
child: CustomPaint( // <-- CustomPaint widget
size: Size(300, 300),
painter: MyPainter(),
),
);
}
}
class MyPainter extends CustomPainter { // <-- CustomPainter class
#override
void paint(Canvas canvas, Size size) {
// <-- Insert your painting code here.
}
#override
bool shouldRepaint(CustomPainter old) {
return false;
}
}
See also
See this article for my fuller answer.

In this case, you would like to use Custom Painter widget instead. You can draw the shape based on coordinates.
Refer this tutorial for more info.
Drawing Custom Shapes in Flutter using CustomPainter

Related

Flutter custom clipper with stack not working correctly

I really interested with flutter, and currently I'm trying to create a custom clipper with clipPart widget. but it's seems not working correctly.
here is my code
import 'package:flutter/material.dart';
void main() => runApp(MaterialApp(
title: "My App",
home: HomePage(),
debugShowCheckedModeBanner: false,
));
class HomePage extends StatelessWidget {
#override
Widget build(BuildContext context) {
return Scaffold(
body: Column(
children: <Widget>[
Container(
alignment: Alignment.topCenter,
color: Colors.teal,
child: HomeScreenTop(),
)
],
),
);
}
}
class HomeScreenTop extends StatefulWidget {
#override
_HomeScreenTopState createState() => _HomeScreenTopState();
}
class _HomeScreenTopState extends State<HomeScreenTop> {
#override
Widget build(BuildContext context) {
return Stack(
textDirection: TextDirection.ltr,
children: <Widget>[
Align(
alignment: Alignment.topCenter,
child: WaveContainer(),
),
],
);
}
}
class WaveContainer extends StatelessWidget {
#override
Widget build(BuildContext context) {
return ClipPath(
clipper: CustomShapeClipper(),
clipBehavior: Clip.antiAlias,
child: Container(
width: MediaQuery.of(context).size.width,
height: 400.0,
color: Colors.orange,
),
);
}
}
class CustomShapeClipper extends CustomClipper<Path> {
#override
getClip(Size size) {
print(size.height);
final Path path =Path();
path.lineTo(size.width / 2, size.height);
path.lineTo(size.width, 0);
path.close();
return path;
}
#override
bool shouldReclip(CustomClipper oldClipper) => true;
}
what I want to do is, create a triangle from top to bottom, as I know from the tutorial, Offset of x = 0 and y = 0 should be pointing to left-top corner of the page. So if create like this
path.lineTo(size.width / 2, size.height);
path.lineTo(size.width, 0);
the path should be drawing from left-top to middle-down to right-top and back to left-top
But what I've got when I try the code is like this
And, the stack is not working.. the triangle should isolated inside the container but why the custom clipper can exceed the bottom container??
I really confuse, Please help
Solved by change the emulator from Genymotion to iOS IphoneX emulator. Don't know why it show different behaviour between iOS and android, but I think iOS emulator is the right one..

How to rotate a Widget around a focal point using gestures in Flutter?

In Flutter, I can rotate a Widget using the Transform Widget. However, the rotation is around origin specified in the Transform widget properties rather than around the current focal point.
I tried modifying the Matrix by translating to the focal point, rotating, and then translating back.
import 'package:flutter/material.dart';
void main() => runApp(MyApp());
class MyApp extends StatelessWidget {
#override
Widget build(BuildContext context) {
return MaterialApp(
home: Scaffold(
body: new TransformContainer(),
),
);
}
}
class TransformContainer extends StatefulWidget {
const TransformContainer({
Key key,
}) : super(key: key);
#override
TransformContainerState createState() {
return new TransformContainerState();
}
}
class TransformContainerState extends State<TransformContainer> {
Matrix4 matrix = Matrix4.identity();
#override
Widget build(BuildContext context) {
return Stack(
children: <Widget>[
GestureDetector(
onTapDown: (details) {
matrix.translate(-details.globalPosition.dx, -details.globalPosition.dy);
matrix.rotateZ(0.174533);
matrix.translate(details.globalPosition.dx, details.globalPosition.dy);
setState(() {});
},
onDoubleTap: () {
setState(() {
matrix = Matrix4.identity();
});
},
child: Transform(
transform: matrix,
alignment: FractionalOffset.topLeft,
child: Container(
color: Colors.black54,
child: Center(
child: Container(
width: 320,
height: 320,
color: Colors.redAccent,
),
),
),
),
),
Positioned(
top: 64.0,
right: 64.0,
child: Container(
color: Colors.pinkAccent,
child: IconButton(
icon: Icon(Icons.refresh),
iconSize: 72.0,
color: Colors.white,
onPressed: () {
setState(() {
matrix = Matrix4.identity();
});
},
),
),
),
],
);
}
}
When you run the code and tap on the screen, the Widget is rotated around the origin. How can I make it rotate around the tap position?
Set the transform origin as center before applying rotation on the widget;
alignment: FractionalOffset.center

How to take screenshot of widget beyond the screen in flutter?

I am using RepaintBoundary to take the screenshot of the current widget which is a listView. But it only captures the content which is visible on the screen at the time.
RepaintBoundary(
key: src,
child: ListView(padding: EdgeInsets.only(left: 10.0),
scrollDirection: Axis.horizontal,
children: <Widget>[
Align(
alignment: Alignment(-0.8, -0.2),
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: listLabel(orientation),
)
),
Padding(padding: EdgeInsets.all(5.0)),
Align(
alignment: FractionalOffset(0.3, 0.5),
child: Container(
height: orientation == Orientation.portrait? 430.0: 430.0*0.7,
decoration: BoxDecoration(
border: Border(left: BorderSide(color: Colors.black))
),
//width: 300.0,
child:
Wrap(
direction: Axis.vertical,
//runSpacing: 10.0,
children: colWidget(orientation),
)
)
),
Padding(padding: EdgeInsets.all(5.0)),
Column(
mainAxisAlignment: MainAxisAlignment.center,
children: listLabel(orientation),
)
],
),
);
screenshot function:
Future screenshot() async {
RenderRepaintBoundary boundary = src.currentContext.findRenderObject();
ui.Image image = await boundary.toImage();
ByteData byteData = await image.toByteData(format: ui.ImageByteFormat.png);
Uint8List pngBytes = byteData.buffer.asUint8List();
print(pngBytes);
final directory = (await getExternalStorageDirectory()).path;
File imgFile =new File('$directory/layout2.pdf');
imgFile.writeAsBytes(pngBytes);
}
Is there any way, so that I can capture the whole listView, i.e., not only the content which is not visible on the screen but the scrollable content also. Or maybe if the whole widget is too large to fit in a picture, it can be captured in multiple images.
I achieve the solution of this problem using this package: Screenshot, that takes a screenshot of the entire widget. It's easy and simple, follow the steps on the PubDev or GitHub and you can make it work.
OBS: To take a full screenshot of the widget make sure that your widget is fully scrollable, and not just a part of it.
(In my case, i had a ListView inside a Container, and the package doesn't take the screenshot of all ListView because i have many itens on it, SO i have wrap my Container inside a SingleChildScrollView and add the NeverScrollableScrollPhysics physics in the ListView and it works! :D).
Screenshot of my screen
More details in this issue
This made me curious whether it was possible so I made a quick mock-up that shows it does work. But please be aware that by doing this you're essentially intentionally breaking the things flutter does to optimize, so you really shouldn't use it beyond where you absolutely have to.
Anyways, here's the code:
import 'dart:math';
import 'dart:ui' as ui;
import 'package:flutter/material.dart';
import 'package:flutter/rendering.dart';
void main() => runApp(MyApp());
class UiImagePainter extends CustomPainter {
final ui.Image image;
UiImagePainter(this.image);
#override
void paint(ui.Canvas canvas, ui.Size size) {
// simple aspect fit for the image
var hr = size.height / image.height;
var wr = size.width / image.width;
double ratio;
double translateX;
double translateY;
if (hr < wr) {
ratio = hr;
translateX = (size.width - (ratio * image.width)) / 2;
translateY = 0.0;
} else {
ratio = wr;
translateX = 0.0;
translateY = (size.height - (ratio * image.height)) / 2;
}
canvas.translate(translateX, translateY);
canvas.scale(ratio, ratio);
canvas.drawImage(image, new Offset(0.0, 0.0), new Paint());
}
#override
bool shouldRepaint(UiImagePainter other) {
return other.image != image;
}
}
class UiImageDrawer extends StatelessWidget {
final ui.Image image;
const UiImageDrawer({Key key, this.image}) : super(key: key);
#override
Widget build(BuildContext context) {
return CustomPaint(
size: Size.infinite,
painter: UiImagePainter(image),
);
}
}
class MyApp extends StatefulWidget {
#override
_MyAppState createState() => _MyAppState();
}
class _MyAppState extends State<MyApp> {
GlobalKey<OverRepaintBoundaryState> globalKey = GlobalKey();
ui.Image image;
#override
Widget build(BuildContext context) {
return MaterialApp(
home: Scaffold(
appBar: AppBar(),
body: image == null
? Capturer(
overRepaintKey: globalKey,
)
: UiImageDrawer(image: image),
floatingActionButton: image == null
? FloatingActionButton(
child: Icon(Icons.camera),
onPressed: () async {
var renderObject = globalKey.currentContext.findRenderObject();
RenderRepaintBoundary boundary = renderObject;
ui.Image captureImage = await boundary.toImage();
setState(() => image = captureImage);
},
)
: FloatingActionButton(
onPressed: () => setState(() => image = null),
child: Icon(Icons.remove),
),
),
);
}
}
class Capturer extends StatelessWidget {
static final Random random = Random();
final GlobalKey<OverRepaintBoundaryState> overRepaintKey;
const Capturer({Key key, this.overRepaintKey}) : super(key: key);
#override
Widget build(BuildContext context) {
return SingleChildScrollView(
child: OverRepaintBoundary(
key: overRepaintKey,
child: RepaintBoundary(
child: Column(
children: List.generate(
30,
(i) => Container(
color: Color.fromRGBO(random.nextInt(256), random.nextInt(256), random.nextInt(256), 1.0),
height: 100,
),
),
),
),
),
);
}
}
class OverRepaintBoundary extends StatefulWidget {
final Widget child;
const OverRepaintBoundary({Key key, this.child}) : super(key: key);
#override
OverRepaintBoundaryState createState() => OverRepaintBoundaryState();
}
class OverRepaintBoundaryState extends State<OverRepaintBoundary> {
#override
Widget build(BuildContext context) {
return widget.child;
}
}
What it's doing is making a scroll view that encapsulates the list (column), and making sure the repaintBoundary is around the column. With your code where you use a list, there's no way it can ever capture all the children as the list is essentially a repaintBoundary in and of itself.
Note in particular the 'overRepaintKey' and OverRepaintBoundary. You might be able to get away without using it by iterating through render children, but it makes it a lot easier.
There is a simple way
You need wrap SingleChildScrollView Widget to RepaintBoundary. just wrap your Scrollable widget (or his father) with SingleChildScrollView
SingleChildScrollView(
child: RepaintBoundary(
key: _globalKey
)
)

Flutter circle file image with clip oval

I want to clip an image that I extracted from the image picker plugin and it does not work with BoxDecoration.circle , so I want to clip it as circle with oval clipper. How to achive it ?
You can use CircleAvatar widget to display the obtained image to make it circular.
import 'dart:async';
import 'dart:io';
import 'package:flutter/material.dart';
import 'package:image_picker/image_picker.dart';
void main() => runApp(new MaterialApp(debugShowCheckedModeBanner: false, home: new MyApp()));
class MyApp extends StatefulWidget {
#override
_MyAppState createState() => new _MyAppState();
}
class _MyAppState extends State<MyApp> {
File _image;
Future getImage() async {
var image = await ImagePicker.pickImage(source: ImageSource.camera);
setState(() {
_image = image;
});
}
#override
Widget build(BuildContext context) {
return new Scaffold(
appBar: new AppBar(
title: new Text('Home'),
),
body: new Center(
child: _image == null
? new Text('No image selected.')
: new CircleAvatar(backgroundImage: new FileImage(_image), radius: 200.0,),
),
floatingActionButton: new FloatingActionButton(
onPressed: getImage,
tooltip: 'Pick Image',
child: new Icon(Icons.add_a_photo),
),
);
}
}
You can also use ClipOval to circle the image. Just wrap your file image with ClipOval.
ClipOval(
child: FileImage(_image)
),
If you want to make use of BoxDecoration.Circle, this is what you need to do
Container(
width: 120.0,
height: 120.0,
decoration: BoxDecoration(
shape: BoxShape.circle,
image: DecorationImage(
fit: BoxFit.fill,
image: FileImage(_image)
)
)
),
I hope this helps
I have figured it out, this a class that I will use later for clipping it's child
class CircleRevealClipper extends CustomClipper<Rect> { CircleRevealClipper();
#override Rect getClip(Size size) {
final epicenter = new Offset(size.width, size.height);
// Calculate distance from epicenter to the top left corner to make sure clip the image into circle.
final distanceToCorner = epicenter.dy;
final radius = distanceToCorner;
final diameter = radius;
return new Rect.fromLTWH(
epicenter.dx - radius, epicenter.dy - radius, diameter, diameter); }
#override bool shouldReclip(CustomClipper<Rect> oldClipper) {
return true; } }

How to change origin of the custom paint in Flutter?

Code for CustomPaint:
return new Scaffold(
body:
new GestureDetector(
onTap: () {
debugPrint("hello");
},
child:
new Container(
alignment: FractionalOffset.center,
child: new CustomPaint(
size: new Size(400.0, 400.0),
painter: new BarChartPainter(currentHeight),
))),
);
//x axis code
canvas.drawLine(new Offset(0.0, 0.0), new Offset(500.0, 0.0), paintAx);```
The x axis code will draw line from (0,0) to (500,0), which is on the top of the box of the Paint. The origin is on the top left of the box. How do I change the origin so that (0,0) is at the bottom left of the paint box?
Here is the screenshot:
Just use the method translate in canvas: canvas.translate(0, size.height). But mind that in such a case, you will need to use negative values in the y axis.
If you want your canvas coordinates to behave like a classical graph, use the method scale:
#override
void paint(Canvas canvas, Size size) {
canvas.translate(0, size.height);
canvas.scale(1, -1);
final paint = Paint();
paint.color = Colors.black;
canvas.drawLine(Offset.zero, Offset(500, 500), paint);
}
I am not really sure how can you manipulate the origin of Canvas area. You can, however, apply a translation on your Offset coordinates, which should allow you to place your line where you want eventually.
I have made this simple example, it may be of help:
import "package:flutter/material.dart";
void main() {
runApp(new MaterialApp(home: new MyApp(),
));
}
class MyApp extends StatefulWidget {
#override
_MyAppState createState() => new _MyAppState();
}
class _MyAppState extends State<MyApp> {
CustomPaint _myCustomPainter = new CustomPaint(
size: new Size(400.0, 400.0),
painter: new Line(),
);
#override
Widget build(BuildContext context) {
final key = new GlobalKey<ScaffoldState>();
return new Scaffold(
key: key,
body:
new GestureDetector(
onTap: () {
debugPrint("hello");
},
child:
new Container(
alignment: FractionalOffset.center,
child: _myCustomPainter
),
),);
}
}
class Line extends CustomPainter {
#override
void paint(Canvas canvas, Size size) {
// canvas.translate(0.0, 100.0);
canvas.drawLine(new Offset(100.0, -50.0).translate(0.0, 100.0),
new Offset(0.0, 100.0).translate(0.0, 100.0),
new Paint()
);
}
#override
bool shouldRepaint(Line oldDelegate) {
// Since this Line painter has no fields, it always paints
// the same thing, and therefore we return false here. If
// we had fields (set from the constructor) then we would
// return true if any of them differed from the same
// fields on the oldDelegate.
return false;
}
}

Resources