tools_page.dart 4.2 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146
  1. import 'package:flutter/material.dart';
  2. import 'plugins.dart';
  3. /// 工具网格:可搜索的插件入口。
  4. ///
  5. /// 工具页面的实现全部在 lib/tools/ 下,是从 mathtool2 直接搬过来的
  6. /// (依赖 rational / math_expressions / fl_chart,全是纯 Dart)。
  7. class ToolsPage extends StatefulWidget {
  8. const ToolsPage({super.key});
  9. @override
  10. State<ToolsPage> createState() => _ToolsPageState();
  11. }
  12. class _ToolsPageState extends State<ToolsPage> {
  13. final _search = TextEditingController();
  14. String _q = '';
  15. static const _palette = <Color>[
  16. Color(0xFF5C6BC0),
  17. Color(0xFF26A69A),
  18. Color(0xFFEF5350),
  19. Color(0xFFAB47BC),
  20. Color(0xFF42A5F5),
  21. Color(0xFFFFA726),
  22. Color(0xFF66BB6A),
  23. Color(0xFF8D6E63),
  24. ];
  25. @override
  26. void dispose() {
  27. _search.dispose();
  28. super.dispose();
  29. }
  30. @override
  31. Widget build(BuildContext context) {
  32. final items = kTools.where((t) => t.matches(_q)).toList();
  33. return Column(
  34. children: [
  35. Padding(
  36. padding: const EdgeInsets.fromLTRB(16, 12, 16, 8),
  37. child: TextField(
  38. controller: _search,
  39. decoration: InputDecoration(
  40. isDense: true,
  41. hintText: '搜索工具(支持拼音首字母)',
  42. prefixIcon: const Icon(Icons.search),
  43. suffixIcon: _q.isEmpty
  44. ? null
  45. : IconButton(
  46. icon: const Icon(Icons.clear),
  47. onPressed: () {
  48. _search.clear();
  49. setState(() => _q = '');
  50. },
  51. ),
  52. border: const OutlineInputBorder(),
  53. ),
  54. onChanged: (v) => setState(() => _q = v),
  55. ),
  56. ),
  57. Expanded(
  58. child: items.isEmpty
  59. ? const Center(child: Text('没有匹配的工具'))
  60. : GridView.builder(
  61. padding: const EdgeInsets.fromLTRB(16, 8, 16, 16),
  62. gridDelegate:
  63. const SliverGridDelegateWithFixedCrossAxisCount(
  64. crossAxisCount: 2,
  65. mainAxisSpacing: 12,
  66. crossAxisSpacing: 12,
  67. childAspectRatio: 1.5,
  68. ),
  69. itemCount: items.length,
  70. itemBuilder: (_, i) {
  71. final t = items[i];
  72. final color = _palette[kTools.indexOf(t) % _palette.length];
  73. return _ToolCard(
  74. plugin: t,
  75. color: color,
  76. onTap: () => Navigator.of(context).push(
  77. MaterialPageRoute<void>(
  78. builder: (_) => t.build(),
  79. settings: RouteSettings(name: t.name),
  80. ),
  81. ),
  82. );
  83. },
  84. ),
  85. ),
  86. ],
  87. );
  88. }
  89. }
  90. class _ToolCard extends StatelessWidget {
  91. const _ToolCard({
  92. required this.plugin,
  93. required this.color,
  94. required this.onTap,
  95. });
  96. final ToolPlugin plugin;
  97. final Color color;
  98. final VoidCallback onTap;
  99. @override
  100. Widget build(BuildContext context) {
  101. return Material(
  102. color: Colors.transparent,
  103. child: InkWell(
  104. onTap: onTap,
  105. borderRadius: BorderRadius.circular(16),
  106. child: Ink(
  107. decoration: BoxDecoration(
  108. borderRadius: BorderRadius.circular(16),
  109. gradient: LinearGradient(
  110. begin: Alignment.topLeft,
  111. end: Alignment.bottomRight,
  112. colors: [color, Color.lerp(color, Colors.black, 0.22)!],
  113. ),
  114. ),
  115. padding: const EdgeInsets.all(14),
  116. child: Column(
  117. crossAxisAlignment: CrossAxisAlignment.start,
  118. mainAxisAlignment: MainAxisAlignment.spaceBetween,
  119. children: [
  120. Icon(plugin.icon, color: Colors.white, size: 26),
  121. Text(
  122. plugin.name,
  123. style: const TextStyle(
  124. color: Colors.white,
  125. fontSize: 15,
  126. fontWeight: FontWeight.w600,
  127. ),
  128. ),
  129. ],
  130. ),
  131. ),
  132. ),
  133. );
  134. }
  135. }