how to add multiple sliders on one slider? #Flutter - android

I want to add multiple sliders in one slider. If you're not clear about what I'm asking please refer the below image
I want these three squares to be sliding and get the values of them.
I did some searching and could not find any flutter widget or a plugin that has the support.
I tried to use a stack and use multiple Slider widgets at the same location but it is also not working. (I know it's not a good approach.)
How can I make this happen. To have multiple sliders on the same line and get the values.
Any help or ideas are very much appreciated.

Using Stack with three sliders did not work because it was being overlapped.
I have made this Slider3X of being curious. There are few things need to fix here, start and end points missing some fractional position.
Code on Gist, dart pad
class Slider3x extends StatefulWidget {
const Slider3x({
Key? key,
required this.onSliderUpdate,
this.size = const Size(5, 10),
this.min = 0,
this.max = 1.0,
this.colorX = Colors.green,
this.colorY = Colors.blue,
this.colorZ = Colors.redAccent,
}) : super(key: key);
final Function(double? x, double? y, double? z) onSliderUpdate;
///size of moveable 3x point 😅, forgot the name maybe thumbs
final Size size;
final double? min;
final double? max;
final Color colorX;
final Color colorY;
final Color colorZ;
#override
State<Slider3x> createState() => _Slider3xState();
}
class _Slider3xState extends State<Slider3x> {
/// three slider position
double? x;
double? y;
double? z;
final double tapSpacesArea = .05;
// currect active slider , help to prevent overlLAp while sliding
int activeSliderNumber = 0;
//* Update sldier
void _updateSlider(double dx, double maxWidth) {
final tapPosition = dx;
//* update logic
if (tapPosition <= 0 || tapPosition >= maxWidth) {
return;
}
//* update on UI based on slider number
if (activeSliderNumber == 0) {
setState(() {
x = tapPosition;
});
} else if (activeSliderNumber == 1) {
setState(() {
y = tapPosition;
});
} else if (activeSliderNumber == 2) {
setState(() {
z = tapPosition;
});
}
//pass value on main widget
widget.onSliderUpdate(
dp(_generateSliderValue(maxWidth: maxWidth, x: x!)),
dp(_generateSliderValue(maxWidth: maxWidth, x: y!)),
dp(_generateSliderValue(maxWidth: maxWidth, x: z!)),
);
}
//round number
double dp(double val, {int places = 2}) {
num mod = pow(10.0, places);
return ((val * mod).round().toDouble() / mod);
}
//* calculate slider value
double _generateSliderValue({
required double maxWidth,
required double x,
}) {
// x is slider original position on width:maxWidth
return (widget.max! - widget.min!) * (x / maxWidth) + widget.min!;
}
//* select ActiveSlider, fixed overLap issue
//* slider Selector logic
void _selectSlider({
required double maxWidth,
required double tapPosition,
}) {
final maxArea = maxWidth * tapSpacesArea;
if ((tapPosition - x!).abs() < maxArea) {
setState(() {
activeSliderNumber = 0;
});
} else if ((tapPosition - y!).abs() < maxArea) {
setState(() {
activeSliderNumber = 1;
});
} else if ((tapPosition - z!).abs() < maxArea) {
setState(() {
activeSliderNumber = 2;
});
}
}
#override
Widget build(BuildContext context) {
return Padding(
padding: const EdgeInsets.all(8.0),
child: Column(
mainAxisSize: MainAxisSize.min,
children: [
SizedBox(
height: 50,
child: LayoutBuilder(builder: (context, constraints) {
final maxWidth = constraints.maxWidth - 10;
x = x ?? 0;
y = y ?? constraints.maxWidth / 2;
z = z ?? maxWidth;
return Stack(
alignment: Alignment.center,
children: [
Positioned(
left: x,
child: Container(
height: activeSliderNumber == 0
? widget.size.height * 1.5
: widget.size.height,
width: widget.size.width,
color: widget.colorX,
),
),
//* paint Y
Positioned(
left: y,
child: Container(
height: activeSliderNumber == 1
? widget.size.height * 1.5
: widget.size.height,
width: widget.size.width,
color: widget.colorY,
),
),
//* paint z
Positioned(
left: z,
child: Container(
height: activeSliderNumber == 2
? widget.size.height * 1.5
: widget.size.height,
width: widget.size.width,
color: widget.colorZ,
),
),
const Divider(
endIndent: 10,
),
GestureDetector(
onTapDown: (details) => _selectSlider(
maxWidth: maxWidth,
tapPosition: details.localPosition.dx),
onPanUpdate: (details) =>
_updateSlider(details.localPosition.dx, maxWidth),
),
],
);
}),
),
Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: [
Text(
widget.min.toString(),
),
Text(
widget.max.toString(),
),
],
)
],
),
);
}
}

