I've created a simple screen that takes a List of letters and renders them in a grid. I have a button with a shuffle method that shuffles this list. Inside my build method, I see that the state is getting updated with the new list and is printing out a shuffled list each time the button is pressed, but the screen doesn't change.
class _LetterContainerState extends State<LetterContainer> {
List<String> _letters = ['D', 'A', 'B', 'C', 'E', 'F', 'G', 'H'];
void shuffle() {
var random = new Random();
List<String> newLetters = _letters;
for (var i = newLetters.length - 1; i > 0; i--) {
var n = random.nextInt(i + 1);
var temp = newLetters[i];
newLetters[i] = newLetters[n];
newLetters[n] = temp;
}
setState(() {
_letters = newLetters;
});
}
@override
Widget build(BuildContext context) {
print('LETTERS');
print(_letters);
List<LetterTile> letterTiles =
_letters.map<LetterTile>((letter) => new LetterTile(letter)).toList();
return new Column(
children: <Widget>[
new FlatButton(onPressed: shuffle, child: new Text("Shuffle")),
new Container(
color: Colors.amberAccent,
constraints: BoxConstraints.expand(height: 200.0),
child: new GridView.count(
crossAxisCount: 4,
mainAxisSpacing: 4.0,
crossAxisSpacing: 4.0,
children: letterTiles,
))
],
);
}
}
EDIT:
import 'package:flutter/material.dart';
class Vowels {
static const values = ['A', 'E', 'I', 'O', 'U'];
static bool isVowel(String letter) {
return values.contains(letter.toUpperCase());
}
}
class LetterTile extends StatefulWidget {
final String value;
final bool isVowel;
LetterTile(value)
: value = value,
isVowel = Vowels.isVowel(value);
@override
_LetterTileState createState() => new _LetterTileState(this.value);
}
class _LetterTileState extends State<LetterTile> {
_LetterTileState(this.value);
final String value;
@override
Widget build(BuildContext context) {
Color color = Vowels.isVowel(this.value) ? Colors.green : Colors.deepOrange;
return new
Card(
color: color,
child: Padding(
padding: EdgeInsets.all(8.0),
child: Text(
this.value,
style: TextStyle(fontSize: 40.0, color: Colors.white)
)
)
);
}
}
If you replace your example LetterTile widget with a Text widget, the shuffling will work again. The reason this is not working is that a State object is only created the first time a widget is instantiated. So by passing the value directly to the state, you ensure that it never updates. Instead reference the value via widget.value
:
class LetterTile extends StatefulWidget {
final String value;
final bool isVowel;
LetterTile(this.value) : isVowel = Vowels.isVowel(value);
@override
_LetterTileState createState() => new _LetterTileState();
}
class _LetterTileState extends State<LetterTile> {
@override
Widget build(BuildContext context) {
Color color = Vowels.isVowel(widget.value) ? Colors.green : Colors.deepOrange;
return Card(
color: color,
child: Padding(
padding: EdgeInsets.all(8.0),
child: Text(
widget.value,
style: TextStyle(fontSize: 40.0, color: Colors.white)
)
)
);
}
}
The point of a State object is that it is persistent across builds. The first time you build a particular LetterTile widget, this also creates a new State object. The second time build is called, the framework finds the existing State object and reuses it. This is how you can have resources like timers, network requests, and other bound to an immutable tree of widgets.
In your case, since you passed the letter to the State object, each one would stay associated with whatever the first passed letter was. Instead, by reading them off the widget you always receive the most up to date data when the widget associated with the State object is replaced.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With