import 'package:flutter/material.dart'; import 'package:provider/provider.dart'; import 'calculator_page.dart'; import 'console_page.dart'; import 'settings.dart'; import 'symja.dart'; import 'tools_page.dart'; void main() => runApp(const CalApp()); class CalApp extends StatelessWidget { const CalApp({super.key}); @override Widget build(BuildContext context) { return MultiProvider( providers: [ ChangeNotifierProvider(create: (_) => SymjaSession()), // 设置异步读盘,读完 notifyListeners 一次,界面自己会换上 E-Ink ChangeNotifierProvider(create: (_) => AppSettings()..load()), ], child: const _App(), ); } } class _App extends StatelessWidget { const _App(); @override Widget build(BuildContext context) { final eInk = context.watch().eInk; return MaterialApp( title: '某人的计算器2', debugShowCheckedModeBanner: false, theme: _theme(eInk), themeAnimationDuration: eInk ? Duration.zero : kThemeAnimationDuration, // **始终**套同样两层的 widget,只改参数:切 E-Ink 时若把这两层加进来/ // 拿出去,Navigator 会因为"这一格的 widget 类型变了"被整个重建, // 表现是"一开 E-Ink 就被踢回首页、弹着的面板也没了"。 builder: (context, child) => MediaQuery( data: MediaQuery.of(context) .copyWith(disableAnimations: eInk), child: ColorFiltered( // 关掉时用单位矩阵,等于没过滤镜(`ColorFiltered` 本身还在, // 但任何颜色乘上单位矩阵都不变) colorFilter: eInk ? _Grayscale.filter : _Grayscale.identity, child: child!, ), ), home: const ShellPage(), ); } /// E-Ink 模式下额外关掉动画:路由切换和点击水波纹是屏幕上最明显的两处动画。 ThemeData _theme(bool eInk) { final base = ThemeData(colorSchemeSeed: Colors.indigo, useMaterial3: true); if (!eInk) return base; return base.copyWith( splashFactory: NoSplash.splashFactory, pageTransitionsTheme: const PageTransitionsTheme( builders: { TargetPlatform.android: _NoTransition(), TargetPlatform.iOS: _NoTransition(), TargetPlatform.fuchsia: _NoTransition(), TargetPlatform.linux: _NoTransition(), TargetPlatform.macOS: _NoTransition(), TargetPlatform.windows: _NoTransition(), }, ), ); } } class _NoTransition extends PageTransitionsBuilder { const _NoTransition(); @override Widget buildTransitions( PageRoute route, BuildContext context, Animation animation, Animation secondaryAnimation, Widget child, ) => child; } /// 灰阶滤镜。 /// /// 用**一个**颜色滤镜覆盖全局,而不是去给每个配色/每张图表换一套灰阶调色板 —— /// 那些颜色散在主题、工具卡片、绘图组件、Symja 的 PNG 里,逐个改必然漏。 /// E-Ink 关掉时套的是单位矩阵(同样的 widget、同样的开销,只是颜色不变), /// 这样切换不会动到 widget 树的形状。 class _Grayscale { const _Grayscale._(); /// 亮度权重就是通常的 Rec.709 公式。 static const ColorFilter filter = ColorFilter.matrix([ 0.2126, 0.7152, 0.0722, 0, 0, // 0.2126, 0.7152, 0.0722, 0, 0, // 0.2126, 0.7152, 0.0722, 0, 0, // 0, 0, 0, 1, 0, // ]); static const ColorFilter identity = ColorFilter.matrix([ 1, 0, 0, 0, 0, // 0, 1, 0, 0, 0, // 0, 0, 1, 0, 0, // 0, 0, 0, 1, 0, // ]); } /// 三个界面(Q16 的「双界面」+ 插件页): /// 计算器 —— 默认入口,卡西欧式按键 /// 工具 —— 16 个插件的网格入口 /// 控制台 —— 表达式 REPL /// /// 用 IndexedStack 保住各页状态:切回来时控制台的历史、计算器正在输入的 /// 表达式都还在。 class ShellPage extends StatefulWidget { const ShellPage({super.key}); @override State createState() => _ShellPageState(); } class _ShellPageState extends State { int _index = 0; @override Widget build(BuildContext context) { return Scaffold( body: IndexedStack( index: _index, children: const [ CalculatorPage(), ToolsPage(), ConsolePage(), ], ), bottomNavigationBar: NavigationBar( selectedIndex: _index, onDestinationSelected: (i) => setState(() => _index = i), destinations: const [ NavigationDestination( icon: Icon(Icons.calculate_outlined), selectedIcon: Icon(Icons.calculate), label: '计算器', ), NavigationDestination( icon: Icon(Icons.widgets_outlined), selectedIcon: Icon(Icons.widgets), label: '工具', ), NavigationDestination( icon: Icon(Icons.terminal_outlined), selectedIcon: Icon(Icons.terminal), label: '控制台', ), ], ), ); } }