47

I am trying out things with Flutter/Dart right now. But my static variables keep getting reinitialised when accessed from another class.

I have a class, in its separate dart source file, holding the server status, declared as such:

class ServerStatus{
  static int newestBinary;
  static bool serverUp;
}

I initialised them @ main() by

ServerStatus.newestBinary = 20;
ServerStatus.serverUp = true;

. Afterwards, when I try to access them at another page in my application, the variables 'newestBinary' and 'serverUp' both became null, as if they are reinitalised. (If I declare them like static int newestBinary = 10;, then reassign ServerStatus.newestBinary = 20; at main(), it would still show up as 10 at another page in my application.

My application did not quit or stop between the two operations. Under what circumstances would static variables be reinitalised?

If I have to hold global and commonly used information for the application, what would be the best way to do it other than using static variables?

Thanks in advance.

Live0
  • 1,001
  • 1
  • 8
  • 8
  • 1
    That's quite unlikely. Can you please provide more code that shows how and where you initialize and read the values. The only idea is, that it might be caused by hot-reload (after code modifications). – Günter Zöchbauer Aug 19 '17 at 13:54
  • I was tidying up my code to upload here when I discovered what appears to be the reason behind: It's the import statements. I will add an answer below to detail what I mean. Thank you for your help! – Live0 Aug 20 '17 at 07:13
  • Definitely seems like an issue with the Dart SDK so I've filled an issue on GitHub: https://github.com/dart-lang/sdk/issues/32922 – Mark O'Sullivan Apr 18 '18 at 21:26

10 Answers10

40

I toyed around for an hour and realise what appears to be the reason. Apparently when I do:

import 'package:flutter_test_app/main.dart';

It is different from

import 'main.dart';

Even if both source files belong to the same package.

So in the end my test code looks like:

main.dart:

import 'package:flutter/material.dart';
import 'pageA.dart';
import 'pageB.dart';
import 'pageH.dart';

void main() {
  runApp(new MyApp());
}

class MyApp extends StatelessWidget {

  static bool testFlag = false;
  // This widget is the root of your application.
  @override
  Widget build(BuildContext context) {

    testFlag = true;
    ThemeData mainTheme = new ThemeData(
      primarySwatch: Colors.cyan,
    );
    print("testFlag @ MyApp: " + testFlag.toString());
    MaterialApp mainApp = new MaterialApp(
      title: 'Instabazaar',
      theme: mainTheme,
      home: new HomePage(title: 'Instabazaar'),
    );

    return mainApp;
  }
}

class HomePage extends StatefulWidget {

  final String title;
  HomePage({Key key, this.title}) : super(key: key);

  @override
  _HomePageState createState() {

    return new _HomePageState();
  }
}

class _HomePageState extends State<HomePage> {
  int _currentPageID = 0; // 0=home, 1=pageA, 2=pageB



  @override
  Widget build(BuildContext context) {

    print("testFlag @ HomePage: " + MyApp.testFlag.toString());


    AppBar appBar = new AppBar(
        title: new Text("TestApp"),
        centerTitle: true,
    );

    BottomNavigationBar bottomNavigationBar = new BottomNavigationBar(
        type: BottomNavigationBarType.shifting,
        items: <BottomNavigationBarItem>[
          new BottomNavigationBarItem(icon: new Icon(Icons.home), title: new Text('Home'), backgroundColor: Theme.of(context).accentColor),
          new BottomNavigationBarItem(icon: new Icon(Icons.explore), title: new Text('PageA'), backgroundColor: Colors.purple),
          new BottomNavigationBarItem(icon: new Icon(Icons.star), title: new Text('PageB'), backgroundColor: Colors.redAccent),
        ],
        onTap: (i) => setState( () => _currentPageID = i ),
        currentIndex: _currentPageID
    );


    Scaffold mainScaffold = new Scaffold(
      appBar: appBar,
      body: _getNewSubPage(),
      bottomNavigationBar: bottomNavigationBar,
    );
    return mainScaffold;
  }


  //MARK: navigation


  Widget _getNewSubPage(){
    switch (_currentPageID)
    {
      case 1:
        return new pageA();
      case 2:
        return new pageB();
      default:
        return new pageH();
    }
  }


}

pageA.dart / pageB.dart:

import 'package:flutter/material.dart';
import 'package:flutter_test_app/main.dart';

class pageA extends StatefulWidget{
  pageAState createState() => new pageAState();
}


class pageAState extends State<pageA> {

  @override
  Widget build(BuildContext context) {
    print("testFlag @ pageA: " + MyApp.testFlag.toString());
    return new Container();
  }
}

pageH.dart:

import 'package:flutter/material.dart';
import 'main.dart';

class pageH extends StatefulWidget{
  pageHState createState() => new pageHState();
}


class pageHState extends State<pageH> {
  @override
  Widget build(BuildContext context) {
    print("testFlag @ pageH: " + MyApp.testFlag.toString());
    return new Container();
  }
}

The only difference is the import statement. However, for pageA/pageB, the print statement would give "false". As for pageH, the print statement would give "true". I have switched around the import statements and it checks out. I am not familiar with how dart actually interprets the code, so I am not sure if it is a dart thing, a setup thing or a flutter thing. I will continue investigating but for now my problem is solved.

Thanks for everyone's help.

Live0
  • 1,001
  • 1
  • 8
  • 8
  • Interesting. Sounds like a bug to me. Dart should'nt treat it as different. It should be able to properly canonicalize. – Günter Zöchbauer Aug 20 '17 at 08:17
  • This was exacctly what was happening to me. THank you so much for your answer!! – Daniel Oliveira Oct 19 '20 at 17:46
  • For me problem was related to import statements too, but i didi mistake during refactoring - i have renamed some folders ("Entities" to "entities") using refactoring Android Studio, as result some import statements wasn't renamed, so i got null instead of value which must be written by login method to global variable. In my case i had entities folder which contains class type of my global variable, but in place where i'm using it i had import on "Entities" folder which was my mistake, but there wasn't build errors. – Artemy Jul 26 '21 at 19:36
  • i still have this in pure dart 2.16 – Amine Da. Apr 26 '22 at 17:41
  • It still exists in dart 2.17. – Hamed Jafarzadeh Oct 06 '22 at 16:21
20

It seems that Flutter and Dart have issue to find the same instance for static (global) variable if the import starts with: "package:your_app_package/file.dart".

So let say you want to have static variable (myStaticVariable) in your main.dart file, where you have MyApp class. And let say that you want to get that static variable in some different .dart file in your project, by calling it with MyApp.myStaticVariable.

In that case if you import main.dart with "import package:your_app_package/main.dart" the variable will have "null" value , even if it has been initialised before!

If you import main.dart with just "import main.dart" (if the files are in the same directory) or "import ../main.dart" (if your file is one directory dipper then main.dart), you will get the right value for MyApp.myStaticVariable.

I am not sure why is that, but maybe like @Kevin Moore mentioned, there is an issue and Flutter team needs to resolve it.

Stoycho Andreev
  • 6,163
  • 1
  • 25
  • 27
15
class Glob {
  //One instance, needs factory 
  static Glob _instance;
  factory Glob() => _instance ??= new Glob._();
  Glob._();
  //

  String account ='johanacct1';

  String getServerUrl(){
    return 'http://192.168.1.60';
  }

  String getAccountUrl(){
    return getServerUrl()+'/accounts/'+account;
  }
}

Use it in another file:

`

Glob().getAccountUrl(); //http://192.168.1.60/accounts/johanacct1
Glob().account = 'philip.k.dick';
Glob().getAccountUrl(); //http://192.168.1.60/accounts/philip.k.dick

` It works with import 'glob.dart'; when both files are in the lib/ directory. (IDK if there are problems in other scenarios.)

Johan vdH
  • 364
  • 4
  • 11
  • Johan, I just want to point out that is probably the best practice answer that works out the best for all use cases. good job writing up this answer. – Fred Grott Jul 03 '19 at 11:56
7

It's a known issue that the entry-point file (lib/main.dart) must not contain relative imports.

If all imports start with

import 'dart:...';
import 'package:my_project/...'

then this problem can be avoided.

This is because Flutter doesn't fully follow the pub package convention where entry-point files are outside lib/ (like bin/, web/, tool/, test/, or example/).

See also https://github.com/flutter/flutter/issues/15748

Update 2018-10-17

This issue is fixed in Dart but might not have landed yet in all Flutter channels.

Günter Zöchbauer
  • 623,577
  • 216
  • 2,003
  • 1,567
6

You can initialize static variable directly into the declaration. Something like this would be better :

class ServerStatus{
  static int newestBinary = 20;
  static bool serverUp = false;
}

Also, are you sure your assignation is correctly executed, and before anything else ? Without more code it would be quite hard to give a full answer.

Another reason may be about how you do your assignation. Are you doing newestBinary = 20; or ServerStatus.newestBinary = 20; ? Static variables are different then globals. If you do newestBinary = 20; you won't change the static variable of ServerStatus, but a local variable instead.

Rémi Rousselet
  • 256,336
  • 79
  • 519
  • 432
  • Yea i am doing ServerStatus.newestBinary = 20; Sorry about the confusion. I can't initialise right at declaration as the variables actually hold information that I pull from the server. I think it has something to do with the app lifecycle more than the code tbh, as I am just doing what I do all the time with all other languages (and even Dart without Flutter). It works normally. – Live0 Aug 20 '17 at 06:40
2

I also encountered this problem but my cause was little strange so I thought that I will post it here. In my flutter project I have 3 classes in separate files, lets say main.dart, helper.dart and user.dart. main and helper both use a static field form user. But even though I used 'import package:...' everywhere the problem was when reading that field from main and helper the values were different. After changing it from one class the change wasn't present when reading from the other. In my case, after hours of searching for the answer to this behavior I found that in main.dart my import looks like this

import 'package:my_app/user.dart';

and in helper.dart it is

import 'package:my_app/User.dart';

Changing the latter to lowercase 'user.dart' fixed the problem.

I still don't know why my imports were different (I almost always use auto-generated imports) and also expected dart/flutter import to be case sensitive when it really isn't which I find strange. And what is even weirder to me is that dart/flutter treats those imports as separate files/libs what caused the problem.

Shin Q'dan
  • 81
  • 1
  • 7
0

In my app I had to read content from an xml source and use it in many parts of the app. I wanted to load the content once, when the app started.

A static object of the class BrandsCollection was the perfect solution but from some classes it was null. I tried to change the path as the other users wrote but it still didn't work everywhere so I used this simple solution:

My Main.dart:

class GlobalData {
  //this is what I need to have in many parts of my app
  static BrandsCollection brandsCollection;
}

void main() {
  XmlDataReader dataReader = new XmlDataReader();

  dataReader.loadContent().then((content) {
      //After reading the xml file, I create the instance
      GlobalData.brandsCollection = new BrandsCollection(content);

      //I run the app only when the object is initialized
      runApp(new MyApp());
  });
}

In the classes where it finds the instance you can just use:

import '../main.dart';
class A {
    A(){
        BrandsCollection _brandsCollection = GlobalData.brandsCollection;

        _brandsCollection.foo();
    }
}

Where it somehow doesn't find the instance:

I created the same static variable:

import '../main.dart';
class B {
    //This is gonna be the copy of the value
    static BrandsCollection brandsCollection;

    B(){
        brandsCollection.foo();
    }
}

and I set the link between the 2 static variables in the main:

void main() {
  XmlDataReader dataReader = new XmlDataReader();

  dataReader.loadContent().then((content) {
      GlobalData.brandsCollection = new BrandsCollection(content);

      //This is the link
      B.brandsCollection = GlobalData.brandsCollection;

      runApp(new MyApp());
  });
}

Of course you have to create the link before the class B call.

Leonardo Rignanese
  • 865
  • 11
  • 22
0

The following code caused

[ERROR:flutter/lib/ui/ui_dart_state.cc(148)] Unhandled Exception: Reading static variable '_this@69070523' during its initialization

// Singleton Pattern
factory KeyValueService() => _this;
static final KeyValueService _this = new KeyValueService._internal();
KeyValueService._internal();

// Exception .. accessing '_this' during initialization
KeyJsonValue _accountAdapter = new KeyJsonValue(_this, accountKey);

The problem is some kind of 'race condition': While KeyValueService._internal() executes, accountAdapter is also initialized which itself refers to the yet uninitialized _this.

Using a function (=>) instead of a property (=) solved the problem, because new KeyJsonValue(this, …) is evaluated every time when the function is called and not only once and during initialization.

KeyJsonValue get _accountAdapter => new KeyJsonValue(this, accountKey);
Markus Schmidt
  • 442
  • 5
  • 4
0

In my case, the issue come from the way I imported the file which contains the static property (import 'package:app/Shared//DIProvider.dart';). I should not have double /

Cuong Nguyen
  • 376
  • 1
  • 5
  • 8
-1

This issue is for imports.

  • clean the project

  • get pub again

  • reAddress imports if need

    you note to the caps letters in the imports

    import 'package:app/AA.dart';

to

import 'package:app/aa.dart';
Ali Bagheri
  • 3,068
  • 27
  • 28