How to conditionally render in Flutter - flutter

I have a wallet circle which is responsive to the payments that are done. What my issue is when ever the wallet amount is zero, I want the widget that draws the circle to be non visible.
What I did now, is that I used a ternary operatior for checking it inside Custompaint Widget. What am I missing?.
CustomPaint(
painter: (this.total <= 0)
? CurvePainter(colors: [
// To test if the color changes
Colors.red.withOpacity(0.9),
Colors.red.withOpacity(0.9)
], angle: 0, strokeWidth: 0)
: CurvePainter(
colors: [
Colors.white.withOpacity(0.9),
Colors.white.withOpacity(0.9),
],
angle: 360 - ((this.used / this.total) * 360),
strokeWidth: this.strokeWidth,
),
size: Size.fromRadius(strokeWidth),
child: SizedBox(
width: this.radius,
height: this.radius,
),
)

If you want to paint widget conditionally you can use ternary operator like this:
(this.total <= 0)
? CustomPaint(
painter:CurvePainter(
colors: [
Colors.white.withOpacity(0.9),
Colors.white.withOpacity(0.9),
],
angle: 360 - ((this.used / this.total) * 360),
strokeWidth: this.strokeWidth,
),
size: Size.fromRadius(strokeWidth),
child: SizedBox(
width: this.radius,
height: this.radius,
),
) : Container(), // <--- Use it here, not inside CustomPainter

Do you change the variable inside setState?
setState((){
total = 0;
});
setstate rerenders the screen.

Related

I have a complex design, I don't know what to do with flutter,

I have a complex design, I don't know what to do with flutter, is there any help on how to implement this design?
Usually to create complex designs I prefer using Flutter Shape Maker.
All you need is to upload your svg picture and it will convert it to CustomPaint code in flutter. Perhaps this may help you.
I have tried to achieve a shape and the center painter here. Few things, I am able to achieve but for complete part, I think you need to use pie chart https://pub.dev/packages/syncfusion_flutter_charts specially Doughnut type.
Please check my sample as below:
This painter is used to prepare the pie.
class WheelPainter extends CustomPainter {
Path getWheelPath(double wheelSize, double fromRadius, double toRadius) {
return new Path()
..moveTo(wheelSize, wheelSize)
..arcTo(
Rect.fromCircle(
radius: wheelSize, center: Offset(wheelSize, wheelSize)),
fromRadius,
toRadius,
false)
..close();
}
Paint getColoredPaint(Color color) {
Paint paint = Paint();
paint.color = color;
return paint;
}
#override
void paint(Canvas canvas, Size size) {
double wheelSize = 150;
double nbElem = 6;
double radius = (2 * pi) / nbElem;
// canvas.drawPath(getWheelPath(wheelSize, 0, radius), getColoredPaint(Colors.red));
canvas.drawShadow(getWheelPath(wheelSize, radius * 0.5, radius * 2).shift(Offset(0, -10)), Colors.black, 10.0, true);
canvas.drawPath(getWheelPath(wheelSize, radius * 0.5, radius * 2),
getColoredPaint(Colors.purple));
// canvas.drawPath(getWheelPath(wheelSize, radius * 2, radius), getColoredPaint(Colors.blue));
canvas.drawShadow(getWheelPath(wheelSize, radius * 2.7, radius * 1.7).shift(Offset(0, -10)), Colors.black, 10.0, true);
canvas.drawPath(getWheelPath(wheelSize, radius * 2.7, radius * 1.7),
getColoredPaint(Colors.green));
// canvas.drawPath(getWheelPath(wheelSize, radius * 4, radius), getColoredPaint(Colors.yellow));
canvas.drawShadow(getWheelPath(wheelSize, radius * 4.6, radius * 1.7).shift(Offset(0, -10)), Colors.black, 10.0, true);
canvas.drawPath(getWheelPath(wheelSize, radius * 4.6, radius * 1.7),
getColoredPaint(Colors.orange));
}
#override
bool shouldRepaint(CustomPainter oldDelegate) {
return oldDelegate != this;
}
}
Below is the sample to use it.
Stack(alignment: Alignment.center, children: [
CustomPaint(
size: Size(300, 300),
painter: WheelPainter(),
),
Container(
width: 150,
height: 150,
decoration:
BoxDecoration(shape: BoxShape.circle, color: Colors.white),
),
Container(
width: 110,
height: 110,
decoration:
BoxDecoration(shape: BoxShape.circle, color: Colors.white, boxShadow: [
BoxShadow(
color: Colors.grey,
blurRadius: 10.0,
blurStyle: BlurStyle.outer,
spreadRadius: 3.0,
offset: Offset(1.0,1.0)
)
]),
)
]),
This is how it will look like.
Hope it may help you.
You can take two container in Stack Widget
and implement customPaint
customPaint
You can try packges from pub.dev for this and you can customise as you need in app this designs.
syncfusion_flutter-chart
Try above package for your requirements

