Reputation: 12423
I have ListView
widget whose contents are loaded dynamically.
So I decided to make myStatelessWidget.
My basic ideas are
Keep variable articles
to be shown on ListView in the StatefulWidget or State.
Pass the contents from outside.
So for now, I write like this, but it has error.
Is my basic idea is correct? or where should I fix?
//// to pass the argument from outside.
new BodyLayout(articles: myarticles),
////
class BodyLayout extends StatefulWidget {
// List<Article> articles // ???I should put here/?
BodyLayout({articles});
@override
_BodyLayoutState createState() => _BodyLayoutState();
}
class _BodyLayoutState extends State<BodyLayout>{
// List<Article> articles // ???I should put here/?
@override
Widget build(BuildContext context) {
return ListView.builder(
itemCount: widget.articles.length, // the getter 'articles' is not defined error....
itemBuilder: (context, index) {
return ListTile(
title: Text(widget.articles[index].title),
onTap: () => onTapped(context,widget.articles[index].url),
);
},
);
}
}
Upvotes: 1
Views: 952
Reputation: 24661
If you want to convert your widget to a StatelessWidget
, then you can just delete the createState
and move the stuff in the build
method of the state class into the widget class. This works just fine if your widget doesn't maintain an internal state, but if it has interactive elements (like buttons or such) you will want to delegate them to the parent widget caller via a callback.
To define properties for your custom widget, define the fields as final
and instantiate them in the class constructor with this.fieldName
. For example:
class BodyLayout extends StatefulWidget {
BodyLayout({
this.articles,
this.onArticleTapped,
});
final List<Article> articles; // Defining the articles property
final void Function(String) onArticleTapped; // Defining the on-tapped callback
@override
Widget build(BuildContext context) {
return ListView.builder(
itemCount: widget.articles.length,
itemBuilder: (context, index) {
return ListTile(
title: Text(widget.articles[index].title),
onTap: () => onArticleTapped(widget.articles[index].url),
);
},
);
}
}
You can then use it like such:
...
BodyLayout(
articles: [some list of articles],
onArticleTapped: (url) => <do something with url>
),
Upvotes: 0
Reputation: 3361
You only need to use a stateful widget if you are going to call the setState() method to rebuild the widget with some new state. One case in which you might do that, if you need to retrieve the list of articles from some api or database call, is to have the widget return a loading indicator if the articles list is null, make the async call to retrieve the articles in the state class's initState() method, and when it is returned, rebuild the widget by calling setState() with the retrieved list of articles. Like this, maybe:
/// to pass the argument from outside.
new BodyLayout(),
///
class BodyLayout extends StatefulWidget {
BodyLayout();
@override
_BodyLayoutState createState() => _BodyLayoutState();
}
class _BodyLayoutState extends State<BodyLayout>{
List<Article> articles;
bool loading = true;
@override
void initState(){
_getArticles();
}
void getArticles() async {
articles = await Repository.instance.getArticles(); //some async method to retrieve the articles
setState((){
loading = false;
}); // after the articles are retrieved you can call setState to rebuild the widget
}
@override
Widget build(BuildContext context) {
if(loading) {
return CircularProgressIndicator();
}
return ListView.builder(
itemCount: articles.length,
itemBuilder: (context, index) {
return ListTile(
title: Text(articles[index].title),
onTap: () => onTapped(context, articles[index].url),
);
},
);
}
}
If you have the list of articles to begin with and don't need to rebuild the list, you can just make that a stateless widget and pass in the list of articles.
The error you indicated that you got, seems to be because articles is not actually defined as a variable for that class. Dart supports multiple syntax options for passing instance variables like this but this is how I would define that variable and ensure that it is being passed in when the widget is created (could be stateless or stateful widget):
//// to pass the argument from outside.
new BodyLayout(articles: myarticles),
////
class BodyLayout extends StatelessWidget {
final List<Article> articles
BodyLayout({this.articles}) : assert(articles != null);
@override
Widget build(BuildContext context){ ... };
}
Upvotes: 1