How to give gradient ListView.builder items? - flutter

I have a listview.builder in my code and I have multiple widgets inside it. The problem is, that I have a list of numbers and I'm trying to make the gradient background of specific numbers. I only know starting index and length of the gradient background. But ListView.builder childrens building lazily. For example, I'm creating gradient numbers from 10th to 20th but it's not drawing if starting point is not showing in screen.
How can I show all colored/gradient numbers inside of ListView.builder? I don't want to use SingleChildScrollView and Column/Row, that may cause an performance issue. Is there any way to draw gradient items even if starting point not showing in screen?
Dartpad Example
I have data like that
final items = [
for (var index = 1; index <= 30; index++)
if (index == 10)
DayType(
value: index,
type: GradientType.gradient,
)
else
DayType(
value: index,
type: GradientType.common,
)
];
and I'm trying to show inside of listView.
ListView.builder(
controller: _scrollController,
itemExtent: 40,
padding: EdgeInsets.symmetric(
horizontal: (width - 40) / 2,
),
itemCount: items.length,
physics: _SnapScrollPhysics(
itemSize: 40,
onItemPositionUpdate: (index) {},
),
scrollDirection: Axis.horizontal,
itemBuilder: (context, index) {
final item = items;
return Padding(
padding: const EdgeInsets.symmetric(
horizontal: 2,
),
child: GestureDetector(
onTap: () => _scrollToPosition(index),
child: Column(
children: [
DayIndicator(
day: item[index],
),
],
),
),
);
},
),
class DayIndicator extends StatelessWidget {
const DayIndicator({
Key? key,
required this.day,
}) : super(key: key);
final DayType day;
#override
Widget build(BuildContext context) {
if (day.type == GradientType.common) {
return _CommonIndicator(
day: day.value,
);
} else if (day.type == GradientType.gradient) {
return _GradientIndicator(
day: day.value,
length: 8,
);
} else {
throw Exception('Unknown type: ${day.type}');
}
}
}

Related

Flutter - Divider widget changing width based on screen position

