Flutter: Use custom ErrorWidget - flutter

Is there a way to display (in production) a custom error widget instead of the red screen of death? I will not change the framework code, plus even though we are trying to program without errors, we can't guarantee it to happen.

It is possible to change the default error rendering by changing ErrorWidget.builder
ErrorWidget.builder = (errorDetails) {
return Container(color: Colors.red);
}

Use ErrorWidget.builder instead main() function, like this
Future main() async {
WidgetsFlutterBinding.ensureInitialized();
ErrorWidget.builder = (FlutterErrorDetails details) => SomethingWrong();
runApp(YourApp());
}
And if you want to show Certain Widget only on Production app, then you can use kReleaseMode-
if (kReleaseMode) {
ErrorWidget.builder = (FlutterErrorDetails details) => SomethingWrong();
}
Note: The top-level kReleaseMode constant is use to determine whether the app was compiled in release mode.

Related

Problems with camera in flutter app (The selected imageFormatGroup is not supported by > Android. Defaulting to yuv420)

I have problems with integration of a photography function in my app.
I get asked if I permit the access to the camera, but after that nothing happens exept this errror:
W/Camera (26849): The selected imageFormatGroup is not supported by
Android. Defaulting to yuv420
I/CameraManagerGlobal(26849): Camera 0 facing CAMERA_FACING_BACK state
now CAMERA_STATE_OPEN for client...
This is my code:
class FaultReporting extends StatefulWidget {
#override
_FaultReportingState createState()=> _FaultReportingState();
}
class _FaultReportingState extends State<FaultReporting>{
bool isReady=false;
List<CameraDescription> cameras;
CameraController camController;
#override
void initState() {
super.initState();
setupCameras();
}
Future<void> setupCameras() async {
try {
cameras = await availableCameras();
camController = new CameraController(cameras[0], ResolutionPreset.medium);
await camController.initialize();
} on CameraException catch (_) {
setState(() {
isReady = false;
});
}
setState(() {
isReady = true;
});
}
...
child: ElevatedButton(
onPressed: (){
if(!isReady && !camController.value.isInitialized)
{
return Container();
}
return AspectRatio(
aspectRatio: camController.value.aspectRatio,
child: CameraPreview(camController),
);
},
...
I had the exact same error when I used the camera on flutter. The message is just informing you that the imageFormatGroup parameter must be ImageFormatGroup.yuv420.
So try this:
camController = new CameraController(
cameras[0],
ResolutionPreset.medium,
imageFormatGroup: ImageFormatGroup.yuv420,
);
I had the same issue. However, the abovementioned solution (adding imageFormatGroup: ImageFormatGroup.yuv420) did not solve the problem.
I found out what was the real problem. Turns out Flutter Navigation requires pushed routes to be popped before another one pushed into navigator.
Otherwise, it will cause big issues with all camera using packages (CameraController from camera, QRViewController? controller from qr_code_scanner, MobileScannerController cameraController from mobile_scanner all affected) using packages (Both Android and iOS), specially if you use your own custom internal navigation without using Flutter Navigator.
Because, that way you use Navigator partially and in some situations you push several different routes without popping them or even pushing same route several times.
The camera will come either black or keep loading forever. Only thing will solve the issue is to kill/force stop the app and open again as it clears Flutter Navigator.
In Android you will see this error: "MessageQueue: java.lang.IllegalStateException: Handler sending message to a Handler on a dead thread"
In iOS you will see this: "The selected imageFormatGroup is not supported by iOS. Defaulting to brga8888"
Another related error from mobile_scanner package: flutter: MobileScanner: Called start() while already started!
Realted error from qr_code_scanner package: Terminating app due to uncaught exception 'NSInternalInconsistencyException', reason: 'Scan rect cannot be set when not (yet) scanning. You may want to set it within didStartScanningBlock.
Here is the solution which may help you to prevent it. What we need to do is to make sure that when we push new route to the Navigator we are clearing other previously pushed routes from it:
For undefined routes:
Navigator.of(context).pushAndRemoveUntil(
MaterialPageRoute(
builder: (context) => YourPage()),
(Route<dynamic> route) => false);
For defined routes:
Navigator.of(context)
.pushNamedAndRemoveUntil('/yourdefinedroute', (Route<dynamic> route) => false);

How do I run Flutter widget tests with shadows enabled?

I'd like to render some of my Flutter golden files with shadows. How can I accomplish that?
Shadows are disabled by default:
https://github.com/flutter/flutter/blob/master/packages/flutter_test/lib/src/binding.dart#L942
With the debugDisableShadows flag.
import 'package:flutter_test/flutter_test.dart';
void main() {
testWidgets('my golden test with shadows enabled', (tester) async {
// Enable shadows
debugDisableShadows = false;
await tester.pumpWidget(MyWidget());
await expectLater(find.byType(MyWidget), matchesGoldenFile('..'));
// Set the flag back to normal
debugDisableShadows = true;
});
}
Note that you have to toggle the flag back to normal inside the test case (and not setUp / tearDown) - otherwise, it will fail.
This happens because this check is performed immediately after the body of testWidgets() completes, but before the test case is considered finished. Which is also before tearDown() is executed.

In Flutter, How to avoid crash screen by replacing problem content with a text object? [duplicate]

Is there a way to display (in production) a custom error widget instead of the red screen of death? I will not change the framework code, plus even though we are trying to program without errors, we can't guarantee it to happen.
It is possible to change the default error rendering by changing ErrorWidget.builder
ErrorWidget.builder = (errorDetails) {
return Container(color: Colors.red);
}
Use ErrorWidget.builder instead main() function, like this
Future main() async {
WidgetsFlutterBinding.ensureInitialized();
ErrorWidget.builder = (FlutterErrorDetails details) => SomethingWrong();
runApp(YourApp());
}
And if you want to show Certain Widget only on Production app, then you can use kReleaseMode-
if (kReleaseMode) {
ErrorWidget.builder = (FlutterErrorDetails details) => SomethingWrong();
}
Note: The top-level kReleaseMode constant is use to determine whether the app was compiled in release mode.

Fully restart(discard and recreate) the whole flutter app programmatically [duplicate]

In production mode, is there a way to force a full restart of the application (I am not talking about a hot reload at development time!).
Practical use cases:
At initialization process the application detects that there is no network connection. The lack of network connectivity might have prevented a correct start up (e.g. loading of external resource such as JSON files...).
During the initial handshaking, new versions of some important resources need to be downloaded (kind of update).
In both use cases, I would like the application to proceed with a full restart, rather than having to build a complex logic at the ApplicationState level.
You could wrap your whole app into a statefulwidget. And when you want to restart you app, rebuild that statefulwidget with a child that possess a different Key.
This would make you loose the whole state of your app.
import 'package:flutter/material.dart';
void main() {
runApp(
RestartWidget(
child: MaterialApp(),
),
);
}
class RestartWidget extends StatefulWidget {
RestartWidget({this.child});
final Widget child;
static void restartApp(BuildContext context) {
context.findAncestorStateOfType<_RestartWidgetState>().restartApp();
}
#override
_RestartWidgetState createState() => _RestartWidgetState();
}
class _RestartWidgetState extends State<RestartWidget> {
Key key = UniqueKey();
void restartApp() {
setState(() {
key = UniqueKey();
});
}
#override
Widget build(BuildContext context) {
return KeyedSubtree(
key: key,
child: widget.child,
);
}
}
In this example you can reset your app from everywhere using RestartWidget.restartApp(context).
The flutter_phoenix package is based on Rémi Rousselet's answer, making it even simpler.
void main() {
runApp(
Phoenix(
child: App(),
),
);
}
Then when you need to restart the app, just call:
Phoenix.rebirth(context);
I developed the restart_app plugin to restart the whole app natively.
Update:
For anyone who get this exception:
MissingPluginException(No implementation found for method restartApp on channel restart)
Just stop and rebuild the app.
You can also use the runApp(new MyWidget) function to do something similar
This is what this function does:
Inflate the given widget and attach it to the screen.
The widget is given constraints during layout that force it to fill the entire screen. If you wish to align your widget to one side of the screen (e.g., the top), consider using the Align widget. If you wish to center your widget, you can also use the Center widget
Calling runApp again will detach the previous root widget from the screen and attach the given widget in its place. The new widget tree is compared against the previous widget tree and any differences are applied to the underlying render tree, similar to what happens when a StatefulWidget rebuilds after calling State.setState.
https://docs.flutter.io/flutter/widgets/runApp.html
So simple package: flutter_restart
dependencies:
flutter_restart: ^0.0.3
to use:
void _restartApp() async {
FlutterRestart.restartApp();
}
I just want to add Regarding I have Tried #Remi answer which works great on most of the cases to restart the app. The only problem with the answer is that some things if you are doing Navigation route extensively you probably go to a state which It gives you an error like,
The method 'restartApp' was called on null.
To resolve this error you have to know the Context and use Navigator.of(context).pop(); multiples times back. For me, the solution is that just go to the initial route. It will inject all the states from a new. Where you want to restart just add this Line.
Navigator.pushNamedAndRemoveUntil(context,'/',(_) => false);
If you want to only restart a specific widget then the Remi solution is awesome. Thanks for the solution Remi though. It help me understand states in flutter.
I have found Hossein's restart_app package also pretty useful for native restarts (not only on Flutter level).
To everyone having the MissingPluginException error, just reinstall the app again on the device, means that hot reload won't work. The app has native methods which need to compiled in the Android/iOS App.
I wanted to restart my app after logout.
so I used https://pub.dev/packages/flutter_phoenix (flutter phoenix).
It worked for me.
Install flutter_phoenix by running this command on your terminal inside your flutter app directory.
$ flutter pub add flutter_phoenix
Import it inside your "main.dart".
Wrap your root widget inside Phoenix.
runApp(
Phoenix(
child: MyApp()
));
Now you can call this wherever you want to restart your app :-
Phoenix.rebirth(context)
Note: flutter_phoenix does not restart the app on OS level, it only restarts the app on app level.
Thecnically this is not a restart but it will work for most of the scenarios:
// Remove any route in the stack
Navigator.of(context).popUntil((route) => false);
// Add the first route. Note MyApp() would be your first widget to the app.
Navigator.push(
context,
CupertinoPageRoute(builder: (context) => const MyApp()),
);
Follow the steps-
Go to your terminal and type in the following:
flutter pub add flutter_restart
This will update some dependencies in pubspec.yaml file.
Import the following package in whichever file you want to implement the restart code-
import 'package:flutter_restart/flutter_restart.dart';
Create a void function
void _restartApp() async {
await FlutterRestart.restartApp();
}
Write this wherever you want to start the app-
_restartApp();
I tried the above suggested methods and none of them worked and i was using getx.
so i ended up modified the accepted answer with a delay as a workaround and it works now.
class RestartAppWidget extends StatefulWidget {
RestartAppWidget({this.child});
final Widget child;
static void restartApp(BuildContext context) {
context.findAncestorStateOfType<_RestartAppWidgetState>().restartApp();
}
#override
_RestartAppWidgetState createState() => _RestartAppWidgetState();
}
class _RestartAppWidgetState extends State<RestartAppWidget> {
bool restarting = false;
void restartApp() async {
restarting = true; // restart variable is set to true
setState(() {});
Future.delayed(Duration(milliseconds: 300)).then((value) {
setState(() {
restarting = false; //restart variable is set to false
});
});
// setState(() {
// key = UniqueKey();
// });
}
#override
Widget build(BuildContext context) {
if (restarting) {
return SizedBox(); //an empty Sizedbox is displayed for 300 milliseconds you can add a loader if you want
}
return SizedBox(
child: widget.child,
);
}
}`
wrap the root widget with RestartAppWidget
runApp(RestartAppWidget(
child: MyApp(),
))
you can use this code to restart the app at flutter level
RestartAppWidget.restartApp(Get.context);

How to force a Flutter application restart (in production mode)?

In production mode, is there a way to force a full restart of the application (I am not talking about a hot reload at development time!).
Practical use cases:
At initialization process the application detects that there is no network connection. The lack of network connectivity might have prevented a correct start up (e.g. loading of external resource such as JSON files...).
During the initial handshaking, new versions of some important resources need to be downloaded (kind of update).
In both use cases, I would like the application to proceed with a full restart, rather than having to build a complex logic at the ApplicationState level.
You could wrap your whole app into a statefulwidget. And when you want to restart you app, rebuild that statefulwidget with a child that possess a different Key.
This would make you loose the whole state of your app.
import 'package:flutter/material.dart';
void main() {
runApp(
RestartWidget(
child: MaterialApp(),
),
);
}
class RestartWidget extends StatefulWidget {
RestartWidget({this.child});
final Widget child;
static void restartApp(BuildContext context) {
context.findAncestorStateOfType<_RestartWidgetState>().restartApp();
}
#override
_RestartWidgetState createState() => _RestartWidgetState();
}
class _RestartWidgetState extends State<RestartWidget> {
Key key = UniqueKey();
void restartApp() {
setState(() {
key = UniqueKey();
});
}
#override
Widget build(BuildContext context) {
return KeyedSubtree(
key: key,
child: widget.child,
);
}
}
In this example you can reset your app from everywhere using RestartWidget.restartApp(context).
The flutter_phoenix package is based on Rémi Rousselet's answer, making it even simpler.
void main() {
runApp(
Phoenix(
child: App(),
),
);
}
Then when you need to restart the app, just call:
Phoenix.rebirth(context);
I developed the restart_app plugin to restart the whole app natively.
Update:
For anyone who get this exception:
MissingPluginException(No implementation found for method restartApp on channel restart)
Just stop and rebuild the app.
You can also use the runApp(new MyWidget) function to do something similar
This is what this function does:
Inflate the given widget and attach it to the screen.
The widget is given constraints during layout that force it to fill the entire screen. If you wish to align your widget to one side of the screen (e.g., the top), consider using the Align widget. If you wish to center your widget, you can also use the Center widget
Calling runApp again will detach the previous root widget from the screen and attach the given widget in its place. The new widget tree is compared against the previous widget tree and any differences are applied to the underlying render tree, similar to what happens when a StatefulWidget rebuilds after calling State.setState.
https://docs.flutter.io/flutter/widgets/runApp.html
So simple package: flutter_restart
dependencies:
flutter_restart: ^0.0.3
to use:
void _restartApp() async {
FlutterRestart.restartApp();
}
I just want to add Regarding I have Tried #Remi answer which works great on most of the cases to restart the app. The only problem with the answer is that some things if you are doing Navigation route extensively you probably go to a state which It gives you an error like,
The method 'restartApp' was called on null.
To resolve this error you have to know the Context and use Navigator.of(context).pop(); multiples times back. For me, the solution is that just go to the initial route. It will inject all the states from a new. Where you want to restart just add this Line.
Navigator.pushNamedAndRemoveUntil(context,'/',(_) => false);
If you want to only restart a specific widget then the Remi solution is awesome. Thanks for the solution Remi though. It help me understand states in flutter.
I have found Hossein's restart_app package also pretty useful for native restarts (not only on Flutter level).
To everyone having the MissingPluginException error, just reinstall the app again on the device, means that hot reload won't work. The app has native methods which need to compiled in the Android/iOS App.
I wanted to restart my app after logout.
so I used https://pub.dev/packages/flutter_phoenix (flutter phoenix).
It worked for me.
Install flutter_phoenix by running this command on your terminal inside your flutter app directory.
$ flutter pub add flutter_phoenix
Import it inside your "main.dart".
Wrap your root widget inside Phoenix.
runApp(
Phoenix(
child: MyApp()
));
Now you can call this wherever you want to restart your app :-
Phoenix.rebirth(context)
Note: flutter_phoenix does not restart the app on OS level, it only restarts the app on app level.
Thecnically this is not a restart but it will work for most of the scenarios:
// Remove any route in the stack
Navigator.of(context).popUntil((route) => false);
// Add the first route. Note MyApp() would be your first widget to the app.
Navigator.push(
context,
CupertinoPageRoute(builder: (context) => const MyApp()),
);
Follow the steps-
Go to your terminal and type in the following:
flutter pub add flutter_restart
This will update some dependencies in pubspec.yaml file.
Import the following package in whichever file you want to implement the restart code-
import 'package:flutter_restart/flutter_restart.dart';
Create a void function
void _restartApp() async {
await FlutterRestart.restartApp();
}
Write this wherever you want to start the app-
_restartApp();
I tried the above suggested methods and none of them worked and i was using getx.
so i ended up modified the accepted answer with a delay as a workaround and it works now.
class RestartAppWidget extends StatefulWidget {
RestartAppWidget({this.child});
final Widget child;
static void restartApp(BuildContext context) {
context.findAncestorStateOfType<_RestartAppWidgetState>().restartApp();
}
#override
_RestartAppWidgetState createState() => _RestartAppWidgetState();
}
class _RestartAppWidgetState extends State<RestartAppWidget> {
bool restarting = false;
void restartApp() async {
restarting = true; // restart variable is set to true
setState(() {});
Future.delayed(Duration(milliseconds: 300)).then((value) {
setState(() {
restarting = false; //restart variable is set to false
});
});
// setState(() {
// key = UniqueKey();
// });
}
#override
Widget build(BuildContext context) {
if (restarting) {
return SizedBox(); //an empty Sizedbox is displayed for 300 milliseconds you can add a loader if you want
}
return SizedBox(
child: widget.child,
);
}
}`
wrap the root widget with RestartAppWidget
runApp(RestartAppWidget(
child: MyApp(),
))
you can use this code to restart the app at flutter level
RestartAppWidget.restartApp(Get.context);