Flutter Provider : cannot remove item from list in provider - flutter

I have checkbox listtile inside listview builder I want when I check any one then its data add to list then when I uncheck it be removed from list:
Directionality(
textDirection: TextDirection.rtl,
child: ListView.builder(
itemCount: student.length,
itemBuilder: (context, index) {
return Card(
child: CheckBoxedListTile(
student[index], widget.date, widget.time,widget.teacher,widget.subject));
}),
),
);
}
}
check listtile widget is :
class _CheckBoxedListTileState extends State<CheckBoxedListTile> {
var checked;
#override
void initState() {
checked = false;
super.initState();
}
#override
Widget build(BuildContext context) {
return Consumer<AbsenceProvider>(builder: (context, absProv, child) {
return CheckboxListTile(
value: checked,
onChanged: (val) {
setState(() {
checked = !checked;
});
var data = {
"name": widget.student.name,
"stage": widget.student.stage,
"group": widget.student.group,
"teacher": widget.teacher,
"subject": widget.subject,
"date": widget.date,
"time": widget.time,
"vacs": "No"
};
if (checked == true) {
absProv.addAbs(data);
} else {
absProv.remAbs(data);
}
print(absProv.absences);
},
title: Text('${widget.student.name}'),
);
});
}
}
provider is :
class AbsenceProvider with ChangeNotifier {
var absences = [];
addAbs(item) {
absences.add(item);
notifyListeners();
}
remAbs(item) {
absences.remove(item);
notifyListeners();
}
}
when I click on check box it is add successfully
but when i click again it is nor remove it

I solved it by using removeWhere :
before :
absences.remove(item);
after :
absences.removeWhere((e) => e['name'] == item['name']);

Related

Flutter Dynamic TabBar for data coming from FireStore Collection

I have a FireStore collection named "products" and in there I have documents consisting of product data such as name, price, and category. It follows the structure like this
{"name": "Milk Shake Strawberry",
"price": "250",
"category": "Drinks"
},
{"name": "Swiss Roll",
"price": "150",
"category": "Cake"
}
.
I want to create the UI to show Each category as a Tab (ex: Drinks Tab, Cake Tab) and inside that tab, I want to show products related to that exact category.
How can I achieve this in Flutter?
Try this
class ProductCategoryTabs extends StatefulWidget {
#override
_ProductCategoryTabsState createState() => _ProductCategoryTabsState();
}
class _ProductCategoryTabsState extends State<ProductCategoryTabs> {
List<String> _tabs = [];
Map<String, List<Product>> _products = {};
#override
void initState() {
super.initState();
// Fetch the list of categories and products from Firestore
Firestore.instance.collection('products').getDocuments().then((snapshot) {
snapshot.documents.forEach((document) {
var product = Product.fromFirestore(document);
if (!_tabs.contains(product.category)) {
_tabs.add(product.category);
}
if (_products[product.category] == null) {
_products[product.category] = [product];
} else {
_products[product.category].add(product);
}
});
setState(() {});
});
}
#override
Widget build(BuildContext context) {
return DefaultTabController(
length: _tabs.length,
child: Scaffold(
appBar: AppBar(
bottom: TabBar(
tabs: _tabs.map((String tab) {
return Tab(text: tab);
}).toList(),
),
),
body: TabBarView(
children: _tabs.map((String tab) {
return ListView.builder(
itemCount: _products[tab].length,
itemBuilder: (BuildContext context, int index) {
return ProductTile(product: _products[tab][index]);
},
);
}).toList(),
),
),
);
}
}

Flutter : scrollController.isAttached is always false

