Flutter Bloc Error : emit was called after an event handler completed normally - between two functions - flutter

I have the following problem...
emit was called after an event handler completed normally. This is
usually due to an unawaited future in an event handler. Please make
sure to await all asynchronous operations with event handlers and use
emit.isDone after asynchronous operations before calling emit() to
ensure the event handler has not completed.
BAD on((event, emit) {
future.whenComplete(() => emit(...)); });
GOOD on((event, emit) async {
await future.whenComplete(() => emit(...)); }); )
What happens is that in a function called _onLogIn, if the user has changed the language, it goes from there to another function inside the bloc, these two functions do not depend on each other, I mean that each function is called in different pages of the application, but still _onLogIn checks the _onChangeLanguage function.
UserBloc({this.usecases}) : super(UserInitial()) {
on<LogInEvent>(_onLogIn);
on<ChangeLanguageEvent>(_onChangeLanguage);
}
_onLogIn function :
void _onLogIn(
LogInEvent event,
Emitter<StateA> emit,
) async {
emit(UserLoading());
final userOrFailure = await services.logIn(
x: event.x,
y: event.y,
);
await userOrFailure.fold((user) async {
/// If the user is logging in for the first time and does not
/// have a preferred language.
if (user.preferredLanguage == null) {
emit(UserSuccess());
emit(UserAlreadyLogged(connectedUser));
} else {
/// An ChangeLanguageEvent object
ChangeLanguageEvent event = ChangeLanguageEvent(
user.preferredLanguage,
user.someId,
);
/// Call the other function in the same bloc
this._onChangeLanguage(
event,
emit,
isFromLogin: true,
);
}
}, (failure) {
emit(UserError(failure.message));
});
}
_onChangeLanguage function :
void _onChangeLanguage(
ChangeLanguageEvent event,
Emitter<StateA> emit, {
bool isFromLogin = false,
}) async {
final successOrFailure = await services.updateLanguage(
event.language,
event.someId,
);
await successOrFailure.fold( // ! HERE THE ERROR WHEN I LOG IN; but when i changed the language from the application i don't have an error
(language) async {
emit(ChangeAppLanguage(language));
final sessionOrFailure = await services.getSession();
sessionOrFailure.fold(
(session) {
/// I need this condition to know if the language comes from login
if (isFromLogin) {
emit(UserSuccess());
}
emit(UserAlreadyLogged(session));
},
(failure) => emit(UserError(failure.message)),
);
},
(failure) {
emit(UserError(failure.message));
},
);
}
Any idea why? Thank you

void _onChangeLanguage(
ChangeLanguageEvent event,
Emitter<StateA> emit, {
bool isFromLogin = false,
}) async
This should be a major red flag. A call marked as async, but not returning a Future<>. There is no way, the caller could possibly await this call. Or even know that they should await this call.
Make it return a proper Future<void> instead of just void and your bloc should pick up on that and properly await the call.
There even is a linter rule for this: avoid_void_async. Did you turn off your linter? Don't do that. Turn your linter on and listen to it. Your other function has the same problem.

In my case I had to return Future<...>, but have not done it in the 'switch' statement, rather I've used the 'break' on all cases. So a compiler have not pointed me the lack of the 'return' statement.
After I have put 'return' on all cases, the error disappeared.

Related

microtasks and event loop

The dart documentation says that if the Future is completed, then the then methods will be run in one of the following microtasks. In the following example, everything works as expected because await is applied.
// print: exit_main, micro, then
main() async {
var p = Future.value(1);
await p;
scheduleMicrotask(() {
print('micro');
});
p.then((e) {
print('then');
});
print('exit_main');
}
But if await is removed, then the logic described above does not work. A microtask that is created before the then function is run after the then function is executed. But the then function is run after main, you can see that because a message is printed at the end of the main function.
// print: exit_main, then, micro
main() {
var p = Future.value(1);
scheduleMicrotask(() {
print('micro');
});
p.then((e) {
print('then');
});
print('exit_main');
}
In connection with what has been said, the following question. Why does the then function run before the created microtask?
Case 2 was expected to work just like the first case when await is applied.
Understood, no answer needed. Presumably, the Future.value function with a simple value completes the Future in the future microtask, i.e. the Future is scheduled to complete before another microtask is called. After the Future completes, all then methods are called immediately, provided that they return a non-terminated Future. The behavior changes if the future has already completed. An example is presented below.
main() {
var p = Future.value(1);
Timer.run(() {
scheduleMicrotask(() {
print('micro');
});
p.then((e) {
print('then');
});
print('exit_main');
});
}

Run multiple asyn function flutter one after one flutter

