Can't update ListView inside showModalBottomSheet after delete from db - flutter

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.

Related

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;
});
}
}

setState doesn't work with DraggableScrollable don't work

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
},
),
);
});

How to update the ui when my list gets filled with data GetX Flutter

Im trying to show a listView.builder inside a AlertDialog, and Im filling the its list by calling a function everytime the button to open the AlertDialog is pressed but the problem is that the ui doesn’t update when the list is filled with the data, I'm using getX and I'm very new to it, can someone show me what I'm doing wrong?
I'm using the GetX builder:
GetX<DashboardController>(
init: Get.put<DashboardController>(DashboardController()),
builder: (DashboardController dashboardController) {
return GridView.builder(
My Get.dialog function:
return GestureDetector(
onTap: () {
// this is where I'm filling the list
dashboardController
.callEmployeeCheckInOutList(_employeeModel.id);
Get.dialog(
AlertDialog(
contentPadding: EdgeInsets.zero,
content: SizedBox(
height: size.height * 0.55,
width: size.width,
child: Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: [
EmployeeProfileWidget(
size: size,
profileBackgroudPath: profileBackgroudPath,
employeeModel: _employeeModel,
),
// this is where my listview.builder resides
EmployeeActivityWidget(
closeCrossPath: closeCrossPath,
employeeCheckInOutList:
_employeeCheckInOutList,
employeeModel: _employeeModel,
onTap: () {},
),
],
),
),
),
);
},
My listview.builder:
Expanded(
child: Padding(
padding: const EdgeInsets.only(
left: 32.0,
right: 50.0,
),
child: ListView.builder(
itemCount: employeeCheckInOutList.length,
shrinkWrap: true,
itemBuilder: (context, index) {
final _checkInOutModel = employeeCheckInOutList[index];
return SizedBox(
height: 120,
child: TimelineTile(
beforeLineStyle: const LineStyle(
color: Color(0xffa5affb),
),
My Controller:
Rx<List<CheckInOutModel>> _employeeCheckInOutList =
Rx<List<CheckInOutModel>>([]);
List<CheckInOutModel> get employeeCheckInOutList =>
_employeeCheckInOutList.value;
Future<void> callEmployeeCheckInOutList(String id) async {
_employeeCheckInOutList =
await EmployeeService.employeeCheckInOutFuture(docId: id);
update();
}
Use .assignAll method on the RxList to trigger UI update:
Future<void> callEmployeeCheckInOutList(String id) async {
final result = await EmployeeService.employeeCheckInOutFuture(docId: id);
_employeeCheckInOutList.assignAll(result);
}
And you don't need to call update() when using Rx.
I already faced same issue.
Solution:
Simply use again GetX<Controller> inside AlertDialog
like
GetX<DashboardController>(
init: Get.put<DashboardController>(DashboardController()),
builder: (DashboardController dashboardController) {
return GridView.builder(
.....
Get.dialog(
AlertDialog(
contentPadding: EdgeInsets.zero,
content: GetX<DashboardController>(
init: Get.put<DashboardController>(DashboardController()),
builder: (DashboardController dashboardController) {
SizedBox(

Type 'Future<dynamic>' is not subtype of type 'Widget'

I am showing markers from API on google maps. Here is my build method. When the program reaches the _widgetbuilder() method, it throws the specific error of type Future is not a subtype of the widget. If someone could please help to solve the problem and also tell me that what exactly this error means.....
Widget build(BuildContext context) {
return Scaffold(
backgroundColor: Colors.white,
body: FutureBuilder<List<MarkersOnMap>>(
future: future,
builder: (BuildContext context, AsyncSnapshot snapshot) {
if (!snapshot.hasData)
return Container(
child: Center(
child: CircularProgressIndicator(),
),
);
if (snapshot.hasData && snapshot.data.isEmpty) {
return Center(
child: Container(
child: Column(
children: [
Text(
'No Properties Added Yet\nPlease Add Some!',
style:
TextStyle(fontWeight: FontWeight.bold, fontSize: 20),
),
ElevatedButton.icon(
onPressed: () {
Navigator.push(
context,
PageTransition(
duration: Duration(microseconds: 500),
type: PageTransitionType.fade,
child: AddNewEproperty(
createEproperty: widget.createEproperty),
),
);
},
label: Text('Add'),
icon: Icon(Icons.add),
),
],
),
),
);
} else
_widgetbuilder();
if (snapshot.hasData) {
return ListView.builder(
itemCount: allWidgets.length + 1,
shrinkWrap: true,
padding: EdgeInsets.only(top: 16),
physics: NeverScrollableScrollPhysics(),
itemBuilder: (context, i) {
return Stack(
children: <Widget>[
Container(),],);},);},},),);}
This is the _widgetbuilder() method. When it reaches this return _widgetbuilder, throws _typeerror.
_widgetbuilder() async {
allWidgets = [];
widget.markersonmap = await future;
widget.markersonmap.forEach(
(element) {
print(element);
allWidgets.add(
Container(
height: 25,
width: 50,
child: new DecoratedBox(
decoration: BoxDecoration(
border: Border.all(color: Colors.black),
borderRadius: BorderRadius.circular(5.0),
color: Colors.black54),
child: Text(
element.ePropertiesCardsList.price.toString(),
textAlign: TextAlign.center,
style: TextStyle(
fontSize: 20,
fontWeight: FontWeight.bold,
color: Colors.white),
),
),
),
);
},
);
}
You are getting this error because your function _widgetbuilder returns Future<dynamic> because the function is async.
Widget _widgetbuilder(){
// code here
}
The function should be in this structure to return of type Widget. The code that needs to be async should either be taken out of build function or use .then pattern for async code instead of async await if you really need to.
This short 9 min video will help you understand async better in flutter.
In here now the type error is solved but after reading 'future.then..... it does not goto the future and fetch data but jumps to the next foreach line and then calls it as null.
_widgetbuilder() {
allWidgets = [];
// widget.markersonmap = await future;
future.then((value) {
widget.markersonmap = value;
});
widget.markersonmap.forEach(
(element) {
print(element);
allWidgets.add(
Container(
// other code
}

How can i turn container to Floatting action button

i am developin app i just got some of those code from an app i just need to cart page button make a floating action button this button shows how many products in cart also i want to do this if counter=0 i need to hide that FloatingActionButton if they added item to basket just show that time if you have any suggestions thanks a lot for now
'class CustomAppBar extends StatelessWidget {
#override
Widget build(BuildContext context) {
final CartListBloc bloc = BlocProvider.getBloc<CartListBloc>();
// TODO: implement build
return Container(
margin: EdgeInsets.only(bottom: 5),
child: Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: <Widget>[
StreamBuilder(
stream: bloc.listStream,
builder: (context, snapshot) {
List<FoodItem> foodItems = snapshot.data;
int length = foodItems != null ? foodItems.length : 0;
return buildGestureDetector(length, context, foodItems);
},
)
],
),
);
}
GestureDetector buildGestureDetector(
int length, BuildContext context, List<FoodItem> foodItems) {
return GestureDetector(
onTap: () {
if (length > 0) {
Navigator.push(
context, MaterialPageRoute(builder: (context) => Cart()));
} else {
return;
}
},
child: Container(
margin: EdgeInsets.only(left: 30),
child: Text(length.toString()),
padding: EdgeInsets.all(15),
decoration: BoxDecoration(
color: Colors.yellow[800], borderRadius: BorderRadius.circular(50)),
),
);
}'
First, you need a stateful widget and not a stateless one. If you are using VS code, it can actually do it for you automatically.
Once you have that, one variable will be "length".
String length;
void initState() {
length=0;
}
Whenever you increment it up, you have to call...
setState(() {
length+=1;
});
or down...
setState(() {
length-=1;
});
your floating action button will be...
(length==0)?
Container():
GestureDetector(
onTap: () {
Navigator.push(
context, MaterialPageRoute(builder: (context) => Cart()));
},
child: Container(
margin: EdgeInsets.only(left: 30),
child: Text(length.toString()),
padding: EdgeInsets.all(15),
decoration: BoxDecoration(
color: Colors.yellow[800], borderRadius: BorderRadius.circular(50)),
),
);
This means that if the cart length is 0, it will show an empty container.
If the cart is not 0, it will show a button with length