How can I scroll to a special widget in a ListView? For example, I want to automatically scroll to some container in ListView if I press a certain button on a previous screen. I will pass to the next screen an Id (from id I will know the index) and when I navigate to the next screen I want to automatically scroll to this widget.
the code in main screen : Navigator.push(context, MaterialPageRoute(builder: (_) => CreatedEstatesScreen(estateId: id)));
the code in the next screen :
class RecentEstateOrdersScreen extends StatefulWidget {
static const String id = "RecentEstateOrdersScreen";
String? estateId;
RecentEstateOrdersScreen({Key? key, this.estateId}) : super(key: key);
#override
_RecentEstateOrdersScreenState createState() =>
_RecentEstateOrdersScreenState();
}
class _RecentEstateOrdersScreenState extends State<RecentEstateOrdersScreen> {
late RecentEstatesOrdersBloc _recentEstatesOrdersBloc;
late ItemScrollController scrollController;
late ItemPositionsListener itemPositionsListener;
String? userToken;
List<EstateOrder> orders = [];
#override
void initState() {
super.initState();
_recentEstatesOrdersBloc = RecentEstatesOrdersBloc(EstateOrderRepository());
_onRefresh();
User? user = BlocProvider.of<UserLoginBloc>(context).user;
if (user != null && user.token != null) {
userToken = user.token;
}
scrollController = ItemScrollController();
itemPositionsListener = ItemPositionsListener.create();
}
_onRefresh() {
if (BlocProvider.of<UserLoginBloc>(context).user!.token != null) {
_recentEstatesOrdersBloc.add(
RecentEstatesOrdersFetchStarted(
token: BlocProvider.of<UserLoginBloc>(context).user!.token!),
);
}
}
#override
Widget build(BuildContext context) {
return SafeArea(
child: Scaffold(
appBar: AppBar(
title: Text(
AppLocalizations.of(context)!.recent_created_orders,
),
),
body: BlocConsumer<RecentEstatesOrdersBloc, RecentEstatesOrdersState>(
bloc: _recentEstatesOrdersBloc,
listener: (context, recentOrdersState) async {
if (recentOrdersState is RecentEstatesOrdersFetchError) {
var error = recentOrdersState.isConnectionError
? AppLocalizations.of(context)!.no_internet_connection
: recentOrdersState.error;
await showWonderfulAlertDialog(
context, AppLocalizations.of(context)!.error, error);
}
},
builder: (BuildContext context, recentOrdersState) {
if (recentOrdersState is RecentEstatesOrdersFetchProgress) {
return const ClientsOrdersShimmer();
}
if (recentOrdersState is! RecentEstatesOrdersFetchComplete) {
return Container();
}
orders = recentOrdersState.estateOrders;
if (orders.isEmpty) {
return Center(
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: [
SvgPicture.asset(
documentOutlineIconPath,
width: 0.5.sw,
height: 0.5.sw,
color: Theme.of(context)
.colorScheme
.onBackground
.withOpacity(0.42),
),
48.verticalSpace,
Text(
AppLocalizations.of(context)!.have_not_recent_orders,
style: Theme.of(context).textTheme.headline4,
),
],
),
);
}
if (widget.estateId != null) {
SchedulerBinding.instance!.addPostFrameCallback((_) {
jumpToOrder(orders);
});
}
return RefreshIndicator(
color: Theme.of(context).colorScheme.primary,
onRefresh: () async {
_onRefresh();
},
child: ListView.builder(
itemCount: orders.length,
itemBuilder: (_, index) {
return EstateOrderCard(
estateOrder: orders.elementAt(index),
);
}),
);
},
),
),
);
}
jumpToOrder(List<EstateOrder> orders) {
int index = getIndexFromId(orders);
if (index != -1) {
if (scrollController.isAttached) {
scrollController.scrollTo(
index: index,
duration: const Duration(seconds: 2),
curve: Curves.easeInOutCubic);
}
}
}
getIndexFromId(List<EstateOrder> orders) {
for (int i = 0; i < orders.length; i++) {
if (orders.elementAt(i).id == int.parse(widget.estateId!)) {
return i;
}
}
return -1;
}
}```
If you are using the library then you have to use ScrollablePositionedList.builder, not the normal ListView.builder.

How to Implement Search Functionality In flutter

I have been stuck trying implement the search functionality in flutter,
So I have this search icon button
IconButton(
onPressed: () {
showSearch(
context: context,
delegate: CustomSearchDelegate(),
);
},
icon: Icon(Icons.search),
)
OnPressed It uses the CustomSearchDelegate Class Below
class CustomSearchDelegate extends SearchDelegate {
AllBackEnds _allBackEnds = AllBackEnds();
#override
List<Widget> buildActions(BuildContext context) {
return [
IconButton(
onPressed: () {
if (query.isEmpty) {
close(context, null);
} else {
query = '';
}
},
icon: Icon(Icons.clear)),
];
}
#override
Widget buildLeading(BuildContext context) => IconButton(
icon: BackButtonIcon(),
onPressed: () {
close(context, null);
},
);
#override
Widget buildResults(BuildContext context) {
return FutureBuilder<List>(
future: _allBackEnds.getUsers(),
builder: (context, adsSnapshot) {
if (!adsSnapshot.hasData) {
return CustProgIndicator();
} else {
List _dat = adsSnapshot.data!;
return ListView.builder(
itemCount: _dat.length,
shrinkWrap: true,
itemBuilder: (context, int index) {
Map<String, dynamic> _userAds = _dat[index];
return CustomWid(data: _userAds);
},
);
}
});
}
#override
Widget buildSuggestions(BuildContext context) {
return FutureBuilder<List>(
future: _allBackEnds.getUsers(),
builder: (context, adsSnapshot) {
if (!adsSnapshot.hasData) {
return CustProgIndicator();
} else {
List _dat = adsSnapshot.data!;
return ListView.builder(
itemCount: _dat.length,
shrinkWrap: true,
itemBuilder: (context, int index) {
Map<String, dynamic> _userAds = _dat[index];
return CustomWid(data: _userAds);
},
);
}
});
}
}
The _allBackEnds.getUsers() is returning an array of objects like this
[{s_n: 1, name: Drizzy}, {s_n: 2, name: Omah Lay,}];
So I want to search using the name
So the buildSuggesion Widget & buildResults Widget are just displaying a list of custom users card, without the actual search feature.
How can I implement the suggestions & search functionality with my code shown?
the proper way to solve this issue is : first of all you have to make model to store that data,after making model use textfield and enter sometext, in onchange method of textfield you can check that the list of model data is containing this data you have entered in text field like
void searchContact(String textfieldresult,modelList) async {
searchedList.clear();
var value = modelList.where((element) =>
element.name!.toLowerCase().contains(textfieldresult.toLowerCase()) ||
element.name!.toUpperCase().contains(textfieldresult.toUpperCase()));
searchedList.addAll(value);
return searchedList ;
}
this method will return searched values

Flutter bool value resets after setstate in listview multiselect

I have listview with checkboxtile filled with data in which when i will select option it will checkmark that item. Listview meant to be multi select option. There is model class for converting jsonresponse to data. UI code is working fine but ontap action causing bug in application.
When on tap item selected it check the box and reset spontaneously. Issue is regarding selecting the checkbox on all item it reset automatically. It's look like something wrong with model class. Facing issue on emulator as well as physical device.
Appreciate your time.
This is my CommonResponse class
class CommonResponse {
final String id;
final String name;
bool isSelected = false;
CommonResponse({this.id, this.name});
factory CommonResponse.fromJson(Map<String, dynamic> json) {
return CommonResponse(
id: json['id'],
name: json['name'] as String,
);
}
}
This is UI code:
import 'dart:convert';
import 'package:flutter/material.dart';
class WorkList extends StatefulWidget {
Key key;
WorkList({this.key}) : super(key: key);
#override
_WorkListState createState() => _WorkListState();
}
class _WorkListState extends State<WorkList> {
Constants constants = Constants();
List<String> selectedPrefIdList = List();
Future<List<CommonResponse>> _getList() async {
var jsonData =
'[{"id": "1", "name": "1-2 day"}, {"id": "2", "name": "cameo"}, {"id": "5", "name": "movies"}, {"id": "6", "name": "ads"}]';
List jsonResponse = json.decode(jsonData);
var workPrefOutput =
jsonResponse.map((list) => CommonResponse.fromJson(list)).toList();
return workPrefOutput;
}
#override
Widget build(BuildContext context) {
return Scaffold(
body: SafeArea(
child: Container(
child: FutureBuilder<List<CommonResponse>>(
future: _getList(),
builder: (context, snapshot) {
if (snapshot.hasData) {
return ListView.separated(
separatorBuilder: (context, index) {
return Divider();
},
itemCount: snapshot.data.length,
itemBuilder: (context, index) {
return CheckboxListTile(
title: Text(snapshot.data[index].name),
controlAffinity: ListTileControlAffinity.platform,
value: snapshot.data[index].isSelected,
onChanged: (value) {
setState(() {
snapshot.data[index].isSelected = value;
var selectedId = snapshot.data[index].id;
selectedPrefIdList.contains(selectedId)
? selectedPrefIdList.remove(selectedId)
: selectedPrefIdList.add(selectedId);
// print(selectedPrefIdList);
});
},
);
});
} else {
return Center(
child: CircularProgressIndicator(),
);
}
},
),
),
),
);
}
}
I guess setstate is creating a problem. It is reloading future builder again.
Because of this your selection is resetting
Im not sure if this will work, but maybe you can try switching this
snapshot.data[index].isSelected = value;
to this
snapshot.data[index].isSelected = !snapshot.data[index].isSelected;

Flutter: how to filter listview with the data loaded from API

I have a list populated with a Future builder. The items are loaded correctly in the list from API.
Following is the relevant part of the code. I have a textfield in an appbar, which I want to use to filter the list.
List newList = List();
List originalList = List();
bool _showSearchBox = false;
TextEditingController _textController = TextEditingController();
Future _future;
#override
void initState() {
_future = commonApiProvider.fetchUserList(offset, widget.selectedDate);
super.initState();
}
#override
Widget build(BuildContext context) {
size = Screen(MediaQuery.of(context).size);
loadMoreNewStatus = ItemLoadMoreStatus.LOADING;
return Scaffold(
backgroundColor: Color(0xfff0f0f0),
appBar: AppBar(
automaticallyImplyLeading: _showSearchBox == true ? false : true,
backgroundColor: CustomColors.absentTileColor,
elevation: 1,
title:
_showSearchBox == true ? _buildSearchWidget() : Text("Absent List"),
actions: <Widget>[
_showSearchBox == false ? _buildSearchIcon() : Container(),
],
),
body: FutureBuilder(
builder: (context, snapshot) {
if (snapshot.connectionState == ConnectionState.none &&
snapshot.hasData == null) {
return Text("Records not found for selected date.");
} else if (snapshot.hasData) {
return _buildListChild(snapshot);
} else if (snapshot.hasError) {
return Text(snapshot.error.toString());
}
},
future: _future,
),
);
}
Widget _buildListChild(AsyncSnapshot snapshot) {
var data = snapshot.data.d;
newList = json.decode(data.userList);
originalList = json.decode(data.userList);
return RefreshIndicator(
key: _refreshIndicatorKey,
child: NotificationListener(
onNotification: onNotificationHandler,
child: ListView.builder(
padding: EdgeInsets.only(top: size.getSizePx(10)),
scrollDirection: Axis.vertical,
shrinkWrap: true,
physics: const BouncingScrollPhysics(),
itemCount: newList.length,
controller: scrollContainer,
itemBuilder: (context, index) {
if (index == newList.length) {
return _buildProgressIndicator();
} else {
loadMoreNewStatus = ItemLoadMoreStatus.STABLE;
animationController.forward();
return cardView(newList[index]);
}
}),
),
onRefresh: _refreshStuffs,
);
}
Widget cardView(userList){
//build list items here.
}
bool onNotificationHandler(ScrollNotification notification){
//stuffs here
}
_refreshStuffs(){
//code to refresh list.
}
Widget _buildSearchWidget(){
return Container(
child: TextField(
controller: _textController,
style: TextStyle(fontSize: 14.0, color: Colors.grey[800]),
onChanged: onSearchTextChanged,
);
);
}
onSearchTextChanged(String text) async {
List tempSearchList = List();
tempSearchList.addAll(originalList);
if (text.isNotEmpty) {
List tempListData = List();
tempSearchList.forEach((item) {
String empName = item["empname"];
if (empName.toLowerCase().contains(text.toLowerCase())) {
tempListData.add(item);
}
});
setState(() {
newList.clear();
newList.addAll(tempListData);
});
return;
} else {
setState(() {
newList.clear();
newList.addAll(originalList);
});
}
}
Problem
The problem is that above code is not working, the list doesn't change at all. If I debug method onSearchTextChanged it works very well. I have cleared newList on this method as well, but doesn't seem to work. Can anybody help how to achieve filter?
The idea here is: Once FutureBuilder completes, it doesn't get rebuild.
I hope the code below helps. Let me know if your problem exists.
class _MyHomePageState extends State<MyHomePage> {
var items = [];
#override
void initState() {
callApi();
super.initState();
}
#override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: YourFilteringTextField(),
),
body: ListView.builder(
itemBuilder: (context, position) {
return Text(items[position]);
},
itemCount: items.length,
),
);
}
callApi() {
//call api to get your latest items
setState(() {
// items= itemsFetchedFromApi;
});
}
filter(query) {
//applyFilter
setState(() {
// items= itemsAfterFiltering;
});
}
}