fix(kline-chart): prevent overflow in indicator selector and legend

- Wrap indicator selector Row in SingleChildScrollView for horizontal scrolling
- Add maxX boundary checks in _drawLegend to stop drawing when exceeding available space
- Prevents text overflow on narrow screens or when displaying many indicators

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
This commit is contained in:
hailin 2026-01-16 09:55:49 -08:00
parent 27bf67e561
commit d5e5bf642c
2 changed files with 43 additions and 35 deletions

View File

@ -232,36 +232,39 @@ class _KlineChartWidgetState extends State<KlineChartWidget> {
decoration: BoxDecoration(
border: Border(bottom: BorderSide(color: _borderGray)),
),
child: Row(
children: [
const Text('主图:', style: TextStyle(fontSize: 12, color: _grayText)),
const SizedBox(width: 8),
_buildIndicatorChip('MA', _selectedMainIndicator == 0, () {
setState(() => _selectedMainIndicator = 0);
}),
const SizedBox(width: 4),
_buildIndicatorChip('EMA', _selectedMainIndicator == 1, () {
setState(() => _selectedMainIndicator = 1);
}),
const SizedBox(width: 4),
_buildIndicatorChip('BOLL', _selectedMainIndicator == 2, () {
setState(() => _selectedMainIndicator = 2);
}),
const SizedBox(width: 16),
const Text('副图:', style: TextStyle(fontSize: 12, color: _grayText)),
const SizedBox(width: 8),
_buildIndicatorChip('MACD', _selectedSubIndicator == 0, () {
setState(() => _selectedSubIndicator = 0);
}),
const SizedBox(width: 4),
_buildIndicatorChip('KDJ', _selectedSubIndicator == 1, () {
setState(() => _selectedSubIndicator = 1);
}),
const SizedBox(width: 4),
_buildIndicatorChip('RSI', _selectedSubIndicator == 2, () {
setState(() => _selectedSubIndicator = 2);
}),
],
child: SingleChildScrollView(
scrollDirection: Axis.horizontal,
child: Row(
children: [
const Text('主图:', style: TextStyle(fontSize: 12, color: _grayText)),
const SizedBox(width: 8),
_buildIndicatorChip('MA', _selectedMainIndicator == 0, () {
setState(() => _selectedMainIndicator = 0);
}),
const SizedBox(width: 4),
_buildIndicatorChip('EMA', _selectedMainIndicator == 1, () {
setState(() => _selectedMainIndicator = 1);
}),
const SizedBox(width: 4),
_buildIndicatorChip('BOLL', _selectedMainIndicator == 2, () {
setState(() => _selectedMainIndicator = 2);
}),
const SizedBox(width: 16),
const Text('副图:', style: TextStyle(fontSize: 12, color: _grayText)),
const SizedBox(width: 8),
_buildIndicatorChip('MACD', _selectedSubIndicator == 0, () {
setState(() => _selectedSubIndicator = 0);
}),
const SizedBox(width: 4),
_buildIndicatorChip('KDJ', _selectedSubIndicator == 1, () {
setState(() => _selectedSubIndicator = 1);
}),
const SizedBox(width: 4),
_buildIndicatorChip('RSI', _selectedSubIndicator == 2, () {
setState(() => _selectedSubIndicator = 2);
}),
],
),
),
);
}

View File

@ -370,10 +370,12 @@ class KlinePainter extends CustomPainter {
final textPainter = TextPainter(textDirection: ui.TextDirection.ltr);
double x = leftPadding;
final y = 4.0;
final maxX = size.width - 60; //
if (maData != null) {
int colorIndex = 0;
for (final entry in maData!.entries) {
if (x > maxX) break; //
final lastValue = entry.value.lastWhere((v) => v != null, orElse: () => null);
if (lastValue != null) {
textPainter.text = TextSpan(
@ -381,6 +383,7 @@ class KlinePainter extends CustomPainter {
style: TextStyle(color: _maColors[colorIndex % _maColors.length], fontSize: 9),
);
textPainter.layout();
if (x + textPainter.width > maxX) break;
textPainter.paint(canvas, Offset(x, y));
x += textPainter.width + 8;
}
@ -391,6 +394,7 @@ class KlinePainter extends CustomPainter {
if (emaData != null) {
int colorIndex = 0;
for (final entry in emaData!.entries) {
if (x > maxX) break;
final lastValue = entry.value.lastWhere((v) => v != null, orElse: () => null);
if (lastValue != null) {
textPainter.text = TextSpan(
@ -398,6 +402,7 @@ class KlinePainter extends CustomPainter {
style: TextStyle(color: _maColors[colorIndex % _maColors.length], fontSize: 9),
);
textPainter.layout();
if (x + textPainter.width > maxX) break;
textPainter.paint(canvas, Offset(x, y));
x += textPainter.width + 8;
}
@ -405,10 +410,8 @@ class KlinePainter extends CustomPainter {
}
}
if (bollData != null) {
if (bollData != null && x <= maxX) {
final middle = bollData!['middle']?.lastWhere((v) => v != null, orElse: () => null);
final upper = bollData!['upper']?.lastWhere((v) => v != null, orElse: () => null);
final lower = bollData!['lower']?.lastWhere((v) => v != null, orElse: () => null);
if (middle != null) {
textPainter.text = TextSpan(
@ -416,8 +419,10 @@ class KlinePainter extends CustomPainter {
style: const TextStyle(color: _bollMiddleColor, fontSize: 9),
);
textPainter.layout();
textPainter.paint(canvas, Offset(x, y));
x += textPainter.width + 8;
if (x + textPainter.width <= maxX) {
textPainter.paint(canvas, Offset(x, y));
x += textPainter.width + 8;
}
}
}
}