favorite icon does not updated using Getx in flutter - flutter

//code of the screen
ListView.builder(
physics: const NeverScrollableScrollPhysics(),
shrinkWrap: true,
padding: EdgeInsets.zero,
scrollDirection: Axis.vertical,
itemCount: controller.favDestinationList.length,
itemBuilder: (BuildContext context, int index) {
return Container(
width: MediaQuery
.of(context)
.size
.width * 0.9,
child: Row(
children: [
IconButton(
icon:FaIcon(controller.favDestinationList[index].isfav?
FontAwesomeIcons.solidHeart : FontAwesomeIcons.heart,
color: secondaryHeaderColor,),
onPressed: () {
controller.changeStatus(
controller.favDestinationList[index].isfav);
print(controller.favDestinationList[index].isfav);
},
);
Padding(
padding: const EdgeInsets.all(8.0),
child: Text(controller.favDestinationList[index].name),
)
]
,
)
,
);
}
),
///////code of the screen
ListView.builder(
physics: const NeverScrollableScrollPhysics(),
shrinkWrap: true,
padding: EdgeInsets.zero,
scrollDirection: Axis.vertical,
itemCount: controller.favDestinationList.length,
itemBuilder: (BuildContext context, int index) {
return Container(
width: MediaQuery
.of(context)
.size
.width * 0.9,
child: Row(
children: [
IconButton(
icon:FaIcon(controller.favDestinationList[index].isfav?
FontAwesomeIcons.solidHeart : FontAwesomeIcons.heart,
color: secondaryHeaderColor,),
onPressed: () {
controller.changeStatus(
controller.favDestinationList[index].isfav);
print(controller.favDestinationList[index].isfav);
},
);
Padding(
padding: const EdgeInsets.all(8.0),
child: Text(controller.favDestinationList[index].name),
)
]
,
)
,
);
}
),
/////////////////controller class
class SearchFavoriteController extends GetxController{
var favDestinationList=[].obs;
#override
void onInit() {
// TODO: implement onInit
super.onInit();
getFavDestination();
}
getFavDestination() async{
try{
var destination=await Api().getfavdestinations();//call api
favDestinationList.assignAll(destination);
}catch(e){
Get.snackbar('title', e.toString());
}
}
changeStatus(bool isfavorite) {
isfavorite = !isfavorite;
update();
}
}
/////////////api
List<FavoriteModel> getfavdestinations() {
return [
FavoriteModel( id: '1',name: 'Germany', isfav: false),
FavoriteModel( id: '2',name: 'Turkey', isfav: true),
FavoriteModel( id: '3',name: 'Jordan', isfav: false),
FavoriteModel( id: '4',name: 'London', isfav: false)
].obs;
}

Your question is not very clear, and you have to Rephrase it
However, i think there isn't any Observer Widget or GetXBuilder in your view
try to wrap the listView.Builder with one of them, it should works

Related

Flutter TextFormField suggestion or auto complete