I am trying to use the Divider class. It seems that its rendered width is changing based on where it is on the screen even though I have explicitly set the width.
I have the following code (which I've simplified a bit):
class HistoryListState extends State<HistoryList> {
// use this function to generate the list of widgets used as a label for each list item
// each label consists of the workout name, a divider, and the first 3 exercises
List<Widget> generateLabel(TrackedWorkout workout) {
List<Widget> widgets = [
Text(workout.name),
// DIVIDER IN QUESTION
const Divider(
color: Color.fromARGB(255, 0, 0, 0),
thickness: 1,
),
];
for (int i = 0; i < workout.exercises.length; i++) {
if (i > 2) {
widgets.add(const Text('...'));
break;
}
widgets.add(Text(workout.exercises[i].name));
}
return widgets;
}
#override
Widget build(BuildContext context) {
return Scrollbar(
child: ListView.builder(
itemCount: widget.workouts.length,
itemBuilder: (BuildContext context, int index) {
return ElevatedButton(
onPressed: () {},
child: Column(
children: generateLabel(widget.workouts[index]),
),
);
}),
);
}
}
Video demo
As you can see, initially, each divider has slightly different widths. Then when the list is scrolled, the dividers change widths too.

checkbox lost checked value in flutter

I show my list of answers via ListView.builder and check value on checkbox work ok, but when I scroll down and turn back checked value is lost. Other way when lost focus in checked answer automatic checkbox lost checked value.
Below is my code. I would be grateful if someone could help me.
class AnswerItem extends StatefulWidget {
#override
_AnswerItemState createState() => _AnswerItemState();
}
class _AnswerItemState extends State<AnswerItem> {
List<bool> _data = [false, false, false, false];
void _onChange(bool value, int index) {
setState(() {
_data[index] = value;
});
}
Widget build(BuildContext context) {
final questionItems = Provider.of<Item>(context);
List<Answer> listOfAnswers = questionItems.answers.toList();
return SingleChildScrollView(
child:
ListView.builder(
shrinkWrap: true,
itemCount: listOfAnswers.length,
itemBuilder: (context, index) {
return Padding(
padding: const EdgeInsets.symmetric(horizontal: 25),
child: Card(
child: Padding(
padding: const EdgeInsets.symmetric(horizontal: 10),
child: CheckboxListTile(
value: _data[index],
title: Text(listOfAnswers[index].title),
onChanged: (val) {
_onChange(val, index);
},
),
),
),
);
},
),
);
}
}
Somewhere, you're confusing "model" with "view", and storing state in the view. This necessarily means when the view is refreshed or updated, you will lose state.
Specifically, your model here appears to be listOfAnswers, which being a local variable inside a build() method, will possibly be rebuilt on every refresh (possibly 120 fps!). You need to put your model outside any build method.

How can I use List view.builder and item builder with a select range?

Lets say I have a list of numbers from one to ten. How can I use list view builder/ item builder to, say, only print 2-7 or 7-9 instead of the entire list?
You can use the core library provided by dart to handle value generation for List as follow:
var listNumber = new List<int>.generate(10, (i) => i + 1);
And then loop through your list in ListView.builder or just use ListView since you haven't a huge list ,
ListView.builder(
itemCount: listNumber.length,
itemBuilder: (_, i) => Text(listNumber[i]),
),
with some improvement, the whole working app would be :
import 'package:flutter/material.dart';
void main() => runApp(MyApp());
class MyApp extends StatelessWidget {
var listNumber = new List<int>.generate(10, (i) => i + 1);
var startItem = 2;
var endItem = 7;
bool all = false;
#override
Widget build(BuildContext context) {
return MaterialApp(
title: 'Printing number',
home: Scaffold(
appBar: AppBar(
title: Text('Printing number'),
),
body: Padding(
padding: EdgeInsets.symmetric(
vertical: 15.0,
horizontal: 10.0,
),
child: ListView.builder(
itemCount: all ? listNumber.length : (endItem - startItem) + 1,
itemBuilder: (_, i) => PrintRange(i,all,listNumber,startItem),
),
),
),
);
}
}
class PrintRange extends StatelessWidget {
final int i;
final bool all;
final List listNumber;
final int startItem;
PrintRange(this.i,this.all,this.listNumber,this.startItem);
#override
Widget build(BuildContext context) {
return Card(
elevation: 5.0,
child: ListTile(
title: Center(
child: Text(
all ? listNumber[i].toString() : listNumber[i + startItem].toString(),
style: TextStyle(fontSize: 20.0),
),
),
),
);
}
}
As a bonus, you can use the Slider class to improve it : here
List views build its items one by one. if you assigned a key to each item and then before returning the you can try returning a empty widget if that key is met.
put your range into a List and check if the item, the list view returns has that property
List range = [];
modify the List view,
ListView.builder(itemBuilder: (context, index){
if (range.contains('Your Key')){
return Container();
} else {
return Your Normal List Item;
}
});
Instead of giving a useless container widget, you should use itemBuilder and simple maths.
Taking your example suppose We have a list of numbers:
List<int> numbers = [0,1, 2, 3, 4, 5, 6, 7, 8, 9];
Now we can have start and end index for which you want the list to be shown
int startIndex = 2;
int endIndex = 7;
So now using simple maths you could achieve what you want. Just see the code below, it works like a charm and is more efficient rather than provide a widget which would be no use:
int itemCount = (endIndex - startIndex)+1;
now use this in your listview as:
ListView.builder(
itemCount: itemCount,
itemBuilder: (context, index) {
return (Text(numbers[startIndex + index].toString()));
})
So the whole working code would be:
Widget build(BuildContext context) {
List<int> numbers = [0, 1, 2, 3, 4, 5, 6, 7, 8, 9];
int startIndex = 2;
int endIndex = 6;
int itemCount = (endIndex - startIndex) + 1;
return Scaffold(
body: Center(
child: SizedBox(
height: 300,
width: 100,
child: ListView.builder(
itemCount: itemCount,
itemBuilder: (context, index) {
return (Text(numbers[startIndex + index].toString()));
}),
),
),
);
}
So you can customize the way you want your listview builder to work

RangeError (index): Invalid value: Valid value range is empty: 1

after whole day of trying to solve this myself I had to come and ask for help.
I'm trying to build this ListView.builder, it has fixed amount of itemCount. And its building Widgets using data retrieved from locally stored JSON file.
I'm using Provider to pass that data around. The problem is, on app start or hot restart that ListView.builder turns red and shows error, and then after like quarter of a second it shows my data.
I understand why this happens, my list of data that I get from json is initially empty. So I put ternary operator like: provider.data == null ? CircularProgressIndicator() : ListView.builder... but this doesnt stop it from crashing.
I dont know why and its driving me crazy. Here is full code:
We are talking here about widget called RecommendedCardList, its showing widgets from above mentioned list by having random number (in range of list length) as index.
I have similar ListView on HomeScreen called CategoryCardList and its working similarly to RecommendedCardList but I'm not having this issue with it. Also the rest of the home screen shows good, only the portion where RecommendedCardList is turns red for a short period of time.
Home Screen class:
class HomeScreen extends StatelessWidget {
#override
Widget build(BuildContext context) {
// Get user's screen properties
// We are using this properties to adjust size accordingly
// In order to achieve maximum responsivnes for different screen sizes
var height = MediaQuery.of(context).size.height;
var width = MediaQuery.of(context).size.width;
var repoProvider = Provider.of<Repository>(context);
var recipeDataList = repoProvider.recipeDataList;
return Container(
color: backgroundColor,
child: repoProvider.recipeDataList == null
? Center(child: CircularProgressIndicator())
: Padding(
padding: contentPadding,
child: ListView(
children: <Widget>[
AppTitle(),
SizedBox(
height: height * 0.03,
),
Column(
children: <Widget>[
CategoryAndSeeAll(),
CategoryCardsList(height: height, provider: repoProvider),
],
),
SizedBox(
height: height * 0.05,
),
Container(
width: double.infinity,
height: height * 0.1,
decoration: BoxDecoration(
border: Border.all(color: accentColor),
),
child: Text(
'Reserved for AD',
textAlign: TextAlign.center,
),
),
SizedBox(
height: height * 0.05,
),
RecommendedCardsList(height: height, width: width, recipeDataList: recipeDataList),
],
),
),
);
}
}
RecommendedCardsList class:
class RecommendedCardsList extends StatelessWidget {
const RecommendedCardsList({
Key key,
#required this.height,
#required this.width,
#required this.recipeDataList,
}) : super(key: key);
final double height;
final double width;
final recipeDataList;
#override
Widget build(BuildContext context) {
return Container(
height: height * 0.30,
child: ListView.builder(
scrollDirection: Axis.horizontal,
itemCount: numberOfRecommendedRecipes,
itemBuilder: (context, counter) {
int randomNumber = Random().nextInt(recipeDataList.length);
return Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: <Widget>[
RecommendedCard(
width: width,
height: height,
imagePath: recipeDataList.elementAt(randomNumber).image,
text: recipeDataList.elementAt(randomNumber).title,
),
],
);
}),
);
}
}
Repository class:
class Repository extends ChangeNotifier {
Repository() {
loadJson();
}
var _recipeData;
List<RecipeModel> _recipeDataList = [];
List<RecipeModel> get recipeDataList => _recipeDataList;
void loadJson() async {
var json = await rootBundle.loadString('assets/recipes.json');
var parsedJson = jsonDecode(json);
for (var item in parsedJson) {
_recipeData = RecipeModel.fromJson(item);
_recipeDataList.add(_recipeData);
}
//print('Title:${_recipeDataList[0].title}\nImage:${_recipeDataList[0].image}'); // For debugging
notifyListeners();
}
}
This error is related to the fact that the code searched for an index in your list and this index is more than you list length.
I think the error is in that part:
int randomNumber = Random().nextInt(recipeDataList.length);
Supposing the length is 10 the random function will retrieve a num between 0 and 10, but the last index is 9.
With that in mind, I have two suggestions:
1)
// changing ternary logic
(repoProvider.recipeDataList == null && repoProvider.recipeDataList.length > 0)
2)
// inside ListView.Builder change to get the list length
itemCount: recipeDataList.length
Put the following condition in build() of RecommendedCardsList widget as the first line.
if(recipeDataList == null || recipeDataList.length == 0){
return Container();
}

