Filled circle progress bar [closed] - flutter

Closed. This question needs to be more focused. It is not currently accepting answers.
Want to improve this question? Update the question so it focuses on one problem only by editing this post.
Closed 6 months ago.
Improve this question
Does anyone know how to do filled circle progress bar like on a picture?

You can use CustomPainter
class CirclePaint extends CustomPainter {
final double value;
CirclePaint(this.value);
#override
void paint(Canvas canvas, Size size) {
final area = Rect.fromCircle(
center: size.center(Offset.zero), radius: size.width / 2);
canvas.drawArc(
area, -pi / 2, 2 * pi * value, true, Paint()..color = Colors.amber);
}
#override
bool shouldRepaint(covariant CustomPainter oldDelegate) => true;
}
And play with this widget and change the color you like to have.
double value = .4;
final size = 200.0;
#override
Widget build(BuildContext context) {
return Scaffold(
body: Center(
child: Column(
children: [
Slider(
value: value,
onChanged: (v) {
setState(() {
value = v;
});
},
),
Container(
width: size,
height: size,
decoration: BoxDecoration(
shape: BoxShape.circle,
border: Border.all(
color: Colors.blue,
width: 4,
),
),
child: CustomPaint(
painter: CirclePaint(value),
),
),
],
),
),
);
}
Find more about CustomPaint
With Listenable repaint as pskink mentioned on comment
class Psf extends StatelessWidget {
const Psf({Key? key}) : super(key: key);
#override
Widget build(BuildContext context) {
ValueNotifier<double> value = ValueNotifier(.3);
const size = 200.0;
return Scaffold(
body: Center(
child: Column(
children: [
Slider(
value: value.value,
onChanged: (v) {
value.value = v;
},
),
Container(
width: size,
height: size,
decoration: BoxDecoration(
shape: BoxShape.circle,
border: Border.all(
color: Colors.blue,
width: 4,
),
),
child: CustomPaint(
painter: CirclePaint(value),
),
),
],
),
),
);
}
}
class CirclePaint extends CustomPainter {
final ValueNotifier<double> value;
CirclePaint(this.value) : super(repaint: value);
#override
void paint(Canvas canvas, Size size) {
final area = Rect.fromCircle(
center: size.center(Offset.zero), radius: size.width / 2);
canvas.drawArc(area, -pi / 2, 2 * pi * value.value, true,
Paint()..color = Colors.amber);
}
#override
bool shouldRepaint(covariant CirclePaint oldDelegate) =>
oldDelegate.value != value;
}
With custom decoration as #pskink included, you need to use some custom snippet for this, check them here
Container(
width: size,
height: size,
decoration: AnimatedDecoration(
listenable: value,
onPaintFrame: (canvas, bounds, double v) {
canvas.drawArc(bounds, -pi / 2, 2 * pi * v, true,
Paint()..color = Colors.amber);
},
),
),

Related

How do I resize an image within a container using a pinch gesture?

