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);
},
),
Related
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]),
),
),
);
},
),
),
);
As the question suggests I have an ExpansionPanelList, one ExpansionPanel (the last one or the 7th one) should have 2 additional buttons, but how can I add them just in this one last panel & not in all the others as well?
This is the code of my whole Expansion panel, as Im not sure where you have to add the behaviour, but guessing in the body of the ExpansionPanel (close to line 40):
class ExpansionList extends StatefulWidget {
final Info info;
const ExpansionList({
Key key,
this.info,
}) : super(key: key);
#override
_ExpansionListState createState() => _ExpansionListState();
}
class _ExpansionListState extends State<ExpansionList> {
Widget _buildListPanel() {
return Container(
child: Theme(
data: Theme.of(context).copyWith(
cardColor: Color(0xffDDBEA9),
),
child: ExpansionPanelList(
dividerColor: Colors.transparent,
elevation: 0,
expansionCallback: (int index, bool isExpanded) {
setState(() {
infos[index].isExpanded = !isExpanded;
});
},
children: infos.map<ExpansionPanel>((Info info) {
return ExpansionPanel(
headerBuilder: (BuildContext context, bool isExpanded) {
return ListTile(
title: !isExpanded
? Text(
info.headerValue,
) //code if above statement is true
: Text(
info.headerValue,
textScaleFactor: 1.3,
style: TextStyle(
fontWeight: FontWeight.bold,
),
),
);
},
body: Padding(
padding: const EdgeInsets.all(8.0),
child: Container(
decoration: BoxDecoration(
color: Color(0xffFFE8D6),
borderRadius: BorderRadius.circular(25)),
child: Column(
children: [
ListView.separated(
physics: const NeverScrollableScrollPhysics(),
shrinkWrap: true,
padding: EdgeInsets.only(left: 40.0,),
itemCount: info.expandedValueData.length,
itemBuilder: (context, index) {
return CheckboxListTile(
title: Text(info.expandedValueData[index].title,
style: TextStyle(
decoration: info.expandedValueData[index]
.completed
? TextDecoration.lineThrough
: null)),
value: info.expandedValueData[index].completed,
onChanged: (value) {
setState(() {
// Here you toggle the checked item state
infos.firstWhere(
(currentInfo) => info == currentInfo)
..expandedValueData[index].completed =
value;
});
});
},
separatorBuilder: (BuildContext context, int index) {
return SizedBox(
height: 20,
);
},
),
Row(children: [
SizedBox(
width: MediaQuery.of(context).size.width * 0.16),
Text("Abschnitt bis zum Neustart löschen"),
SizedBox(
width: MediaQuery.of(context).size.width * 0.11),
IconButton(
icon: Icon(Icons.delete),
onPressed: () {
setState(() {
infos.removeWhere(
(currentInfo) => info == currentInfo);
});
},
)
]),
],
),
),
),
isExpanded: info.isExpanded);
}).toList(),
),
),
);
}
#override
Widget build(BuildContext context) {
return SingleChildScrollView(
child: Container(
child: _buildListPanel(),
),
);
}
}
Thanks for suggestions!
Hi Just add a field (if you already do not have one) in the info object that will allow you to change the widget that is inflated based on that field.
For example
...
children: infos.map<ExpansionPanel>((Info info) {
return ExpansionPanel(
headerBuilder: (BuildContext context, bool isExpanded) {
return info.type == TYPE_A ? TypeAWidgetHeader(info) : TypeBWidgetHeader(info);
body: info.type == TYPE_A ? TypeAWidgetBody(info) : TypeBWidgetBody(info);
...
I am trying to create some ocassion cards inside a pageview and was wondering if there was a way to make my fontsize dynamic to avoid pixel overflow. Here is a screenshot of a card that works fine:
But when I add an occasion that has more characters...
Here is my code:
class Category {
String name;
IconData icon;
Widget route;
Category(this.name, this.icon, this.route);
}
class CalendarEvents {
String title;
String date;
CalendarEvents(this.title, this.date);
}
class AccountPage extends StatefulWidget {
#override
_AccountPageState createState() => _AccountPageState();
}
class _AccountPageState extends State<AccountPage> {
List<Category> _categories = [
Category('My History', Icons.history, MyHistory()),
Category('Dates to Remember', Icons.event_note, DatesToRemember()),
Category('Terms and Conditions', Icons.assignment, TermsandConditions()),
Category('Privacy Notice', Icons.security, PrivacyNotice()),
Category('Rate us' , Icons.stars, RateUs()),
Category('Send us Feedback' , Icons.feedback, GiveUsFeedback())
];
DateFormat formatter = DateFormat('dd/MM/yyyy');
List<CalendarEvents> _events = [
CalendarEvents('Christmas Day', "25/12/2020"),
CalendarEvents('New Years Eve', "31/12/2020"),
CalendarEvents('New Years Day',"01/01/2021"),
CalendarEvents('Valentines Day', "14/02/2021"),
CalendarEvents('A very long occasion that needs to be resized','01/01/2021')
];
int _index = 0;
#override
Widget build(BuildContext context) {
return SingleChildScrollView(
scrollDirection: Axis.vertical,
child: Column(
children: [
Container(
child: SizedBox(
height: 75, // card height
child: PageView.builder(
itemCount: _events.length,
controller: PageController(viewportFraction: 0.5),
onPageChanged: (int index) => setState(() => _index = index),
itemBuilder: (_, i) {
return Transform.scale(
scale: i == _index ? 1 : 0.5,
child: Card(
elevation: 6,
shape: RoundedRectangleBorder(borderRadius: BorderRadius.circular(10)),
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: [
Text(formatter.parse(_events[i].date).day.toString()),
Text(DateFormat.MMMM().format(formatter.parse(_events[i].date)).toString()),
Text(
_events[i].title,
style: AppBarTextStyle,
),
],
),
),
);
},
),
),
),
// SizedBox(height: 100.0,),
Container(
height: MediaQuery.of(context).size.height * 0.7,
child: ListView.builder(
itemCount: _categories.length,
itemBuilder: (context, index) {
return Column(
children: <Widget>[
ListTile(
leading: Icon(_categories[index].icon, color: Colors.black,),
title: Text(_categories[index].name),
trailing: Icon(Icons.arrow_forward_ios),
onTap: () => Navigator.push(context, MaterialPageRoute(builder: (context) => _categories[index].route)),
),
Divider(), //
],
);
}),
),
],
),
);
}
}
It would be useful to resize the font depending on the length of occasion. If this is possible, what would be the best way of achieving these results? Thanks
If you want control over the fontsize even after resized, you should use https://pub.dev/packages/auto_size_text package like #Abhishek mentioned
If you just want the text to resize freely, you can use FittedBox and wrap it around Text widget, like this:
FittedBox(
fit: BoxFit.contain,
child: Text()
)
you can use https://pub.dev/packages/auto_size_text package like
title: Text(_categories[index].name),
instead of this
title: AutoSizeText(
_categories[index].name,
minFontSize: 10,
stepGranularity: 10,
maxLines: 4,
overflow: TextOverflow.ellipsis,
)
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.
Trying to make a ui that contains Header and Footer with rearrangeable content items. There is a property called header from which we can add header item. But what to do if I want to add footer item as well.
import 'package:flutter/material.dart';
class MyStickyHeader extends StatefulWidget {
#override
State<StatefulWidget> createState() {
return _MyStickyHeaderState();
}
}
class _MyStickyHeaderState extends State<MyStickyHeader> {
List<Widget> _list = [
Text("Apple"),
Text("Ball"),
Text("Cat"),
Text("Dog"),
Text("Elephant")
];
#override
Widget build(BuildContext context) {
return Container(
margin: EdgeInsets.only(top: 30, left: 10),
color: Colors.white,
child: showData(),
);
}
Widget showData() {
return Container(
child: ReorderableListView(
header: Container(
height: 100,
color: Colors.red,
),
children: _list
.map((item) => Container(
padding: EdgeInsets.all(10),
key: Key("${(item as Text).data}"),
child: Row(
children: <Widget>[
Icon(Icons.ac_unit),
Expanded(
child: item,
)
],
),
))
.toList(),
onReorder: (int start, int current) {
// dragging from top to bottom
if (start < current) {
int end = current - 1;
Widget startItem = _list[start];
int i = 0;
int local = start;
do {
_list[local] = _list[++local];
i++;
} while (i < end - start);
_list[end] = startItem;
}
// dragging from bottom to top
if (start > current) {
Widget startItem = _list[start];
for (int i = start; i > current; i--) {
_list[i] = _list[i - 1];
}
_list[current] = startItem;
}
setState(() {});
},
),
);
}
}
Last element of your list can be a footer. It has to be a widget with onLongPress property. For example:
ReorderableListView(
onReorder: (int oldIndex, int newIndex) {},
children: List.generate(someList.items.length + 1, (index) {
if (index < someList.items.length)
return ListTile(
key: Key(someList.items[index].description),
);
else
return RaisedButton(key: Key('footer'), onPressed: () {}, onLongPress: (){}, child: Text('Button'));
})),
If you wrap your ReorderableListView with a Column and an Expanded widget, you can add a Container at the bottom to act as a footer:
Column(
children: <Widget>[
Expanded(
child: ReorderableListView(
header: Container(
height: 100,
color: Colors.red,
),
children: _list
.map((item) => Container(
padding: EdgeInsets.all(10),
key: Key("${(item as Text).data}"),
child: Row(
children: <Widget>[
Icon(Icons.ac_unit),
Expanded(
child: item,
)
],
),
)).toList(),
onReorder: (int start, int current) {
// dragging from top to bottom
if (start < current) {
int end = current - 1;
Widget startItem = _list[start];
int i = 0;
int local = start;
do {
_list[local] = _list[++local];
i++;
} while (i < end - start);
_list[end] = startItem;
}
// dragging from bottom to top
if (start > current) {
Widget startItem = _list[start];
for (int i = start; i > current; i--) {
_list[i] = _list[i - 1];
}
_list[current] = startItem;
}
setState(() {});
},
),
),
Container(
height: 40,
alignment: Alignment.center,
child: Text('Footer'),
color: Colors.orange,
),
],
),
To implement such view i recommend using Slivers.
benefits:
Sticky header/Footer.
infinity body/content scroll.
check the code below:
import 'package:flutter/material.dart';
class MyHomePage extends StatelessWidget {
#override
Widget build(BuildContext context) {
return Scaffold(
body: CustomScrollView(
slivers: [
SliverList(
delegate: SliverChildListDelegate(
[
Container(
width: double.infinity,
height: 50,
color: Colors.orangeAccent,
child: Center(
child: Text(
'Header',
style: TextStyle(color: Colors.white, letterSpacing:4),
),
),
),
ListView.builder(
shrinkWrap: true,
itemCount: 100,
itemBuilder: (BuildContext context, int index) {
return ListTile(
title: Center(child: Text('$index')),
);
},
),
],
),
),
SliverFillRemaining(
hasScrollBody: false,
child: Align(
alignment: Alignment.bottomCenter,
child: Container(
width: double.infinity,
height: 50,
color: Colors.blueAccent,
child: Center(
child: Text(
'Footer',
style: TextStyle(color: Colors.white, letterSpacing: 4),
),
),
),
),
)
],
),
);
}
}
For more detail take a look here:
https://itnext.io/create-a-header-footer-with-scrollable-body-view-in-flutter-5551087270de