@@ -164,18 +164,29 @@ class FlutterCirclePackChartPainter extends CustomPainter {
164164 ) {
165165 if (! showValue && ! showLabels) return ;
166166
167- final double valueFontSize = (baseFontSize / cameraScale).clamp (0.1 , 100.0 );
168- final double labelFontSize = ((baseFontSize * 0.75 ) / cameraScale).clamp (0.1 , 100.0 );
169- final double maxWidth = radius * 2.5 ;
167+ // --- MATHEMATICALLY STABLE FONT SIZING & SPACING ---
168+ // Instead of using TextPainter.height (which jitters), we use a fixed multiplier
169+ // of the baseFontSize for vertical positioning.
170+
171+ final double valueSize = (baseFontSize / cameraScale).clamp (0.1 , 100.0 );
172+ final double labelSize = ((baseFontSize * 0.75 ) / cameraScale).clamp (0.1 , 100.0 );
173+
174+ // Total visual vertical height block (in canvas units)
175+ // 1.2 and 1.0 are stable line-height multipliers.
176+ final double valueHeight = showValue ? (valueSize * 1.2 ) : 0.0 ;
177+ final double labelHeight = showLabels ? (labelSize * 1.0 ) : 0.0 ;
178+ final double totalHeight = valueHeight + labelHeight;
179+ final double maxWidth = radius * 2.2 ; // Tighter constraint to avoid edges
180+
181+ double currentY = center.dy - (totalHeight / 2 );
170182
171- TextPainter ? valuePainter;
172183 if (showValue) {
173- valuePainter = TextPainter (
184+ final valuePainter = TextPainter (
174185 text: TextSpan (
175186 text: node.displayValue ?? node.value.toStringAsFixed (0 ),
176187 style: TextStyle (
177188 color: Colors .white.withValues (alpha: opacity),
178- fontSize: valueFontSize ,
189+ fontSize: valueSize ,
179190 fontWeight: FontWeight .bold,
180191 ),
181192 ),
@@ -184,16 +195,21 @@ class FlutterCirclePackChartPainter extends CustomPainter {
184195 maxLines: 1 ,
185196 ellipsis: '...' ,
186197 )..layout (maxWidth: maxWidth);
198+
199+ valuePainter.paint (
200+ canvas,
201+ Offset (center.dx - (valuePainter.width / 2 ), currentY),
202+ );
203+ currentY += valueHeight; // Advance by the FIXED multiplier, not dynamic height
187204 }
188205
189- TextPainter ? labelPainter;
190206 if (showLabels) {
191- labelPainter = TextPainter (
207+ final labelPainter = TextPainter (
192208 text: TextSpan (
193209 text: node.label,
194210 style: TextStyle (
195211 color: Colors .white.withValues (alpha: opacity),
196- fontSize: labelFontSize ,
212+ fontSize: labelSize ,
197213 fontWeight: FontWeight .normal,
198214 ),
199215 ),
@@ -202,24 +218,7 @@ class FlutterCirclePackChartPainter extends CustomPainter {
202218 maxLines: 1 ,
203219 ellipsis: '...' ,
204220 )..layout (maxWidth: maxWidth);
205- }
206-
207- // Calculate total vertical height and center
208- double totalHeight = 0 ;
209- if (valuePainter != null ) totalHeight += valuePainter.height;
210- if (labelPainter != null ) totalHeight += labelPainter.height;
211-
212- double currentY = center.dy - (totalHeight / 2 );
213-
214- if (valuePainter != null ) {
215- valuePainter.paint (
216- canvas,
217- Offset (center.dx - (valuePainter.width / 2 ), currentY),
218- );
219- currentY += valuePainter.height;
220- }
221221
222- if (labelPainter != null ) {
223222 labelPainter.paint (
224223 canvas,
225224 Offset (center.dx - (labelPainter.width / 2 ), currentY),
0 commit comments