I'm struggling with the ListView.
My problem is that I've a Listview inside another Listview and the second Listview items height are not always the same. I want to get rid of the itemExtent and create an automatically height for the first Listview.
What I really want to acomplish is something like this:
#override
Widget build(BuildContext context) {
return
Column(
children: <Widget>[
TextField(),
Expanded(
child: ListView.builder(
key: new Key("ditisdekeyvoordelistview"),
itemBuilder: _makeMovieList,
padding: EdgeInsets.all(0.0),
itemCount: _movies.length,
itemExtent: 300.0,
),
),
],
);
}
//FIRST LIST
Widget _makeMovieList(BuildContext context, int index) {
return Container(
child: ListTile(
contentPadding: EdgeInsets.symmetric(horizontal: 20.0, vertical: 10.0),
leading: Container(
child: Column(mainAxisSize: MainAxisSize.max, children: <Widget>[
Image.network(
_movies[index].movieImage,
fit: BoxFit.cover,
width: 100.0,
)
])),
title: Text(
_movies[index].movieTitle,
),
subtitle: Column(
mainAxisAlignment: MainAxisAlignment.start,
crossAxisAlignment: CrossAxisAlignment.start,
children: <Widget>[
_makeStarRating(_movies[index].movieRating),
Text(_movies[index].movieDescription),
_makeCardDates(index)
],
),
),
);
}
//SECOND LIST
Widget _makeCardDates(int index) {
return Expanded(
child: ListView.builder(
physics: const NeverScrollableScrollPhysics(),
padding: EdgeInsets.all(0.0),
itemCount: _movies[index].dateTimeList.length,
itemBuilder: (context, indexx) {
return GestureDetector(
onTap: () {
print(_movies[index].dateTimeList[indexx].toString());
},
child: Card(
elevation: 8.0,
color: Color.fromRGBO(64, 75, 96, .9),
child: Column(
children: <Widget>[
Text(_movies[index].cinema),
Text(((dateFormatMovieHours
.format(_movies[index].dateTimeList[indexx]))
.toString())),
],
)));
},
itemExtent: 40.0,
),
);
}
Using itemExtent on a ListView isn't required, though ListView needs to have a finite height set for vertical scroll (default) and finite width is needed for horizontal scroll. Otherwise, the ListView will throw a "constraints are unbounded" error.
For your use case, you can either set height on the list items in the first ListView, or set height on the second ListView. However, if you'd like the second ListView to be non-scrollable and the list items in the first ListView will adapt dynamically, you can use a Column of Widgets similar to this sample.
import 'package:flutter/material.dart';
void main() {
runApp(MyApp());
}
class MyApp extends StatelessWidget {
#override
Widget build(BuildContext context) {
return MaterialApp(
title: 'Flutter Demo',
theme: ThemeData(
primarySwatch: Colors.blue,
),
home: MyHomePage(title: 'Flutter Demo Home Page'),
);
}
}
class MyHomePage extends StatefulWidget {
MyHomePage({Key key, this.title}) : super(key: key);
final String title;
#override
_MyHomePageState createState() => _MyHomePageState();
}
class _MyHomePageState extends State<MyHomePage> {
final items = [
'Apple',
'Banana',
'Carrot',
'Dog',
'Egg',
'Flower',
'Goat',
'Honey'
];
final subItems = ['1.00', '2.00', '3.00', '4.00'];
#override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: Text(widget.title),
),
body: Center(
child: firstList(),
),
);
}
firstList() {
return ListView.builder(
itemCount: items.length,
itemBuilder: (context, index) {
return Container(
padding: EdgeInsets.all(8.0),
child: Card(
child: Container(
color: Colors.lightBlue[50],
padding: EdgeInsets.all(16.0),
child: Row(
children: [
Expanded(
flex: 2,
child: Text('${items[index]}'),
),
Expanded(
flex: 1,
child: secondList(subItems),
),
],
),
),
),
);
},
);
}
secondList(List item) {
// Create List<Widget> for the second "List"
var subList = List<Widget>();
item.forEach((data) {
subList.add(
Card(
child: Container(
padding: EdgeInsets.all(16.0),
color: Colors.lightBlueAccent,
child: Text('$data'),
),
),
);
});
// Populate Column instead of ListView
return Column(
children: subList,
);
}
}
Related
I currently have a listview operating on the whole of my screen. I would like to have a button in the bottom of the screen, thus splitting it up so the listview doens't fill up the whole of my window.
This is the current code building the class:
#override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: Text('HT scoreboard'),
),
body: _buildBody(context),
);
}
Widget _buildBody(BuildContext context) {
return StreamBuilder<QuerySnapshot>(
stream: Firestore.instance.collection('Spillere').orderBy("score", descending: true).snapshots(),
builder: (context, snapshot) {
if (!snapshot.hasData) return LinearProgressIndicator();
return _buildList(context, snapshot.data.documents);
},
);
}
Widget _buildList(BuildContext context, List<DocumentSnapshot> snapshot) {
return ListView(
padding: const EdgeInsets.only(top: 10.0),
children: snapshot.map((data) => _buildListItem(context, data)).toList(),
);
}
Widget _buildListItem(BuildContext context, DocumentSnapshot data) {
final record = Record.fromSnapshot(data);
return Padding(
key: ValueKey(record.name),
padding: const EdgeInsets.all(5.0),
child: Container(
decoration: BoxDecoration(
border: Border.all(color: Colors.grey),
borderRadius: BorderRadius.circular(5.0),
),
child: ListTile(
title: Text(record.name + ": " + record.score.toString()),
trailing: new IconButton(icon: new Icon(isAdmin ? Icons.add : null, color: Colors.green),
onPressed: (){
if(isAdmin){
record.reference.updateData({'score': record.score + 1});
}
}
),
),
),
);
change your buildlist function to include a column with the button and listview as children
Widget _buildList(BuildContext context, List<DocumentSnapshot> snapshot) {
return Column(
children:[
Expanded(
child: ListView(
padding: const EdgeInsets.only(top: 10.0),
children: snapshot.map((data) => _buildListItem(context, data)).toList(),
),
),
RaisedButton(
// fill in required params
)
])
}
To prevent the buttons being pushed above the keyboard;
return CustomScrollView(
slivers: <Widget>[
SliverToBoxAdapter(
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: <Widget>[
// list items
],
),
),
SliverFillRemaining(
hasScrollBody: false,
child: Row(
crossAxisAlignment: CrossAxisAlignment.end,
children: <Widget>[
RaisedButton()
],
),
)
],
);
I have an image and a text in a column.
I want the text container to expand to the column width, creating a black border with the text centered.
This is what i got now.
Code.
List<Container> getMediaItem(List<Media> mediaItems) {
List<Container> mediaContainers = [];
for (Media media in mediaItems) {
mediaContainers.add(
Container(
padding: EdgeInsets.only(left: 8, right: 8, bottom: 8),
child: Column(
mainAxisSize: MainAxisSize.min,
children: [
media.image,
Container(
color: Colors.black,
padding: EdgeInsets.only(top: 8, bottom: 8),
child: Text(media.title),
)
],
),
),
);
}
return mediaContainers;
}
If you know how to solve it please share.
Update:
The full code (I'm trying to make a nested scroll view to display a gallery of media items, like Netflix):
class MyApp extends StatelessWidget {
#override
Widget build(BuildContext context) {
return MaterialApp(
title: 'Live Tree',
theme: ThemeData(
brightness: Brightness.dark,
),
home: Scaffold(
appBar: AppBar(
title: Row(
mainAxisAlignment: MainAxisAlignment.center,
mainAxisSize: MainAxisSize.max,
children: <Widget>[
Text("LiveTree"),
Icon(Icons.check_circle_outline),
],
),
),
body: HomePage(),
),
);
}
}
class HomePage extends StatefulWidget {
HomePage({Key key}) : super(key: key);
#override
_HomePageState createState() => _HomePageState();
}
class _HomePageState extends State<HomePage> {
#override
Widget build(BuildContext context) {
List<Column> getMediaItem(List<Media> mediaItems) {
List<Column> mediaContainers = [];
for (Media media in mediaItems) {
mediaContainers.add(
Column(
mainAxisSize: MainAxisSize.min,
crossAxisAlignment: CrossAxisAlignment.center,
children: [
media.image,
Container(
color: Colors.black,
padding: EdgeInsets.only(top: 8, bottom: 8),
child: Text(media.title),
)
],
),
);
}
return mediaContainers;
}
List<Widget> getCategoryRows(List<CategoryModel> categoryModels) {
List<Widget> categoryRows = [];
for (CategoryModel category in categoryModels) {
final mediaItemsForCategory = getMediaItem(category.media);
categoryRows.add(
ListView(scrollDirection: Axis.horizontal,
children: mediaItemsForCategory,
),
);
}
return categoryRows;
}
Widget gallerySection = Column(
mainAxisSize: MainAxisSize.min,
children: getCategoryRows(mockCategoryDataSet),
);
return Scaffold(
body: ListView(
children: <Widget>[
gallerySection,
],
),
);
}
Column(
crossAxisAlignment: CrossAxisAlignment.stretch,
children: [
media.image,
Container(
color: Colors.black,
padding: EdgeInsets.only(top: 8, bottom: 8),
child: Text(media.title),
),
],
);
or
Column(
children: [
media.image,
Container(
color: Colors.black,
padding: EdgeInsets.only(top: 8, bottom: 8),
child: Center(
child: Text(media.title),
),
),
],
);
I have a small data table and i want to change the column and row background color.
But unfortunately there is no property in DataColumn or DataRow to achieve this.
The only way i found is through modifying the label of DataColumn
DataColumn(label: Container(child: Text('Person'),color: Colors.amberAccent,)),
but there is a default padding for the DataColumn and the color only applies to the text.
and here is my code:
class _ContactLocationsState extends State<ContactLocations> {
#override
Widget build(BuildContext context) {
return Center(
child: DataTable(columns: <DataColumn>[
DataColumn(label: Text('Person')),
DataColumn(label: Text('Rating')),
DataColumn(label: Text('Distance')),
DataColumn(label: Text('Max Price')),
DataColumn(label: Text('Fee')),
], rows: <DataRow>[
DataRow(
cells: <DataCell>[
DataCell(Text("test")),
DataCell(Text("test")),
DataCell(Text("test")),
DataCell(Text("test")),
DataCell(Text("test")),
],
),
]),
);
}
}
If you are still looking for answer, you can use MaterialStateColor property. Here is the working code
return DataRow.byIndex(
index: row.key,
color: MaterialStateColor.resolveWith(
(states) {
if (row.key % 2 == 0) {
return Colors.blue[50];
} else {
return Colors.white;
}
},
),
I found a way using that you can achieve exactly same look as table and also change the colors of background using Row and Expanded Widgets.
I hope that Following Code Help You.
import 'package:flutter/material.dart';
void main() => runApp(new MyApp());
class MyApp extends StatelessWidget {
#override
Widget build(BuildContext context) {
return new MaterialApp(
title: 'Flutter Demo',
theme: new ThemeData(
primarySwatch: Colors.blue,
),
debugShowCheckedModeBanner: false,
home: new MyHomePage(),
);
}
}
class MyHomePage extends StatefulWidget {
#override
_MyHomePageState createState() => _MyHomePageState();
}
class _MyHomePageState extends State<MyHomePage> with SingleTickerProviderStateMixin {
AnimationController _controller;
#override
void initState() {
_controller = AnimationController(vsync: this);
super.initState();
}
#override
void dispose() {
_controller.dispose();
super.dispose();
}
#override
Widget build(BuildContext context) {
return Scaffold(
body: Container(
child: Center(
child: Column(
mainAxisSize: MainAxisSize.min,
children: <Widget>[
Row(
children: <Widget>[
Expanded(
child: Container(
color: Colors.green,
child: Text("person")
),
),Expanded(
child: Container(
color: Colors.red,
child: Text("Rating")
),
),Expanded(
child: Container(
color: Colors.green,
child: Text("Distance")
),
),Expanded(
child: Container(
color: Colors.red,
child: Text("Max Price")
),
),Expanded(
child: Container(
color: Colors.green,
child: Text("Free")
),
),
],
),
Row(
children: <Widget>[
Expanded(
child: Container(
color: Colors.red,
child: Text("Test")
),
),Expanded(
child: Container(
color: Colors.green,
child: Text("Test")
),
),Expanded(
child: Container(
color: Colors.red,
child: Text("Test")
),
),Expanded(
child: Container(
color: Colors.green,
child: Text("Test")
),
),Expanded(
child: Container(
color: Colors.red,
child: Text("Test")
),
),
],
),
],
),
)
),
);
}
}
I would like to basically make a Settings Screen. Actually my screen look like that with Settings section that are composed of one title and a non-scrollable list view. I would like to put these sections in a list view to make the screen scrollable and to make every settings section following the last one.
Here is my actual screen.
And here is my code.
Widget build(BuildContext context) {
return Container(
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
mainAxisAlignment: MainAxisAlignment.start,
mainAxisSize: MainAxisSize.min,
children: <Widget>[
Expanded(
child: SettingSection('Communication', [
SettingsPayload('Email', 'Switch', () => (print('Function :)'))),
SettingsPayload('Notifications', 'Switch'),
])),
Expanded(
child: SettingSection('Hello', [
SettingsPayload('Email', 'Normal', () => print('value'),
Icons.arrow_forward_ios),
SettingsPayload('Notifications', 'Normal', () => print('hello')),
]))
],
));
How I build each list
buildlistSettings() {
return ListView.builder(
physics: ScrollPhysics(parent: NeverScrollableScrollPhysics()),
padding: const EdgeInsets.all(15.0),
itemBuilder: (context, i) {
if (i < widget.listSettings.length) {
return Column(
children: <Widget>[
buildTileSettings(widget.listSettings[i]),
Divider(),
],
);
}
});
}
#override
Widget build(BuildContext context) {
return Container(
child: Column(
crossAxisAlignment: CrossAxisAlignment.stretch,
mainAxisSize: MainAxisSize.min,
children: <Widget>[
Container(
padding: EdgeInsets.only(left : 10.0),
height: 25.0,
decoration:
BoxDecoration(color: Color.fromRGBO(223, 230, 233, 0.5), border: Border(bottom: BorderSide(color: Color.fromRGBO(223, 230, 233, 0.5), width: 1.0))),
child: Row(children: <Widget>[Text(
widget.title,
style: TextStyle(fontSize: 15.0, color: Colors.blueGrey),
)])),
Expanded(child: buildlistSettings()),
],
),
);
}
}
I think there's a set of problems here not least of which is trying to put a ListView inside a ListView (and having to turn off scrolling), but mostly the use of Expanded which tries to fill up all the space in the relevant direction, but inside another container, which is itself unbounded.
I would use a ListView at the root, then build up the children using simple Column, ListTile and Divider widgets. Here's a working example (duplicated your data to ensure there's enough to scroll):
import 'package:flutter/material.dart';
void main() => runApp(new MyApp());
class MyApp extends StatelessWidget {
#override
Widget build(BuildContext context) {
return new MaterialApp(
title: 'Flutter Demo',
home: new MyHomePage(title: 'Flutter Demo Home Page'),
);
}
}
class MyHomePage extends StatefulWidget {
final String title;
MyHomePage({Key key, this.title}) : super(key: key);
#override
_MyHomePageState createState() => new _MyHomePageState();
}
class _MyHomePageState extends State<MyHomePage> {
#override
Widget build(BuildContext context) {
return new Scaffold(
appBar: new AppBar(
title: new Text(widget.title),
),
body: ListView(
children: <Widget>[
SettingSection('Communication', [
SettingsPayload('Email', 'Switch', () => (print('Function :)'))),
SettingsPayload('Notifications', 'Switch'),
]),
SettingSection('Hello', [
SettingsPayload('Email', 'Normal', () => print('value'),
Icons.arrow_forward_ios),
SettingsPayload('Notifications', 'Normal', () => print('hello')),
]),
SettingSection('Communication', [
SettingsPayload('Email', 'Switch', () => (print('Function :)'))),
SettingsPayload('Notifications', 'Switch'),
]),
SettingSection('Hello', [
SettingsPayload('Email', 'Normal', () => print('value'),
Icons.arrow_forward_ios),
SettingsPayload('Notifications', 'Normal', () => print('hello')),
]),
],
),
);
}
}
class SettingSection extends StatelessWidget {
final String title;
final List<SettingsPayload> payload;
SettingSection(this.title, this.payload);
#override
Widget build(BuildContext context) {
return Container(
child: Column(
crossAxisAlignment: CrossAxisAlignment.stretch,
mainAxisSize: MainAxisSize.min,
children: <Widget>[
Container(
padding: EdgeInsets.only(left: 10.0),
height: 25.0,
decoration: BoxDecoration(
color: Color.fromRGBO(223, 230, 233, 0.5),
border: Border(
bottom: BorderSide(
color: Color.fromRGBO(223, 230, 233, 0.5),
width: 1.0))),
child: Row(children: <Widget>[
Text(
this.title,
style: TextStyle(fontSize: 15.0, color: Colors.blueGrey),
)
])),
_buildListSettings(context),
],
),
);
}
Widget _buildListSettings(BuildContext context) {
List<Widget> items = List<Widget>();
for (var setting in this.payload) {
// TODO: Add logic here to determine what kind of setting widget to
// create based on the payload.
items.add(ListTile(
title: Text(setting.title),
trailing: Icon(setting.icon),
));
items.add(Divider());
}
return Column(children: items);
}
}
class SettingsPayload {
final String title;
final String type;
final Function handler;
final IconData icon;
SettingsPayload(this.title, this.type, [this.handler, this.icon]);
}
You could use ListView.builder at the root level if your incoming settings data is variable and create SettingSection widgets for each item. Depends how you're handling your data.
I have an issue with a bottomNavigationBar, the issue happens only on iPhone X, as there appears some padding below the BNB as if it were inside a SafeArea widget (and its not)
Well, how can I remove that padding? or maybe color it somehow?
This is my code for the build function
#override
Widget build(BuildContext context) {
return new Scaffold(
appBar: _buildAppBar(),
drawer: _buildDrawer(),
body: _isLoading
? Center(
child: CircularProgressIndicator(),
)
: new Container(
color: Colors.white,
child: _unauthorizedOrders()),
// floatingActionButton: FloatingActionButton(
// onPressed: () {},
// backgroundColor: primaryColor,
// child: Icon(Icons.flash_on, size: 30.0,),
// tooltip: 'Authorize Orders',
// ),
// floatingActionButtonLocation: FloatingActionButtonLocation.centerDocked,
backgroundColor: Colors.red,
bottomNavigationBar: BottomAppBar(
child: Container(
height: 40.0,
color: primaryColor,
child: Row(
children: <Widget>[
// Padding(
// padding: const EdgeInsets.only(left: 10.0),
// child: Text(
// 'Orders: ${orders.length}',
// style: TextStyle(
// color: Colors.white,
// fontSize: 18.0,
// fontWeight: FontWeight.bold),
// ),
// ),
],
),
)),
);
}
EDIT: I have added the backgroundColor to the scaffold, removed the docked FAB and wrapped the body inside a container to paint it white, still doesn't work, I have updated the code above to show it
Use
SafeArea(
child: BottomAppBar(...)
);
A work-around will be to set the backgroundColor of your Scaffold to the same color as your BottomNavigationBar and have your content in a container with the color that you want.
Edit:
Here is a Sample code:
import 'package:flutter/material.dart';
void main() => runApp(new MyApp());
class MyApp extends StatelessWidget {
// This widget is the root of your application.
#override
Widget build(BuildContext context) {
return new MaterialApp(
title: 'BNB Demo on iPhone X',
theme: new ThemeData(
brightness: Brightness.dark,
primaryColor: Colors.black,
),
home: new MyHomePage(title: 'BNB Demo on iPhone X'),
);
}
}
class MyHomePage extends StatefulWidget {
MyHomePage({Key key, this.title}) : super(key: key);
final String title;
#override
_MyHomePageState createState() => new _MyHomePageState();
}
class _MyHomePageState extends State<MyHomePage> {
int _counter = 0;
void _incrementCounter() {
setState(() {
_counter++;
});
}
#override
Widget build(BuildContext context) {
return new SafeArea(
child: new Scaffold(
appBar: new AppBar(
title: new Text(widget.title),
),
body: new Center(
child: new Column(
mainAxisAlignment: MainAxisAlignment.center,
children: <Widget>[
new Text(
'You have pushed the button this many times:',
),
new Text(
'$_counter',
style: Theme.of(context).textTheme.display1,
),
],
),
),
floatingActionButton: FloatingActionButton(
child: Icon(Icons.add, color: Colors.white,),
backgroundColor: Colors.black,
onPressed: _incrementCounter,
),
floatingActionButtonLocation: FloatingActionButtonLocation.centerDocked,
bottomNavigationBar: new BottomAppBar(
child: Container(
height: 40.0,
color: Colors.black,
),
),
),
);
}
}
I had the same problem with this bottomAppBar.
I only erased the color attibute.
Hope it helps to someone.
BottomAppBar(
elevation: 0.0,
shape: widget.notchedShape,
child: Container(
decoration: BoxDecoration(
borderRadius: BorderRadius.only(
topLeft: Radius.circular(20.0), topRight: Radius.circular(20.0)),
color: Colors.white),
child: Row(
mainAxisSize: MainAxisSize.max,
mainAxisAlignment: MainAxisAlignment.spaceAround,
children: items)
),
color: Colors.teal[50]);