💄 Better graph card

This commit is contained in:
2025-09-14 15:04:11 +08:00
parent d652df407f
commit a1d4400455

View File

@@ -1,5 +1,7 @@
import 'package:flutter/material.dart'; import 'package:flutter/material.dart';
import 'package:fl_chart/fl_chart.dart'; import 'package:fl_chart/fl_chart.dart';
import 'package:google_fonts/google_fonts.dart';
import 'package:latext/latext.dart';
import 'package:simple_math_calc/parser.dart'; import 'package:simple_math_calc/parser.dart';
import 'package:simple_math_calc/calculator.dart'; import 'package:simple_math_calc/calculator.dart';
import 'package:simple_math_calc/solver.dart'; import 'package:simple_math_calc/solver.dart';
@@ -25,6 +27,7 @@ class GraphCard extends StatefulWidget {
class _GraphCardState extends State<GraphCard> { class _GraphCardState extends State<GraphCard> {
final SolverService _solverService = SolverService(); final SolverService _solverService = SolverService();
FlSpot? _currentTouchedPoint;
/// 生成函数图表的点 /// 生成函数图表的点
List<FlSpot> _generatePlotPoints(String expression, double zoomFactor) { List<FlSpot> _generatePlotPoints(String expression, double zoomFactor) {
@@ -145,6 +148,19 @@ class _GraphCardState extends State<GraphCard> {
); );
} }
String _formatAxisValue(double value) {
if (value.abs() < 1e-10) return "0";
if ((value - value.roundToDouble()).abs() < 1e-10) {
return value.round().toString();
}
double absVal = value.abs();
if (absVal >= 100) return value.toStringAsFixed(0);
if (absVal >= 10) return value.toStringAsFixed(1);
if (absVal >= 1) return value.toStringAsFixed(2);
if (absVal >= 0.1) return value.toStringAsFixed(3);
return value.toStringAsFixed(4);
}
@override @override
Widget build(BuildContext context) { Widget build(BuildContext context) {
return ListView( return ListView(
@@ -212,22 +228,52 @@ class _GraphCardState extends State<GraphCard> {
leftTitles: AxisTitles( leftTitles: AxisTitles(
sideTitles: SideTitles( sideTitles: SideTitles(
showTitles: true, showTitles: true,
reservedSize: 80, reservedSize: 60,
interval: (bounds.maxY - bounds.minY) / 8,
getTitlesWidget: (value, meta) => getTitlesWidget: (value, meta) =>
SideTitleWidget( SideTitleWidget(
axisSide: meta.axisSide, axisSide: meta.axisSide,
child: Text(value.toStringAsFixed(2)), child: Text(
_formatAxisValue(value),
style: GoogleFonts.robotoFlex(),
),
), ),
), ),
), ),
bottomTitles: AxisTitles( bottomTitles: AxisTitles(
sideTitles: SideTitles( sideTitles: SideTitles(
showTitles: true, showTitles: true,
reservedSize: 24, reservedSize: 80,
getTitlesWidget: (value, meta) => interval: (bounds.maxX - bounds.minX) / 10,
SideTitleWidget( getTitlesWidget: (value, meta) => SideTitleWidget(
axisSide: meta.axisSide, axisSide: meta.axisSide,
child: Text(value.toStringAsFixed(2)), child: Column(
mainAxisSize: MainAxisSize.min,
children: _formatAxisValue(value)
.split('')
.map(
(char) => ['-', '.'].contains(char)
? Transform.rotate(
angle: pi / 2,
child: Text(
char,
style:
GoogleFonts.robotoFlex(
height: char == '.'
? 0.7
: 0.9,
),
),
)
: Text(
char,
style: GoogleFonts.robotoFlex(
height: 0.9,
),
),
)
.toList(),
),
), ),
), ),
), ),
@@ -246,6 +292,17 @@ class _GraphCardState extends State<GraphCard> {
), ),
lineTouchData: LineTouchData( lineTouchData: LineTouchData(
enabled: true, enabled: true,
touchCallback: (event, response) {
if (response != null &&
response.lineBarSpots != null &&
response.lineBarSpots!.isNotEmpty) {
setState(() {
_currentTouchedPoint =
response.lineBarSpots!.first;
});
}
// Keep the last touched point visible
},
touchTooltipData: LineTouchTooltipData( touchTooltipData: LineTouchTooltipData(
getTooltipItems: (touchedSpots) { getTooltipItems: (touchedSpots) {
return touchedSpots.map((spot) { return touchedSpots.map((spot) {
@@ -276,6 +333,28 @@ class _GraphCardState extends State<GraphCard> {
}, },
), ),
), ),
if (_currentTouchedPoint != null)
Container(
margin: const EdgeInsets.only(top: 16),
padding: const EdgeInsets.all(12),
decoration: BoxDecoration(
color: Theme.of(
context,
).colorScheme.surfaceContainerHighest,
borderRadius: BorderRadius.circular(8),
),
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: [
LaTexT(
laTeXCode: Text(
'\$\$x = ${_currentTouchedPoint!.x.toStringAsFixed(4)},\\quad y = ${_currentTouchedPoint!.y.toStringAsFixed(4)}\$\$',
style: Theme.of(context).textTheme.bodyLarge,
),
),
],
),
),
], ],
), ),
), ),