Flutter display pop-up once based on value - flutter

I am trying to display a pop-up dialog like (+20) based on the value coming from server. I have a variable name ageRestriction which is getting the value from server. And I want to display pop-up based on the value of this variable. (Eg: If ageRestriction has the value of "18" the pop-up will be displayed only once then later on if the value will change to "20" the pop-up will be displayed once again, so these values will be stored somewhere and the pop-up will not be displayed if the same value comes again)
I have tried to do it with shared preferences unfortunately it did not work:
// initializing shared pref
#override
void initState() async{
WidgetsFlutterBinding.ensureInitialized();
SharedPreferences prefs = await SharedPreferences.getInstance();
widget.ageRestriction = await prefs.getString("ageRestriction");
await prefs.setString("ageRestriction", widget.ageRestriction);
}
// displaying pop-up dialog
widget.ageRestriction.toString() == widget.ageRestriction ? null :
AwesomeDialog(
popContext: false,
context: context,
dialogType: DialogType.WARNING,
animType: AnimType.TOPSLIDE,
title: "${widget.ageRestriction} Warning",
desc: "We only sell this product to persons who are ${widget.ageRestriction} years old. Age will be verified upon delivery.",
btnOkText: "Continue",
btnOkOnPress: () async{
widget.onPressed();
Navigator.of(context).pop();
},
btnCancelOnPress: () {
Navigator.of(context).pop();
},
btnCancelText: S.current.cancel,
btnOkColor: Theme.of(context).accentColor,
btnCancelColor: Color(0xFF084457).withOpacity(0.9),
).show();
ageRestrict();
}

Store widget.ageRestriction in a variable in the state of the widget and then check in didUpdateWidget whether the value changed and if it did show the popup

Simple Demo App to show age restriction :
Future<String> getAgeRestrictionFromServer() async {
// write your own logic
await Future.delayed(Duration(seconds: 2));
return "22";
}
enum RequestState { LOADING, SUCCESS, ERROR }
class MyHomePage extends StatefulWidget {
MyHomePage({Key key}) : super(key: key);
#override
_MyHomePageState createState() => _MyHomePageState();
}
class _MyHomePageState extends State<MyHomePage>{
// request status
RequestState requestState = RequestState.LOADING;
// disable multiple clickes
bool retryButtonEnabled = true;
#override
void initState() {
super.initState();
SchedulerBinding.instance.addPostFrameCallback((timeStamp) {
getData();
});
}
showAgeRestrictionDialog(String age) {
return showDialog(
context: context,
child: AlertDialog(
title: Text('Age policy changed!'),
content: Text('New Age: $age'),
),
);
}
void changeRequestState(RequestState newRequestState) {
if (mounted) {
setState(() {
requestState = newRequestState;
});
}
}
Future<void> getData() async {
changeRequestState(RequestState.LOADING);
try {
// get age from server
var newAgeRestriction = await getAgeRestrictionFromServer();
// get age stored locally
SharedPreferences sharedPreferences =
await SharedPreferences.getInstance();
var previousAgeRestriction =
sharedPreferences.getString('ageRestriction');
print('$previousAgeRestriction, $newAgeRestriction');
int.parse(newAgeRestriction);
if (mounted) {
// compare previous and new age
if (previousAgeRestriction != newAgeRestriction) {
// save new age
await sharedPreferences.setString(
'ageRestriction', newAgeRestriction);
// show dialog because age changed
showAgeRestrictionDialog(newAgeRestriction);
}
}
retryButtonEnabled = true;
changeRequestState(RequestState.SUCCESS);
} catch (e) {
print(e);
retryButtonEnabled = true;
changeRequestState(RequestState.ERROR);
}
}
#override
Widget build(BuildContext context) {
var child;
if (requestState == RequestState.LOADING) {
child = Center(
child: CircularProgressIndicator(),
);
} else if (requestState == RequestState.SUCCESS) {
child = Center(
child: Text('Got data from server!'),
);
} else {
child = Center(
child: FlatButton(
color: Colors.blue,
onPressed: retryButtonEnabled
? () {
setState(() {
retryButtonEnabled = false;
});
getData();
}
: null,
child: Text('Retry')),
);
}
return Scaffold(
appBar: AppBar(
title: Text('Demo App'),
),
body: child,
);
}
}
More info about state management here

