@@ -3,21 +3,41 @@ import 'package:flutter/material.dart';
33import 'package:flutter_riverpod/flutter_riverpod.dart' ;
44import 'package:ubuntu_provision/src/error/error_model.dart' ;
55import 'package:ubuntu_provision/ubuntu_provision.dart' ;
6+ import 'package:ubuntu_utils/ubuntu_utils.dart' ;
67import 'package:ubuntu_widgets/ubuntu_widgets.dart' ;
78import 'package:ubuntu_wizard/ubuntu_wizard.dart' ;
89import 'package:yaru/foundation.dart' ;
910import 'package:yaru/icons.dart' ;
1011import 'package:yaru/widgets.dart' ;
1112
13+ class ErrorDetails {
14+ const ErrorDetails ({
15+ this .title,
16+ this .message,
17+ this .details,
18+ this .action,
19+ this .actionLabel,
20+ }) : assert ((action == null ) == (actionLabel == null ));
21+ final String ? title;
22+ final List <String >? message;
23+ final String ? details;
24+ final void Function (WidgetRef )? action;
25+ final String ? actionLabel;
26+ }
27+
1228/// This is an error page that is shown when an unexpected error occurs.
1329/// It shows a message and a the log.
1430class ErrorPage extends ConsumerWidget with ProvisioningPage {
1531 const ErrorPage ({
1632 required this .allowRestart,
33+ this .error,
34+ this .errorParser,
1735 super .key,
1836 });
1937
2038 final bool allowRestart;
39+ final Object ? error;
40+ final ErrorDetails ? Function (Object ? )? errorParser;
2141
2242 @override
2343 Widget build (BuildContext context, WidgetRef ref) {
@@ -33,11 +53,37 @@ class ErrorPage extends ConsumerWidget with ProvisioningPage {
3353 final endText = match? .end != null ? bodyText.substring (match! .end) : '' ;
3454 final model = ref.watch (errorModelProvider);
3555
56+ final errorDetails =
57+ errorParser? .call (error ?? ModalRoute .of (context)? .settings.arguments);
58+
59+ final content = errorDetails? .message != null
60+ ? errorDetails! .message! .map (Text .new ).withSpacing (kWizardSpacing / 2 )
61+ : [
62+ Text .rich (
63+ TextSpan (
64+ text: normalText,
65+ children: [
66+ TextSpan (
67+ text: linkText,
68+ style: const TextStyle (
69+ color: Colors .blue,
70+ decoration: TextDecoration .underline,
71+ ),
72+ recognizer: TapGestureRecognizer ()
73+ ..onTap = model.launchApport,
74+ // Handle link tap
75+ ),
76+ TextSpan (text: endText),
77+ ],
78+ ),
79+ ),
80+ ];
81+
3682 return WizardPage (
3783 headerPadding: EdgeInsets .zero,
3884 contentPadding: EdgeInsets .zero,
3985 title: YaruWindowTitleBar (
40- title: Text (lang.errorPageTitle),
86+ title: Text (errorDetails ? .title ?? lang.errorPageTitle),
4187 closeSemanticLabel: lang.closeIconSemanticLabel,
4288 maximizeSemanticLabel: lang.maximizeIconSemanticLabel,
4389 minimizeSemanticLabel: lang.minimizeIconSemanticLabel,
@@ -47,31 +93,45 @@ class ErrorPage extends ConsumerWidget with ProvisioningPage {
4793 Row (
4894 mainAxisAlignment: MainAxisAlignment .center,
4995 children: [
50- SizedBox (
51- width: 400 ,
52- child: Column (
53- mainAxisAlignment: MainAxisAlignment .center,
54- children: [
55- if (image != null ) image,
56- Text .rich (
57- TextSpan (
58- text: normalText,
59- children: [
60- TextSpan (
61- text: linkText,
62- style: const TextStyle (
63- color: Colors .blue,
64- decoration: TextDecoration .underline,
65- ),
66- recognizer: TapGestureRecognizer ()
67- ..onTap = model.launchApport,
68- // Handle link tap
69- ),
70- TextSpan (text: endText),
71- ],
96+ if (image != null )
97+ Expanded (
98+ flex: 6 ,
99+ child: image,
100+ ),
101+ const SizedBox (width: kWizardSpacing),
102+ Expanded (
103+ flex: 8 ,
104+ child: Padding (
105+ padding: const EdgeInsets .only (right: 3 * kWizardSpacing),
106+ child: Column (
107+ mainAxisAlignment: MainAxisAlignment .center,
108+ crossAxisAlignment: CrossAxisAlignment .start,
109+ children: [
110+ Semantics (
111+ focused: true ,
112+ header: true ,
113+ child: Text (
114+ errorDetails? .title ?? lang.errorPageTitle,
115+ style: Theme .of (context).textTheme.titleLarge,
116+ ),
72117 ),
73- ),
74- ],
118+ SizedBox (height: kWizardSpacing),
119+ ...content,
120+ if (errorDetails? .details != null ) ...[
121+ const SizedBox (height: kWizardSpacing),
122+ YaruExpandable (
123+ expandButtonPosition:
124+ YaruExpandableButtonPosition .start,
125+ header: Text ('Technical details' ),
126+ child: TextFormField (
127+ initialValue: errorDetails! .details,
128+ readOnly: true ,
129+ maxLines: null ,
130+ ),
131+ ),
132+ ],
133+ ],
134+ ),
75135 ),
76136 ),
77137 ],
@@ -115,7 +175,11 @@ class ErrorPage extends ConsumerWidget with ProvisioningPage {
115175 model.copyWith (isLogVisible: ! model.isLogVisible);
116176 },
117177 ),
118- const SizedBox (width: kWizardBarSpacing),
178+ if (errorDetails? .action != null )
179+ PushButton .filled (
180+ onPressed: () => errorDetails.action! .call (ref),
181+ child: Text (errorDetails! .actionLabel! ),
182+ ),
119183 WizardButton (
120184 label: allowRestart ? lang.restart : lang.close,
121185 highlighted: true ,
@@ -128,7 +192,7 @@ class ErrorPage extends ConsumerWidget with ProvisioningPage {
128192 }
129193 },
130194 ),
131- ],
195+ ]. withSpacing (kWizardBarSpacing) ,
132196 ),
133197 );
134198 }
0 commit comments