show AlertDialog based on condition - flutter

I want to show a What's New style AlertDialog to inform users what has changed in my app after updating. I've created the following function to see if the app has been updated:
Future<bool> checkNewVersion() async {
PackageInfo packageInfo = await PackageInfo.fromPlatform();
String appVersion = packageInfo.version;
SharedPreferences prefs = await SharedPreferences.getInstance();
final String? currVersion = prefs.getString("version");
print("App version: $appVersion");
print("Current version: $currVersion");
if (currVersion == null) {
await prefs.setString("version", appVersion);
return true;
}
if (currVersion != appVersion) return true;
return false;
}
When this function is called in the build method below, the print statements output the following, but the alert dialog is not shown:
flutter: App version: 2.0
flutter: Current version: null
#override
Widget build(BuildContext context) {
if (checkNewVersion() == true) {
showDialog(context: context, builder: (_) =>
AlertDialog(
title: const Text("What's New / Que ha Cambiado"),
content: Text(updateInfo),
actions: <Widget>[
TextButton(
child: const Text("OK"),
onPressed: () {
Navigator.of(context).pop();
},
)
],
));
}
return Scaffold(
// app main menu
...
);
}

You are Performing an async operation to get the result. So when performing an async function that returns some Future you must await for it otherwise it will return you an incomplete future.
So when calling checkNewVersion()
You should await for its result like
var versionResult = await checkNewVersion();
Your code will be like
#override
Widget build(BuildContext context) {
var versionResult = await checkNewVersion();
if (versionResult) {
showDialog(context: context, builder: (_) =>
AlertDialog(
title: const Text("What's New / Que ha Cambiado"),
content: Text(updateInfo),
actions: <Widget>[
TextButton(
child: const Text("OK"),
onPressed: () {
Navigator.of(context).pop();
},
)
],
));
}
return Scaffold(
// app main menu
...
);
}

your checkNewVersion is an async function so you have to wait for its result, Try this:
#override
Widget build(BuildContext context) {
bool result = await checkNewVersion();
if (result) {
showDialog(context: context, builder: (_) =>
AlertDialog(
title: const Text("What's New / Que ha Cambiado"),
content: Text(updateInfo),
actions: <Widget>[
TextButton(
child: const Text("OK"),
onPressed: () {
Navigator.of(context).pop();
},
)
],
));
}
return Scaffold(
// app main menu
...
);
}

Related

Flutter: My notifyListeners() doesn't work, but only in the release apk