Related

Do not use BuildContexts across async gaps - Flutter

I use SnackBar after, the await.
So, It's showing error like this:
Do not use BuildContexts across async gaps
I used if (!mounted) this line to remove the error. It removed the problem but, SnackBar is not showing. When complete the task
My code here:
Future removeMethod() async {
String res = await DatabaseMethods().idReject(widget.uid);
if (res == "success") {
if (!mounted) return;
showSnackBar(context, "Job done!");
} else {
if (!mounted) return;
showSnackBar(context, "Error!!");
}
}
showSnackBar is customWidget. code of it:
void showSnackBar(BuildContext context, String title) {
final snackBar = SnackBar(
content: CustomText(
text: title,
size: 16,
),
backgroundColor: darkblueColor,
);
ScaffoldMessenger.of(context).showSnackBar(snackBar);
}
What can I do about this?. I want to show snackbar after await.
This basic example works based on your snippets. Here a snackbar is called after the result of a future is returned (e.g. a database call). If this helps?
import 'dart:math';
import 'package:flutter/material.dart';
class SnackBarAfterFutureResult extends StatelessWidget {
const SnackBarAfterFutureResult({Key? key}) : super(key: key);
#override
Widget build(BuildContext context) {
return Scaffold(
body: Center(
child: OutlinedButton(
onPressed: () async {
await removeMethod()
.then((value) => showSnackBar(context, value));
},
child: const Text('Database call')),
),
);
}
}
void showSnackBar(BuildContext context, bool result) {
String text = "Job done!";
if (!result) {
text = "Error!!";
}
final snackBar = SnackBar(content: Text(text), backgroundColor: Colors.blue);
ScaffoldMessenger.of(context).showSnackBar(snackBar);
}
Future<bool> removeMethod() async {
//e.g. getting result from database call
bool isSuccess = false;
await Future.delayed(const Duration(seconds: 2), () {
isSuccess = Random().nextBool();
});
return isSuccess;
}

Can not update variable value inside a void method in flutter

