setState doesn't work with DraggableScrollable don't work - flutter

I have a DraggableScrollableSheet in which I have two buttons Yes and No. If the user clicked on yes then the spinner is shown and if the function sendEmail returns true then a new DraggableScrollableSheet will be shown.
My problem is when I do a setState for the variable isLoading while clicking on Yes the DraggableScrollableSheet is closed then if the email is send DraggableScrollableSheet will be shown. I don't know why the first one is closed. When I removed the set State the spinner is not shown at all!
InkWell(
onTap: () {
print(isLoading);
// setState(() {
//isLoading = true;
//});
sendEmail(widget.measure).then((sendEmail) {
if (sendEmail == true) {
// setState(() {
// isLoading = false;
// });
print(isLoading);
newTransmissionController.reset();
transmissionDoneController.animateTo(
0.95,
duration:
const Duration(milliseconds: 100),
curve: Curves.easeOutBack,
);
} else {
// setState(() {
isLoading = false;
// });
Navigator.of(context).pop();
Navigator.of(context).push(PageRouteBuilder(
pageBuilder: (context, animation1,
animation2) =>
const TransmissionErrorPage()));
}
});
},
child: AnimatedContainer(
duration:
const Duration(milliseconds: 400),
height:
MediaQuery.of(context).size.height *
0.08,
margin: const EdgeInsets.symmetric(
horizontal: 5),
width:
MediaQuery.of(context).size.height *
0.18,
decoration: BoxDecoration(
color: const Color(0xFF008DFF),
borderRadius: BorderRadius.circular(10),
),
child: Center(
child: !isLoading
? const Text(
"Yes",
style: TextStyle(
color: Colors.white,
fontSize: 17,
fontFamily: 'SFProRegular',
),
)
: const Center(
child:
CircularProgressIndicator(
backgroundColor: Colors.white,
valueColor:
AlwaysStoppedAnimation<
Color>(
Color(0xFF008DFF)),
),
),
)),
),
newTransmissionController and transmissionDoneController are DraggableScrollableController.
DraggableScrollableController newTransmissionController =
DraggableScrollableController();
DraggableScrollableController transmissionDoneController =
DraggableScrollableController();

Wrap DraggableScrollableSheet with StatefulBuilder and use its setstate to update the bottomSheet UI.
showModalBottomSheet(
context: context,
builder: (c) {
return StatefulBuilder(
builder: (context, setStateSB) => DraggableScrollableSheet(
builder: (context, scrollController) {
/// use `setStateSB` to update inside dialog
/// use `setState` to update state class UI
},
),
);
});

Related

FLUTTER showModalBottomSheet

