I have a ListView.builder showing a card for each of my images. On each card, I have an uploadFunction() and a SyncStatus. Syncstatus is enum with uploading, downloading, synced, and not synced. Now when I call uploadFunction(), the syncStatus changes to uploading and a circular progress indicator appears within the card. Now I want to show how much percentage is uploaded by passing a value to the CircularProgressIndicator(). This value resides in the uploadFunction(). How I can do so?
At first, I thought of creating a ValueNotifier set to value 0 for each card within the ListView.builder's itemBuilder and pass it to the uploadFunction(), which will change its value but I don't see any change in the UI.
This is my ListView.builder code:
ListView.builder(
physics: const AlwaysScrollableScrollPhysics(),
itemCount: documentsList.length,
itemBuilder: (BuildContext context, int index) {
return DocCard(
//I pass syncStatus value so that my DocCard can show whether the document is uploaded or not;
mySyncStatus: documentsList[index].syncStatus,
uploadDocumentOnPressed: () async {
if (documentsList[index].syncStatus ==
SyncStatus.notSynced) {
documentsList[index].syncStatus =
SyncStatus.uploading;
setState(() {});
Document myDoc;
myDoc = await databaseHelper.getDocumentValues(
docId: documentsList[index].docId);
//call uploadFunction
await uploadDocumentInCloud(myDoc: myDoc,).then((value) {
if (value) {
documentsList[index].syncStatus =
SyncStatus.synced;
setState(() {});
} else {
documentsList[index].syncStatus =
SyncStatus.notSynced;
setState(() {});
}
});
}
},
);
},
),
and this is my DocCard class code:
class DocCard extends StatefulWidget {
DocCard({
#required this.uploadDocumentOnPressed,
#required this.mySyncStatus,
});
final Function uploadDocumentOnPressed;
final SyncStatus mySyncStatus;
#override
State<DocCard> createState() => _DocCardState();
}
class _DocCardState extends State<DocCard> {
#override
Widget build(BuildContext context) {
return Card(
color: Colors.white,
child: Row(
children: [
IconButton(
onPressed: widget.uploadDocumentOnPressed,
icon: const Icon(
Icons.cloud_download_outlined,
color: Colors.blue,
),
),
syncStatusWidget(),
],
),
);
}
Widget syncStatusWidget(){...}
}
The syncStatusWidget() returns a widget according to the value of syncStatus passed:
Widget syncStatus() {
switch (widget.mySyncStatus) {
case SyncStatus.uploading:
{
return Container(
height: 20,
width: 20,
child: const CircularProgressIndicator(
color: Colors.white,
strokeWidth: 1,
),
);
}
break;
case SyncStatus.synced:
{
return const SizedBox.shrink();
}
break;
case SyncStatus.notSynced:
{
return notSyncedIcon;
}
break;
case SyncStatus.downloading:
{
return Container(
height: 20,
width: 20,
child: const CircularProgressIndicator(
color: Colors.white,
strokeWidth: 1,
),
);
}
break;
}
}
and the uploadFucntion has the total progress of the downloading or the uploading.
Related
I am getting data from the network. I display a list with data. I have the ability to remove one element from a list using the removeInfo method. When deleting, the element does not disappear from the list, but it is deleted. How can I make sure that when one element is removed from the list, the entire page is not updated for me, but just the element is deleted? If I chose to delete - so that the element disappears and there is no need to refresh the page to see the changes.
body
BlocBuilder<InfoCubit, InfoState>(
builder: (context, state) {
final InfoCubit infoCubit =
BlocProvider.of<InfoCubit>(context);
if (state is InfoLoaded) {
return SizedBox(
height: MediaQuery.of(context).size.height,
child: Padding(
padding: const EdgeInsets.only(top: 40, left: 14, right: 14),
child: MediaQuery.removePadding(
context: context,
removeTop: true,
child: ListView.builder(
physics: const BouncingScrollPhysics(),
itemCount: state.info.length + 1,
itemBuilder: (context, index) {
return Padding(
padding: const EdgeInsets.only(bottom: 16),
child: GestureDetector(
onTap: () {},
child: Container(
height: 112,
padding: const EdgeInsets.symmetric(
horizontal: 14, vertical: 10),
decoration: BoxDecoration(
borderRadius: BorderRadius.circular(25),
color: constants.Colors.greyMiddle,
),
child: Row(
children: [
IconButton(
onPressed: () {
if (state.info[index].status ==
true) {
showDialog(
context: context,
builder: (BuildContext context) {
return const PendingDialog();
},
);
} else if (state.info[index].status ==
null &&
!state.info[index].blocked) {
ShowCustomDialog()
.showBorderDialog(
barrierDismissible: false,
context: context,
child:
const DeleteInfoDialog(),
)
.then((value) async {
if (value) {
await infoCubit
.removeInfo(
infoId:
state.info[index].id,
userId: widget.userId,
)
.then((value) {
if (value) {
deleteNotification(
context);
}
});
}
});
} else {
try {
setState(() {
state.info[index].selected =
!state.info[index]
.selected;
});
} catch (_) {}
}
},
icon: state.info[index].selected
? SvgPicture.asset(
constants.Assets.remove2)
: state.info[index].status ==
true
? SvgPicture.asset(
constants.Assets.pending,
)
: state.info[index]
.status ==
false
? !state.info[index]
.blocked
? SvgPicture.asset(
constants
.Assets.remove,
color: constants
.Colors
.greyLight,
)
: SvgPicture.asset(
constants.Assets
.threeDot,
)
: SvgPicture.asset(
constants
.Assets.threeDot,
),
padding: EdgeInsets.zero,
constraints: const BoxConstraints(),
),
],
),
),
),
);
),
),
),
);
// }
}
return const Center(
child: CircularProgressIndicator(
color: constants.Colors.purpleMain,
),
);
},
),
state
#immutable
abstract class InfoState {}
class InfoInitial extends InfoState {}
class InfoLoading extends InfoState {}
class InfoLoaded extends InfoState {
final List<InfoModel> info;
InfoLoaded(this.info);
}
class InfoDeletedSuccess extends InfoState {
final int infoId;
InfoDeletedSuccess(this.infoId);
}
class InfoError extends InfoState {}
cubit
class InfoCubit extends Cubit<InfoState> {
final InfoRepository _repository;
InfoCubit(this._repository) : super(InfoInitial());
Future loadPage(int userId) async {
try {
emit(InfoLoading());
List<InfoModel> info=
await _repository.getInfo(userId: userId);
emit(InfoLoaded(info));
} catch (_) {
emit(InfoError());
}
}
Future<bool> removeChargingStation(
{required int infoId, int? userId}) async {
try {
await _repository.removeInfo(
infoId: infoId);
if (userId != null) {
loadPage(userId);
}
// emit(InfoDeletedSuccess(chargingStationId));
return true;
} catch (_) {
return false;
}
}
void reload() => emit(StationSwitcherInitial());
}
I am trying to update a GridView.builder with setState and when the change is triggered, the actual state is updated behind the scene but the GridView.builder does not change until I manually refresh the app.
The setState function triggers and I have tested this. Here is the code:
import 'package:flutter/material.dart';
import 'image_display_card.dart';
void main() {
runApp(MaterialApp(
home: ChooseScreen(),
));
}
class ChooseScreen extends StatefulWidget {
#override
State<ChooseScreen> createState() => _ChooseScreenState();
}
class _ChooseScreenState extends State<ChooseScreen> {
DisplayCard? currentSelectedCard;
// The List of image cards
List<DisplayCard> baseDisplayList = [];
// These are dummy images I added
List<Image> listOfInitialImages = const [
Image(image: AssetImage('images/testing_stock/stickFigureMale.png')),
Image(image: AssetImage('images/testing_stock/stickFigureFemale.png')),
Image(image: AssetImage('images/testing_stock/gown.png')),
Image(image: AssetImage('images/testing_stock/hat1.png')),
];
#override
void initState() {
super.initState();
baseDisplayList = [
for (int i = 0; i < listOfInitialImages.length; i++)
DisplayCard(
picture: listOfInitialImages[i],
onCardSelected: () {
setCardToSelected(i);
},
),
];
}
/// unselect the previous selected card and
/// set currentSelectedCard to the new selected card.
setCardToSelected(int index) {
if (currentSelectedCard != null) {
currentSelectedCard!.selectOrUnselect(false);
}
print('triggered');
// set the new selected card.
currentSelectedCard = baseDisplayList[index];
currentSelectedCard!.selectOrUnselect(true);
print('triggered again');
}
#override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: Text('Choose picture'),
),
body: GridView.builder(
gridDelegate: SliverGridDelegateWithFixedCrossAxisCount(
crossAxisCount: 2,
),
itemCount: baseDisplayList.length,
itemBuilder: (BuildContext context, int index) {
return baseDisplayList[index];
},
),
);
}
}
And this is display_card.dart:
import 'package:flutter/material.dart';
class DisplayCard extends StatelessWidget {
final Image picture;
final onCardSelected;
bool isSelected;
// TODO: Implement color change on selected picture
DisplayCard({
this.isSelected = false,
this.onCardSelected,
this.picture = const Image(
image: AssetImage('images/testing_stock/stickFigureMale.png')),
});
selectOrUnselect(bool choice) {
isSelected = choice;
}
#override
Widget build(BuildContext context) {
return GestureDetector(
onTap: onCardSelected,
child: Container(
height: 200,
margin: const EdgeInsets.all(5),
decoration: BoxDecoration(
color: Colors.grey[300],
image: DecorationImage(
image: picture.image,
colorFilter: ColorFilter.mode(
Colors.black.withOpacity(isSelected ? 0.4 : 0.0),
BlendMode.srcOver
),
fit: BoxFit.cover,
),
),
),
);
}
}
I've edited it to contain only the necessary code to replicate the error. Sorry for before.
The state of baseDisplayList is updated but the GridView.builder is not rebuilt.
Ragarding how I know the GridView.builder isn't rebuilt, I tried changing the backgroundColor of the scaffold in the setCardToSelected function and the background color changed but the GridView didn't change. But when I refresh the app immediately afterwards, the GridView updated.
Please, how can I make the GridView.builder update when the setCardToSelected is called?
Thank you. I appreciate your help.
I tried using the provider package but I still got the same problem.
It appears you have to build each individual item inside the GridView.builder for it to rebuild if there are any changes.
Here is the solution I found to the problem.
This is main.dart:
import 'image_display_card.dart';
void main() {
runApp(MaterialApp(
home: ChooseScreen(),
));
}
class ChooseScreen extends StatefulWidget {
#override
State<ChooseScreen> createState() => _ChooseScreenState();
}
class _ChooseScreenState extends State<ChooseScreen> {
Image? currentlySelectedImage;
// The List of image cards
List<Image> baseImageList = [];
// The list of image card states
List<bool> baseStateList = [];
// These are dummy images I added
List<Image> listOfInitialImages = const [
Image(image: AssetImage('images/testing_stock/stickFigureMale.png')),
Image(image: AssetImage('images/testing_stock/stickFigureFemale.png')),
Image(image: AssetImage('images/testing_stock/gown.png')),
Image(image: AssetImage('images/testing_stock/hat1.png')),
];
initialize() {
for (int i = 0; i < listOfInitialImages.length; i++) {
baseImageList.add(listOfInitialImages[i]);
baseStateList.add(false);
}
}
#override
void initState() {
super.initState();
initialize();
}
/// Changes the currentlySelectedImage and the color of
/// the chosen card by setting all values in the baseStatesList
/// to false, then assigning only the chosen one to true.
setToSelectedImage(int index) {
currentlySelectedImage = baseImageList[index];
setState(() {
baseStateList.setAll(
0, [for (int i = 0; i < baseStateList.length; i++) false]);
baseStateList[index] = true;
});
}
#override
Widget build(BuildContext context) {
return Scaffold(
body: GridView.builder(
gridDelegate: SliverGridDelegateWithFixedCrossAxisCount(
crossAxisCount: 2,
),
itemCount: baseImageList.length,
itemBuilder: (BuildContext context, int index) {
return DisplayCard(
picture: baseImageList[index],
isSelected: baseStateList[index],
onCardSelected: () {
setToSelectedImage(index);
},
);
},
),
);
}
}
The DisplayCard class remains the same except that the selectOrUnselect method is no longer needed.
The state of the gridview's contents actually update with setState if they are defined individually inside the gridview.
This method is also shorter. Thanks for the help, guys.
In my case I used a list of DataRecord types and then used setState to assign the lstData to the list of DataRecords in my model. The GridView uses the itembuilder receiving lstData as a parameter to build the GridTiles.
class DataRecord {
String WordValue;
String State;
int Index;
DataRecord(this.WordValue, this.State, this.Index);
}
class _Test_SinglePageState extends State<Test_SinglePage> {
List<DataRecord> lstData = [];
Widget _itemBuilder(BuildContext context, int index,
ConcentrationMonitor model, List<DataRecord> lstData) {
model.lstData = lstData;
return MouseRegion(
cursor: SystemMouseCursors.click,
child: GestureDetector(
onTap: () {
if (StaticVar.index1 != 0 && StaticVar.index2 != 0) {
_stopWatchTimer.onExecute.add(StopWatchExecute.reset);
_stopWatchTimer.onExecute.add(StopWatchExecute.start);
}
model.checkForMatch(index);
setState(() {
lstData = model.lstData;
});
}
,
child: Container(
color: Colors.grey,
child: GridTile(
child: Visibility(
visible: model.lstData[index].State == "Show" ||
model.lstData[index].State == "TempShow"
? true
: false,
child: Center(
child: Column(children: [
Text(
model.lstData[index].WordValue,
style: TextStyle(
fontSize: 20,
color: model.lstData[index].State == "Show"
? Colors.purple
: Colors.black),
),
Text(model.lstData[index].Index.toString(),
style: Theme.of(context).textTheme.headline5)
//Text(model.lstData[index].State,
//style: Theme.of(context).textTheme.headline5)
])))))));
}
Expanded(
child: SizedBox(
height: 400,
width: MediaQuery.of(context).size.width,
child: GridView.builder(
gridDelegate:
const SliverGridDelegateWithMaxCrossAxisExtent(
maxCrossAxisExtent: 120,
childAspectRatio: 3 / 2,
crossAxisSpacing: 20,
mainAxisSpacing: 20),
itemCount: lstData.length,
itemBuilder: (ctxt, Index) =>
_itemBuilder(ctxt, Index, model, lstData))))
I have an appBar with one icon, this icon has a number which have to be updated after I change somethings in the app. I was using notifyListeners(), but this command is cleaning a list I need so I have to update that number in appbar without notifyListeners().
I tried to call SetState but it doesn't worked.. is there a way to update only the app bar?
In provider which I include more items:
void setBadge() {
_number = number;
notifyListeners(); // this line I dropped out
}
App bar Icon widget:
class AppBarWidget extends StatelessWidget {
#override
Widget build(BuildContext context) {
return Consumer<Cart>(
child: IconButton(
icon: Icon(Icons.shopping_bag_outlined),
onPressed: () async {
Navigator.of(context).pushNamed(ROUTE_CART);
},
),
builder: (_, cart, child) {
return BagBadge(
child: child,
value: cart.isEmpty ? '' : cart.number.toString(),
);
},
);
}
}
BagBadge:
class BagBadge extends StatelessWidget {
final Widget child;
final String value;
BagBadge({
#required this.child,
#required this.value,
});
#override
Widget build(BuildContext context) {
return Stack(
alignment: Alignment.center,
children: <Widget>[
child,
if (value != '')
Positioned(
right: value.length < 4 ? 20 : 10,
top: 30,
child: Container(
padding: EdgeInsets.all(value.length < 4 ? 2 : 3),
decoration: BoxDecoration(
borderRadius: BorderRadius.circular(10),
color: Theme.of(context).accentColor
),
constraints: BoxConstraints(
minHeight: 16,
minWidth: 16,
),
child: Text(
value,
textAlign: TextAlign.center,
style: TextStyle(
fontSize: 12,
fontWeight: FontWeight.bold,
color: Colors.white,
),
),
)
],
);
}
}
Edit: this would work only if you use a stateful widget. With stateless widget the change won't be shown.
You can try something like this:
import 'package:flutter/material.dart';
class AppBarWidget extends StatefulWidget {
#override
_AppBarWidgetState createState() => _AppBarWidgetState();
}
class _AppBarWidgetState extends State<AppBarWidget> {
int _appBarValue = 0;
#override
Widget build(BuildContext context) {
return Consumer<Cart>(
child: IconButton(
icon: Icon(Icons.shopping_bag_outlined),
onPressed: () async {
Navigator.of(context).pushNamed(ROUTE_CART);
},
),
builder: (_, cart, child) {
return BagBadge(
child: child,
value: _appBarValue == 0 ? '' : '$appBarValue',
);
},
);
}
}
setAppBarValue(int value) {
setState(() { _appBarValue = value; });
}
}
Whenever you want to change the value, just call the setAppBarValue() function.
I have a flutter grid view with multiple selection.
Each time I select an item, it goes inside the SelectedList and i can see a blue tick on each element.
But each time I add a new element, I update the the list and the Consumer receive the notification, I can see the new elements but I lost all the previous selected item.
Only the GridItemCustom is impacted for the CustomExercises.
Does someone has an idea, on how to keep the previous selected elements?
it look like that once the new list is updated, i have to check if the image has been selected or not..
In the video, I select 'Superman' and then add 'Test145', then I lost the selected item 'Superman'...
Future<void> updateOnceCustomExercisesList() async {
return this._memoizer.runOnce(() async {
List<ExerciseItem> newList = await dbHelper!
.findCustomExercises(widget.status == "cooldown" ? true : false);
exerciseLoader.updateList(newList); -> does nofify ExerciseLoader Consumer
});
}
Text('Custom Exercises'),
FutureBuilder(
future: updateOnceCustomExercisesList(),
builder:
(BuildContext context, AsyncSnapshot<void> snapshot) {
if (snapshot.hasError) {
print("ERROR\n");
}
switch (snapshot.connectionState) {
case ConnectionState.done:
return Container();
default:
return buildLoadingScreen();
}
},
),
Consumer<ExerciseLoader>(
builder: (context, customExercises, child) =>
GridView.builder(
shrinkWrap: true,
physics: ScrollPhysics(),
itemCount:
customExercises.getCustomExercises().length,
gridDelegate:
SliverGridDelegateWithFixedCrossAxisCount(
crossAxisCount: 4,
childAspectRatio: 0.56,
crossAxisSpacing: 2,
mainAxisSpacing: 2),
itemBuilder: (context, index) {
return GridItemCustom(
item: customExercises
.getCustomExercises()
.elementAt(index),
isSelected: (bool value) {
setState(() {
if (value) {
widget.selectedList.add(customExercises
.getCustomExercises()
.elementAt(index));
} else {
widget.selectedList.remove(customExercises
.getCustomExercises()
.elementAt(index));
}
});
print("$index : $value");
},
key: Key(customExercises
.getCustomExercises()
.elementAt(index)
.uniqueKey
.toString()));
}),
),
My GridCustomItem is like:
class GridItemCustom extends StatefulWidget {
final Key key;
final ExerciseItem item;
final ValueChanged<bool> isSelected;
GridItemCustom(
{required this.item, required this.isSelected, required this.key});
String get2FirstLetters(String str) {
String initial = "";
List<String> words = str.split(" ");
for (int i = 0; i < words.length; i++) {
initial += words[i].substring(0, 1);
}
return initial.toUpperCase();
}
#override
_GridItemCustomState createState() => _GridItemCustomState();
}
class _GridItemCustomState extends State<GridItemCustom> {
bool isSelected = false;
#override
void initState() {
super.initState();
}
#override
Widget build(BuildContext context) {
return InkWell(
onTap: () {
setState(() {
isSelected = !isSelected;
widget.isSelected(isSelected);
});
},
child: Column(
children: <Widget>[
Stack(alignment: Alignment.bottomRight, children: <Widget>[
CircleAvatar(
backgroundColor: Colors.black.withOpacity(isSelected ? 0.9 : 0),
child: Text(widget.get2FirstLetters(widget.item.title)),
),
isSelected
? Align(
alignment: Alignment.bottomRight,
child: Padding(
padding: const EdgeInsets.all(2.0),
child: Icon(
Icons.check_circle,
color: Colors.blue,
)),
)
: Container(),
]),
SizedBox(height: 10),
Text(
widget.item.title,
style: TextStyle(
color: Colors.orange,
fontFamily: 'LibreBaskerville',
fontSize: 10),
),
//: Container()
],
),
);
}
}
Thanks for your time
When I'm trying to remove item(like 0 index item) from this listview using provider it removed the last item from the list. while I'm deleting last element from the list successfully remove last item. I'm bit confusing why this kind of problem is happening to me.
Here I'm posting some code please check give your valuable suggestion. Also demonstrate on this video what issue is happening
Link:https://drive.google.com/file/d/1UYl8Z7vEj_tZCaYzqe0VqZL2iMla5nIZ/view?usp=sharing
Expected result: Whenever user press delete button then delete that particular row(item).
Delete method:- This is the delete method It'll be call when user press delete button from the list.
Future<void> acceptdeclinerequest(String requestStatus,int requestId) async{
String token = await CustomPreferences.getpreferences('token');
Map<String, String> requestHeaders;
if (token.isNotEmpty) {
requestHeaders = {
'Accept': 'application/json',
'Authorization': 'Bearer ' + token
};
} else {
requestHeaders = {
'Accept': 'application/json',
};
}
var reqdata = {
"request_id":requestId.toString(),
"status":requestStatus
};
print('accept request data is $reqdata');
try
{
final response =
await http.post(Connection.url + 'respond-place-request', headers: requestHeaders,body: reqdata);
if (response.statusCode == 200) {
Map<String, dynamic> responseJson = json.decode(response.body);
final existingProductIndex = _items.indexWhere((prod) => prod.id == requestId);
var existingProduct = _items[existingProductIndex];
_items.removeAt(existingProductIndex);
notifyListeners();
return responseJson;
} /*else if (response.statusCode == 500) {
return servererrorresponse;
}*/
} catch (exception) {
throw exception;
}
}
Main Widget class: This is the main widget class where I define Listview widget. I've used provider to get data from api which is written in modal class to populate in Listview and Listview child widgets is in seprate class which is RequestWidgets. In this class I've passed rowitems data to show in listview.
GlobalKey<ScaffoldState> _scaffoldKey = GlobalKey<ScaffoldState>();
var connectionstatus;
var product;
var _isInit = true;
var _isLoading = false;
#override
void initState() {
super.initState();
}
#override
void didChangeDependencies() {
// TODO: implement didChangeDependencies
if (_isInit) {
setState(() {
_isLoading = true;
});
Provider.of<BandRequestModal>(context).getBandRequestList().then((_) {
setState(() {
_isLoading = false;
});
});
}
_isInit = false;
super.didChangeDependencies();
}
#override
Widget build(BuildContext context) {
connectionstatus = Provider.of<ConnectivityResult>(context);
product = Provider.of<BandRequestModal>(context, listen: false);
// getRequestData();
return WillPopScope(
onWillPop: _onWillPop,
child: Scaffold(
key: _scaffoldKey,
appBar: CustomAppbar(
_scaffoldKey, Constants.requests, 100.0, filterRecord),
endDrawer: MenuDrawer(),
body:
/*(connectionstatus == ConnectivityResult.wifi ||
connectionstatus == ConnectivityResult.mobile)
? */
Consumer<BandRequestModal>(builder: (context, modal, child) {
return !_isLoading
? Container(child: LayoutBuilder(builder:
(BuildContext context, BoxConstraints constraints) {
return Container(
height: constraints.maxHeight,
child: modal.item.length > 0
? ListView.builder(
padding:
EdgeInsets.only(top: 10.0, bottom: 0.0),
itemCount: modal.item.length,
shrinkWrap: true,
// physics: NeverScrollableScrollPhysics(),
itemBuilder: (context, int i) {
return RequestWidgets(data: modal.item[i]);
})
: Center(
child: new Text(
Constants.norecordfound,
style: TextStyle(
fontSize: 20.0,
fontWeight: FontWeight.bold),
),
),
// ],
// ),
);
}))
: Comman.loadingIndicator(Theme.of(context).primaryColor);
})
// : Comman.nointernetconnection(context)
// FutureBuilder<BandRequestModal>(
// future: Connection.bandRequestList(),
// builder: (context, snapshot) {
// switch (snapshot.connectionState)
// {
// case ConnectionState.none:
// break;
// case ConnectionState.waiting:
// return Comman.loadingIndicator(
// Theme.of(context).primaryColor);
// break;
// case ConnectionState.active:
// break;
// case ConnectionState.done:
// if (snapshot.hasError) {
// return Center(
// child: new Text(Constants.servererror),
// );
// }else if(snapshot.data==null){
// return Center(
// child: new Text(Constants.servererror),
// );
// } else if (snapshot.data.data.length == 0) {
// return Center(
// child: new Text(
// Constants.norecordfound,
// style: TextStyle(
// fontSize: 20.0, fontWeight: FontWeight.bold),
// ),
// );
// } else {
// return ListView.builder(
// padding:
// EdgeInsets.only(top: 10.0, bottom: 60.0),
// itemCount: snapshot.data.data.length,
// shrinkWrap: true,
// physics: NeverScrollableScrollPhysics(),
// itemBuilder: (context, int i) {
// return RequestWidgets(data:snapshot.data.data[i]);
// });
// }
// break;
// }
// }):Comman.nointernetconnection(context)
));
}
Child widget class: This is the row items class of listview In this class we used many widgets to show place data.
class _RequestWidgetsState extends State<RequestWidgets> {
var getData;
var product;
#override
void initState() {
// TODO: implement initState
getData = widget.data;
super.initState();
}
#override
Widget build(BuildContext context) {
product = Provider.of<BandRequestModal>(context, listen: false);
return Container(
// alignment: Alignment.topLeft,
margin: EdgeInsets.only(top: 5.0),
child: ListTile(
// contentPadding: EdgeInsets.zero,
key: ObjectKey(getData),
leading: CircleAvatar(
radius: 30,
backgroundColor: Colors.transparent,
child: ClipOval(
child: (getData.placeDetails.image != null &&
getData.placeDetails.image != '')
? Image.network(
getData.placeDetails.image,
width: 90,
height: 90,
fit: BoxFit.cover,
)
: Image.asset(
Res.defaultImage,
width: 90,
height: 90,
fit: BoxFit.cover,
)),
),
title: Row(
mainAxisAlignment: MainAxisAlignment.start,
children: <Widget>[
Expanded(
child: Text(getData.placeDetails.name,
style: TextStyle(
fontSize: 16.0,
fontFamily: 'Metropolis',
color: CustomColors.commentTitleColor))),
],
),
subtitle: Container(
margin: EdgeInsets.only(top: 1.0),
child: Column(children: <Widget>[
Container(
margin: EdgeInsets.only(top: 1.0),
child: Row(children: <Widget>[
Expanded(
child: Text(getData.placeDetails.address,
style: TextStyle(
fontSize: 15.0,
height: 1.2,
fontFamily: 'Metropolis',
color: CustomColors.commentSubtitleColor))),
]),
),
Container(
margin: EdgeInsets.only(top: 15.0, bottom: 15.0),
child: Row(
mainAxisAlignment: MainAxisAlignment.end,
children: <Widget>[],
)),
Divider(
color: CustomColors.commentlineColor,
thickness: 0.8,
)
])),
trailing: Row(
mainAxisSize: MainAxisSize.min,
children: <Widget>[
GestureDetector(
child: CircleAvatar(
radius: 20,
backgroundColor: Colors.green,
child: Icon(
Icons.check,
color: Colors.white,
),
),
onTap: () {
acceptrejectpopup('1');
// {
// print('accept data $data');
// Comman.hideLoading(context);
// Comman.showSnakBar(data['message'],context);
// });
},
),
SizedBox(
width: 15.0,
),
GestureDetector(
child: CircleAvatar(
backgroundColor: Colors.red,
child: Icon(
Icons.clear,
color: Colors.white,
),
),
onTap: () {
// Comman.showLoading(context);
acceptrejectpopup('0');
/*product.acceptdeclinerequest('0',getData.id.toString()).then((data){
print('decline data $data');
Comman.hideLoading(context);
Comman.showSnakBar(data['message'],context);
});*/
},
)
],
),
),
);
}
//accept and reject
void acceptRejectRequest(String requestStatus) async {
try {
var response =
await product.acceptdeclinerequest(requestStatus, getData.id);
if (response['status'] == Constants.status_true) {
Comman.hideLoading(context);
Comman.showSnakBar(response['message'], context);
// setState(() {});
} else {
Comman.hideLoading(context);
}
} catch (exception) {
Comman.hideLoading(context);
Comman.showSnakBar(Constants.servererror, context);
}
}
//request accept/reject popup
Future<void> acceptrejectpopup(String reqStatus) {
return showDialog(
context: context,
builder: (context) => new AlertDialog(
title: new Text('Alert!',
style: TextStyle(color: Colors.black, fontWeight: FontWeight.bold)),
content: new Text(reqStatus == '1'
? Constants.reqAcceptmessage
: Constants.reqRejectemessage),
actions: <Widget>[
new FlatButton(
onPressed: () => Navigator.of(context).pop(),
child: new Text(Constants.notxt),
),
new FlatButton(
onPressed: () {
Navigator.of(context).pop();
Comman.showLoading(context);
acceptRejectRequest(reqStatus);
},
child: new Text(Constants.yestxt),
),
],
),
);
}
The provider is working just fine, the problem is when the provider notify the consumer the ListView updates the children, but the StatefulWidget check they're the same type (They're all RequestWidget) so they just update themselves (if you don't provide a key to the StatefulWidget they will try to check if they're the same element and update via the didChangeDependencies method), but you're updating the getData var in initState (which will call only once) so even if the consumer updates the value won't. Try it like this
#override
void initState() {
// TODO: implement initState
//getData = widget.data; not here
super.initState();
}
#override
void didChangeDependencies() {
// TODO: implement initState
getData = widget.data; //update it here
super.didChangeDependencies();
}
Other option would be just to give a specific key when building your widget in the itemBuilder so when the consumer updates it changes them accordingly
return RequestWidgets(key: ValueKey(modal.item[i].id),data: modal.item[i]);
// Or some value unique for each item
The problem here I guess is, in your Child widget class, since I can't see any requestId of the selected card being passed to the acceptdeclinerequest().
Your acceptdeclinerequest() expects two unique arguments to be passed when called:
String requestStatus
int requestId
If you look closely into the Child widget class, you are just passing requestStatus. I wonder from where are you getting this getData.id, and how is it identifying that some particular card is selected.
// look here, only requestStatus is being passed
onTap: () {
acceptrejectpopup('0');
}
// and here
onTap: () {
acceptrejectpopup('1');
}
And in your acceptRejectRequest, you are only passing requestStatus
acceptRejectRequest(reqStatus);
And then you call your acceptdeclinerequest() with this data
// machine is confused, where are we getting the getData.id
// it assumes the id as per it's need, hence the error
await product.acceptdeclinerequest(requestStatus, getData.id);
The machine is trying to figure out, which element you selected. Try to give the id from the selected card, and pass it to the method with correct getData.id of that particular element.
Suggestion: Pass in your id of the selected card when you are tapping on it, and then call your methods, and then pass it along to get the right requestId and remove it. Let your methods acceptrejectpopup() and acceptRejectRequest() accept the id of the selected item and then finally pass it to your acceptdeclinerequest()
// first step
onTap: () => acceptrejectpopup('0', your_card_reuqest_id);
// second step, pass data from the above popup method to acceptRejectRequest()
acceptRejectRequest(reqStatus, requestId);
//finally from acceptRejectRequest(reqStatus, requestId), pass it to the final method acceptdeclinerequest
acceptdeclinerequest(requestStatus, requestId);