In the app, on starting splash screen appears after that I reach to the login screen where there is no app bar or back button but I'm able to back from device back button. I don't want to back on the splash screen when the back is pressed from login screen. I tried many solutions but they are not working.
class Login extends StatelessWidget {
#override
Widget build(BuildContext context) {
return MaterialApp(
title: 'xyz',
home: LoginPage(),
debugShowCheckedModeBanner: false,
);
}
}
class LoginPage extends StatefulWidget {
#override
_LoginPageState createState() => _LoginPageState();
}
class _LoginPageState extends State<LoginPage> {
#override
Widget build(BuildContext context) {
return WillPopScope(
onWillPop: () {
if (Navigator.canPop(context)) {
Navigator.pop(context); // i tried both the way
Navigator.of(context).pop();
} else {
SystemNavigator.pop();
}
},
child:Scaffold(
body: Container(.........................//here is my desiging stuff
I also tried return Future.value(false); with true and false value –
Splash screen code
#override
void initState (){
super.initState();
// TODO initial state stuff
new Future.delayed(
const Duration(seconds: 2),
() => Navigator.push(
context,
MaterialPageRoute(builder: (context) => Login()),
));
}
Simply pass empty function to - WillPopScope widget.
class LoginPage extends StatefulWidget {
#override
_LoginPageState createState() => _LoginPageState();
}
class _LoginPageState extends State<LoginPage> {
#override
Widget build(BuildContext context) {
return WillPopScope(
onWillPop: () {}, // Empty Function.
child: Scaffold(
body: Container(), //here is my desiging stuff
));
}
}
OR
#override
Widget build(BuildContext context) {
return WillPopScope(
onWillPop: () async {
return false;
},
child: Scaffold(
body: Container(),
appBar: AppBar(),
),
);
}
If you never want to goto Splash Screen.
It's Better to use:
Navigator.of(context)
.pushReplacement(MaterialPageRoute(builder: (context) => Login()));
Just change your code to:
class _LoginPageState extends State<LoginPage> {
#override
Widget build(BuildContext context) {
return WillPopScope(
onWillPop: ()async => false,
child:Scaffold(
body: Container(.........................//here is my desiging stuff
I had the same problem, and it was because wrongly surrounding MaterialApp with WillPopScope (instead of surrounding Scaffold).
Appreciating #anmol-majhail, here is the correct solution in my case:
#override
Widget build(BuildContext context) {
return MaterialApp(
title: 'Fetch Data Example',
home: WillPopScope(
child: Scaffold(...),
onWillPop: () async {
return false;
}),
);
}
Related
i want wait 5 seconds on main page and display loading animation then navigate to another page.
here is my code
import 'mainPage.dart';
import 'package:flutter/material.dart';
import 'package:flutter_spinkit/flutter_spinkit.dart';
import 'dart:async';
void main() {
runApp(MyApp());
}
class MyApp extends StatefulWidget {
#override
_MyAppState createState() => _MyAppState();
}
class _MyAppState extends State<MyApp> {
bool isLoading = true;
#override
void initState() {
super.initState();
loadData();
}
#override
Widget build(BuildContext context) {
return MaterialApp(
debugShowCheckedModeBanner: false,
home: Scaffold(
backgroundColor: Colors.cyan,
body: Builder(
builder: (context) => Center(
child: Container(
child: SpinKitCubeGrid(color: Colors.white, size: 50.0),
),
),
),
),
);
}
Future loadData() async {
return new Timer(Duration(seconds: 5), () {
setState(() {
Navigator.of(context)
.push(MaterialPageRoute(builder: (context) => MainPage()));
});
});
}
}
but i got this error:
Unhandled Exception: Navigator operation requested with a context that does not include a Navigator.
what should i do?
Wrap MyApp with MaterialApp which will provide the right context to Navigator
void main() {
runApp(MaterialApp(home: MyApp()));
}
Maybe this will help U
static Route route() {
return MaterialPageRoute<void>(builder: (_) => MyApp());
}
onPressed: () => Navigator.of(context).push<void>(MainPage.route()),
................
static Route route() {
return MaterialPageRoute<void>(builder: (_) => MainPage());
}
onPressed: () => Navigator.of(context).push<void>(MyApp.route()),
Can you try it like this? I didn't run the code but showing the basic idea. Just pass the context and call it from build function.
class _MyAppState extends State<MyApp> {
bool isLoading = true;
#override
Widget build(BuildContext context) {
loadData(context);
return MaterialApp(
...
);
}
Future loadData(context) async {
...
}
}
You need a context to navigate with the Navigator.
To navigate without using context you can use a package called GetX
Example:
Add "Get" before your MaterialApp, turning it into GetMaterialApp
GetMaterialApp( // Before: MaterialApp(
home: MyHome(),
)
Navigate to a new screen:
Get.to(NextScreen());
I am trying to display an Alert that shows a disclaimer to the user as soon as the app is opened. The build method will run, that is the app will start its processing only after the user presses okay on the alert.
I've managed to show the alert in init using
SchedulerBinding.instance.addPostFrameCallback((_) => AlertWindow().showAlert(context));
or
Future.delayed(Duration.zero, () => AlertWindows().showAlert(context));
This shows the alert, but the app starts building in the background. I want the app to run/build only after OKAY button is pressed, and after the alert is popped.
Hey I implemented some code, you can try this code directly on dartPad Paste the code in this Editor
I used setState, if it is for real time project you can use Providers or bloc, for performance.
import 'package:flutter/material.dart';
final Color darkBlue = Color.fromARGB(255, 18, 32, 47);
void main() {
runApp(MyApp());
}
class MyApp extends StatelessWidget {
#override
Widget build(BuildContext context) {
return MaterialApp(
theme: ThemeData.dark().copyWith(scaffoldBackgroundColor: darkBlue),
debugShowCheckedModeBanner: false,
home: Scaffold(
body: Center(
child: MyWidget(),
),
),
);
}
}
class MyWidget extends StatefulWidget {
#override
_MyWidgetState createState() => _MyWidgetState();
}
class _MyWidgetState extends State<MyWidget> {
Widget viewHolder;
void initState() {
viewHolder = Container();
WidgetsBinding.instance
.addPostFrameCallback((_) => afterPostFrameCallBack());
super.initState();
}
afterPostFrameCallBack() {
_showDialog();
}
#override
Widget build(BuildContext context) {
return viewHolder;
}
Widget _buildView() {
return Container(child: Text('This is after okay button'));
}
void _showDialog() {
// flutter defined function
showDialog(
context: context,
builder: (BuildContext context) {
// return object of type Dialog
return AlertDialog(
title: Text("App Update Available"),
content: Text(
"We have fixed some issues and added some cool features in this update"),
actions: <Widget>[
// usually buttons at the bottom of the dialog
FlatButton(
child: new Text("ok"),
onPressed: () {
Navigator.of(context).pop();
setState(() {
viewHolder = _buildView();
});
},
),
],
);
},
);
}
}
I'm having a problem with my app.
The situation is: i have a very simple login system on the app and i save the logged user using SharedPreferences.
But if the user leaves the app and then return it will open the login screen again, so i want to skip the login screen if the user is logged.
So in my main i put a function to check if there is login information, if yes it would redirect right to the app page or to the login page if not.
But when i try to call the app page it always calls the page setted on the Home part.
How can i solve this?
Is there any way to make it ignore the Home?
Is there a way to make the "if" part on the home? Would be the better solution but its not possible.
Also i know i'm not using the best way to make this control, but it works (despite of this problem i have now, sure) and if you have any tips on making it better i would appreciate.
Heres my code:
class MyApp extends StatefulWidget {
#override
_MyAppState createState() => _MyAppState();
}
void main() => runApp(new MyApp());
class _MyAppState extends State<MyApp> {
Future<void> verificaLogin() async {
print("running ok"); //just to test if the function runs
final prefs = await SharedPreferences.getInstance();
final key = 'usuario';
final value = prefs.getString(key);
print('saved tester $value');
String usu = value; /
if (usu.isEmpty) {
BuildContext context;
Navigator.push(
context,
MaterialPageRoute(builder: (context) => LoginScreen()), //sends to loginscreen if not logged
);
}
if (usu.isNotEmpty) {
BuildContext context;
Navigator.of(context)
.pushReplacement(MaterialPageRoute(builder: (context) => Pedidos())); //sends to main (not main.dart) app page
}
}
#override
void initState() {
super.initState();
WidgetsBinding.instance.addPostFrameCallback((_) => verificaLogin());
}
Widget build(BuildContext context) {
return BotToastInit(
child: MaterialApp(
navigatorObservers: [BotToastNavigatorObserver()],
title: "Test App",
theme: ThemeData(
primarySwatch: Colors.green,
),
debugShowCheckedModeBanner: false,
home: LoginScreen(), //i'm calling the loginscreen, it ignores the function on the top
),
);
}
}
Please make one SplashScreen like below it resolves your issue..
and call this as home: SplashScreen(),
class SplashScreen extends StatefulWidget {
#override
_SplashScreenState createState() => _SplashScreenState();
}
class _SplashScreenState extends State<SplashScreen> {
String loginData = "";
SharedPreferences sharedPreferences;
void initState() {
super.initState();
readFromStorage();
}
void readFromStorage() async {
sharedPreferences = await SharedPreferences.getInstance();
final key = 'usuario';
loginData = sharedPreferences.getString(key);
if (loginData.isNotEmpty) {
Future.delayed(const Duration(milliseconds: 3000), () {
setState(() {
Navigator.of(context)
.pushReplacement(MaterialPageRoute(builder: (context) => Pedidos()));
});
});
} else {
Future.delayed(const Duration(milliseconds: 3000), () {
setState(() {
Navigator.push(
context,
MaterialPageRoute(builder: (context) => LoginScreen()),);
});
});
}
}
#override
Widget build(BuildContext context) {
return Scaffold(
body: Center(
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: <Widget>[
FlutterLogo(
size: 100.0,
),
],
)),
);
}
}
Hi you can use a FutureBuilder at vefiricaLoigin, and then Material App at home use verificaLogin,
https://api.flutter.dev/flutter/widgets/FutureBuilder-class.html
verificaLogin() {
return FutureBuilder<Widget>(
future: SharedPreferences.getInstance(),
builder: (BuildContext context, prefs) {
final key = 'usuario';
final value = prefs.getString(key);
String usu = value;
if (usu.isEmpty) {
return LoginScreen()l
} else {
return Pedidos();
}
);
}
Widget build(BuildContext context) {
return MaterialApp(
title: "Test App",
theme: ThemeData(
primarySwatch: Colors.green,
),
debugShowCheckedModeBanner: false,
home: verificaLogin()
);
}
I'm building android app using flutter. I have a problem to close the simple dialog programmatically.
Now I have a stateful page named ListVessel. This page contains listTile from array otherVessels.
Below is the code for this page.
class ListVessel extends StatefulWidget {
final Function() notifyParent;
ListVessel({Key key, #required this.notifyParent}) : super(key: key);
#override
_ListVesselState createState() => _ListVesselState();
}
class _ListVesselState extends State<ListVessel> {
#override
Widget build(BuildContext context) {
return ListView.separated(
separatorBuilder: (context, index) => Divider(color: Colors.blueGrey),
itemCount: otherVessels.length,
itemBuilder: (context, index) {
return ListTile(
title: Text("Name: "+otherVessels[index]["shipName"]),
onTap: () {
showDialog (
context: context,
builder: (_){
return otherTap(idx:index);
}
);
}
);
},
);
}
}
}
From above code, each tile (vessel) can be tapped and it calls otherTap() method. otherTap() method displays a simple dialog (popup) that contains the details of the tapped vessel.
Below is the code for otherTap().
class otherTap extends StatefulWidget{
otherTap({Key key, #required this.idx}) : super(key: key);
final int idx;
#override
_otherTapState createState() => new _otherTapState();
}
class _otherTapState extends State<otherTap>{
#override
Widget build(BuildContext context){
_isDialogShowing = true;
return SimpleDialog(
title: Text(otherVessels[widget.idx]["shipName"]),
children: <Widget>[
SimpleDialogOption(
child: Text('MMSI : ' + otherVessels[widget.idx]['MMSI']),
)
],
);
}
}
I have a global boolean variable (_isDialogShowing) to keep tracking if the dialog is showing.
Now i want the showdialog (popup) to dismiss after 5 second.
I use Navigator.pop() to dismiss the dialog in the MyApp function. I put it inside setstate() function.
void main() {
runApp(
MyApp(storage: CounterStorage()),
);
}
class MyApp extends StatefulWidget {
MyApp({Key key, #required this.storage}) : super(key: key);
final CounterStorage storage;
#override
State<StatefulWidget> createState() => new _MyAppState();
}
class _MyAppState extends State<MyApp> {
final appTitle = 'Testing applicatin';
void _update(BuildContext context) async {
await Future.delayed(Duration(milliseconds: 5000));
setState(() {
if(_isDialogShowing){
_isDialogShowing = false;
Navigator.pop(context);
//Navigator.of(context).pop();
}
});
}
#override
Widget build(BuildContext context) {
_update(context);
return new WillPopScope(
onWillPop: null,
child: new MaterialApp(
debugShowCheckedModeBanner: false,
title: appTitle,
home: MyHomePage(title: appTitle),
routes: {
Routes.home: (context) => MyHomePage(),
Routes.settings: (context) => SettingsPage(),
},
),
);
}
}
However the navigator.pop methods above doesn't close the popup.
Can anyone help?
You need to call pop on the context that you receive in builder of showDialog(), only then the dialog will pop that was created by that showDialog().
Replace your showDialog() with following and it will work for you:
showDialog(
context: context,
builder: (BuildContext context) {
Future.delayed(Duration(seconds: 5)).then((_) {
Navigator.pop(context);
});
return otherTap(idx:index);
},
);
I've solved this issue using
Navigator.of(context, rootNavigator: true).pop();
In my example app below, I have two routes: HomeRoute and OtherRoute. I want OtherRoute to always return a result when it is popped from the navigator. In this example, it does this when the RaisedButton in OtherRoute is pressed. However, I also want it to return a result when the back button is pressed. I know I can override the back button on the AppBar, but I also want to override the behavior of the "system" back button on Android.
I know I can use WillPopScope to prevent the system back button from popping the current route, but I do not want this. If possible, I want the system back button to still be able to pop the current route, just with a result included. Is this possible?
Here is my sample app:
import 'package:flutter/material.dart';
void main() => runApp(MyApp());
class MyApp extends StatelessWidget {
// This widget is the root of your application.
#override
Widget build(BuildContext context) {
return MaterialApp(
title: 'Test',
home: Scaffold(
appBar: AppBar(title: Text("Home"),),
body: HomeRoute(),
),
);
}
}
class HomeRoute extends StatelessWidget{
#override
Widget build(BuildContext context) {
return RaisedButton(onPressed: (){
Navigator.push(context, MaterialPageRoute(builder: (context){
return OtherRoute();
})).then((result){
// I want result here to never be null
Scaffold.of(context).showSnackBar(SnackBar(content: Text("$result")));
});
});
}
}
class OtherRoute extends StatelessWidget{
#override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(title: Text("Other route"),),
body: RaisedButton(onPressed: (){
Navigator.pop(context, "Result!");
})
);
}
}
Wrap your scaffold with WillPopScope in OtherRoute and override onWillPop method as follows:
#override
Widget build(BuildContext context) {
return WillPopScope(
//....
),
onWillPop: () async {
Navigator.pop(context, "Result!");
return false;
},
);
}