I have a problem with asynchrone in my piano app with flutter. It needs to play notes and change color on the keys when I call context.read().playNoteFromIndex(noteIndex, midiNote).
So, each time I call the playNoteFromIndex, the color of the key changes.
Here's the PianoKeyProvider :
Future playNoteFromIndex(int index, int midi) async { _colorList![index] = Colors.blue;
notifyListeners();
_flutterMidi.playMidiNote(midi: midi);
await Future.delayed(Duration(milliseconds: 500));
_colorList![index] = Colors.white;
notifyListeners();
}
In the build widget, I have a consumer that rebuild widgets when the color change : Flexible( flex: 1, child: Consumer( builder: (context, pianoKeysProvider, child) { return ListView.builder( itemCount: 7, scrollDirection: Axis.horizontal, controller: ScrollController(initialScrollOffset: 1500.0), itemBuilder: (BuildContext context, int index) { final int i = index * 12; return SafeArea( child: Stack( children: [ Row( mainAxisSize: MainAxisSize.min, children: [ _buildKey(24 + i, false, context, i), _buildKey(26 + i, false, context, i + 2), _buildKey(28 + i, false, context, i + 4), _buildKey(29 + i, false, context, i + 5), _buildKey(31 + i, false, context, i + 7), _buildKey(33 + i, false, context, i + 9), _buildKey(35 + i, false, context, i + 11), ], ),
I have another provider that changes the scale (set of notes to play on the keyboard) and I watch in my build widget for a change.
This is the call :
@override Widget build(BuildContext context) {
final ScaleProvider scaleProvider = context.watch<ScaleProvider>();
Scale? myScale = scaleProvider.getScale();
playScale(myScale!, context);
So I need to have a while loop that play notes one at the time in a function and calls the read for changing each note on the keyboard.
Future playScale(Scale scale, BuildContext context) async {
int currentIndex = 0;
while (_buttonPressPlay) {
if(currentIndex == scale.scaleLenght! - 1)
{
currentIndex = 0;
}
else
{
currentIndex++;
}
Note note = scale!.notes!.elementAt(currentIndex);
if (context.mounted)
{
await context.read<PianoKeysProvider>().playNoteFromIndex(note.positionX, note.positionY);
}
await Future.delayed(Duration(milliseconds: 500));
} }
I got an exception : setState() or markNeedsBuild called during build
I tried to use future.delayed (the application stop being asynchrone or freeze at context.watch) or context.monted but same exception. Future.delayed(Duration.zero, () {
final ScaleProvider scaleProvider = context.watch<ScaleProvider>();
Scale? myScale = scaleProvider.getScale();
playScale(myScale!, context);
} });
Where I should go from there ? Any thoughts ? Thank you so much (first time posting on Stack Overflow :D )!
you can use
WidgetsBinding.instance.addPostFrameCallback()
to make sure thatsetState()
ornotifyListeners()
will not be called during build