How to create dotted border around circular avatar in flutter

I'd like to display Instagram-like stories on my Flutter app and want to show the number of stories a user has uploaded by using borders around the user's avatar.
Say a user has uploaded 3 stories, i'll show 3 rounded border lines around the avatar image separated by equal number of spaces & if a user uploads 80 stories, i'll show 80 tiny round border lines separated by equal number of spaces.
I tried using plugins from pub.dev for this, like
dotted_border 2.0.0+2
fdottedline 1.0.1
just to name a few, but i can't seem to get an accurate count of spaces & dashes to fulfill the requirements above.
Below is an example:
FDottedLine(
color: Colors.black,
strokeWidth: 2.0,
dottedLength: 30,
space: 4,
corner: FDottedLineCorner.all(100.0),
child: Padding(
padding: const EdgeInsets.all(3.0),
child: SizedBox.square(
dimension: 0.055.h,
child: ClipRRect(
borderRadius: BorderRadius.circular(100),
child: ImageBox.network(
photo: user.photo.getOrEmpty,
elevation: 2,
replacement: Image.asset(AppAssets.defaultUserImage(user.gender.getOrNull)),
borderRadius: BorderRadius.circular(100),
),
),
),
),
),
No matter how i tweak the dottedLength & space params, i can't get equal number of spaces nor dashes.
I also tried using Path(), CustomPainter() but i barely know much about how to use it.
Any idea how i can achieve this using either CustomPainter() or a plugin?
Thanks for posting all your tries as it made me jump to CustomPaint directly to try
the approach that (could) work (but not tested well) is drawArc
the logic is simply to draw an arc based on the number of stories and start the next arc after leaving some space
the below code is looping for the number of stories to draw every story arc and start the next story arc (if stories > 1) after adding some value (the space between the stories) to the start of the next arc location (on the circle).
for(int i =0;i<numberOfStories;i++){
canvas.drawArc(
rect,
inRads(startOfArcInDegree),
inRads(arcLength),
false,
Paint()
..color = i==0||i==1?Colors.grey:Colors.teal
..strokeWidth =14.0
..style = PaintingStyle.stroke
);
startOfArcInDegree += arcLength + spaceLength;
}
full code with detailed explanation:
import 'dart:math';
import 'package:flutter/material.dart';
class DottedBorder extends CustomPainter {
//number of stories
final int numberOfStories;
//length of the space arc (empty one)
final int spaceLength;
//start of the arc painting in degree(0-360)
double startOfArcInDegree = 0;
DottedBorder({required this.numberOfStories, this.spaceLength = 10});
//drawArc deals with rads, easier for me to use degrees
//so this takes a degree and change it to rad
double inRads(double degree){
return (degree * pi)/180;
}
#override
bool shouldRepaint(DottedBorder oldDelegate) {
return true;
}
#override
void paint(Canvas canvas, Size size) {
//circle angle is 360, remove all space arcs between the main story arc (the number of spaces(stories) times the space length
//then subtract the number from 360 to get ALL arcs length
//then divide the ALL arcs length by number of Arc (number of stories) to get the exact length of one arc
double arcLength = (360 - (numberOfStories * spaceLength))/numberOfStories;
//be careful here when arc is a negative number
//that happens when the number of spaces is more than 360
//feel free to use what logic you want to take care of that
//note that numberOfStories should be limited too here
if(arcLength<=0){
arcLength = 360/spaceLength -1;
}
Rect rect = Rect.fromLTWH(0, 0, size.width, size.height);
//looping for number of stories to draw every story arc
for(int i =0;i<numberOfStories;i++){
//printing the arc
canvas.drawArc(
rect,
inRads(startOfArcInDegree),
//be careful here is: "double sweepAngle", not "end"
inRads(arcLength),
false,
Paint()
//here you can compare your SEEN story index with the arc index to make it grey
..color = i==0||i==1?Colors.grey:Colors.teal
..strokeWidth =14.0
..style = PaintingStyle.stroke
);
//the logic of spaces between the arcs is to start the next arc after jumping the length of space
startOfArcInDegree += arcLength + spaceLength;
}
}
}
class DottedBorderExample extends StatelessWidget {
const DottedBorderExample({Key? key}) : super(key: key);
#override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(title: const Text('Arcs etc')),
body:Center(
child: Stack(
alignment: Alignment.center,
children: [
SizedBox(
width: 300,height: 300,
child: CustomPaint(
painter: DottedBorder(numberOfStories: 13,spaceLength:4 ),
),),
Container(child:const Center(child: Text("Some Image",style: TextStyle(fontSize: 18,color: Colors.black),)),width: 270,height: 270,decoration: const BoxDecoration(color: Colors.purple,shape: BoxShape.circle),)
],
),
),
);
}
}
void main() {
runApp(
const MaterialApp(
home: DottedBorderExample(),
),
);
}
click to look at images:
we should determine 2 things
color width
separation width\
color width can be measured through below function
double colorWidth(double radius, int statusCount, double separation)
{
return ((2 * pi * radius) - (statusCount * separation)) / statusCount;
}
2 * PI * radius >> Circumference of a circle
SO >> Circumference minus total separation pixels needed, Then result divided by total status count.
now we have the width of each status equally, To fit the circle border
measuring separation pixels width
depending on the status number to be more enhanced as WhatsApp
double separation(int statusCount) {
if (statusCount <= 20)
return 3.0;
else if (statusCount <= 30)
return 1.8;
else if (statusCount <= 60)
return 1.0;
else
return 0.3;
}
Now we add the dotted_border package to our project and import it
https://pub.dev/packages/dotted_border
import 'package:dotted_border/dotted_border.dart';
assuming we have some declarations above they are:
//each digit express a status number
List status = [1, 2, 5, 4, 9, 13, 15, 20, 30, 40, 80];
//circle radius
double radius = 27.0;
dashPattern:
we have two states
one status or more than one (multiple statuses)
dashPattern: status[index] == 1
? [
//one status
(2 * pi * (radius + 2)), // take all border
0, //zere separators
]
: [
//multiple status
colorWidth(radius + 2, status[index],
separation(status[index])),
separation(status[index]),
],
FULL CODE:
import 'dart:math';
import 'package:dotted_border/dotted_border.dart';
import 'package:flutter/material.dart';
void main() {
runApp(const MyApp());
}
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(
title: 'STATUS',
home: MyHomePage(),
);
}
}
class MyHomePage extends StatelessWidget {
List status = [1, 2, 5, 4, 9, 13, 15, 20, 30, 40, 80];
double radius = 27.0;
double colorWidth(double radius, int statusCount, double separation) {
return ((2 * pi * radius) - (statusCount * separation)) / statusCount;
}
double separation(int statusCount) {
if (statusCount <= 20)
return 3.0;
else if (statusCount <= 30)
return 1.8;
else if (statusCount <= 60)
return 1.0;
else
return 0.3;
}
#override
Widget build(BuildContext context) {
return MaterialApp(
home: Scaffold(
body: ListView.separated(
itemCount: status.length,
separatorBuilder: (context, index) => Divider(
color: Colors.black,
height: 15,
),
itemBuilder: ((context, index) => Row(
children: [
Padding(
padding: const EdgeInsets.all(8.0),
child:
/// Creating a circle with a dotted border.
DottedBorder(
color: Colors.teal.shade300,
borderType: BorderType.Circle,
radius: Radius.circular(radius),
dashPattern: status[index] == 1
? [
//one status
(2 * pi * (radius + 2)),
0,
]
: [
//multiple status
colorWidth(radius + 2, status[index],
separation(status[index])),
separation(status[index]),
],
strokeWidth: 3,
child: CircleAvatar(
radius: radius,
backgroundColor: Colors.transparent,
child: CircleAvatar(
radius: radius - 2,
),
),
),
),
SizedBox(
width: 10,
),
Text(
'${status[index]}',
style: TextStyle(
fontSize: 20,
fontWeight: FontWeight.bold,
),
),
],
)),
),
),
);
}
}
You can use a package named status_view:
Link:
status_view
Usage from official docs
StatusView(
radius: 40,
spacing: 15,
strokeWidth: 2,
indexOfSeenStatus: 2,
numberOfStatus: 5,
padding: 4,
centerImageUrl: "https://picsum.photos/200/300",
seenColor: Colors.grey,
unSeenColor: Colors.red,
),