hello I want have to run two functions(Function1() and Function2()) and store value of these returns and run third function. But some time according to condition Function1() or Function2() or both not be run.
if(condition1){
await Function1();
}
if(condition2){
await Function2();
}
await Functon3();
I try as above but Function3() run simultaneously with Function1() or with Function2().
My Function1() code looks like following...
Future Function1() async {
apiService
.apiFileUpload()
.then((value) async {
///codes goes here
}).catchError((error) {
print('EEEE: ' + error.toString());
});
}
If anything not clear please let me know in the comment section.
Please do not use .then() in combination with async/await. It is technically possible, but it takes some skill to get it right, so why make it hard on yourself. Stick with one way of doing it, use either one or the other. You mixed it up and through a slight oversight, your Function1 does not actually wait on it's result. It just returns, with the function still running in the then block. So you await it, but that does not help.
Since you are using await already, stick with that and remove .then() from your repertoire for now:
Future Function1() async {
try {
final value = await apiService.apiFileUpload();
///codes goes here
} catch(error) {
print('EEEE: ' + error.toString());
}
}
You can use await
Future Function1() async {
try{
final value = await apiService
.apiFileUpload();
final value2 = await secondFuntion();
///add more and condition on values
} catch(e){
.....
}
}
from your question you need to tell the compiler to stop on particular task with await and avoid using then function it will never stop your compiler
your future fuction:
Future Function1() async {
apiService
.apiFileUpload()
.then((value) async {
///codes goes here
}).catchError((error) {
print('EEEE: ' + error.toString());
});
}
Modified Future func
Future Function1() async {
var result = await apiService.apiFileUpload();
if(result == success){
// code goes here
}else{
//you can show your error here
}
}

got an AgoraRtcException(20, Make sure you call RtcEngine.initialize first) even after initializing the engine

