Related
Hello friends i am working on speech recognition in flutter i made custom alert dialogue like native dialog when user click on button alert dialog appear and when user speak it show text on alert dialog my problem is that i want when user finishing his speech alert dialogue will automatically close please let me know how i can perform this task?
stt.SpeechToText speechToText = stt.SpeechToText();
bool islistening = false;
late String text = 'Example:Genesis chapter 1 verse 5';
bool complete=false;
final GlobalKey _dialogKey = GlobalKey();
ValueNotifier<bool> buttonClickedTimes =ValueNotifier(false);
_showDialog() async {
showDialog(
context:context,
barrierDismissible: true,
builder: (BuildContext context) {
return StatefulBuilder(
key: _dialogKey,
builder: (context, setState) {
return Container(
child: Dialog(
child: Padding(
padding: const EdgeInsets.all(8),
child: Column(
mainAxisSize: MainAxisSize.min,
children: [
const AvatarGlow(
glowColor: Colors.blue,
endRadius: 80,
duration: Duration( milliseconds: 2500),
repeat: true,
showTwoGlows: true,
repeatPauseDuration: Duration( milliseconds: 150),
child: Material(
elevation: 5,
shape: CircleBorder(),
child: CircleAvatar(
backgroundColor: Colors.white,
child: Icon(Icons.mic, color: Colors.blue, size: 40,),
radius: 40,
),
),
),
Text(text),
const SizedBox(height: 10),
TextButton(
onPressed: () => Navigator.pop(context, false), // passing false
child: const Text('Cancel Voice'),
),
],
),
),
),
);
},
);
},
);
}
final GlobalKey<ScaffoldState> _scaffoldKey = GlobalKey();
void _listen() async {
if (!islistening) {
bool available = await speechToText.initialize(
onStatus: (val) => print('onStatus: $val'),
onError: (val) => print('onError: $val'),
);
if (available) {
setState(() {
islistening = true;
});
speechToText.listen(
onResult: (result) =>
setState(() {
text = result.recognizedWords;
if (_dialogKey.currentState != null && _dialogKey.currentState!.mounted && speechToText.isListening) {
_dialogKey.currentState!.setState(() {
text =result.recognizedWords;
});}
else{
if(text.contains('Genesis')){
setState(() {
String bigSentence =text;
var voice= bigSentence.split(" ");
var bookname=voice[0];
var booknumber=1;
int chapternumber=int.parse(voice[2]);
int versenumber=int.parse(voice[4]);
if(_regExp.hasMatch(chapternumber.toString())&&_regExp.hasMatch(versenumber.toString())){
Navigator.push(
context,
MaterialPageRoute(builder: (context) => Allverse(bookname, booknumber,chapternumber, versenumber)),
);
}else{
Fluttertoast.showToast(
msg: "check chapter number or versenumber",
toastLength: Toast.LENGTH_SHORT,
gravity: ToastGravity.CENTER,
backgroundColor: Colors.red,
textColor: Colors.white,
fontSize: 16.0
);
}
});
}
}
})
);
}
} else
{
setState(() => islistening = false);
speechToText.stop();
}
}
I'm building a form that signs up the user on Firebase, and where the ElevatedButton either displays "CREATE YOUR ACCOUNT" or a CircularProgressIndicator, based on the state of isLoading. Yet, the button's child doesn't change based on isLoading.
The states does change -- I'm printing out isLoading in the middle of my onPressed methode, and I do see it as true.
Why isn't the button changing its child?
import 'package:flutter/material.dart';
import 'package:tedy/components/custom_text_form_field.dart';
import 'package:tedy/components/custom_wrapper.dart';
class EmployerOnboardingOneView extends StatefulWidget {
const EmployerOnboardingOneView({Key? key}) : super(key: key);
#override
State<EmployerOnboardingOneView> createState() =>
_EmployerOnboardingOneViewState();
}
class _EmployerOnboardingOneViewState extends State<EmployerOnboardingOneView> {
var firstNameController = TextEditingController();
var lastNameController = TextEditingController();
var emailController = TextEditingController();
var passwordController = TextEditingController();
var passwordConfirmController = TextEditingController();
bool isLoading = false;
#override
Widget build(BuildContext context) {
var screenWidth = MediaQuery.of(context).size.width;
return Scaffold(
appBar: AppBar(
backgroundColor: Colors.white,
centerTitle: true,
title: SizedBox(
height: 50,
child: Image.asset(
'assets/images/tedy_logo_color.png',
filterQuality: FilterQuality.high,
),
),
),
body: CustomWrapper(
maxWidth: 400,
children: [
Text(
'Create a Tedy account for your company',
style: Theme.of(context).textTheme.headline3,
textAlign: TextAlign.center,
),
const SizedBox(
height: 20,
),
CustomTextFormField(
controller: firstNameController, label: 'First Name'),
const SizedBox(
height: 20,
),
CustomTextFormField(
controller: lastNameController, label: 'Last Name'),
const SizedBox(
height: 20,
),
CustomTextFormField(controller: emailController, label: 'Email'),
const SizedBox(
height: 20,
),
CustomTextFormField(
controller: passwordController,
isPassword: true,
label: 'Password'),
const SizedBox(
height: 20,
),
CustomTextFormField(
controller: passwordConfirmController,
isPassword: true,
label: 'Confirm your password'),
const SizedBox(
height: 20,
),
SizedBox(
width: double.infinity,
height: 50,
child: ElevatedButton(
style: ElevatedButton.styleFrom(
primary: Theme.of(context).primaryColor),
onPressed: () async {
setState(() {
isLoading = true;
});
if (passwordController.text != passwordConfirmController.text) {
ScaffoldMessenger.of(context).showSnackBar(
const SnackBar(
content: Text('Passwords must match'),
backgroundColor: Colors.red,
),
);
} else {
try {
FirebaseAuth.instance
.createUserWithEmailAndPassword(
email: emailController.text,
password: passwordController.text)
.then((value) {
Navigator.pushNamedAndRemoveUntil(
context, '/', (route) => false);
});
print(isLoading);
} on FirebaseAuthException catch (e) {
ScaffoldMessenger.of(context).showSnackBar(
SnackBar(
content: Text(e.message != null
? e.message as String
: 'There was an error.'),
backgroundColor: Colors.red,
),
);
}
}
setState(() {
isLoading = false;
});
},
child: isLoading
? const SizedBox(
width: 30,
height: 30,
child: CircularProgressIndicator(
color: Colors.white,
),
)
: const Text('CREATE YOUR ACCOUNT'),
),
)
],
),
);
}
}
You are using then for the future. so the last isLoading = false; executed when you pressed the button. You can change state inside .then instead of end. Also you can add inside FirebaseAuthException. make sure to remove last isLoading = false;
FirebaseAuth.instance
.createUserWithEmailAndPassword(
email: emailController.text,
password: passwordController.text)
.then((value) {
setState(() {
isLoading = false;
});
Navigator.pushNamedAndRemoveUntil(
context, '/', (route) => false);
});
createUserWithEmailAndPassword is async, so you have to await it
await FirebaseAuth.instance
.createUserWithEmailAndPassword(
email: emailController.text,
password: passwordController.text);
Navigator.pushNamedAndRemoveUntil(
context, '/', (route) => false);
Your code set isLoading to true, does the password logic and calls this method. But since you don't await it here it just skips it and calls the setState with isLoading = false.
I integrated Agora in Flutter Application with the help of agora_rtc_engine 4.0.7, but upon making a video call for the very first time the local camera view is not working. When we disconnect the call and call back again then everything works fine, the local camera view started working.
Again when we kill the application from Phone memory and rerun it then also the same problem occurs for the first time.
VideoCallingScreen.dart file
class VideoCallingScreen extends StatefulWidget {
String doctorId;
String bookingId;
VideoCallingScreen(this.doctorId, this.bookingId);
#override
_VideoCallingScreenState createState() => _VideoCallingScreenState();
}
class _VideoCallingScreenState extends State<VideoCallingScreen> {
int? _remoteUid;
RtcEngine? _engine;
bool isJoined = false,
switchCamera = true,
switchRender = true,
muteAudio = false,
muteVideo = false;
bool remoteUserMicMute = false, remoteUserVideoMute = false;
bool resizeVideo = false;
ServerHandler _serverHandler = ServerHandler();
String? token;
String? channelName;
String? channelId;
late user.Details userDetails;
final GlobalKey<ScaffoldState> _scaffoldKey = GlobalKey<ScaffoldState>();
#override
void initState() {
super.initState();
getAgoraToken().then((value) {
if (value) {
initForAgora();
} else {
_showMyDialog();
}
});
// initForAgora();
}
Future<void> _showMyDialog() async {
return showDialog<void>(
context: context,
barrierDismissible: false, // user must tap button!
builder: (BuildContext context) {
return AlertDialog(
title: const Text('No Channel Found'),
content: SingleChildScrollView(
child: ListBody(
children: const <Widget>[
Text('No video channel has been created by the Doctor'),
],
),
),
actions: <Widget>[
TextButton(
child: const Text('Ok'),
onPressed: () async {
// int count = 0;
Navigator.pop(context);
Navigator.pop(_scaffoldKey.currentContext!);
},
),
],
);
},
);
}
Future<bool> getAgoraToken() async {
await Screen.keepOn(true);
var tokenBody = await _serverHandler.joinAgoraChannel(
widget.doctorId, widget.bookingId);
print('token Body from videoPage' + tokenBody.toString());
if (tokenBody['success'] == 1) {
setState(() {
token = tokenBody['channel']['access_token_patient'];
channelName = tokenBody['channel']['channel_name'];
print('**********Token Set********' + token!);
channelId = tokenBody['channel']['id'].toString();
});
return true;
} else {
ScaffoldMessenger.of(context).showSnackBar(SnackBar(
content: Text(
"Unable to get Video Call Token, Please check your internet Connection and try again."),
));
return false;
}
}
Future<void> initForAgora() async {
// retrieve permissions
await [Permission.microphone, Permission.camera].request();
userDetails = await _serverHandler.getUserProfile();
//create the engine
_engine = await RtcEngine.create(appId);
await _engine?.enableVideo();
_engine?.setEventHandler(
RtcEngineEventHandler(
joinChannelSuccess: (String channel, int uid, int elapsed) async {
print("local user $uid joined");
// await _engine!.enableVideo();
},
userJoined: (int uid, int elapsed) {
print("remote user $uid joined");
setState(() {
_remoteUid = uid;
});
},
tokenPrivilegeWillExpire: (token) async {
await getAgoraToken();
await _engine?.renewToken(token);
},
userOffline: (int uid, UserOfflineReason reason) {
print("remote user $uid left channel");
// _engine!.enableVideo();
setState(() {
_remoteUid = null;
});
_userLeftTheCall();
},
userMuteVideo: (int uid, bool isMute) {
print('Audio Mutted');
setState(() {
remoteUserVideoMute = isMute;
});
},
userMuteAudio: (int uid, bool isMute) {
print('Audio Mutted');
setState(() {
remoteUserMicMute = isMute;
});
},
),
);
await getAgoraToken();
await _engine?.joinChannel(token, channelName!, null, userDetails.id!);
}
// Create UI with local view and remote view
#override
Widget build(BuildContext context) {
return WillPopScope(
onWillPop: _onWillPop,
child: Scaffold(
key: _scaffoldKey,
body: SafeArea(
child: Stack(
children: [
Center(
child:
resizeVideo ? _renderLocalPreview() : _renderRemoteVideo(),
),
if (remoteUserVideoMute)
BackdropFilter(
filter: ImageFilter.blur(
sigmaX: 10,
sigmaY: 10,
),
child: Padding(
padding: const EdgeInsets.only(bottom: 32),
child: Center(
child: Text(
'Doctor video Paused',
style: TextStyle(
color: Colors.white,
fontSize: 16,
fontWeight: FontWeight.bold),
textAlign: TextAlign.center,
),
),
),
),
if (remoteUserMicMute)
Padding(
padding: const EdgeInsets.only(top: 32),
child: Center(
child: Text(
'Doctor Mic Muted',
style: TextStyle(
color: Colors.white,
fontSize: 16,
fontWeight: FontWeight.bold),
textAlign: TextAlign.center,
),
),
),
Positioned(
top: 8,
left: 8,
child: InkWell(
//Uncomment it to unable resize
// onTap: () {
// setState(() {
// resizeVideo = !resizeVideo;
// });
// },
child: Container(
width: 100,
height: 120,
child: Stack(
children: [
ImageFiltered(
imageFilter: muteVideo
? ImageFilter.blur(sigmaX: 3.0, sigmaY: 3.0)
: ImageFilter.blur(sigmaX: 0, sigmaY: 0),
child: resizeVideo
? _renderRemoteVideo()
: _renderLocalPreview(), // Widget that is blurred
),
if (muteVideo)
Positioned(
top: 4,
left: 4,
child: Icon(
Icons.videocam_off_outlined,
color: Colors.white,
size: 32,
),
),
if (muteAudio)
Positioned(
bottom: 4,
right: 4,
child: Icon(
Icons.mic_off_outlined,
color: Colors.white,
size: 32,
),
),
],
),
),
),
),
Positioned(
bottom: 32,
right: 8,
left: 8,
child: Row(
crossAxisAlignment: CrossAxisAlignment.center,
mainAxisAlignment: MainAxisAlignment.spaceAround,
children: [
InkWell(
onTap: () async {
await Screen.keepOn(false);
await _engine?.leaveChannel();
await _serverHandler.leaveChannel(channelId!);
Navigator.pop(_scaffoldKey.currentContext!);
},
child: Container(
padding: EdgeInsets.all(16),
decoration: BoxDecoration(
borderRadius: BorderRadius.circular(50),
color: Colors.red),
child: Icon(
Icons.call_end,
color: Colors.white,
),
),
),
InkWell(
onTap: this._switchCamera,
child: Container(
padding: EdgeInsets.all(16),
decoration: BoxDecoration(
borderRadius: BorderRadius.circular(50),
color: Colors.white),
child: Icon(
Icons.flip_camera_android_rounded,
color: Colors.black87,
),
),
),
InkWell(
onTap: this._onToggleMuteVideo,
child: Container(
padding: EdgeInsets.all(16),
decoration: BoxDecoration(
borderRadius: BorderRadius.circular(50),
color: Colors.white),
child: Icon(
muteVideo
? Icons.videocam_off_outlined
: Icons.videocam_outlined,
color: Colors.black87,
),
),
),
InkWell(
onTap: this._onToggleMute,
child: Container(
padding: EdgeInsets.all(16),
decoration: BoxDecoration(
borderRadius: BorderRadius.circular(50),
color: Colors.white),
child: Icon(
muteAudio
? Icons.mic_off_outlined
: Icons.mic_none_outlined,
color: Colors.black87,
),
),
),
],
),
)
],
),
),
),
);
}
// current user video
Widget _renderLocalPreview() {
return RtcLocalView.SurfaceView();
}
// remote user video
Widget _renderRemoteVideo() {
// return RtcRemoteView.SurfaceView(uid: 70);
if (_remoteUid != null) {
return RtcRemoteView.SurfaceView(uid: _remoteUid!);
} else {
return Text(
'Please wait, doctor is joining shortly',
style: TextStyle(
color: Colors.white, fontSize: 16, fontWeight: FontWeight.bold),
textAlign: TextAlign.center,
);
}
}
_switchCamera() {
_engine?.switchCamera().then((value) {
setState(() {
switchCamera = !switchCamera;
});
}).catchError((err) {
log('switchCamera $err');
});
}
void _onToggleMute() {
setState(() {
muteAudio = !muteAudio;
});
_engine!.muteLocalAudioStream(muteAudio);
}
void _onToggleMuteVideo() {
setState(() {
muteVideo = !muteVideo;
});
_engine!.muteLocalVideoStream(muteVideo);
}
Future<bool> _onWillPop() async {
return (await showDialog(
context: _scaffoldKey.currentContext!,
builder: (context) => new AlertDialog(
title: new Text('Are you sure?'),
content: new Text('Do you want to exit exit Video Call?'),
actions: <Widget>[
TextButton(
onPressed: () => Navigator.pop(_scaffoldKey.currentContext!),
child: new Text('No'),
),
TextButton(
onPressed: () async {
await Screen.keepOn(false);
await _serverHandler.leaveChannel(channelId!);
await _engine?.leaveChannel();
Navigator.pop(_scaffoldKey.currentContext!);
Navigator.pop(_scaffoldKey.currentContext!);
},
child: new Text('Yes'),
),
],
),
)) ??
false;
}
_userLeftTheCall() async {
return (await showDialog(
context: _scaffoldKey.currentContext!,
builder: (context) => new AlertDialog(
title: new Text('Doctor Left'),
content: new Text('Doctor left this call please join Again'),
actions: <Widget>[
TextButton(
onPressed: () async {
// await _serverHandler.leaveChannel(channelId!);
await Screen.keepOn(false);
await _engine?.leaveChannel();
Navigator.pop(_scaffoldKey.currentContext!);
Navigator.pop(_scaffoldKey.currentContext!);
// Navigator.of(context).pop(true);
},
child: new Text('Okay'),
),
],
),
)) ?? false;
}
}
Please ignore things which are not relevent. Also the log is too long to share, the log keeps on running during the entire video call.
Mobile Screenshot
Thank You
Hello guys I have registration form with checkbox to be enabled in order to allow registration.
I need my user to do checkbox checked to have button enabled otherwise an tooltip will be shown.. like... " u need to accept terms and condition to register... "
This is the part of the CheckBox:
Row(
mainAxisAlignment: MainAxisAlignment.center,
children: <Widget>[
Theme(
data: ThemeData(unselectedWidgetColor: Colors.white,),
child: Checkbox(
focusColor: Colors.lightBlue,
activeColor: Colors.orange,
value: rememberMe,
onChanged: (newValue) {
setState(() {
rememberMe = (newValue);
});
},
),
),
RichText(
text: TextSpan(children: [
TextSpan(
text: 'Accetto le condizioni e ',
style: TextStyle(fontSize: 10),
),
TextSpan(
text: 'il trattamento dei dati personali ',
style: TextStyle(fontSize: 10,decoration: TextDecoration.underline,),
),
]),
)
],
),
and this the "registration Button"
ButtonTheme(
minWidth: 100,
height: 50.0,
child: RaisedButton(
shape: RoundedRectangleBorder(
borderRadius: BorderRadius.circular(5.0),
),
elevation: 10,
onPressed: () async {
setState(() {
showSpinner = true;
});
try {
final newUser =
await _auth.createUserWithEmailAndPassword(
email: email, password: password);
if (newUser != null) {
Navigator.push(
context,
MaterialPageRoute(builder: (context) => HomePage()),
);
}
setState(() {
showSpinner = false;
});
} catch (e) {
print(e);
}
},
color: Color(0xFF1f2032),
child: Text(
'SIGNUP',
style: TextStyle(fontSize: 18, color: Colors.white),
),
),
),
There are certain things which I would like to give you an insight about. Which will help you surely, so follow along.
Pointers
Read upon Flutter Form and it's Validation, which answers your question on showing up the error message under your forms for the validations
One very useful widget to achieve what you want is Flutter Tooltip Unfortunately, you cannot bring up the tooltip which you wanted to do programmatically
Workaround: Do use any of these to show up you message
Snackbar Flutter
Flutter AlerDialog
Simply show up a text, like a validator for the form under the checkbox like I will demonstrate in the code for you
Now, I have demonstrated both of them in this code, but the code is not similar. It would be enough to you let you know the best practices you can do along with your tooltip message showcase
Please note: To make a tooltip like structure using Container(), you can follow this answer, will help you in a great extent
class _MyHomePageState extends State<MyHomePage> {
bool rememberMe = false;
// this bool will check rememberMe is checked
bool showErrorMessage = false;
//for form Validation
final _formKey = GlobalKey<FormState>();
#override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: Text(widget.title),
),
body: Form(
key: _formKey,
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
crossAxisAlignment: CrossAxisAlignment.center,
children: [
Padding(
padding: EdgeInsets.symmetric(horizontal: 20.0),
child: TextFormField(
validator: (value) {
// retiurning the validator message here
return value.isEmpty ? "Please enter the message" : null;
}
)
),
Row(
mainAxisAlignment: MainAxisAlignment.center,
children: [
Text('Accept Terms & Conditions'),
SizedBox(width: 20.0),
Checkbox(
focusColor: Colors.lightBlue,
activeColor: Colors.orange,
value: rememberMe,
onChanged: (newValue) {
setState(() => rememberMe = newValue);
}
)
]
),
// based up on this bool value
showErrorMessage ?
Container(
decoration: BoxDecoration(
color: Colors.red,
borderRadius: BorderRadius.circular(80.0)
),
child: Padding(
padding: EdgeInsets.all(10.0),
child: Text('Please accept the terms and conditions to proceed...')
)
)
: Container(),
SizedBox(height: 20.0),
RaisedButton(
child: Text('Submit'),
onPressed: (){
// for your form validation
if(_formKey.currentState.validate()){
// do your success operation here!
// checking for the rememberValue
// and setting the message bool data
if(rememberMe != true)
setState(() => showErrorMessage = true);
else
setState(() => showErrorMessage = false);
}
}
)
]
)
)
);
}
}
How it works?
It will first check whether the form is empty, if it not, then checks whether the checkbox is empty or not
Fun Fact
You can use the above logic or bool showErrorMessage, to show anything, be it, SnackBar, AlertDialog or the message which I showed in the above code.
Result
You need to pass null to the onPressed of a Button to make it disabled. So you'll need to pass null to the onPressed of the RaisedButton when rememberMe is false.
ButtonTheme(
minWidth: 100,
height: 50.0,
child: RaisedButton(
shape: RoundedRectangleBorder(
borderRadius: BorderRadius.circular(5.0),
),
elevation: 10,
onPressed: rememberMe ? () async {
setState(() {
showSpinner = true;
});
try {
final newUser =
await _auth.createUserWithEmailAndPassword(
email: email, password: password);
if (newUser != null) {
Navigator.push(
context,
MaterialPageRoute(builder: (context) => HomePage()),
);
}
setState(() {
showSpinner = false;
});
} catch (e) {
print(e);
}
} : null, // make it null if false
color: Color(0xFF1f2032),
child: Text(
'SIGNUP',
style: TextStyle(fontSize: 18, color: Colors.white),
),
),
),
I wanted to change a value in firestore by a button onPressed. I know there might be seconds delay and I wanted to show a CircularProgressIndicator widget while waiting. But It's not not working.
Here is my widget:
Widget save(String id) {
return new FutureBuilder(
future: PostController().isAlreadySaved(id),
builder: (BuildContext context, AsyncSnapshot<bool> snapshot) {
if (snapshot.hasData) {
if (snapshot.data) {
return FlatButton(
onPressed: () {
setState(() {
isSaveLoading = true;
});
PostController().deleteSaved(id);
setState(() {
isSaveLoading = false;
});
},
child: !isSaveLoading
? Icon(
MyFlutterApp.star,
size: 30,
color: Colors.redAccent,
)
: SizedBox(
height: 15,
width: 15,
child: CircularProgressIndicator(
strokeWidth: 1,
),
),
);
} else {
return FlatButton(
onPressed: () {
setState(() {
isSaveLoading = true;
});
PostController().save(context, id);
setState(() {
isSaveLoading = false;
});
},
child: !isSaveLoading
? Icon(
MyFlutterApp.star,
size: 30,
color: Colors.grey,
)
: SizedBox(
height: 15,
width: 15,
child: CircularProgressIndicator(
strokeWidth: 1,
),
),
);
}
} else {
return Container(
//color: Colors.white,
width: Adapt.screenW(),
child: Column(
mainAxisSize: MainAxisSize.min,
children: <Widget>[
Flexible(
child: Shimmer.fromColors(
baseColor: Colors.grey[400],
highlightColor: Colors.grey[50],
enabled: true,
child: Row(
children: <Widget>[
Padding(
padding: const EdgeInsets.fromLTRB(5, 0, 0, 0),
child: new Icon(
MyFlutterApp.star,
size: 30,
color: Colors.white,
),
),
],
),
),
),
],
),
);
}
},
);
}
Here is my isAlreadySaved() function:
Future<bool> isAlreadySaved(String id) async {
bool isSaved = false;
QuerySnapshot snapshot =
await databaseReference.collection('saved').getDocuments();
snapshot.documents.forEach((a) async {
if (a.data['postID'] == id&&
a.data['saver'] == globals.currentUser.uid) {
isSaved = true;
}
});
return isSaved;
}
The delete function actually deletes the document from my saved collection in firestore and the save function creates a document and saves.
Thank you in advance!
I think the problem is because you update the widget tree too fastly, or the flutter UI thread is locked by waiting the PostController job to finish...
In these lines:
onPressed: () {
setState(() {
isSaveLoading = true;
});
PostController().deleteSaved(id);
setState(() {
isSaveLoading = false;
});
},
Here I see that you want to update the loading state of the button. But the problem is that when you set isSaveLoading to true you don't wait for the PostController().deleteSaved(id) to finish before resetting isSaveLoading to false.
On another side, if PostController().deleteSaved() is doing a long job, since it's not async, it can lock the UI thread for a time, so you will never see your circular progress bar.
You can make the onPressed callback async and await for the PostController job.
onPressed: () async {
setState(() {
isSaveLoading = true;
});
// The deleteSaved function have to be async too
await PostController().deleteSaved(id);
setState(() {
isSaveLoading = false;
});
},
Hope this will help!