How to implement search function in Alert Dialog? - flutter

I want to implement a search bar filter for my ListView items in Alert Dialog. However i think set State does not work for Alert Dialog as I am not getting the desired result. Is there a way to implement this?
My code
class _CreateTaskScreenState extends State<CreateTaskScreen> {
List<String> societiesNames;
var items = List<String>();
#override
void initState() {
int length = widget.societies.length;
List<String> list = new List<String>();
for(var i = 0; i<length; i++){
list.add(widget.societies[i].name);
}
setState(() {
societiesNames = list;
});
items.addAll(societiesNames);
super.initState();
}
String selected;
//Searching the List
void filterSearchResults(String query) {
List<String> dummySearchList = List<String>();
dummySearchList.addAll(societiesNames);
if(query.isNotEmpty) {
List<String> dummyListData = List<String>();
dummySearchList.forEach((item) {
if(item.contains(query)) {
dummyListData.add(item);
}
});
setState(() {
items.clear();
items.addAll(dummyListData);
});
return;
} else {
setState(() {
items.clear();
items.addAll(societiesNames);
});
}
}
#override Widget build(BuildContext context){
//searchbar
Container searchBar = Container(
child: TextField(
onChanged: (value) {
filterSearchResults(value);
},
controller: TextEditingController(),
decoration: InputDecoration(
labelText: "Search",
border: OutlineInputBorder(
borderRadius: BorderRadius.all(Radius.circular(15.0)))),
),
);
//alert dialog on calling show dialog
AlertDialog dialog = AlertDialog(
title: searchBar,
content: Container(
child: ListView.builder(
shrinkWrap: true,
itemCount: items == null? 0: items.length,
itemBuilder: (BuildContext context, int index) {
return Ink(
child: InkWell(
onTap: (){
setState(() {
selected = items[index];
Navigator.pop(context);
});
},
child: ListTile(
title: Text(items[index]),
),
),
);
},
),
),
);
}
}
The list shows properly the first time i open it , however no change takes place on typing in the search bar. And on dismissing and reopening the dialog it shows empty list.

The setState method that you are calling is the one of the '_CreateTaskScreenState' , so you should use a StatefulBuilder and wrap the content of the AlertDialog with it, like that you will be provided with the a setState method that will work for the widget that the builder return !
Like that:
AlertDialog dialog = AlertDialog(
title: searchBar,
content: StatefulBuilder(
builder: (context, setState) => Container(
child: ListView.builder(
shrinkWrap: true,
itemCount: items == null? 0: items.length,
itemBuilder: (BuildContext context, int index) {
return Ink(
child: InkWell(
onTap: (){
setState(() {
selected = items[index];
Navigator.pop(context);
});
},
child: ListTile(
title: Text(items[index]),
),
),
);
},
),
),
)
);

Related

Flutter Firebase Realtime Database.Streambuilder and listview.builder returns error Index out of range: index should be less

