whitebear
whitebear

Reputation: 12423

Handling variables for stateful widget

I have ListView widget whose contents are loaded dynamically.

So I decided to make myStatelessWidget.

My basic ideas are

  1. Keep variable articles to be shown on ListView in the StatefulWidget or State.

  2. 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

Answers (2)

Abion47
Abion47

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

Kris
Kris

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

Related Questions