I am pulling an image from the camera or gallery and need to make is so that image can be moved and resized with a pinch gesture freely within the bounds of a container.
The movement of the image is fine however I am having trouble getting the image to resize when zoomed in or out on. I am currently using GestureDetector but cannot get it to work.
Based on the attached code is there a better method of achieving the pinch zoom or am I misusingGestureDetector or putting it in the wrong place?
Code:
import 'package:flutter/material.dart';
import 'dart:io';
double canvasWidth = 0.7;
double canvasHeight = 0.5;
double imageCurrentScale = 1.0;
double imageBaseScale = 1.0;
class CanvasAreaWidget extends StatefulWidget {
const CanvasAreaWidget({super.key});
#override
State<CanvasAreaWidget> createState() => CanvasArea();
}
class CanvasArea extends State<CanvasAreaWidget> {
// Setup for canvas properties
final textWidgetKey = GlobalKey();
double titleLeftPosition = 0;
double imageScale = 1.0;
double prevImageScale = 1.0;
Offset postition = const Offset(0,0);
void initPosition() {
setState(() {
postition = Offset((MediaQuery.of(context).size.width*canvasWidth/2) - 25, (MediaQuery.of(context).size.height*canvasHeight/2) -25);
});
}
#override
void initState() {
super.initState();
WidgetsBinding.instance.addPostFrameCallback((_) => initPosition());
}
void updateScale(double zoom) => setState(() => imageScale = prevImageScale * zoom);
void commitScale() => setState(() => prevImageScale = imageScale);
void updatePosition(Offset newPosition) {
setState(() {
newPosition = newPosition - Offset(MediaQuery.of(context).size.width*((1-canvasWidth)/2), MediaQuery.of(context).size.width*((1-canvasHeight)/2));
postition = newPosition;
});
}
#override
Widget build(BuildContext context) {
//initPosition();
return Scaffold(
// The appBar contains the title of the page (current section of the app) as well as the back key to return to the main menu.
extendBodyBehindAppBar: true,
appBar: AppBar(
leading: const Icon(Icons.chevron_left),
title: const Text('Card Editor'),
centerTitle: true,
backgroundColor: Colors.transparent,
elevation: 0,
),
// Parent stack that all other widgets will be children of.
body: Stack(
children: [
Positioned(
// Position of drag area.
left: MediaQuery.of(context).size.width * ((1-canvasWidth)/2),
top: MediaQuery.of(context).size.width * ((1-canvasHeight)/2),
child: ClipRect(
child: Stack(
children: [
Container(
// Size of the drag area.
width: MediaQuery.of(context).size.width * canvasWidth,
height: MediaQuery.of(context).size.height * canvasHeight,
decoration: BoxDecoration(
border: Border.all(color: Colors.grey),
),
),
Positioned(
left: postition.dx,
top: postition.dy,
child: GestureDetector(
onScaleUpdate: (ScaleUpdateDetails details) => updateScale(details.scale),
onScaleEnd: (ScaleEndDetails details) => commitScale(),
child: Draggable(
maxSimultaneousDrags: 1,
feedback: const ImageItemWidget(),
childWhenDragging: Container(),
onDragEnd: (details) => updatePosition(details.offset),
child: const ImageItemWidget(),
),
),
),
],
),
),
),
],
),
);
}
}
File? recievedImage;
class ImageItemWidget extends StatefulWidget {
const ImageItemWidget ({super.key});
#override
State<ImageItemWidget> createState() => ImageItem();
}
class ImageItem extends State<ImageItemWidget> {
static void getImageFile(File? imageFile) {
recievedImage = imageFile;
print("File received");
}
#override
Widget build(BuildContext context) {
return SizedBox(
width: MediaQuery.of(context).size.width * canvasWidth,
height: MediaQuery.of(context).size.height * canvasHeight,
child: Image.file(
recievedImage!,
),
);
}
}

How to add arrow at bottom of a message box in flutter?

