Future of Flutter

Common mistake when working with Future

This post will demonstrate common mistake when working with Future in Flutter.

Assumption: #

  • You already know about Flutter
  • You already know how to use Bloc and Cubit
  • You know a little bit of Java

Let’s say that we going to have several Future that we want to combine their values. The code will be like this, and we have 3 of them.

Future<int> _progress1() async {
  var step = 0;
  while (state.progress1 <= 1.0) {
    await Future.delayed(Duration(milliseconds: 10));
    step++;
    emit(state.copyWith(progress1: state.progress1 + 0.01));
  }
  return step;
}

Await #

Most people will use await to get the result of the process:

serial() async {
  emit(ThreadState());
  final step1 = await _progress1();
  final step2 = await _progress2();
  final step3 = await _progress3();

  emit(state.copyWith(totalStep: step1 + step2 + step3));
}

The code above will get the data in serial, which means it will wait the process 1 to be finished, then working on process 2, and finally process 3.

The problem is await syntax, this syntax means to wait the Future to be finished before executing the next statement.

Functional? #

Adding functional pattern doesn’t help if we doing it wrong, like the example below:

wrong() async {
  emit(ThreadState());

  var step1 = 0;
  var step2 = 0;
  var step3 = 0;

  _progress1().then((value) => step1 = value);
  _progress2().then((value) => step2 = value);
  _progress3().then((value) => step3 = value);

  emit(state.copyWith(totalStep: step1 + step2 + step3));
}

That code? won’t work. That is a beginner mistakes, the code seems okay, right? You have variables, the variables are getting assigned, then you put it in a state.

Imagine this:

  • I have step1 notes here, currently it is 0.
  • Hey process1, please do your work, then update the notes when you finished.
  • I will update the notes to the report.

process1: Hey, I didn’t finished yet!

Yes, that’s what happened. The main process update all the variable to the state before all the sub-process finished.

Imperative Fix #

To fix the issue, actually can be done with processing the Futures one by one, and wait for each of them.

This is the imperative way to do so.

alternative() async {
  emit(ThreadState());

  final step1 = _progress1();
  final step2 = _progress2();
  final step3 = _progress3();

  final result1 = await step1;
  final result2 = await step2;
  final result3 = await step3;

  emit(state.copyWith(totalStep: result1 + result2 + result3));
}

calling the process1, process2, and process3, will store the Future object into step variables. This also will execute the Future, as long as we have the Future object, we can request for the result.

Functional Fix #

The code below also can work, but it comes with more functional way.

functional() async {
  emit(ThreadState());

  Future.wait<int>([
    _progress1(),
    _progress2(),
    _progress3(),
  ]).then(
    (steps) => emit(
      state.copyWith(
        totalStep: steps.reduce((sum, value) => sum + value),
      ),
    ),
  );
}

This code is calling all the process, and stores them into a list to be processed in the next statement.

Since the result are same type, we can apply functional method reduce to sum all the result.

Conclusion #

Be findful with how to process the Future in Flutter. Handling Future incorrectly might hurt the performance of the application.