Skip to content

Commit 6398ab7

Browse files
authored
2.1.2 (#10)
* ci: fix * ci: fix * Error handler widget (#9) * Reworked ErrorHandlerWidget to LeafRenderObjectWidget * chore: bump to 2.1.2 * fix: exports
1 parent 4fe3db3 commit 6398ab7

File tree

9 files changed

+176
-72
lines changed

9 files changed

+176
-72
lines changed

.github/workflows/push.yaml

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -13,5 +13,4 @@ jobs:
1313
- uses: jacopocarlini/action-autotag@master
1414
with:
1515
GITHUB_TOKEN: "${{ secrets.GITHUB_TOKEN }}"
16-
package_root: "/packages/mad_pay"
1716
tag_prefix: "v"

CHANGELOG.md

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,7 @@
1+
## 2.1.2
2+
3+
* Reworked ErrorHandlerWidget to LeafRenderObjectWidget to solve the loop bug
4+
15
## 2.1.1
26

37
* Add `onPlatformError` in `RunnerConfiguration`. It allows you to handle isolate errors via `PlatformDispatcher.instance.onError`

lib/app_runner.dart

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -12,4 +12,5 @@ export 'src/app_runner.dart'
1212
InitializeBinding,
1313
InitializeFunctions,
1414
OnError,
15-
ErrorWidgetBuilder;
15+
RenderObjectBuilder,
16+
ErrorRenderObjectBuilder;

lib/src/_app_runner.dart

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -31,10 +31,10 @@ mixin _AppRunner on WidgetsBinding {
3131
..scheduleForcedFrame();
3232
}
3333

34-
static void _platformErrorSetup(final ErrorCallback? onPlatformError) {
34+
static void _platformErrorSetup(final ui.ErrorCallback? onPlatformError) {
3535
if (onPlatformError == null) return;
3636

37-
final ErrorCallback? oldCallback = PlatformDispatcher.instance.onError;
37+
final ui.ErrorCallback? oldCallback = PlatformDispatcher.instance.onError;
3838

3939
PlatformDispatcher.instance.onError = (
4040
Object exception,

lib/src/app_runner.dart

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
import 'dart:async';
2-
import 'dart:ui';
2+
import 'dart:ui' as ui;
33

44
import 'package:flutter/foundation.dart';
55
import 'package:flutter/material.dart' hide ErrorWidgetBuilder;

lib/src/error_handler_widget.dart

Lines changed: 146 additions & 57 deletions
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,12 @@
11
part of 'app_runner.dart';
22

3+
const double _kMaxWidth = 100000.0;
4+
const double _kMaxHeight = 100000.0;
5+
36
/// {@template ErrorHandlerWidget}
47
/// Widget that catches and handles widget errors
58
/// {@endtemplate}
6-
class ErrorHandlerWidget extends StatelessWidget {
9+
class ErrorHandlerWidget extends LeafRenderObjectWidget {
710
/// {@macro ErrorHandlerWidget}
811
ErrorHandlerWidget({
912
required this.errorDetails,
@@ -15,89 +18,134 @@ class ErrorHandlerWidget extends StatelessWidget {
1518
final FlutterErrorDetails errorDetails;
1619

1720
/// Widget for error handling in debug and profile mode
18-
final ErrorWidgetBuilder? errorBuilder;
21+
final ErrorRenderObjectBuilder? errorBuilder;
1922

2023
/// Widget for error handling in release mode
21-
final WidgetBuilder? releaseErrorBuilder;
24+
final RenderObjectBuilder? releaseErrorBuilder;
2225

2326
@override
24-
Widget build(BuildContext context) {
27+
RenderObject createRenderObject(BuildContext context) {
2528
if (kReleaseMode) {
26-
final WidgetBuilder? _releaseErrorBuilder = releaseErrorBuilder;
29+
final RenderObjectBuilder? _releaseErrorBuilder = releaseErrorBuilder;
2730
if (_releaseErrorBuilder != null) {
2831
return _releaseErrorBuilder(context);
2932
}
3033

31-
return const _ReleaseErrorWidget();
34+
return _RenderReleaseErrorBox(context.reloadWidget);
3235
}
3336

34-
final ErrorWidgetBuilder? _errorBuilder = errorBuilder;
37+
final ErrorRenderObjectBuilder? _errorBuilder = errorBuilder;
3538
if (_errorBuilder != null) {
3639
return _errorBuilder(context, errorDetails);
3740
}
3841

39-
return _ErrorWidget(errorDetails: errorDetails);
42+
return _RenderErrorBox(errorDetails, context.reloadWidget);
4043
}
4144
}
4245

43-
class _ReleaseErrorWidget extends StatelessWidget {
44-
const _ReleaseErrorWidget({
45-
Key? key,
46-
}) : super(key: key);
46+
class _RenderReleaseErrorBox extends RenderBox {
47+
_RenderReleaseErrorBox(this.onTap) {
48+
try {
49+
final ui.ParagraphBuilder builder = ui.ParagraphBuilder(paragraphStyle);
50+
builder.pushStyle(textStyle);
51+
builder.addText('Something went wrong\n\nTap to reload application');
52+
_paragraph = builder.build();
53+
} catch (_) {
54+
// If an error happens here we're in a terrible state, so we really should
55+
// just forget about it and let the developer deal with the already-reported
56+
// errors. It's unlikely that these errors are going to help with that.
57+
}
58+
}
59+
60+
final ui.ParagraphStyle paragraphStyle = ui.ParagraphStyle(
61+
textDirection: TextDirection.ltr,
62+
textAlign: TextAlign.center,
63+
);
64+
65+
final ui.TextStyle textStyle = ui.TextStyle(
66+
fontSize: 18.0,
67+
);
68+
69+
late final ui.Paragraph _paragraph;
70+
71+
final VoidCallback onTap;
4772

4873
@override
49-
Widget build(BuildContext context) {
50-
return Scaffold(
51-
body: Center(
52-
child: Column(
53-
mainAxisAlignment: MainAxisAlignment.center,
54-
children: <Widget>[
55-
const Text('Something went wrong'),
56-
Padding(
57-
padding: const EdgeInsets.all(8.0),
58-
child: ElevatedButton(
59-
onPressed: () => context.reloadWidget(),
60-
child: const Text('Reload application'),
61-
),
62-
),
63-
],
64-
),
65-
),
66-
);
74+
double computeMaxIntrinsicWidth(double height) {
75+
return _kMaxWidth;
6776
}
68-
}
6977

70-
class _ErrorWidget extends StatelessWidget {
71-
const _ErrorWidget({Key? key, required this.errorDetails}) : super(key: key);
78+
@override
79+
double computeMaxIntrinsicHeight(double width) {
80+
return _kMaxHeight;
81+
}
7282

73-
final FlutterErrorDetails errorDetails;
83+
@override
84+
bool get sizedByParent => true;
7485

7586
@override
76-
Widget build(BuildContext context) {
77-
return Scaffold(
78-
body: Padding(
79-
padding: const EdgeInsets.all(10),
80-
child: ListView(
81-
children: <Widget>[
82-
Center(
83-
child: ElevatedButton(
84-
onPressed: () => context.reloadWidget(),
85-
child: const Text('Reload Widgets'),
86-
),
87-
),
88-
Text('Library: ${errorDetails.toStringShort()}'),
89-
const Divider(),
90-
Text('DiagnosticsNode: ${errorDetails.context?.toDescription()}'),
91-
const Divider(),
92-
Text('ErrorDetails: ${_stringify(errorDetails.exception)}'),
93-
const Divider(),
94-
Text('StackTrace: ${errorDetails.stack.toString()}'),
95-
],
96-
),
97-
),
98-
);
87+
bool hitTestSelf(Offset position) {
88+
onTap();
89+
return true;
9990
}
10091

92+
@override
93+
Size computeDryLayout(BoxConstraints constraints) {
94+
return constraints.constrain(const Size(_kMaxWidth, _kMaxHeight));
95+
}
96+
97+
@override
98+
void paint(PaintingContext context, Offset offset) {
99+
try {
100+
context.canvas.drawRect(
101+
offset & size,
102+
Paint()..color = const ui.Color(0xEC2E2E2E),
103+
);
104+
105+
_paragraph.layout(ui.ParagraphConstraints(width: size.width));
106+
context.canvas.drawParagraph(_paragraph, size.centerLeft(offset));
107+
} catch (_) {
108+
// If an error happens here we're in a terrible state, so we really should
109+
// just forget about it and let the developer deal with the already-reported
110+
// errors. It's unlikely that these errors are going to help with that.
111+
}
112+
}
113+
}
114+
115+
class _RenderErrorBox extends RenderBox {
116+
_RenderErrorBox(final FlutterErrorDetails errorDetails, this.onTap) {
117+
try {
118+
final ui.ParagraphBuilder builder = ui.ParagraphBuilder(paragraphStyle);
119+
builder.pushStyle(textStyle);
120+
builder.addText('''
121+
Library: ${errorDetails.toStringShort()}
122+
DiagnosticsNode: ${errorDetails.context?.toDescription()}
123+
124+
ErrorDetails: ${_stringify(errorDetails.exception)}
125+
126+
StackTrace: \n${errorDetails.stack.toString()}
127+
''');
128+
_paragraph = builder.build();
129+
} catch (_) {
130+
// If an error happens here we're in a terrible state, so we really should
131+
// just forget about it and let the developer deal with the already-reported
132+
// errors. It's unlikely that these errors are going to help with that.
133+
}
134+
}
135+
136+
final ui.ParagraphStyle paragraphStyle = ui.ParagraphStyle(
137+
textDirection: TextDirection.ltr,
138+
textAlign: TextAlign.left,
139+
);
140+
141+
final ui.TextStyle textStyle = ui.TextStyle(
142+
fontSize: 14.0,
143+
);
144+
145+
late final ui.Paragraph _paragraph;
146+
147+
final VoidCallback onTap;
148+
101149
String _stringify(Object exception) {
102150
try {
103151
return exception.toString();
@@ -106,4 +154,45 @@ class _ErrorWidget extends StatelessWidget {
106154
}
107155
return 'Error';
108156
}
157+
158+
@override
159+
double computeMaxIntrinsicWidth(double height) {
160+
return _kMaxWidth;
161+
}
162+
163+
@override
164+
double computeMaxIntrinsicHeight(double width) {
165+
return _kMaxHeight;
166+
}
167+
168+
@override
169+
bool get sizedByParent => true;
170+
171+
@override
172+
bool hitTestSelf(Offset position) {
173+
onTap();
174+
return true;
175+
}
176+
177+
@override
178+
Size computeDryLayout(BoxConstraints constraints) {
179+
return constraints.constrain(const Size(_kMaxWidth, _kMaxHeight));
180+
}
181+
182+
@override
183+
void paint(PaintingContext context, Offset offset) {
184+
try {
185+
context.canvas.drawRect(
186+
offset & size,
187+
Paint()..color = const ui.Color(0xEC2E2E2E),
188+
);
189+
190+
_paragraph.layout(ui.ParagraphConstraints(width: size.width - 20));
191+
context.canvas.drawParagraph(_paragraph, offset + const Offset(10, 28));
192+
} catch (_) {
193+
// If an error happens here we're in a terrible state, so we really should
194+
// just forget about it and let the developer deal with the already-reported
195+
// errors. It's unlikely that these errors are going to help with that.
196+
}
197+
}
109198
}

lib/src/models.dart

Lines changed: 13 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -28,10 +28,15 @@ typedef RunnerBuilder<T> = Widget Function(
2828
/// {@endtemplate}
2929
typedef OnError = void Function(Object error, StackTrace stackTrace);
3030

31-
/// {@template ErrorWidgetBuilder}
31+
/// {@template RenderObjectBuilder}
32+
/// Custom Builder for error handling in release mode
33+
/// {@endtemplate}
34+
typedef RenderObjectBuilder = RenderObject Function(BuildContext context);
35+
36+
/// {@template ErrorRenderObjectBuilder}
3237
/// Custom Builder for error handling in debug and profile mode
3338
/// {@endtemplate}
34-
typedef ErrorWidgetBuilder = Widget Function(
39+
typedef ErrorRenderObjectBuilder = RenderObject Function(
3540
BuildContext context,
3641
FlutterErrorDetails errorDetails,
3742
);
@@ -47,7 +52,7 @@ abstract class RunnerConfiguration {
4752
/// If this method returns [false], the engine may use some fallback method to provide information about the error;
4853
const factory RunnerConfiguration({
4954
required WidgetConfiguration widgetConfig,
50-
ErrorCallback? onPlatformError,
55+
ui.ErrorCallback? onPlatformError,
5156
}) = _RunnerConfiguration;
5257

5358
/// Runner Configuration Guarded
@@ -73,7 +78,7 @@ class _RunnerConfiguration extends RunnerConfiguration {
7378

7479
/// A callback that is invoked when an unhandled error occurs in the root isolate.
7580
/// If this method returns [false], the engine may use some fallback method to provide information about the error.
76-
final ErrorCallback? onPlatformError;
81+
final ui.ErrorCallback? onPlatformError;
7782
}
7883

7984
class _RunnerConfigurationGuarded extends RunnerConfiguration {
@@ -112,11 +117,11 @@ class WidgetConfiguration {
112117
/// Called whenever the Flutter framework catches an error.
113118
final void Function(FlutterErrorDetails errorDetails) onFlutterError;
114119

115-
/// {@macro ErrorWidgetBuilder}
116-
final ErrorWidgetBuilder? errorBuilder;
120+
/// {@macro ErrorRenderObjectBuilder}
121+
final ErrorRenderObjectBuilder? errorBuilder;
117122

118-
/// Custom Builder for error handling in release mode
119-
final WidgetBuilder? releaseErrorBuilder;
123+
/// {@macro RenderObjectBuilder}
124+
final RenderObjectBuilder? releaseErrorBuilder;
120125

121126
/// {@macro InitializeBinding}
122127
final InitializeBinding? initializeBinding;

lib/src/reloadable_widget.dart

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -19,7 +19,13 @@ class ReloadableWidget extends StatefulWidget {
1919
/// Reloads widgets when [reloadWidget] is called.
2020
/// Reloads occurs by changing [UniqueKey].
2121
static void reloadWidget(BuildContext context) {
22-
context.findAncestorStateOfType<_ReloadableWidgetState>()?.reloadWidget();
22+
try {
23+
context.findAncestorStateOfType<_ReloadableWidgetState>()?.reloadWidget();
24+
} catch (_) {
25+
// If an error happens here we're in a terrible state, so we really should
26+
// just forget about it and let the developer deal with the already-reported
27+
// errors. It's unlikely that these errors are going to help with that.
28+
}
2329
}
2430
}
2531

pubspec.yaml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
name: app_runner
22
description: Launch your Flutter app like a pro! AppRunner is a configurator for quick and controlled launch of your application.
3-
version: 2.1.1
3+
version: 2.1.2
44
repository: https://github.com/MadBrains/App-Runner-Flutter
55
issue_tracker: https://github.com/MadBrains/App-Runner-Flutter/issues
66
homepage: https://madbrains.ru/

0 commit comments

Comments
 (0)