how can i controll the default pop property of bottom sheet.Like I want to asign a value to a variable when showModalBottomSheet is popped .I have tried to do with controllers
Why don't you just do :
showModalBottomSheet(
context: context,
builder: (context) {
var a = "desired value";
return Widget;
you can trigger when the bottom sheet is popped/dismissed with an AnimationController like this:
in your StatefulWidget's State:
late AnimationController _controller;
#override
void initState() {
_controller = AnimationController(
vsync: this,
duration: const Duration(milliseconds: 300),
);
_controller.addListener(() {
if (_controller.isDismissed) {
print("dismissed");
}
});
super.initState();
}
#override
void dispose() {
_controller.dispose;
super.dispose();
}
in your showModalBottomSheet:
showModalBottomSheet(
context: context,
builder: (context) => Container(),
transitionAnimationController: _controller, // assign the controller
);
You can set isDismissible: false, and than add one button (Close button) on tap of button, you have to do your code and pop the bottomSheet.
showModalBottomSheet(
isScrollControlled: true,
isDismissible: false,
shape: const RoundedRectangleBorder(
borderRadius: BorderRadius.vertical(
top: Radius.circular(15),
),
),
context: context,
builder: (context) {
return SizedBox(
height:
MediaQuery.of(context).size.height * (0.6),
child: Padding(
padding: const EdgeInsets.only(top: 15),
child: Column(
children: [
Row(
mainAxisAlignment:
MainAxisAlignment.spaceBetween,
children: [
InkWell(
onTap: () {
// Add your code here. which you want to perform before closing bottomSheet
Navigator.pop(context);
},
child: const Icon(Icons.close)),
InkWell(
onTap: () {},
child: const Text(
"Reset",
)),
],
),
const SizedBox(height: 15),
//Other widgets of bottomSheet
Container(
height:
MediaQuery.of(context).size.height * (0.5),
color: Colors.amber,
)
],
),
),
);
});
thanks for your help
I solved the problem with WillPopScope
popfunction() {
SelectedValue = tempValue;
Navigator.pop(context, true);
}
onWillPop: () async {
return popfunction() ?? false;
},

Flutter > Unselect a RadioButton in a View.builder

I am not finding any answer for my question so I am hoping to find someone who can help.
I have a GridView with text buttons.
I can select the buttons, however I can't unselect any of them.
this is my code
#override
Widget build(BuildContext context) {
return TextButton(
onLongPress: () => showDialog<String>(
),
style: ButtonStyle(
side: MaterialStateProperty.all(BorderSide(
width: 5,
color: widget.isSelected ? Colors.black : Colors.white)),
shape: MaterialStateProperty.all(
RoundedRectangleBorder(borderRadius: BorderRadius.circular(10))),
backgroundColor: MaterialStateProperty.all(widget.pickerColor),
elevation: MaterialStateProperty.all(10)),
onPressed: () {
widget.selectedCard(widget.index); //This selects the cards, how to unselect (if Statements?)
},
child: FittedBox(
fit: BoxFit.fitHeight,
child: Text(
widget.cardTitle,
style: TextStyle(
fontSize: 17,
color: useWhiteForeground(widget.pickerColor)
? const Color(0xffffffff)
: const Color(0xff000000),
),
),
),
);
}
}
This is the Grid
#override
Widget build(BuildContext context) {
return Consumer<MyCardData>(
builder: (context, cardData, child) {
return Padding(
padding: const EdgeInsets.all(10),
child: GridView.builder(
clipBehavior: Clip.none,
itemBuilder: (context, index) {
final card = cardData.cards[index];
return MyCard(
selectedCard,
index: index,
isSelected: _selectedCard == index,
cardTitle: card.name,
pickerColor: card.cardColor,
deleteCallback: () {
cardData.deleteCallback(card);
},
);
},
itemCount: cardData.cardCount,
gridDelegate: const SliverGridDelegateWithMaxCrossAxisExtent(
maxCrossAxisExtent: 150,
childAspectRatio: 2.5 / 1,
crossAxisSpacing: 0,
mainAxisSpacing: 0,
),
),
);
},
);
}
}
feel free to use my git to see the full code
get from version control
since you want to make a single selection, it will need a simple workaround.
int _selectedCard = -1;
selectedCard(index) {
// this condition is when user press the same button
// set the _selectedCard back into -1
if (_selectedCard == index) {
setState(() {
_selectedCard = -1;
});
} else{
setState(() {
_selectedCard = index;
});
}
}

Can't update ListView inside showModalBottomSheet after delete from db

