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.
Related
When I transition to a screen where I get a list of information via an API, it initially gives an error:
_CastError (Null check operator used on a null value)
and only after loading the information, the screen is displayed correctly.
I am declaring the variables like this:
#observable
ObservableFuture<Model?>? myKeys;
#action
getKeys() {
myKeys = repository.getKeys().asObservable();
}
How can I enter the page only after loading the information?
In button action I tried this but to no avail!
await Future.wait([controller.controller.getKeys()]);
Modular.to.pushNamed('/home');
This is the page where the error occurs momentarily, but a short time later, that is, when the api call occurs, the data appears on the screen.
class MyKeyPage extends StatefulWidget {
const MyKeyPage({Key? key}) : super(key: key);
#override
State<MyKeyPage> createState() => _MyKeyPageState();
}
class _MyKeyPageState
extends ModularState<MyKeyPage, KeyController> {
KeyController controller = Modular.get<KeyController>();
Widget countKeys() {
return FutureBuilder(
builder: (BuildContext context, AsyncSnapshot<dynamic> snapshot) {
final count =
controller.myKeys?.value?.data!.length.toString();
if (snapshot.connectionState == ConnectionState.none &&
!snapshot.hasData) {
return Text('..');
}
return ListView.builder(
scrollDirection: Axis.vertical,
shrinkWrap: true,
itemCount: 1,
itemBuilder: (context, index) {
return Text(count.toString() + '/5');
});
},
future: controller.getCountKeys(),
);
}
#override
Widget build(BuildContext context) {
Size _size = MediaQuery.of(context).size;
return controller.getCountKeys() != "0"
? TesteScaffold(
removeHorizontalPadding: true,
onBackPressed: () => Modular.to.navigate('/exit'),
leadingIcon: ConstantsIcons.trn_arrow_left,
title: '',
child: Container(
width: double.infinity,
child: Padding(
padding: const EdgeInsets.only(left: 24),
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Text(
'Keys',
style: kHeaderH3Bold.copyWith(
color: kBluePrimaryTrinus,
),
),
countKeys(),
],
),
),
),
body: Observer(builder: (_) {
return Padding(
padding: const EdgeInsets.only(bottom: 81),
child: Container(
child: ListView.builder(
padding: EdgeInsets.only(
left: 12.0,
top: 2.0,
right: 12.0,
),
itemCount:
controller.myKeys?.value?.data!.length,
itemBuilder: (context, index) {
var typeKey = controller
.myKeys?.value?.data?[index].type
.toString();
var id =
controller.myKeys?.value?.data?[index].id;
final value = controller
.myKeys?.value?.data?[index].value
.toString();
return GestureDetector(
onTap: () {
.
.
},
child: CardMeyKeys(
typeKey: typeKey,
value: value!.length > 25
? value.substring(0, 25) + '...'
: value,
myKeys: pixController
.minhasChaves?.value?.data?[index].type
.toString(),
),
);
},
),
),
);
}),
bottomSheet: ....
)
: TesteScaffold(
removeHorizontalPadding: true,
onBackPressed: () => Modular.to.navigate('/exit'),
leadingIcon: ConstantsIcons.trn_arrow_left,
title: '',
child: Container(
width: double.infinity,
child: Padding(
padding: const EdgeInsets.only(left: 24),
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Text(
'...',
style: kHeaderH3Bold.copyWith(
color: kBluePrimaryTrinus,
),
),
],
),
),
),
body: Padding(
padding: const EdgeInsets.only(bottom: 81),
child: Container(
child: Center(
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: <Widget>[
Image.asset(
'assets/images/Box.png',
fit: BoxFit.cover,
width: 82.75,
height: 80.91,
),
SizedBox(
height: 10,
),
],
),
), //Center
),
),
bottomSheet: ...
);
}
List<ReactionDisposer> disposers = [];
#override
void initState() {
super.initState();
controller.getKeys();
}
#override
void dispose() {
disposers.forEach((toDispose) => toDispose());
super.dispose();
}
}
Initially the error occurs in this block
value: value!.length > 25
? value.substring(0, 25) + '...'
: value,
_CastError (Null check operator used on a null value)
I appreciate if anyone can help me handle ObservableFuture correctly!
You need to call the "future" adding
Future.wait
(the return type of getKeys) keys=await Future.wait([
controller.getKeys();
]);
The problem is your getKeys function isn't returning anything, so there's nothing for your code to await. You need to return a future in order to await it.
Future<Model?> getKeys() {
myKeys = repository.getKeys().asObservable();
return myKeys!; // Presumably this isn't null anymore by this point.
}
...
await controller.controller.getKeys();
Modular.to.pushNamed('/home');
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]),
),
),
);
},
),
),
);
I have a parent widget that draws multiple child widgets using a listview. There is a checkbox within each of these child widgets. I am trying to implement a "select all" button in the parent widget which checks all of the children's checkboxes, but I'm having a hard time figuring out how to accomplish this.
Here is my parent widget:
class OrderDisplay extends StatefulWidget {
static const routeName = '/orderDisplay';
//final Order order;
//const OrderDisplay(this.order);
#override
OrderDisplayState createState() {
return OrderDisplayState();
}
}
class OrderDisplayState extends State<OrderDisplay> {
bool preChecked = false;
double total = 0;
List<OrderedItem> itemsToPayFor = [];
#override
Widget build(BuildContext context) {
final OrderDisplayArguments args =
ModalRoute.of(context).settings.arguments;
return Scaffold(
backgroundColor: MyColors.backgroundColor,
body: SafeArea(
child: Column(
children: [
Expanded(
child: SingleChildScrollView(
physics: ScrollPhysics(),
child: Container(
padding: EdgeInsets.only(top: 10),
child: Column(
children: [
Text(args.order.restaurantName,
style: MyTextStyles.headingStyle),
ListView.separated(
physics: NeverScrollableScrollPhysics(),
shrinkWrap: true,
itemCount: args.order.orderedItems.length,
itemBuilder: (context, index) {
return FoodOrderNode(
preChecked, args.order.orderedItems[index],
onCheckedChanged: (isChecked) {
isChecked
? setState(() {
itemsToPayFor.add(
args.order.orderedItems[index]);
})
: setState(() {
itemsToPayFor.remove(
args.order.orderedItems[index]);
});
});
},
separatorBuilder: (context, index) =>
MyDividers.MyDivider)
],
)),
),
),
MyDividers.MyDivider,
Container(
height: 140,
color: MyColors.backgroundColor,
child: Row(children: [
Expanded(
flex: 5,
child: Column(
children: [
Expanded(flex: 2, child: SizedBox()),
Expanded(
flex: 6,
child: SelectAllButton(() {
print("SELECT ALL");
setState(() {
preChecked = true;
});
})),
Expanded(flex: 2, child: SizedBox())
],
)),
Expanded(
flex: 5,
child: Column(
children: [
Expanded(flex: 1, child: SizedBox()),
Expanded(
flex: 8,
child: PayNowButton(() {
print("PAY NOW");
},
double.parse(itemsToPayFor
.fold(0, (t, e) => t + e.itemPrice)
.toStringAsFixed(
2)))),
Expanded(flex: 1, child: SizedBox())
],
))
]))
],
)));
}
}
And here is FoodOrderNode:
typedef void SelectedCallback(bool isChecked);
class FoodOrderNode extends StatefulWidget {
final bool preChecked;
final OrderedItem item;
final SelectedCallback onCheckedChanged;
const FoodOrderNode(this.preChecked, this.item,
{#required this.onCheckedChanged});
#override
FoodOrderNodeState createState() {
return FoodOrderNodeState();
}
}
class FoodOrderNodeState extends State<FoodOrderNode> {
bool isChecked = false;
bool isSplitSelected = false;
#override
Widget build(BuildContext context) {
isChecked = widget.preChecked;
return Container(
height: 80,
padding: EdgeInsets.only(left: 15, right: 15),
decoration: BoxDecoration(
color: MyColors.nodeBackgroundColor,
),
child: Row(
children: [
Expanded(
flex: 1,
child: CircularCheckBox(
value: isChecked,
checkColor: Colors.white,
activeColor: Colors.blue,
autofocus: false,
onChanged: (bool value) {
print("Change to val: $value");
widget.onCheckedChanged(value);
setState(() {
isChecked = value;
});
},
)),
Expanded(
flex: 7,
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
mainAxisAlignment: MainAxisAlignment.center,
children: [
Container(
padding: EdgeInsets.only(bottom: 5, left: 40),
child: Text(
widget.item.itemName,
style: TextStyle(fontSize: 18, color: Colors.black),
textAlign: TextAlign.left,
maxLines: 2,
overflow: TextOverflow.ellipsis,
)),
Container(
padding: EdgeInsets.only(left: 40),
child: Text(
"\$${widget.item.itemPrice}",
style:
TextStyle(fontSize: 16, color: MyColors.labelColor),
))
],
),
),
Expanded(
flex: 2,
child: isSplitSelected
? SplitButtonSelected(() {
setState(() {
isSplitSelected = false;
});
})
: SplitButtonUnselected(() {
setState(() {
isSplitSelected = true;
});
}))
],
),
);
}
}
I have tried creating a "preChecked" argument for FoodOrderNode and then using setState from the parent widget, however, that hasn't worked out. I have also tried using keys, but I couldn't figure out how to get those working for this either. Thank you, and let me know if you'd like any more relevant code.
Just put a global checkbox above the list items and give it isAllChecked (bool) on its value so when it will be checked set the state to isAllChecked => true and then in child checkboxes check for condition if isAllChecked is true then mark as true or checked.
GlobalCheckbox(
onChanged(value){
setState(()
{
isAllChecked==value;
});
}
);
ChildCheckBox(
value: isAllChecked ? true : false
)
this might help you:)
List<ServicesMensCollection> menServicesList = []
..add(ServicesMensCollection('ihdgfstfyergjergdshf', 'janik', 10))
..add(ServicesMensCollection('ihdgfstfyergjerg', 'janik', 10))
..add(ServicesMensCollection('ihdgfstfyergjerg', 'janik', 10))
..add(ServicesMensCollection('ihdgfstfyergjergdf', 'janik', 10))
bool _value2 = false;
void _value2Changed(bool value) => setState(() => _value2 = value);
#override
Widget build(BuildContext context) {
return MaterialApp(
debugShowCheckedModeBanner: false,
home: new Scaffold(
body: new Container(
decoration: new BoxDecoration(color: const Color(0xFFEAEAEA)),
child: Padding(
padding: EdgeInsets.fromLTRB(10.0, 10.0, 10.0, 10.0),
child: Column(
children: <Widget>[
servicesCategory(),
],),),)); }
Widget servicesButton() {
return Container(
child: Row(
mainAxisAlignment: MainAxisAlignment.spaceEvenly,
children: <Widget>[
RaisedButton(
onPressed: () {listView();},
child: Text('Mens'),),
RaisedButton(
onPressed: () {listView();},
child: Text('Womens')),
RaisedButton(
onPressed: () {listView();},
child: Text('Childrens'),
)]); }
Widget listView(){
return ListView.builder(
itemCount: menServicesList.length,
itemBuilder: (BuildContext context, int index) {
return list(index); },);
}
Widget list(int index){
return Container(
child: Row(
mainAxisAlignment: MainAxisAlignment.spaceEvenly,
children: <Widget>[
Text(menServicesList[index].name),
Text(menServicesList[index].name),
Checkbox(onChanged:_value2Changed,
value: _value2,
)],),);
}}
I am implementing listview with checkbox in my project.I have 3 buttons which is created in a row.I want to display the list when the button is clicked.Here the issue is listview is not at all visible for me.I had implemented the same example in android but i don't know how to do this in flutter.
Try this. This is a sample screen which you can refer for your implementation.
In this there are 3 sample list which are being replaced to main list on selection, you can add a function which will sort the list based on selection (so no need to have multiple lists)
import 'package:flutter/material.dart';
/*
These are the sample list for demo
*/
List<ItemVO> mainList = List();
List<ItemVO> sampleMenList = [
ItemVO("1", "Mens 1"),
ItemVO("2", "Mens 2"),
ItemVO("3", "Mens 3")
];
List<ItemVO> sampleWomenList = [
ItemVO("1", "Women 1"),
ItemVO("2", "Women 2"),
ItemVO("3", "Women 3")
];
List<ItemVO> sampleKidsList = [
ItemVO("1", "kids 1"),
ItemVO("2", "kids 2"),
ItemVO("3", "kids 3")
];
class TestScreen extends StatefulWidget {
#override
State<StatefulWidget> createState() {
return _TestScreen();
}
}
class _TestScreen extends State<TestScreen> {
#override
void initState() {
super.initState();
mainList.addAll(sampleMenList);
}
#override
Widget build(BuildContext context) {
return Material(
child: Stack(
children: <Widget>[
ListView.builder(
itemBuilder: (BuildContext context, index) {
return getCard(index);
},
itemCount: mainList.length,
),
Container(
margin: EdgeInsets.only(bottom: 20),
alignment: Alignment.bottomCenter,
child: Row(
mainAxisAlignment: MainAxisAlignment.spaceEvenly,
children: <Widget>[
FloatingActionButton(
onPressed: () {
mainList.clear();
setState(() {
mainList.addAll(sampleMenList);
});
},
heroTag: "btn1",
child: Text("Mens"),
),
FloatingActionButton(
onPressed: () {
mainList.clear();
setState(() {
mainList.addAll(sampleWomenList);
});
},
heroTag: "btn2",
child: Text("Women"),
),
FloatingActionButton(
onPressed: () {
mainList.clear();
setState(() {
mainList.addAll(sampleKidsList);
});
},
heroTag: "btn3",
child: Text("Kids"),
)
],
),
),
],
),
);
}
/*
Get the card item for a list
*/
getCard(int position) {
ItemVO model = mainList[position];
return Card(
child: Container(
height: 50,
alignment: Alignment.center,
child: Row(
mainAxisAlignment: MainAxisAlignment.center,
children: <Widget>[
Text(
"ID:: "+model._id,
style: TextStyle(fontSize: 18, color: Colors.black),
),
Padding(padding: EdgeInsets.only(left: 5,right: 5)),
Text(
"Name:: "+model._name,
style: TextStyle(fontSize: 18, color: Colors.black),
)
],
),
),
margin: EdgeInsets.all(10),
);
}
}
/*
Custom model
i.e. for itemList
*/
class ItemVO {
String _id, _name;
String get id => _id;
set id(String value) {
_id = value;
}
get name => _name;
set name(value) {
_name = value;
}
ItemVO(this._id, this._name);
}
In your code you didn't added ListView in widget, so it will not show any list, so try adding ListView in widget and then change the list data and try it.
I think You have 2 choices on how to tackle your problem.
Preload the listViews and set their visibility to gone / invisible
Try to play around with the code from this blog
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);
},
),