Related

Swipe effect like inshorts news app for Flutter

Unable to achieve the page to start from the bottom in the page viewer I have tried converted bit of code this but was unable to load it from bottom.
Reference link to what I am trying to achieve : Swipe effect like inshorts news app
Here is my code for what I am trying
class InshortsPageTransformer extends PageTransformer {
#override
Widget transform(Widget child, TransformInfo info) {
double position = info.position ?? 0;
double rotation = position * 0.2;
double scale = math.max(0.8, 1 - position.abs());
if (position < -1) {
rotation = 0;
child = Opacity(
opacity: 0,
child: child,
);
} else if (position <= 0) {
child = Opacity(
opacity: 1,
child: child,
);
child = Transform(
transform: Matrix4.identity()
..setEntry(3, 2, 0.001)
..rotateY(rotation)
..scale(scale),
alignment: Alignment.center,
child: child,
);
} else if (position <= 1) {
child = Opacity(
opacity: 1,
child: child,
);
child = Transform(
transform: Matrix4.identity()
..setEntry(3, 2, 0.001)
..rotateY(rotation)
..scale(scale),
alignment: Alignment.center,
child: child,
);
} else {
rotation = 0;
child = Opacity(
opacity: 0,
child: child,
);
}
return Transform(
transform: Matrix4.identity()
..setEntry(3, 2, 0.001)
..rotateY(rotation)
..scale(scale),
alignment: Alignment.center,
child: child,
);
}
}
class ScaleAndFadeTransformer extends PageTransformer {
final double _scale;
final double _fade;
ScaleAndFadeTransformer({double fade = 0.3, double scale = 0.8})
: _fade = fade,
_scale = scale;
#override
Widget transform(Widget item, TransformInfo info) {
double position = info.position ?? 0;
double scaleFactor = (1 - position.abs()) * (1 - _scale);
double fadeFactor = (1 - position.abs()) * (1 - _fade);
double opacity = _fade + fadeFactor;
double scale = _scale + scaleFactor;
return Opacity(
opacity: opacity,
child: Transform.scale(
scale: scale,
child: item,
),
);
}
}
I recently created the same project look into it
https://github.com/bibek-ranjan-saha/prodt_test
I have used https://pub.dev/packages/another_transformer_page_view this package for achieving this effect.
sample code snippet
TransformerPageView(
scrollDirection: Axis.vertical,
itemCount: snapshot.data?.data.length ?? 0,
loop: false,
controller: homePageController,
transformer: DeepthPageTransformer()
itemBuilder: (context, index) {
return NewsView(
screenSize: screenSize,
news: snapshot.data!.data[index],
);
},
);

Resizing a rotated container in flutter from corners and middle points ( 8 points)