I have a ModalBottomSheet and I have a ListView inside it. ListView is connected to a sqlite db. All create,read and delete methods are working fine but when I delete an item, I get the toast message which approves the operation but my view does not get updated. When I close the sheet and open again It's updated.
My ModalBottomSheet codes are :
void popUpScreen(context) {
showModalBottomSheet(
context: context,
builder: (BuildContext context) {
return Container(
color: Colors.blue[600],
height: MediaQuery.of(context).size.height * .8,
child: ListView.builder(
itemCount: _loclist.length,
itemBuilder: (context, index) {
return Padding(
padding: EdgeInsets.only(top: 8, left: 16, right: 16),
child: Card(
child: ListTile(
leading: Text(_loclist[index].name),
title: IconButton(
icon: Icon(Icons.delete),
onPressed: () async {
var result = await locationdbservice
.deleteLocation(_loclist[index].id);
this._loclist.removeAt(index);
if (result > 0) {
Toast.show("Silindi", context,
duration: Toast.LENGTH_SHORT,
gravity: Toast.BOTTOM);
getLocations();
}
},
),
),
));
}));
});
}
and getLocations() :
getLocations() async {
_loclist = List<Loc>();
var locations = await locationdbservice.readLocations();
locations.forEach((location) {
setState(() {
var locationModel = Loc();
locationModel.lat = location['lat'];
locationModel.lon = location['lon'];
locationModel.name = location['name'];
locationModel.note = location['note'];
locationModel.id = location['id'];
_loclist.add(locationModel);
});
});
}
I tried to write an initState() function which returns getLocations() and called it at the end of onPressed() with initState() but didn't work. I tried to make an empty initState() but didn't work. How can I update my ListView while I view it?
Edit: Here's where I call popUpScreen:
Widget locationsButton(BuildContext context) {
return Container(
width: 250,
height: 60,
child: ElevatedButton(
style: ElevatedButton.styleFrom(
padding: EdgeInsets.zero,
shape:
RoundedRectangleBorder(borderRadius: BorderRadius.circular(30)),
),
clipBehavior: Clip.antiAlias,
onPressed: () {
popUpScreen(context);
},
child: Ink(
decoration: BoxDecoration(
gradient: LinearGradient(
begin: Alignment.bottomCenter,
end: Alignment.topCenter,
colors: [Colors.white70, Colors.white38])),
child: Container(
constraints: BoxConstraints(maxHeight: 300, minWidth: 50),
alignment: Alignment.center,
child: Text(
"KONUMLARIM",
style: TextStyle(fontSize: 16, fontWeight: FontWeight.bold),
),
),
),
));
}
Here I come with the solution. It happens always with showModelBottomSheet that it doesn't rebuild or change its state so I come across the solution. Add StatefulBuilder in ModelBottomSheet which will change its state onPressed Function. Also, your code is throwing exceptions so I handled these exceptions. Go to GitHub merge pull request and continue.
Thumbs up if this solution helped
Here is the sample code. I had implemented this just go to GitHub and start coding
void popUpScreen() {
showModalBottomSheet(
context: context,
builder: (BuildContext context) {
return StatefulBuilder(
builder: (BuildContext context, StateSetter mystate) {
return Container(
color: Colors.blue[600],
height: MediaQuery.of(context).size.height * .8,
child: ListView.builder(
itemCount: _loclist.length,
itemBuilder: (context, index) {
return Padding(
padding: EdgeInsets.only(top: 8, left: 16, right: 16),
child: Card(
child: ListTile(
leading: Text(_loclist[index].name),
title: IconButton(
icon: Icon(Icons.delete),
onPressed: () async {
var result = await locationdbservice
.deleteLocation(_loclist[index].id);
mystate(() {
this._loclist.removeAt(index);
});
if (result > 0) {
Toast.show("Silindi", context,
duration: Toast.LENGTH_SHORT,
gravity: Toast.BOTTOM);
getLocations();
}
setState(() {});
},
),
),
));
}));
});
});
}
Please check with setstate method
void popUpScreen(context) {
showModalBottomSheet(
context: context,
builder: (BuildContext context) {
return Container(
color: Colors.blue[600],
height: MediaQuery.of(context).size.height * .8,
child: ListView.builder(
itemCount: _loclist.length,
itemBuilder: (context, index) {
return Padding(
padding: EdgeInsets.only(top: 8, left: 16, right: 16),
child: Card(
child: ListTile(
leading: Text(_loclist[index].name),
title: IconButton(
icon: Icon(Icons.delete),
onPressed: () async {
var result = await locationdbservice
.deleteLocation(_loclist[index].id);
setState(() {
this._loclist.removeAt(index);
});
if (result > 0) {
Toast.show("Silindi", context,
duration: Toast.LENGTH_SHORT,
gravity: Toast.BOTTOM);
getLocations();
}
},
),
),
));
}));
});
}
According to the docs:
Calling setState notifies the framework that the internal state of
this object has changed in a way that might impact the user interface
in this subtree, which causes the framework to schedule a build for
this State object.
So if the state of the widget changes you have to call setState((){}); to trigger a rebuild of the view and see immediately the changes implied by the new state.

Flutter, how to call Dialog function from another class

