Can't change the status of checkbox in GridView Flutter with Mobx - flutter

I want to add a checkbox for GirdView in Flutter. The data was fetched from API request include attribute selected default is false. When I click on the checkbox of each Item it will change value is True and update on UI and I use Mobx to observe these change actions. When I debugging the values were changed but UI didn't update, I really don't know the reason. I added 2 pictures for UI and Mobx model below.
API:
{
"name": "HuynhDuy Phuc",
"birthday": "None",
"phone": "N/A",
"isSelected": false
},
{
"name": "Doan Phuc",
"birthday": "None",
"phone": "N/A",
"isSelected": false
},
{
"name": "Phuc Vu",
"birthday": "None",
"phone": "N/A",
"isSelected": false
},
final _userApiPresenter = Provider.of<UserApiPresenter>(context);
_userApiPresenter.fetchUsersList();
Observer(
name: 'ListHomePage',
builder: (BuildContext context) {
return (_userApiPresenter.userAPI != null)
? AnimationLimiter(
child: GridView.builder(
physics: BouncingScrollPhysics(),
padding: EdgeInsets.all(12),
addAutomaticKeepAlives: true,
//Determine the number of cells per row
gridDelegate:
new SliverGridDelegateWithFixedCrossAxisCount(
crossAxisCount: 3),
itemCount:
_userApiPresenter.userAPI.users.length,
itemBuilder: (context, index) {
User user =
_userApiPresenter.getUser(index: index);
return AnimationConfiguration.staggeredGrid(
position: index,
duration:
const Duration(milliseconds: 375),
columnCount: 2,
child: Container(
child: ScaleAnimation(
child: GestureDetector(
child: Stack(
children: <Widget>[
UserItem(
name: user.name,
type: user.name,
phone: user.phone,
birthday: user.birthday,
isSelected: user.selected,
),
Align(
alignment: Alignment.topRight,
child: Checkbox(
value: user.selected,
onChanged: (_) {
if(user.selected){
_userApiPresenter.changeStatusCheckBox(index: index);
} else{
_userApiPresenter.changeStatusCheckBox(index: index);
}
},
),
),
],
),
onTap: () {
Navigator.push(
context,
MaterialPageRoute(
builder:
(BuildContext context) =>
UserDetailPage(
index: index,
name: user.name,
),
),
);
},
),
),
),
);
},
),
)
: Center(
child: CircularProgressIndicator(),
);
},
)
UI
Mobx model

Observable information about users array and the user model itself is missing, but what you need to do(if already not) is:
Make the array of users observable as well - this way any addition, deletion, etc will results in update of the number of user boxes in the UI
Make property selected of User observable also - this way when certain user 'selected' state is effected, the UI will render the change
And something off topic:
You don't need #action attribute on getUser method, because this method is not updating any observable data
If this answer does not solve your problem, please provide implementation of userApi and User :)

You just missing one thing.
mobX does not update UI unless you tell it main variable changed..
To do so, just add the following line of code to your changeStatusCheckBox()
_userAPI = _userAPI;

Related

How to show json data in flutter carousel slider

I am creating a slider where I want to show json data in carousel slider the data will be coming from API ofcourse
What are the steps need for that can you guys guide me like have to create http method to first read data.
CarouselSlider(
options: CarouselOptions(
aspectRatio: 1.5,
viewportFraction: 0.95,
enlargeStrategy: CenterPageEnlargeStrategy.height,
enlargeCenterPage: true,
reverse: false,
enableInfiniteScroll: true,
autoPlay: true,
autoPlayAnimationDuration: const Duration(milliseconds: 900),
initialPage: initialPage,
onPageChanged: (index, reason) {
setState(() {
initialPage = index;
debugPrint('$initialPage');
});
}),
items: Category.categories
.map((category) => HeroCarouselCard(category: category))
.toList(),
),
This is something I was firstly doing Category.categories is another equatable class where I was storing static data.
[
{
"CarouselName": "Others",
"CarouselDescription": "A smartphone and tablet-based solution to register and report Factory Assembly Line Inspection information.",
"CarouselImage": "banner3.png"
},
{
"CarouselName": "Production",
"CarouselDescription": "Grow your business",
"CarouselImage": "banner1.jpg"
}
]
following is the json that I wanna use
If I get a complete example would be great for me dont't wanna use getx
The best and easy way is to create a future builder and in future pass the api call in my case I have used http request to fetch all the data.
Container(
margin: const EdgeInsets.symmetric(
horizontal: 10, vertical: 10),
width: MediaQuery.of(context).size.width,
child: FutureBuilder(
future: future,
builder: (context, snapshot) {
final items = snapshot.data;
switch (snapshot.connectionState) {
case ConnectionState.waiting:
return const Center(
child: RepaintBoundary(
child: HomeCardShimmerEffect()),
);
default:
if (snapshot.hasError) {
return Center(
child: Column(
children: [
const Text(
'Something went Wrong Please try again'),
const SizedBox(
height: 5,
),
ElevatedButton(
onPressed: () {
Navigator.push(
context,
MaterialPageRoute(
builder: (context) =>
const BottomNavBar()));
},
child: const Text('Try Again'))
],
));
} else {
return HomeCardWidget(items: items!);
}
}
}),
)
Step 2:
Is to use Carousel.Builder instead of Carousel Simple and pass it items builder
and else it will work same as the data you show in listview.builder or gridview.builder
Passing context and fetching data according to that.
Hopefully this will be helpfull.

