💄 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

3
devtools_options.yaml Normal file
View File

@@ -0,0 +1,3 @@
description: This file stores settings for Dart & Flutter DevTools.
documentation: https://docs.flutter.dev/tools/devtools/extensions#configure-extension-enablement-states
extensions:

View File

@@ -1,20 +1,28 @@
import 'package:flutter/material.dart'; import 'package:flutter/material.dart';
import 'package:google_fonts/google_fonts.dart';
import 'package:latext/latext.dart'; import 'package:latext/latext.dart';
import 'package:simple_math_calc/models/calculation_step.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 { void main() async {
runApp(const MyApp()); runApp(const CalcApp());
} }
class MyApp extends StatelessWidget { class CalcApp extends StatelessWidget {
const MyApp({super.key}); const CalcApp({super.key});
@override @override
Widget build(BuildContext context) { Widget build(BuildContext context) {
return MaterialApp( return MaterialApp(
title: '方程计算器', 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(), home: const CalculatorHomePage(),
); );
} }
@@ -68,32 +76,41 @@ class _CalculatorHomePageState extends State<CalculatorHomePage> {
Widget build(BuildContext context) { Widget build(BuildContext context) {
return Scaffold( return Scaffold(
appBar: AppBar(title: const Text('方程与表达式计算器')), appBar: AppBar(title: const Text('方程与表达式计算器')),
body: Padding( body: Column(
padding: const EdgeInsets.all(16.0), children: [
child: Column( Padding(
children: [ padding: const EdgeInsets.only(left: 16.0, right: 16.0, top: 16.0),
TextField( child: Row(
controller: _controller, spacing: 8,
decoration: const InputDecoration( children: [
border: OutlineInputBorder(), Expanded(
labelText: '输入方程或表达式', child: TextField(
hintText: '例如: 2x^2 - 8x + 6 = 0', controller: _controller,
), textAlign: TextAlign.center,
onSubmitted: (_) => _solveEquation(), 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('计算')), Expanded(
const SizedBox(height: 16), child: _isLoading
const Divider(), ? const Center(child: CircularProgressIndicator())
Expanded( : _result == null
child: _isLoading ? const Center(child: Text('请输入方程开始计算'))
? const Center(child: CircularProgressIndicator()) : buildResultView(_result!),
: _result == null ),
? const Center(child: Text('请输入方程开始计算')) ],
: buildResultView(_result!),
),
],
),
), ),
); );
} }
@@ -101,23 +118,66 @@ class _CalculatorHomePageState extends State<CalculatorHomePage> {
// 构建结果展示视图 // 构建结果展示视图
Widget buildResultView(CalculationResult result) { Widget buildResultView(CalculationResult result) {
return ListView( return ListView(
padding: EdgeInsets.only(
left: 16,
right: 16,
bottom: MediaQuery.of(context).padding.bottom + 16,
top: 16,
),
children: [ children: [
...result.steps.map( ...result.steps.map(
(step) => Card( (step) => Card(
margin: const EdgeInsets.symmetric(vertical: 8), margin: const EdgeInsets.symmetric(vertical: 8),
child: Padding( child: ClipRRect(
padding: const EdgeInsets.all(12.0), borderRadius: const BorderRadius.all(Radius.circular(8)),
child: Column( child: Stack(
crossAxisAlignment: CrossAxisAlignment.start,
children: [ children: [
Text( Padding(
step.title, padding: const EdgeInsets.only(
style: Theme.of(context).textTheme.titleMedium, 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( Card(
color: Theme.of(context).colorScheme.primaryContainer, color: Theme.of(context).colorScheme.primaryContainer,
child: Padding( child: Padding(
padding: const EdgeInsets.all(16.0), padding: const EdgeInsets.symmetric(horizontal: 16, vertical: 8),
child: Column( child: Column(
children: [ children: [
const SizedBox(height: 16),
Text( Text(
"最终答案:", "最终答案",
style: Theme.of(context).textTheme.titleLarge?.copyWith( style: Theme.of(context).textTheme.titleLarge?.copyWith(
color: Theme.of(context).colorScheme.onPrimaryContainer, color: Theme.of(context).colorScheme.onPrimaryContainer,
), ),
), ),
const SizedBox(height: 8), LaTexT(
LaTexT(laTeXCode: Text(result.finalAnswer)), 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 { class CalculationStep {
final String title; // 这一步的标题,例如:“第一步:整理方程” final int stepNumber; // 步骤编号例如1, 2, 3...
final String title; // 这一步的标题,例如:"整理方程"
final String explanation; // 对这一步的具体文字描述 final String explanation; // 对这一步的具体文字描述
final String formula; // 这一步得到的数学式子 (可以使用 LaTeX 格式) final String formula; // 这一步得到的数学式子 (可以使用 LaTeX 格式)
CalculationStep({ CalculationStep({
required this.stepNumber,
required this.title, required this.title,
required this.explanation, required this.explanation,
required this.formula, required this.formula,

View File

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