Flutter/Dart Variables estáticas perdidas/se siguen reiniciando

11 minutos de lectura

Avatar de usuario de Live0
en vivo0

Estoy probando cosas con Flutter/Dart ahora mismo. Pero mis variables estáticas se siguen reiniciando cuando se accede desde otra clase.

Tengo una clase, en su archivo fuente dart separado, que contiene el estado del servidor, declarado como tal:

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

Los inicialicé @ main() por

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

. Luego, cuando trato de acceder a ellos en otra página de mi aplicación, las variables ‘newestBinary‘ y ‘serverUpAmbos se convirtieron null, como si estuvieran reiniciados. (Si los declaro como static int newestBinary = 10;luego reasignar ServerStatus.newestBinary = 20; en main()aún aparecería como 10 en otra página de mi aplicación.

Mi aplicación no se cerró ni se detuvo entre las dos operaciones. ¿Bajo qué circunstancias se reinicializarían las variables estáticas?

Si tengo que almacenar información global y de uso común para la aplicación, ¿cuál sería la mejor manera de hacerlo además de usar variables estáticas?

Gracias de antemano.

  • Eso es bastante improbable. ¿Puede proporcionar más código que muestre cómo y dónde inicializa y lee los valores? La única idea es que podría deberse a una recarga en caliente (después de las modificaciones del código).

    – Günter Zöchbauer

    19 de agosto de 2017 a las 13:54

  • Estaba arreglando mi código para subirlo aquí cuando descubrí lo que parece ser el motivo: son las declaraciones de importación. Agregaré una respuesta a continuación para detallar lo que quiero decir. ¡Gracias por su ayuda!

    – En vivo0

    20 de agosto de 2017 a las 7:13

  • Definitivamente parece ser un problema con Dart SDK, así que resolví un problema en GitHub: github.com/dart-lang/sdk/issues/32922

    – Mark O’Sullivan

    18 de abril de 2018 a las 21:26

Jugué durante una hora y me di cuenta de cuál parece ser la razón. Aparentemente cuando hago:

import 'package:flutter_test_app/main.dart';

es diferente de

import 'main.dart';

Incluso si ambos archivos de origen pertenecen al mismo paquete.

Entonces, al final, mi código de prueba se ve así:

dardo principal:

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();
    }
  }


}

páginaA.dart / páginaB.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();
  }
}

páginaH.dardo:

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();
  }
}

La única diferencia es la declaración de importación. Sin embargo, para la página A/página B, la declaración de impresión daría “falso”. En cuanto a la página H, la declaración de impresión daría “verdadero”. He cambiado las declaraciones de importación y se comprueba. No estoy familiarizado con la forma en que Dart realmente interpreta el código, por lo que no estoy seguro de si es una cosa de Dart, una cosa de configuración o una cosa de aleteo. Seguiré investigando pero por ahora mi problema está solucionado.

Gracias por la ayuda de todos.

  • Interesante. Suena como un error para mí. Dart no debería tratarlo como diferente. Debería poder canonicalizar correctamente.

    – Günter Zöchbauer

    20 de agosto de 2017 a las 8:17

  • Esto era exactamente lo que me estaba pasando. ¡¡Muchas gracias por tu respuesta!!

    –Daniel Oliveira

    19 oct 2020 a las 17:46

  • Para mí, el problema también estaba relacionado con las declaraciones de importación, pero cometí un error durante la refactorización: cambié el nombre de algunas carpetas (“Entidades” a “entidades”) usando la refactorización de Android Studio, como resultado, no se cambió el nombre de algunas declaraciones de importación, por lo que obtuve nulo en lugar del valor que debe escribirse mediante el método de inicio de sesión en la variable global. En mi caso, tenía una carpeta de entidades que contiene el tipo de clase de mi variable global, pero en el lugar donde la estoy usando, importé en la carpeta “Entidades”, que fue mi error, pero no hubo errores de compilación.

    – Artemia

    26 de julio de 2021 a las 19:36

  • Todavía tengo esto en pure dart 2.16

    – Amina Da.

    26 de abril de 2022 a las 17:41

  • Todavía existe en dart 2.17.

    – Hamed Yafarzadeh

    6 oct 2022 a las 16:21

Parece que Flutter y Dart tienen problemas para encontrar la misma instancia para la variable estática (global) si la importación comienza con: “package:your_app_package/file.dart”.

Entonces, digamos que desea tener una variable estática (myStaticVariable) en su archivo main.dart, donde tiene la clase MyApp. Y supongamos que desea obtener esa variable estática en algún archivo .dart diferente en su proyecto, llamándolo con MyApp.myStaticVariable.

En ese caso, si importa main.dart con “import package:your_app_package/main.dart”, la variable tendrá un valor “nulo”, incluso si se ha inicializado antes.

Si importa main.dart con solo “importar main.dart” (si los archivos están en el mismo directorio) o “importar ../main.dart” (si su archivo es un dipper de directorio entonces main.dart), obtenga el valor correcto para MyApp.myStaticVariable.

