Why using <Null> after showDialog? - flutter

Here I'm rendering an AlertDialog inside showDialog to display when error thrown from provider file. But that didn't work first time( Seems like stuck in catchError block, didn't execute future of catchError), Then I told to add <Null> after showDialog. That worked But How, What is changed, what it means?
Here is the code
if (_editedProduct.id == null) {
Provider.of<Products>(context, listen: false)
.addProduct(_editedProduct)
.catchError((error) {
return showDialog<Null>( //Here is that Null I didn't understand
context: context,
builder: (ctx) {
return AlertDialog(
title: Text('ERROR'),
content: Text('Error Occured'),
actions: [
TextButton(
onPressed: () {
Navigator.of(context).pop();
},
child: Text('I GOT IT'),
)
],
);
});
}).then((_) {
print('THEN');
setState(() {
isLoading = false;
});
Navigator.of(context).pop();
});
} else {
Provider.of<Products>(context, listen: false)
.updateProduct(_editedProduct.id, _editedProduct);
Navigator.of(context).pop();
}
}
Nb: isLoading true shows a circluarProgressIndicator()

From the Official Docs
Returns a Future that resolves to the value (if any) that was passed to Navigator.pop when the dialog was closed.
The place where you sent Null indicates the type of data the Future will return.
By using Null you are indicating that the Future won't return any data, this tell your program not to wait for the possible value that might be returned.
Suppose in your dialog user has to pick 2 numbers and you want the picked number to be returned to the place where you called the showDialog() function then you'll use int instead of Null.
Something like
showDialog<int>(...)

Related

Null check operator used on a null value when calling a function from another class

