Navigator.pop won't close the simpledialog in flutter - android

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

Related

Undefined name '_image'. Try correcting the name to one that is defined, or defining the name

Please help.
I'm trying to display the photo captured in 'Generated1Group1Widget1.dart' to 'GeneratedResultsWidget.dart'. However, 'main.dart' is having some errors.
GeneratedGroup1Widget1.dart
class GeneratedGroup1Widget1 extends StatefulWidget {
#override
_GeneratedGroup1Widget1State createState() => _GeneratedGroup1Widget1State();
}
class _GeneratedGroup1Widget1State extends State<GeneratedGroup1Widget1> {
XFile? _image;
Future _pickImage() async {
final imageSource = await showDialog<ImageSource>(
context: context,
builder: (context) => SimpleDialog(
title: const Text('Select Image Source'),
children: [
SimpleDialogOption(
onPressed: () => Navigator.pop(context, ImageSource.camera),
child: const Text('Camera'),
),
SimpleDialogOption(
onPressed: () => Navigator.pop(context, ImageSource.gallery),
child: const Text('Gallery'),
),
],
),
);
if (imageSource != null) {
final image = await ImagePicker().pickImage(source: imageSource);
setState(() {
_image = image;
});
Navigator.push(
context,
MaterialPageRoute(
builder: (context) => GeneratedResultsWidget(image: _image),
),
);
}
}
#override
Widget build(BuildContext context) {
return GestureDetector(
onTap: () => _pickImage(),
...
GeneratedResultsWidget.dart
class GeneratedResultsWidget extends StatelessWidget {
final XFile? image;
GeneratedResultsWidget({
required this.image,
});
#override
Widget build(BuildContext context) {
return Material(
child: ClipRRect(
...
main.dart
void main() {
runApp(food_classifierApp());
}
class food_classifierApp extends StatelessWidget {
#override
Widget build(BuildContext context) {
return ScreenUtilInit(
designSize: Size(360, 640),
builder: (BuildContext context,child) => MaterialApp(
title: 'food-classifier',
theme: ThemeData(
primarySwatch: Colors.blue,
),
initialRoute: '/GeneratedHomepageWidget',
routes: {
'/GeneratedScanWidget': (context) => GeneratedScanWidget(),
'/GeneratedResultsWidget': (context) => GeneratedResultsWidget(image: _image),
'/GeneratedHomepageWidget': (context) => GeneratedHomepageWidget(),
'/GeneratedFoodlistWidget': (context) => GeneratedFoodlistWidget(),
},
),
);
}
}
Error
Undefined name '_image'.
Try correcting the name to one that is defined, or defining the name.
I already searched up google but I can't find answers to my question. Thanks in advance!
Where exactly you define _image variable in your food_classifierApp class?
You should use state management to access your image or ... in every where of your app
You need to create model to store your picked image file from GeneratedGroup1Widget1.
class AppModel {
String fileName;
AppModel(this.fileName);
}
Now you can call it from GeneratedGroup1Widget1
Navigator.pushNamed(context, '/GeneratedResultsWidget',
arguments: AppModel('your file or file name'));
And recieve it here
class GeneratedResultsWidget extends StatelessWidget {
const GeneratedResultsWidget({Key? key}) : super(key: key);
#override
Widget build(BuildContext context) {
final args = ModalRoute.of(context)!.settings.arguments as AppModel;
return Scaffold(body: Center(child: Text(args.fileName)));
}
}
Route should be
routes: {
//***
'/GeneratedResultsWidget': (context) => GeneratedResultsWidget(),
//***
}

How to show a page in flutter at a specific date and time

how can I show a page in flutter at a specific date and time
so I have an events app when the user sign in it will navigate him
to a page that has a countdown to the date this I made
but I want to know how to navigate him to the page when the time comes
I think the solution is to display the said page while the return account will be terminated.
To do so, you would have to set a condition like this
if event.date == DateTime.now() show page contents, else show countdown
You can try this widget,
class HomePage extends StatefulWidget {
const HomePage({Key? key}) : super(key: key);
#override
State<HomePage> createState() => _HomePageState();
}
class _HomePageState extends State<HomePage> {
DateTime specificDate = DateTime.now()
.add(const Duration(seconds: 3)); //change based on your date.
#override
void initState() {
super.initState();
WidgetsBinding.instance.addPostFrameCallback((timeStamp) {
_todayIsTheDay();
});
}
_todayIsTheDay() {
Timer.periodic(const Duration(seconds: 1), (timer) {
if (DateTime.now().difference(specificDate).inSeconds ==
Duration.zero.inSeconds) {
timer.cancel();
Navigator.of(context).push(
MaterialPageRoute(
builder: (context) => const SpecificPage(),
),
);
}
});
}
#override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: Text("Home Page"),
),
);
}
}
class SpecificPage extends StatelessWidget {
const SpecificPage({Key? key}) : super(key: key);
#override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: Text("Specific Page"),
),
);
}
}
I think this can be improved

Navigate to a specific page in a specific tab Flutter

I am quite new to flutter and I am trying to implement navigation between different tabs.
I am using a CupertinoTabView because I need a parallel navigators and a specific history for each tab in my bottom bar.
My question is whether it is possible to switch from a child page of Tab1 to a child page of Tab2, and if so in which way.
In this image you can see my widget tree:
Class MyApp.dart
class MyApp extends StatefulWidget {
#override
_MyAppState createState() => _MyAppState();
}
class _MyAppState extends State<MyApp> {
TabItem _currentTab = TabItem.tab1;
final Map<TabItem, GlobalKey<NavigatorState>> navigatorKeys = {
TabItem.tab1: GlobalKey<NavigatorState>(),
TabItem.tab1: GlobalKey<NavigatorState>(),
};
Map<TabItem, WidgetBuilder> get widgetBuilders {
return {
TabItem.tab1: (_) => Tab1(),
TabItem.tab2: Tab2(),
};
}
void _select(TabItem tabItem) {
if (tabItem == _currentTab) {
navigatorKeys[tabItem].currentState.popUntil((route) => route.isFirst);
} else {
setState(() => _currentTab = tabItem);
}
}
#override
Widget build(BuildContext context) {
return CupertinoScaffold(
currentTab: _currentTab,
onSelectTab: _select,
widgetBuilders: widgetBuilders,
navigatorKeys: navigatorKeys,
);
}
}
Class CupertinoScaffold.dart (contains CupertinoTabView)
class CupertinoScaffold extends StatelessWidget {
const CupertinoScaffold({
Key key,
#required this.currentTab,
#required this.onSelectTab,
#required this.widgetBuilders,
#required this.navigatorKeys,
}) : super(key: key);
final TabItem currentTab;
final ValueChanged<TabItem> onSelectTab;
final Map<TabItem, WidgetBuilder> widgetBuilders;
final Map<TabItem, GlobalKey<NavigatorState>> navigatorKeys;
#override
Widget build(BuildContext context) {
return CupertinoTabScaffold(
tabBar: CupertinoTabBar(
key: Key(Keys.tabBar),
items: [
BottomNavigationBarItem(title: Text('Tab1')),
BottomNavigationBarItem(title: Text('Tab2'))
],
onTap: (index) => onSelectTab(TabItem.values[index]),
),
tabBuilder: (context, index) {
final item = TabItem.values[index];
return CupertinoTabView(
navigatorKey: navigatorKeys[item],
builder: (context) => widgetBuilders[item](context),
onGenerateRoute: CupertinoTabViewRouter.generateRoute,
);
},
);
}
}
Class Tab1.dart
class Tab1 extends StatelessWidget {
#override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(title: Text('Tab1')),
body: Center(
child: Button(
child: Text(
'Open child tab1'
),
onPressed: () => Navigator.of(context).pushNamed('/tab1-child'),
),
),
);
}
}
Class CupertinoTabViewRouter.dart
class CupertinoTabViewRouter {
static Route generateRoute(RouteSettings settings) {
switch (settings.name) {
case '/tab1-child':
return CupertinoPageRoute<dynamic>(
builder: (_) => Tab1Child(),
settings: settings,
fullscreenDialog: true,
);
case '/tab2-child':
return CupertinoPageRoute<dynamic>(
builder: (_) => Tab2Child(),
settings: settings,
fullscreenDialog: true,
);
}
return null;
}
}
Class Tab1Child.dart
class Tab1Child extends StatelessWidget {
#override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(title: Text('ChildTab1')),
body: Center(
child: Button(
child: Text(
'Open child tab2'
),
onPressed: () => //TODO!,
),
),
);
}
}

Flutter: Calling different pages deppending on the situation (logged or not logged user)

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

Flutter: close app on back press is not working

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

Categories

Resources