How to check 'late' variable is initialized in Dart
Asked Answered
A

8

109

In kotlin we can check if the 'late' type variables are initialized like below

lateinit var file: File    
if (this::file.isInitialized) { ... }

Is it possible to do something similar to this in Dart..?

Abram answered 21/4, 2021 at 14:29 Comment(2)
what is the purpose of this check?Continuate
If you want something like this you should just be using a nullable variable.Tactless
P
126

Unfortunately this is not possible.

From the docs:

AVOID late variables if you need to check whether they are initialized.

Dart offers no way to tell if a late variable has been initialized or assigned to. If you access it, it either immediately runs the initializer (if it has one) or throws an exception. Sometimes you have some state that’s lazily initialized where late might be a good fit, but you also need to be able to tell if the initialization has happened yet.

Although you could detect initialization by storing the state in a late variable and having a separate boolean field that tracks whether the variable has been set, that’s redundant because Dart internally maintains the initialized status of the late variable. Instead, it’s usually clearer to make the variable non-late and nullable. Then you can see if the variable has been initialized by checking for null.

Of course, if null is a valid initialized value for the variable, then it probably does make sense to have a separate boolean field.

https://dart.dev/guides/language/effective-dart/usage#avoid-late-variables-if-you-need-to-check-whether-they-are-initialized

Prolepsis answered 21/4, 2021 at 14:46 Comment(3)
I think you should emphasize the solution shown in this quote. I would recommend adding at the end or bolding to use a non-late and nullable variable.Tactless
For the discussion by the Dart team on the choice not to allow (at least for now) to check if a late variable has been initialized, see the interesting issue #324 - Should we provide a way to query the status of late variables?Ty
Oh what a bloody mess, Can't declare variables getting data from internet as late final and filtering makes it a mess of original data loss.Screamer
S
29

Some tips I came up with from advice of different dart maintainers, and my self-analysis:

late usage tips:

  • Do not use late modifier on variables if you are going to check them for initialization later.
  • Do not use late modifier for public-facing variables, only for private variables (prefixed with _). Responsibility of initialization should not be delegated to API users. EDIT: as Irhn mentioned, this rule makes sense for late final variables only with no initializer expression, they should not be public. Otherwise there are valid use cases for exposing late variables. Please see his descriptive comment!
  • Do make sure to initialize late variables in all constructors, exiting and emerging ones.
  • Do be cautious when initializing a late variable inside unreachable code scenarios. Examples:
    • late variable initialized in if clause but there's no initialization in else, and vice-versa.
    • Some control-flow short-circuit/early-exit preventing execution to reach the line where late variable is initialized.

Please point out any errors/additions to this.

Enjoy!

Sources:

Shortcoming answered 22/10, 2021 at 21:53 Comment(0)
E
9

You can create a Late class and use extensions like below:

import 'dart:async';
import 'package:flutter/foundation.dart';

class Late<T> {
  ValueNotifier<bool> _initialization = ValueNotifier(false);
  late T _val;
  
  Late([T? value]) {
    if (value != null) {
      this.val = value;
    }
  }

  get isInitialized {
    return _initialization.value;
  }

  T get val => _val;

  set val(T val) => this
    .._initialization.value = true
    .._val = val;
}

extension LateExtension<T> on T {
  Late<T> get late => Late<T>();
}

extension ExtLate on Late {
  Future<bool> get wait {
    Completer<bool> completer = Completer();
    this._initialization.addListener(() async {
      completer.complete(this._initialization.value);
    });

    return completer.future;
  }
}

Create late variables with isInitialized property:

var lateString = "".late;
var lateInt = 0.late;
//or
Late<String> typedLateString = Late();
Late<int> typedLateInt = Late();

and use like this:

print(lateString.isInitialized)
print(lateString.val)
lateString.val = "initializing here";

Even you can wait for initialization with this class:

Late<String> lateVariable = Late();

lateTest() async {
  if(!lateVariable.isInitialized) {
    await lateVariable.wait;
  }
  //use lateVariable here, after initialization.
}
Excruciation answered 24/6, 2021 at 13:4 Comment(0)
W
7

Someone may kill you if they encounter it down the road, but you can wrap it in a try/catch/finally to do the detection. I like it better than a separate boolean.

We have an instance where a widget is disposed if it fails to load and contains a late controller that populates on load. The dispose fails as the controller is null, but this is the only case where the controller can be null. We wrapped the dispose in a try catch to handle this case.

Whipple answered 10/6, 2021 at 22:1 Comment(1)
to make it appear cleaner, I have created a separate method for it.... Methods appear beautifulInhabitant
L
7

Use nullable instead of late:

File? file;
File myFile;

if (file == null) {
    file = File();
}

myFile = file!;

Note the exclamation mark in myFile = file!; This converts File? to File.

Litre answered 21/11, 2022 at 13:42 Comment(0)
I
2

I'm using boolean variable when I initiliaze late varible. My case is : I'm using audio player and I need streams in one dart file.

I'm sharing my code block this methodology easily implement with global boolean variables to projects. My problem was the exception i got from dispose method when user open and close the page quickly

late initializations

Boolean value assign

If check for dispose method

Inconsolable answered 17/5, 2022 at 14:36 Comment(1)
Why not just make them nullable and use null aware operators? durationSubscription?.cancel();Kept
T
0

Dart says:

AVOID late variables if you need to check whether they are initialized

Read about it here -> https://dart.dev/effective-dart/usage#avoid-late-variables-if-you-need-to-check-whether-they-are-initialized

Turgite answered 25/8, 2023 at 14:50 Comment(0)
F
-1

you can use try-catch.

import 'dart:io';

void main() {
  example(false);
}

void example(bool initialize) {
  late final File file;
  bool check = false;

  if (initialize) {
    file = File("test.txt");
  }

  try {
    // ignore: unnecessary_type_check
    check = file is File;
  } catch (e) {
    // print(e);
  }

  if (check) {
    print("file is defined");
  } else {
    print("file is not defined");
  }
}
Fideicommissum answered 15/6, 2023 at 18:43 Comment(1)
This does not provide an answer to the question. Once you have sufficient reputation you will be able to comment on any post; instead, provide answers that don't require clarification from the asker. - From ReviewCacophonous

© 2022 - 2024 — McMap. All rights reserved.