I am developing an shape editor in flutter. For that, I need to add a feature of adding shapes dynamically to the screen. Each of them should be able to independently scale, position, and rotate. There should be an anchor point like in word or in Photoshop to indicate the active shapes and to scale and rotate them. The shape should be positioned with drag and drop.
I found this answer :
Rotate and resize container
But the answer only rotate the shape from the center, I want to scale the shape from the corners also.
The code :
class FooResizer extends StatefulWidget {
#override
_FooResizerState createState() => _FooResizerState();
}
class _FooResizerState extends State<FooResizer> with TickerProviderStateMixin, ChangeNotifier {
Sizer currentSizer;
double angle = 0.0;
AnimationController ctrl;
final Map<String, Sizer> sizers = {
'l': Sizer('l', Alignment.centerLeft, {'t': 0.5, 'b': 0.5, 'M': 0.5}),
't': Sizer('t', Alignment.topCenter, {'l': 0.5, 'r': 0.5, 'M': 0.5}),
'r': Sizer('r', Alignment.centerRight, {'t': 0.5, 'b': 0.5, 'R': 1.0, 'M': 0.5}),
'b': Sizer('b', Alignment.bottomCenter, {'l': 0.5, 'r': 0.5, 'R': 1.0, 'M': 0.5}),
'R': Sizer('R', Alignment.bottomRight, {}),
'M': Sizer('M', Alignment.center, {}),
};
#override
void initState() {
super.initState();
ctrl = AnimationController(vsync: this, duration: Duration(milliseconds: 300), value: 1.0);
}
#override
Widget build(BuildContext context) {
return ClipRect(
child: ColoredBox(
color: Colors.black12,
// CustomMultiChildLayoutPainter:
// https://gist.github.com/pskink/0f82724b41d9ebe89604782fbf62fe03#file-multi_layout_painter-dart-L447
child: CustomMultiChildLayoutPainter(
delegate: _FooResizerDelegate(sizers, this),
children: [
LayoutId(
id: 'body',
child: AnimatedBuilder(
animation: this,
builder: (ctx, child) {
return Transform.rotate(
angle: angle,
child: child,
);
},
child: Material(color: Colors.grey[300], elevation: 4, child: FlutterLogo()),
),
),
...sizers.values.map(_sizerBuilder),
],
),
),
);
}
Widget _sizerBuilder(Sizer sizer) {
final colors = {
'M': Colors.orange,
'R': Colors.teal,
};
return LayoutId(
id: sizer.id,
child: GestureDetector(
onPanStart: (details) => _panStart(sizer),
onPanUpdate: _panUpdate,
onPanEnd: _panEnd,
child: AnimatedBuilder(
animation: ctrl,
builder: (context, child) {
final color = colors[sizer.id] ?? Colors.green;
return Opacity(
opacity: currentSizer == sizer? 1.0 : Curves.ease.transform(ctrl.value),
child: Container(
decoration: ShapeDecoration(
shape: CircleBorder(side: BorderSide(width: lerpDouble(0.0, 2.0, ctrl.value), color: Colors.black38)),
color: currentSizer == sizer? Color.lerp(Colors.deepPurple, color, ctrl.value) : color,
shadows: [BoxShadow.lerp(BoxShadow(spreadRadius: 2, blurRadius: 4, offset: Offset(2, 2)), null, ctrl.value)],
),
),
);
}
),
),
);
}
_panStart(Sizer sizer) {
currentSizer = sizer;
ctrl.reverse();
}
_panUpdate(DragUpdateDetails details) {
assert(currentSizer != null);
if (currentSizer.id == 'M') {
// move
sizers.values.forEach((sizer) => sizer.center += details.delta);
} else
if (currentSizer.id == 'R') {
// rotate
final localCenter = sizers['M'].center;
final globalCenter = (context.findRenderObject() as RenderBox).localToGlobal(localCenter);
final angle0 = (details.globalPosition - details.delta - globalCenter).direction;
final angle1 = (details.globalPosition - globalCenter).direction;
final deltaAngle = angle1 - angle0;
sizers.values
.where((sizer) => sizer.id != 'M')
.forEach((sizer) {
final vector = sizer.center - localCenter;
sizer.center = localCenter + Offset.fromDirection(vector.direction + deltaAngle, vector.distance);
});
angle += deltaAngle;
} else {
// resize
final adjustedAngle = angle + currentSizer.angleAdjustment;
final rotatedDistance = details.delta.distance * math.cos(details.delta.direction - adjustedAngle);
final vector = Offset.fromDirection(adjustedAngle, rotatedDistance);
currentSizer.center += vector;
currentSizer.dependents.forEach((id, factor) => sizers[id].center += vector * factor);
}
notifyListeners();
}
_panEnd(DragEndDetails details) {
assert(currentSizer != null);
// currentSizer = null;
ctrl.forward();
}
}
class _FooResizerDelegate extends MultiChildLayoutPainterDelegate {
static const SIZE = 48.0;
final Map<String, Sizer> sizers;
_FooResizerDelegate(this.sizers, Listenable relayout) : super(relayout: relayout);
#override
void performLayout(Size size) {
sizers['M'].center ??= init(size);
for (var sizer in sizers.values) {
layoutChild(sizer.id, BoxConstraints.tight(Size(SIZE, SIZE)));
positionChild(sizer.id, sizer.center - Offset(SIZE / 2, SIZE / 2));
}
final w = (sizers['l'].center - sizers['r'].center).distance;
final h = (sizers['t'].center - sizers['b'].center).distance;
layoutChild('body', BoxConstraints.tight(Size(w, h)));
positionChild('body', sizers['M'].center - Offset(w / 2, h / 2));
}
Offset init(Size size) {
final rect = (Offset.zero & size).deflate(24);
print('init rect: $rect');
for (var sizer in sizers.values) {
sizer
..center = sizer.alignment.withinRect(rect)
..angleAdjustment = sizer.alignment.x == 0? math.pi / 2 : 0;
}
return sizers['M'].center;
}
#override
bool shouldRelayout(covariant MultiChildLayoutDelegate oldDelegate) => true;
#override
void foregroundPaint(Canvas canvas, Size size) {
}
#override
void paint(Canvas canvas, Size size) {
final w = (sizers['r'].center - sizers['l'].center).distance;
final h = (sizers['b'].center - sizers['t'].center).distance;
final rect = Rect.fromCenter(center: sizers['M'].center, width: w, height: h);
final angle = (sizers['r'].center - sizers['l'].center).direction;
final matrix = Matrix4.identity()
..translate(rect.center.dx, rect.center.dy)
..rotateZ(angle)
..translate(-rect.center.dx, -rect.center.dy);
final transformedRect = MatrixUtils.transformRect(matrix, rect);
final points = [
Offset(transformedRect.left, 0), Offset(transformedRect.left, size.height),
Offset(0, transformedRect.top), Offset(size.width, transformedRect.top),
Offset(transformedRect.right, 0), Offset(transformedRect.right, size.height),
Offset(0, transformedRect.bottom), Offset(size.width, transformedRect.bottom),
];
canvas.drawPoints(PointMode.lines, points, Paint());
}
}
class Sizer {
final String id;
final Alignment alignment;
final Map<String, double> dependents;
Offset center;
double angleAdjustment;
Sizer(this.id, this.alignment, this.dependents);
}
The result :
what I am trying to achieve :

