import 'package:flutter/material.dart'; import 'package:provider/provider.dart'; import 'plugins.dart'; import 'settings.dart'; /// 工具网格:可搜索的插件入口。 /// /// 工具页面的实现全部在 lib/tools/ 下(从 mathtool2 搬过来的那批)。 /// **计算一律交给 Symja**,走 lib/tools/tool_kit.dart 那套外壳;Dart 侧只有 /// 绘图采样和排版。原来那批纯 Dart 的算法(rational / math_expressions) /// 已经全部撤掉 —— 同一个算式不该有 Dart 和 Symja 两份实现。 class ToolsPage extends StatefulWidget { const ToolsPage({super.key}); @override State createState() => _ToolsPageState(); } class _ToolsPageState extends State { final _search = TextEditingController(); String _q = ''; static const _palette = [ Color(0xFF5C6BC0), Color(0xFF26A69A), Color(0xFFEF5350), Color(0xFFAB47BC), Color(0xFF42A5F5), Color(0xFFFFA726), Color(0xFF66BB6A), Color(0xFF8D6E63), ]; @override void dispose() { _search.dispose(); super.dispose(); } @override Widget build(BuildContext context) { final items = kTools.where((t) => t.matches(_q)).toList(); // 这一页是唯一没有自己的 Scaffold/AppBar 的 tab(另外两个都有), // 所以得自己让开状态栏 —— 否则搜索框会顶到状态栏上,和时钟叠在一起。 // bottom 不用让:下面紧挨着的就是 Shell 的 NavigationBar,它已经把 // 底部安全区吃掉了。 return SafeArea( bottom: false, child: Column( children: [ Padding( padding: const EdgeInsets.fromLTRB(16, 12, 16, 8), child: TextField( controller: _search, decoration: InputDecoration( isDense: true, hintText: '搜索工具(支持拼音首字母)', prefixIcon: const Icon(Icons.search), suffixIcon: _q.isEmpty ? null : IconButton( icon: const Icon(Icons.clear), onPressed: () { _search.clear(); setState(() => _q = ''); }, ), border: const OutlineInputBorder(), ), onChanged: (v) => setState(() => _q = v), ), ), Expanded( child: items.isEmpty ? const Center(child: Text('没有匹配的工具')) : GridView.builder( padding: const EdgeInsets.fromLTRB(16, 8, 16, 16), gridDelegate: const SliverGridDelegateWithFixedCrossAxisCount( crossAxisCount: 2, mainAxisSpacing: 12, crossAxisSpacing: 12, childAspectRatio: 1.5, ), itemCount: items.length, itemBuilder: (_, i) { final t = items[i]; final color = _palette[kTools.indexOf(t) % _palette.length]; return _ToolCard( plugin: t, color: color, onTap: () => Navigator.of(context).push( MaterialPageRoute( builder: (_) => t.build(), settings: RouteSettings(name: t.name), ), ), ); }, ), ), ], ), ); } } class _ToolCard extends StatelessWidget { const _ToolCard({ required this.plugin, required this.color, required this.onTap, }); final ToolPlugin plugin; final Color color; final VoidCallback onTap; @override Widget build(BuildContext context) { // E-Ink 下卡片一律白底黑字黑框 —— 彩色渐变灰阶之后是深浅不一的灰块, // 墨水屏上不好看也不好认。 final eInk = context.watch().eInk; final fg = eInk ? Colors.black : Colors.white; return Material( color: Colors.transparent, child: InkWell( onTap: onTap, borderRadius: BorderRadius.circular(16), child: Ink( decoration: BoxDecoration( borderRadius: BorderRadius.circular(16), color: eInk ? Colors.white : null, border: eInk ? Border.all(color: Colors.black) : null, gradient: eInk ? null : LinearGradient( begin: Alignment.topLeft, end: Alignment.bottomRight, colors: [color, Color.lerp(color, Colors.black, 0.22)!], ), ), padding: const EdgeInsets.all(14), child: Column( crossAxisAlignment: CrossAxisAlignment.start, mainAxisAlignment: MainAxisAlignment.spaceBetween, children: [ Icon(plugin.icon, color: fg, size: 26), Text( plugin.name, style: TextStyle( color: fg, fontSize: 15, fontWeight: FontWeight.w600, ), ), ], ), ), ), ); } }