Pass a class as method parameter in Dart - flutter

Suppose this class:
class SimpleRectPaint extends Object {
static CustomPaint build( ParamsBundle paramBundle ) {
return CustomPaint(
painter: SimpleRectPainter( paramBundle: paramBundle ),
child: Container(width: paramBundle.width, height: paramBundle.height)
);
}
}
I'd like to abstract the class SimpleRectPaint by means of a ClassReference parameter someClass and make the method more generic. Unfortunately, this isn't valid code:
class SimpleRectPaint extends Object {
static CustomPaint build( ParamsBundle paramBundle, ClassReference someClass ) {
return CustomPaint(
painter: someClass( paramBundle: paramBundle ),
child: Container(width: paramBundle.width, height: paramBundle.height)
);
}
}
Q: How do I have to write it instead?

You could do that by passing a CustomPainterCreator:
import 'package:flutter/material.dart';
void main() {
runApp(
MaterialApp(
title: 'Generic Painter',
home: Scaffold(
body: MyWidget(),
),
),
);
}
class MyWidget extends StatelessWidget {
#override
Widget build(BuildContext context) {
return Center(
child: SizedBox(
width: 200,
height: 200,
child: Stack(
fit: StackFit.expand,
children: [
Background(),
MyPainter(creator: RectPainter.creator, color: Color(0xFF104C91)),
MyPainter(creator: OvalPainter.creator, color: Color(0xFF1F8AC0)),
],
),
),
);
}
}
class Background extends StatelessWidget {
#override
Widget build(BuildContext context) {
return Container(
decoration: BoxDecoration(
color: Color(0xFFEFC9AF),
border: Border.all(width: 3.0),
borderRadius: BorderRadius.all(Radius.circular(10.0)),
),
);
}
}
typedef CustomPainterCreator(Color color);
class MyPainter extends StatelessWidget {
final CustomPainterCreator creator;
final Color color;
const MyPainter({
Key key,
this.creator,
this.color,
}) : super(key: key);
#override
Widget build(BuildContext context) {
return CustomPaint(
painter: creator(color),
);
}
}
class OvalPainter extends CustomPainter {
static CustomPainterCreator creator = (color) => OvalPainter(color: color);
final Color color;
OvalPainter({this.color = Colors.green});
#override
void paint(Canvas canvas, Size size) {
final paint = Paint()..color = color;
canvas.drawOval(
Rect.fromLTWH(size.width * .4, size.height * .15, size.width * .5,
size.height * .5),
paint,
);
}
#override
bool shouldRepaint(OvalPainter oldDelegate) => false;
}
class RectPainter extends CustomPainter {
static CustomPainterCreator creator = (color) => RectPainter(color: color);
final Color color;
RectPainter({this.color = Colors.indigo});
#override
void paint(Canvas canvas, Size size) {
final paint = Paint()..color = color;
canvas.drawRRect(
RRect.fromRectAndRadius(
Rect.fromLTWH(size.width * .15, size.height * .25, size.width * .6,
size.height * .6),
Radius.circular(20)),
paint,
);
}
#override
bool shouldRepaint(RectPainter oldDelegate) => false;
}
But, though the exercise is interesting... Why?
Instead of:
MyPainter(creator: RectPainter.creator, color: Color(0xFF104C91)),
You can just do:
CustomPaint(painter: RectPainter(color: Color(0xFF104C91))),
If, not, what is the specific needs that would require more abstraction?

Related

How to paint a location icon in flutter?

I am trying to paint a icon with green color. I am using custom painter class.
Sample code:
import 'dart:ui';
import 'package:flutter/material.dart';
class HomePage extends StatefulWidget {
const HomePage({super.key});
#override
State<HomePage> createState() => _HomePageState();
}
class _HomePageState extends State<HomePage> {
double x = 0.0;
double y = 0.0;
List<Offset> mark = [];
void updatePosition(TapDownDetails details) {
x = details.localPosition.dx;
y = details.localPosition.dy;
setState(() {
mark.add(Offset(x, y));
print("mark:$mark");
});
}
#override
Widget build(BuildContext context) {
return Scaffold(
body: Center(
child: GestureDetector(
onTapDown: updatePosition,
child: RepaintBoundary(
child: Container(
margin: const EdgeInsets.only(left: 8, right: 8),
height: 300,
width: double.infinity,
decoration: BoxDecoration(
border: Border.all(color: Colors.blue),
image: const DecorationImage(
image: AssetImage(
'assets/dark.jpg',
),
fit: BoxFit.cover)),
child: CustomPaint(
painter: MyCustomPainter(mark),
),
)),
),
),
);
}
}
class MyCustomPainter extends CustomPainter {
final List<Offset> marks;
const MyCustomPainter(this.marks);
#override
void paint(Canvas canvas, Size size) async {
Paint paint = Paint()
..color = Colors.green
..strokeCap = StrokeCap.round
..strokeWidth = 15.0;
canvas.drawPoints(PointMode.points, marks, paint);
}
#override
bool shouldRepaint(MyCustomPainter oldDelegate) {
return true;
}
}
It's helped me to draw a points list. How can I draw a list of location icon instead of green points?

Can a child RenderObject take a parent transformation into account?