what is the proper way to call Dialog function from another class.
I have been searching this topic for a while but seems none of them are my answer.
my Dialog has a little complicated logic for server communicating and some paginations
so this code is going to be long for just one dart file. so I want to separate them.
and I need the some dialog animations so I picked the showGeneralDialog()
I also saw the example dialog implementaion using StatefulBuilder() which can use setState,
but this problem is it is not able to use initState()
for now, what I did is below
dart1 file
import 'package:aaa/bbb/some_dialog_file.dart'
as someDialog;
GestureDetector(
onTap: () async{
var result =
await someDialog.displayDialogOKCallBack(
context,
);
},
child: Container(
width: 60,
height: 60,
child: Icon(
Icons.comment,
size: 38,
),
),
)
dart2 file
Future<dynamic> displayDialogOKCallBack(BuildContext context) async {
return await showGeneralDialog(
barrierLabel: "Label",
barrierDismissible: true,
// barrierColor: ,
transitionDuration: Duration(milliseconds: 400),
context: context,
pageBuilder: (context, anim1, anim2) {
return StatefulBuilder(builder: (context, setState) {
return Scaffold(
body: SafeArea(
),
);
});
},
transitionBuilder: (context, anim1, anim2, child) {
return SlideTransition(
position:
Tween(begin: Offset(0, 1), end: Offset(0, -0.02)).animate(anim1),
child: child,
);
},
);
}
so my question is I want to build very clean animation dialog
which is logically separated from base class file and it has to have initState(), and setState()
how could I acheive this ? thanks
class _MyHomePageState extends State<MyHomePage> {
#override
Widget build(BuildContext context) {
return Container(
child: RaisedButton(
onPressed: () {
someDialog(context);
},
child: Text("click"),
),
);
}
Future<dynamic> someDialog(BuildContext context) async {
return await showGeneralDialog(
barrierLabel: "Label",
barrierDismissible: true,
context: context,
pageBuilder: (context, anim1, anim2) {
return Scaffold(
backgroundColor: Colors.transparent,
body: SafeArea(
child: Center(
child: Column(
mainAxisAlignment: MainAxisAlignment.end,
children: [
Column(
mainAxisAlignment: MainAxisAlignment.start,
children: [
// List
AnotherClassDialog(),
],
),
],
),
),
),
);
});
}
}
class AnotherClassDialog extends StatefulWidget {
#override
_AnotherClassDialogState createState() => _AnotherClassDialogState();
}
class _AnotherClassDialogState extends State<AnotherClassDialog> {
Color color;
#override
void initState() {
// TODO: implement initState
super.initState();
color = Colors.black;
}
#override
Widget build(BuildContext context) {
return Center(
child: Column(
children: [
RaisedButton(
onPressed: () {
setState(() {
color = Colors.red;
});
},
),
Container(
width: 100,
height: 100,
color: color,
),
RaisedButton(
onPressed: () {
setState(() {
color = Colors.green;
});
},
)
],
),
);
}
}
I use a custom dialog in my app in some classes and had the same problem.
You should define a dialog and pass context and other variables to it and call it everywhere you want.
You can define a dialog like this :
showCustomDialog(BuildContext context, String title, String description) {
showDialog(
context: context,
builder: (BuildContext context) {
return AlertDialog(
title: Text(
title,
textAlign: TextAlign.right,
),
content: SingleChildScrollView(
child: Text(
description,
style: Theme.of(context).textTheme.bodyText1,
textAlign: TextAlign.right,
),
),
actions: [
FlatButton(
child: Text(
'ok',
style: Theme.of(context).textTheme.bodyText2.copyWith(
color: Theme.of(context).accentColor,
),
),
onPressed: () => Navigator.of(context).pop(),
),
],
actionsPadding: EdgeInsets.symmetric(
horizontal: 10,
vertical: 5,
),
);
});
}
and use it everywhere you want like this :
InkWell(
child: Icon(
Icons.error_outline,
size: 17,
),
onTap: () => showCustomDialog(context,"text1" , "text2") ,
),
I hope my answer will help you.

How do I implement CircularProgressIndicator dialog and '"Message sent!" Dialog in Flutter?