StreamUnreadIndicator does not update and displays nothing getStream Api

I have had tough luck with the StreamUnreadIndicator() within the getStream API. I am trying to essentially have an indicator on the list tile for whenever a new message comes in. But nothing returns. I tried putting some debug prints to at least get the number of unread messages for the channel, but it is always 0.
Here's my message list view:
Widget _messagesList(List<dynamic>? messages, StreamChatClient client,
int messageCount, bool friendsTab) {
return ListView.separated(
keyboardDismissBehavior: ScrollViewKeyboardDismissBehavior.onDrag,
itemCount: messageCount,
itemBuilder: (context, index) {
//print("messaging:"+messages![index].channel);
return GestureDetector(
onTap: () {
Navigator.of(context).push(MaterialPageRoute(builder: (context) =>
MessageApi(
sourceType: SourceType.justMet,
receiverUser: friendsTab ? friends[index] : chatRequesters[index],
userName: userName,
channelId: messages![index].channel,
streamToken: streamToken,
client: StreamChatCore.of(context).client,
)
));
},
child: ListTile(
title: friendsTab ? Text(friends[index].firstName) : Text(chatRequesters[index].firstName),
subtitle: _buildLastMessage(messages![index].channel, client),
trailing: Column(
children: [
StreamUnreadIndicator(
cid: "messaging:"+messages[index].channel,
),
_buildLastMessageAt(messages[index].channel, client),
],
),
leading: CircleAvatar(
radius: 30,
backgroundImage: CachedNetworkImageProvider(
friendsTab ? friends[index].photoUrl : chatRequesters[index].photoUrl
),
),
),
);
},
separatorBuilder: (context, index) {
return const Divider();
},
);
}
Version 4.3 of Stream Chat Flutter introduced unreadMessagesSeparatorBuilder:
Try
StreamMessageListView(
unreadMessagesSeparatorBuilder: (context, unreadCount) =>
Text('$unreadCount unread message'),
)
See the changeling for additional details: https://pub.dev/packages/stream_chat_flutter/changelog
They seem to have updated their backend and now it works without me changing anything. I noticed they changed their docs recently too after this question.

Flutter : Popup for each Listtile

I am working on a flutter project and I want to popup to get generated on clicking a particular tile. This is my code
This is my ListTile generator
Future<Widget> getRecordView() {
print("405 name " + name.toString());
print(nameArr);
var items = List<Record>.generate(int.parse(widget.vcont), (index) => Record(
name: nameArr[index],
type: typeArr[index],
address: addressArr[index],
state: stateArr[index],
phone:phoneArr[index],
city: cityArr[index],
id: idArr[index],
));
print("Started");
var listItems = items;
var listview = ListView.builder(
itemCount: int.parse(widget.vcont),
itemBuilder: (context,index){
return listItems[index] ;
}
);
return Future.value(listview);
}
The Popup I need on tap :
Future <bool> details(BuildContext context,String type) {
return Alert(
context: context,
type: AlertType.success,
title: "Submission",
desc: type, //The parameter
buttons: [
DialogButton(
child: Text(
"OKAY",
style: TextStyle(color: Colors.white, fontSize: 20),
),
onPressed: () => Navigator.pop(context),
color: Color.fromRGBO(0, 179, 134, 1.0),
radius: BorderRadius.circular(0.0),
),
],
).show();
}
I tried to wrap Record with GestureDetector and Inkwell, but I only got errors and Android Studio tells me that Record is not expected in that context. I looked up in the internet and couldnt find anything on this matter. Please help.
Record, as far I can see is just a model, and not a widget. Item Builder requires a widget. You should wrap what you are passing to the item builder with an actual widget like a Container(), ListTile(), .. etc. These widgets can be wrapped with Gesture Detector to perform the pop ups you want.
It would look like this
var listview = ListView.builder(
itemCount: items.length,
itemBuilder: (context, index) {
return GestureDetector(
onTap: () {
// Tap on an item in the list and this will get executed.
},
// Return an actual widget, I'm using a ListTile here, but you can
// use any other type of widget here or a create custom widget.
child: ListTile(
// this will display the record names as list tiles.
title: Text(items[index].name),
),
);
},
);

Animated List not showing inserted item flutter