How to get InteractiveViewer image pixel coordinate in flutter?

i want to get pixel coordinate when i tapped the screen, i use interactiveView and GestureDetect in Fultter, i am so confused about the matrix transform , i am new to App develop, please give some advice if you could, very appraciate, Below is my code, which now i can zoom the image, but i can't calculate the coorect pixel coordiante when i click the screen. and since i have no idea how to calculate the ratio between pixel distance<->screen distance, i was stucked there. please help me.
What i am doing is i need pick a pixel position from the image, so i need zoom image first to get precise position,that's why i need ineractiveViewer . and at the same time i need record the gesture behavior, to monitor the behavior, then i wrapper InteractiveView to GestureDetect.
it look like this for now:
enter image description here
Widget mapView() {
double offsetX, offsetY;
return Stack(
children: <Widget>[
Positioned.fill(
child:
GestureDetector(
onTapUp: (TapUpDetails details) {
offsetX = details.localPosition.dx;
offsetY = details.localPosition.dy;
// print(
//"tap local pos, X: ${details.localPosition.dx}, Y: ${details.localPosition.dy}");
// print(
// "tap global pos, X: ${details.globalPosition.dx}, Y: ${details.globalPosition.dy}");
_childWasTappedAt = _transformationController!.toScene(details.localPosition);
// print(
// "child pos to scene , X: ${_childWasTappedAt!.dx}, Y: ${_childWasTappedAt!.dy}");
//double origin_scree_pixel_radio = 17;
MediaQueryData queryData;
queryData = MediaQuery.of(context);
double pixel_ratio = queryData.devicePixelRatio;
double scree_pixel_radio = (1.0 / _cur_scale_value!)*pixel_ratio;
double trans_x = -1.0 * _transformationController!.value.getTranslation().x;
double local_offset_x = offsetX;
double pixel_x = trans_x + local_offset_x * scree_pixel_radio;
print("scale: ${_cur_scale_value}");
print("radio: ${pixel_ratio}");
print("view tran x: ${trans_x}");
print("offset x: ${local_offset_x}");
//print("image_Info: ${_image_info.toString()}");
print("Pixel X: ${pixel_x}");
},
child:
InteractiveViewer(
transformationController: _transformationController,
minScale: 0.001,
maxScale: 200.0,
constrained: false,
child: Image.asset(
imagePath,
filterQuality:FilterQuality.high,
),
onInteractionEnd: (ScaleEndDetails details) {
_cur_scale_value = _transformationController!.value.getMaxScaleOnAxis();
//print("current scale: ${_cur_scale_value}");
},
onInteractionUpdate: (ScaleUpdateDetails details){
//print('onInteractionUpdate----' + details.toString());
},
),
),
),
Positioned(
top: 0.0,//_vehicle_y,
left: 0.0,//_vehicle_x,
child: Icon(Icons.favorite, color: Colors.red,),
),
],
);
}
Use this onInteractionUpdate method to get Coordinates. use also use different methods.
Vist This site for more info:-
https://api.flutter.dev/flutter/widgets/InteractiveViewer/onInteractionUpdate.html
https://api.flutter.dev/flutter/gestures/ScaleStartDetails/localFocalPoint.html
Widget build(BuildContext context) {
return Scaffold(
body: Center(
child: InteractiveViewer(
onInteractionUpdate: (v) {
print(v.localFocalPoint.dx);
print(v.localFocalPoint.dy);
},
child: Image.network(
"https://images.unsplash.com/photo-1643832678771-fdd9ed7638ae?ixlib=rb-1.2.1&ixid=MnwxMjA3fDB8MHx0b3BpYy1mZWVkfDd8Ym84alFLVGFFMFl8fGVufDB8fHx8&auto=format&fit=crop&w=2400&q=60",
fit: BoxFit.fitHeight,
),
),
),
);
}