I want to implement suggestions inside textFormField. same as below
So, I've searched regarding this but with no success. Everywhere I've got is suggestions inside list. Which is easy to do. If you have any suggestions then please add your valuable answer and comment.
Here is my code
Column(
mainAxisSize: MainAxisSize.min,
children: [
TextFormField(
controller: controller,
onFieldSubmitted: (value) {},
onChanged: (value) {
displaySuggestionInList(value);
},
),
const SizedBox(height: 30),
ConstrainedBox(
constraints: const BoxConstraints(
maxHeight: 100,
maxWidth: 200,
minWidth: 200,
minHeight: 100,
),
child: ListView.builder(
shrinkWrap: true,
itemCount: dashboardLayouts!.length,
itemBuilder: (context, index) {
return Text((dashboardLayouts![index]['dashBoardData']
as DashboardInfo)
.commonName
.toString());
},
),
)
],
),
What you need to create is a Type-Ahead Widget. To do that, you will firstly create the normal List suggestion StatefulWidget. While on the filter function you should update the hint with the first value from the suggestion list. This way you can call the hint value and place it anywhere on your screen. Unfortunately for us, flutter doesn't allow the update showing of hint within the input field while typing.
Although I made an example for you to get the idea.
class AutocompleteExample extends StatefulWidget {
const AutocompleteExample({super.key});
#override
State<AutocompleteExample> createState() => _AutocompleteExampleState();
}
class _AutocompleteExampleState extends State<AutocompleteExample> {
TextEditingController controller = TextEditingController();
List suggestionList = [];
String hint = "";
List<String> nameList = <String>[
'aardvark',
'bobcat',
'chameleon',
'Nathaniel Bond',
'Taylor Story',
'Lamont Padilla',
'Jamia Sun',
'Nikki Reichert',
'Tea Holguin',
'Rafael Meade',
'Mercedez Goad',
'Aileen Foltz',
'Bryant Burt',
];
void typeAheadFilter(String value) {
suggestionList.clear();
if (value.isEmpty) {
setState(() {});
return;
}
for (String name in nameList) {
if (name.toLowerCase().contains(value)) {
suggestionList.add(name);
}
}
if (suggestionList.isNotEmpty) {
var firstSuggestion = suggestionList[0];
setState(() => hint = firstSuggestion);
}
setState(() {});
}
#override
Widget build(BuildContext context) {
return Column(
mainAxisSize: MainAxisSize.min,
children: [
TextFormField(
controller: controller,
onFieldSubmitted: (value) {},
onChanged: (value) => typeAheadFilter(value),
decoration: InputDecoration(
hintText: hint,
labelText: hint.isEmpty ? "Search" : hint,
alignLabelWithHint: true,
hintTextDirection: TextDirection.rtl),
),
const SizedBox(height: 10),
if (suggestionList.isNotEmpty || controller.text.isNotEmpty) ...[
Expanded(
child: ListView.separated(
padding: const EdgeInsets.all(10),
shrinkWrap: true,
itemCount: suggestionList.length,
separatorBuilder: (context, index) => const Divider(),
itemBuilder: (context, index) {
return Text((suggestionList[index]));
},
),
)
] else ...[
Expanded(
child: ListView.separated(
padding: const EdgeInsets.all(10),
shrinkWrap: true,
itemCount: nameList.length,
separatorBuilder: (context, index) => const Divider(),
itemBuilder: (context, index) {
return Text((nameList[index]));
},
),
)
]
],
);
}
}

How can I make the gridview respond to the search filters using FormbuilderChoiceChip and Bloc in flutter?

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!

In ListView.builder Bloc event triggered only once

