error when trying to load data from json to DropDownButton Flutter dart

50 views Asked by At

I am trying to upload data from a json file to a DropDownButton and it gives me the following error When I run my application it gives me the following message: Exception has occurred. LateError (LateInitializationError: Field '_values@24401662' has not been initialized.)

I am new developing in flutter, I copy the codes I created:

deporte.json

[ { "nombre": "Fútbol", "icono": "sports_soccer" }, { "nombre": "Baloncesto", "icono": "sports_basketball" }, { "nombre": "Tenis", "icono": "sports_tennis" } ]

carga_deportes.dart


import 'package:flutter/material.dart';
import 'dart:convert';
import 'package:flutter/services.dart';

class Valor {
  final String nombre;
  final IconData icono;

  Valor({required this.nombre, required this.icono});

  factory Valor.fromJson(Map<String, dynamic> json) {
    return Valor(
      nombre: json['nombre'] ?? '',
      icono: _getIconDataFromString(json['icono'] ?? ''),
    );
  }

  static IconData _getIconDataFromString(String iconoString) {
    switch (iconoString) {
      case 'arrow_back':
        return Icons.arrow_back;
      case 'search':
        return Icons.search;
      case 'add':
        return Icons.add;
      // Agrega más casos según los iconos que necesites manejar
      default:
        return Icons.error; // Icono por defecto en caso de que no se encuentre el icono
    }
  }
}

 // Importa la clase Valor

Future<List<Valor>> cargarValores() async {
  String data = await rootBundle.loadString('assets/deportes.json');
  List<dynamic> jsonList = json.decode(data);
  List<Valor> valores = [];
  for (var item in jsonList) {
    valores.add(Valor.fromJson(item));
  }
  return valores;
}

main.dart

import 'package:flutter/material.dart';
import 'package:sports_community/funciones/carga_deportes.dart';
// Importa la función cargarValores


void main() => runApp(const SelectorValores());


class SelectorValores extends StatefulWidget {
  const SelectorValores({super.key});

  @override
  // ignore: library_private_types_in_public_api
  _SelectorValoresState createState() => _SelectorValoresState();
}

class _SelectorValoresState extends State<SelectorValores> {
  late List<Valor> _valores;
  late Valor _valorSeleccionado = Valor(nombre: 'Selecciona un valor', icono: Icons.error); // Inicializa _valorSeleccionado

  @override
  void initState() {
    super.initState();
    cargarValores().then((listaValores) {
      setState(() {
        _valores = listaValores;
        _valorSeleccionado = _valores.first; // Puedes establecer el valor predeterminado aquí
      });
    });
  }

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(
        title: Text('Selecciona un Valor'),
      ),
      body: Center(
        child: DropdownButton<Valor>(
          value: _valorSeleccionado,
          onChanged: (Valor? newValue) {
            setState(() {
              _valorSeleccionado = newValue!;
            });
          },
          items: _valores.map<DropdownMenuItem<Valor>>((Valor value) {
            return DropdownMenuItem<Valor>(
              value: value,
              child: Row(
                children: [
                  Icon(value.icono),
                  SizedBox(width: 10),
                  Text(value.nombre),
                ],
              ),
            );
          }).toList(),
        ),
      ),
    );
  }
}

I would greatly appreciate your guidance so I can advance with my learning!

I hope you can guide me with a solution

3

There are 3 answers

0
Abdul Awal On BEST ANSWER

1. Change the name of your assets from

deporte.json 

to

deportes.json

this is giving you this error

[ERROR:flutter/runtime/dart_vm_initializer.cc(41)] Unhandled Exception: Unable to load asset: "assets/deportes.json".

2. From your project's root directory run.

flutter pub get

Then copy the whole code I am providing you and paste it inside your main.dart I fixed the code and its working without any errors

 import 'package:flutter/material.dart';
import 'dart:convert';
import 'package:flutter/services.dart';

void main() => runApp(const MaterialApp(
      home: SelectorValores(),
    ));

class SelectorValores extends StatefulWidget {
  const SelectorValores({super.key});

  @override
  // ignore: library_private_types_in_public_api
  _SelectorValoresState createState() => _SelectorValoresState();
}

class _SelectorValoresState extends State<SelectorValores> {
  List<Valor> _valores = [];
  Valor _valorSeleccionado = Valor(
      nombre: 'Selecciona un valor',
      icono: Icons.error); // Inicializa _valorSeleccionado

  @override
  void initState() {
    super.initState();
    initializingLateVariables();
  }