want to get an animated container small again to initial size with a click. It expands perfectly but I can't close it again

want to get an animated container small again to initial size with a click. It expands perfectly but I can't close it again.............................................................................................................................
child: AnimatedContainer(
duration: Duration(milliseconds: 200),
width: isTextOpen ? 200 : 95,
height: 50,
decoration: BoxDecoration(
borderRadius:
BorderRadius.all(Radius.circular(3)),
border: isTextOpen
? Border.all(
color: Colors.deepOrangeAccent,
width: 1.0,
)
: null,
),
child: isTextOpen
? TextField()
: GestureDetector(
onTap: () {
setState(() {
isTextOpen = true;
kapaac = 1;
});
},
child: Text(
"User_007",
style: TextStyle(
color: Colors.deepOrangeAccent,
fontSize: 16.0,
fontWeight: FontWeight.bold,
),
),
),
),
You are not setting it back to false.
setState(() => isTextOpen = !isTextOpen);
Once the user clicks the textbox if you want to get the textBox size to the initial size then you need to define a double variable with the expanded size then once the user clicks the text box for entering the text you need to reset the variable
double animatedContainerSize = 200;
After that set your AnimatedContainer widget width with the variable
width: isTextOpen ? animatedContainerSize : 95,
Then in your text box onTap function change the size to initial size with setState
isTextOpen
? TextField(
onTap: () {
setState(() {
animatedContainerSize = 95;
});
},
)