i am using Flutter and Firebase to build an order app.I'm using Realtime Database and a streambuilder to fetch the data and every update and listview.builder to portray them.
When i display the data works fine but the problem is that when i am trying to expand the data in the RTDB i'm getting this error
Another exception was thrown: RangeError (index): Index out of range: index should be less than 22: 22.
I'm trying to expand the data on another page.Sorry if my question is a bit of a crap i am asking for the first time
Scaffold(
body: StreamBuilder(
stream: ref.onValue,
builder: (context, snapshot) {
if(
snapshot.hasData &&
snapshot.data != null &&
(snapshot.data! as DatabaseEvent).snapshot.children !=
null){
List<Orderlist> listorder = [];
final myorders = snapshot.data!.snapshot.children;
myorders.forEach((numoftable) {
count+=1;
String payment_state = '';
String payment_method = '';
List<Order> orders=[];
final number_of_orders = numoftable.children.length;
numoftable.children.forEach((order) {
if(order.key=='payment_state') payment_state=order.value.toString();
if(order.key=='payment_method') payment_method=order.value.toString();
List<Orderitem> orderitems = [];
final order_id = order.key.toString();
// print(order_id);
if(order_id.length<=3){
final node = order.value as Map<dynamic,dynamic>?;
String temp_pending='';
String temp_payment='';
String customer_uid='';
node!.forEach((keys,values) {
// orderitem.name = element;
if(keys!='description' && keys!='customer_uid'){
final value = values as Map<String,dynamic>;
String price='';
String description='';
String number_of_appereance='';
value.forEach((key, value) {
if(key=='price') price=value.toString();
if(key=='description') description=value;
if(key=='number of appereances') number_of_appereance = value.toString();
});
final orderitem = Orderitem(name: keys,price: price,description: description,number_of_appereance: number_of_appereance);
orderitems.add(orderitem);
}
if(keys=='description'){
temp_pending = values.toString();
}
if(keys=='customer_uid'){
customer_uid=values.toString();
}
},);
final nextorder = Order(number_of_table: int.tryParse(numoftable.key.toString()),
items: orderitems,order_ids: order_id,customer_uid: customer_uid,
pending: temp_pending,);
orders.add(nextorder);
}
});
final current_index = int.parse(numoftable.key.toString())-1;
`your text`
if(temp_length.isEmpty || count<=myorders.length){
temp_length.insert(current_index, orders.length);
}
else if(temp_length[current_index]<orders.length ){
temp_length[current_index]= orders.length;
ref.child(numoftable.key.toString()).update({'payment_state':'Not payed'});
ref.child(numoftable.key.toString()).update({'payment_method':'Cash'});
}
final nextorderlist = Orderlist(
orderlist: orders,
number_of_table: numoftable.key.toString(),
payment_state: payment_state,
payment_method: payment_method,
number_of_orders: number_of_orders);
// print(nextorder.order_ids);
listorder.add(nextorderlist);
// ref.child(numoftable.key.toString()).update({'check':'false'});
},
);
return Column(
children: [
GeneralSettings(),
Flexible(
child: GridView.builder(
shrinkWrap: true,
itemCount: listorder.length ,
gridDelegate: SliverGridDelegateWithFixedCrossAxisCount(crossAxisCount: 3),
itemBuilder: (context, index) {
return _listoforders(listorder, index,);
},
),
),
],
);
}else if(snapshot.connectionState==ConnectionState.waiting){
Center(child: CircularProgressIndicator(),);
}
else if(snapshot.hasError){
return Text('Error');
}
return Text('Error');
}
),
);
}
Widget _listoforders(List<Orderlist> listoforders,int index) {
return GestureDetector(
onTap: (){
orders.clear();
listoforders[index].orderlist!.forEach((element) { orders.add(element); });
context.goNamed('Details');
},
child: Container(
color:listoforders[index].payment_state=='None' ? Colors.lightBlue : (listoforders[index].payment_state=='Not payed') ? Colors.red : Colors.green,
child: Column(
children: [
ListTile(title: Text(listoforders[index].number_of_table.toString()),
subtitle: listoforders[index].payment_state=='None' ? Icon(Icons.no_food) : listoforders[index].payment_state=='Not payed' ? Icon(Icons.money_off) : Text('Payed'),
trailing: IconButton(onPressed: () {
listoforders[index].orderlist!.forEach((element) {
setState(() {
temp_length[index]=0;
ref.child(listoforders[index].number_of_table.toString()).child(element.order_ids!).remove();
ref.child(listoforders[index].number_of_table.toString()).update({'payment_state':'None'});
ref.child(listoforders[index].number_of_table.toString()).update({'payment_method':'None'});
delete(element.customer_uid!);
});
});
}, icon: Icon(Icons.delete)),
leading: (listoforders[index].payment_method=='None' ) ? Text('No payment method') : (listoforders[index].payment_method=='With card' ) ? Icon(Icons.credit_card) : Icon(Icons.money_rounded),),
Flexible(
child: ListView.builder(
physics: ClampingScrollPhysics(),
shrinkWrap: true,
itemCount: listoforders[index].orderlist!.length,
itemBuilder: (context, current_index) {
final current_order = listoforders[index].orderlist![current_index];
return Container(
color: current_order.pending=='Unchecked' ? Colors.red : (current_order.pending=='pending') ? Colors.amber : Colors.green,
child: ListTile(
onTap: () => Navigator.push(context, MaterialPageRoute(builder: (context)=>OrderSettings(order: current_order))),
title: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Text(current_order.order_ids!),
],
),
subtitle: Text(current_order.pending!),
trailing: current_order.pending=='Unchecked' ? IconButton(onPressed: () => setState(() {
ref.child(current_order.number_of_table.toString()).child(current_order.order_ids!).update({"description":"pending"});
}) , icon: Icon(Icons.alarm_add)) :
current_order.pending=='pending' ? IconButton(onPressed: () => setState(() {
ref.child(current_order.number_of_table.toString()).child(current_order.order_ids!).update({"description":"Done"});
}) , icon: Icon(Icons.pending_actions)) : Icon(Icons.check),
leading: IconButton(onPressed:() {
ref.child(current_order.number_of_table.toString()).child(current_order.order_ids!).remove();
} ,
icon:Icon(Icons.delete) ,),
),
);
},),
),
],
),
),
);}
}
Future delete(String uid) async{
final doc = FirebaseFirestore.instance.collection('Customers').doc(uid);
await doc.update({'order':''});
}
I am updating the RTDB on this page
class UpdatePanel extends StatefulWidget {
String? name;
bool check;
int? tables;
UpdatePanel({super.key,this.name,required this.check, this.tables});
#override
State<UpdatePanel> createState() => _UpdatePanelState();
}
class _UpdatePanelState extends State<UpdatePanel> {
late DatabaseReference ref;
TextEditingController namecontroller = TextEditingController();
TextEditingController number_of_tables = TextEditingController();
#override
void initState() {
// TODO: implement initState
ref=FirebaseDatabase.instance.ref().child(widget.name!);
super.initState();
}
#override
Widget build(BuildContext context) {
print(widget.name);
print(widget.tables);
Size size = MediaQuery.of(context).size;
final futurefiles = FirebaseStorage.instance.ref().child('${widget.name}/');
return AlertDialog(
title: Text(widget.check? 'Update businness name':'Update tables number'),
content: Container(
height: size.height*0.3,
child: SingleChildScrollView(
child: Column(children: [
widget.check ?
Textwidget(controller: namecontroller, hinttext: 'Update name', labeltext: 'Name', icon: Icon(Icons.business_center), color: Colors.black) :
Textwidget(controller: number_of_tables, hinttext: 'Update number of tables', labeltext: 'Number of tables', icon: Icon(Icons.table_bar), color: Colors.black),
SizedBox(height: size.height*0.05,),
ElevatedButton.icon(onPressed: () {
setState(() {
DatabaseManager(displayName: widget.name).settabledata(int.parse(number_of_tables.text.trim()));
createQrcodes();
resetRTDB();
(context as Element).reassemble();
Navigator.pop(context);
});
}, icon: Icon(Icons.update), label: Text('Update'))
]),
),
),
actions: [
OutlinedButton(onPressed: () => Navigator.pop(context), child: Text('Close'))
],
);
}
void resetRTDB() async{
for(int i=widget.tables!+1;i<=int.parse(number_of_tables.text.trim());i++){
await ref.child('${i}').set({"payment_state":"None","payment_method":"None",});
}
}
Future<Uint8List> toQrImageData(String text) async {
try {
final image = await QrPainter(
data: text,
version: QrVersions.auto,
gapless: false,
color: Colors.black,
emptyColor: Colors.white,
).toImage(300);
final a = await image.toByteData(format: ImageByteFormat.png);
return a!.buffer.asUint8List();
} catch (e) {
throw e;
}
}
Future createQrcodes() async{
for(int i=widget.tables!+1;i<=int.parse(number_of_tables.text.trim());i++){
final path = '${widget.name!.trim()}/${i}';
final ref = FirebaseStorage.instance.ref().child(path);
final file = await toQrImageData(widget.name!.trim().toLowerCase()+' '+'${i}');
ref.putData(file,SettableMetadata(contentType: 'image/png'));
}
}
}
this is where your error occurs
void resetRTDB() async{
for(int i=widget.tables!+1;i<=int.parse(number_of_tables.text.trim());i++){
await ref.child('${i}').set({"payment_state":"None","payment_method":"None",});
}
after getting all the data and it tries to get new data by adding 1 in every time ,change try changing your logic here

How to search/filter from a list from an API?

I have managed to load a list of cryptocurrencies from an API. This is done via the ListView.builder.
Subsequently, how does one perform a search/filter in order to select an item from the list?
By scrolling towards the end to see the last code, I have shown the code that I presumed would be able to do the job of 'search'. But I am unsure where to place this code.
Image below shows current crypto list loaded from API:
The code for the above screen is as follows:
class AddCryptoAssetScreen extends StatefulWidget {
#override
_AddCryptoAssetScreenState createState() => _AddCryptoAssetScreenState();
}
class _AddCryptoAssetScreenState extends State<AddCryptoAssetScreen> {
Future<List<Asset>> fetchCoin() async {
assetList = [];
final response = await http.get(Uri.parse(
'https://api.coingecko.com/api/v3/coins/markets?vs_currency=usd&order=market_cap_desc&per_page=100&page=1&sparkline=false'));
if (response.statusCode == 200) {
List<dynamic> values = [];
values = json.decode(response.body);
if (values.length > 0) {
for (int i = 0; i < values.length; i++) {
if (values[i] != null) {
Map<String, dynamic> map = values[i];
assetList.add(Asset.fromJson(map));
}
}
setState(() {
assetList;
});
}
return assetList;
} else {
throw Exception('Failed to load coins');
}
}
#override
void initState() {
fetchCoin();
Timer.periodic(Duration(seconds: 1), (timer) => fetchCoin());
super.initState();
}
#override
Widget build(BuildContext context) {
return GestureDetector(
behavior: HitTestBehavior.opaque,
onTap: () => Navigator.pop(context),
child: DraggableScrollableSheet(
builder: (_, controller) => Container(
decoration: BoxDecoration(),
clipBehavior: Clip.antiAlias,
child: Scaffold(
appBar: AppBar(),
body: Column(
children: [
Container(
margin: const EdgeInsets.fromLTRB(),
child: TextField(
keyboardType: TextInputType.text,
textAlign: TextAlign.start,
decoration: InputDecoration(
prefixIcon: const Icon(Icons.search),
hintText: 'Search',
border: OutlineInputBorder(),
contentPadding: EdgeInsets.only()),
),
),
Expanded(
child: ListView.builder(
scrollDirection: Axis.vertical,
itemCount: assetList.length,
itemBuilder: (context, index) {
return AssetCryptoCard(
name: assetList[index].name,
image: assetList[index].image,
);
},
),
),
],
),
),
),
),
);
}
}
The Asset class is as follows. To derive assetList.
class Asset {
String name;
String image;
num currentPrice;
num priceChange24h;
num priceChangePercentage24h;
String symbol;
Asset({
required this.name,
required this.image,
required this.currentPrice,
required this.priceChange24h,
required this.priceChangePercentage24h,
required this.symbol,
});
factory Asset.fromJson(Map<String, dynamic> json) {
return Asset(
name: json['name'],
symbol: json['symbol'],
image: json['image'],
currentPrice: json['current_price'],
priceChange24h: json['price_change_24h'],
priceChangePercentage24h: json['price_change_percentage_24h'],
);
}
}
List<Asset> assetList = [];
The AssetCryptoCard class is as follows.
class AssetCryptoCard extends StatelessWidget {
AssetCryptoCard({
required this.name,
required this.image,
});
final String name;
final String image;
#override
Widget build(BuildContext context) {
return InkWell(
onTap: () {
showModalBottomSheet(
context: context,
builder: (context) => EditAssetScreen(),
);
},
child: Container(
padding: EdgeInsets.only(),
child: Column(
children: [
Row(
children: [
Container(child: Image.network(image)),
SizedBox(),
Text(name),
Spacer(),
Icon(Icons.arrow_forward_ios_rounded),
],
),
Container(),
],
),
),
);
}
}
I have written the code below but unsure where is the right place to put it. Also, presumably this is the right code to do a search/filter on the list.
List<Map<String, dynamic>> foundAssetList = [];
#override
initState() {
foundAssetList = assetList;
super.initState();
}
void _runFilter(String enteredKeyword) {
List<Map<String, dynamic>> results = [];
if (enteredKeyword.isEmpty) {
results = assetList;
} else {
results = assetList
.where((user) =>
user["name"].toLowerCase().contains(
enteredKeyword.toLowerCase().toList();
}
setState(() {
foundAssetList = results;
});
}
Any help would be much appreciated.
You're missing a few things:
You need a state that keeps track of the text in the TextField - you can do this by adding a state variable and use onChanged of the TextField to update the variable
Write a function that returns a list of the cryptocurrencies based on the keyword: You can create a function that returns the list of all cryptocurrencies if the keyword is empty, else return a filtered list
Replace the assetList variable in the ListView.builder with the filteredList (should be the value of the function in step 2)
The result code should look like this (put this in _AddCryptoAssetScreenState:
String _keyword = "";
List<Asset> _getFilteredList() {
if (_keyword.isEmpty) {
return assetList;
}
return assetList
.where((user) =>
user.name.toLowerCase().contains(
_keyword.toLowerCase())).toList();
}
#override
Widget build(BuildContext context) {
final filteredList = _getFilteredList();
return GestureDetector(
behavior: HitTestBehavior.opaque,
onTap: () => Navigator.pop(context),
child: DraggableScrollableSheet(
builder: (_, controller) => Container(
decoration: BoxDecoration(),
clipBehavior: Clip.antiAlias,
child: Scaffold(
appBar: AppBar(),
body: Column(
children: [
Container(
margin: const EdgeInsets.fromLTRB(),
child: TextField(
keyboardType: TextInputType.text,
textAlign: TextAlign.start,
decoration: InputDecoration(
prefixIcon: const Icon(Icons.search),
hintText: 'Search',
border: OutlineInputBorder(),
contentPadding: EdgeInsets.only()),
onChanged: (text) {
setState(() {
_keyword = text;
});
},
),
),
Expanded(
child: ListView.builder(
scrollDirection: Axis.vertical,
itemCount: filteredList.length,
itemBuilder: (context, index) {
return AssetCryptoCard(
name: filteredList[index].name,
image: filteredList[index].image,
);
},
),
),
],
),
),
),
),
);
}
Let me know if this works.
Use the textfoemfield onChanged property
TextFormField(
OnChanged: (value){
_runFilter(value);
}
);
Or
For API request use future feature in textfield_search package
please review this package can provide solution with different approach
I hope it works for you.

Flutter Refresh grid view with selected item

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

Flutter List View Data Duplication when I do setState and pagination with StreamBuilder

I am implementation the list data from API to show in list view. There I am using Streambuiler and pull to refresh library for scrolling changes. I set the new data from API to local list inside StreamBuilder. Every time when I made setState for state changes. The StreamBuilder rebuild and setting data again. Then my list was duplicated, I do not know yet what's wrong with my code. I am developing the App with flutter recently. Please check my code what is wrong in there. 🙏🙏🙏
class OrderListScreen extends StatefulWidget {
#override
_OrderListScreenState createState() => _OrderListScreenState();
}
class _OrderListScreenState extends State<OrderListScreen> {
String showingFromDate, showingToDate;
OrderBloc _orderBloc;
OrderListOb _orderListOb = OrderListOb();
int paginationPage = 0;
DateTime fromDate, toDate;
List<Orders> _orderList = [];
var _refreshController = RefreshController();
#override
void initState() {
super.initState();
_orderBloc = OrderBloc();
initDate();
fetchAPI();
}
#override
void dispose() {
super.dispose();
_orderBloc.dispose();
}
#override
Widget build(BuildContext context) {
return Scaffold(
appBar: defaultAppBar("Order List Screen", showBackArrow: false),
body: Padding(
padding: const EdgeInsets.all(8.0),
child: Column(
children: [
Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: [
textView("From Date", isBold: true),
textView("To Date", isBold: true),
],
),
Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: [
ActionChip(
padding: EdgeInsets.all(4.0),
avatar: Icon(
Icons.calendar_today_sharp,
color: Colors.white,
size: 18,
),
backgroundColor: PRIMARY_COLOR,
label: textView(showingFromDate, textColor: Colors.white),
onPressed: () {
_selectDate(context, isFromDate: true);
},
),
ActionChip(
padding: EdgeInsets.all(4.0),
avatar: Icon(
Icons.calendar_today_sharp,
color: Colors.white,
size: 18,
),
backgroundColor: PRIMARY_COLOR,
label: textView(showingToDate, textColor: Colors.white),
onPressed: () {
_selectDate(context, isFromDate: false);
},
),
],
),
StreamBuilder(
stream: _orderBloc.orderListStream(),
initialData:
BaseResponse(data: null, message: MsgState.loading),
builder: (BuildContext context, AsyncSnapshot snapshot) {
BaseResponse ob = snapshot.data;
if (ob.message == MsgState.loading) {
return Center(
child: Container(
child: CircularProgressIndicator(),
),
);
} else if (ob.message == MsgState.data ) {
_orderListOb = OrderListOb();
_orderListOb = ob.data;
_orderList.addAll(_orderListOb.result.orders);
return buildListView();
} else {
return handleErrorWidget(ob.data);
}
},
),
],
),
));
}
Widget buildListView() {
return Container(
height: 450,
child: SmartRefresher(
enablePullUp: _orderListOb.result.orders.length > 9,
enablePullDown: true,
onRefresh: () {
print("Pull To Refresh");
},
onLoading: () {
paginationPage = _orderListOb.result.pagination.nextId;
fetchAPI(); //Do pagination
},
controller: _refreshController,
child: ListView.builder(
shrinkWrap: true,
physics: ClampingScrollPhysics(),
itemCount: _orderList.length,
itemBuilder: (BuildContext context, int index) {
return InkWell(
onTap: () {
context.navigateName("order_detail", data: {
'isAcceptOrder': true,
'order_id': _orderList[index].id
});
},
child: orderItemWidget(_orderList[index],
isAcceptedOrder: false));
}),
),
);
}
void fetchAPI() {
_orderBloc.getOrderList(
serviceId: ServiceTypes.AIRCON_SERVICE.value,
fromDate: DateUtil.requestDateFormat(fromDate.toString()),
toDate: DateUtil.requestDateFormat(toDate.toString()),
page: paginationPage);
}
void initDate() {
showingFromDate = DateUtil.covertDate(DateUtil.getCurrentDate().toString());
showingToDate = DateUtil.covertDate(DateUtil.getCurrentDate().toString());
fromDate = DateUtil.getCurrentDate();
toDate = DateUtil.getCurrentDate();
}
void resetData() {
print("Clear");
paginationPage = 0;
_orderList.clear();
}
_selectDate(BuildContext context, {bool isFromDate = true}) async {
final DateTime picked = await showDatePicker(
context: context,
initialDate: isFromDate ? fromDate : toDate, // Refer step 1
firstDate: DateTime(2000),
lastDate: DateTime(2025),
);
if (picked != null)
setState(() {
if (isFromDate) {
showingFromDate = DateUtil.covertDate(picked.toString());
fromDate = picked;
} else {
showingToDate = DateUtil.covertDate(picked.toString());
toDate = picked;
}
resetData();
fetchAPI();
});
}
}
The problem is that you're doing _orderList.addAll(_orderListOb.result.orders); . Instead, you should clean the list before or just _orderList=_orderListOb.result.orders;
Check for existence the value before adding to list, if list doesnt contain the value then add like this:
if(!_orderList.contains(_orderListOb.result.orders))
_orderList.addAll(_orderListOb.result.orders);
I hope it will work!

Exporting a Flutter List to CSV file

I have this screen which lists the items of a class into ListView. What I would want is to have the button (FloatingActionButton in the code) that would export that list to a CSV file. I have the code of what I think it should look, but my issue is that I don't know how to use onPressed on the button in order to export, for now it is null, but it should call the getCSV() function with something in it. Here's the code:
class MailListAdmin extends StatefulWidget {
#override
_MailListAdminState createState() => _MailListAdminState();
}
class _MailListAdminState extends State<MailListAdmin> {
List<String> list = [];
List<Email> finalExport;
#override
void initState() {
super.initState();
DatabaseProvider.db.getMail().then(
(mailList) {
BlocProvider.of<MailBloc>(context).add(SetMail(mailList));
},
);
}
getCsv(List<Email> mailList) async {
List<List<dynamic>> rows = List<List<dynamic>>();
for (int i = 0; i < mailList.length; i++) {
List<dynamic> row = List();
row.add(mailList[i].id);
row.add(mailList[i].email);
rows.add(row);
setState(() {
mailList = finalExport;
});
print(rows);
}
await SimplePermissions.requestPermission(Permission.WriteExternalStorage);
bool checkPermission = await SimplePermissions.checkPermission(
Permission.WriteExternalStorage);
if (checkPermission) {
String dir =
(await getExternalStorageDirectory()).absolute.path + "/documents";
String file = "$dir";
File f = new File(file + "filename.csv");
String csv = const ListToCsvConverter().convert(rows);
f.writeAsString(csv);
}
}
//-------------------------------------
#override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
backgroundColor: Colors.blueGrey[900],
title: Text('Newsletter Users'),
actions: [
Padding(
padding: EdgeInsets.only(right: 20.0),
child: Badge(
child: Text('Copied ' + list.length.toString()),
showBadge: false)),
],
),
body: Container(
child: BlocConsumer<MailBloc, List<Email>>(
builder: (context, mailList) {
return ListView.separated(
itemCount: mailList.length,
itemBuilder: (BuildContext context, int index) {
Email mail = mailList[index];
return Card(
child: ListTile(
leading: GestureDetector(
child: Icon(Icons.copy),
onTap: () {
FlutterClipboard.copy(mail.email);
print('Copied ${mail.email}!');
Scaffold.of(context).showSnackBar(SnackBar(
backgroundColor: Colors.grey[600],
duration: Duration(milliseconds: 1000),
content: Text(
'${mail.email} Copied To Clipboard!',
style: TextStyle(fontSize: 20.0),
),
));
list.add(mail.email);
setState(() {
list.length.toString();
});
print(mail.email);
print(list);
}),
trailing: Icon(Icons.delete),
onTap: () {
DatabaseProvider.db.deleteMail(mail.id).then((_) {
BlocProvider.of<MailBloc>(context).add(
DeleteMail(index),
);
});
list.remove(mail.email);
print(list);
setState(() {
list.length.toString();
});
},
title: Text(
mail.email,
style: TextStyle(fontSize: 20.0),
),
),
);
},
separatorBuilder: (BuildContext context, int index) =>
Divider(color: Colors.black),
);
},
listener: (BuildContext context, mailList) {},
),
),
floatingActionButton: FloatingActionButton(
child: Text('Export'),
onPressed: () =>
null, //im not sure how to add the mailList to getCsv(???)
),
);
}
}
Check the export button, where I am not sure how to activate it with getCSV function. Also, how would I access the saved CSV file after it is saved (where would it be saved in the device?)
Try the following steps:
Create a new list in the class.
Assign it to the mail list from the BlocConsumer.
Then use the list in onPressed by passing it to getCsv method.
The Implementation
theEmails is the new list.
class _MailListAdminState extends State<MailListAdmin> {
List<String> list = [];
List<Email> finalExport;
#override
void initState() {
super.initState();
DatabaseProvider.db.getMail().then(
(mailList) {
BlocProvider.of<MailBloc>(context).add(SetMail(mailList));
},
);
}
List<Email> theEmails = [];
getCsv(List<Email> mailList) async {
List<List<dynamic>> rows = List<List<dynamic>>();
for (int i = 0; i < mailList.length; i++) {
List<dynamic> row = List();
row.add(mailList[i].id);
row.add(mailList[i].email);
rows.add(row);
setState(() {
mailList = finalExport;
});
print(rows);
}
await SimplePermissions.requestPermission(Permission.WriteExternalStorage);
bool checkPermission = await SimplePermissions.checkPermission(
Permission.WriteExternalStorage);
if (checkPermission) {
String dir =
(await getExternalStorageDirectory()).absolute.path + "/documents";
String file = "$dir";
File f = new File(file + "filename.csv");
String csv = const ListToCsvConverter().convert(rows);
f.writeAsString(csv);
}
}
//-------------------------------------
#override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
backgroundColor: Colors.blueGrey[900],
title: Text('Newsletter Users'),
actions: [
Padding(
padding: EdgeInsets.only(right: 20.0),
child: Badge(
child: Text('Copied ' + list.length.toString()),
showBadge: false)),
],
),
body: Container(
child: BlocConsumer<MailBloc, List<Email>>(
builder: (context, mailList) {
theEmails = mailList;
return ListView.separated(
itemCount: mailList.length,
itemBuilder: (BuildContext context, int index) {
Email mail = mailList[index];
return Card(
child: ListTile(
leading: GestureDetector(
child: Icon(Icons.copy),
onTap: () {
FlutterClipboard.copy(mail.email);
print('Copied ${mail.email}!');
Scaffold.of(context).showSnackBar(SnackBar(
backgroundColor: Colors.grey[600],
duration: Duration(milliseconds: 1000),
content: Text(
'${mail.email} Copied To Clipboard!',
style: TextStyle(fontSize: 20.0),
),
));
list.add(mail.email);
setState(() {
list.length.toString();
});
print(mail.email);
print(list);
}),
trailing: Icon(Icons.delete),
onTap: () {
DatabaseProvider.db.deleteMail(mail.id).then((_) {
BlocProvider.of<MailBloc>(context).add(
DeleteMail(index),
);
});
list.remove(mail.email);
print(list);
setState(() {
list.length.toString();
});
},
title: Text(
mail.email,
style: TextStyle(fontSize: 20.0),
),
),
);
},
separatorBuilder: (BuildContext context, int index) =>
Divider(color: Colors.black),
);
},
listener: (BuildContext context, mailList) {},
),
),
floatingActionButton: FloatingActionButton(
child: Text('Export'),
onPressed: (){
getCsv(theEmails);
}, //im not sure how to add the mailList to getCsv(???)
),
);
}
}