I'm trying to update a variable value inside a void method in flutter, tried using StatefulBuilder but the value does not get changed.
Here is my code:
class HomeScreen extends StatefulWidget {
const HomeScreen({super.key});
#override
State<HomeScreen> createState() => _HomeScreenState();
}
class _HomeScreenState extends State<HomeScreen> {
final user = FirebaseAuth.instance.currentUser;
String type = "";
void checkUser() async {
await FirebaseFirestore.instance
.collection('users')
.doc(user!.uid)
.get()
.then(
(DocumentSnapshot documentSnapshot) {
if (documentSnapshot.exists) {
var data = documentSnapshot.data();
var res = data as Map<String, dynamic>;
if (res["type"] == "Salarié") {
print('Salarié');
} else if (res["type"] == "Auto entrepreneur") {
print('Auto entrrepreneu');
} else {
showDialog<String>(
context: context,
builder: (_) => StatefulBuilder(
builder: (modalContext, modalSetState) => AlertDialog(
title: const Text('Choissisez votre type'),
content: const Text('Choisir votre type de user'),
actions: <Widget>[
TextButton(
onPressed: () {
setState(() {
type = "Salarié";
});
Navigator.pop(context, 'Cancel');
},
child: const Text('Salarié'),
),
TextButton(
onPressed: () {
setState(() {
type = "Auto entrepreneur";
});
Navigator.pop(context, 'OK');
},
child: const Text('Auto-entrepreneur'),
),
],
),
));
return FirebaseFirestore.instance
.collection("users")
.doc(user!.uid)
.update({
"type": type,
});
}
} else {}
},
);
}
#override
void initState() {
super.initState();
checkUser();
}
#override
Widget build(BuildContext context) {
return Scaffold(...);
It's all inside a StatefulWidget though, I'm not sure whether this is the correct way to do this because the value does not get changed! I appreciate your help.
edit: I'm calling this method inside the initState()
showDialog is not a synchronous operation and the code basically sends the dialog and immediately continues. to fix that, I had to add async and await, and it worked like a charm.

How to move classes and functions to a separate file in Flutter/Dart?

Good day! I am new to Flutter/Dart. And the more I experiment, the bigger my main file gets. Obviously, I need a separate file in which I will store all the classes and functions that I will refer to in the future.
I have a separate screen with what I need. Here is its code:
//Internet route
class InternetRoute extends StatefulWidget {
const InternetRoute({Key? key}) : super(key: key);
#override
State<InternetRoute> createState() => _InternetRouteState();
}
class _InternetRouteState extends State<InternetRoute> {
bool ActiveConnection = false;
String T = "";
Future CheckUserConnection() async {
try {
final result = await InternetAddress.lookup('example.com');
if (result.isNotEmpty && result[0].rawAddress.isNotEmpty) {
setState(() {
ActiveConnection = true;
T = "Turn off the data and repress again";
});
}
} on SocketException catch (_) {
setState(() {
ActiveConnection = false;
T = "Turn On the data and repress again";
showInternetDialog(context);
});
}
}
#override
void initState() {
CheckUserConnection();
super.initState();
}
#override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: const Text("GeeksforGeeks"),
),
body: Column(
children: [
Text("Active Connection? $ActiveConnection"),
const Divider(),
Text(T),
OutlinedButton(
onPressed: () {
CheckUserConnection();
},
child: const Text("Check"))
],
),
);
}
}
//Alert Dialog about Internet connection
showInternetDialog(BuildContext context) {
// set up the button
Widget okButton = Center(
child: TextButton(
child: Text("OK"),
onPressed: () {
Navigator.of(context).pop(); // dismiss dialog
},
),
);
// set up the AlertDialog
AlertDialog alert = AlertDialog(
// title: Text("My title"),
content: Text("Internet connection required"),
actions: [
okButton,
],
);
// show the dialog
showDialog(
context: context,
builder: (BuildContext context) {
return alert;
},
);
I want to create a my classes.dart file that will gradually populate with the most commonly used things. In particular, I need class _InternetRouteState and showInternetDialog.
How to transfer them to a new file? I completely copied the code of that screen. Is it correct? Would that be enough to then refer to them in main.dart (after import)? Will all their variables be visible to my screens as well?
Edit 1. I don't know how I can move CheckUserConnection to my file. I mean I took the piece of code I needed and wrapped it in the CheckUserConnection class (in my separate file), but it doesn't work. What am I doing wrong?
class CheckUserConnection {
bool ActiveConnection = false;
String T = "";
Future CheckUserConnection() async {
try {
final result = await InternetAddress.lookup('example.com');
if (result.isNotEmpty && result[0].rawAddress.isNotEmpty) {
setState(() {
ActiveConnection = true;
T = "Turn off the data and repress again";
});
}
} on SocketException catch (_) {
setState(() {
ActiveConnection = false;
T = "Turn On the data and repress again";
// showInternetDialog(context); //temporary
});
}
}
}
The Problems tab shows the following errors:
Constructors can't have a return type.
The modifier 'async' can't be applied to the body of a constructor.
The await expression can only be used in an async function.
The method 'setState' isn't defined for the type 'CheckUserConnection'.
The method 'setState' isn't defined for the type 'CheckUserConnection'.
Create a new dart file. Name it internet_dialog_handler.dart. Add this to the file
class InternetDialogHandler{
//Alert Dialog about Internet connection
showInternetDialog(BuildContext context) {
// set up the button
Widget okButton = Center(
child: TextButton(
child: Text("OK"),
onPressed: () {
Navigator.of(context).pop(); // dismiss dialog
},
),
);
// set up the AlertDialog
AlertDialog alert = AlertDialog(
// title: Text("My title"),
content: Text("Internet connection required"),
actions: [
okButton,
],
);
// show the dialog
showDialog(
context: context,
builder: (BuildContext context) {
return alert;
},
);
}
In internetRoute use this
//Internet route
class InternetRoute extends StatefulWidget {
const InternetRoute({Key? key}) : super(key: key);
#override
State<InternetRoute> createState() => _InternetRouteState();
}
class _InternetRouteState extends State<InternetRoute> {
bool ActiveConnection = false;
String T = "";
InternetDialogHandler _internetDialogHandler = InternetDialogHandler();
Future CheckUserConnection() async {
try {
final result = await InternetAddress.lookup('example.com');
if (result.isNotEmpty && result[0].rawAddress.isNotEmpty) {
setState(() {
ActiveConnection = true;
T = "Turn off the data and repress again";
});
}
} on SocketException catch (_) {
setState(() {
ActiveConnection = false;
T = "Turn On the data and repress again";
//Use the variable here to access the method in that class
_internetDialogHandler.showInternetDialog(context);
});
}
}
#override
void initState() {
CheckUserConnection();
super.initState();
}
#override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: const Text("GeeksforGeeks"),
),
body: Column(
children: [
Text("Active Connection? $ActiveConnection"),
const Divider(),
Text(T),
OutlinedButton(
onPressed: () {
CheckUserConnection();
},
child: const Text("Check"))
],
),
);
}
}
EDIT
class CheckUserConnection {
Future checkInternetAvailability() async {
try {
final result = await InternetAddress.lookup('example.com');
if (result.isNotEmpty && result[0].rawAddress.isNotEmpty) {
return true;
}
} on SocketException catch (_) {
return false;
}
}
}
Use a different name for the method. Same name is used to defined the constructor of the class. Also make it work independent. Just return a true or false. Now to use this define a variable of type checkUserConnection
CheckUserConnection _checkUserConnection = CheckUserConnection();
bool _internetAvailable = await _checkUserConnection.checkInternetAvailability();
if(_internetAvailable)
{
//do something here;
} else{
//handle no internet here
}

