I want to create something like this:
How can I do that in flutter? I can't find any similar lib for that.
You can create a custom CircularProgressIndicator using CustomPainter.
class CustomCircularProgress extends CustomPainter {
final double value;
CustomCircularProgress({required this.value});
#override
void paint(Canvas canvas, Size size) {
final center = Offset(size.width / 2, size.height / 2);
canvas.drawArc(
Rect.fromCenter(center: center, width: 170, height: 170),
vmath.radians(140),
vmath.radians(260),
false,
Paint()
..style = PaintingStyle.stroke
..color = Colors.black12
..strokeCap = StrokeCap.round
..strokeWidth = 20,
);
canvas.saveLayer(
Rect.fromCenter(center: center, width: 200, height: 200),
Paint(),
);
const Gradient gradient = SweepGradient(
startAngle: 1.25 * math.pi / 2,
endAngle: 5.5 * math.pi / 2,
tileMode: TileMode.repeated,
colors: <Color>[
Colors.blueAccent,
Colors.lightBlueAccent,
],
);
canvas.drawArc(
Rect.fromCenter(center: center, width: 170, height: 170),
vmath.radians(140),
vmath.radians(260 * value),
false,
Paint()
..style = PaintingStyle.stroke
..strokeCap = StrokeCap.round
..shader = gradient
.createShader(Rect.fromLTWH(0.0, 0.0, size.width, size.height))
..strokeWidth = 20,
);
canvas.restore();
}
#override
bool shouldRepaint(covariant CustomPainter oldDelegate) => true;
}
Then use CustomPaint to render the CustomPainter on the Screen.
CustomPaint(painter: CustomCircularProgress(value: 0.75))
Complete Sample
import 'package:flutter/material.dart';
import 'package:vector_math/vector_math.dart' as vmath;
import 'dart:math' as math;
void main() {
runApp(const MyApp());
}
class MyApp extends StatelessWidget {
const MyApp({Key? key}) : super(key: key);
#override
Widget build(BuildContext context) {
return MaterialApp(
title: 'Flutter Demo',
theme: ThemeData(
primarySwatch: Colors.blue,
),
home: const MyHomePage(title: 'Flutter Demo Home Page'),
);
}
}
class MyHomePage extends StatefulWidget {
const MyHomePage({Key? key, required this.title}) : super(key: key);
final String title;
#override
State<MyHomePage> createState() => _MyHomePageState();
}
class _MyHomePageState extends State<MyHomePage> with TickerProviderStateMixin {
late AnimationController controller;
#override
void initState() {
controller = AnimationController(
vsync: this,
duration: const Duration(seconds: 5),
)..addListener(() {
setState(() {});
});
controller.repeat(reverse: true);
super.initState();
}
#override
void dispose() {
controller.dispose();
super.dispose();
}
#override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: Text(widget.title),
),
body: Center(
child: CustomPaint(painter: CustomCircularProgress(value: controller.value)),
),
);
}
}
class CustomCircularProgress extends CustomPainter {
final double value;
CustomCircularProgress({required this.value});
#override
void paint(Canvas canvas, Size size) {
final center = Offset(size.width / 2, size.height / 2);
canvas.drawArc(
Rect.fromCenter(center: center, width: 170, height: 170),
vmath.radians(140),
vmath.radians(260),
false,
Paint()
..style = PaintingStyle.stroke
..color = Colors.black12
..strokeCap = StrokeCap.round
..strokeWidth = 20,
);
canvas.saveLayer(
Rect.fromCenter(center: center, width: 200, height: 200),
Paint(),
);
const Gradient gradient = SweepGradient(
startAngle: 1.25 * math.pi / 2,
endAngle: 5.5 * math.pi / 2,
tileMode: TileMode.repeated,
colors: <Color>[
Colors.blueAccent,
Colors.lightBlueAccent,
],
);
canvas.drawArc(
Rect.fromCenter(center: center, width: 170, height: 170),
vmath.radians(140),
vmath.radians(260 * value),
false,
Paint()
..style = PaintingStyle.stroke
..strokeCap = StrokeCap.round
..shader = gradient
.createShader(Rect.fromLTWH(0.0, 0.0, size.width, size.height))
..strokeWidth = 20,
);
canvas.restore();
}
#override
bool shouldRepaint(covariant CustomPainter oldDelegate) => true;
}
Related
appBar: AppBar(
title: Center(
child: SvgPicture.asset("assets/images/logo.svg",
height: 15, width: 15, color: Colors.white,),
),
backgroundColor: const Color.fromRGBO(91, 189, 146, 1),
),
How to add the wave at the bottom of the appbar as in the picture?
The following would do the trick. It uses a CustomPaint widget to draw the semi-circle in the middle and Transform.translate to move the "icon" a bit down.
This is the result (also, check the live demo on the DartPad)
The minimal-reproducible-example source code
import 'dart:math';
import 'package:flutter/material.dart';
void main() {
runApp(const MyApp());
}
class MyApp extends StatelessWidget {
const MyApp({Key? key}) : super(key: key);
#override
Widget build(BuildContext context) {
return MaterialApp(
title: 'Flutter Demo',
theme: ThemeData(
primarySwatch: Colors.blue,
),
home: const MyHomePage(),
debugShowCheckedModeBanner: false,
);
}
}
class MyHomePage extends StatefulWidget {
const MyHomePage({Key? key}) : super(key: key);
#override
State<MyHomePage> createState() => _MyHomePageState();
}
class _MyHomePageState extends State<MyHomePage> {
#override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
toolbarHeight: 60,
flexibleSpace: const CustomPaint(
painter: MyCustomPainter(),
size: Size.infinite,
),
title: Center(
child: Transform.translate(
offset: const Offset(0, 5),
child: const Center(
child: Text(
"Q",
style: TextStyle(color: Colors.white, fontSize: 30),
),
),
),
),
backgroundColor: Colors.transparent,
elevation: 0,
),
);
}
}
class MyCustomPainter extends CustomPainter {
const MyCustomPainter({Listenable? repaint}) : super(repaint: repaint);
static const circleSize = 90.0;
static const gap = 15.0;
#override
void paint(Canvas canvas, Size size) {
var paint = Paint()
..style = PaintingStyle.fill
..color = const Color.fromRGBO(91, 189, 146, 1);
var shadow = Paint()
..style = PaintingStyle.fill
..color = const Color.fromARGB(255, 127, 127, 127)
..maskFilter = MaskFilter.blur(
BlurStyle.normal,
Shadow.convertRadiusToSigma(5),
);
var path = Path();
path.lineTo(0, size.height - gap);
path.lineTo(size.width / 2, size.height - gap);
path.arcTo(
Rect.fromLTWH(
size.width / 2 - circleSize / 2,
size.height - circleSize,
circleSize,
circleSize,
),
pi,
-pi,
false,
);
path.lineTo(size.width / 2, size.height - gap);
path.lineTo(size.width, size.height - gap);
path.lineTo(size.width, 0);
path.close();
canvas.drawPath(path, shadow);
canvas.drawPath(path, paint);
}
#override
bool shouldRepaint(MyCustomPainter oldDelegate) {
return false;
}
}
I have a CustomPaint which paints an oval.
I want to cut out a hole at a specific position which I couldn't figure out yet how that works.
I tried:
canvas.drawPath(
Path.combine(PathOperation.difference, ovalPath, holePath),
ovalPaint,
);
but that doesn't cut the hole, but gives me the following result:
But this is what I want to achieve:
This oval is just an example, the "real" custom paint is gonna get more complex and I need more than just one cutout. So just painting several lines is not an alternative. I want to first define the path and then apply a cutout (or even inverted clipping) to get the hole.
Is that possible?
Here is a full working example of what I have:
import 'package:flutter/material.dart';
import 'dart:math';
const Color darkBlue = Color.fromARGB(255, 18, 32, 47);
void main() {
runApp(MyApp());
}
class MyApp extends StatelessWidget {
#override
Widget build(BuildContext context) {
return MaterialApp(
theme: ThemeData.dark().copyWith(
scaffoldBackgroundColor: darkBlue,
),
debugShowCheckedModeBanner: false,
home: const Scaffold(
body: Center(
child: OvalCustomPaint(),
),
),
);
}
}
class OvalCustomPaint extends StatelessWidget {
const OvalCustomPaint({Key? key}) : super(key: key);
#override
Widget build(BuildContext context) {
return Center(
child: LayoutBuilder(
builder: (context, constraints) {
return Center(
child: CustomPaint(
painter: _Painter(),
child: SizedBox(
width: constraints.maxWidth,
height: constraints.maxHeight,
),
),
);
},
),
);
}
}
class _Painter extends CustomPainter {
#override
void paint(Canvas canvas, Size size) {
canvas.translate(size.width / 2, size.height / 2);
const curveRadius = 50.0;
const legLength = 150.0;
canvas.rotate(pi/2);
final ovalPaint = Paint()
..color = Colors.blue
..strokeWidth = 2.5
..style = PaintingStyle.stroke
..strokeCap = StrokeCap.round;
const fixPoint = Offset.zero;
//* OVAL LINE
final ovalPath = Path()..moveTo(fixPoint.dx, fixPoint.dy);
ovalPath.relativeArcToPoint(
const Offset(curveRadius * 2, 0),
radius: const Radius.circular(curveRadius),
);
ovalPath.relativeLineTo(0, legLength);
ovalPath.relativeArcToPoint(
const Offset(-curveRadius * 2, 0),
radius: const Radius.circular(curveRadius),
);
ovalPath.relativeLineTo(0, -legLength);
//* CLP HOLE
final holePath = Path();
holePath.addArc(Rect.fromCircle(center: fixPoint, radius: 13), 0, 2 * pi);
canvas.drawPath(
Path.combine(PathOperation.difference, ovalPath, holePath),
ovalPaint,
);
}
#override
bool shouldRepaint(_Painter oldDelegate) => false;
}
Okay I found a solution for it.
I created a rect path with the size of the CustomPainter area and built the difference with the cutout hole path.
That created a cutout template:
final rectWithCutout = Path.combine(
PathOperation.difference,
Path()
..addRect(
Rect.fromCenter(
center: Offset.zero,
width: size.width,
height: size.height,
),
),
holePath);
Which I could clip the canvas with: canvas.clipPath(rectWithCutout);
The final result:
This is the working full code example again:
import 'dart:math';
import 'package:flutter/material.dart';
const Color darkBlue = Color.fromARGB(255, 18, 32, 47);
void main() {
runApp(MyApp());
}
class MyApp extends StatelessWidget {
#override
Widget build(BuildContext context) {
return MaterialApp(
theme: ThemeData.dark().copyWith(
scaffoldBackgroundColor: darkBlue,
),
debugShowCheckedModeBanner: false,
home: const Scaffold(
body: Center(
child: OvalCustomPaint(),
),
),
);
}
}
class OvalCustomPaint extends StatelessWidget {
const OvalCustomPaint({Key? key}) : super(key: key);
#override
Widget build(BuildContext context) {
return Center(
child: LayoutBuilder(
builder: (context, constraints) {
return Center(
child: CustomPaint(
painter: _Painter(),
child: SizedBox(
width: constraints.maxWidth,
height: constraints.maxHeight,
),
),
);
},
),
);
}
}
class _Painter extends CustomPainter {
#override
void paint(Canvas canvas, Size size) {
canvas.translate(size.width / 2, size.height / 2);
const curveRadius = 40.0;
const legLength = 130.0;
canvas.rotate(pi / 2);
final ovalPaint = Paint()
..color = Colors.blue
..strokeWidth = 2.5
..style = PaintingStyle.stroke
..strokeCap = StrokeCap.round;
const fixPoint = Offset.zero;
//* OVAL LINE
final ovalPath = Path()..moveTo(fixPoint.dx, fixPoint.dy);
ovalPath.relativeArcToPoint(
const Offset(curveRadius * 2, 0),
radius: const Radius.circular(curveRadius),
);
ovalPath.relativeLineTo(0, legLength);
ovalPath.relativeArcToPoint(
const Offset(-curveRadius * 2, 0),
radius: const Radius.circular(curveRadius),
);
ovalPath.relativeLineTo(0, -legLength);
//* CLIP HOLE
final holePath = Path();
holePath.addArc(Rect.fromCircle(center: fixPoint, radius: 13), 0, 2 * pi);
final rectWithCutout = Path.combine(
PathOperation.difference,
Path()
..addRect(
Rect.fromCenter(
center: Offset.zero,
width: size.width,
height: size.height,
),
),
holePath);
canvas.clipPath(rectWithCutout);
canvas.drawPath(
ovalPath,
ovalPaint,
);
}
#override
bool shouldRepaint(_Painter oldDelegate) => false;
}
You can control the length of the line.
ovalPath.relativeArcToPoint(
const Offset(-curveRadius * 2, 0),
radius: const Radius.circular(curveRadius),
);
ovalPath.relativeLineTo(0, -legLength + (13 * 2)); //here based on radius
canvas.drawPath(
ovalPath,
ovalPaint,
);
In my flutter app, I tried to use CustomPainter, to draw custom shapes.
Here, I want to draw 4 small squares, in different colors, in a bigger square (here in yellow).
I expected to see the 4 squares, but for some reason, the 4 squares are on the top left of the CustomPainter.
Here is my sample app:
import 'package:flutter/material.dart';
void main() {
runApp(MyApp());
}
class MyApp extends StatelessWidget {
#override
Widget build(BuildContext context) {
return MaterialApp(
title: 'Flutter Demo',
home: MyHomePage(),
);
}
}
class MyHomePage extends StatefulWidget {
#override
_MyHomePageState createState() => _MyHomePageState();
}
class _MyHomePageState extends State<MyHomePage> {
#override
Widget build(BuildContext context) {
return Scaffold(
backgroundColor: Colors.black,
body: Center(
child: Container(
color: Colors.amber,
width: 180,
height: 180,
child: CustomPaint(
painter: TestCustomPainter(),
),
),
),
);
}
}
class TestCustomPainter extends CustomPainter {
Paint _paintWhite = Paint()
..color = Colors.white
..isAntiAlias = true;
Paint _paintBlue = Paint()
..color = Colors.blue
..isAntiAlias = true;
Paint _paintRed = Paint()
..color = Colors.red
..isAntiAlias = true;
Paint _paintGreen = Paint()
..color = Colors.green
..isAntiAlias = true;
#override
void paint(Canvas canvas, Size size) {
canvas.drawRect(Rect.fromLTRB(0, 0, 90, 90), _paintWhite);
canvas.drawRect(Rect.fromLTRB(90, 0, 0, 90), _paintBlue);
canvas.drawRect(Rect.fromLTRB(0, 90, 90, 0), _paintRed);
canvas.drawRect(Rect.fromLTRB(90, 90, 0, 0), _paintGreen);
}
#override
bool shouldRepaint(covariant CustomPainter oldDelegate) => true;
}
And here is what I get:
What did I do wrong?
Thanks.
I think this is 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',
home: MyHomePage(),
);
}
}
class MyHomePage extends StatefulWidget {
#override
_MyHomePageState createState() => _MyHomePageState();
}
class _MyHomePageState extends State<MyHomePage> {
#override
Widget build(BuildContext context) {
return Scaffold(
backgroundColor: Colors.black,
body: Center(
child: Container(
color: Colors.amber,
width: 180,
height: 180,
child: CustomPaint(
painter: TestCustomPainter(),
),
),
),
);
}
}
class TestCustomPainter extends CustomPainter {
Paint _paintWhite = Paint()
..color = Colors.white
..isAntiAlias = true;
Paint _paintBlue = Paint()
..color = Colors.blue
..isAntiAlias = true;
Paint _paintRed = Paint()
..color = Colors.red
..isAntiAlias = true;
Paint _paintGreen = Paint()
..color = Colors.green
..isAntiAlias = true;
#override
void paint(Canvas canvas, Size size) {
canvas.drawRect(Rect.fromLTRB(90, 0, 180, 90), _paintWhite);
canvas.drawRect(Rect.fromLTRB(90, 0, 0, 90), _paintBlue);
canvas.drawRect(Rect.fromLTRB(0, 90, 90, 180), _paintRed);
canvas.drawRect(Rect.fromLTRB(180, 180, 90,90), _paintGreen);
}
#override
bool shouldRepaint(covariant CustomPainter oldDelegate) => true;
}
since
Rect.fromLTRB(double left, double top, double right, double bottom)
Construct a rectangle from its left, top, right, and bottom edges.
more info about this over here in docs https://api.flutter.dev/flutter/dart-ui/Rect-class.html
I want to work exactly like this image with the circles inside it, how can I do it?
Here is your widget
Center(
child: Container(
width: 200,
height: 100,
decoration: BoxDecoration(
borderRadius: BorderRadius.only(bottomLeft: Radius.circular(20.0)),
gradient: LinearGradient(begin: Alignment.topLeft, end: Alignment.bottomRight, colors: [Color(0xFF9B6EEF), Color(0xFF715ED7)]),
),
child: ClipRect(child: CustomPaint(painter: CirclePainter())),
),
),
CustomPainter class for rings inside
class CirclePainter extends CustomPainter {
#override
void paint(Canvas canvas, Size size) {
var paint = Paint()
..color = Colors.white10
..style = PaintingStyle.stroke
..strokeWidth = 25;
canvas.drawCircle(Offset.zero, 60, paint);
canvas.drawCircle(Offset(size.width, size.height), 60, paint);
}
#override
bool shouldRepaint(CustomPainter oldDelegate) {
return false;
}
}
Result
main() => runApp(MyApp());
class MyApp extends StatelessWidget {
#override
Widget build(BuildContext context) {
return MaterialApp(
home: SO(),
debugShowCheckedModeBanner: false,
);
}
}
class SO extends StatelessWidget {
#override
Widget build(BuildContext context) {
return Scaffold(
body: Center(
child: ClipRRect(
borderRadius: BorderRadius.only(bottomLeft: Radius.circular(24)),
child: CustomPaint(
size: Size(250, 200),
painter: SOP(
fillColor: Colors.indigo,
spreadColor: Colors.indigoAccent,
spread: 25,
radius: 100,
),
),
),
),
);
}
}
class SOP extends CustomPainter {
final double spread; //the thickness of inner circles
final Color spreadColor; //the color of inner circles
final Color fillColor; //the background color
final double radius; //the radius of inner circles
final Paint p;
SOP({
#required this.spread,
#required this.spreadColor,
#required this.fillColor,
#required this.radius,
}) : p = Paint()
..strokeWidth = spread
..style = PaintingStyle.stroke
..color = spreadColor;
#override
void paint(Canvas canvas, Size size) {
canvas.drawColor(fillColor, BlendMode.src);
canvas.drawCircle(Offset(0, 0), radius, p);
canvas.drawCircle(Offset(size.width, size.height), radius, p);
}
#override
bool shouldRepaint(CustomPainter oldDelegate) => false;
}
My UI draw like this picture:
So I write some code by using CodePainter in Flutter. Here's my code:
import 'package:flutter/cupertino.dart';
import 'package:flutter/material.dart';
class BackgroundShape extends CustomPainter {
#override
void paint(Canvas canvas, Size size) {
final Paint paint = Paint();
paint.color = Colors.black;
var smallRect = Alignment.bottomCenter.inscribe(Size(100, 50), Rect.fromLTWH(size.width/2 -35, size.height-40, 40, 30));
var path = Path();
path.fillType = PathFillType.evenOdd;
path.addRRect(RRect.fromRectAndCorners(Rect.fromLTWH(0, 0, size.width, size.height), bottomLeft: Radius.circular(10), bottomRight: Radius.circular(10)));
path.addRRect(RRect.fromRectAndCorners(smallRect, topLeft: Radius.circular(10), topRight: Radius.circular(10)));
path.lineTo(0, size.height);
path.lineTo(size.width/2 - 35, size.height);
path.lineTo(size.width/2 - 35, size.height-40);
path.lineTo(size.width/2 + 35, size.height-40);
path.lineTo(size.width/2 + 35, size.height);
path.lineTo(size.width, size.height);
path.lineTo(size.width, 0);
path.close();
canvas.drawPath(path, paint);
}
#override
bool shouldRepaint(CustomPainter oldDelegate) => false;
}
Here is my result:
How to add border radius? please help me :D thanks!
For some reason, I'm not able to run completely the code you've provided, but taking consideration your current requirements and from the comments. I've created the following samples for your references:
import 'package:flutter/cupertino.dart';
import 'package:flutter/material.dart';
void main() {
runApp(MyApp());
}
class MyApp extends StatelessWidget {
#override
Widget build(BuildContext context) {
return MaterialApp(
title: 'Flutter Demo',
home: MyHomePage(),
);
}
}
class MyHomePage extends StatefulWidget {
#override
_MyHomePageState createState() => _MyHomePageState();
}
class _MyHomePageState extends State<MyHomePage> {
#override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: Text('Flutter Demo'),
),
body: Center(
child: Container(
width: 200,
height: 200,
child: CustomPaint(
painter: PainterTwo(),
),
),
),
);
}
}
class PainterOne extends CustomPainter {
#override
void paint(Canvas canvas, Size size) {
double w = size.width;
double h = size.height;
double r = 15;
var paint1 = Paint()
..color = Color(0xff888888)
..style = PaintingStyle.fill;
RRect fullRect = RRect.fromRectAndRadius(
Rect.fromCenter(center: Offset(w / 2, h / 2), width: w, height: h),
Radius.circular(r),
);
canvas.drawRRect(fullRect, paint1);
}
#override
bool shouldRepaint(CustomPainter oldDelegate) => true;
}
class PainterTwo extends CustomPainter {
#override
void paint(Canvas canvas, Size size) {
Paint paint = Paint()
..color = Colors.blue
..style = PaintingStyle.stroke
..strokeWidth = 8.0;
Path path = Path();
path.addRRect(RRect.fromRectAndRadius(
Rect.fromLTWH(
size.width / 2, size.height / 2, size.width / 4, size.height / 4),
Radius.circular(15)));
canvas.drawPath(path, paint);
}
#override
bool shouldRepaint(CustomPainter oldDelegate) => true;
}
Output of PainterOne():
Output of PainterTwo():