I m using Bloc for state management , I have my screen where I'm calling event in ListView.builder
loadSuccess: (state) {
return ListView.builder(
shrinkWrap: true,
physics: const NeverScrollableScrollPhysics(),
itemCount: state.questions.size,
itemBuilder: (
context,
index,
) {
// ignore: avoid_unnecessary_containers
debugPrint("this is index $index");
debugPrint(
"this is user id ${state.questions.get(index).userId.getorCrash()}");
context.read<UsersWatcherBloc>().add(
UsersWatcherEvent.watchAllUsers(
state.questions.get(index).userId.getorCrash(),
),
);
return Container(
color: Colors.white,
......)
But problem is that my event is triggered only one time and state changed one time but I want to change my event for each index :
Event.dart:
part of 'users_watcher_bloc.dart';
#freezed
abstract class UsersWatcherEvent with _$UsersWatcherEvent {
const factory UsersWatcherEvent.watchAllUsers(String uId) = _Started;
}
Bloc.dart:
#injectable
class UsersWatcherBloc extends Bloc<UsersWatcherEvent, UsersWatcherState> {
final IElearningRepository _iElearningRepository;
UsersWatcherBloc(this._iElearningRepository)
: super(const UsersWatcherState.initial());
#override
Stream<UsersWatcherState> mapEventToState(
UsersWatcherEvent event,
) async* {
yield* event.map(
watchAllUsers: (e) async* {
print("this is user id ${e.uId}");
yield const UsersWatcherState.loadInProgress();
yield* _iElearningRepository.watchAllUsers(e.uId.toString()).map(
(failureOrUsers) => failureOrUsers.fold(
(f) => UsersWatcherState.loadFailure(f),
(users) {
if (users.isEmpty) {
return const UsersWatcherState.empty();
}
return UsersWatcherState.loadSuccess(users);
},
),
);
},
);
}
}
After 2 days struggle I found solution of this question, I have to wrap my container with another BlocProvider and use dependency injection
loadSuccess: (state) {
return ListView.builder(
shrinkWrap: true,
physics: const NeverScrollableScrollPhysics(),
itemCount: state.questions.size,
itemBuilder: (
context,
index,
) {
// ignore: avoid_unnecessar_containers
return BlocProvider(
create: (context) => getIt<UsersWatcherBloc>()
..add(
UsersWatcherEvent.watchCurrentUser(
state.questions.get(index).userId.getorCrash(),
),
),
child: Container(
color: Colors.white,
margin: const EdgeInsets.only(bottom: 5),
padding: EdgeInsets.only(
left: leftPadding.w - 8.w,
right: rightpadding.w - 8.w,
bottom: bottomPadding.h,
),
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [

Force navDrawer state update from outside state scope

I am currently developing an e-commerce mobile app.
Right now I am working on my navigation. There's a bunch of categories that can have subcategories and the subcategories can have their own subcategories. I retrieve a list of all categories via an API on app init and then I store it.
Here's an example of my dataset:
{
"id":"41490",
"name":"Electrical Equipment",
"subCategories":[
{
"id":"41492",
"name":"Breakers",
"subCategories":[
{
"id":"167542",
"name":"1 Pole",
"subCategories":[
{
"id":"167577",
"name":"15 Amp",
"subCategories":null
},
{
"id":"167585",
"name":"20 Amp",
"subCategories":null
},
{
"id":"167600",
"name":"30 Amp",
"subCategories":null
},
{
"id":"167606",
"name":"40 Amp",
"subCategories":null
}
]
},
I am using a listview and a listview builder to make my category list.
The listview builder also calls a recursive function to make the subcategories.
I've managed to get everything to generate dynamically meaning that if on the website we add a bunch of categories then the app will update itself automatically via the API.
My problem now is that when I click my categories, the navDrawer doesn't redraw. I have to close the categories and re-open them to make it redraw. I need some new concepts, been scratching my head on this one for a while.
I think there might be an issue with the structure of my code since I initialize the categories outside the state.
Here's my navDrawer class:
class navDrawer extends StatefulWidget {
bool _expandCategories = false;
bool _expandAccount = false;
List _categories;
var _categoryList;
List _tempSubCats;
void flickCategories(){
//_expandCategories = !_expandCategories;
//sleep(const Duration(microseconds: 100));
//_expandCategories = !_expandCategories;
}
void setCategories(List categories){
_categories = categories;
int catCount = categories.length;
_categoryList = new ListView.builder(
//shrinkWrap: true,
//physics: ClampingScrollPhysics(),
padding:EdgeInsets.all(0.0),
itemCount: catCount,
itemBuilder: (BuildContext context, int index) => buildCategories(context, index),
);
}
Widget buildCategories(BuildContext context, int index){
if(_categories[index]['subCategories']!=null){
if(idHandler.isIdOpen(_categories[index]['id'])){
_tempSubCats = [];
buildSubCategories(_categories[index],2);
ListView subCategories = new ListView.builder(
shrinkWrap: true,
physics: NeverScrollableScrollPhysics(),
itemCount: _tempSubCats.length,
itemBuilder: (BuildContext ct, int i){
return _tempSubCats[i];
}
);
return Column(
mainAxisSize: MainAxisSize.min,
children: [
Container(
height: 30.0,
child: ListTile(
title: Row(
children: [
Text(" " + _categories[index]['name']),
Transform.rotate(
angle: -math.pi/2,
child:
Transform.scale(
scale: 0.75,
child:
Icon(Icons.arrow_back)
)
)
]
),
onTap: () {
flickCategories();
idHandler.toggleId(_categories[index]['id']);
}
),
padding: EdgeInsets.all(0.0),
margin: EdgeInsets.all(0.0)
),
MediaQuery.removePadding(
removeTop: true,
removeBottom: true,
removeLeft: true,
removeRight: true,
context: context,
child: subCategories
)
]
);
} else {
return Container(
height: 30.0,
child: ListTile(
title: Row(
children: [
Text(" " + _categories[index]['name']),
Transform.scale(
scale: 0.75,
child:
Icon(Icons.arrow_back)
)
]
),
onTap: () {
flickCategories();
idHandler.toggleId(_categories[index]['id']);
}
),
padding: EdgeInsets.all(0.0),
margin: EdgeInsets.all(0.0)
);
}
} else {
return Container(
height: 30.0,
child: ListTile(
title: Text(" "+_categories[index]['name']),
onTap: () {
//TODO: implement category navigation
}
),
padding: EdgeInsets.all(0.0),
margin: EdgeInsets.all(0.0)
);
}
}
void buildSubCategories(var parent, int depth){
if(parent['subCategories']!=null){
List subCategoryList = parent['subCategories'];
int subCategoryCount = subCategoryList.length;
//Column subCats = new Column();
if(idHandler.isIdOpen(parent['id'])) {
for (var i = 0; i < subCategoryCount; i++) {
String formattedCategory = indentCategory(parent['subCategories'][i]['name'], depth);
_tempSubCats.add(
parent['subCategories'][i]['subCategories']!=null ?
Container(
height:20.0,
child:
ListTile(
title: idHandler.isIdOpen(parent['subCategories'][i]['id']) ?
Row(
children:[
Text(formattedCategory),
Transform.rotate(
angle:-math.pi/2,
child:
Transform.scale(
scale:0.75,
child:
Icon(Icons.arrow_back)
)
)
]
)
:
Row(
children: [
Text(formattedCategory),
Transform.scale(
scale: 0.75,
child:
Icon(Icons.arrow_back)
)
]
),
onTap: (){
flickCategories();
idHandler.toggleId(parent['subCategories'][i]['id']);
}
)
)
:
Container(
height:20.0,
child:
ListTile(
title: Text(formattedCategory),
onTap: (){
//TODO: implement category navigation
}
)
)
);
buildSubCategories(parent['subCategories'][i], depth+1);
}
}
}
}
String indentCategory(String input, int amount){
String output='';
for(var i=0; i<amount; i++){
output += ' ';
}
output+=input;
return output;
}
#override
_navDrawerState createState() => _navDrawerState();
}
class _navDrawerState extends State<navDrawer>{
#override
Widget build(BuildContext Context){
return Drawer(
child:
Container(
padding:EdgeInsets.fromLTRB(0.0, 0.0, 0.0, 5),
child:
Column(
children:[
Container(
height:80.0,
width:double.infinity,
child:
DrawerHeader(
child: Text('Menu'),
decoration: BoxDecoration(
gradient: LinearGradient(
begin: Alignment.topLeft,
end: Alignment.bottomRight,
colors: <Color>[
Colors.grey,
Colors.red
])
)
),
margin:EdgeInsets.all(0.0),
padding:EdgeInsets.all(0.0)
),
Expanded(
child:
ListView(
padding:EdgeInsets.zero,
children: <Widget>[
widget._expandCategories?
Column(
children:[
Container(
height:40.0,
child: ListTile(
title: Row(
children: [
Text('Categories'),
Transform.rotate(
angle: -math.pi/2,
child:
Transform.scale(
scale: 0.75,
child:
Icon(Icons.arrow_back)
)
)
]
),
onTap:() {
_expandCat();
}
)
),
MediaQuery.removePadding(
removeTop:true,
context: context,
child:
SizedBox(
height:300.0,
child: widget._categoryList,
)
)
]
)
:Container(
height:40.0,
child:
ListTile(
title: Row(
children: [
Text('Categories'),
Transform.scale(
scale: 0.75,
child:
Icon(Icons.arrow_back)
)
]
),
onTap:(){
_expandCat();
//Update state of the app
}
),
margin:EdgeInsets.all(0.0),
padding:EdgeInsets.all(0.0)
),
Container(
height:40.0,
child:
ListTile(
title:Text('Your quotes'),
onTap:(){
//Update state of the app
}
),
margin:EdgeInsets.all(0.0),
padding:EdgeInsets.all(0.0)
),
widget._expandAccount?
Column(
children:[
Container(
height:40.0,
child:
ListTile(
title: Row(
children:[
Text('Your account'),
Transform.rotate(
angle:-math.pi/2,
child:
Transform.scale(
scale:0.75,
child:
Icon(Icons.arrow_back)
)
)
]
),
onTap:(){
_expandAcc();
}
),
margin:EdgeInsets.all(0.0),
padding:EdgeInsets.all(0.0)
),
Container(
height:30.0,
child:
ListTile(
title:Text(' Your Information'),
onTap:(){
}
)
),
Container(
height:30.0,
child:
ListTile(
title:Text(' Your Address'),
onTap:(){
}
)
),
Container(
height:30.0,
child:
ListTile(
title:Text(' Listed Equipment'),
onTap:(){
}
)
),
Container(
height:30.0,
child:
ListTile(
title:Text(' Add Equipment'),
onTap:(){
}
)
),
]
)
:Container(
height:40.0,
child:
ListTile(
title: Row(
children:[
Text('Your account'),
Transform.scale(
scale:0.75,
child:
Icon(Icons.arrow_back)
)
]
),
onTap:(){
_expandAcc();
}
),
margin:EdgeInsets.all(0.0),
padding:EdgeInsets.all(0.0)
)
]
),
)
]
)
)
);
}
void _expandCat(){
setState((){
widget._expandCategories=!widget._expandCategories;
});
}
void _expandAcc(){
setState((){
widget._expandAccount=!widget._expandAccount;
});
}
}
NOTE: idHandler is a public member of main.dart.
NOTE2: flickCategories() is one of my attempts at updating the state.
In the screenshot below you can see what I mean:
If I click Electrical Equipment then I have to click Categories twice to make it redraw and I have to scroll back to where it was in the list.
So, how do I make the state update when one of my categories gets clicked?
Do I need something like a stateful category widget?
I'm trying to make it look responsive with arrows and indents and etc.
I figured this out on my own.
I needed to make a productCategory stateful widget and update its state from within the widget.
Each productCategory widget has a List representing the subCategories. During my recursion I add to the subCategories for each productCategory.
The productCategory widget redraws itself properly because I call setState() which has the added bonus of keeping the scroll position where it is.
Here's my productCategory widget:
class productCategory extends StatefulWidget{
String _id = '';
String _name = '';
List<productCategory> _subCategories = [];
productCategory(String id, String name){
_id = id;
_name = name;
}
void addAllSubCategories(List<productCategory> subCats){
_subCategories.addAll(subCats);
}
void addSubCategory(productCategory cat){
_subCategories.add(cat);
}
void setName(String name){
_name = name;
}
void setId(String id){
_id = id;
}
#override
_productCategoryState createState() => _productCategoryState();
}
class _productCategoryState extends State<productCategory>{
#override
Widget build(BuildContext context) {
if(widget._subCategories.isNotEmpty){
if(idHandler.isIdOpen(widget._id)){
return Column(
mainAxisSize: MainAxisSize.min,
children: [
Container(
height: 30.0,
child: ListTile(
title: Row(
children: [
Text(widget._name),
Transform.rotate(
angle: -math.pi/2,
child:
Transform.scale(
scale: 0.75,
child:
Icon(Icons.arrow_back)
)
)
]
),
onTap: () {
setState((){
idHandler.toggleId(widget._id);
});
}
),
padding: EdgeInsets.all(0.0),
margin: EdgeInsets.all(0.0)
),
MediaQuery.removePadding(
removeTop: true,
removeBottom: true,
removeLeft: true,
removeRight: true,
context: context,
child: ListView.builder(
physics: NeverScrollableScrollPhysics(),
shrinkWrap: true,
padding: EdgeInsets.all(0.0),
itemCount: widget._subCategories.length,
itemBuilder: (BuildContext context, int index){
return widget._subCategories[index];
}
)
)
]
);
} else {
return Container(
height: 30.0,
child: ListTile(
title: Row(
children: [
Text(widget._name),
Transform.scale(
scale: 0.75,
child:
Icon(Icons.arrow_back)
)
]
),
onTap: () {
setState((){
idHandler.toggleId(widget._id);
});
}
),
padding: EdgeInsets.all(0.0),
margin: EdgeInsets.all(0.0)
);
}
} else {
return Container(
height: 30.0,
child: ListTile(
title: Text(widget._name),
onTap: () {
//TODO: implement category navigation
}
),
padding: EdgeInsets.all(0.0),
margin: EdgeInsets.all(0.0)
);
}
}
}

Create Gridview with user input(Row and Column) in flutter

How can we create Gridview with the user input? the user is allowed to enter the no of rows and columns.
class Class extends StatefulWidget {
#override
_ClassState createState() => _ClassState();
}
class _ClassState extends State<Class> {
TextEditingController row = TextEditingController();
TextEditingController column = TextEditingController();
int rowC = 2;
int colC = 2;
#override
Widget build(BuildContext context) {
return Scaffold(
body: Column(
children: [
Container(
height: 500,
child: GridView.builder(
itemCount: colC * rowC,
gridDelegate: SliverGridDelegateWithFixedCrossAxisCount(crossAxisCount: rowC,childAspectRatio: colC*rowC/2 ,crossAxisSpacing: 10,mainAxisSpacing: 10),
shrinkWrap: true,
itemBuilder: (context, index) => Container(
color: Colors.greenAccent,
),
),
),
Text("Row"),
TextField(
controller: row,
),
SizedBox(height: 20,),
Text("Column"),
TextField(
controller: column,
),
SizedBox(height: 20,),
FlatButton(onPressed: (){
rowC = int.parse(row.text);
colC = int.parse(column.text);
setState(() {
});
}, child: Container(
color: Colors.purple,
padding: EdgeInsets.all(20),
child: Text("Add")))
],
),
);
}
}
You can achieve your requirement by using the GridView.builder.
GridView.builder(
shrinkWrap: true,
itemCount: (rowCount * ColumnCount),
gridDelegate:
SliverGridDelegateWithFixedCrossAxisCount(crossAxisCount: ColumnCount),
itemBuilder: (context, index) {
return Container(
child: Text(index.toString()),
);
}, );
Every user input you must to refresh the widget.