I want to change the status of checkbox via use Mobx, after the click the value have changed but not update on UI, Observer don't see change action that it is rebuilding this Widget
In file Mobx:
#observable
List<bool> statusCheckbox = List<bool>();
#computed
List<bool> get getStatusCheckBox {
return statusCheckbox;
}
#action
changeStatusItem(bool val, int index) {
statusCheckbox[index] = val;
}
In file contain Widget:
Observer(
name: 'ListHomePage',
builder: (BuildContext context) {
return child: GridView.builder(
gridDelegate:
new SliverGridDelegateWithFixedCrossAxisCount(
crossAxisCount: 3,
childAspectRatio: (itemWidth / itemHeight),
),
itemCount:
_userManagementStore.userAPI.users.length,
itemBuilder: (context, index) {
UserManagement user =
_userManagementStore.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>[
Align(
alignment: Alignment.topRight,
child: Checkbox(
value: _userManagementStore.getStatusCheckBox[index],
onChanged: (bool val) {
_userManagementStore.changeStatusItem(val, index);
},
),
),
],
),
},
)
I can't find the root cause of the issue
As said Antoan Elenkov, you should remove the #computed property.
But you main problem is that you should not using a List but an ObservalbleList, it works like a List.
So, you have to replace
List<bool> statusCheckbox = List<bool>();
by
#observable
ObservableList<bool> statusCheckbox = ObservableList();
Related
i have a list of items coming from firebase firestore collection in gridview builder. i need to give bordercolor to only the selected items(like favourite icon , when user clicks,color of the selected icon from list changes.like wise i need to change the border color).using Obx Getx.
here is my getx controller page
class PetSellController extends GetxController{
final petType = <SaleModel>[].obs;
final isClicked = false.obs;
final selectedIndex = 0.obs;
#override
void onInit() async {
petType.bindStream(listType());
super.onInit();
}
Stream <List<SaleModel>> listType() {
Stream <QuerySnapshot> stream =
FirebaseFirestore.instance.collection('pet_category').snapshots();
return stream.map((qShot) => qShot.docs
.map((doc) => SaleModel(
image: doc['title'],
icon: doc['pet_icon'],
title: doc['title']))
.toList());
}
}
below is the part where i call the obx
Obx(()=> GridView.builder(
physics: const ScrollPhysics(),
padding: const EdgeInsets.symmetric(horizontal:15,vertical: 10),
shrinkWrap: true,
gridDelegate: const SliverGridDelegateWithFixedCrossAxisCount(
crossAxisCount: 2,
crossAxisSpacing: 15,
mainAxisSpacing: 15,
mainAxisExtent: 100
),
itemCount:controller.petType.length,
itemBuilder: (BuildContext context, int index) {
return GestureDetector(//////////here is ontap function
onTap: (){
controller.isClicked.value =! controller.isClicked.value;
print(controller.isClicked);
},
child: Container(
decoration: BoxDecoration(//////// here is where i put condition
border: controller.isClicked.value == false? Border.all(
width: 3,
color: Colors.black,
): null,
borderRadius: BorderRadius.circular(10),
color: AppColor.surface
),
child: Column(
mainAxisAlignment: MainAxisAlignment.spaceAround,
children: [
Container(
height:maxHeight*.1,
decoration: BoxDecoration(
image: DecorationImage(
image:NetworkImage(controller.petType[index].icon))
),
),
Text(controller.petType[index].title),
],
),
),
);
},
)
)
the value of onClicked is changing when i click on a item.i can see this in debug console using print statement.But UI only changes after hot reload.
If you only plan to change the border colour of the GridView item, you can move the Obx down to the itemBuilder. Simply wrap the Container inside the GestureDetector with an Obx.
itemBuilder: (BuildContext context, int index) {
return GestureDetector(
onTap: () {
// change your variable here
},
child: Obx(() => Container(//your code here))
);
}
I'm trying to display items based on the selected category but I'm not finding the right way to do that.
I suppose the id of the category need to match the categoryId of the item but I'm not getting there.
Here the code for the backend_api:
Future<List<Item>> fetchItems(
{int? categoryId,
String? zipcode,
String? searchText,
String? radius}) async {
var path =
categoryId != null ? "/item/list/category/$categoryId" : "/item/list";
path += zipcode != null ? "/zipcode/$zipcode" : "";
path += "?";
if (searchText != null) {
path += "&search=$searchText";
}
if (radius != null) {
path += "&radiusInKm=$radius";
}
final http.Response response = await _httpClient.get(path);
return jsonDecode(utf8.decode(response.bodyBytes))
.map<Item>((json) => Item.fromJson(json))
.toList();
}
Here the code for displaying the items:
return Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
SingleChildScrollView(
scrollDirection: Axis.horizontal,
physics: const ClampingScrollPhysics(),
child: Row(
children: [
BlocBuilder<ItemCategoriesBloc, ItemCategoriesState>(
builder: ((context, state) {
if (state is ItemCategoriesLoadedState) {
List<MapEntry<int, Category>> categoryList =
List.from(state.categories.entries);
return Container(
width: 800,
child: FormBuilderChoiceChip(
decoration: const InputDecoration(border: InputBorder.none),
selectedColor: MyTheme.primary,
alignment: WrapAlignment.spaceEvenly,
direction: Axis.horizontal,
initialValue: categoryList.map((value) => value).toList(),
name: 'filter_category',
options: categoryList
.map(
(category) => FormBuilderFieldOption(
value: category.value.id,
child: Text(category.value.name),
),
)
.toList(),
//onChanged: showFilteredItems(),
),
);
}
return Container();
}),
),
],
),
),
Expanded(
child: RefreshIndicator(
onRefresh: onRefresh ?? () async {},
child: GridView.builder(
shrinkWrap: true,
physics: const AlwaysScrollableScrollPhysics(),
gridDelegate: SliverGridDelegateWithFixedCrossAxisCount(
crossAxisSpacing: crossAxisSpacing,
childAspectRatio: aspectRatio,
crossAxisCount: crossAxisCount,
),
itemCount: items.length,
itemBuilder: (BuildContext context, int index) {
return _ItemListView(
onTap: onTap,
item: items[index],
// Todo: add ngos
);
},
),
),
),
],
);
Thank you in advance for your help!
I have to explain a bit the current structure:
news_view.dart has got FutureBuilder and fetching all necessary items from API and sending to another page Widget: customListTile3(articles, index, context)
hallo.dart has got above Widget:
Widget customListTile3(List<Datum> articles, int index, BuildContext context) {
final urlImages = [
articles[index].imageUrl!,
];
final urlImage = urlImages[index];
and
child: Column(
children: <Widget>[
Center(
child: CarouselSlider.builder(
options: CarouselOptions(height: 200),
itemCount: 10,
itemBuilder: (context, index, realIndex) {
return buildImage(urlImage, index);
},
),
bottom there is buildImage class:
child: Column(
children: <Widget>[
Center(
child: CarouselSlider.builder(
options: CarouselOptions(height: 200),
itemCount: 10,
itemBuilder: (context, index, realIndex) {
return buildImage(urlImage, index);
},
),
Slider works but it show only single image with multiple times. What is problem here?
You are only returning a single image in your function. Passing an index into a list would give you a single element at that particular index.
final urlImages = [
articles[index].imageUrl!,
];
final urlImage = urlImages; //remove [index];
I solved:
final urlImages = [
articles[0].imageUrl!,
articles[1].imageUrl!,
articles[2].imageUrl!,
articles[3].imageUrl!,
articles[4].imageUrl!,
articles[5].imageUrl!,
articles[6].imageUrl!,
articles[7].imageUrl!,
articles[8].imageUrl!,
articles[9].imageUrl!,
];
Center(
child: CarouselSlider.builder(
options: CarouselOptions(
),
itemCount: 10,
itemBuilder: (context, idx, realIndex) {
return buildImage(articles, urlImages[idx], idx, context);
},
),
),
I have checkbox for selecting and deselecting photos.
There is a visible loading screen for each tap.
_mediaList has the photo asset. mediaModel has the necessary methods to add and remove the path of selected and deselected photos respectively.
Widget build(BuildContext context) {
super.build(context);
return GridView.builder(
itemCount: _mediaList.length,
gridDelegate: SliverGridDelegateWithFixedCrossAxisCount(
crossAxisCount: 3, mainAxisSpacing: 4.0, crossAxisSpacing: 4.0),
itemBuilder: (BuildContext context, int index) {
final saved = mediaModel.getMedia().contains(
_mediaList[index].relativePath + '/' + _mediaList[index].title);
return FutureBuilder(
future: _mediaList[index].thumbDataWithSize(200, 200),
builder: (BuildContext context, snapshot) => snapshot.hasData
? GridTile(
header: saved
? Icon(Icons.check_circle, color: Colors.white,)
: Icon(Icons.check_circle_outline, color: Colors.white,),
child: GestureDetector(
child: Image.memory(
snapshot.data,
fit: BoxFit.cover,
),
onTap: () => setState(() => saved
? mediaModel.removeMedia(
_mediaList[index].relativePath +
'/' +
_mediaList[index].title)
: mediaModel.addMedia(
_mediaList[index].relativePath +
'/' +
_mediaList[index].title))),
)
: Container());
},
);
}
EDIT: After some analysis, I found out using Provider to load images might be the right way.
Can you help me in converting this code to Provider?
Thanks in advance!!!
Screenshot:
Full code:
class FooPage extends State<SoPage> {
static const int _count = 10;
final List<bool> _checks = List.generate(_count, (_) => false);
#override
Widget build(BuildContext context) {
return Scaffold(
body: GridView.builder(
itemCount: _count,
gridDelegate: SliverGridDelegateWithFixedCrossAxisCount(crossAxisCount: 2),
itemBuilder: (_, i) {
return Stack(
children: [
Container(color: Colors.red[(i * 100) % 900]),
Align(
alignment: Alignment.topCenter,
child: Checkbox(
value: _checks[i],
onChanged: (newValue) => setState(() => _checks[i] = newValue),
),
),
],
);
},
),
);
}
}
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'),
),
),
);
},
),
),
],
),
);
}