Flutter - How to change the Color class' value that placed inside a custom widget?

I'd like to create a widget that I'll use it a lot of times, so I don't have to create it manully.
here's what I've built:
Widget keyWidget(String keyNumber, String keyName) {
return Container(
width: 100.0,
height: 50.0,
color: Colors.red, //How I change the color's value? (Colors.{this_one})?
child: FlatButton(
onPressed: () {
final player = AudioCache();
player.play('key$keyNumber.mp3');
},
child: Text('$keyName'),
),
);
}
How do I change the color's value (color: Colors.{this_one},) so I could set it in the keyWidget properties?
You can pass your Color as argument in your keyWidget() method
Try this way
Widget keyWidget(String keyNumber, String keyName,Color mColor) {
return Container(
width: 100.0,
height: 50.0,
color: mColor, //How I change the color's value? (Colors.{this_one})?
child: FlatButton(
onPressed: () {
final player = AudioCache();
player.play('key$keyNumber.mp3');
},
child: Text('$keyName'),
),
);
}
Now you can call this method like this.
keyWidget('keyNumber','keyName',Colors.red);
keyWidget('keyNumber','keyName',Colors.pink);
keyWidget('keyNumber','keyName',Color(0xffACACAC));
You can also make Color parameter optional
SAMPLE CODE
Widget keyWidget(String keyNumber, String keyName,{ mColor=Colors.red}) {
return Container(
width: 100.0,
height: 50.0,
color: mColor, //How I change the color's value? (Colors.{this_one})?
child: FlatButton(
onPressed: () {
final player = AudioCache();
player.play('key$keyNumber.mp3');
},
child: Text('$keyName'),
),
);
}
Now you can call this method like this.
keyWidget('keyNumber', 'keyName');
keyWidget('keyNumber', 'keyName',mColor: Colors.accents);