| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223 |
- 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<AppSettings>().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;
- // 白底黑字。只靠灰阶滤镜不够 —— 配色本来就有浅灰底、浅灰卡片,
- // 灰阶之后还是灰,看着发闷(而且键盘的浅色按键会跟浅色底糊在一起)。
- // 这里把整个配色压成"白底、黑字、黑线",再让下面的控件自己描边。
- final cs = base.colorScheme.copyWith(
- primary: Colors.black,
- onPrimary: Colors.white,
- primaryContainer: Colors.white,
- onPrimaryContainer: Colors.black,
- secondary: Colors.black,
- onSecondary: Colors.white,
- secondaryContainer: const Color(0xFFE0E0E0),
- onSecondaryContainer: Colors.black,
- surface: Colors.white,
- onSurface: Colors.black,
- surfaceContainerLowest: Colors.white,
- surfaceContainerLow: Colors.white,
- surfaceContainer: Colors.white,
- surfaceContainerHigh: Colors.white,
- surfaceContainerHighest: Colors.white,
- onSurfaceVariant: Colors.black,
- outline: Colors.black,
- outlineVariant: const Color(0xFF9E9E9E),
- inverseSurface: Colors.black,
- onInverseSurface: Colors.white,
- error: Colors.black,
- onError: Colors.white,
- errorContainer: Colors.white,
- onErrorContainer: Colors.black,
- shadow: Colors.black,
- scrim: Colors.black,
- );
- return base.copyWith(
- colorScheme: cs,
- scaffoldBackgroundColor: Colors.white,
- canvasColor: Colors.white,
- appBarTheme: const AppBarTheme(
- backgroundColor: Colors.white,
- foregroundColor: Colors.black,
- elevation: 0,
- scrolledUnderElevation: 0,
- ),
- navigationBarTheme: const NavigationBarThemeData(
- backgroundColor: Colors.white,
- indicatorColor: Color(0xFFE0E0E0),
- ),
- // 卡片全靠描边分辨,不靠阴影(墨水屏上阴影就是一团脏灰)
- cardTheme: CardThemeData(
- color: Colors.white,
- elevation: 0,
- shape: RoundedRectangleBorder(
- borderRadius: BorderRadius.circular(12),
- side: const BorderSide(color: Color(0xFF9E9E9E)),
- ),
- ),
- dividerTheme: const DividerThemeData(color: Color(0xFF9E9E9E)),
- splashFactory: NoSplash.splashFactory,
- pageTransitionsTheme: const PageTransitionsTheme(
- builders: <TargetPlatform, PageTransitionsBuilder>{
- 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<T>(
- PageRoute<T> route,
- BuildContext context,
- Animation<double> animation,
- Animation<double> secondaryAnimation,
- Widget child,
- ) =>
- child;
- }
- /// 灰阶滤镜。
- ///
- /// 用**一个**颜色滤镜覆盖全局,而不是去给每个配色/每张图表换一套灰阶调色板 ——
- /// 那些颜色散在主题、工具卡片、绘图组件、Symja 的 PNG 里,逐个改必然漏。
- /// E-Ink 关掉时套的是单位矩阵(同样的 widget、同样的开销,只是颜色不变),
- /// 这样切换不会动到 widget 树的形状。
- class _Grayscale {
- const _Grayscale._();
- /// 亮度权重就是通常的 Rec.709 公式。
- static const ColorFilter filter = ColorFilter.matrix(<double>[
- 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(<double>[
- 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<ShellPage> createState() => _ShellPageState();
- }
- class _ShellPageState extends State<ShellPage> {
- 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: '控制台',
- ),
- ],
- ),
- );
- }
- }
|