I have a page that shows a loading while making my API call, and once the call is done it shows the received data.
On debugger everything works correctly, but when I create the apk with 'flutter build apk', and download it, the loading remains indefinitely.
I also put a showDialog at the end of my Provider function that makes the API call (I put this showDialog just below notifyListeners().
I can't understand why in debug it works and in release it doesn't.
(This notifyListeners thing not working just does it for every API call I make)
This is the code of the provider function that makes the api call:
Future<void> getUserSites(context) async {
_userSites.clear();
isLoading = true;
notifyListeners();
try {
final response = await NetworkService.call(
url: '/api/structure/Sites',
method: Method.Get,
context: context) as List<dynamic>;
for (var i = 0; i < response.length; i++) {
_userSites.add(Sites.fromJson(response.elementAt(i)));
}
if (defaultSite == null) {
if (SimplePreferences.getDefaultSite() == null) {
defaultSite = _userSites.isNotEmpty ? _userSites.first : null;
if (defaultSite != null) {
SimplePreferences.setDefaultSite(defaultSite!.id);
}
} else {
defaultSite = _userSites.firstWhere(
(element) => element.id == SimplePreferences.getDefaultSite()!);
}
}
} catch (e) {
inspect(e);
if (SimplePreferences.getToken() != null) {
showDialog(
context: context,
builder: (ctx) => AlertDialog(
title: const Text('General Error'),
content: Text(e.toString()),
actions: [
TextButton(
onPressed: () {
Navigator.of(context).pop();
},
child: const Text(
'Ok',
),
)
],
),
);
}
// throw e;
}
isLoading = false;
notifyListeners();
showDialog(
context: context,
builder: (ctx) => AlertDialog(
title: const Text('getUserSites done!'),
content: Text(_userSites.toString()),
actions: [
TextButton(
onPressed: () {
Navigator.of(context).pop();
},
child: const Text(
'Ok',
),
)
],
),
);
}
this is the Home page code:
class HomePageScreen extends StatelessWidget { const HomePageScreen({super.key}); static const String routeName = '/';
#override Widget build(BuildContext context) { log('New Page: Home Page'); final provider = Provider.of<MyManager>(context);
return provider.isLoading ? const Center(
child: CircularProgressIndicator(),
)
: SingleChildScrollView(
physics: const BouncingScrollPhysics(),
child: Container(
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: [
MainButton(
onTap: () async {
Navigator.of(context)
.pushNamed(ShowPatrolScreen.routeName);
await provider.getPatrol(context);
},
icon: Icons.home,
title: 'ShowPatrol',
),
printSito(provider.defaultSite?.description ?? 'Nessun Sito', context),
PrintRequestZ(
showCompleted: false,
),
],
),
),
);
}
Widget printSito(String name, context) { .... //pass context for Navigator and Theme } } `
this is the main page:
...
final myScreens = [
const HomePageScreen(),
...
];
#override
void initState() {
// TODO: implement initState
super.initState();
print('token: ${SimplePreferences.getToken()}');
if (SimplePreferences.getToken() == null){
Navigator.of(context).pushReplacementNamed('/Auth');
}
var provider = Provider.of<MyManager>(context, listen: false);
provider.setAll(context); //this function calls all my API calls, but for testing, I commented out all other functions and kept only the one written above
}
#override
Widget build(BuildContext context) {
var provider = Provider.of<MyManager>(context);
return Scaffold(
appBar: const MyAppBar(title: 'Ronda',canGoBack: false,),
body: myScreens[currentPage],
bottomNavigationBar: ...
),
}
Thanks in advance!
after some research i found the solution.
You have to use WidgetsBinding.instance.addPostFrameCallback
in the parent component.
So my home page now looks like this:
#override
void initState() {
// TODO: implement initState
super.initState();
print('token: ${SimplePreferences.getToken()}');
if (SimplePreferences.getToken() == null){
Navigator.of(context).pushReplacementNamed('/Auth');
}
WidgetsBinding.instance.addPostFrameCallback((timeStamp) {
var provider = Provider.of<MyManager>(context, listen: false);
provider.setAll(context); //this function calls all my API calls, but for testing, I commented out all other functions and kept only the one written above
});
}
I don't quite understand why though. If someone could explain it to me, I'd be very happy
Use Consumer to access the Provider's Variable
return Consumer<YourProviderName>(builder : (context, value, child){
return value.isLoading? const Center(
child: CircularProgressIndicator(),
):YourWidget(),
});

How to check Alert Dialog is open only one times instead of multiple new dialog box after onTap in flutter

I am working on my flutter application and I want to check whether the alert dialog is open or not on the screen . Can anyone tell me how to do that, now everytime i press ontap and it will appear a new dialog box. how can i only appear one dialog box instead of multiple of new dialog box ?
I have try bool, ontap cancel all not working.
Future? _dialog;
Future<void> _checkTimer() async {
if (_dialog == null) {
_dialog = await Future.delayed(Duration(seconds: 5));
showTimer(context);
await _dialog;
_dialog = null;
} else {
//do nothing
}
}
showTimer(BuildContext context) {
// set up the buttons
// ignore: deprecated_member_use
if (didUserTouchedScreen = true){
Container alert = Container(child: _imageslideshowProductDetailstimer());
// show the dialog
showDialog(
barrierDismissible: true,
context: context,
builder: (BuildContext context) {
return WillPopScope(
onWillPop: () async {
didUserTouchedScreen = false;
// _checkTimer();
return true;
},
child: alert);
},
).then((_) => didUserTouchedScreen = false);
}}
behavior: HitTestBehavior.translucent,
onTapDown: (tapdown) {
print("down");
_checkTimer();
},
onTapCancel: (){print('up');_checkTimer();}
You can achieve this with a boolean state, let's call it isButtonActive. The button is enabled/disabled depending on the value of this state. When the button is pressed, set the state to false, and when the dialog box is closed, set the state to true.
Below is an example code:
class _HomePageState extends State<HomePage> {
bool isButtonActive = true;
showTimer(BuildContext context) async {
setState(() {
isButtonActive = false;
});
await Future.delayed(Duration(seconds: 2));
showDialog(
context: context,
builder: (BuildContext context) {
return Column(
children: const [
Text('qwerty'),
],
);
},
).then((value) {
setState(() {
isButtonActive = true;
});
});
}
#override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: const Text('총톤수'),
),
body: Center(
child: ElevatedButton(
onPressed: isButtonActive ? () => showTimer(context) : null,
child: const Text('총톤수'),
),
),
);
}
}

Flutter - Custom back button doesn't work

