1+ import 'dart:async' ;
2+
13import 'package:flutter/material.dart' ;
24import 'package:flutter_bloc/flutter_bloc.dart' ;
35import 'package:flutter_platform_widgets/flutter_platform_widgets.dart' ;
@@ -29,15 +31,17 @@ class UpdateStepView extends StatelessWidget {
2931 );
3032 case UpdateFirmwareStateHistory ():
3133 return Column (
34+ crossAxisAlignment: CrossAxisAlignment .start,
3235 children: [
33- for (var state in state.history)
36+ for (var s in state.history)
3437 Row (
3538 children: [
3639 _stateIcon (
37- state ,
40+ s ,
3841 Colors .green,
3942 ),
40- PlatformText (state.stage),
43+ const SizedBox (width: 8 ),
44+ PlatformText (s.stage),
4145 ],
4246 ),
4347 if (state.currentState != null )
@@ -54,6 +58,7 @@ class UpdateStepView extends StatelessWidget {
5458 _currentState (state),
5559 ],
5660 ),
61+ const SizedBox (height: 12 ),
5762 if (state.isComplete && state.updateManager? .logger != null )
5863 ElevatedButton (
5964 onPressed: () {
@@ -76,14 +81,23 @@ class UpdateStepView extends StatelessWidget {
7681 },
7782 child: PlatformText ('Update Again' ),
7883 ),
84+
85+ // Verification info + countdown
7986 if (state.isComplete &&
8087 state.history.last is UpdateCompleteSuccess )
81- PlatformText (
82- 'Firmware upload complete.\n\n '
83- 'The image has been successfully uploaded and is now being verified by the device. '
84- 'The device will automatically restart once verification is complete.\n\n '
85- 'This may take up to 3 minutes. Please keep the device powered on and nearby.' ,
86- textAlign: TextAlign .start,
88+ Column (
89+ crossAxisAlignment: CrossAxisAlignment .start,
90+ children: [
91+ PlatformText (
92+ 'Firmware upload complete.\n\n '
93+ 'The image has been successfully uploaded and is now being verified by the device. '
94+ 'The device will automatically restart once verification is complete.\n\n '
95+ 'This may take up to 3 minutes. Please keep the device powered on and nearby.' ,
96+ textAlign: TextAlign .start,
97+ ),
98+ const SizedBox (height: 8 ),
99+ const _VerificationCountdown (),
100+ ],
87101 ),
88102 ],
89103 );
@@ -139,3 +153,62 @@ class UpdateStepView extends StatelessWidget {
139153 );
140154 }
141155}
156+
157+ /// Small stateful widget that starts a 3-minute countdown when built.
158+ /// It appears only once the firmware upload completed successfully.
159+ class _VerificationCountdown extends StatefulWidget {
160+ const _VerificationCountdown ();
161+
162+ @override
163+ State <_VerificationCountdown > createState () => _VerificationCountdownState ();
164+ }
165+
166+ class _VerificationCountdownState extends State <_VerificationCountdown > {
167+ static const Duration _total = Duration (minutes: 3 );
168+ late Duration _remaining;
169+ Timer ? _timer;
170+
171+ @override
172+ void initState () {
173+ super .initState ();
174+ _remaining = _total;
175+
176+ _timer = Timer .periodic (const Duration (seconds: 1 ), (timer) {
177+ if (! mounted) {
178+ timer.cancel ();
179+ return ;
180+ }
181+ if (_remaining.inSeconds <= 1 ) {
182+ setState (() {
183+ _remaining = Duration .zero;
184+ });
185+ timer.cancel ();
186+ } else {
187+ setState (() {
188+ _remaining -= const Duration (seconds: 1 );
189+ });
190+ }
191+ });
192+ }
193+
194+ @override
195+ void dispose () {
196+ _timer? .cancel ();
197+ super .dispose ();
198+ }
199+
200+ String _format (Duration d) {
201+ final m = d.inMinutes.remainder (60 ).toString ().padLeft (2 , '0' );
202+ final s = d.inSeconds.remainder (60 ).toString ().padLeft (2 , '0' );
203+ return '$m :$s ' ;
204+ }
205+
206+ @override
207+ Widget build (BuildContext context) {
208+ return PlatformText (
209+ 'Estimated remaining: ${_format (_remaining )}' ,
210+ textAlign: TextAlign .start,
211+ style: Theme .of (context).textTheme.bodyLarge? .copyWith (fontSize: 16 ),
212+ );
213+ }
214+ }
0 commit comments