How to clear objects in a listviewbuilder in flutter? - flutter

How to control listviewbuilder from outside the listview in flutter?
In a textfield I can use a controller like so: controller: Textcontroller. Can I do something similar in listviewbuilder to clear all the objects in it?
So to be exact. My code looks something like this
Expanded(
child: new ListView.builder(
itemCount: List.length,
itemBuilder: (context,index){
return new Card(
//all stuff with data
),
),
);
},
....
How would I do so that when called from another function it removes all the items in the listview?

You need a Store class that holds this list along with list manipulation methods, then you can use provider for example to access that class and render that list.

You can define a variable in the state of your widget:
var _clear = false;
When this variable is true, the list will be cleared and when it's false, the list will be displayed. You can use setState to toggle this variable. Setting the itemCount of the ListView.builder to 0 clears the list.
Full code:
var _clear = false;
#override
Widget build(BuildContext context) {
return Scaffold(
body: Column(
children: <Widget>[
Padding(
padding: const EdgeInsets.all(30.0),
child: FlatButton(
child: Text('clear'),
color: Colors.pinkAccent,
onPressed: () {
setState(() {
_clear = true;
});
},
),
),
FlatButton(
child: Text('add'),
color: Colors.greenAccent,
onPressed: () {
setState(() {
_clear = false;
});
},
),
Expanded(
child: ListView.builder(
controller: _scrollController,
itemCount: _clear ? 0 : 100,
itemBuilder: (context, index) {
return Container(
height: 300,
child: Card(
child: Center(
child: Text('$index'),
),
),
);
},
),
),
],
),
);
}

Related

Listview.builder with dynamic items

I'm pretty new at Flutter and trying to make a simple app, where I fetch data with an API and trying to show the results.
This function is responsible to get the data (this function works fine, I get the data):
Connection connection = Connection();
String textValue = '';
Future<void> createlist() async {
List<MoviesByTitle> movieTitle = [];
String response = await connection.getMovieByTitle();
var data = jsonDecode(response);
var results = data['results'];
for (int i = 0; i < results.length; i++) {
movieTitle.add(
MoviesByTitle(
movieId: results[i]['id'],
title: results[i]['original_title'],
shortDescription: results[i]['overview'],
year: results[i]['release_date'],
),
);
}
}
And here comes the screen itself:
#override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: Text("Movies app"),
),
body: Column(
children: [
TextField(
decoration: const InputDecoration(
border: OutlineInputBorder(),
labelText: 'Movie title',
),
onSubmitted: (value) {
textValue = value;
},
onChanged: (value) {
textValue = value;
},
),
TextButton(
onPressed: () {
createlist();
},
child: Text("Press"),
),
Expanded(
child: ListView.builder(
itemCount: 30,
itemBuilder: (BuildContext context, int index) {
return Card(
child: Padding(
padding: const EdgeInsets.all(10.0),
child: Column(
children: [
Text("Movie title"),
Text("Short decscription"),
],
),
),
);
}),
),
],
),
bottomNavigationBar: BottomMenu(),
);
}
What I want is: if the TextButton is pressed to show the data of the movies in separate cards. Somehow I can not find a way to create cards dynamically based on the data from the API (maybe I will wrap the ListView builder with a Visibility widget).
Is there any way to change the number of the card and their content dynamically?
You have added the items tothe list movieTitle.. you can use that as a reference to build the ui.. You can try
ListView.builder(
itemCount: movieTitle.length,
itemBuilder: (BuildContext context, int index) {
return Card(
child: Padding(
padding: const EdgeInsets.all(10.0),
child: Column(
children: [
Text("${movieTitle[index].title}"),
Text("${movieTitle[index].shortDescription}>"),
],
),
),
);
}),
Also you may have to move the movieTitle variable outside the fetch api method so it can be accessed from the ui part too.

Flutter RangeError while when comparing two list

