From 00ad413022ccbd3d42bfd34a666b173052115a3b Mon Sep 17 00:00:00 2001 From: Mikkeli Matlock Date: Sun, 25 Jan 2026 18:47:35 +0900 Subject: [PATCH] Flutter code restructuring --- arduino/hello.ino | 10 - pi/ui/lib/app_root.dart | 52 +++++ pi/ui/lib/main.dart | 231 +--------------------- pi/ui/lib/screens/dashboard_screen.dart | 115 +++++++++++ pi/ui/lib/screens/splash_screen.dart | 42 ++++ pi/ui/lib/widgets/stat_box.dart | 30 +++ scripts/__pycache__/build.cpython-39.pyc | Bin 0 -> 3577 bytes scripts/__pycache__/deploy.cpython-39.pyc | Bin 0 -> 2738 bytes 8 files changed, 241 insertions(+), 239 deletions(-) delete mode 100644 arduino/hello.ino create mode 100644 pi/ui/lib/app_root.dart create mode 100644 pi/ui/lib/screens/dashboard_screen.dart create mode 100644 pi/ui/lib/screens/splash_screen.dart create mode 100644 pi/ui/lib/widgets/stat_box.dart create mode 100644 scripts/__pycache__/build.cpython-39.pyc create mode 100644 scripts/__pycache__/deploy.cpython-39.pyc diff --git a/arduino/hello.ino b/arduino/hello.ino deleted file mode 100644 index 47c69e6..0000000 --- a/arduino/hello.ino +++ /dev/null @@ -1,10 +0,0 @@ -void setup() { - pinMode(LED_BUILTIN, OUTPUT); -} - -void loop() { - digitalWrite(LED_BUILTIN, HIGH); - delay(10); - digitalWrite(LED_BUILTIN, LOW); - delay(1000); -} diff --git a/pi/ui/lib/app_root.dart b/pi/ui/lib/app_root.dart new file mode 100644 index 0000000..4015afd --- /dev/null +++ b/pi/ui/lib/app_root.dart @@ -0,0 +1,52 @@ +import 'package:flutter/material.dart'; + +import 'screens/splash_screen.dart'; +import 'screens/dashboard_screen.dart'; + +/// Root widget that manages app state transitions +class AppRoot extends StatefulWidget { + const AppRoot({super.key}); + + @override + State createState() => _AppRootState(); +} + +class _AppRootState extends State { + bool _initialized = false; + String _initStatus = 'Starting...'; + + @override + void initState() { + super.initState(); + _runInitSequence(); + } + + Future _runInitSequence() async { + // Simulate init checks - replace with real checks later + // (UART, GPS, sensors, etc.) + + setState(() => _initStatus = 'Checking systems...'); + await Future.delayed(const Duration(milliseconds: 800)); + + setState(() => _initStatus = 'UART: standby'); + await Future.delayed(const Duration(milliseconds: 400)); + + setState(() => _initStatus = 'GPS: standby'); + await Future.delayed(const Duration(milliseconds: 400)); + + setState(() => _initStatus = 'Ready'); + await Future.delayed(const Duration(milliseconds: 300)); + + setState(() => _initialized = true); + } + + @override + Widget build(BuildContext context) { + return AnimatedSwitcher( + duration: const Duration(milliseconds: 500), + child: _initialized + ? const DashboardScreen(key: ValueKey('dashboard')) + : SplashScreen(key: const ValueKey('splash'), status: _initStatus), + ); + } +} diff --git a/pi/ui/lib/main.dart b/pi/ui/lib/main.dart index 98504d2..d89e8aa 100644 --- a/pi/ui/lib/main.dart +++ b/pi/ui/lib/main.dart @@ -1,7 +1,7 @@ -import 'dart:async'; -import 'dart:math'; import 'package:flutter/material.dart'; +import 'app_root.dart'; + void main() { runApp(const SmartSerowApp()); } @@ -25,230 +25,3 @@ class SmartSerowApp extends StatelessWidget { ); } } - -/// Root widget that manages app state transitions -class AppRoot extends StatefulWidget { - const AppRoot({super.key}); - - @override - State createState() => _AppRootState(); -} - -class _AppRootState extends State { - bool _initialized = false; - String _initStatus = 'Starting...'; - - @override - void initState() { - super.initState(); - _runInitSequence(); - } - - Future _runInitSequence() async { - // Simulate init checks - replace with real checks later - // (UART, GPS, sensors, etc.) - - setState(() => _initStatus = 'Checking systems...'); - await Future.delayed(const Duration(milliseconds: 800)); - - setState(() => _initStatus = 'UART: standby'); - await Future.delayed(const Duration(milliseconds: 400)); - - setState(() => _initStatus = 'GPS: standby'); - await Future.delayed(const Duration(milliseconds: 400)); - - setState(() => _initStatus = 'Ready'); - await Future.delayed(const Duration(milliseconds: 300)); - - setState(() => _initialized = true); - } - - @override - Widget build(BuildContext context) { - return AnimatedSwitcher( - duration: const Duration(milliseconds: 500), - child: _initialized - ? const DashboardScreen(key: ValueKey('dashboard')) - : SplashScreen(key: const ValueKey('splash'), status: _initStatus), - ); - } -} - -/// Splash screen - shown during initialization -class SplashScreen extends StatelessWidget { - final String status; - - const SplashScreen({super.key, required this.status}); - - @override - Widget build(BuildContext context) { - return Scaffold( - backgroundColor: Colors.black, - body: Center( - child: Column( - mainAxisAlignment: MainAxisAlignment.center, - children: [ - Icon( - Icons.terrain, - size: 120, - color: Theme.of(context).colorScheme.primary, - ), - const SizedBox(height: 24), - Text( - 'Smart Serow', - style: Theme.of(context).textTheme.headlineLarge?.copyWith( - color: Colors.white, - fontWeight: FontWeight.bold, - ), - ), - const SizedBox(height: 16), - Text( - status, - style: Theme.of(context).textTheme.bodyLarge?.copyWith( - color: Colors.grey, - ), - ), - ], - ), - ), - ); - } -} - -/// Main dashboard - placeholder with random updating values -class DashboardScreen extends StatefulWidget { - const DashboardScreen({super.key}); - - @override - State createState() => _DashboardScreenState(); -} - -class _DashboardScreenState extends State { - final _random = Random(); - Timer? _timer; - - int _speed = 0; - int _rpm = 0; - double _voltage = 12.6; - int _temp = 25; - - @override - void initState() { - super.initState(); - // Update random values every 500ms - simulates live data - _timer = Timer.periodic(const Duration(milliseconds: 500), (_) { - setState(() { - _speed = _random.nextInt(120); - _rpm = 1000 + _random.nextInt(8000); - _voltage = 11.5 + _random.nextDouble() * 2; - _temp = 20 + _random.nextInt(60); - }); - }); - } - - @override - void dispose() { - _timer?.cancel(); - super.dispose(); - } - - @override - Widget build(BuildContext context) { - return Scaffold( - backgroundColor: Colors.black, - body: Padding( - padding: const EdgeInsets.all(32), - child: Column( - crossAxisAlignment: CrossAxisAlignment.stretch, - children: [ - // Header - Row( - mainAxisAlignment: MainAxisAlignment.spaceBetween, - children: [ - Text( - 'SMART SEROW', - style: Theme.of(context).textTheme.titleMedium?.copyWith( - color: Colors.teal, - letterSpacing: 2, - ), - ), - Text( - '${_voltage.toStringAsFixed(1)}V', - style: Theme.of(context).textTheme.titleMedium?.copyWith( - color: _voltage < 12.0 ? Colors.red : Colors.green, - ), - ), - ], - ), - - const SizedBox(height: 48), - - // Main speed display - Expanded( - child: Center( - child: Column( - mainAxisAlignment: MainAxisAlignment.center, - children: [ - Text( - '$_speed', - style: const TextStyle( - fontSize: 180, - fontWeight: FontWeight.w200, - color: Colors.white, - height: 1, - ), - ), - Text( - 'km/h', - style: Theme.of(context).textTheme.headlineSmall?.copyWith( - color: Colors.grey, - ), - ), - ], - ), - ), - ), - - // Bottom stats row - Row( - mainAxisAlignment: MainAxisAlignment.spaceAround, - children: [ - _StatBox(label: 'RPM', value: _rpm.toString()), - _StatBox(label: 'TEMP', value: '$_temp°C'), - _StatBox(label: 'GEAR', value: '—'), - ], - ), - ], - ), - ), - ); - } -} - -class _StatBox extends StatelessWidget { - final String label; - final String value; - - const _StatBox({required this.label, required this.value}); - - @override - Widget build(BuildContext context) { - return Column( - children: [ - Text( - value, - style: Theme.of(context).textTheme.headlineMedium?.copyWith( - color: Colors.white, - ), - ), - Text( - label, - style: Theme.of(context).textTheme.bodySmall?.copyWith( - color: Colors.grey, - letterSpacing: 1, - ), - ), - ], - ); - } -} diff --git a/pi/ui/lib/screens/dashboard_screen.dart b/pi/ui/lib/screens/dashboard_screen.dart new file mode 100644 index 0000000..79a69e3 --- /dev/null +++ b/pi/ui/lib/screens/dashboard_screen.dart @@ -0,0 +1,115 @@ +import 'dart:async'; +import 'dart:math'; +import 'package:flutter/material.dart'; + +import '../widgets/stat_box.dart'; + +/// Main dashboard - placeholder with random updating values +class DashboardScreen extends StatefulWidget { + const DashboardScreen({super.key}); + + @override + State createState() => _DashboardScreenState(); +} + +class _DashboardScreenState extends State { + final _random = Random(); + Timer? _timer; + + int _speed = 0; + int _rpm = 0; + double _voltage = 12.6; + int _temp = 25; + + @override + void initState() { + super.initState(); + // Update random values every 500ms - simulates live data + _timer = Timer.periodic(const Duration(milliseconds: 500), (_) { + setState(() { + _speed = _random.nextInt(120); + _rpm = 1000 + _random.nextInt(8000); + _voltage = 11.5 + _random.nextDouble() * 2; + _temp = 20 + _random.nextInt(60); + }); + }); + } + + @override + void dispose() { + _timer?.cancel(); + super.dispose(); + } + + @override + Widget build(BuildContext context) { + return Scaffold( + backgroundColor: Colors.black, + body: Padding( + padding: const EdgeInsets.all(32), + child: Column( + crossAxisAlignment: CrossAxisAlignment.stretch, + children: [ + // Header + Row( + mainAxisAlignment: MainAxisAlignment.spaceBetween, + children: [ + Text( + 'SMART SEROW', + style: Theme.of(context).textTheme.titleMedium?.copyWith( + color: Colors.teal, + letterSpacing: 2, + ), + ), + Text( + '${_voltage.toStringAsFixed(1)}V', + style: Theme.of(context).textTheme.titleMedium?.copyWith( + color: _voltage < 12.0 ? Colors.red : Colors.green, + ), + ), + ], + ), + + const SizedBox(height: 48), + + // Main speed display + Expanded( + child: Center( + child: Column( + mainAxisAlignment: MainAxisAlignment.center, + children: [ + Text( + '$_speed', + style: const TextStyle( + fontSize: 180, + fontWeight: FontWeight.w200, + color: Colors.white, + height: 1, + ), + ), + Text( + 'km/h', + style: Theme.of(context).textTheme.headlineSmall?.copyWith( + color: Colors.grey, + ), + ), + ], + ), + ), + ), + + // Bottom stats row + Row( + mainAxisAlignment: MainAxisAlignment.spaceAround, + children: [ + StatBox(label: 'RPM', value: _rpm.toString()), + StatBox(label: 'TEMP', value: '$_temp°C'), + StatBox(label: 'GEAR', value: '—'), + ], + ), + ], + ), + ), + ); + } +} diff --git a/pi/ui/lib/screens/splash_screen.dart b/pi/ui/lib/screens/splash_screen.dart new file mode 100644 index 0000000..d09dd78 --- /dev/null +++ b/pi/ui/lib/screens/splash_screen.dart @@ -0,0 +1,42 @@ +import 'package:flutter/material.dart'; + +/// Splash screen - shown during initialization +class SplashScreen extends StatelessWidget { + final String status; + + const SplashScreen({super.key, required this.status}); + + @override + Widget build(BuildContext context) { + return Scaffold( + backgroundColor: Colors.black, + body: Center( + child: Column( + mainAxisAlignment: MainAxisAlignment.center, + children: [ + Icon( + Icons.terrain, + size: 120, + color: Theme.of(context).colorScheme.primary, + ), + const SizedBox(height: 24), + Text( + 'Smart Serow', + style: Theme.of(context).textTheme.headlineLarge?.copyWith( + color: Colors.white, + fontWeight: FontWeight.bold, + ), + ), + const SizedBox(height: 16), + Text( + status, + style: Theme.of(context).textTheme.bodyLarge?.copyWith( + color: Colors.grey, + ), + ), + ], + ), + ), + ); + } +} diff --git a/pi/ui/lib/widgets/stat_box.dart b/pi/ui/lib/widgets/stat_box.dart new file mode 100644 index 0000000..2ef0644 --- /dev/null +++ b/pi/ui/lib/widgets/stat_box.dart @@ -0,0 +1,30 @@ +import 'package:flutter/material.dart'; + +/// A labeled stat display box for the dashboard +class StatBox extends StatelessWidget { + final String label; + final String value; + + const StatBox({super.key, required this.label, required this.value}); + + @override + Widget build(BuildContext context) { + return Column( + children: [ + Text( + value, + style: Theme.of(context).textTheme.headlineMedium?.copyWith( + color: Colors.white, + ), + ), + Text( + label, + style: Theme.of(context).textTheme.bodySmall?.copyWith( + color: Colors.grey, + letterSpacing: 1, + ), + ), + ], + ); + } +} diff --git a/scripts/__pycache__/build.cpython-39.pyc b/scripts/__pycache__/build.cpython-39.pyc new file mode 100644 index 0000000000000000000000000000000000000000..94c3e1a6501082d5394dd5e36ed5dc80d3f31ca7 GIT binary patch literal 3577 zcmZuz&2QVt73Vi8S(Y8gcI+g(JKGk`DhXv^8w5qV4mMHZuDfw;$ab3&C+tI`=d3#;?=9G!cic=L3bEwBYT4{{6CdSQBtbdg>F&IPtamku@l z4|d_XqAFC|R$3Pe?`|H&lw=`~hJx&+oNNyQF32|H=@YUR=Rz<}cGuhWdN)sqIEXS5 zCFFP84?ZVPB5^?WDiN2(QIbCeUM7M#X0%=Z5iF%VY^kNO6^H{*8%Ca%N2S@rFP2ud zp)>iH7^Dl%sws3U}awD{ZQG9~83rLs_20=ov z6ZSL`Bu&U(5XCuX?c9Rz5c01-|CPK}3CH8K?9v?aC=sR6Pot!)XL)bP(~xCZsq-8N zle4Fkm3sCpD-G1z(zy-0OY87S!1pt}VV1|@?+QN;-1Qu-P-!A4s`rLAKJOf`@Q_4% z?*&Lkg2!Ogpck|2q_PkhLV&bA^T|OJ9=vY89a1|j&1;6i5W;}@X)cC27+kQYLOP+i za=+W%?B06!Gf9fk455V4x^-vgtKy^eDl`c?mXlDP?^&Ajiak^B0QOi(y&X=Vaa3mh_sEOc@8 zm`9;`c?y4WVZTa=kAagd!b8yv)lxN8`=4djRR{h{;9~v>aNhGQhLrT|Phf(z2R$m^ z=C{_7^z~T7{D6p8z(ZDXbDIe=Is5?c{W#zedL@%(;7)hr<_!|^G|ODbiXnJRL}}74 z7J`6>2RCoHGU46*BzO12@Pp)Q*NXY~r33yUYD`;}r*%4|-g)%M)9!RV?ZG{7wzF~P z>-&Dk?`&>ttv|Tm_2wp&M~_ZvkWm-m*8CDYN-H1IKrkpf4qVQMOJ(m`eIaAQmpAjP zn=v0mUD%zWlN23He)4~9IgnF71?n1 zl_M2+`sj?N15E>(F@nrnR!;NE7@I;xe+d;);Yc6pW9wMSh9FyIq`eO~@xA=cNa242 zIYbOZ5I~J1V`QK_&=oo*&*F@0BSUIbXhTv^!*rzqq?$+r%FOrbktynQ7PMF+OKSNq z@Xw8`lhcjN@f4k_)>5NAGNr}_N`IsB-wFj$26mm7bcN1~#>sBc7*8K7_mn?eJ+epk zxOuGfXO0|@T^QN@*;fi(969vDD}ye5C$)Z~{_*E@c^kD|JkvriZ7cLL#PUbQN`*~~ z$m&QYRk%&M5G}Sid&?xce5zjhskW` zjXYrd7U2Xu&ao*dK3h*B5e0EnfGnB7Ae;bo5J>(!#LCrfw_7&HdhJ;uXAq&K+Af-| zJCSsgV8Fa-S@C7T_w%SQT{q?XMH8a5ea7KBej5R>xX{6P#P4=CfDtqS7rbvN8!-`ya+AUp(J zXIH6CBCT-Vbp^l~6E0SVRSD|F@+on$)fLZ%Y<0gkD6XEeCMn2WtdlWYeXn2?HP=1q z4(h|m2PEZbDvCMj9kLaX2?ZQ=?o{oBwpuKrt1I_*ppL~%FoJ$|HYJw8CXK+j1-O2_ zut@KjVA)Rb=>%4)jTP*WGf)WxS$o(zSHXa_yYAmx@5;g~ONA^)P%I8;#G%o78f7Ar z*a4_;kc%kxEItsNv9ht%-Tck{&W_*R+}tUxV2I8t8+UitAKdddcXzgScS<{g77R5R z&{~Po&V-*u1uG2OM4Ao;@*AsqTSt z(D_cKl>Yq{=*rgMp=gfU)GayYw7QOfa6DIbpSA&HvqUk zRt>GGzJ**E$)`QZSFkwgNnWaxUIbChq!zVb8i-tD{TM5#F;e>`bwElJ*4Q3t{5Gv6 z8uTSl!6@R})WhO(<+~5}vw#m%lvPa?z*=FuuB`2a=7!M8i4>X*$X!-^DpTyu{31OD znKG$0kT-w}RuCdMmc{{#5$n+rx2US(RK;(`i@H(o0K9Kro;Tc(AHy?>MeB20@hk3BFw+8ra`}f7MDj8X($j;FXT= zqnz)z)V0=fY5P7+L*M7W1ZD-PS8*m=A3Q^8V|2w)k86|ddr{1MUmgmsgk5}=#*dj- z-|lqRw{~P&TtgjK@PQCp8Zo2;^uvM!x=#|4Le0g(Ts2tO;K)2Gjb56@l_5?Jj6n}a wJZq%nOm z>j<9H|ETy6@b+)>@!{ja$7j&wKcQo2h!|!h!9#3EG9-3%hK?QGp=(EP=)veD{-idn zVZ>`ZxWMDNtNI%>Z1Bc-ZrJ1v=5C_lJZ~~@6EPoJ4O#$g4q6>r1N1Gn!9U^)>xeIM zXS~GbUpuVDK6>d5Z}DZe@EWs4w)D~+-e#L%vB;KR6Ly=efPRIovbC3JxXRbq`g0=w z!B;O3MvU}P`%ZE6m`{`JjLMOSr-~kBg7znoP_)lQc1m}XTq!Q-7kk}${UDboT++il zP8jqlOE^^-J&0+PGMY_QoTX8coKeB0f;A+ST)c=!yjy<@UPI5?Si1+2Iw_Wz-8BTO zW3IYmnWdUcV@>i{dv<;8^CV95FSQqm$!jA!I;_|F(T;})%6 zp3;bpvdILl*Uf!cfYN{c>96!1rlJ8~ZC86!5vNMK<19{fUFL^Vk&U>N+7Wr$c7y?y zwblOfQ@E`BI}*1*_U``HBvo7EEyiDLN#pPi_$oWyD&rtQZo<-?o{2>;zYR^g&>^yn zt)z`b6Fx^qcKu*@383vjlYfLxp)33b$@>b>snGiyv-0N7mCKwr#s{847x*t5=ScZi zHRGM%A%d9eA{Sja81>PQkv#YjpvL@u4~@CtBd_@G(=1}NJS#1O2@Xq-;^SOI1`gfg z?vq|`zqd_yte76f0Gc#2NCNO z-PqZGw!8PMaCh(N6M)K>u~bsDK&c&hCbbI^h)Oo)Da6L0%T3^{a<#=WHdzglrnPt3yx|8khb>%^_v^96OZV6_HZvstdoyck=H0CBvRgBsEtisG$FApH z`RumEN~v8jTDE%W96PMR)?N{e(ob(zTeovC+pzm?TH8C9cGnx5?-(0)7jX1(aj#+_ z%O+jgv$-tkK;+!uLFOZqR9)@nkR{qZ$)wT^!6%vG;S>l(H!G$I(`drq;m?YVhYufq zgUPBLKwJa^OFP?;$lFEG&vG&1fW)6xNd4UM8;b=WsZ5-`M=Kl*D8bSbBql&vNY&jf z7W!xDC{B+pwq>{5)t-PFqw(f;kW6~k@BQi zl{w2O;8^j=NF~>FO5YlCD;D1?0F5e_v}n*yC(Sz3=$H#22E$E7ySgy&7i5p(T{_5a zR;o69K!K&U3!hThmKN*ulmD~xJN!v;Z(kU5s!n1c_Oe`@#_9z2uJVE&CDHMqU9$;O zX5-xFU+g`796sLbiB&k2r7rENc*3^ z2Uhq7wK^a$1;8fwui-_Zu`T_t@Hr&Xm@tRA%ws;Qy>d-znFKw@3Q~RSu>h=yrB)&y zvALA6x=AGl#M*8=E7qz|eOP#HCIDYG7%zPdWLnJKxVj)60rdv($wLKMQrxfL1mWQo zbl5enXmn(1c#7^D>>S?7Lc;WM%SuD{&w`-Nff7jm?4*-L%4}u_z_IL z_Pu~KvG_Sibr6QeE)3gv(8jy%bzKWXmW{$td+ut9E6|Ic&FKv{!C_7P|Ddx)DEP%BV|l_KnR7}Co$FQj1ynYj RmPIUeHvCp&sT0fx_