I have implemented a 'Marker' that uses a GlobalKey to find the location of a Container and draws a blue box around it. This works great as long as no transformation is taking place (e.g. FittedBox with scaleDown). But if there is, the blue box appears at an incorrect position.
Does anyone know how to account for the transformation to make the blue box appear in the correct location?
Full code example:
import 'package:flutter/material.dart';
import 'package:flutter/rendering.dart';
void main() {
runApp(const MyApp());
}
var boxKey = GlobalKey();
class MyApp extends StatelessWidget {
const MyApp({Key? key}) : super(key: key);
// This widget is the root of your application.
#override
Widget build(BuildContext context) {
return MaterialApp(
home: Center(
child: SizedBox(
height: 100,
width: 100,
child: FittedBox(
fit: BoxFit.scaleDown,
child: Container(
height: 200,
width: 200,
color: Colors.yellow,
child: Stack(
children: [
const Marker(),
Align(
alignment: const Alignment(-0.5, -0.5),
child: Container(
key: boxKey,
width: 30,
height: 30,
color: Colors.red,
)),
],
),
),
),
),
),
);
}
}
class Marker extends LeafRenderObjectWidget {
const Marker({Key? key}) : super(key: key);
#override
RenderMarker createRenderObject(BuildContext context) {
return RenderMarker();
}
#override
void updateRenderObject(BuildContext context, RenderMarker renderObject) {}
}
class RenderMarker extends RenderProxyBox {
#override
void performLayout() {
super.performLayout();
}
#override
void paint(PaintingContext context, Offset offset) {
super.paint(context, offset);
var renderBox = boxKey.currentContext!.findRenderObject() as RenderBox;
var center = renderBox.localToGlobal(Offset.zero) +
Offset(renderBox.size.width / 2, renderBox.size.height / 2);
context.canvas.drawRect(
Rect.fromCenter(center: center, width: 50, height: 50),
Paint()..color = Colors.blue);
}
}
I found a working solution:
#override
void paint(PaintingContext context, Offset offset) {
super.paint(context, offset);
var renderBox = boxKey.currentContext!.findRenderObject() as RenderBox;
var p = parent! as RenderObject;
var parentOffset = renderBox.localToGlobal(Offset.zero, ancestor: p);
var center = parentOffset +
Offset(renderBox.size.width / 2, renderBox.size.height / 2);
context.canvas.drawRect(
Rect.fromCenter(center: center + offset, width: 50, height: 50),
Paint()..color = Colors.blue);
}
Notice how I calculate everything in relation to the parent now.

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()

Dynamic filled star in flutter

Am trying to create a star rating component which can dynamically fill from left.
So far I am able to create a crude star by using custom painter.
class StarPainter extends CustomPainter {
#override
void paint(Canvas canvas, Size size) {
double w = size.width;
double h = size.height;
Path path = Path()
..moveTo(0, (2 * h) / 5)
..lineTo(w, (2 * h) / 5)
..lineTo(w / 5, h)
..lineTo(w / 2, 0)
..lineTo((4 * w) / 5, h)
..lineTo(0, (2 * h) / 5)
..close();
Paint fillPaint = Paint()
..style = PaintingStyle.fill
..color = Colors.black;
// canvas.drawPath(path, paint);
canvas.drawPath(path, fillPaint);
}
#override
bool shouldRepaint(covariant CustomPainter oldDelegate) {
return false;
}
}
But I am not able to figure out how to show a portion of it as filled. As of now I have used a double stack one with +2 size which acts as the border and used a shader mask with linear gradient to show progress.
But can this be done without the crude way of stacking ?
Widget build(BuildContext context) {
return MaterialApp(
home: Scaffold(
body: Center(
child: SizedBox(
height: starSize,
width: starSize,
child: Stack(
fit: StackFit.expand,
children: [
DynamicStar(percent: 1.0),
Center(
child: SizedBox(
width: starSize - 2,
height: starSize - 2,
child: DynamicStar(percent: 0.8),
),
)
],
),
),
),
),
);
}
class DynamicStar extends StatelessWidget {
final double percent;
const DynamicStar({Key? key, required this.percent}) : super(key: key);
#override
Widget build(BuildContext context) {
return ShaderMask(
blendMode: BlendMode.srcATop,
shaderCallback: (bounds) {
return LinearGradient(
tileMode: TileMode.clamp,
colors: [Colors.black, Colors.white],
begin: Alignment.centerLeft,
end: Alignment.centerRight,
stops: [percent, percent],
).createShader(bounds);
},
child: CustomPaint(painter: StarPainter()),
);
}
}
EDIT: am trying to learn in flutter, so any clues on how to fix my code or snippets would help me better.
Please refer to below link
https://pub.dev/packages/smooth_star_rating
Or
https://pub.dev/packages/flutter_rating_bar
Smooth star rating example
import 'package:flutter/material.dart';
import 'package:smooth_star_rating/smooth_star_rating.dart';
void main() => runApp(MyApp());
class MyApp extends StatefulWidget {
#override
_MyAppState createState() => _MyAppState();
}
class _MyAppState extends State<MyApp> {
var rating = 1.0;
#override
Widget build(BuildContext context) {
return MaterialApp(
title: 'Star rating',
theme: ThemeData(
primarySwatch: Colors.blue,
),
debugShowCheckedModeBanner: false,
home: Scaffold(
body: Center(
child: SmoothStarRating(
rating: rating,
isReadOnly: false,
size: 80,
filledIconData: Icons.star,
halfFilledIconData: Icons.star_half,
defaultIconData: Icons.star_border,
starCount: 5,
allowHalfRating: true,
spacing: 2.0,
onRated: (value) {
print("rating value -> $value");
},
)),
),
);
}
}

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(),
),
))
],
),
);
}
}