title: Rebuilding only one row in Flutter ListView?
body: <p>I followed example app on Flutter
  <a href="https://codelabs.developers.google.com/codelabs/first-flutter-app-pt2/#0" rel="nofollow noreferrer">https://codelabs.developers.google.com/codelabs/first-flutter-app-pt2/#0</a></p>
  
  <p>and noticed that every time i click the row (make wordpair favourite and display red heart next to it) whole listview rebuilds. 
  Thought it might be inefficient to rebuild whole list so i modified original code from the example so that only the tile state changes and thought that would make only the tile to be redrawn. I see the state changes but the tile is not being redrawn/rebuild</p>
  
  <p>Now as i think of it it might have no sense as the tile size might change during update and it would require listview to be rebuild anyway but my questions is:</p>
  
  <p>What exactly prevents the tile from being rebuild/redrawn if setState() call proceed as normal and the tile is being marked as dirty?</p>
  
  <pre><code>import 'package:flutter/material.dart';
  import 'package:english_words/english_words.dart';
  
  void main() =&gt; runApp(new MyApp());
  
  class MyApp extends StatelessWidget {
    @override
    Widget build(BuildContext context) {
      return new MaterialApp(
        title: "This is the title of the App",
        home: RandomWords(),
      );
    }
  }
  
  class RandomWordsState extends State&lt;RandomWords&gt; {
    final List&lt;WordPair&gt; _suggestions = &lt;WordPair&gt;[];
    final Set&lt;WordPair&gt; _saved = Set&lt;WordPair&gt;();
  
    @override
    Widget build(BuildContext context) {
      print("dupa scaffold");
      return new Scaffold(
        appBar: AppBar(
          title: Text('Startup Name Generator'),
        ),
        body: _buildSuggestions(),
      );
    }
  
    Widget _buildSuggestions() {
      print("dupa listview");
      return ListView.builder(
        padding: const EdgeInsets.all(16.0),
        itemBuilder: (BuildContext _context, int i) {
          if (i.isOdd) {
            return new Divider();
          }
          final int index = i ~/ 2;
  
          if (index &gt;= _suggestions.length) {
            _suggestions.addAll(generateWordPairs().take(10));
          }
  
          return _buildRow(_suggestions[index]);
        },
      );
    }
  
    Widget _buildRow(WordPair wordPair) {
      print ("build row");
      return MyListTile(wordPair, _saved.contains(wordPair), () {
        print("$wordPair is saved ${_saved.contains(wordPair)}");
        final isSaved = _saved.contains(wordPair);
        isSaved ? _saved.remove(wordPair) : _saved.add(wordPair);
      });
    }
  }
  
  class MyListTile extends StatefulWidget {
  
    final MyListTileState _state;
  
    MyListTile(WordPair wordPair, bool isSaved, Function stateChangeOnTap):
      _state = MyListTileState(wordPair, isSaved, stateChangeOnTap);
  
    @override
    State&lt;StatefulWidget&gt; createState() =&gt; _state;
  }
  
  class RandomWords extends StatefulWidget {
    @override
    RandomWordsState createState() =&gt; new RandomWordsState();
  
  }
  
  class MyListTileState extends State&lt;MyListTile&gt; {
  
    final TextStyle _biggerFont = const TextStyle(fontSize: 18.0);
    WordPair wordPair;
    bool isSaved;
    Function stateChangeOnTap;
  
  
    MyListTileState(this.wordPair, this.isSaved, this.stateChangeOnTap);
  
    @override
    Widget build(BuildContext context) {
      // TODO: implement build
      return new ListTile(
        title: Text(
          wordPair.asPascalCase,
          style: _biggerFont,
        ),
        trailing: new Icon(
          isSaved ? Icons.favorite : Icons.favorite_border,
          color: isSaved ? Colors.red : null,
        ),
        onTap: () {
          setState(stateChangeOnTap);
        },
      );
    }
  
  }
  </code></pre>