Please help me on how can I create this box
message box to be created in flutter
you ca use these packages to have a bubble shape for a message
bubble: ^1.2.1
flutter_chat_bubble: ^2.0.0
You can use the bubble from material design by importing
import 'package:flutter/material.dart';
or simply :-
Bubble(
margin: BubbleEdges.only(leftBottom: 10),
nip: BubbleNip.rightTop,
color: Color.fromRGBO(225, 255, 199, 1.0),
child: Text('Your Text', textAlign: TextAlign.right)
If u need more reference read this.
you can use customPainter or CustomClipper , in this case, better use clipper because we need all size from its child,
code :
import 'package:flutter/material.dart';
class MessageBox extends StatefulWidget {
const MessageBox({Key? key}) : super(key: key);
#override
_MessageBoxState createState() => _MessageBoxState();
}
class _MessageBoxState extends State<MessageBox> {
#override
Widget build(BuildContext context) {
return Material(
child: Column(
children: [
const SizedBox(height: 200,),
Container(
width: MediaQuery.of(context).size.width,
height: 100,
decoration: BoxDecoration(
color: Colors.purple,
borderRadius: BorderRadius.circular(35.0)
),
),
ClipPath(
clipper: MessageClipper(),
child: Container(
height: 20,
width: MediaQuery.of(context).size.width,
color: Colors.purple,
),
)
],
),
);
}
}
class MessageClipper extends CustomClipper<Path> {
#override
Path getClip(Size size) {
var firstOffset = Offset(size.width * 0.1, 0.0);
var secondPoint = Offset(size.width * 0.15, size.height );
var lastPoint = Offset(size.width * 0.2, 0.0);
var path = Path()
..moveTo(firstOffset.dx, firstOffset.dy)
..lineTo(secondPoint.dx, secondPoint.dy)
..lineTo(lastPoint.dx, lastPoint.dy)
..close();
return path;
}
#override
bool shouldReclip(covariant CustomClipper<Path> oldClipper) {
return true;
}
}
here result :

Quarter Circle does not fit my initial circle Flutter Painter

I'm trying to make a loading icon for my loading screen using CustomPaint widget. Unfortunally, it doesn't work as expected...
My quarter circle does not fit in my primary circle, here's a screen of it :
I don't understand why it is doing this...
Here's the code:
import 'package:flutter/material.dart';
import 'package:responsive_sizer/responsive_sizer.dart';
import 'dart:math' as math;
class LoadingPage extends StatelessWidget {
const LoadingPage({Key? key}) : super(key: key);
#override
Widget build(BuildContext context) {
return Column(
children: [
SizedBox(
height: 25.h,
),
Stack(
alignment: Alignment.center,
children: [
Container(
width: 28.w,
height: 28.w,
decoration: BoxDecoration(
color: Colors.transparent,
shape: BoxShape.circle,
border: Border.all(width: 3.w, color: Colors.white.withOpacity(0.2)),
),
),
CustomPaint(
painter: MyPainter(),
size: Size(28.w, 28.w),
),
]
),
SizedBox(
height: 6.h,
),
Text(
"LOADING",
style: Theme.of(context).textTheme.headline2,
)
],
);
}
}
class MyPainter extends CustomPainter {
#override
void paint(Canvas canvas, Size size) {
final Paint paint = Paint()
..strokeWidth = 3.w
..style = PaintingStyle.stroke
..color = Colors.blue;
double degToRad(double deg) => deg * (math.pi / 180.0);
final path = Path()
..arcTo(
Rect.fromCircle(
center: Offset(size.width/2, size.height/2),
radius: 28.w/2,
),
degToRad(90),
degToRad(90),
false);
canvas.drawPath(path, paint);
}
#override
bool shouldRepaint(CustomPainter oldDelegate) => false;
}
Does anyone knows why ? :)
Thanks for your answers,
Chris

Flutter: How to set boundaries for a Draggable widget?

