How to restart a Flutter app from the beginning of the main()? - flutter

I want to restart my app at some point from the beginning of the main() function and not just rebuild the whole widget tree.
Here is an example of my code, the thing is I want to call init() when restart
void main() async {
WidgetsFlutterBinding.ensureInitialized();
await init();
runApp(
const MyApp(),
);
}
I have tried to wrap the widget tree with a Restart widget and tried phoenix package
But both of them just rebuild the widget tree regardless of what is above in main(). I even tried to call the main() directly but it didn't work as expected.
Is there any solution for this case?

I think you can try "Restart App" module
Instal from your teminal:
flutter pub add restart_app
Import from your dart file:
import 'package:restart_app/restart_app.dart';
Call "Restart App" from main():
onPressed: () {
Restart.restartApp();}
Have a nice day...

You runApp(YourApp()) again where you want to restart.1

Related

How do I automatically route when in Flutter from within main()?

I implemented a third-party "listener" in my Flutter ios app. I have two goals:
Whenever the listener receives an event, route to LandingPage;
Pass to LandingPage the values captured by the listener -- globalReferralData.
Values should also be written to local variables on disk. The screen/page that opens should be able read that variable.
Because the listener is in main(), there is no context.
I also cannot use Get for GetPage, because it seems to collide with using GoRouter as it is used and defined throughout the rest of the app in the nav.dart file.
ReferralData globalReferralData;
List<BuildContext> buildContextList = [];
void main() async {
WidgetsFlutterBinding.ensureInitialized();
await Firebase.initializeApp();
FFAppState(); // Initialize FFAppState
GetSocial.addOnInitializedListener(() => {
// GetSocial SDK is ready to use
});
runApp(MaterialApp(
home: MyApp(),
routes: {
'/landingPage': (context) => const LandingPageWidget(),
},
navigatorKey: locator<NavigationService>().navigatorKey));
registerListeners();
}
void registerListeners() {
Invites.setOnReferralDataReceivedListener((received) {
globalReferralData = received;
print(globalReferralData);
print(globalReferralData.linkParams);
print(globalReferralData.linkParams['referralID']);
// pass value and open route -- this line failed to do anything
Navigator.pushNamed(context, '/landingPage');
// showAlert(buildContextList.last, 'Referral Data Received', '$received');
});
}
Based on your comment
... generate state from outside build or State and then access it if I can only route that way. Ideally, I can route from within main().....
Yes it's possible. That's with reference to routing without BuildContext. The following is the mechanism behind Navigation in Stacked Architecture.
In summary, you provide MaterialApp with your own navigatorKey and use that key to navigate anywhere in the Flutter code. A clean way to do this is to use dependency injection. Have a service provided by GetIt package that will make the navigatorKey accessible to the entire Flutter app.
In details, we can use the following 4 steps:
1. NavigationService and key
Have a navigation.service.dart file with a navigatorKey. This key will have the type and value of GlobalKey<NavigatorState>
import 'package:flutter/material.dart';
class NavigationService {
final GlobalKey<NavigatorState> navigatorKey = GlobalKey<NavigatorState>();
}
This NavigationService class can do other things but let's leave it like this for now.
2. GetIt package
The package provides some mechanism to make services available app-wide. You actually initialise it before the runApp() call just as you do with Firebase for example.
Install the package. Run the following command.
flutter pub add get_it
Then, have a file, let's say app.locator.dart with the following.
import 'package:get_it/get_it.dart';
import 'navigation.service.dart';
final locator = GetIt.instance;
void setupLocator {
locator.registerLazySingleton(() => NavigationService());
}
Then in main.dart, call setupLocator() before runApp(). Based on the main method you provided:
void main() async {
WidgetsFlutterBinding.ensureInitialized();
await Firebase.initializeApp();
FFAppState(); // Initialize FFAppState
GetSocial.addOnInitializedListener(() => {
// GetSocial SDK is ready to use
});
setupLocator(); //add this line
runApp(MyApp());
registerListeners();
}
3. Key and MaterialApp
Attach the navigatorKey from the NavigationService (obtained from locator) to the navigatorKey on the topmost MaterialApp.
So in whatever file where you defined MaterialApp, have something like this
// ...
return MaterialApp(
navigatorKey: locator<NavigationService>().navigatorKey,
// ...
);
4. Routing without BuildContext
In the registerListeners() function (called from main and not having access to BuildContext), you navigate with the navigatorKey. You can also use the context attached to the navigatorKey to showAlert too. Change your registerListeners() function to the following
void registerListeners() {
Invites.setOnReferralDataReceivedListener((received) {
globalReferralData = received;
print(globalReferralData);
print(globalReferralData.linkParams);
print(globalReferralData.linkParams['referralID']);
// instead of navigating with Navigator,
// Navigator.pushNamed(context, '/landingPage');
// navigate with the navigatorKey
locator<NavigationService>().navigatorKey.currentState!.pushNamed('/landingPage');
// You can as well use the key's context to showAlert.
// so instead of the following
// showAlert(buildContextList.last, 'Referral Data Received', '$received');
// You can have
showAlert(
locator<NavigationService>().navigatorKey.currentContext!,
'Referral Data Received',
'$received',
);
});
}
This way you have access to routing without the BuildContext and can use it in any other Dart file in the same project.
And hence also you've ideally routed from within main.
The above doesn't invalidate any routing with Navigator.of(context).navigate... in other parts of the app as it'll still be the same NavigationState that is been used by Flutter.
Update
I've updated the snippet of step 4 above.
From your comment
I'm unclear how from within main() which is where I put the listener class to capture the globalReferralData how I can pass that based on the example above to then route....
If you carry step 4 well above (and all the other steps) the problem should be solved. It should be solved because your main method calls this registerListeners() function, so that should do the trick.
Do you now understand?
It's also good that you called registerListeners after runApp, that way you're sure the navigatorKey must have been attached to MaterialApp.
...
That should solve one part of the problem, that's navigation. The above is just minimalist setup to serve the purpose you want. You can go further to use the entire StackedServices and or StackedApp if you wish.
The other issue about setState not working in main, it's really not possible. It's just natural. main is an entry point for compiled code and not a StatefulWidget. I'm supposing that if the above routing works for you, you might not have the issue of setState again.
But if you still need something, I propose you notifyListeners. Other state management architectures, not only Stacked, use notifyListeners (a method from ChangeNotifier) to update UI from outside StatefulWidget.
I guess you would have your way around this. And you could still go ahead with the View and ViewModel classes of Stacked and call notifyListeners in a viewModel (maybe that of a splash screen) when the globalReferredData is received.