No estoy seguro de por qué, pero tal vez, como mencionó @Kevin Moore, hay un problema y el equipo de Flutter necesita resolverlo.

  • Eso es solo un problema en Flutter, tal vez porque el punto de entrada está en lib mientras que de lo contrario está afuera lib (bin, web…)

    – Günter Zöchbauer

    20 de diciembre de 2017 a las 8:09

  • tengo este problema en solo dardo

    – Amina Da.

    26 de abril de 2022 a las 17:43

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;
  }
}

Úselo en otro archivo:

`

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

` Funciona con import 'glob.dart'; cuando ambos archivos están en el directorio lib/. (No sé si hay problemas en otros escenarios).

  • Johan, solo quiero señalar que es probablemente la respuesta de mejores prácticas que funciona mejor para todos los casos de uso. buen trabajo escribiendo esta respuesta.

    – Fred Grott

    3 de julio de 2019 a las 11:56

Avatar de usuario de Günter Zöchbauer
Günter Zöchbauer

Es un problema conocido que el archivo de punto de entrada (lib/main.dart) no debe contener importaciones relativas.

Si todas las importaciones comienzan con

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

entonces este problema se puede evitar.

Esto se debe a que Flutter no sigue completamente la convención del paquete de publicación donde los archivos de punto de entrada están fuera lib/ (como bin/, web/, tool/, test/o example/).

Ver también https://github.com/flutter/flutter/issues/15748

Actualización 2018-10-17

Este problema se solucionó en Dart, pero es posible que aún no se haya presentado en todos los canales de Flutter.

Puede inicializar la variable estática directamente en la declaración. Algo como esto sería mejor:

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

Además, ¿está seguro de que su asignación se ejecutó correctamente y antes que nada? Sin más código, sería bastante difícil dar una respuesta completa.

Otra razón puede ser sobre cómo haces tu asignación. Estás haciendo newestBinary = 20; o ServerStatus.newestBinary = 20; ? Las variables estáticas son diferentes a las globales. Si lo haces newestBinary = 20; no cambiará la variable estática de ServerStatus, sino una variable local en su lugar.

  • Sí, estoy haciendo ServerStatus.newestBinary = 20; Perdón por la confusión. No puedo inicializar directamente en la declaración ya que las variables en realidad contienen información que extraigo del servidor. Creo que tiene algo que ver con el ciclo de vida de la aplicación más que con el código tbh, ya que solo hago lo que hago todo el tiempo con todos los demás idiomas (e incluso Dart sin Flutter). Funciona normalmente.

    – En vivo0

    20 de agosto de 2017 a las 6:40


Avatar de usuario de Shin Q'dan
Shin Q’dan

También me encontré con este problema, pero mi causa era un poco extraña, así que pensé que lo publicaría aquí. En mi proyecto flutter tengo 3 clases en archivos separados, digamos main.dart, helper.dart y user.dart. main y helper usan un usuario de formulario de campo estático. Pero a pesar de que usé ‘importar paquete:…’ en todas partes, el problema fue que al leer ese campo de main y helper, los valores eran diferentes. Después de cambiarlo de una clase, el cambio no estaba presente al leer de la otra. En mi caso, después de horas de buscar la respuesta a este comportamiento, encontré que en main.dart mi importación se ve así

import 'package:my_app/user.dart';

y en helper.dart es

import 'package:my_app/User.dart';

Cambiar este último a minúsculas ‘user.dart’ solucionó el problema.

Todavía no sé por qué mis importaciones fueron diferentes (casi siempre uso importaciones generadas automáticamente) y también esperaba que la importación de dart/flutter distinguiera entre mayúsculas y minúsculas cuando realmente no lo es, lo que me parece extraño. Y lo que es aún más extraño para mí es que dart/flutter trata esas importaciones como archivos/libs separados que causaron el problema.

  • Sí, estoy haciendo ServerStatus.newestBinary = 20; Perdón por la confusión. No puedo inicializar directamente en la declaración ya que las variables en realidad contienen información que extraigo del servidor. Creo que tiene algo que ver con el ciclo de vida de la aplicación más que con el código tbh, ya que solo hago lo que hago todo el tiempo con todos los demás idiomas (e incluso Dart sin Flutter). Funciona normalmente.

    – En vivo0

    20 de agosto de 2017 a las 6:40


Avatar de usuario de Leonardo Rignanese
leonardo rignanese

En mi aplicación, tenía que leer contenido de una fuente xml y usarlo en muchas partes de la aplicación. Quería cargar el contenido una vez, cuando se inició la aplicación.

Un objeto estático de la clase. BrandsCollection fue la solución perfecta pero de algunas clases fue nula. Traté de cambiar la ruta como escribieron los otros usuarios, pero aún no funcionó en todas partes, así que usé esta solución simple:

Mi dardo principal:

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());
  });
}

En las clases donde encuentra la instancia, solo puede usar:

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

        _brandsCollection.foo();
    }
}

Donde de alguna manera no encuentra la instancia:

Creé la misma variable estática:

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

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

y configuré el enlace entre las 2 variables estáticas en la principal:

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());
  });
}

Por supuesto, debe crear el enlace antes de la llamada de clase B.

¿Ha sido útil esta solución?