I am a newbie in flutter and I would like to get assistance on how to implement CircularProgressIndicator dialog and "Message sent!" dialog when a flatbutton is pressed. In this case, I am implementing a contact form for users to send their messages through FirebaseFirestore.instance. My initial approach of setting a bool _isLoading and using it to trigger the CircularProgressIndicator is working only that it does not respond when I set it as false after the message has been sent. As a result, I am getting a CircularProgressIndicator that does not stop even after confirming that the message has been sent. Could anyone help me with this problem?
FlatButton(
shape: RoundedRectangleBorder(
borderRadius: BorderRadius.circular(20),
),
color: Colors.pinkAccent,
onPressed: () {
setState(() {
_isLoading = true;
});
if (_isLoading) {
showDialog(
barrierDismissible: true,
context: context,
builder: (BuildContext context) {
return Dialog(
child: Container(
height: _height * 0.09495,
width: _width * 0.17644444,
padding: EdgeInsets.only(
top: 15,
),
child: Center(
child: Column(
children: [
CircularProgressIndicator(),
Text('Please wait...'),
],
),
),
),
);
});
if (_fbKey.currentState.saveAndValidate()) {
FirebaseFirestore.instance
.collection('message')
.doc()
.set({
'name': _fbKey.currentState.value['name'],
'email':
_fbKey.currentState.value['email'],
'details':
_fbKey.currentState.value['details'],
'category':
_fbKey.currentState.value['category'],
'created': FieldValue.serverTimestamp(),
}).then((_) {
print('Sent!');
}).catchError((error) {
print("Failed to send message: $error");
});
}
} else {
showDialog(
barrierColor: Colors.transparent,
barrierDismissible: true,
context: context,
builder: (BuildContext context) {
return Dialog(
child: Container(
height: _height * 0.09495,
width: _width * 0.17644444,
child: Center(
child: Text(
'Message sent2!',
style: TextStyle(
color: Colors.green,
fontSize: 16,
),
),
),
),
);
});
}
setState(() {
_isLoading = false;
});
},
),
'''
You don't actually need booleans to show and pop dialogs. This logic can easily be implemented if we understand asynchronous functions and await calls. As soon as the form is validated we will show loading dialog and then await firebase query. When firebase query is executed we will see if it has caught error or successfully executed using try catch block. If it has caught an error we will pop the dialog, print error and then call return so our method is terminated. If it hasn't caught any error it will continue normal executed.We will pop this dialog and show message sent dialog.
FlatButton(
shape: RoundedRectangleBorder(
borderRadius: BorderRadius.circular(20),
),
color: Colors.pinkAccent,
onPressed: () async {
if (_fbKey.currentState.saveAndValidate()) {
showDialog(
barrierDismissible: true,
context: context,
builder: (BuildContext context) {
return Dialog(
child: Container(
height: _height * 0.09495,
width: _width * 0.17644444,
padding: EdgeInsets.only(
top: 15,
),
child: Center(
child: Column(
children: [
CircularProgressIndicator(),
Text('Please wait...'),
],
),
),
),
);
});
try{
await FirebaseFirestore.instance
.collection('message')
.doc()
.set({
'name': _fbKey.currentState.value['name'],
'email':
_fbKey.currentState.value['email'],
'details':
_fbKey.currentState.value['details'],
'category':
_fbKey.currentState.value['category'],
'created': FieldValue.serverTimestamp(),
});
} catch (e){
//Pop loading dialog because error has occured. We will print error and call return so our function
//should be terminated
Navigator.pop(context);
print("Exception occured");
return;
}
//Pop loading dialog because query is executed and now we want to show message sent dialog
Navigator.pop(context);
print("Query successfully executed i.e Message Sent");
showDialog(
barrierColor: Colors.transparent,
barrierDismissible: true,
context: context,
builder: (BuildContext context) {
return Dialog(
child: Container(
height: _height * 0.09495,
width: _width * 0.17644444,
child: Center(
child: Text(
'Message sent2!',
style: TextStyle(
color: Colors.green,
fontSize: 16,
),
),
),
),
);
});
}
},
)