How can i add 2 functions in void main before run app , in Flutter?

I need to run 2 function inside my main.dart file, before run app.
One is the bottom navigation bar, the other one is the easy localization.
My main.dart is:
void main() => runApp(
MyApp()
);
Future <void> main() async {
WidgetsFlutterBinding.ensureInitialized();
await EasyLocalization.ensureInitialized();
runApp(
EasyLocalization(
supportedLocales: [Locale('en', 'US'), Locale('it', 'IT'), Locale('fr', 'FR')],
path: 'assets/translations', // <-- change the path of the translation files
fallbackLocale: Locale('en', 'US'),
assetLoader: CodegenLoader(),
child: MyLangApp(),
),
);
}
class MyApp extends StatelessWidget {
#override
Widget build(BuildContext context){
return new MaterialApp(
home: MyBottomNavBar(),
);
}
}
So basically, i cannot have 2 void main inside the main.dart. How can i put the 2 functions together?
There are two separate parts to your question, and the answers are different.
You're making main() async, and that handles your localization. If you want even more control, we had a discussion about using async functions before rendering the first frame on HumpDayQandA this week, and went into how you can extend the native splash screen as long as needed so your functions can finish. It's the episode for 29 June, 2022 on the FlutterCommunity YouTube channel. https://www.youtube.com/watch?v=9KFk4lypFD4
The BottomAppBar issue is probably why you got dinged for a -1 on this question. That's because this isn't how you use BottomAppBar. It goes in a Scaffold. If you want to not have the AppBar on top then just leave it out.
But you don't want to be calling a BottomNavBar as the home parameter of a MaterialApp. Whatever you use for home is going to be the foundational visible widget in your tree, the lowest widget that all others are built on.
Does your whole app fit inside that BottomNavBar? Then you want to put the bar inside of something else that takes up the whole screen and provides a base to build the rest of your app on... Like a Scaffold.

Need help to resolve this issue while running flutter code

This is the error I'm facing while running flutter programs:
lib/main.dart: Warning: Interpreting this as package URI,
'package:flutter_guide_1/main.dart'.
My code:
import 'package:flutter/material.dart';
void mian() {}
class MyApp extends StatelessWidget {
Widget build(BuildContext context) {
return MaterialApp(
home: Text('Hello!'),
);
}
}
you need to return runApp
void main() => runApp(MyApp());
At the top of Android Studio, there is a drop-down menu with targets. It probably says "main.dart".
Inside the box, left of the "main.dart", there is a blue symbol. Either it is an "[ (F) main.dart ]" (the Flutter logo) or it is a Dart-arrow fin logo "[ (X) main.dart ]". It should be the "F" version to build properly, so select that.

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);