How to set any widget on screen at specific position with responsiveness in flutter

I am trying to set any widget to screen at any position so if user open app in other devices the widget must should remain at that give position , for example if I set widget on 1/4 part of the screen then it should be the same in every device . I tested in simulator from Iphone 8 to Ipad 8th gen.
Below is my code I am using Positioned.fromRect and passing Offset(dx,dy). I also tried MediaQuery but all in vain . Please guide me even if you find my question stupid.
Reference app Story click or post maker app with pre built in templates.
This is simple constructor.
FlutterSimpleStickerImage(
this.image, {
Key key,
this.width,
this.height,
this.viewport,
this.minScale = 1.0,
this.maxScale = 2.0,
this.onTapRemove,
}) : super(key: key);
Widget image;
final double width;
final double height;
final Size viewport;
final double minScale;
final double maxScale;
A class for positioning widget in stack the Position.fromRect()
class _FlutterSimpleStickerImageState extends State<FlutterSimpleStickerImage> {
_FlutterSimpleStickerImageState();
double _scale = 1.0;
double _previousScale = 1.0;
Offset _previousOffset = Offset(0, 0);
Offset _startingFocalPoint = Offset(0, 0);
Offset _offset = Offset(0, 0);
double _rotation = 0.0;
double _previousRotation = 0.0;
bool _isSelected = false;
#override
void dispose() {
super.dispose();
_offset = Offset(0, 0);
_scale = 1.0;
}
#override
Widget build(BuildContext context) {
double sizewidth=183;
double sizeheight=0;
return Positioned.fromRect(
rect: Rect.fromPoints(Offset(sizewidth, sizeheight),Offset(sizewidth , sizeheight )),
child: Container(
child: Stack(
children: <Widget>[
Center(
child: Transform(
transform: Matrix4.diagonal3(Vector3(_scale, _scale, _scale)),
// ..setRotationZ(_rotation),
alignment: FractionalOffset.center,
child: GestureDetector(
onScaleStart: (ScaleStartDetails details) {
_startingFocalPoint = Offset(sizewidth,sizeheight);
_previousOffset = Offset(sizewidth,sizeheight);
_previousRotation = _rotation;
_previousScale = _scale;
// print(
// "begin - focal : ${details.focalPoint}, local : ${details.localFocalPoint}");
},
onScaleUpdate: (ScaleUpdateDetails details) {
_scale = min(
max(_previousScale * details.scale, widget.minScale),
widget.maxScale);
_rotation = details.rotation;
final Offset normalizedOffset =
(_startingFocalPoint - _previousOffset) /
_previousScale;
Offset __offset =
details.focalPoint - (normalizedOffset * _scale);
__offset = Offset(max(__offset.dx, -widget.width),
max(__offset.dy, -widget.height));
__offset = Offset(min(__offset.dx, widget.viewport.width),
min(__offset.dy, widget.viewport.height));
setState(() {
_offset = __offset;
// print("move - $_offset, scale : $_scale");
});
},
onTap: () {
setState(() {
_isSelected = !_isSelected;
});
},
onTapCancel: () {
setState(() {
_isSelected = false;
});
},
onDoubleTap: () {
setState(() {
_scale = 1.0;
});
},
onLongPress: (){
setState(() {
widget.image=Container(height: 100,width: 100,color: Color.fromRGBO(255, 150, 0, 1.0),);
});
},
child: Container(child: widget.image),
),
),
),
_isSelected
? Positioned(
top: 12,
right: 12,
width: 24,
height: 24,
child: Container(
child: IconButton(
padding: EdgeInsets.zero,
icon: Icon(Icons.remove_circle),
color: Color.fromRGBO(255, 150, 0, 1.0),
onPressed: () {
print('tapped remove sticker');
if (this.widget.onTapRemove != null) {
this.widget.onTapRemove(this.widget);
}
},
),
),
)
: Container(),
],
),
),
);
}
void hideRemoveButton() {
setState(() {
_isSelected = false;
});
}
}

