I get an error while I'm building the ListView, At first I thought the API wasn't returning anything but I printed the movies variable in flutter and it consoled log the values.
Btw I'm trying to recreate this project:
https://github.com/mlabouardy/flutter-watchnow
The error I get is:
RangeError (index): Invalid value: Valid value range is empty: 0
This is the list view builder:
class TopMoviesState extends State<TopMovies> {
List<Movie> _movies = new List();
final _apiGatewayURL = "https://gfioehu47k.execute-api.us-west-1.amazonaws.com/staging/main";
Widget _fetchMovies() {
return new ListView.builder(
padding: const EdgeInsets.all(16.0),
itemBuilder: (context, i){
return new Card(
child: new Container(
height: 250.0,
child: new Padding(
padding: new EdgeInsets.all(2.0),
child: new Row(
children: <Widget>[
new Align(
child: new Hero(
child: new Image.network("https://image.tmdb.org/t/p/w500"+this._movies[i].getPoster()),
tag: this._movies[i].getTitle()
),
alignment: Alignment.center,
),
new Expanded(
child: new Stack(
children: <Widget>[
new Align(
child: new Text(
this._movies[i].getTitle(),
style: new TextStyle(fontSize: 11.0, fontWeight: FontWeight.bold),
),
alignment: Alignment.topCenter,
),
new Align(
child: new Padding(
padding: new EdgeInsets.all(4.0),
child: new Text(
this._movies[i].getOverview(),
maxLines: 8,
overflow: TextOverflow.ellipsis,
style: new TextStyle(fontSize: 12.0, fontStyle: FontStyle.italic)
)
),
alignment: Alignment.centerRight,
),
new Align(
child: new Text(
this._movies[i].getReleaseDate(),
style: new TextStyle(fontSize: 11.0, fontWeight: FontWeight.bold),
),
alignment: Alignment.bottomRight,
),
]
)
)
]
)
)
)
);
}
);
}
This is how I'm running through each value I'm getting from the API:
void _addMovie(dynamic movie){
this._movies.add(new Movie(
title: movie["title"],
overview: movie["overview"],
poster: movie["poster_path"],
releaseDate: movie["release_date"]
));
}
#override
void initState() {
super.initState();
http.get(this._apiGatewayURL)
.then((response) => response.body)
.then(json.decode)
.then((movies) {
movies.forEach(_addMovie);
});
}
You are not specifying an itemCount to ListView. So if you ever have enough space to display more then the number of items available, you'll try to access _movies at an invalid index. Resulting in this error.
Try to add
new ListView(
...
itemCount: _movie.length
you need to add item count
new ListView(
...
itemCount: _movie.length
without it ListView.Builder not know the position of list and and we are not showing the list according to position
Try to add the index of listview.builder,
Listview.builder is used for a very long or unknown set of lists, which you are doing correct.
Just count the number of values you are getting from the API response and add the index as a parameter.
For more reference, check this URL:
https://docs.flutter.io/flutter/widgets/ListView/ListView.builder.html
Related
I have a card that contains row of items (text and checkbox widgets). The problem is that the card can only fill up limited space each row, but it isn't going to the next line in the row. I tried using the wrap widget but it had no effect. I keep getting this:
As you can see it is not wrapping to the next but trying to fit everything in that one line. Here is my code:
Widget _buildCategories() {
return Card(
margin: const EdgeInsets.only(top: 20.0),
child: Padding(
padding: const EdgeInsets.all(20.0),
child: Column(
children: <Widget>[
Text(
'Categories',
style: TextStyle(fontFamily: 'MonteSerrat', fontSize: 16.0),
),
Wrap(
children: <Widget>[
Row(
children: <Widget>[
_checkBox('Gaming'),
_checkBox('Sports'),
_checkBox('Casual'),
_checkBox('21 +'),
_checkBox('Adult'),
_checkBox('Food'),
_checkBox('Club'),
_checkBox('Activities'),
_checkBox('Shopping'),
],
)
],
)
],
),
));
}
Widget _checkBox(String category) {
return Expanded(
child: Column(
children: <Widget>[
Text(
'$category',
textAlign: TextAlign.center,
style: TextStyle(fontFamily: 'MonteSerrat'),
),
Checkbox(
value: false,
onChanged: (value) {
// We will update the value to the firebase data here
print('updated value to: $value');
},
)
],
));
}
I fixed your code with the following changes:
Removed Row widget inside Wrap.
Removed Expanded widget.
Add the property maxLines to your Text widget.
Widget _buildCategories() {
return Card(
margin: const EdgeInsets.only(top: 20.0),
child: Padding(
padding: const EdgeInsets.all(20.0),
child: Column(
children: <Widget>[
Text(
'Categories',
style: TextStyle(fontFamily: 'MonteSerrat', fontSize: 16.0),
),
Wrap(
children: <Widget>[
_checkBox('Gaming'),
_checkBox('Sports'),
_checkBox('Casual'),
_checkBox('21 +'),
_checkBox('Adult'),
_checkBox('Food'),
_checkBox('Club'),
_checkBox('Activities'),
_checkBox('Shopping')
],
)
],
),
));
}
Widget _checkBox(String category) {
return Column(
children: <Widget>[
Text(
'$category',
maxLines: 1,
textAlign: TextAlign.center,
style: TextStyle(fontFamily: 'MonteSerrat'),
),
Checkbox(
value: false,
onChanged: (value) {
// We will update the value to the firebase data here
print('updated value to: $value');
},
)
],
);
}
}
Also you can add the properties runSpacing and spacing to your Wrap widget to give more space between your items in horizontal and vertical.
Wrap(
runSpacing: 5.0,
spacing: 5.0,
More info here: https://api.flutter.dev/flutter/widgets/Wrap-class.html
Good morning,
I'm new on Flutter.
I need to open a new page from a card after a button press. To open the new page I need the card data object that I use to show the information (data[X]). The button for open the new page is located in ButtonTheme.bar but in this location I don't have my "data[X]" object.
This is my code:
#override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: Text("Records Page", style: TextStyle(color: Colors.white)),
iconTheme: new IconThemeData(color: Colors.white),
backgroundColor: Color.fromRGBO(32, 38, 48, 1),
),
drawer: MainDrawer(),
body: new ListView.builder(
//reverse: true,
itemCount: reversedData != null ? reversedData.length : 0,
itemBuilder: (BuildContext ctxt, int index) {
if (reversedData[index].stop != null) {
return Card(
child: Column(
mainAxisSize: MainAxisSize.min,
children: <Widget>[
const ListTile(
leading: Icon(Icons.calendar_today),
title: Text('Last Period'),
//subtitle: Text('Music by Julie Gable. Lyrics by Sidney Stein.'),
),
new Container(
margin: EdgeInsets.symmetric(horizontal: 15),
child: new ListView(
shrinkWrap: true,
children: <Widget>[
new Text(
"Hour: ${reversedData[index].hourType.description}"),
new Text("Job: ${reversedData[index].job.description}"),
new Text(
"Opened at: ${DateFormat.yMd().add_jm().format(DateTime.parse(reversedData[index].start))}"),
new Text(
"Closed at: ${DateFormat.yMd().add_jm().format(DateTime.parse(reversedData[index].stop))}"),
],
),
),
ButtonTheme.bar(
// make buttons use the appropriate styles for cards
child: ButtonBar(
children: <Widget>[
FlatButton(
child: const Text('Modify',
style: TextStyle(
color: Color.fromRGBO(32, 38, 48, 1))),
onPressed: () {
Navigator.push(context, new MaterialPageRoute(builder: (__) => new PeriodEditPage(toChangeData: //my card data? ,)));
},
),
],
),
),
],
),
);
}
}));
}
I simply need to have the specific card data when I'm going to open the new page.
Hope I was clear.
Thank you
The trick is to declare your data in your statefulWidget (or statelessWidget) then use then wherever you want.
You can also create an object data where you'll have all your information instanciate one into your widget then pass it to another screen.
Here's an intro about passing data : https://flutter.dev/docs/cookbook/navigation/passing-data
Finally I found the solution to my problem:
#override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: Text("Records Page", style: TextStyle(color: Colors.white)),
iconTheme: new IconThemeData(color: Colors.white),
backgroundColor: Color.fromRGBO(32, 38, 48, 1),
),
drawer: MainDrawer(),
body: new ListView.builder(
//reverse: true,
itemCount: reversedData != null ? reversedData.length : 0,
itemBuilder: (BuildContext ctxt, int index) {
if (reversedData[index].stop != null) {
return Card(
child: Column(
mainAxisSize: MainAxisSize.min,
children: <Widget>[
const ListTile(
leading: Icon(Icons.calendar_today),
title: Text('Last Period'),
//subtitle: Text('Music by Julie Gable. Lyrics by Sidney Stein.'),
),
new Container(
margin: EdgeInsets.symmetric(horizontal: 15),
child: new ListView(
shrinkWrap: true,
children: <Widget>[
new Text(
"Hour: ${reversedData[index].hourType.description}"),
new Text(
"Job: ${reversedData[index].job.description}"),
new Text(
"Opened at: ${DateFormat.yMd().add_jm().format(DateTime.parse(reversedData[index].start))}"),
new Text(
"Closed at: ${DateFormat.yMd().add_jm().format(DateTime.parse(reversedData[index].stop))}"),
],
),
),
ButtonTheme.bar(
// make buttons use the appropriate styles for cards
child: ButtonBar(
children: <Widget>[
FlatButton(
child: const Text('Modify',
style: TextStyle(
color: Color.fromRGBO(32, 38, 48, 1))),
onPressed: () {
Navigator.push(
context,
new MaterialPageRoute(
builder: (__) => new PeriodEditPage(
toChangeData: reversedData[index],
)));
},
),
],
),
),
],
),
);
}
}));
}
My problem was solved thank to this line:
toChangeData: reversedData[index],
This code permit to open every card details with the same object used to populate my card.
I haven't understand properly what happen with the object reference but it works for me.
I'm attempting to create a scrollable listview inside of a container which also contains a static image. However, the listview doesn't appear to be scrollable and I get a "bottom overflow by x pixels" artifact on my app.
static List<Widget> getClubs() {
var myClubs = new List<Widget>();
for (var i = 0; i < 10; i++) {
myClubs.add(new Padding(
padding: EdgeInsets.all(8.0),
child: new CircleAvatar(
backgroundImage:
new NetworkImage("https://i.imgur.com/p2oUDLD.png"),
backgroundColor: Colors.black,
radius: 34.0,
)));
}
return myClubs;
}
final leftSection = new Container(
color: Color(0xFF212121),
width: 100,
child: Column(
children: <Widget>[
SizedBox(height: 20.0),
new Image.asset(
'assets/logo.png',
alignment: Alignment.center,
),
SizedBox(height: 10.0),
new Container(
child: new ListView(
shrinkWrap: true,
padding: const EdgeInsets.all(5.0),
children: getClubs(),
))
],
));
You can use Expanded widget or set the height for the Container.
I would also recommend Wrap for anyone running into an issue where a container is sized wrong within the listview, causing an overflow:
new Container(
alignment: Alignment.center,
child: Wrap( //the magic
children: [
new Container(),
],
),
),
Sometimes best way to fix an estimated height is using sizedbox
int heightToBeReduced = 380;
SizedBox(
height: MediaQuery.of(context).size.height - heightToBeReduced,
child: ListView....
)
wrap the Column in an Expanded widget:
Expanded(
child: Column(
),
),
Another way is to wrap the Column in a Flexible widget and specify a flex factor.
Flexible(
flex: 1,
child: Column(
children: [
],
),
),
I used a Flexible in a Row to control the output of the listview builder
Define the scollController
final _scrollController = ScrollController();
Define the ListView builder
ListView builder = ListView.builder(
itemCount: listPerformance?.length,
shrinkWrap: true,
itemBuilder: (context, index) {
return PerformanceCardWidget(performanceView: listPerformance[index]);
});
Define a flexible that outputs the listview builder contents
Row(crossAxisAlignment: CrossAxisAlignment.start, children: [
Flexible(
flex: 6,
fit: FlexFit.loose,
child: Scrollbar(
isAlwaysShown: true,
controller: _scrollController,
child: SingleChildScrollView(
controller: _scrollController, child: builder))
)
])
am using my customized ExpandedList .. but the header is overflowed when the text is long ....
and this is how am using it:
new CustomExpansionPanelList2(
expansionCallback: (int index, bool isExpanded) {
setState(() {
faqList[index].isExpanded =
!faqList[index].isExpanded;
});
},
children: faqList.map((Faq i) {
return new ExpansionPanel(
headerBuilder:
(BuildContext context, bool isExpanded) {
return new Container(
child: new Row(
children: <Widget>[
SizedBox(child: Text(
i.question,
style:
TextStyle(color: Color(0xff606060)),
),),
],
),
);
},
isExpanded: i.isExpanded,
body: new Center(
child: new Padding(
padding: EdgeInsets.all(10.0),
child: Text(i.answer),
),
),
);
}).toList(),
),
I want the text to be in two lines or more is needed to make it fit the width of the Expanded list..
How to achieve this?
Just replace SizedBox by Expanded in the headerBuilder
return new Container(
child: new Row(
children: <Widget>[
Expanded(child: Text(
i.question,
style:
TextStyle(color: Color(0xff606060)),
),),
],
),
);
I am consulting the news section of my website.
I'm using Future Builder to get the data from the web.
The problem I get is related to the image that I try to show on the screen.
And when there is a lot of news, the data load takes a long time and I do not know if there is a solution for loading faster.
I am consulting the text of the news through a json.
At that moment you get the URL of another JSON where the image is in thumbnail format.
I hope to solve this problem, I appreciate any help.
News.dart - Code
#override
Widget build(BuildContext context) {
return Scaffold(
appBar: searchBar.build(context),
key: _scaffoldKey,
body: new Container(
color: Colors.grey[800],
child: new RefreshIndicator(
child: new ListView(
children: <Widget>[
new FutureBuilder<List<Post>>(
future: fetchPosts(URLWEB),
builder: (context, snapshot) {
if(snapshot.hasData) {
List<Post> posts = snapshot.data;
return new Column(
children: posts.map((post2) => new Column(
children: <Widget>[
new Card(
margin: new EdgeInsets.symmetric(vertical: 20.0, horizontal: 20.0),
color: Colors.white,
child: new GestureDetector(
child: new Column(
crossAxisAlignment: CrossAxisAlignment.stretch,
children: <Widget>[
new FutureBuilder(
future: fetchPostsIMG(post2.imagen),
builder: (context, AsyncSnapshot<PostImg> snapshot2){
return new Container(
height: 200.0,
decoration: new BoxDecoration(
image: new DecorationImage(
image: CachedNetworkImageProvider(snapshot2.data.imagen == null ? new AssetImage('images/logotipo.png') : snapshot2.data.imagen),
fit: BoxFit.fitWidth
)
),
width: MediaQuery.of(context).size.width,
);
},
),
new ListTile(
title: new Text(post2.titulo.replaceAll("‘", "").replaceAll(
"’", "").replaceAll("–", "")
.replaceAll("…", "").replaceAll(
"”", "")
.replaceAll("“", ""),
style: new TextStyle(
color: Colors.black,
fontSize: 18.0,
fontWeight: FontWeight.bold),),
subtitle: new HtmlView(data: post2.informacion),
dense: true,
)
],
),
onTap: () {
//Navigator.of(context).push(new MaterialPageRoute(builder: (BuildContext context)=> new WebView(url: post2.urlweb, titulo: titulo)));
},
)
)
],
)).toList(),
);
}
else if(snapshot.hasError)
{
return new Container();
}
return new Center(
child: new Column(
children: <Widget>[
new Padding(padding: new EdgeInsets.all(50.0)),
new CircularProgressIndicator(),
],
),
);
},
),
],
),
onRefresh: _autoRefresh
),
),
);
}
}
It's because you are trying to access imagen on null object. You can do hasData check like below
CachedNetworkImageProvider(snapshot2.hasData ? snapshot2.data.imagen : new AssetImage('images/logotipo.png')),