How to update the ui when my list gets filled with data GetX Flutter - 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(

Related

The argument type 'List<String>' can't be assigned to the parameter type 'String'. How can I solve this problem?

I've been learning flutter for 2 months. I'm trying to develop a wallpaper app. I created a model and a function. But right now I can only download 1 wallpaper. How can I make this a list? I get this error when I make a list.
This is url.
String url =
'https://images.hdqwalls.com/download/the-witcher-season-2-2022-5k-u1-1080x1920.jpg';
the list i want to use
List<String> url = [
'https://images.hdqwalls.com/download/the-witcher-season-2-2022-5k-u1-1080x1920.jpg',
'https://images.hdqwalls.com/download/the-witcher-season-2-2022-5k-u1-1080x1920.jpg',
];
and function
void saveimage() async {
await GallerySaver.saveImage(url, albumName: album_name);
}
and clicking this button provides download
ElevatedButton DownloadButton(BuildContext context) {
return ElevatedButton(
style: ElevatedButton.styleFrom(
minimumSize: Size(40, 40),
shape: CircleBorder(),
backgroundColor: Colors.grey.shade600.withOpacity(0.1),
),
child: Icon(Icons.download, color: Colors.white.withOpacity(0.7)),
onPressed: () {
saveimage();
ScaffoldMessenger.of(context).showSnackBar(
SnackBar(
duration: Duration(seconds: 2),
content: Text('Wallpaper downloaded!'),
action: SnackBarAction(
label: '',
onPressed: () {},
),
),
);
},
);
}
Problem image
The packages I use are
gallery_saver: ^2.3.2
async_wallpaper: ^2.0.1
I want to use it in gridview
GridView.builder(
itemCount: url.length,
gridDelegate: SliverGridDelegateWithFixedCrossAxisCount(
crossAxisCount: 2,
childAspectRatio: 9 / 16,
),
itemBuilder: (BuildContext context, int index) {
return Card(
child: Padding(
padding: EdgeInsets.all(1.0),
child: FullScreenWidget(
child: Stack(fit: StackFit.expand, children: [
Image.network(url, fit: BoxFit.cover),
void saveimage() async {
for(int i = 0; i <= url.length; i++) {
await GallerySaver.saveImage(url[I], albumName: album_name);
}
}
Above code will resolve your error
Let consider you want to download image when you click on the card then code will be like as following.
void saveimage(String imageUrl) async {
await GallerySaver.saveImage(imageUrl, albumName: album_name);
}
GridView.builder(
itemCount: url.length,
gridDelegate: SliverGridDelegateWithFixedCrossAxisCount(
crossAxisCount: 2,
childAspectRatio: 9 / 16,
),
itemBuilder: (BuildContext context, int index) {
return GestureDetector(
onTap: (){
await saveimage(url[index]);
},
child: Card(
child: Padding(
padding: EdgeInsets.all(1.0),
child: FullScreenWidget(
child: Stack(fit: StackFit.expand, children: [
Image.network(url, fit: BoxFit.cover),]),),);),
),)

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.

How to clear objects in a listviewbuilder in flutter?

How to control listviewbuilder from outside the listview in flutter?
In a textfield I can use a controller like so: controller: Textcontroller. Can I do something similar in listviewbuilder to clear all the objects in it?
So to be exact. My code looks something like this
Expanded(
child: new ListView.builder(
itemCount: List.length,
itemBuilder: (context,index){
return new Card(
//all stuff with data
),
),
);
},
....
How would I do so that when called from another function it removes all the items in the listview?
You need a Store class that holds this list along with list manipulation methods, then you can use provider for example to access that class and render that list.
You can define a variable in the state of your widget:
var _clear = false;
When this variable is true, the list will be cleared and when it's false, the list will be displayed. You can use setState to toggle this variable. Setting the itemCount of the ListView.builder to 0 clears the list.
Full code:
var _clear = false;
#override
Widget build(BuildContext context) {
return Scaffold(
body: Column(
children: <Widget>[
Padding(
padding: const EdgeInsets.all(30.0),
child: FlatButton(
child: Text('clear'),
color: Colors.pinkAccent,
onPressed: () {
setState(() {
_clear = true;
});
},
),
),
FlatButton(
child: Text('add'),
color: Colors.greenAccent,
onPressed: () {
setState(() {
_clear = false;
});
},
),
Expanded(
child: ListView.builder(
controller: _scrollController,
itemCount: _clear ? 0 : 100,
itemBuilder: (context, index) {
return Container(
height: 300,
child: Card(
child: Center(
child: Text('$index'),
),
),
);
},
),
),
],
),
);
}

Flutter Dismissible Event in LIstView

I have already implemented within the onDismissed: function a remove item method.
This method only works though for the last item of the list.
If I remove any other item I get the following error:
A dismissed Dismissible widget is still part of the tree.
The complication is that I use a Map to save and delete all the events that I need. Still when on the debugging mode the indexing seems fine.
The Error appears after this setState method:
setState(() {
print('_controller.selectedDay = ${_controller.selectedDay}');
customEvents[_controller.selectedDay].removeAt(index);
});
Here is the Code part:
Widget myCalendarBuild(BuildContext context, List<dynamic> shiftList) {
return ListView.separated(
separatorBuilder: (BuildContext context, int index) =>
Divider(color: Colors.black),
itemCount: _listOfShiftsPerGivenDay.length,
itemBuilder: (BuildContext context, int index) {
return Dismissible(
key: Key(customEvents[_controller.selectedDay][index].toString()),
onDismissed: (direction){
// removeShift(index);
Shift deleteShift = getParametersFromWidgetInTheList(index: index);
// remove from database
DatabaseProvider.db.delete(deleteShift.id).then((_) =>
BlocProvider.of<ShiftBloc>(context)
.add(DeleteShift(deleteShift.id)));
setState(() {
print('_controller.selectedDay = ${_controller.selectedDay}');
customEvents[_controller.selectedDay].removeAt(index);
});
showSnackBar(context);
},
child: Container(
width: MediaQuery.of(context).size.width,
padding: EdgeInsets.symmetric(horizontal: 10.0, vertical: 5.0),
child: Row(
children: <Widget>[
Expanded(
flex: 3,
child: customEvents[_controller.selectedDay][index],
),
Expanded(
flex: 1,
//fixme: catch when the user clicks back instead of submit when editing/ ading a shift
child: GestureDetector(
onTap: () {
Shift shift =
getParametersFromWidgetInTheList(index: index);
Navigator.push(
context,
MaterialPageRoute(
builder: (BuildContext context) => ShiftForm(
shift: shift,
),
),
);
},
child: Container(
child: Icon(FontAwesomeIcons.edit),
color: Colors.teal,
),
),
),
Expanded(
flex: 1,
//fixme: catch when the user clicks back instead of submit when editing/ ading a shift
child: GestureDetector(
onTap: () {
Shift deleteShift =
getParametersFromWidgetInTheList(index: index);
// remove from database
DatabaseProvider.db.delete(deleteShift.id).then((_) =>
BlocProvider.of<ShiftBloc>(context)
.add(DeleteShift(deleteShift.id)));
// remove from customEvents Map
setState(() {
customEvents[_controller.selectedDay].removeAt(index);
showSnackBar(context);
});
// after deleting the item I have to refresh the list
// calendarBuild(context, shiftList);
},
child: Container(
child: Icon(FontAwesomeIcons.trash),
color: Colors.red,
),
),
)
],
),
// ),
),
);
},
);
}
Here you can see what happens at the moment that I swipe to the left to delete an event. Also marked as yellow:
Found the problem!
It occurred on the following line:
key: Key(customEvents[_controller.selectedDay][index].toString()),
I only had to replace this with the following:
key: UniqueKey(),
The problem probably occurs within the Map.

How to update state from static method in Flutter?

I have a bottom nav bar that uses this code to switch widgets:
void _onItemTapped(int index) {
setState(() {
selectedIndexGlobal = index;
print(index);
});
}
And shows the needed widgets in body:
body: SafeArea(
child: new LayoutBuilder(
builder: (BuildContext context, BoxConstraints constraints) {
Container con = Container(
child: _widgetOptions.elementAt(selectedIndexGlobal)
);
return con;
}
))
I also have a GridView with static children list loaded from Firebase, and tried to apply a GestureDetector to it, to change selectedIndex and update screen state:
static getExpenseItems(AsyncSnapshot<QuerySnapshot> snapshot, BuildContext context) {
try {
snapshot.data.documents.sort((a, b) =>
a.data["order"].compareTo(b.data["order"]));
} on NoSuchMethodError {}
return snapshot.data.documents
.map((doc) =>
new GestureDetector(
behavior: HitTestBehavior.translucent,
onTap: () {
catName = doc["catName"];
setState(() {
selectedIndexGlobal = 4;
});
},
child: new Container(
child: Container(
padding: EdgeInsets.only(top: 15, left: 10, bottom: 15),
decoration: new BoxDecoration(
border: new Border.all(color: Colors.grey[200],
width: 0.8)),
child: Align(
alignment: Alignment.centerLeft,
child: Text(doc["name"],
style: TextStyle(fontSize: categoriesFont),
textAlign: TextAlign.center),
)
)
)
)
).toList();
}
But setState can't be called from a static method, as well as GridView.count doesn't let me use non-static widgets as child. What should I do then to update state on click?
This line will help you get the instance of the Widget State object you need.
All you need access to is the current context object:
_HomeState stateObject = context.findAncestorStateOfType<_HomeState>();
stateObject.setState(() {
_tabIndex = index;
});
For this to work, ensure that the context you use to access the State Object is from a child widget of the State object you are trying to find.