how to change the state inside FutureBuilder

i just need when the user open the screen the notification icon button change when he click, it's value is coming from shared preferences. the problem is the icon is never changed!
the initState code:
#override
void initState() {
super.initState();
_isActiveNotification = _notificationGetState();
}
_notificationGetState function is:
//getting notification on/off
Future<bool> _notificationGetState() async {
final SharedPreferences _prefs = await SharedPreferences.getInstance();
return _prefs.getBool('notification') ?? true;
}
_isActiveNotification variable is:
late Future<bool> _isActiveNotification;
the class of the notification icon button is:
class _NoificationActivationButton extends StatefulWidget {
_NoificationActivationButton();
#override
_NoificationActivationButtonState createState() =>
_NoificationActivationButtonState();
}
class _NoificationActivationButtonState
extends State<_NoificationActivationButton> {
#override
Widget build(BuildContext context) {
return FutureBuilder<bool>(
//function haveing the return value
future: _isActiveNotification,
builder: (context, snapshot) {
bool data = snapshot.data!;
return IconButton(
icon: Icon(
data
? Icons.notifications_active_outlined
: Icons.notifications_off_outlined,
color: Colors.white,
size: 40,
),
onPressed: () {
setState(() {
data = !data;
});
},
);
});
}
just call setstate
onPressed: () {
data = !data;
// just call setstate((){});
},
Make data a global state.
NOTE: I'm only assuming that you will only call _notificationGetState once (in initState).
Sample...
class _NoificationActivationButtonState
extends State<_NoificationActivationButton> {
final bool _isOtherVersion = true;
late Future<bool> _isActiveNotification;
bool? _data;
#override
void initState() {
super.initState();
_isActiveNotification = _notificationGetState();
}
//getting notification on/off
Future<bool> _notificationGetState() async {
final SharedPreferences _prefs = await SharedPreferences.getInstance();
return _isOtherVersion
? _prefs.getBool('notification') ?? true
: _data = _prefs.getBool('notification') ?? true;
}
#override
Widget build(BuildContext context) {
return FutureBuilder<bool>(
//function haveing the return value
future: _isActiveNotification,
builder: (context, snapshot) {
if (snapshot.connectionState != ConnectionState.done) {
return const CircularProgressIndicator();
}
if (_isOtherVersion && _data == null) {
_data = snapshot.data;
}
return IconButton(
icon: Icon(
_data!
? Icons.notifications_active_outlined
: Icons.notifications_off_outlined,
color: Colors.white,
size: 40,
),
onPressed: () => setState(() => _data = !_data!),
);
},
);
}
}

Flutter, How to pass value from one function to another function on a different page

I a trying to pass a Value from when a button is clicked, What I want in the code is to pass the value from the Button Widget to another Page's Variable named pdfNo. Here's my code:
FlatButton(
padding: EdgeInsets.fromLTRB(2, 5, 5, 5),
child: ListTile(
leading: Icon(Icons.book, color: Color(0xFFEB3549)),
title: Text('Book5'),
),
onPressed: () {
Navigator.push(
context,
MaterialPageRoute(
builder: (context) => PDFPage() ));
print('Pressed 6');
},
),`
This is the Button on the First Page , On Pressed I would like to pass a Value to a function which is child, so I have created a variable , but cant figure out how to go about this next:
var pdfNo = 2;
bool _isLoading = true;
PDFDocument document;
#override
void initState() {
super.initState();
changePDF(pdfNo);
loadDocument();
}
changePDF(value) async {
setState(() => _isLoading = true);
if (value == 1) {
document = await PDFDocument.fromAsset('assets/sample2.pdf');
} else if (value == 2) {
document = await PDFDocument.fromURL(
"http://conorlastowka.com/book/CitationNeededBook-Sample.pdf");
} else {
document = await PDFDocument.fromAsset('assets/sample.pdf');
}
setState(() => _isLoading = false);
}
So, I would like to pass a int from the button on page 1 Staleful Class to a Stateful Class on second page to changePDF(here).
Please help me out. PS New to Flutter,Dart
You can create a to arguments...
Something like this
class LoginWidgetArguments {
final String username;
LoginWidgetArguments(
{this.username});
}
class LoginWidget extends StatefulWidget {
#override
State<StatefulWidget> createState() => _LoginWidgetState();
}
class _LoginWidgetState extends State<LoginWidget>{
LoginWidgetArguments args;
#override
void initState() {
//Get context
// I do this because sometimes it doesn't get the context without the FutureDelay... This workaround I used with the first flutter versions, maybe now its not needed
Future.delayed(Duration.zero, () {
args = ModalRoute.of(context).settings.arguments;
if (args != null) {
print(args.username)
}
});
}
....
}
And to navigate
Navigator.pushNamed(context, LoginPage.routeName,
arguments: LoginWidgetArguments(
user: "user#yay.com");
Edit
Maybe it could be simple...
FlatButton(
padding: EdgeInsets.fromLTRB(2, 5, 5, 5),
child: ListTile(
leading: Icon(Icons.book, color: Color(0xFFEB3549)),
title: Text('Book5'),
),
onPressed: () {
Navigator.push(
context,
MaterialPageRoute(
builder: (context) => PDFPage(pageNumber: 6) ));
print('Pressed 6');
},
)
class PDFPage extends StatefulWidget {
final int pageNumber;
PDFPage({this.pageNumber});
#override
_PDFPageStage createState() => _PDFPageStage();
}
class _PDFPageStage extends State<PDFPage> {
...
#override
void initState() {
super.initState();
changePDF(widget.pageNumber);
loadDocument();
}
...
}
I'm not sure if I understand the problem correctly, but I think you can pass the number to the constructor of the StatefulWidget. I changed the PDFDocument to a String for simplicity.
Some button press on first page:
onPressed: () {
Navigator.push(
context,
MaterialPageRoute(
builder: (context) => PDFPage(pdfNo: 4),
),
);
},
The second page:
class PDFPage extends StatefulWidget {
final int pdfNo;
const PDFPage({Key key, this.pdfNo}) : super(key: key);
#override
_PDFPageState createState() => _PDFPageState();
}
class _PDFPageState extends State<PDFPage> {
bool _isLoading = false;
String _document;
void changePDF(int value) async {
setState(() => _isLoading = true);
if (value == 1) {
_document = await Future.delayed(Duration(seconds: 1), () => 'Value 1');
} else if (value == 2) {
_document = await Future.delayed(Duration(seconds: 1), () => 'Value 2');
} else {
_document = await Future.delayed(Duration(seconds: 1), () => 'Other value');
}
setState(() => _isLoading = false);
}
#override
void initState() {
super.initState();
changePDF(widget.pdfNo);
}
#override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(title: Text('Test'),),
body: Center(
child: _isLoading ? CircularProgressIndicator() : Text(_document ?? '(null)'),
),
);
}
}