How to handle the uniqueness of widget in flutter? - flutter

I have a project. I am trying to make a scanner. Everything is going well except for previews of the images.
This how preview should like this. I manage the make the view of this screenshot. But the problem is that I couldn't manage to make unique for every title. I hold my titles in location table and my images in image table. Image table has location id so that in every location has different images from each other. But my code is overriding all the locations so they are showing same images of one location. Like this:
:
Only first one has a picture but my code show like they have all the same image.
This is my part of the homescreen code:
future: future,
builder: (ctx, snapshot) =>
snapshot.connectionState == ConnectionState.waiting
? Center(
child: CircularProgressIndicator(),
)
: Consumer<Titles>(
builder: (ctx, titles, ch) => Expanded(
child: ListView.builder(
controller: ScrollController(),
scrollDirection: Axis.vertical,
shrinkWrap: true,
itemCount: titles.items.length,
itemBuilder: (ctx, i) { if (searchString=="") {
getimages(titles.items[i].id);
print(list_of_images);
print("yukarıda");
return TitleList(titleList:titles.items[i],images:list_of_images);
}
getimages(titles.items[i].id);
return titles.items[i].title!.contains(searchString) ? TitleList(titleList:titles.items[i],images:list_of_images) :Container();
}
I am sending the title and images to TitleList widget:
I am getting the images like this :
List<File> list_of_images=[];
void getimages(id) {
print("fark ne kadar");
Future<List<Map<String, dynamic>>> futureTasks = DBHelper.selectImageforlist(id); //first make the querylist_of_images
futureTasks.then((data){
print("heyoo");
print(data);
for(int i = 0; i < data.length; i++) {
list_of_images.add(File(data[i]['Image']));
}
});
}
And this is the part of the TitleListWidget code:
var titleList;
List<File> images;
TitleList({required this.titleList,required this.images});
return GestureDetector(
onTap: () {
Navigator.of(context).pushNamed(
ImageScreen.routeName,
arguments: {'id': titleList.id, 'name': titleList.title},
);
},
child: Row(
children: [
Column(
crossAxisAlignment: CrossAxisAlignment.start,
mainAxisAlignment: MainAxisAlignment.start,
children: [
Container(
padding: EdgeInsets.only(top: 15, left: 12),
child: Row(
children: [
Container(
child: Text(
truncate(titleList.title, length: 7),
style: TextStyle(
fontSize: 20,
fontWeight: FontWeight.bold,
color: Colors.white),
),
),
Container(
padding: EdgeInsets.only(left: 15),
child: Text(
getdate(titleList.date),
style: TextStyle(
fontWeight: FontWeight.normal,
color: Colors.white),
)),
],
),
),
Container(
padding: EdgeInsets.only(left: 10),
child: Row(children: [
Stack(
overflow: Overflow.visible,
children: makephoto(
images.length, images),
),
]),
)
],
),

I used map. I took every image to Map. The key of the image is the FileId.
Future<Map> getimages() async {
print("fark ne kadar");
Future<List<Map<String, dynamic>>> futureTasks = DBHelper.selectImageforlist(); //first make the querylist_of_images
futureTasks.then((data){
print("heyoo");
print(data);
for(int i = 0; i < data.length; i++) {
if (identifier.containsKey(data[i]['fileId'])){
identifier[data[i]['fileId']].add(File(data[i]['Image']));
}
else {
identifier[data[i]['fileId']]=[File(data[i]['Image'])];
}
}
});
return identifier;
}

Related

CupertinoPicker with multiple column fields

Hey I'm looking to implement the above widget:
I'm a bit struggling to implement it using CupertinoPicker or CupertinoDatePicker.
How do I do that?
The solution was to upload two Cupertino.builders side by side.
The downside is that the onSelectedItemChanged value is related to each component separtly. It's not that a big problem since we can always merge their values later.
Here is the outcome:
My code:
Row(
mainAxisSize: MainAxisSize.min,
children: [
Expanded(
child: CupertinoPicker.builder(
childCount: 100,
itemExtent: _kItemExtent,
onSelectedItemChanged: (itemIndex) => context
.read<ScheduleCubit>()
.updateTimePreference(index, dateTimeIndex: itemIndex),
itemBuilder: (context, index) {
final dateTextStyle = CupertinoTheme.of(context)
.textTheme
.dateTimePickerTextStyle;
final selectedDate = _selectedDate(index);
final dateText = index == 0
? CupertinoLocalizations.of(context).todayLabel
: dateFormat04(selectedDate);
return Center(
child: Text(
dateText,
textAlign: TextAlign.center,
style: dateTextStyle,
),
);
},
),
),
Expanded(
child: CupertinoPicker(
itemExtent: _kItemExtent,
onSelectedItemChanged: (itemIndex) => context
.read<ScheduleCubit>()
.updateTimePreference(index, timeRangeIndex: itemIndex),
children: List<Widget>.generate(
_timeRanges.length,
(index) => Center(child: Text(_timeRanges[index].toString())),
),
),
)
],
)

Refresh Indicator does not update a list after deleting data

I have a FutureBuilder getting specific items from Firestore and returning them in a list. To update this list I added RefreshIndicator. When something is added to the list and I refresh it, it works normally and the item appears. The problem occurs when I remove an item from the list, it loads but the list remains the same.
This is my Widget:
#override
void initState() {
super.initState();
userFuture = getCollectionItems();
}
Widget build(BuildContext context) {
return Scaffold(
backgroundColor: Colors.white,
body: Column(
children: [
Container(
margin: EdgeInsets.only(top: 5.5),
padding: EdgeInsets.only(left: 17, right: 17),
child: Row(
mainAxisAlignment: MainAxisAlignment.start,
children: [
Text(
"Collection",
style: TextStyle(
fontFamily: 'Montserrat',
fontSize: 28,
fontWeight: FontWeight.w800,
),
),
],
),
),
createGridAndList(),
Expanded(
child: RefreshIndicator(
onRefresh: () => getCollectionItems(),
child: displayCollection(),
),
)
],
),
);
}
When I restart the app or go to another page and come back with pushNamedRemoveUntil the list updates properly, this indicates that the query is working.
getCollectionItems() and displayCollection():
getCollectionItems() async {
QuerySnapshot querySnapshot = await Firestore.instance
.collection("users")
.document(_userId)
.collection("userCollection")
.getDocuments();
List<Users> collectionID = [];
for (DocumentSnapshot item in querySnapshot.documents) {
var data = item.data;
Users user = Users();
user.id = data["id"];
collectionID.add(user);
}
final collectionIDs = collectionID.map((doc) => doc.id).toList();
var splitCollection = partition<dynamic>(collectionIDs, 10);
for (int i = 0; i < splitCollection.length; i++) {
QuerySnapshot querySnapshotCollections = await Firestore.instance
.collection('items')
.where('itemId', whereIn: splitCollection.elementAt(i))
.orderBy('timestamp', descending: true)
.getDocuments();
setState(() {
countItem = querySnapshotCollections.documents.length;
itemsList = querySnapshotCollections.documents
.map((documentSnapshot) =>
CollectionItem.fromDocument(documentSnapshot))
.toList();
});
}
}
displayCollection() {
return FutureBuilder(
future: userFuture,
builder: (context, snapshot) {
if (snapshot.connectionState != ConnectionState.done) {
return Padding(
padding: EdgeInsets.only(top: 20),
child: SizedBox(
child: CircularProgressIndicator(
valueColor: new AlwaysStoppedAnimation<Color>
(Colors.grey),
),
width: 20.0,
height: 20.0,
),
);
}
if (itemsList == null) {
return Container(
padding: EdgeInsets.only(top: 20),
child: SizedBox(
child: CircularProgressIndicator(
valueColor: new AlwaysStoppedAnimation<Color>
(Colors.grey),
),
width: 20.0,
height: 20.0,
),
);
} else if (itemsList.isEmpty) {
return ListView(
physics: const BouncingScrollPhysics(
parent: AlwaysScrollableScrollPhysics()),
children: [
Center(
child: Container(
padding: EdgeInsets.only(
top: MediaQuery.of(context).size.width * 0.50,
left: 17,
right: 17),
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: [
Text(
"Nothing here.",
style: TextStyle(
fontFamily: 'Montserrat',
fontSize: 13,
color: Color(0xff9e9999),
fontWeight: FontWeight.w500),
textAlign: TextAlign.center,
),
],
),
),
),
],
);
} else if (itemOrientation == "grid") {
List<GridTile> gridTilesList = [];
itemsList.forEach((eachItem) {
gridTilesList.add(GridTile(child:
CollectionItemTile(eachItem)));
});
return GridView.count(
crossAxisCount: 2,
padding: EdgeInsets.fromLTRB(10, 15, 10, 0),
childAspectRatio: 3 / 2,
mainAxisSpacing: 15,
crossAxisSpacing: 10,
shrinkWrap: true,
physics: const BouncingScrollPhysics(
parent: AlwaysScrollableScrollPhysics()),
children: gridTilesList,
);
} else {
return Container(
padding: EdgeInsets.only(bottom: 15),
child: ListView(
padding: EdgeInsets.all(0.0),
shrinkWrap: true,
physics: const BouncingScrollPhysics(
parent: AlwaysScrollableScrollPhysics()),
children: itemsList),
);
}
});
}
I've tried several things, switched to Stream (it didn't work), added another setState to the Widget itself, rebuilt the classes but the problem persists.
Hmmm, your displayCollection widget is displaying data based on userFuture, but halfway through you using itemList instead, and your onRefresh function is updating the itemList but not userFuture.
I won't do exactly like you do, but i refactored a bit.
You can try something like this, i didn't test it but let me know if it works 😊
// I changed `userFuture` to `futureItems`
Future<List<CollectionItem>> futureItems;
#override
void initState() {
super.initState();
futureItems = getCollectionItems();
}
Future<List<CollectionItem>> getCollectionItems() async {
// ... Do your query here
return querySnapshotCollections.documents.map((documentSnapshot) {
return CollectionItem.fromDocument(documentSnapshot);
}).toList();
}
Future<void> refreshCollectionItems() async {
setState(() {
// This will update the futureItems
futureItems = getCollectionItems();
});
}
Widget displayCollection() {
return FutureBuilder<List<CollectionItem>>(
future: futureItems, // The data returned will be inside `snapshot` below
builder: (context, snapshot) {
if (snapshot?.hasData ?? false) {
List<CollectionItem> items = snapshot.data; // This is the return value from `futureItems`
return RefreshIndicator(
onRefresh: refreshCollectionItems,
child: ListView.builder(
itemCount: items.length, // This is how to get the length, so no need to use `countItem`
itemBuilder: (context, index){
CollectionItem item = items[index];
return // ...Display your widget with item data
},
),
);
}
return // Display widget to handle loading/error/no data
},
);
}
Plus it is important to define the return type of a function so that you will know what you will get after executing a function.
One of the simplest ways to solve this is re-setting the state onRefresh.
Expanded(
child: RefreshIndicator(
onRefresh: () {
getCollectionItems();
setState(() {
userFuture = getCollectionItems();
});
},
child: displayCollection(),
),
),
Your Firestore query might be reading from cache. Try disable persistence. Official Tutorial
FirebaseFirestore.instance.settings = Settings(persistenceEnabled: false);
Or if want to clear any persisted data, you can call the clearPersistence() method.
await FirebaseFirestore.instance.clearPersistence();

ListView is not showing the result if empty list

I am loading data from a remote api:
This is the dart file that provides the connection and download:
clinica-api.dart
import 'package:flutter_capenergy/modelos/clinica.dart';
import 'package:http/http.dart' as http;
Future<List<Clinica>> fetchClinicas(String idUsuario) async {
String url ="https://..flutter_api/get_clinicas.php";
final response = await http.get(url);
if (response.body == "[]"){
}
return clinicaFromJson(response.body);
}
And this is the piece of code from misclinicas.dart where I am showing the list:
Expanded(
child: Container(
child: FutureBuilder(
future: fetchClinicas(miId),
builder: (context, snapshot) {
if (snapshot.hasData) {
return ListView.builder(
itemCount: snapshot.data.length,
shrinkWrap: true,
itemBuilder: (BuildContext context, index) {
print(index.toString());
Clinica clinica = snapshot.data[index];
return new GestureDetector(
onTap: () {
clinicaProvider.setClinica(clinica.nombreClinica);
clinicaProvider.setClinicaId(clinica.idClinica);
} ,
child: new Card(
elevation: 6,
child: new Column(
children: [
new Padding(
padding: const EdgeInsets.all(8.0),
child: Column(
crossAxisAlignment: CrossAxisAlignment
.start,
children: [
Row(
crossAxisAlignment: CrossAxisAlignment
.center,
mainAxisAlignment: MainAxisAlignment
.center,
children: <Widget>[
Image.network(
'https://.../${clinica
.logoClinica}',
height: 180,
alignment: Alignment.center,),
],
),
Text(
'${clinica.nombreClinica}',
style: TextStyle(fontSize: 16,
fontWeight: FontWeight.bold,
color: Colors.blue),
),
Text(
'${clinica.direccionClinica}',
style: TextStyle(fontSize: 14,
color: Colors.grey,
fontStyle: FontStyle.italic),
),
],
),
),
],
),
),
);
},
);
}
else {
Text ("NO HAY CLINICAS");
}
return Text("Cargando clínicas");
},
),
),
),
If there are items on the list, they are shown, but if the list is empty I would like to show a text with a message reporting that the list is empty.
I am trying to do it putting this text widget if snapshot.hasdata is false:
Text ("NO HAY CLINICAS");
but it is not shown, I am only getting a blank listView.
In the empty list case, snapshot.hasData will be true and snapshot.data.length will be 0.
snapshot.hasData == false means it's either loading or an error has happened.
in the api call return empty list if response.body ="[]"
if (response.body == "[]"){
List<Clinica> emptyClinica = [];
return emptyClinica;
}
in misclinicas.dart
snapshot.data.lenth > 0 ? your list work : Text('No Data Found')

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.

Flutter : How to add a Header Row to a ListView

Very new to Flutter. I've been able to utilize HTTP requests for data, build a ListView, edit a Row in that List and other basics. Excellent environment.
I've managed to cobble together a badly constructed Header for a ListView but I know this isn't right. I can't get the Header text to line up properly.
I see that the Drawer Class has a DrawerHeader Class, but can't see that ListView has a ListViewHeader.
#override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: Text('Contacts'),
actions: <Widget>[
IconButton(icon: Icon(Icons.add_circle),
onPressed: getCustData
),
],
),
//body:
body: Column(
children: <Widget>[
Row(
children: <Widget>[
Expanded(child: Text('', style: TextStyle(height: 3.0, fontSize: 15.2, fontWeight: FontWeight.bold,))),
Expanded(child: Text('First Name', style: TextStyle(height: 3.0, fontSize: 15.2, fontWeight: FontWeight.bold,))),
Expanded(child: Text('Last Name', style: TextStyle(height: 3.0, fontSize: 15.2, fontWeight: FontWeight.bold,))),
Expanded(child: Text('City', style: TextStyle(height: 3.0, fontSize: 15.2, fontWeight: FontWeight.bold,))),
Expanded(child: Text('Customer Id', style: TextStyle(height: 3.0, fontSize: 15.2, fontWeight: FontWeight.bold,))),
Expanded(child: Text('', style: TextStyle(height: 3.0, fontSize: 15.2, fontWeight: FontWeight.bold,))),
]
),
Expanded(child:Container(
child: ListView.builder(
itemCount: data == null ? 0 : data.length,
itemBuilder: (BuildContext context, int index) {
return InkWell(
onTap: () {
Navigator.push(
context,
MaterialPageRoute(
builder: (context) => APIDetailView(data[index])),
);
},
child: ListTile( //return new ListTile(
onTap: null,
leading: CircleAvatar(
backgroundColor: Colors.blue,
child: Text(data[index]["FirstName"][0]),
),
title: Row(
children: <Widget>[
Expanded(child: Text(data[index]["FirstName"])),
Expanded(child: Text(data[index]["LastName"])),
Expanded(child: Text(data[index]["Bill_City"])),
Expanded(child: Text(data[index]["Customer_Id"])),
]
)
),
);
}, //itemBuilder
),
),
),
]
)
);
}
}
Thanks.
Return the header as first row by itemBuilder:
ListView.builder(
itemCount: data == null ? 1 : data.length + 1,
itemBuilder: (BuildContext context, int index) {
if (index == 0) {
// return the header
return new Column(...);
}
index -= 1;
// return row
var row = data[index];
return new InkWell(... with row ...);
},
);
Here's how I solved this. Thanks #najeira for getting me thinking about other solutions.
In the first body Column I used the same layout for my Header that I used for the ListTile.
Because my data ListTile, in this case, includes a CircleAvatar, all the horizontal spacing is off a bit... 5 columns where the CircleAvatar is rendered... then 4 evenly spaced columns.
So... I added a ListTile to the first body Column, a CircleAvatar with a backgroundColor of transparent, and then a Row of my 4 Headings.
ListTile(
onTap: null,
leading: CircleAvatar(
backgroundColor: Colors.transparent,
),
title: Row(
children: <Widget>[
Expanded(child: Text("First Name")),
Expanded(child: Text("Last Name")),
Expanded(child: Text("City")),
Expanded(child: Text("Id")),
]
),
),
You can add Container and ListView in Column.
import 'package:flutter/material.dart';
void main() => runApp(MyApp());
class MyApp extends StatefulWidget {
#override
_MyAppState createState() => _MyAppState();
}
class _MyAppState extends State<MyApp> {
#override
void initState() {
// TODO: implement initState
super.initState();
}
#override
Widget build(BuildContext context) {
return MaterialApp(
debugShowCheckedModeBanner: false,
home: Scaffold(
appBar: AppBar(
title: Text("Demo App1"),
),
body: Column(
children: <Widget>[
Container(
height: 40.0,
child: Row(
children: <Widget>[
Container(
padding: EdgeInsets.all(4.0),
width: 100.0,
child: Text(
"Name",
style: TextStyle(fontSize: 18),
)),
Container(
padding: EdgeInsets.all(4.0),
width: 100.0,
child: Text(
"Age",
style: TextStyle(fontSize: 18),
)),
],
),
),
Expanded(
child: ListView.builder(
itemCount: 100,
itemBuilder: (BuildContext context, int index) {
return Row(
children: <Widget>[
Container(
padding: EdgeInsets.all(4.0),
width: 100.0,
child: Text(
"Name $index",
style: TextStyle(fontSize: 18),
)),
Container(
padding: EdgeInsets.all(4.0),
width: 100.0,
child: Text(
"Age $index",
style: TextStyle(fontSize: 18),
),
)
],
);
},
),
),
],
),
),
);
}
}
You can add a column to the first item in the item list like this
new ListView.builder(
itemCount: litems.length,
itemBuilder: (BuildContext ctxt, int index) {
if (index == 0) {
return Column(
children: <Widget>[
Header(),
rowContent(index),
],
);
} else {
return rowContent(index);
}
},
)
najeira's solution is easy and simple, but you can get the same and more flexible result without touching index.
Instead of using listView, you could use CustomScrollView & SliverList which is functionally the same as listView.
return Scaffold(
body: CustomScrollView(
slivers: <Widget>[
SliverToBoxAdapter(
// you could add any widget
child: ListTile(
leading: CircleAvatar(
backgroundColor: Colors.transparent,
),
title: Row(
children: <Widget>[
Expanded(child: Text("First Name")),
Expanded(child: Text("Last Name")),
Expanded(child: Text("City")),
Expanded(child: Text("Id")),
],
),
),
),
SliverList(
delegate: SliverChildBuilderDelegate(
(context, index) {
return InkWell(
onTap: () {
Navigator.push(
context,
MaterialPageRoute(
builder: (context) => APIDetailView(data[index])),
);
},
child: ListTile(
//return ListTile(
leading: CircleAvatar(
backgroundColor: Colors.blue,
child: Text(data[index]["FirstName"][0]),
),
title: Row(
children: <Widget>[
Expanded(child: Text(data[index]["FirstName"])),
Expanded(child: Text(data[index]["LastName"])),
Expanded(child: Text(data[index]["Bill_City"])),
Expanded(child: Text(data[index]["Customer_Id"])),
],
),
),
);
},
childCount: data == null ? 0 : data.length,
),
),
],
),
);
Use DataTable widget !
That widget allows you to build a table. Code : DataTable(columns: [], rows: [],)
Example :
DataTable(
columns: [
DataColumn(label: Text('Lang.')),
DataColumn(label: Text('Year')),
],
rows: [
DataRow(cells: [DataCell(Text('Dart')), DataCell(Text('2010'))]),
DataRow(cells: [DataCell(Text('Go')), DataCell(Text('2009'))]),
DataRow(cells: [DataCell(Text('PHP')), DataCell(Text('1994'))]),
DataRow(cells: [DataCell(Text('Java')), DataCell(Text('1995'))]),
],
)
Output:
You can learn more about DataTable by watching this official video or by visiting flutter.dev
It seems what you are really looking for is the DataTable widget instead of a ListView.
It has a customizable Header including sorting options.
Read the documentation including some great examples on api.flutter.dev: Data Table CLass
I have created listview_utils package to reduce boilerplate code needed to build header and footer list items. Here's an example code using the package:
import 'package:listview_utils/listview_utils.dart';
CustomListView(
header: Container(
child: Text('Header'),
),
itemCount: items.length,
itemBuilder: (BuildContext context, int index, _) {
return ListTile(
title: Text(item['title']),
);
},
);
Disclaimer: I am maintainer of the package.
Looking for dynamic section headers according to your api data. Add this class to your project.
class _FlutterSectionListViewState extends State<FlutterSectionListView> {
/// List of total number of rows and section in each group
var itemList = [];
int itemCount = 0;
int sectionCount = 0;
#override
void initState() {
/// ----#4
sectionCount = widget.numberOfSection();
/// ----#5
itemCount = listItemCount();
super.initState();
}
/// ----#6
#override
Widget build(BuildContext context) {
return ListView.builder(
itemCount: itemCount,
itemBuilder: (context, index) {
return buildItemWidget(index);
},
key: widget.key,
);
}
/// Get the total count of items in list(including both row and sections)
int listItemCount() {
itemList = [];
int rowCount = 0;
for (int i = 0; i < sectionCount; i++) {
/// Get the number of rows in each section using callback
int rows = widget.numberOfRowsInSection(i);
/// Here 1 is added for each section in one group
rowCount += rows + 1;
itemList.insert(i, rowCount);
}
return rowCount;
}
/// ----#7
/// Get the widget for each item in list
Widget buildItemWidget(int index) {
/// ----#8
IndexPath indexPath = sectionModel(index);
/// ----#9
/// If the row number is -1 of any indexPath it will represent a section else row
if (indexPath.row < 0) {
/// ----#10
return widget.sectionWidget != null
? widget.sectionWidget!(indexPath.section)
: SizedBox(
height: 0,
);
} else {
return widget.rowWidget!(indexPath.section, indexPath.row);
}
}
/// Calculate/Map the indexPath for an item Index
IndexPath sectionModel(int index) {
int? row = 0;
int section = 0;
for (int i = 0; i < sectionCount; i++) {
int item = itemList[i];
if (index < item) {
row = (index - (i > 0 ? itemList[i - 1] : 0) - 1) as int?;
section = i;
break;
}
}
return IndexPath(section: section, row: row!);
}
}
/// Helper class for indexPath of each item in list
class IndexPath {
IndexPath({required this.section, required this.row});
int section = 0;
int row = 0;
}
create your list according to your api data
List<List<Operator>> ops = [];
List<String> sections = [];
if(c.operatorStatuses.value!.availableOperators.length>0){
ops.add(c.operatorStatuses.value!.availableOperators);
sections.add("Müsait Operatörler");
}
if(c.operatorStatuses.value!.busyOperators.length>0){
ops.add(c.operatorStatuses.value!.busyOperators);
sections.add("Meşgul Operatörler");
}
if(c.operatorStatuses.value!.breakOperators.length>0){
ops.add(c.operatorStatuses.value!.breakOperators);
sections.add("Moladaki Operatörler");
}
if(c.operatorStatuses.value!.closedOperators.length>0){
ops.add(c.operatorStatuses.value!.closedOperators);
sections.add("Kapalı Operatörler");
}
show it in ui;
FlutterSectionListView(
numberOfSection: () => ops.length,
numberOfRowsInSection: (section) {
return ops[section].length;
},
sectionWidget: (section) {
if(section<ops.length){
return Container(
child: Padding(
padding: const EdgeInsets.all(8),
child: Text(sections[section]),
),
color: Colors.grey,
);
}else{
return SizedBox();
}
},
rowWidget: (section, row) {
if(row < ops[section].length){
Operator? op = ops[section][row];
return card(op);
}else{
return SizedBox();
}
},
)
thanks to [this article][1].
NOTE : code block produces error some time according to updated data..
[1]: https://medium.com/#dharmendra_yadav/ios-like-sectioned-listview-widget-in-flutter-7cf9dab2dd1a
I use this:
body: Column(
children: [
Container(
// The header will be here
),
Expanded(
// The ListView
child: ListView.builder(
itemCount: // The length,
itemBuilder: (_, index) {
return //List Item Widget Here
}),
),
],
)
Here I've created flat_list widget which has similar specifications as in React Native's FlatList.
FlatList(
+ listHeaderWidget: const Header(),
data: items.value,
buildItem: (item, index) {
var person = items.value[index];
return ListItemView(person: person);
},
),