derivative_calculator.dart 11 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354
  1. // tools/derivative_calculator.dart
  2. import 'package:flutter/material.dart';
  3. import 'package:math_expressions/math_expressions.dart';
  4. class DerivativeCalculatorPage extends StatefulWidget {
  5. @override
  6. _DerivativeCalculatorPageState createState() => _DerivativeCalculatorPageState();
  7. }
  8. class _DerivativeCalculatorPageState extends State<DerivativeCalculatorPage> {
  9. final TextEditingController _functionController = TextEditingController();
  10. final TextEditingController _pointController = TextEditingController();
  11. String _result = '';
  12. String _derivative = '';
  13. bool _showPointInput = false;
  14. @override
  15. void dispose() {
  16. _functionController.dispose();
  17. _pointController.dispose();
  18. super.dispose();
  19. }
  20. void _calculateDerivative() {
  21. final String functionText = _functionController.text.trim();
  22. if (functionText.isEmpty) {
  23. setState(() {
  24. _result = '请输入函数表达式';
  25. _derivative = '';
  26. });
  27. return;
  28. }
  29. try {
  30. // 解析函数表达式
  31. Parser p = Parser();
  32. Expression exp = p.parse(functionText);
  33. // 对x求导
  34. Expression deriv = exp.derive('x');
  35. // 简化导数表达式
  36. Expression simplifiedDeriv = deriv.simplify();
  37. setState(() {
  38. _derivative = '导函数: ${simplifiedDeriv.toString()}';
  39. // 如果用户要求计算某点的导数值
  40. if (_showPointInput && _pointController.text.isNotEmpty) {
  41. try {
  42. double point = double.parse(_pointController.text);
  43. ContextModel cm = ContextModel();
  44. cm.bindVariable(Variable('x'), Number(point));
  45. double value = simplifiedDeriv.evaluate(EvaluationType.REAL, cm);
  46. _result = '在 x = $point 处的导数值: ${value.toStringAsFixed(4)}';
  47. } catch (e) {
  48. _result = '点的格式错误,请输入有效的数字';
  49. }
  50. } else {
  51. _result = _derivative;
  52. }
  53. });
  54. } catch (e) {
  55. setState(() {
  56. _result = '表达式解析错误,请检查格式\n支持的格式示例: x^2 + 3*x + sin(x)';
  57. _derivative = '';
  58. });
  59. }
  60. }
  61. void _clearAll() {
  62. setState(() {
  63. _functionController.clear();
  64. _pointController.clear();
  65. _result = '';
  66. _derivative = '';
  67. });
  68. }
  69. @override
  70. Widget build(BuildContext context) {
  71. return Scaffold(
  72. appBar: AppBar(
  73. title: Text('导数计算器', style: TextStyle(color: Colors.black)),
  74. backgroundColor: Colors.white,
  75. elevation: 1,
  76. actions: [
  77. IconButton(
  78. icon: Icon(Icons.info_outline),
  79. onPressed: () {
  80. _showInstructions();
  81. },
  82. ),
  83. ],
  84. ),
  85. body: Padding(
  86. padding: EdgeInsets.all(16),
  87. child: Column(
  88. crossAxisAlignment: CrossAxisAlignment.start,
  89. children: [
  90. Text(
  91. '函数表达式 (以x为变量):',
  92. style: TextStyle(fontSize: 16, fontWeight: FontWeight.bold),
  93. ),
  94. SizedBox(height: 8),
  95. TextField(
  96. controller: _functionController,
  97. decoration: InputDecoration(
  98. hintText: '例如: x^2 + 3*x + sin(x)',
  99. border: OutlineInputBorder(),
  100. suffixIcon: IconButton(
  101. icon: Icon(Icons.info),
  102. onPressed: _showExpressionHelp,
  103. ),
  104. ),
  105. ),
  106. SizedBox(height: 16),
  107. // 计算某点导数值的选项
  108. Row(
  109. children: [
  110. Checkbox(
  111. value: _showPointInput,
  112. onChanged: (value) {
  113. setState(() {
  114. _showPointInput = value!;
  115. if (!value) {
  116. _pointController.clear();
  117. }
  118. });
  119. },
  120. ),
  121. Text('计算某点处的导数值'),
  122. ],
  123. ),
  124. if (_showPointInput) ...[
  125. SizedBox(height: 8),
  126. Text(
  127. '输入点的x坐标:',
  128. style: TextStyle(fontSize: 14),
  129. ),
  130. SizedBox(height: 8),
  131. TextField(
  132. controller: _pointController,
  133. decoration: InputDecoration(
  134. hintText: '例如: 2.5',
  135. border: OutlineInputBorder(),
  136. ),
  137. keyboardType: TextInputType.numberWithOptions(decimal: true),
  138. ),
  139. ],
  140. SizedBox(height: 20),
  141. Row(
  142. children: [
  143. Expanded(
  144. child: ElevatedButton(
  145. onPressed: _calculateDerivative,
  146. style: ElevatedButton.styleFrom(
  147. backgroundColor: Colors.blue,
  148. padding: EdgeInsets.symmetric(vertical: 12),
  149. ),
  150. child: Text('计算导数', style: TextStyle(color: Colors.white)),
  151. ),
  152. ),
  153. SizedBox(width: 10),
  154. Expanded(
  155. child: ElevatedButton(
  156. onPressed: _clearAll,
  157. style: ElevatedButton.styleFrom(
  158. backgroundColor: Colors.grey,
  159. padding: EdgeInsets.symmetric(vertical: 12),
  160. ),
  161. child: Text('清空', style: TextStyle(color: Colors.white)),
  162. ),
  163. ),
  164. ],
  165. ),
  166. SizedBox(height: 20),
  167. if (_result.isNotEmpty) ...[
  168. Container(
  169. width: double.infinity,
  170. padding: EdgeInsets.all(16),
  171. decoration: BoxDecoration(
  172. color: Colors.grey[50],
  173. borderRadius: BorderRadius.circular(8),
  174. border: Border.all(color: Colors.grey[300]!),
  175. ),
  176. child: Column(
  177. crossAxisAlignment: CrossAxisAlignment.start,
  178. children: [
  179. if (_derivative.isNotEmpty && _showPointInput) ...[
  180. Text(
  181. _derivative,
  182. style: TextStyle(fontSize: 14, color: Colors.grey[700]),
  183. ),
  184. SizedBox(height: 8),
  185. ],
  186. Text(
  187. _result,
  188. style: TextStyle(
  189. fontSize: 16,
  190. fontWeight: FontWeight.bold,
  191. color: Colors.blue[800],
  192. ),
  193. ),
  194. ],
  195. ),
  196. ),
  197. ],
  198. Spacer(),
  199. _buildExamples(),
  200. ],
  201. ),
  202. ),
  203. );
  204. }
  205. Widget _buildExamples() {
  206. return Container(
  207. width: double.infinity,
  208. padding: EdgeInsets.all(16),
  209. decoration: BoxDecoration(
  210. color: Colors.blue[50],
  211. borderRadius: BorderRadius.circular(8),
  212. ),
  213. child: Column(
  214. crossAxisAlignment: CrossAxisAlignment.start,
  215. children: [
  216. Text(
  217. '表达式示例:',
  218. style: TextStyle(fontWeight: FontWeight.bold, color: Colors.blue[800]),
  219. ),
  220. SizedBox(height: 8),
  221. _buildExampleItem('x^2 + 3*x + 2'),
  222. _buildExampleItem('sin(x)'),
  223. _buildExampleItem('cos(x)'),
  224. _buildExampleItem('ln(x)'),
  225. _buildExampleItem('sqrt(x)'),
  226. _buildExampleItem('(x+1)/(x-1)'),
  227. ],
  228. ),
  229. );
  230. }
  231. Widget _buildExampleItem(String example) {
  232. return GestureDetector(
  233. onTap: () {
  234. setState(() {
  235. _functionController.text = example;
  236. });
  237. },
  238. child: Padding(
  239. padding: EdgeInsets.symmetric(vertical: 4),
  240. child: Text(
  241. '• $example',
  242. style: TextStyle(color: Colors.blue[700], fontSize: 14),
  243. ),
  244. ),
  245. );
  246. }
  247. void _showInstructions() {
  248. showDialog(
  249. context: context,
  250. builder: (context) => AlertDialog(
  251. title: Text('使用说明'),
  252. content: SingleChildScrollView(
  253. child: Column(
  254. crossAxisAlignment: CrossAxisAlignment.start,
  255. mainAxisSize: MainAxisSize.min,
  256. children: [
  257. Text('1. 输入以x为自变量的函数表达式'),
  258. SizedBox(height: 8),
  259. Text('2. 支持的基本函数:'),
  260. Text(' • sin(x), cos(x), tan(x)'),
  261. Text(' • ln(x) - 自然对数'),
  262. Text(' • sqrt(x) - 平方根'),
  263. SizedBox(height: 8),
  264. Text('3. 支持的操作符:'),
  265. Text(' • +, -, *, /'),
  266. Text(' • ^ - 幂运算'),
  267. Text(' • () - 括号'),
  268. SizedBox(height: 8),
  269. Text('4. 可选计算某点处的导数值'),
  270. ],
  271. ),
  272. ),
  273. actions: [
  274. TextButton(
  275. onPressed: () => Navigator.pop(context),
  276. child: Text('确定'),
  277. ),
  278. ],
  279. ),
  280. );
  281. }
  282. void _showExpressionHelp() {
  283. showDialog(
  284. context: context,
  285. builder: (context) => AlertDialog(
  286. title: Text('表达式格式帮助'),
  287. content: SingleChildScrollView(
  288. child: Column(
  289. crossAxisAlignment: CrossAxisAlignment.start,
  290. mainAxisSize: MainAxisSize.min,
  291. children: [
  292. Text('支持的数学表达式:'),
  293. SizedBox(height: 8),
  294. _buildHelpItem('x^2', 'x的平方'),
  295. _buildHelpItem('3*x', '3乘以x'),
  296. _buildHelpItem('sin(x)', '正弦函数'),
  297. _buildHelpItem('cos(x)', '余弦函数'),
  298. _buildHelpItem('ln(x)', '自然对数'),
  299. _buildHelpItem('sqrt(x)', '平方根'),
  300. _buildHelpItem('(x+1)/(x-1)', '分式'),
  301. ],
  302. ),
  303. ),
  304. actions: [
  305. TextButton(
  306. onPressed: () => Navigator.pop(context),
  307. child: Text('确定'),
  308. ),
  309. ],
  310. ),
  311. );
  312. }
  313. Widget _buildHelpItem(String expression, String description) {
  314. return Padding(
  315. padding: EdgeInsets.symmetric(vertical: 4),
  316. child: Row(
  317. crossAxisAlignment: CrossAxisAlignment.start,
  318. children: [
  319. Container(
  320. width: 80,
  321. child: Text(
  322. expression,
  323. style: TextStyle(fontFamily: 'Monospace', fontWeight: FontWeight.bold),
  324. ),
  325. ),
  326. SizedBox(width: 10),
  327. Expanded(child: Text(description)),
  328. ],
  329. ),
  330. );
  331. }
  332. }