I'm trying to create a drag and drop game. I would like to make sure that the Draggable widgets don't get out of the screen when they are dragged around.
I couldn't find an answer to this specific question. Someone asked something similar about constraining draggable area Constraining Draggable area but the answer doesn't actually make use of Draggable.
To start with I tried to implement a limit on the left-hand side.
I tried to use a Listener with onPointerMove. I've associated this event with a limitBoundaries method to detect when the Draggable exits from the left side of the screen. This part is working as it does print in the console the Offset value when the Draggable is going out (position.dx < 0). I also associated a setState to this method to set the position of the draggable to Offset(0.0, position.dy) but this doesn't work.
Could anybody help me with this?
import 'package:flutter/material.dart';
void main() {
runApp(MyApp());
}
class MyApp extends StatelessWidget {
#override
Widget build(BuildContext context) {
return MaterialApp(
title: 'Draggable Test',
home: GamePlay(),
);
}
}
class GamePlay extends StatelessWidget {
#override
Widget build(BuildContext context) {
return Scaffold(
body: Stack(
children: <Widget>[
Row(
children: [
Container(
width: 360,
height: 400,
decoration: BoxDecoration(
color: Colors.lightGreen,
border: Border.all(
color: Colors.green,
width: 2.0,
),
),
),
Container(
width: 190,
height: 400,
decoration: BoxDecoration(
color: Colors.white,
border: Border.all(
color: Colors.purple,
width: 2.0,
),
),
),
],
),
DragObject(
key: GlobalKey(),
initPos: Offset(365, 0.0),
id: 'Item 1',
itmColor: Colors.orange),
DragObject(
key: GlobalKey(),
initPos: Offset(450, 0.0),
id: 'Item 2',
itmColor: Colors.pink,
),
],
),
);
}
}
class DragObject extends StatefulWidget {
final String id;
final Offset initPos;
final Color itmColor;
DragObject({Key key, this.id, this.initPos, this.itmColor}) : super(key: key);
#override
_DragObjectState createState() => _DragObjectState();
}
class _DragObjectState extends State<DragObject> {
GlobalKey _key;
Offset position;
Offset posOffset = Offset(0.0, 0.0);
#override
void initState() {
WidgetsBinding.instance.addPostFrameCallback(_afterLayout);
_key = widget.key;
position = widget.initPos;
super.initState();
}
void _getRenderOffsets() {
final RenderBox renderBoxWidget = _key.currentContext.findRenderObject();
final offset = renderBoxWidget.localToGlobal(Offset.zero);
posOffset = offset - position;
}
void _afterLayout(_) {
_getRenderOffsets();
}
void limitBoundaries(PointerEvent details) {
if (details.position.dx < 0) {
print(details.position);
setState(() {
position = Offset(0.0, position.dy);
});
}
}
#override
Widget build(BuildContext context) {
return Positioned(
left: position.dx,
top: position.dy,
child: Listener(
onPointerMove: limitBoundaries,
child: Draggable(
child: Container(
width: 80,
height: 80,
color: widget.itmColor,
),
feedback: Container(
width: 82,
height: 82,
color: widget.itmColor,
),
childWhenDragging: Container(),
onDragEnd: (drag) {
setState(() {
position = drag.offset - posOffset;
});
},
),
),
);
}
}
Try this. I tweaked this from: Constraining Draggable area .
ValueNotifier<List<double>> posValueListener = ValueNotifier([0.0, 0.0]);
ValueChanged<List<double>> posValueChanged;
double _horizontalPos = 0.0;
double _verticalPos = 0.0;
#override
void initState() {
super.initState();
posValueListener.addListener(() {
if (posValueChanged != null) {
posValueChanged(posValueListener.value);
}
});
}
#override
Widget build(BuildContext context) {
return Scaffold(
body: Stack(
children: <Widget>[
_buildDraggable(),
]));
}
_buildDraggable() {
return SafeArea(
child: Container(
margin: EdgeInsets.only(bottom: 100),
color: Colors.green,
child: Builder(
builder: (context) {
final handle = GestureDetector(
onPanUpdate: (details) {
_verticalPos =
(_verticalPos + details.delta.dy / (context.size.height))
.clamp(.0, 1.0);
_horizontalPos =
(_horizontalPos + details.delta.dx / (context.size.width))
.clamp(.0, 1.0);
posValueListener.value = [_horizontalPos, _verticalPos];
},
child: Container(
child: Container(
margin: EdgeInsets.all(12),
width: 110.0,
height: 170.0,
child: Container(
color: Colors.black87,
),
decoration: BoxDecoration(color: Colors.black54),
),
));
return ValueListenableBuilder<List<double>>(
valueListenable: posValueListener,
builder:
(BuildContext context, List<double> value, Widget child) {
return Align(
alignment: Alignment(value[0] * 2 - 1, value[1] * 2 - 1),
child: handle,
);
},
);
},
),
),
);
}
I've found a workaround for this issue. It's not exactly the output I was looking for but I thought this could be useful to somebody else.
Instead of trying to control the drag object during dragging, I just let it go outside of my screen and I placed it back to its original position in case it goes outside of the screen.
Just a quick note if someone tries my code, I forgot to mention that I'm trying to develop a game for the web. The output on a mobile device might be a little bit odd!
import 'package:flutter/material.dart';
void main() {
runApp(MyApp());
}
class MyApp extends StatelessWidget {
#override
Widget build(BuildContext context) {
return MaterialApp(
title: 'Draggable Test',
home: GamePlay(),
);
}
}
class GamePlay extends StatelessWidget {
#override
Widget build(BuildContext context) {
return Scaffold(
body: Stack(
children: <Widget>[
Row(
children: [
Container(
width: 360,
height: 400,
decoration: BoxDecoration(
color: Colors.lightGreen,
border: Border.all(
color: Colors.green,
width: 2.0,
),
),
),
Container(
width: 190,
height: 400,
decoration: BoxDecoration(
color: Colors.white,
border: Border.all(
color: Colors.purple,
width: 2.0,
),
),
),
],
),
DragObject(
key: GlobalKey(),
initPos: Offset(365, 0.0),
id: 'Item 1',
itmColor: Colors.orange),
DragObject(
key: GlobalKey(),
initPos: Offset(450, 0.0),
id: 'Item 2',
itmColor: Colors.pink,
),
],
),
);
}
}
class DragObject extends StatefulWidget {
final String id;
final Offset initPos;
final Color itmColor;
DragObject({Key key, this.id, this.initPos, this.itmColor}) : super(key: key);
#override
_DragObjectState createState() => _DragObjectState();
}
class _DragObjectState extends State<DragObject> {
GlobalKey _key;
Offset position;
Offset posOffset = Offset(0.0, 0.0);
#override
void initState() {
WidgetsBinding.instance.addPostFrameCallback(_afterLayout);
_key = widget.key;
position = widget.initPos;
super.initState();
}
void _getRenderOffsets() {
final RenderBox renderBoxWidget = _key.currentContext.findRenderObject();
final offset = renderBoxWidget.localToGlobal(Offset.zero);
posOffset = offset - position;
}
void _afterLayout(_) {
_getRenderOffsets();
}
#override
Widget build(BuildContext context) {
return Positioned(
left: position.dx,
top: position.dy,
child: Listener(
child: Draggable(
child: Container(
width: 80,
height: 80,
color: widget.itmColor,
),
feedback: Container(
width: 82,
height: 82,
color: widget.itmColor,
),
childWhenDragging: Container(),
onDragEnd: (drag) {
setState(() {
if (drag.offset.dx > 0) {
position = drag.offset - posOffset;
} else {
position = widget.initPos;
}
});
},
),
),
);
}
}
I'm still interested if someone can find a proper solution to the initial issue :-)
you could use the property onDragEnd: of the widget Draggable and before setting the new position compare it with the height or width of your device using MediaQuery and update only if you didn't pass the limits of your screen, else set the new position to the initial one.
Example bellow :
Positioned(
left: position.dx,
top: position.dy,
child: Draggable(
maxSimultaneousDrags: 1,
childWhenDragging:
Opacity(opacity: .2, child: rangeEvent(context)),
feedback: rangeEvent(context),
axis: Axis.vertical,
affinity: Axis.vertical,
onDragEnd: (details) => updatePosition(details.offset),
child: Transform.scale(
scale: scale,
child: rangeEvent(context),
),
),
)
In the method updatePosition, you verify the new position before updating:
void updatePosition(Offset newPosition) => setState(() {
if (newPosition.dy > 10 &&
newPosition.dy < MediaQuery.of(context).size.height * 0.9) {
position = newPosition;
} else {
position = const Offset(0, 0);// initial possition
}
});

