Steps to Reproduce
Minimal repro
import 'dart:async';
import 'package:flutter/material.dart';
void main() {
runApp(const MyApp());
}
class MyApp extends StatelessWidget {
const MyApp({Key? key}) : super(key: key);
@override
Widget build(BuildContext context) {
return MaterialApp(
title: 'Unexpected Behavior with Nested Async Generators in Dart',
theme: ThemeData(
primarySwatch: Colors.blue,
),
home: CounterWidget(),
);
}
}
class CounterWidget extends StatefulWidget {
CounterWidget({Key? key}) : super(key: key);
@override
_CounterWidgetState createState() => _CounterWidgetState();
}
class _CounterWidgetState extends State<CounterWidget> {
var correct = <int>[];
var wrong = <int>[];
final blocCorrect = Counter();
final blocWrong = Counter();
@override
void initState() {
super.initState();
blocCorrect.listen((i) {
correct.add(i);
setState(() {});
});
blocWrong.listen((i) {
wrong.add(i);
setState(() {});
});
}
@override
Widget build(BuildContext context) {
return Scaffold(
body: Center(
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: [
Text(
correct.toString(),
style: TextStyle(color: Colors.black),
),
SizedBox(height: 50),
TextButton(
child: Text('Correct'),
onPressed: () => blocCorrect.add(CounterEvent.correct),
),
SizedBox(height: 50),
Container(
color: Colors.black,
width: 200,
height: 2,
),
SizedBox(height: 50),
Text(
wrong.toString(),
style: TextStyle(color: Colors.black),
),
SizedBox(height: 50),
TextButton(
child: Text('Wrong'),
onPressed: () => blocWrong.add(CounterEvent.wrong),
),
],
),
),
);
}
}
enum CounterEvent { correct, wrong }
class Counter extends Bloc<CounterEvent, int> {
Counter() : super(0);
@override
Stream<int> mapEventToState(CounterEvent event) async* {
if (event == CounterEvent.correct) {
// Works
yield state + 1;
yield state + 1;
} else {
/// Does not work
yield* _increment();
}
}
Stream<int> _increment() async* {
yield state + 1;
yield state + 1;
}
}
abstract class Bloc<E, S> {
Bloc(this._state) {
_eventController.stream.asyncExpand(mapEventToState).listen((s) {
_state = s;
_stateController.add(_state);
});
}
final _eventController = StreamController<E>();
final _stateController = StreamController<S>.broadcast();
S _state;
S get state => _state;
StreamSubscription<S> listen(void Function(S) onData) {
return _stateController.stream.listen(onData);
}
Stream<S> mapEventToState(E event);
void add(E event) => _eventController.add(event);
}
- Run minimal repro
- Click 'Correct', prints 1,2
- Click 'Wrong', prints 1,1
Expected results:
Clicking 'Wrong' should print 1,2 - similar to the output of clicking 'Correct'
Actual results:
yield state doesn't get update as expected. Clicking 'Wrong' prints 1,1

As a workaround, _increment() can be made recursive. Calling yield* _increment(2) prints 1,2
Stream<int> _increment(int n) async* {
if (n > 0) {
yield state + 1;
yield* _increment(n - 1);
}
}
Logs
[✓] Flutter (Channel stable, 2.5.1, on macOS 11.6 20G165 darwin-x64, locale en)
• Flutter version 2.5.1
• Upstream repository https://github.com/flutter/flutter.git
• Framework revision ffb2ecea52 (2 weeks ago), 2021-09-17 15:26:33 -0400
• Engine revision b3af521a05
• Dart version 2.14.2
[!] Android toolchain - develop for Android devices (Android SDK version 30.0.2)
• Android SDK
✗ cmdline-tools component is missing
Run `path/to/sdkmanager --install "cmdline-tools;latest"`
See https://developer.android.com/studio/command-line for more details.
✗ Android license status unknown.
Run `flutter doctor --android-licenses` to accept the SDK licenses.
See https://flutter.dev/docs/get-started/install/macos#android-setup for more details.
[✓] Xcode - develop for iOS and macOS
• Xcode at /Applications/Xcode.app/Contents/Developer
• Xcode 12.0.1, Build version 12A7300
• CocoaPods version 1.10.0
[✓] Chrome - develop for the web
• Chrome at /Applications/Google Chrome.app/Contents/MacOS/Google Chrome
[✓] Android Studio (version 2020.3)
• Android Studio at /Applications/Android Studio.app/Contents
• Flutter plugin can be installed from:
🔨 https://plugins.jetbrains.com/plugin/9212-flutter
• Dart plugin can be installed from:
🔨 https://plugins.jetbrains.com/plugin/6351-dart
• Java version OpenJDK Runtime Environment (build 11.0.10+0-b96-7281165)
[✓] VS Code (version 1.60.2)
• VS Code at /Applications/Visual Studio Code.app/Contents
• Flutter extension version 3.26.0
[✓] Connected device (1 available)
• Chrome (web) • chrome • web-javascript • Google Chrome 94.0.4606.61
! Doctor found issues in 1 category.
Steps to Reproduce
Minimal repro
Expected results:
Clicking 'Wrong' should print 1,2 - similar to the output of clicking 'Correct'
Actual results:
yield statedoesn't get update as expected. Clicking 'Wrong' prints 1,1As a workaround,
_increment()can be made recursive. Callingyield* _increment(2)prints 1,2Logs