Flutter ListView lazy loading

How can I realize items lazy loading for endless listview? I want to load more items by network when user scroll to the end of listview.
You can listen to a ScrollController.
ScrollController has some useful information, such as the scrolloffset and a list of ScrollPosition.
In your case the interesting part is in controller.position which is the currently visible ScrollPosition. Which represents a segment of the scrollable.
ScrollPosition contains informations about it's position inside the scrollable. Such as extentBefore and extentAfter. Or it's size, with extentInside.
Considering this, you could trigger a server call based on extentAfter which represents the remaining scroll space available.
Here's an basic example using what I said.
class MyHome extends StatefulWidget {
#override
_MyHomeState createState() => _MyHomeState();
}
class _MyHomeState extends State<MyHome> {
ScrollController controller;
List<String> items = List.generate(100, (index) => 'Hello $index');
#override
void initState() {
super.initState();
controller = ScrollController()..addListener(_scrollListener);
}
#override
void dispose() {
controller.removeListener(_scrollListener);
super.dispose();
}
#override
Widget build(BuildContext context) {
return Scaffold(
body: Scrollbar(
child: ListView.builder(
controller: controller,
itemBuilder: (context, index) {
return Text(items[index]);
},
itemCount: items.length,
),
),
);
}
void _scrollListener() {
print(controller.position.extentAfter);
if (controller.position.extentAfter < 500) {
setState(() {
items.addAll(List.generate(42, (index) => 'Inserted $index'));
});
}
}
}
You can clearly see that when reaching the end of the scroll, it scrollbar expends due to having loaded more items.
Thanks for Rémi Rousselet's approach, but it does not solve all the problem. Especially when the ListView has scrolled to the bottom, it still calls the scrollListener a couple of times. The improved approach is to combine Notification Listener with Remi's approach. Here is my solution:
bool _handleScrollNotification(ScrollNotification notification) {
if (notification is ScrollEndNotification) {
if (_controller.position.extentAfter == 0) {
loadMore();
}
}
return false;
}
#override
Widget build(BuildContext context) {
final Widget gridWithScrollNotification = NotificationListener<
ScrollNotification>(
onNotification: _handleScrollNotification,
child: GridView.count(
controller: _controller,
padding: EdgeInsets.all(4.0),
// Create a grid with 2 columns. If you change the scrollDirection to
// horizontal, this would produce 2 rows.
crossAxisCount: 2,
crossAxisSpacing: 2.0,
mainAxisSpacing: 2.0,
// Generate 100 Widgets that display their index in the List
children: _documents.map((doc) {
return GridPhotoItem(
doc: doc,
);
}).toList()));
return new Scaffold(
key: _scaffoldKey,
body: RefreshIndicator(
onRefresh: _handleRefresh, child: gridWithScrollNotification));
}
The solution use ScrollController and I saw comments mentioned about page.
I would like to share my finding about package incrementally_loading_listview
https://github.com/MaikuB/incrementally_loading_listview.
As packaged said : This could be used to load paginated data received from API requests.
Basically, when ListView build last item and that means user has scrolled down to the bottom.
Hope it can help someone who have similar questions.
For purpose of demo, I have changed example to let a page only include one item
and add an CircularProgressIndicator.
...
bool _loadingMore;
bool _hasMoreItems;
int _maxItems = 30;
int _numItemsPage = 1;
...
_hasMoreItems = items.length < _maxItems;
...
return IncrementallyLoadingListView(
hasMore: () => _hasMoreItems,
itemCount: () => items.length,
loadMore: () async {
// can shorten to "loadMore: _loadMoreItems" but this syntax is used to demonstrate that
// functions with parameters can also be invoked if needed
await _loadMoreItems();
},
onLoadMore: () {
setState(() {
_loadingMore = true;
});
},
onLoadMoreFinished: () {
setState(() {
_loadingMore = false;
});
},
loadMoreOffsetFromBottom: 0,
itemBuilder: (context, index) {
final item = items[index];
if ((_loadingMore ?? false) && index == items.length - 1) {
return Column(
children: <Widget>[
ItemCard(item: item),
Card(
child: Padding(
padding: const EdgeInsets.all(16.0),
child: Column(
children: <Widget>[
Row(
crossAxisAlignment:
CrossAxisAlignment.start,
children: <Widget>[
Container(
width: 60.0,
height: 60.0,
color: Colors.grey,
),
Padding(
padding: const EdgeInsets.fromLTRB(
8.0, 0.0, 0.0, 0.0),
child: Container(
color: Colors.grey,
child: Text(
item.name,
style: TextStyle(
color: Colors.transparent),
),
),
)
],
),
Padding(
padding: const EdgeInsets.fromLTRB(
0.0, 8.0, 0.0, 0.0),
child: Container(
color: Colors.grey,
child: Text(
item.message,
style: TextStyle(
color: Colors.transparent),
),
),
)
],
),
),
),
Center(child: CircularProgressIndicator())
],
);
}
return ItemCard(item: item);
},
);
full example https://github.com/MaikuB/incrementally_loading_listview/blob/master/example/lib/main.dart
Package use ListView index = last item and loadMoreOffsetFromBottom to detect when to load more.
itemBuilder: (itemBuilderContext, index) {
if (!_loadingMore &&
index ==
widget.itemCount() -
widget.loadMoreOffsetFromBottom -
1 &&
widget.hasMore()) {
_loadingMore = true;
_loadingMoreSubject.add(true);
}
here is my solution for find end of listView
_scrollController.addListener(scrollListenerMilli);
if (_scrollController.position.pixels == _scrollController.position.maxScrollExtent) {
getMoreData();
}
If you want to load more data when 1/2 or 3/4 of a list view size, then use this way.
if (_scrollController.position.pixels == (_scrollController.position.maxScrollExtent * .75)) {//.5
getMoreData();
}
Additional -> Make sure you called getMore API only one time when reaching to the bottom. You can solve this in many ways, This is one of the ways to solve this by boolean variable.
bool loadMore = false;
if (_scrollController.position.pixels == _scrollController.position.maxScrollExtent && !loadMore) {
loadMore = true;
getMoreData().then(() => loadMore = false);
}
here is my approach which is inspired by answers above,
NotificationListener(onNotification: _onScrollNotification, child: GridView.builder())
bool _onScrollNotification(ScrollNotification notification) {
if (notification is ScrollEndNotification) {
final before = notification.metrics.extentBefore;
final max = notification.metrics.maxScrollExtent;
if (before == max) {
// load next page
// code here will be called only if scrolled to the very bottom
}
}
return false;
}
Use lazy_load_scrollview: 1.0.0 package that use same concept behind the scenes that panda world answered here. The package make it easier to implement.
The solutions posted don't solve the issue if you want to achieve lazy loading in up AND down direction. The scrolling would jump here, see this thread.
If you want to do lazy loading in up and down direction, the library bidirectional_listview could help.
Example (Source):
static const double kItemHeight = 30.0;
BidirectionalScrollController controller;
double oldScrollPosition = 0.0;
#override
void initState() {
super.initState();
for (int i = -10; i <= 10; i++) {
items[i] = "Item " + i.toString();
}
controller = new BidirectionalScrollController()
..addListener(_scrollListener);
}
#override
void dispose() {
controller.removeListener(_scrollListener);
super.dispose();
}
#override
void build() {
// ...
List<int> keys = items.keys.toList();
keys.sort();
return new BidirectionalListView.builder(
controller: controller,
physics: AlwaysScrollableScrollPhysics(),
itemBuilder: (context, index) {
return Container(
child: Text(items[index]),
height: kItemHeight,
},
itemCount: keys.first,
negativeItemCount: keys.last.abs(),
);
// ...
}
// Reload new items in up and down direction and update scroll boundaries
void _scrollListener() {
bool scrollingDown = oldScrollPosition < controller.position.pixels;
List<int> keys = items.keys.toList();
keys.sort();
int negativeItemCount = keys.first.abs();
int itemCount = keys.last;
double positiveReloadBorder = (itemCount * kItemHeight - 3 * kItemHeight);
double negativeReloadBorder =
(-(negativeItemCount * kItemHeight - 3 * kItemHeight));
// reload items
bool rebuildNecessary = false;
if (scrollingDown && controller.position.pixels > positiveReloadBorder)
{
for (int i = itemCount + 1; i <= itemCount + 20; i++) {
items[i] = "Item " + i.toString();
}
rebuildNecessary = true;
} else if (!scrollingDown &&
controller.position.pixels < negativeReloadBorder) {
for (int i = -negativeItemCount - 20; i < -negativeItemCount; i++) {
items[i] = "Item " + i.toString();
}
rebuildNecessary = true;
}
// set new scroll boundaries
try {
BidirectionalScrollPosition pos = controller.position;
pos.setMinMaxExtent(
-negativeItemCount * kItemHeight, itemCount * kItemHeight);
} catch (error) {
print(error.toString());
}
if (rebuildNecessary) {
setState(({});
}
oldScrollPosition = controller.position.pixels;
}
I hope that this helps a few people :-)
The accepted answer is correct but you can also do as follows,
Timer _timer;
Widget chatMessages() {
_timer = new Timer(const Duration(milliseconds: 300), () {
_scrollController.animateTo(
_scrollController.position.maxScrollExtent,
curve: Curves.easeOut,
duration: const Duration(milliseconds: 300),
);
});
return StreamBuilder(
stream: chats,
builder: (context, snapshot) {
return snapshot.hasData
? ListView.builder(
// physics: NeverScrollableScrollPhysics(),
controller: _scrollController,
shrinkWrap: true,
reverse: false,
itemCount: snapshot.data.documents.length,
itemBuilder: (context, index) {
return MessageTile(
message: snapshot.data.documents[index].data["message"],
sendByMe: widget.sendByid ==
snapshot.data.documents[index].data["sendBy"],
);
})
: Container();
},
);
}
There is also this package, taking away the boilerplate: https://pub.dev/packages/lazy_load_scrollview
There is a much simpler solution than working with Scroll Controllers and Notifications. Just use the built in lazy loading feature of ListView Builders:
I suggest (and tested) to just wrap two FutureBuilders within each other and let them handle everything for you. Alternatively, the outer FutureBuilder can be replaced by loading the values in the initState.
Create FutureBuilder to retrieve the most compact version of your data. Best a url or an id of the data items to be displayed
Create a ListView.builder, which according to the flutter doc Flutter Lists Codebook, already takes care of the lazy loading part
The standard ListView constructor works well for small lists. To work with lists that contain a large number of items, it’s best to
use the ListView.builder constructor.
In contrast to the default ListView constructor, which requires creating all items at once, the ListView.builder() constructor
creates items as they’re scrolled onto the screen.
Within the ListView builder, add another FutureBuilder, which fetches the individual content.
You're done
Have a look at this example code.
#override
Widget build(BuildContext context) {
return FutureBuilder(
future: <get a short list of ids to fetch from the web>,
builder: (BuildContext context, AsyncSnapshot<List<int>> snapshot) {
if (snapshot.hasData) {
return Expanded(
child: ListView.builder(
itemCount: snapshot.data!.length,
itemBuilder: (BuildContext context, final int index) {
final int recordId = snapshot.data![index];
return FutureBuilder(
future: <get the record content from the web>,
builder: (BuildContext context,
AsyncSnapshot<Issue?> snapshot) {
if (snapshot.hasData) {
final Record? record = snapshot.data;
if (issue != null) {
return ListTile(
isThreeLine: true,
horizontalTitleGap: 0,
title: <build record widget>,
);
}
}
return ListTile(
isThreeLine: true,
horizontalTitleGap: 0,
title: const Text("Loading data..."));
});
}),
);
}
return const Text("Loading data...",
style: TextStyle(color: Colors.orange));
});
Let me know what you think. Performance was great when I've tried it, I'm wondering what you experienced with this. Sure, this needs some clean up, I know :D
This is an old question and the current answer is to use the ListView.builder method.
Same is true for the GridView.builder, please refer to the example below.
GridView.builder(
// ask GridView to cache and avoid redundant callings of Futures
cacheExtent: 100,
shrinkWrap: true,
itemCount: c.thumbnails.length,
// Define this as you like
gridDelegate: SliverGridDelegateWithFixedCrossAxisCount(
crossAxisCount: 3,
mainAxisSpacing: 0.0,
crossAxisSpacing: 0.0,
childAspectRatio: 1.0,
),
itemBuilder: (BuildContext context, int index) {
return FutureBuilder<Image>(builder: (ctx, snap) {
if (!snap.hasData) {
return const SizedBox.expand(); // show nothing
}
if (snap.hasError) {
return Text('An error occured ${snap.error}');
}
return snap.data!;
},
future: <YOUR THUMBNAIL FUTURE>,
);
}
);
You can handle it by knowing the current page and the last page
By using listview builder
itemBuilder: (context, index) {
if(list.length - 1 == index && currentPage! < lastPage!){
currentPage = currentPage! + 1;
/// Call your api here to update the list
return Progress();
}
return ///element widget here.
},