  void initializingLateVariables() async {
    _valores = await cargarValores();
    _valorSeleccionado = _valores.first;
    setState(() {});
  }

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(
        title: Text('Selecciona un Valor'),
      ),
      body: Center(
        child: DropdownButton<Valor>(
          value: _valorSeleccionado,
          onChanged: (Valor? newValue) {
            setState(() {
              _valorSeleccionado = newValue!;
            });
          },
          items: _valores.map<DropdownMenuItem<Valor>>((Valor value) {
            return DropdownMenuItem<Valor>(
              value: value,
              child: Row(
                children: [
                  Icon(value.icono),
                  SizedBox(width: 10),
                  Text(value.nombre),
                ],
              ),
            );
          }).toList(),
        ),
      ),
    );
  }
}

class Valor {
  final String nombre;
  final IconData icono;

  Valor({required this.nombre, required this.icono});

  factory Valor.fromJson(Map<String, dynamic> json) {
    return Valor(
      nombre: json['nombre'] ?? '',
      icono: _getIconDataFromString(json['icono'] ?? ''),
    );
  }

  static IconData _getIconDataFromString(String iconoString) {
    switch (iconoString) {
      case 'arrow_back':
        return Icons.arrow_back;
      case 'search':
        return Icons.search;
      case 'add':
        return Icons.add;
      // Agrega más casos según los iconos que necesites manejar
      default:
        return Icons
            .error; // Icono por defecto en caso de que no se encuentre el icono
    }
  }
}

// Importa la clase Valor

Future<List<Valor>> cargarValores() async {
  String data = await rootBundle.loadString('assets/deportes.json');

  List<dynamic> jsonList = json.decode(data);

  List<Valor> valores = [];
  for (var item in jsonList) {
    valores.add(Valor.fromJson(item));
  }
  return valores;
}

enter image description here

2
Md. Yeasin Sheikh On

Try using FutureBuilder to make sure data has been fetched properly.

class _SelectorValoresState extends State<SelectorValores> {
  late List<Valor> _valores;
  Valor? _valorSeleccionado; // Inicializa _valorSeleccionado

  late Future future = cargarValores();

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(
        title: Text('Selecciona un Valor'),
      ),
      body: Center(
        child: FutureBuilder(
          future: future,
          builder: (BuildContext context, AsyncSnapshot snapshot) {
            if (snapshot.connectionState == ConnectionState.waiting) {
              return CircularProgressIndicator();
            }
            if (snapshot.hasData) {
              _valores = snapshot.data;

              if (_valores.isNotEmpty) _valorSeleccionado = _valores.first;

              return DropdownButton<Valor>(
                value: _valorSeleccionado,
                onChanged: (Valor? newValue) {
                  setState(() {
                    _valorSeleccionado = newValue;
                  });
                },
                items: _valores.map<DropdownMenuItem<Valor>>((Valor value) {
                  return DropdownMenuItem<Valor>(
                    value: value,
                    child: Row(
                      children: [
                        Icon(value.icono),
                        SizedBox(width: 10),
                        Text(value.nombre),
                      ],
                    ),
                  );
                }).toList(),
              );
            }
            return Text('No hay valores, handle other state');
          },
        ),
      ),
    );
  }
}
0
Harmen On

This is a good demonstration of how (not) to handle asynchronous initialization of a widget and quite common for programmers new to Flutter / Dart / Async programming.

The items for the DropDownMenuItems are set to _valores. Since you set _valores after the async method cargarValores has finished, the first calls to build, _valores won't have any data. Since you specified late for _valores, it means it has to contain data the first time it's used.

But it only contains data after the async method is finished, which, by default, is only finished after all the sync methods are finished. Building is a sync method so for the first build dart will find a problem: late _valores is not initialized, but is still used, but you promised you would initialize it before use. So dart throws an error, rightfully so.

A simple solution is to initialize _valores like this (without the late): List _valores = []; as being an empty List. That's enough to fix the issue.

You can also use a FutureBuilder (more complex, more code, but also more options). You can also simply set a bool on initialization: loading=true, set it to false when the _valores is populated asynchronously and then call setState triggering a rebuild.

Than set the items of the dropdownbutton like this

items: loading? [] : the code to create the list of DropdownMenuItems that you already have.

This defines the widget tree as to contain an empty list of DropDownMenuItems as long as loading has the value true and contains a list of items when loading is set to false. When the async method is finished, _valores is populated, set loading to false, you calls setState again to redraw the widget tree with the updated _valores.