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:
parent
27bf67e561
commit
d5e5bf642c
|
|
@ -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);
|
||||
}),
|
||||
],
|
||||
),
|
||||
),
|
||||
);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
Loading…
Reference in New Issue