Flutter: How to draw a star

I am trying to custom my container shape to look like this:
I tried to do it with customPaint but I don't know very well this widget so I need help.
How can I draw a shape like this? Is customPaint the right solution?
You can copy paste run full code below
modified code of package https://pub.dev/packages/flutter_custom_clippers 's
StarClipper https://github.com/lohanidamodar/flutter_custom_clippers/blob/master/lib/src/star_clipper.dart
code snippet
class StarClipper extends CustomClipper<Path>
#override
Path getClip(Size size) {
...
double radius = halfWidth / 1.3;
...
Container(
height: 200,
width: 200,
child: ClipPath(
clipper: StarClipper(14),
child: Container(
height: 150,
color: Colors.green[500],
child: Center(child: Text("+6", style: TextStyle(fontSize: 50),)),
),
),
),
working demo
full code
import 'package:flutter/material.dart';
import 'dart:math' as math;
class StarClipper extends CustomClipper<Path> {
StarClipper(this.numberOfPoints);
/// The number of points of the star
final int numberOfPoints;
#override
Path getClip(Size size) {
double width = size.width;
print(width);
double halfWidth = width / 2;
double bigRadius = halfWidth;
double radius = halfWidth / 1.3;
double degreesPerStep = _degToRad(360 / numberOfPoints);
double halfDegreesPerStep = degreesPerStep / 2;
var path = Path();
double max = 2 * math.pi;
path.moveTo(width, halfWidth);
for (double step = 0; step < max; step += degreesPerStep) {
path.lineTo(halfWidth + bigRadius * math.cos(step),
halfWidth + bigRadius * math.sin(step));
path.lineTo(halfWidth + radius * math.cos(step + halfDegreesPerStep),
halfWidth + radius * math.sin(step + halfDegreesPerStep));
}
path.close();
return path;
}
num _degToRad(num deg) => deg * (math.pi / 180.0);
#override
bool shouldReclip(CustomClipper<Path> oldClipper) {
StarClipper oldie = oldClipper as StarClipper;
return numberOfPoints != oldie.numberOfPoints;
}
}
void main() {
runApp(MyApp());
}
class MyApp extends StatelessWidget {
#override
Widget build(BuildContext context) {
return MaterialApp(
title: 'Flutter Demo',
theme: ThemeData(
primarySwatch: Colors.blue,
visualDensity: VisualDensity.adaptivePlatformDensity,
),
home: MyHomePage(title: 'Flutter Demo Home Page'),
);
}
}
class MyHomePage extends StatefulWidget {
MyHomePage({Key key, this.title}) : super(key: key);
final String title;
#override
_MyHomePageState createState() => _MyHomePageState();
}
class _MyHomePageState extends State<MyHomePage> {
int _counter = 0;
void _incrementCounter() {
setState(() {
_counter++;
});
}
#override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: Text(widget.title),
),
body: Center(
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: <Widget>[
Container(
height: 200,
width: 200,
child: ClipPath(
clipper: StarClipper(14),
child: Container(
height: 150,
color: Colors.green[500],
child: Center(child: Text("+6", style: TextStyle(fontSize: 50),)),
),
),
),
],
),
),
floatingActionButton: FloatingActionButton(
onPressed: _incrementCounter,
tooltip: 'Increment',
child: Icon(Icons.add),
),
);
}
}
I was trying to create Turkish flag for fun. Creating crescent was easy but creating star is not look easy. so I just tried to use my geometric and trigonometric knowledge to draw but no success. while continuing searching I encountered this link . Yeess I found what I look for. of course it was about different topic but functions was useful. so I reach my goal at the end of night.
So creating custom stars are possible with CustomPainter since now. Because I m sleepy and tired of being awake whole night I share all of code block with no further explanation. You can adjust offsetts according to your need. if any question. I m ready to answer. Enjoy coding.
class TurkishFlagPaint extends CustomPainter {
#override
void paint(Canvas canvas, Size size) {
// x and y coordinates of starting point
final bx = 95;
final by = 0;
final paint = Paint();
paint.color = Colors.white;
final innerCirclePoints = 5; //how many edges you need?
final innerRadius = 80 / innerCirclePoints;
final innerOuterRadiusRatio = 2.5;
final outerRadius = innerRadius * innerOuterRadiusRatio;
List<Map> points =
calcStarPoints(bx, by, innerCirclePoints, innerRadius, outerRadius);
var star = Path()..moveTo(points[0]['x'], points[0]['y']);
points.forEach((point) {
star.lineTo(point['x'], point['y']);
});
canvas.drawPath(
Path.combine(
PathOperation.union,
//this combine for crescent
Path.combine(
PathOperation.difference,
Path()..addOval(Rect.fromCircle(center: Offset(-20, 0), radius: 80)),
Path()
..addOval(Rect.fromCircle(center: Offset(2, 0), radius: 60))
..close(),
),
star,// I also combine cresscent with star
),
paint,
);
}
//This function is life saver.
//it produces points for star edges inner and outer. if you need to //rotation of star edges.
// just play with - 0.3 value in currX and currY equations.
List<Map> calcStarPoints(
centerX, centerY, innerCirclePoints, innerRadius, outerRadius) {
final angle = ((math.pi) / innerCirclePoints);
var angleOffsetToCenterStar = 0;
var totalPoints = innerCirclePoints * 2; // 10 in a 5-points star
List<Map> points = [];
for (int i = 0; i < totalPoints; i++) {
bool isEvenIndex = i % 2 == 0;
var r = isEvenIndex ? outerRadius : innerRadius;
var currY =
centerY + math.cos(i * angle + angleOffsetToCenterStar - 0.3) * r;
var currX =
centerX + math.sin(i * angle + angleOffsetToCenterStar - 0.3) * r;
points.add({'x': currX, 'y': currY});
}
return points;
}
#override
bool shouldRepaint(CustomPainter oldDelegate) => true;
}
now you can use painter in a widget;
Center(
child: CustomPaint(
painter: TurkishFlagPaint(),
),
),
and the result will be like this :
Yes, CustomPaint is the right solution. You can calculate the Path (series of points around the Container) and then paint it with the drawPath method of the canvas.
For example, the path of a triangle would look like this:
return Path()
..moveTo(0, y)
..lineTo(x / 2, 0)
..lineTo(x, y)
..lineTo(0, y);
The Path starts at (0,y) (top-left), then a line to (x/2,0) (bottom-center) gets added and so on. This snipped was taken from this answer.
This code will help you build a centered star, to use it you just have to instantiate it in a ClipPath
import 'package:flutter/material.dart';
import 'dart:math' as math;
const STAR_POINTS = 5;
class StarClipper extends CustomClipper<Path> {
#override
Path getClip(Size size) {
var centerX = size.width / 2;
var centerY = size.height / 2;
var path = Path();
var radius = size.width / 2;
var inner = radius / 2;
var rotation = math.pi / 2 * 3;
var step = math.pi / STAR_POINTS;
path.lineTo(centerX, centerY - radius);
for (var i = 0; i < STAR_POINTS; i++) {
var x = centerX + math.cos(rotation) * radius;
var y = centerY + math.sin(rotation) * radius;
path.lineTo(x, y);
rotation += step;
x = centerX + math.cos(rotation) * inner;
y = centerY + math.sin(rotation) * inner;
path.lineTo(x, y);
rotation += step;
}
path.lineTo(centerX, centerY - radius);
path.close();
return path;
}
#override
bool shouldReclip(CustomClipper<Path> oldClipper) => false;
}
/// Instance
ClipPath(
clipper: StarClipper(),
child: Container(
color: Colors.red,
width: 80,
height: 80,
),
)