I have an alertdialog which is supposed to return a bool depending on the user's choice, the message gets removed, but the back button isn't working!
Future<bool?> showwarning(BuildContext context) async {
showDialog<bool>(
context: context,
builder: (context) => AlertDialog(
title: new Text("Alert!!"),
content: new Text("Return To Main Page?!"),
actions: [
TextButton(
child: new Text("Yes"),
onPressed: () {
Navigator.pop(context, true);
},
),
TextButton(
child: new Text("Nope"),
onPressed: () {
Navigator.pop(context, false);
},
)
],
));
}
Widget build(BuildContext context) {
// TODO: implement build
return WillPopScope(
onWillPop: () async {
final user_decision = await showwarning(context);
return user_decision ?? false;
},
Future<bool?> showwarning(BuildContext context) async {
return showDialog<bool>(
context: context,
builder: (context) => AlertDialog(
title: new Text("Alert!!"),
content: new Text("Return To Main Page?!"),
actions: [
TextButton(
child: new Text("Yes"),
onPressed: () {
Navigator.pop(context, true);
},
),
TextButton(
child: new Text("Nope"),
onPressed: () {
Navigator.pop(context, false);
},
)
],
));
}
Widget build(BuildContext context) {
// TODO: implement build
return WillPopScope(
onWillPop: () async {
final user_decision = await showwarning(context);
return user_decision ?? false;
},
The problem is that I wasn't returning anything from showwarning function , hence, it was always false!

close Simple Dialog in flutter when setState needs to called

I'm having a problem calling Navigator.of(context).pop() on my onPressed property in SimpleDialogOption widget. I need to set the state and dismiss the dialog. But calling setState is preventing my dialog to close. Without setState the dialog closes. Here is my dialog
WidgetsBinding.instance.addPostFrameCallback((_) {
showDialog(
builder: (BuildContext context) {
return SimpleDialog(
children: _children(suburbs),
backgroundColor: Colors.white,
title: Text('Pick your suburb'),
);
},
context: context);
});
and the method I use for the list of the Dialog:
List<Widget> _children(List<Suburb> suburbs) {
return suburbs
.map((suburb) => SimpleDialogOption(
onPressed: () {
print('#####################');
setState(() {
postcode = suburb.name;
});
Navigator.of(context).pop();
},
child: Text(suburb.name)))
.toList();
}
you can await until the return value comes from the navigator.pop,
and then call a setState
WidgetsBinding.instance.addPostFrameCallback((_) async {
postcode = await showDialog(
builder: (BuildContext context) {
return SimpleDialog(
children: _children(suburbs),
backgroundColor: Colors.white,
title: Text('Pick your suburb'),
);
},
context: context);
setState(() {
postcode;
});
});
List<Widget> _children(List<Suburb> suburbs) {
return suburbs
.map((suburb) => SimpleDialogOption(
onPressed: () {
print('#####################');
Navigator.of(context).pop(suburb.name);
},
child: Text(suburb.name)))
.toList();
}

Rebuild ListView after operation on List Flutter

I am trying to refresh the list of recipes after i delete one item and i have tried using SetState but I've got no result. I think the issue is that i get to this screen after making a GET call and that call is not executed again after deleting the item. What can I do?
void _pushRecipesByTypeScreen(String type) async {
var recipes = await entityApi.getRecipesByType(type);
Navigator.of(context).push(new MaterialPageRoute(builder: (context) {
return new Scaffold(
appBar: new AppBar(title: new Text(type)),
body: ListView.builder(
itemCount: recipes.length,
itemBuilder: (context, index) {
final item = recipes[index];
return _buildEntityItem2(item);
})
);
}));
}
Widget _buildEntityItem2(Entity entity) {
return ListTile(
title: Text(entity.name),
onTap: () => _pushDeleteScreen(entity),
);
}
void _pushDeleteScreen(Entity entity) {
showDialog(
context: context,
builder: (BuildContext context) {
return new AlertDialog(
title: new Text('Delete "${entity.name}"?'),
actions: <Widget>[
new FlatButton(
child: new Text('CANCEL'),
onPressed: () => Navigator.of(context).pop()),
new FlatButton(
child: new Text('CONFIRM'),
onPressed: () {
_removeEntityItem(entity);
setState(() {});
Navigator.of(context).pop();
})
]);
});
}
void _removeEntityItem(Entity entity) async {
if (await connectivity.checkConnectivity() != ConnectivityResult.none) {
bool success = await entityApi.deleteEntity(entity);
if (success) {
setState(() {});
}
}
}
The setstate should be used outside of dialog for it to reload your listview
void _pushRecipesByTypeScreen(String type) async {
var recipes = await entityApi.getRecipesByType(type);
Navigator.of(context).push(new MaterialPageRoute(builder: (context) {
return new Scaffold(
appBar: new AppBar(title: new Text(type)),
body: ListView.builder(
itemCount: recipes.length,
itemBuilder: (context, index) {
final item = recipes[index];
return _buildEntityItem2(item);
})
);
}));
}
Widget _buildEntityItem2(Entity entity) {
return ListTile(
title: Text(entity.name),
onTap: () async {
if(await _pushDeleteScreen(entity)) {
await _removeEntityItem(entity); //await until this completes
setState(() {});
}
}
);
}
Future<bool> _pushDeleteScreen(Entity entity) {
return showDialog(
context: context,
builder: (BuildContext context) {
return new AlertDialog(
title: new Text('Delete "${entity.name}"?'),
actions: <Widget>[
new FlatButton(
child: new Text('CANCEL'),
onPressed: () => Navigator.of(context).pop(false)),
new FlatButton(
child: new Text('CONFIRM'),
onPressed: () {
Navigator.of(context).pop(true);
})
]);
});
}
// make this return future
Future<void> _removeEntityItem(Entity entity) async {
if (await connectivity.checkConnectivity() != ConnectivityResult.none) {
bool success = await entityApi.deleteEntity(entity);
if (success) {
setState(() {});
}
}
}