💄 Optimize styling

This commit is contained in:
2025-09-13 01:20:09 +08:00
parent 4cf035f2f1
commit a9a1680f5d
4 changed files with 163 additions and 71 deletions

View File

@@ -1,20 +1,28 @@
import 'package:flutter/material.dart';
import 'package:google_fonts/google_fonts.dart';
import 'package:latext/latext.dart';
import 'package:simple_math_calc/models/calculation_step.dart';
import 'package:simple_math_calc/solver_service.dart';
import 'package:simple_math_calc/solver.dart';
import 'dart:math';
void main() async {
runApp(const MyApp());
runApp(const CalcApp());
}
class MyApp extends StatelessWidget {
const MyApp({super.key});
class CalcApp extends StatelessWidget {
const CalcApp({super.key});
@override
Widget build(BuildContext context) {
return MaterialApp(
title: '方程计算器',
theme: ThemeData(primarySwatch: Colors.blue, useMaterial3: true),
theme: ThemeData(
primarySwatch: Colors.blue,
useMaterial3: true,
textTheme: GoogleFonts.notoSerifScTextTheme(
Theme.of(context).textTheme, // Inherit existing text theme
),
),
home: const CalculatorHomePage(),
);
}
@@ -68,32 +76,41 @@ class _CalculatorHomePageState extends State<CalculatorHomePage> {
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(title: const Text('方程与表达式计算器')),
body: Padding(
padding: const EdgeInsets.all(16.0),
child: Column(
children: [
TextField(
controller: _controller,
decoration: const InputDecoration(
border: OutlineInputBorder(),
labelText: '输入方程或表达式',
hintText: '例如: 2x^2 - 8x + 6 = 0',
),
onSubmitted: (_) => _solveEquation(),
body: Column(
children: [
Padding(
padding: const EdgeInsets.only(left: 16.0, right: 16.0, top: 16.0),
child: Row(
spacing: 8,
children: [
Expanded(
child: TextField(
controller: _controller,
textAlign: TextAlign.center,
decoration: InputDecoration(
border: const OutlineInputBorder(),
labelText: '输入方程或表达式',
floatingLabelAlignment: FloatingLabelAlignment.center,
hintText: '例如: 2x^2 - 8x + 6 = 0',
),
onSubmitted: (_) => _solveEquation(),
),
),
IconButton(
onPressed: _solveEquation,
icon: Icon(Icons.play_arrow),
),
],
),
const SizedBox(height: 16),
ElevatedButton(onPressed: _solveEquation, child: const Text('计算')),
const SizedBox(height: 16),
const Divider(),
Expanded(
child: _isLoading
? const Center(child: CircularProgressIndicator())
: _result == null
? const Center(child: Text('请输入方程开始计算'))
: buildResultView(_result!),
),
],
),
),
Expanded(
child: _isLoading
? const Center(child: CircularProgressIndicator())
: _result == null
? const Center(child: Text('请输入方程开始计算'))
: buildResultView(_result!),
),
],
),
);
}
@@ -101,23 +118,66 @@ class _CalculatorHomePageState extends State<CalculatorHomePage> {
// 构建结果展示视图
Widget buildResultView(CalculationResult result) {
return ListView(
padding: EdgeInsets.only(
left: 16,
right: 16,
bottom: MediaQuery.of(context).padding.bottom + 16,
top: 16,
),
children: [
...result.steps.map(
(step) => Card(
margin: const EdgeInsets.symmetric(vertical: 8),
child: Padding(
padding: const EdgeInsets.all(12.0),
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
child: ClipRRect(
borderRadius: const BorderRadius.all(Radius.circular(8)),
child: Stack(
children: [
Text(
step.title,
style: Theme.of(context).textTheme.titleMedium,
Padding(
padding: const EdgeInsets.only(
left: 12,
right: 12,
bottom: 4,
top: 16,
),
child: Column(
crossAxisAlignment: CrossAxisAlignment.center,
children: [
Text(
step.title,
style: Theme.of(context).textTheme.titleMedium,
),
const SizedBox(height: 4),
SelectableText(step.explanation),
Center(
child: LaTexT(
laTeXCode: Text(
step.formula,
style: Theme.of(context).textTheme.titleMedium,
),
),
),
],
),
),
Positioned(
left: -8,
top: -8,
child: Transform.rotate(
angle: pi / -5,
child: Opacity(
opacity: 0.8,
child: Badge(
backgroundColor: Theme.of(
context,
).colorScheme.primary,
label: Text(
step.stepNumber.toString(),
style: TextStyle(fontSize: 32),
),
),
),
),
),
const SizedBox(height: 4),
SelectableText(step.explanation),
const SizedBox(height: 8),
Center(child: LaTexT(laTeXCode: Text(step.formula))),
],
),
),
@@ -127,17 +187,24 @@ class _CalculatorHomePageState extends State<CalculatorHomePage> {
Card(
color: Theme.of(context).colorScheme.primaryContainer,
child: Padding(
padding: const EdgeInsets.all(16.0),
padding: const EdgeInsets.symmetric(horizontal: 16, vertical: 8),
child: Column(
children: [
const SizedBox(height: 16),
Text(
"最终答案:",
"最终答案",
style: Theme.of(context).textTheme.titleLarge?.copyWith(
color: Theme.of(context).colorScheme.onPrimaryContainer,
),
),
const SizedBox(height: 8),
LaTexT(laTeXCode: Text(result.finalAnswer)),
LaTexT(
laTeXCode: Text(
result.finalAnswer,
style: Theme.of(context).textTheme.titleMedium?.copyWith(
color: Theme.of(context).colorScheme.onPrimaryContainer,
),
),
),
],
),
),

View File

@@ -1,10 +1,12 @@
// 用来描述计算过程中的每一步
class CalculationStep {
final String title; // 这一步的标题,例如:“第一步:整理方程”
final int stepNumber; // 步骤编号例如1, 2, 3...
final String title; // 这一步的标题,例如:"整理方程"
final String explanation; // 对这一步的具体文字描述
final String formula; // 这一步得到的数学式子 (可以使用 LaTeX 格式)
CalculationStep({
required this.stepNumber,
required this.title,
required this.explanation,
required this.formula,

View File

@@ -56,16 +56,16 @@ class SolverService {
final steps = <CalculationStep>[];
steps.add(
CalculationStep(
title: '第一步:表达式求值',
stepNumber: 1,
title: '表达式求值',
explanation: '这是一个标准的数学表达式,我们将直接计算其结果。',
formula: input,
),
);
Parser p = Parser();
GrammarParser p = GrammarParser();
Expression exp = p.parse(input);
ContextModel cm = ContextModel();
final result = exp.evaluate(EvaluationType.REAL, cm);
final result = RealEvaluator().evaluate(exp);
return CalculationResult(steps: steps, finalAnswer: result.toString());
}
@@ -75,6 +75,7 @@ class SolverService {
final steps = <CalculationStep>[];
steps.add(
CalculationStep(
stepNumber: 0,
title: '原方程',
explanation: '这是一元一次方程。',
formula: '\$\$$input\$\$',
@@ -89,7 +90,8 @@ class SolverService {
steps.add(
CalculationStep(
title: '第一步:移项',
stepNumber: 1,
title: '移项',
explanation: '将所有含 x 的项移到等式左边,常数项移到右边。',
formula:
'\$\$${a}x ${c >= 0 ? '-' : '+'} ${c.abs()}x = $d ${b >= 0 ? '-' : '+'} ${b.abs()}\$\$',
@@ -98,7 +100,8 @@ class SolverService {
steps.add(
CalculationStep(
title: '第二步:合并同类项',
stepNumber: 2,
title: '合并同类项',
explanation: '合并等式两边的项。',
formula: '\$\$${newA}x = $newD\$\$',
),
@@ -114,7 +117,8 @@ class SolverService {
final x = newD / newA;
steps.add(
CalculationStep(
title: '第三步:求解 x',
stepNumber: 3,
title: '求解 x',
explanation: '两边同时除以 x 的系数 ($newA)。',
formula: '\$\$x = \frac{$newD}{$newA}\$\$',
),
@@ -143,7 +147,8 @@ class SolverService {
steps.add(
CalculationStep(
title: '第一步:整理方程',
stepNumber: 1,
title: '整理方程',
explanation: r'将方程整理成标准形式 ax^2+bx+c=0。',
formula:
'\$\$${a}x^2 ${b >= 0 ? '+' : ''} ${b}x ${c >= 0 ? '+' : ''} $c = 0\$\$',
@@ -155,16 +160,18 @@ class SolverService {
if (factors != null) {
steps.add(
CalculationStep(
title: '第二步:因式分解法 (十字相乘)',
stepNumber: 2,
title: '因式分解法 (十字相乘)',
explanation: '我们发现可以将方程分解为两个一次因式的乘积。',
formula: factors.formula,
),
);
steps.add(
CalculationStep(
title: '第三步:求解',
stepNumber: 3,
title: '求解',
explanation: '分别令每个因式等于 0解出 x。',
formula: '解得 ${factors.solution}',
formula: factors.solution,
),
);
return CalculationResult(steps: steps, finalAnswer: factors.solution);
@@ -173,7 +180,8 @@ class SolverService {
steps.add(
CalculationStep(
title: '第二步:选择解法',
stepNumber: 2,
title: '选择解法',
explanation: '无法进行因式分解,我们选择使用求根公式法。',
formula: '\$\$\\Delta = b^2 - 4ac\$\$',
),
@@ -182,7 +190,8 @@ class SolverService {
final delta = b * b - 4 * a * c;
steps.add(
CalculationStep(
title: '第三步:计算判别式 (Delta)',
stepNumber: 3,
title: '计算判别式 (Delta)',
explanation:
'\$\$\\Delta = b^2 - 4ac = ($b)^2 - 4 \\cdot ($a) \\cdot ($c) = $delta\$\$',
formula: '\$\$\\Delta = $delta\$\$',
@@ -194,7 +203,8 @@ class SolverService {
final x2 = (-b - sqrt(delta)) / (2 * a);
steps.add(
CalculationStep(
title: '第四步:应用求根公式',
stepNumber: 4,
title: '应用求根公式',
explanation:
r'因为 $\Delta > 0$,方程有两个不相等的实数根。公式: $x = \frac{-b \pm \sqrt{\Delta}}{2a}$。',
formula:
@@ -210,7 +220,8 @@ class SolverService {
final x = -b / (2 * a);
steps.add(
CalculationStep(
title: '第四步:应用求根公式',
stepNumber: 4,
title: '应用求根公式',
explanation: r'因为 $\Delta = 0$,方程有两个相等的实数根。',
formula: '\$\$x_1 = x_2 = ${x.toStringAsFixed(4)}\$\$',
),
@@ -222,7 +233,8 @@ class SolverService {
} else {
steps.add(
CalculationStep(
title: '第四步:判断解',
stepNumber: 4,
title: '判断解',
explanation: r'因为 $\Delta < 0$,该方程在实数范围内无解。',
formula: '无实数解',
),
@@ -245,6 +257,7 @@ class SolverService {
steps.add(
CalculationStep(
stepNumber: 0,
title: '原始方程组',
explanation: '这是一个二元一次方程组,我们将使用加减消元法求解。',
formula:
@@ -272,7 +285,8 @@ ${a2}x ${b2 >= 0 ? '+' : ''} ${b2}y = $c2 & (2)
steps.add(
CalculationStep(
title: '第一步:消元',
stepNumber: 1,
title: '消元',
explanation: '为了消去变量 y将方程(1)两边乘以 $b2,方程(2)两边乘以 $b1',
formula:
'''
@@ -291,17 +305,19 @@ ${newA2}x ${b1 * b2 >= 0 ? '+' : ''} ${b1 * b2}y = $newC2 & (4)
steps.add(
CalculationStep(
title: '第二步:相减',
stepNumber: 2,
title: '相减',
explanation: '将方程(3)减去方程(4),得到一个只含 x 的方程。',
formula:
'\$\$($newA1 - $newA2)x = $newC1 - $newC2 \Rightarrow ${xCoeff}x = $constCoeff\$\$',
'\$\$($newA1 - $newA2)x = $newC1 - $newC2 \\Rightarrow ${xCoeff}x = $constCoeff\$\$',
),
);
final x = constCoeff / xCoeff;
steps.add(
CalculationStep(
title: '第三步:解出 x',
stepNumber: 3,
title: '解出 x',
explanation: '求解上述方程得到 x 的值。',
formula: '\$\$x = $x\$\$',
),
@@ -313,7 +329,8 @@ ${newA2}x ${b1 * b2 >= 0 ? '+' : ''} ${b1 * b2}y = $newC2 & (4)
final y = yConst / yCoeff;
steps.add(
CalculationStep(
title: '第四步:回代求解 y',
stepNumber: 4,
title: '回代求解 y',
explanation: '将 x = $x 代入原方程(2)中。',
formula:
'''
@@ -330,14 +347,15 @@ ${b2}y &= ${c2 - a2 * x}
);
steps.add(
CalculationStep(
title: '第五步:解出 y',
stepNumber: 5,
title: '解出 y',
explanation: '求解得到 y 的值。',
formula: '\$\$y = $y\$\$',
),
);
return CalculationResult(
steps: steps,
finalAnswer: '\$\$x = $x, \quad y = $y\$\$',
finalAnswer: '\$\$x = $x, \\quad y = $y\$\$',
);
} else {
final yCoeff = b1;
@@ -345,7 +363,8 @@ ${b2}y &= ${c2 - a2 * x}
final y = yConst / yCoeff;
steps.add(
CalculationStep(
title: '第四步:回代求解 y',
stepNumber: 4,
title: '回代求解 y',
explanation: '将 x = $x 代入原方程(1)中。',
formula:
'''
@@ -362,7 +381,8 @@ ${b1}y &= ${c1 - a1 * x}
);
steps.add(
CalculationStep(
title: '第五步:解出 y',
stepNumber: 5,
title: '解出 y',
explanation: '求解得到 y 的值。',
formula: '\$\$y = $y\$\$',
),