Hello I have 2 list and I want to use these in ListViewBuilder.
List's:
List times = ['08:30', '09:00', '09:30', '10:00', '13:00'];
List obj = [true,false,true];
I tried this:
ListView.builder(
controller: scrollController,
shrinkWrap: true,
itemCount: times.length,
itemBuilder: (BuildContext context, int index) {
return Padding(
padding: const EdgeInsets.all(8.0),
child: InkWell(
onTap: () {
setState(() {
selected = index;
debugPrint(tarih[index]);
});
},
child: Container(
color: obj[index] ? Colors.yellow : Colors.red,
height: 30,
width: 12,
child: Text(times[index]),
),
),
);
},
),
Here is the error:
I know what cause's this error. Because of obj.length does not match with times.length
But I still want to create the other Containers.
How do I solve this?
Many ways you can avoid this here min(int,int) method used lowest integer find
obj[min(index,obj.length-1)] ? Colors.yellow : Colors.red,
widget may like this
ListView.builder(
// controller: scrollController,
shrinkWrap: true,
itemCount: times.length,
itemBuilder: (BuildContext context, int index) {
return Padding(
padding: const EdgeInsets.all(8.0),
child: InkWell(
onTap: () {
setState(() {
selected = index;
// debugPrint(tarih[index]);
});
},
child: Container(
color: obj[min(index,obj.length-1)] ? Colors.yellow : Colors.red,
height: 30,
width: 12,
child: Text(times[index]),
),
),
);
},
)
You try to achieve dartpad live
class _MyAppState extends State<MyApp> {
int selected = 0;
#override
void initState() {
super.initState();
}
List times = ['08:30', '09:00', '09:30', '10:00', '13:00'];
List obj = [];
#override
Widget build(BuildContext context) {
var column = Column(
children: [
Container(
height: 200,
child: Row(
children: [
Expanded(
child: Image.network(
"https://upload.wikimedia.org/wikipedia/commons/thumb/3/3c/Salto_del_Angel-Canaima-Venezuela08.JPG/1200px-Salto_del_Angel-Canaima-Venezuela08.JPG",
// fit: BoxFit.cover,
fit: BoxFit.fitWidth,
),
),
],
),
)
],
);
obj = List.generate(times.length, (index) => false);
var children2 = [
ListView.builder(
// controller: scrollController,
shrinkWrap: true,
itemCount: times.length,
itemBuilder: (BuildContext context, int index) {
if (selected == index)
obj[index] = true;
else
obj[index] = false;
return Padding(
padding: const EdgeInsets.all(8.0),
child: InkWell(
onTap: selected != index
? () {
setState(() {
selected = index;
print(selected);
// debugPrint(tarih[index]);
});
}
: null,
child: Container(
color: obj[index]
? Colors.yellow
: Colors.red,
height: 30,
width: 12,
child: Text(times[index]),
),
),
);
},
),
DropdownButton(
items: [
DropdownMenuItem(
child: Text("1"),
value: "1",
onTap: () {},
)
],
onChanged: (values) {
// _dropdown1=values;
})
];
return MaterialApp(
// theme: theme(),
debugShowCheckedModeBanner: false,
home: Scaffold(
appBar: AppBar(),
body: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: children2,
)),
);
}
}
Well there might be other ways to do so, what I did was, just to copy the obj list items as many as there are items in times list in order. And if the number is not equal just add remaining number at last.
List times = [
'08:30',
'09:00',
'09:30',
'10:00',
'13:00',
'13:00',
'13:00',
'13:00',
'13:00',
];
List obj = [true, false, true];
#override
Widget build(BuildContext context) {
// Remaining length of times list after we copy
int remaining = times.length % obj.length;
//Exact Number of times to copy obj
int exactNumber = (times.length - remaining) ~/ obj.length;
List shallowList = [];
// Using for loop copy the list as many as exactNumber
for (int i = 0; i < exactNumber; i++) {
shallowList += obj;
}
// Add add remaining subList
// Then we have obj list with same length as times
List finalShallowList = [...shallowList, ...obj.sublist(0, remaining)];
// Create Separate Index for obj that we can reset
return Scaffold(
body: Container(
child: ListView.builder(
shrinkWrap: true,
itemCount: times.length,
itemBuilder: (BuildContext context, int index) {
return Padding(
padding: const EdgeInsets.all(8.0),
child: InkWell(
onTap: () {},
child: Container(
// Loop over finalShallowList instead of obj
color: finalShallowList[index] ? Colors.yellow : Colors.red,
height: 30,
width: 12,
child: Text(times[index]),
),
),
);
},
),
),
);

Removing button and load data

Hey guys I need help with removing this button and load data from json file without need to click on that button
Here's code
List _items = [];
// Fetch content from the json file
#override
Widget build(BuildContext context) {
Future readJson() async {
final String response =
await rootBundle.loadString('assets/aaaa.json');
final data = await json.decode(response);
setState(() {
_items = data['first'];
});
}
return Scaffold(
body: Padding(
padding: const EdgeInsets.all(25),
child: Column(
children: [
ElevatedButton(
child: const Text('Load Data'),
onPressed: readJson,
),
// Display the data loaded from sample.json
_items.isNotEmpty
? Expanded(
child: ListView.builder(
itemCount: _items.length,
itemBuilder: (context, index) {
return Card(
margin: const EdgeInsets.all(10),
child: ListTile(
leading: Text(_items[index]["aaaaa"]),
title: Text(_items[index]["aaaaa"]),
subtitle: Text(_items[index]["aaaaaa"]),
),
);
},
),
)
: Container()
],
),
),
);
}
You should check out Future Builder. There are some good examples on that page of how to use the widget, including how to show different widgets depending on if the data was loaded, is in the process of loading, or there was an error. readJson would be the future in your case.
Call initState() before build function
#overrride
initState() {
readJson();
super.initState();
}
Calling the readJson() function just before returning Scaffold will do what you want.
Widget build(BuildContext context) {
//load the json content
readJson();
return Scaffold(
body: Padding(
padding: const EdgeInsets.all(25),
child: Column(
children: [
// Display the data loaded from sample.json
_items.isNotEmpty
? Expanded(
child: ListView.builder(
itemCount: _items.length,
itemBuilder: (context, index) {
return Card(
margin: const EdgeInsets.all(10),
child: ListTile(
leading: Text(_items[index]["aaaaa"]),
title: Text(_items[index]["aaaaa"]),
subtitle: Text(_items[index]["aaaaaa"]),
),
);
},
),
)
: Container()
],
),
),
);}

Creating a new widget on User Click(Flat Button)

I have a Post class that creates a Post model. And I want to create this model every time a user clicks the flat button. What's the best way to go about this using the onPressed function?
It's going to be a post that holds the text the user adds to the text field and when they submit it will show on a new post.
u can try use listview.builder, this the simple example how to use it. i just edit default code when we created new project.
return Scaffold(
appBar: AppBar(
title: Text(widget.title),
),
body: Center(
child: ListView.builder(
itemCount: _counter,
reverse: true,
shrinkWrap: true,
itemBuilder: (BuildContext context, int index) {
return Padding(
padding: const EdgeInsets.all(10.0),
child: Container(
color: Colors.red,
child: Center(child: Text('data $index')),
),
);
},
)
),
floatingActionButton: FloatingActionButton(
onPressed: _incrementCounter,
tooltip: 'Increment',
child: Icon(Icons.add),
),
);
Basically you want to add items to a list, and then render the contents of that list.
List<String> messages = [];
...
onPressed: ()=> setState(()=>messages.add("SomeText"))
Then render the list:
//Can use the map() api, to convert the list of Strings into a list of widgets:
List<Widget> children = messages.map((m) => Text(m));
return ListView(children: children);
//Or, use ListView.builder() to create the widgets on demand:
return ListView.builder(
itemBuilder: (context, index)=>Text(messages[index]),
itemCount: messages.length
)
The builder method is better optimized for large lists.
here is simple demo
List<String> posts = [];
#override
Widget build(BuildContext context) {
return Scaffold(body: Column(
children: <Widget>[
ListView.builder(
itemCount: posts.length,
itemBuilder: (BuildContext ctxt, int index) {
return new Text(posts[index]);//use any widget
}
),
FloatingActionButton(
backgroundColor: Colors.white,
child: Icon(
Icons.close,
color: Colors.red,
),
onPressed: () {
setState(() {
posts.add(newpost);//add what you want
});
},
),
]),
);
}
i hope it helps..

Update view in listview.builder child

I just started working with flutter, so far so good. But I have an issue at the moment:
I wish to make a check Icon visible when I tap on the child view in a Listview.builder widget
child: ListView.builder(
shrinkWrap: true,
itemCount: users.length,
itemBuilder: (BuildContext context, int index){
// final item = feeds[index];
return FlatButton(
onPressed:(){
setState(() {
_selected = !_selected;
choosenUser = users[index];
print("the user:${users[index].fullName},$_selected");
});
},
child:(_selected) ? UserCard(users[index], _selected):UserCard(users[index], _selected)
);
}
)
inside UserCard there is a check Icon I wish to show or hide when the FlatButton in the ListView.builder is clicked.
I passed in a boolean to the UserCard but it does not work
class UserCard extends StatefulWidget{
UserItem userItem;
bool selected;
UserCard(this.userItem, this.selected);
#override
_UserCard createState() => _UserCard(userItem,selected);
}
class _UserCard extends State<UserCard>{
UserItem _userItem;
bool selected;
_UserCard(this._userItem, this.selected);
#override
Widget build(BuildContext context) {
// TODO: implement build
return /* GestureDetector(
onTap: () {
setState(() {
selected = !selected;
print("user:${_userItem.fullName}");
});
},
child:*/Container(
height:80 ,
child:
Column(
children: <Widget>[
Row(
children: <Widget>[
_userItem.profileUrl != null? CircleAvatar(child: Image.asset(_userItem.profileUrl),): Container(
width: 50,
height: 50,
decoration: BoxDecoration(
color: Colors.white70,
shape: BoxShape.circle,
image: DecorationImage(
image:AssetImage('assets/plus.png') //NetworkImage(renderUrl ??'assets/img.png')
)
),
),
SizedBox(width: 30,),
Expanded(
flex: 1,
child:
Container(
child:
Row(
children: <Widget>[
Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: <Widget>[
SizedBox(height: 12,),
_userItem.fullName != null? Text(_userItem.fullName, style: TextStyle(fontSize: 18)): Text('Anjelika Thompson', style: TextStyle(fontSize: 18),),
SizedBox(height: 12,),
Row(
//crossAxisAlignment: CrossAxisAlignment.start,
// mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: <Widget>[
Container(child: Icon(Icons.location_on),alignment: Alignment.topLeft,),
SizedBox(width: 10,),
_userItem.distance_KM.toString() != null ? Text(_userItem.distance_KM.toString()):Text('48.7 km')
]),
],
),
],
)
),
),
SizedBox(width: 0,),
selected ? Icon(Icons.check,color: Colors.red,size: 40,):SizedBox(child: Text('$selected'),)
],
),
Container(
height: 0.5,
color: Colors.grey,
)
],
) ,
// )
);
}
}
Please what am I doing wrong here
Save your selections in list of Boolean.
list<bool> selected = list<bool>();
child: ListView.builder(
shrinkWrap: true,
itemCount: users.length,
itemBuilder: (BuildContext context, int index){
// final item = feeds[index];
return FlatButton(
onPressed:(){
setState(() {
selected[index] = !selected[index];
choosenUser = users[index];
print("the user:${users[index].fullName},$_selected");
});
},
child:UserCard(users[index], selected[index])
);
}
)
so I had to go a different route to fix the issue in my code. here is my code:
in my model class called UserItem, I introduced another parameter called selectedd
class UserItem{
String fullName, profileUrl;
double distance_KM;
bool selected;
UserItem(this.fullName, this.profileUrl, this.distance_KM, this.selected);
}
since am using static values for now, i passed in "false"
List<UserItem> users = []
..add(UserItem("Edward Norton","assets/profile_img.png", 12.0, false))
..add(UserItem("Gary Owen","assets/img.png", 21, false))
..add(UserItem("Eddie L.","assets/img_details.png", 12.7, false))
..add(UserItem("Carlos Snow","assets/header_user.png", 1.3, false))
..add(UserItem("Idibbia Olaiya","assets/profile_img.png", 0, false));
then when user clicks on any of the child item the selected value that was already set as false will be updated. here is my Listview.builder widget:
Expanded(
child: ListView.builder(
shrinkWrap: true,
itemCount: users.length,
itemBuilder: (BuildContext context, int index){
// final item = feeds[index];
return
Stack(
children: <Widget>[
Container(
child: FlatButton(
onPressed:(){
setState(() {
selected = !selected;
users[index].selected =selected;
// _theIcon = selected ? _theIcon : Icon(Icons.check,color: Colors.grey,size: 40,);
choosenUser.add(users[index]) ;
// print("the user:${users[index].fullName},$selected");
// child_card(users[index], selected,index);
});
}, child:child_card(users[index]),
),
)
],
);
}
)
)
Widget child_card(UserItem user){
// print("the user:${user.fullName},$selected");
return UserCard(user);
}
Hope this helps someone.