i have a SliverAnimatedList like this :
SliverAnimatedList(
key: _myListkey,
itemBuilder: (context, index, animation) {
return Container(
child: Column(
children: [
FlashcardCreateTile(
autocreate: autocreate,
entertomovefocus: entertomovefocus,
flashcard: flashcards[index],
islast:
(index + 1) == flashcards.length ? true : false,
plusmode: true,
promode: true,
uid: widget.uid,
focus: null,
animation: animation,
formKey: _formkey,
delete: () {
flashcards.removeAt(index);
SliverAnimatedList.of(context).removeItem(
index,
(context, animation) => FlashcardCreateTile(
autocreate: autocreate,
entertomovefocus: entertomovefocus,
flashcard:
Flashcard(recto: "", verso: ""),
islast: false,
plusmode: true,
promode: true,
uid: widget.uid,
focus: null,
animation: animation,
formKey: _formkey,
delete: () {},
add: () {}),
duration: const Duration(milliseconds: 100));
},
add: () {
int insertitem = index + 1;
print(insertitem);
setState(() {
flashcards.insert(
insertitem,
Flashcard(
recto: "",
verso: "",
mode: 0,
isrelearning: false,
easefactor: widget
.folder
.decklist[widget.deckindex]
.startingEase,
currentInterval:
Duration(microseconds: 0),
previousInterval:
Duration(microseconds: 0)));
SliverAnimatedList.of(context)
.insertItem(insertitem);
SliverAnimatedList.of(context).build(context);
});
},
),
Container(
child: (index + 1) == flashcards.length
? Container(
child: SizedBox(
height: 50,
),
)
: Container(),
)
],
),
);
},
initialItemCount: flashcards.length,
)
The flashcardcreatetile sends back the add function when i click and a button :
IconButton(
icon: Icon(
Icons.add,
color: Colors.red,
),
onPressed: widget.add)
Here's what it's doing :
As you can see, the item is indeed inserted, but the sliveranimatedlist only shows it when i scroll down and back up, so i presume it needs to rebuild itself..
I would like the new card to show directly, any ideas? Remove item is working fine by the way
You need to add a key to your item lists. I recommend you read this article as you will learn why do you need keys, what are they good for, and how to fix your problem.
You should add a key: Key(index) to your FlashcardCreateTile items to make them unique.
Flutter engine needs that to properly build your list

How to select index and delete it's respectively data from API in flutter?

I'm getting images from API and show them into grid view but the requirement is that I press long on any index of the image,a selected icon should be visible on that index image.
but the problem is that when I press long at any index, the selected icon is visible on all indexes.
ScreenShot:
to resolve this, I made model class, in which there are datatype
first is boolean variable(isSelected) for each index, another is for PhotoDetails which is fetching from API, but unable to handle it with FutureBuilder, because it rebuilds the build method when I performed setState and isSelected becomes false.
Code:
Model class:
class Photos{
PhotoDetail photoDetail;
bool isSelected;
Photos({this.photoDetail, this.isSelected});
}
FutureBuilder:
Expanded(
child: FutureBuilder<PhotoModel>(
future: _photoApi.getPhotosByUserList(
token: widget.tokenId,
contactId: widget.userContent.id,
),
builder:(BuildContext context, AsyncSnapshot<PhotoModel> snapshot){
if (!snapshot.hasData) {
return Center(child: CircularProgressIndicator());
}
if (snapshot.hasError){
return Center(child: new Text('Error: ${snapshot.error}'));
}
List<Photos> photos =[];
snapshot.data.content.forEach((element) {
photos.add(
Photos(
isSelected: false,
photoDetail: element
)
);
});
print("photos photos photos length:${photos.length}");
return photos.length>0?
sliverGridWidget(context,photos)
:Container(
alignment: Alignment.center,
child: Text("Empty"),
);
}
)
)
Images in grid view:
Widget sliverGridWidget(BuildContext context, List<Photos> listPhotoDetail){
return StaggeredGridView.countBuilder(
padding: const EdgeInsets.all(8.0),
crossAxisCount: 6,
itemCount: listPhotoDetail.length,
itemBuilder: (context, index){
return InkWell(
onLongPress: (){
setState(() {
enable = true;
print("iinnndexxxxxxx:$index");
// listPhotoDetail[index].isSelected = true;
});
},
child: Container(
alignment: Alignment.bottomRight,
decoration: BoxDecoration(
color:Colors.grey[100],
image: DecorationImage(
image: NetworkImage(listPhotoDetail[index].photoDetail.image.fileUrl),
fit: BoxFit.cover
)
),
child:enable?
Image.asset('assets/icons/selected.png')
:Container()
),
);
},
staggeredTileBuilder: (index)=> view ?StaggeredTile.count(6,6):StaggeredTile.count(2,2),
mainAxisSpacing: 8.0,
crossAxisSpacing:8.0,
);
}
To solve it try to use a specific key for every image