I have a class called DataCollectionPage where I have the following function in the initState function:
void establishConnection (){
BluetoothConnection.toAddress(widget.server!.address).then((connection) {
Utils.showSnackBar2("Successfully connected to your device");
this.connection = connection;
setState(() {
isConnecting = false;
isDisconnecting = false;
});
this.connection!.input!.listen(_onDataReceived).onDone(() {
if (isDisconnecting) {
Utils.showSnackBar2("Device Disconnected!!");
} else {
Utils.showSnackBar2("Device Disconnected!!");
}
if (mounted) {
setState(() {});
}
});
}).catchError((error) {Utils.showSnackBar("Connection failed, please try again later");
Navigator.push(
context,
MaterialPageRoute(builder: (context) => const BluetoothConnectionTask()),
);
showDialog(
context: context,
builder: (BuildContext context) {
return AlertDialog(
title: const Text('Error occurred while connecting'),
content: const Text(
"Connection failed. Please Try connecting to your device again"),
actions: <Widget>[
TextButton(
child: const Text("Close",style: TextStyle(color: Colors.black),),
onPressed: () {
Navigator.of(context).pop();
},
),
],
);
},
);
});
}
Then, in the same class, I have another function (startDataCollection) that looks like this:
void startDataCollection() async {
startFlag = startFlag.trim();
try {
List<int> list = startFlag.codeUnits;
Uint8List bytes = Uint8List.fromList(list);
connection!.output.add(bytes);
await connection!.output.allSent;
} catch (e) {
print(e);
}
}
At this point, there is no issue. However when I tried calling startDataCollection function from another screen (DataCollectionTimer Class), I got an error that
Null check operator used on a null value
which was referring to "connection" from the startDataCollection function.
Basically, I established a bluetooth connection with some hardware when I called the function establishConnection for the first time, and when I called it from the class DataCollectionTimer, it was still connected, but for some reason the connection var was empty when I called it from the DataCollectionPage class.
How can I use the function startDataCollection from another screen correctly?
If you need any more elaboration please let me know and thank you in advance.
Update:
I simple solved the problem by changing Bluetooth connection to global
Old:
BluetoothConnection? connection;
New:
static BluetoothConnection? connection;
Please ensure:
You're using the same connection object in all the pages.
you're awaiting BluetoothConnection.toAddress(widget.server!.address).then before doing anything with the connection value
The connection received from BluetoothConnection.toAddress(widget.server!.address).then((connection) is not null

Returning showDialog() as a Future does not trigger then() callback

I'm developing a flutter application and I am using showDialog() to show some errors that I've caught in catchError() method that I'm using for catching a potential exception for an api request.
i am returning showDialog() in catchError() as a Future result and im using then() after catchError() to know when showDialog() is poped to run some code as failure result of my request.
based on an online course that im watcing, showDialog() is resolved when we pop it from screens stack and i am using Navigator.of(context).pop(); to do that. but it's not working and it's not runing then() method.
I will be so grateful if anyone knows what's wrong.
this is my code and I hope it's clear enough.
Provider.of<Products>(context, listen: false)
.addProduct(Product(
id: edittedProduct.id,
title: edittedProduct.title,
description: edittedProduct.description,
price: edittedProduct.price,
imageUrl: edittedProduct.imageUrl,
isFavorite: edittedProduct.isFavorite))
.catchError((error) {
print("in second catchError method. error is: ${error.toString()}");
return showDialog<void>(
context: context,
builder: (ctx) => AlertDialog(
title: Text("An error occured!"),
content: Text("adding failed due to some issues."),
actions: [
TextButton(
onPressed: () {
Navigator.of(ctx).pop();
},
child: Text("Okay"))
],
),
);
}).then((_) {
print("in second then method");
setState(() {
_isLoading = false;
});
Navigator.of(context).pop();
});

Snackbar won't show when included in .then

So I have reusable snackbar on my code and I wanted to show my snackbar when user already pop from certain page. I tried to put my snackbar inside .then but It wont show anything. Any idea how to fix this ?
Here's my code:
void openChangeClientPage(BuildContext context, user){
Navigator.push(
context,
MaterialPageRoute(
builder: (context) => AddClientPage(
clientId: user!.uid,
clientNickname: Provider.of<UserDataProvider>(context, listen: false).userNickname,
clientProfileLink: Provider.of<UserDataProvider>(context, listen: false).userProfilePicture ?? '',
ticketData: ticketData,
ticketId: ticketId,
source: 'ticketDetail',
),
),
).then((value) {
if (value) {
ReusableSnackBar.buildSnackBar(context, 'New Client has been assigned');
Navigator.pop(context);
} else {
changeInvolvedState(true);
}
});
}
I think you can wrap the ReusableSnackBar into the Build method because snackbar tries to find the BuildContext closet but it is now in the Then method. You can read more in this
I think the code will be this:
void openChangeClientPage(BuildContext context, user){
Navigator.push(
context,
MaterialPageRoute(
builder: (context) => AddClientPage(
clientId: user!.uid,
clientNickname: Provider.of<UserDataProvider>(context, listen: false).userNickname,
clientProfileLink: Provider.of<UserDataProvider>(context, listen: false).userProfilePicture ?? '',
ticketData: ticketData,
ticketId: ticketId,
source: 'ticketDetail',
),
),
).then((value) {
if (value) {
Builder(builder: (context)=>ReusableSnackBar.buildSnackBar(context, 'New Client has been assigned'));
Navigator.pop(context);
} else {
changeInvolvedState(true);
}
});}
The issue is that you are calling Navigator.pop immediately after calling ReusableSnackBar. This will cause the SnackBar to pop too because you can assume the snackbar is like a pop-up/dialog and is immediately dismissed.
You can call Navigator.pop before displaying the snackbar.
You can also check out this answer if you want to show snackbar without context.
Firstly, make sure condition ("value" variable) is true.
Remove this line in your code:
Navigator.pop(context);
This code will close current screen, not dismiss snack bar.
If you want to dismiss snack bar, use:
ScaffoldMessenger.of(context).hideCurrentSnackBar();

Flutter exception: 'Context is not a subtype of BuildContext' error using Navigator

I am working on a flutter app and I am running into the following error: "The argument type 'Context' can't be assigned to the parameter type 'BuildContext'." However, when I try to pass in context to my functions as it says to do on the internet, I am unable to get it to work. Can someone please help me with this? The app is a camera app, and I know the video is successfully being recorded. here is the code:
This is the stop button that is pressed.
IconButton(
icon: const Icon(Icons.stop),
color: Colors.red,
onPressed: () {controller != null &&
controller.value.isInitialized &&
controller.value.isRecordingVideo
? onStopButtonPressed. //this is the function that is being called
: null;},
) //icons.stop
This is where the app isn't working with my Navigator.push call. It works fine when I take it out.
void onStopButtonPressed() {
stopVideoRecording().then((_) {
if (mounted) setState(() {});
print('Video recorded to: $videoPath');
print('Navigator is hit');
Navigator.push(
context,
MaterialPageRoute(
builder: (context) => PreviewImageScreen(videoPath: videoPath),
), //MaterialpageRoute
); //Navigator
});
}
And here is my stopVideoRecording function
Future<void> stopVideoRecording() async {
if (!controller.value.isRecordingVideo) {
return null;
}
try {
await controller.stopVideoRecording();
} on CameraException catch (e) {
_showCameraException(e);
return null;
}
//await _startVideoPlayer();
}
Thanks!
change it to
Navigator.push(
this.context, //add this so it uses the context of the class
MaterialPageRoute(
builder: (context) => PreviewImageScreen(videoPath: videoPath),
), //MaterialpageRoute
); //Navigator
Maybe you're importing a library or something that has a class named context and is interfering with the name context

Need to execute task after setState finish

I need to execute a task, after setState() method complete its whole job. I will describe my problem at below with some code.
I have a login screen and it has a widgets as below:
...
child: TextField(
errorText: getErrorStringOfEmail(),
...
),
...
child: MaterialButton(
onPressed: () => onClickLoginButton(),
...
),
"getErrorStringOfEmail" method is as below: (This method is called when the Textfield is updated by calling "setState()".)
String getErrorStringOfEmail(
if(loginEmailTextEditingController.text.toString() == "a") {
isValidLogin = false;
return 'Wrong password or email';
}
isValidLogin = true;
return null;
}
"onClickLoginButton" is as below:
void onClickLoginButton() {
setState(() {
//in here a boolean is changed to update Textfield.
});
if (isValidLogin) {
Navigator.pushReplacement (
context,
MaterialPageRoute(builder: (context) => HomeWidget()),
);
}
}
As you can see, "isValidLogin" boolean is assigned when Textfield widget is updated. However, getErrorStringOfEmail method is called after onClickLoginButton. I need to execute the following part,
if (isValidLogin) {
Navigator.pushReplacement (
context,
MaterialPageRoute(builder: (context) => HomeWidget()),
);
}
after getErrorStringOfEmail method is called. To achieve this, i need to call this part after setState update widgets.
How can i do this?