'The method 'call' was called on null.' in Flutter

I am trying to extract a value, touchedIndex, from a child widget PieChartWidget that I have to make a pie chart with a hole in the middle using fl_chart, into its parent widget Co2Tracker to allow me to create some text in the middle of the pie chart that changes when a segment of the pie chart is pressed. This returns an error but everything works visually other than the pie chart segments no longer expand.To make debugging easier I replaced the setState(){isTouched = isTouchedNew} with print(isTouchedNew) but this still returns the error below, however, the pie chart now expands.
PieChart example
When using a callback function
int isTouched = -1;
setIsTouched(int isTouchedNew) {
print(isTouchedNew);
}
integrated within a stateful widget I get an output of
════════ Exception caught by gesture ═══════════════════════════════════════════════════════════════
The method 'call' was called on null.
Receiver: null
Tried calling: call(-1)
parent main page
class Co2Tracker extends StatefulWidget {
#override
_Co2TrackerState createState() => _Co2TrackerState();
}
class _Co2TrackerState extends State<Co2Tracker> {
List<String> pieNames = ['Travel', 'Gas', 'Electricity', 'Food shop', 'Water',];
int isTouched = -1;
double radiusNormal = 60;
double centerRadius = 50;
double radiusExpansion = 1.2;
setIsTouched(int isTouchedNew) {
print(isTouchedNew);
}
#override
Widget build(BuildContext context) {
//double co2 = ModalRoute.of(context).settings.arguments;
return Scaffold(
appBar: appBarTemplate('CO\u2082 Tracker'),
body: Column(
children: <Widget>[
Padding(
padding: const EdgeInsets.symmetric(vertical: 15),
child: textStyleTemplateLarge(text: 'Your CO\u2082 emissions today', textColor: Colors.green[400]),
),
AspectRatio(
aspectRatio: 200/((radiusExpansion*radiusNormal) + centerRadius),
child: Row(
children: <Widget>[
Expanded(
child: Stack(
children: <Widget>[
Center(
child: FlatButton(child: textStyleTemplateLarge(text: '70kg', textColor: Colors.black),),
),
Center(
child: PieChartWidget(radiusNormal: radiusNormal, centerRadius: centerRadius, radiusExpanded: (radiusExpansion*radiusNormal), pieNames: pieNames,),
),
],
),
),
],
),
),
],
),
);
}
}
child pie chart widget
import 'package:flutter/material.dart';
import 'package:fl_chart/fl_chart.dart';
class PieChartWidget extends StatefulWidget {
final double radiusNormal;
final double radiusExpanded;
final double centerRadius;
final double fontSizeNormal;
final double fontSizeExpanded;
final List<String> pieNames;
final Function(int) setIsTouched;
PieChartWidget({this.radiusNormal, this.radiusExpanded, this.centerRadius, this.fontSizeNormal, this.fontSizeExpanded, this.pieNames, this.setIsTouched});
#override
_PieChartWidgetState createState() => _PieChartWidgetState(radiusNormal: radiusNormal, radiusExpanded: radiusExpanded, centerRadius: centerRadius,
fontSizeNormal: fontSizeNormal, fontSizeExpanded: fontSizeExpanded, pieNames: pieNames, setIsTouched: setIsTouched,);
}
class _PieChartWidgetState extends State<PieChartWidget> {
int touchedIndex = -1;
double radiusNormal;
double radiusExpanded;
double centerRadius;
double fontSizeNormal;
double fontSizeExpanded;
List<String> pieNames;
Function(int) setIsTouched;
_PieChartWidgetState({this.radiusNormal = 50, this.radiusExpanded = 60, this.centerRadius = 40, this.fontSizeNormal = 16, this.fontSizeExpanded = 25,
this.pieNames, this.setIsTouched,});
#override
Widget build(BuildContext context) {
return PieChart(
PieChartData(
sections: pieSections(pieNames),
borderData: FlBorderData(show: false),
sectionsSpace: 0,
centerSpaceRadius: centerRadius,
pieTouchData: PieTouchData(touchCallback: (pieTouchResponse) {
setState(() {
if (pieTouchResponse.touchInput is FlLongPressEnd ||
pieTouchResponse.touchInput is FlPanEnd) {
touchedIndex = -1;
} else {
touchedIndex = pieTouchResponse.touchedSectionIndex;
}
});
widget.setIsTouched(touchedIndex);
}),
),
);
}
-----------------I-think-everything-below-this-is-irrelevant-but-I-could-be-wrong-------------------------------------------
List<PieChartSectionData> pieSections(List<String> pieNames,) {
return List.generate(5, (i) {
final isTouched = i == touchedIndex;
final double fontSize = isTouched ? 25 : 16;
final double radius = isTouched ? radiusExpanded : radiusNormal;
final int colorNumber = (1+i) * 100;
final List<double> valueList = [20, 10, 10, 20, 30];
final double total = valueList.fold(0, (a, b) => a+b);
switch (i) {
case 0:
return sectionData(valueList[i], Colors.pink[300], title(isTouched, valueList[i], total, pieNames[i]), radius, fontSize);
case 1:
return sectionData(valueList[i], Colors.blue[600], title(isTouched, valueList[i], total, pieNames[i]), radius, fontSize);
case 2:
return sectionData(valueList[i], Colors.amber[300], title(isTouched, valueList[i], total, pieNames[i]), radius, fontSize);
case 3:
return sectionData(valueList[i], Colors.purple[300], title(isTouched, valueList[i], total, pieNames[i]), radius, fontSize);
case 4:
return sectionData(valueList[i], Colors.blue[300], title(isTouched, valueList[i], total, pieNames[i]), radius, fontSize);
default:
return null;
}
});
}
PieChartSectionData sectionData(double value, Color color, String title, double radius, double fontSize,){
return PieChartSectionData(
value: value, color: color, title: title, radius: radius,
titleStyle: TextStyle(color: Colors.black, fontSize: fontSize, fontWeight: FontWeight.bold),
);
}
String title(bool isTouched, double value, double total, String title,) {
return isTouched ? '${double.parse((value/total*100).toStringAsFixed(2))}%' : '$title';
}
}
My thoughts so far have been that maybe it is to do with initialization of variables or maybe this touchCallback function from the child page line 43 but no luck so far.
Thanks in advance for your time.
The problem is that when your printing an integer.
setIsTouched(int isTouchedNew) { print(isTouchedNew); }
You're supposed to print it like this
print("${isTouchedNew}")
Umm..not sure if you tried this,
int isTouched = -1;
setIsTouched(int isTouchedNew) {
setState(() {
isTouched=isTouchedNew;
print(isTouchedNew);
}
}

Categories

Resources