trying to make a 1:1 video meeting with agora with flutter and after following the docs i got
AgoraRtcException(20, Make sure you call RtcEngine.initialize first) exception although I am sure I am initializing it first however this the initialize code
void initState() {
super.initState();
setupVideoSDKEngine();
join();
the setupVideoSDKEngine() method code is
Future<void> setupVideoSDKEngine() async {
// retrieve or request camera and microphone permissions
await [Permission.microphone, Permission.camera].request();
//create an instance of the Agora engine
agoraEngine = createAgoraRtcEngine();
await agoraEngine
.initialize(RtcEngineContext(appId: Environment.agoraAppId));
await agoraEngine.enableVideo();
// Register the event handler
agoraEngine.registerEventHandler(
RtcEngineEventHandler(
onJoinChannelSuccess: (RtcConnection connection, int elapsed) {
showMessage(
"Local user uid:${connection.localUid} joined the channel");
setState(() {
_isJoined = true;
});
},
onUserJoined: (RtcConnection connection, int remoteUid, int elapsed) {
showMessage("Remote user uid:$remoteUid joined the channel");
setState(() {
_remoteUid = uid;
player.stop();
customTimer!.resetAndStart();
});
},
onUserOffline: (RtcConnection connection, int remoteUid,
UserOfflineReasonType reason) {
showMessage("Remote user uid:$remoteUid left the channel");
callEnded = true;
setState(() {
_remoteUid = null;
});
print('stats ${reason.name}');
if (!userOffline) {
Future.delayed(Duration(seconds: 1), () => Navigator.pop(context));
}
userOffline = true;
},
),
);
}
I am expecting to join the channel but nothing happens and it throws this error
I tried to delete the app and reinstall it but nothing happens
and got this exception too AgoraRtcException(-17, null)
you can to call this before using any other agora function i.e. You are trying to use agora sdk function without initializing it so here it is
This will be written globally
late final RtcEngineEx _engine;
this will be written in initState
_engine = createAgoraRtcEngineEx();
await _engine.initialize(const RtcEngineContext(
appId: APP_ID,
channelProfile: ChannelProfileType.channelProfileLiveBroadcasting,
));
This piece of advice I'm giving is for the correct usage of async and await keywords.
In your initState() function, you are calling two functions one after another; out of which the first function setupVideoSDKEngine() is an async function, and another function join() chould also be an async because the Agora join channel code returns a Future.
await engine.joinChannel(agoraToken, channelId, '', 0,);
Your code right now does not wait for the engine initialization and starts joining channel. Thus, the error. So you got to await your initialization and then write your join channel code.
For eg.
/* permission stuffs */
await agoraEngine.initialize(RtcEngineContext(appId: Environment.agoraAppId));
/* do event handling tasks */
// Now join the channel.
await engine.joinChannel(agoraToken, channelId, '', 0,);
It is recommended that you put the whole code into one async function.

Can't yield in forEachAsync inside Stream in dart/flutter

I have a forEachAsync inside an async* Stream and can't yield.
Stream<ProjectState> _mapProjectSelectedEventToState(ProjectSelected event) async* {
try {
yield ProjectLoading(
message: 'Fetching database',
fetchedCount: 0,
totalCount: 1,
);
await forEachAsync(fileModels, (FileEntity fileModel) async {
await downloader.download(filename: fileModel.hashName);
_totalMediaFilesFetched++;
//// ERROR - THIS DOES NOT WORK ////
yield (ProjectLoadingTick(
_totalMediaFiles,
_totalMediaFilesFetched,
));
}, maxTasks: 5);
} catch (error, stacktrace) {
yield ProjectFailure(error: error);
}
}
I've tried other means by dispatching the message and converting it to a state but it doesn't work as well. It seems like the whole app is blocked by this await forEachAsync.
I'm using the bloc pattern which reacts to the emited ProjectStates based on the current ProjectSelected event
Your attempt doesn't work because you're using yield in a callback, not in the function that's returning a Stream. That is, you're attempting the equivalent of:
Stream<ProjectState> _mapProjectSelectedEventToState(ProjectSelected event) async* {
...
await forEachAsync(fileModels, helperFunction);
...
}
Future helperFunction(FileEntity fileModel) async {
...
yield ProjectLoadingTick(...);
}
which doesn't make sense.
Since care about forEachAsync's ability to set a maximum limit to the number of outstanding asynchronous operations,
you might be better off using a StreamController that you can manually add events to:
var controller = StreamController<ProjectState>();
// Note that this is not `await`ed.
forEachAsync(fileModels, (FileEntity fileModel) async {
await downloader.download(filename: fileModel.hashName);
_totalMediaFilesFetched++;
controller.add(ProjectLoadingTick(
_totalMediaFiles,
_totalMediaFilesFetched,
));
},
maxTasks: 5);
yield* controller.stream;

Dart Flutter, help me understand futures

See this code:
class SomeClass{
String someVariable;
SomeClass();
Future<String> getData () async {
Response response = await get('http://somewebsite.com/api/content');
Map map = jsonDecode(response.body); // do not worry about statuscode, trying to keep it minimal
someVariable = map['firstName'];
return 'This is the first name : $someVariable';
}
}
Now look at main:
void main(){
String someFunction() async {
SomeClass instance = SomeClass(); // creating object
String firstNameDeclaration = await instance.getData().then((value) => value);
return firstNameDeclaration;
}
}
When working with Future, like in the case of firstNameDeclaration why do I have to use .then() method to access the string object, since I am waiting for the function to finish?
When searching on the web, some people use .then() others don't, I am confused.
Kindly help me have a clearer understanding of how Futures and async functions overall work.
Background
Asynchronous operations let your program complete work while waiting for another operation to finish. Here are some common asynchronous operations:
Fetching data over a network.
Writing to a database.
Reading data from a file.
To perform asynchronous operations in Dart, you can use the Future class and the async and await keywords.
When an async function invokes "await", it is converted into a Future, and placed into the execution queue. When the awaited future is complete, the calling function is marked as ready for execution and it will be resumed at some later point. The important difference is that no Threads need to be paused in this model.
Futures vs async-await
When an async function invokes "await", it is converted into a Future, and placed into the execution queue. When the awaited future is complete, the calling function is marked as ready for execution and it will be resumed at some later point. The important difference is that no Threads need to be paused in this model.
async-await is just a a declarative way to define asynchronous functions and use their results into Future and it provides syntactic sugar that help you write clean code involving Futures.
Consider this dart code snipped involving Futures -
Future<String> getData(int number) {
return Future.delayed(Duration(seconds: 1), () {
return 'this is a future string $number.';
});
}
main(){
getData(10).then((data) => {
print(data)
});
}
As you can see when you use Futures, you can use then callback when the function return a future value. This is easy to manage if there is single "then" callback but the situation escalates quickly as soon as there are many nested "then" callbacks for example -
Future<String> getProductCostForUser() {
return getUser().then((user) => {
var uid = user.id;
return getOrder(uid).then((order) => {
var pid = order.productId;
return getProduct(pid).then((product) => {
return product.totalCost;
});
});
});
}
main(){
getProductCostForUser().then((cost) => {
print(cost);
});
}
As you can when there multiple chained "then" callback the code become very hard to read and manage. This problem is solved by "async-await". Above chained "then" callbacks can be simplified by using "async-await" like so -
Future<String> getProductCostForUser() async {
var user = await getUser();
var order = await getOrder(user.uid);
var product = await getProduct(order.productId);
return product.totalCost;
}
main() async {
var cost = await getProductCostForUser();
print(cost);
}
As you can above code is much more readable and easy to understand when there are chained "then" callbacks.
I hope this explains some basic concepts and understanding regarding the "async-await" and Futures.
You can further read about topic and examples here
Basically, you should either use await OR then(). However, Dart guidelines advocates that you should prefer use await over then() :
This code :
Future<int> countActivePlayers(String teamName) {
return downloadTeam(teamName).then((team) {
if (team == null) return Future.value(0);
return team.roster.then((players) {
return players.where((player) => player.isActive).length;
});
}).catchError((e) {
log.error(e);
return 0;
});
}
should be replaced by :
Future<int> countActivePlayers(String teamName) async {
try {
var team = await downloadTeam(teamName);
if (team == null) return 0;
var players = await team.roster;
return players.where((player) => player.isActive).length;
} catch (e) {
log.error(e);
return 0;
}
}
In your case, you should write :
void main(){
Future<String> someFunction() async {
SomeClass instance = SomeClass(); // creating object
String firstNameDeclaration = await instance.getData();
return firstNameDeclaration;
// Or directly : return await instance.getData();
// Or : return instance.getData();
}
}