Multiple GestureDetector onPan event in Stack not working

I have to implement a custom analog clock where the clock hands indicate the breakfast,lunch and dinner time and user is able to reset the time by rotating the hands. I was able to draw the clock and hands with the help of canvas and CustomPaint and i haved stacked up in Stack Widget.I have added GestureDetector for each hand and using onPan events am able to rotate the hands also.But the issue is that when multiple hands are put in Stack only the last added clock hand is moving with the gesture others are not detecting the gesture.How to pass down the event to the stack so that all the gesture detectors detects it?
For drawing clock hands
class ClockHandComponent extends StatefulWidget {
final String title;
final int time;
final Color color;
const ClockHandComponent({Key key, this.title, this.time, this.color})
: super(key: key);
#override
ClockHandComponentState createState() {
return new ClockHandComponentState();
}
}
class ClockHandComponentState extends State<ClockHandComponent> {
int time;
double angle;
ClockHandPainter _clockHandPainter;
#override
void initState() {
time = widget.time;
angle = 2 * pi / 24 * time;
initialize();
super.initState();
}
#override
void didUpdateWidget(ClockHandComponent oldWidget) {
initialize();
super.didUpdateWidget(oldWidget);
}
#override
Widget build(BuildContext context) {
return GestureDetector(
onPanDown: _onPanDown,
onPanUpdate: _onPanUpdate,
onPanEnd: _onPanEnd,
child: new Container(
width: double.infinity,
height: double.infinity,
child: new CustomPaint(
painter: _clockHandPainter,
)),
);
}
_onPanUpdate(DragUpdateDetails details) {
print(details);
RenderBox renderBox = context.findRenderObject();
var position = renderBox.globalToLocal(details.globalPosition);
angle = -coordinatesToRadians(_clockHandPainter.center, position);
print(angle);
initialize();
setState(() {});
}
_onPanEnd(_) {}
_onPanDown(DragDownDetails details) {
print(details);
print(angle);
}
initialize() {
_clockHandPainter =
new ClockHandPainter(widget.title, time, widget.color, angle);
}
double coordinatesToRadians(Offset center, Offset coords) {
var a = coords.dx - center.dx;
var b = center.dy - coords.dy;
return atan2(b, a);
}
}
For drawing clock face
class ClockFaceComponent extends StatefulWidget {
#override
ClockFaceComponentState createState() {
return new ClockFaceComponentState();
}
}
class ClockFaceComponentState extends State<ClockFaceComponent> {
#override
Widget build(BuildContext context) {
return new Padding(
padding: const EdgeInsets.all(10.0),
child: new AspectRatio(
aspectRatio: 1.0,
child: new Container(
width: double.infinity,
decoration: new BoxDecoration(
shape: BoxShape.circle,
color: ColorResource.clockBackgroundColor,
),
child: new Stack(
fit: StackFit.expand,
children: <Widget>[
//dial and numbers
new Container(
width: double.infinity,
height: double.infinity,
child: new CustomPaint(
painter: new ClockDialPainter(),
),
),
new Center(
child: new Container(
width: 40.0,
height: 40.0,
decoration: new BoxDecoration(
shape: BoxShape.circle,
color: Colors.white,
),
)),
//centerpoint
new Center(
child: new Container(
width: 15.0,
height: 15.0,
decoration: new BoxDecoration(
shape: BoxShape.circle,
color: ColorResource.clockCenterColor,
),
),
),
getClockHands()
],
),
),
),
);
}
getClockHands() {
return new AspectRatio(
aspectRatio: 1.0,
child: new Stack(
fit: StackFit.expand,
children: getHands(),
));
}
getHands() {
List<Widget> widgets = new List();
List<Hands> hands = Hands.getHands();
for (Hands hand in hands) {
widgets.add(ClockHandComponent(
title: hand.title,
time: hand.time,
color: hand.color,
));
}
return widgets;
}
}