Home > front end >  Flutter Error : Unhandled Exception: RangeError (index): Invalid value: Valid value range is empty:
Flutter Error : Unhandled Exception: RangeError (index): Invalid value: Valid value range is empty:

Time:12-07

I am trying to fetch data from an API, and although data is fetched for a hardcoded request, I am unable to get it dynamically.

It gives the following error:

E/flutter (11813): [ERROR:flutter/runtime/dart_vm_initializer.cc(41)] Unhandled Exception: RangeError (index): Invalid value: Valid value range is empty: 0
E/flutter (11813): #0      List.[] (dart:core-patch/growable_array.dart:264:36)
E/flutter (11813): #1      new Album.fromJson (package:todoapp/models/apihandler.dart:34:32)
E/flutter (11813): #2      fetchAlbum (package:todoapp/models/apihandler.dart:13:18)
E/flutter (11813): <asynchronous suspension>

My home screen code is :

import 'package:flutter/material.dart';
import 'package:todoapp/constants.dart';
import 'package:todoapp/models/apihandler.dart';

class NutrientFact extends StatefulWidget {
  @override
  State<NutrientFact> createState() => _NutrientFactState();
}

class _NutrientFactState extends State<NutrientFact> {
  @override
  late String inputdata;

  // late Future<Album> futureAlbum;
  late double proteins = 0;

  late double carbs = 0;

  late double fibers = 0;

  late double fats = 0;

  @override
  Widget build(BuildContext context) {
    return Scaffold(
        appBar: AppBar(
          title: const Center(
              child: Text(
            'SelfTime',
            style: TextStyle(fontWeight: FontWeight.bold),
          )),
        ),
        body: Column(
          crossAxisAlignment: CrossAxisAlignment.stretch,
          mainAxisAlignment: MainAxisAlignment.spaceBetween,
          children: [
            Expanded(
                child: TextField(
              textAlign: TextAlign.center,
              onChanged: (value) {
                inputdata = value;
              },
            )),
            FactEntry("Protein", proteins.toString()),
            FactEntry("Carbohydrates", carbs.toString()),
            FactEntry("Fats", fats.toString()),
            FactEntry("Fiber", fibers.toString()),
            GestureDetector(
              child: Container(
                alignment: Alignment.center,
                margin: EdgeInsets.only(top: 10),
                color: kBottomButtonColor,
                width: double.infinity,
                height: kBottomButtonHeight,
                child: Text(
                  "SEARCH",
                  style: TextStyle(fontWeight: FontWeight.w600, fontSize: 30),
                ),
              ),
              onTap: () {
                setState(() {
                  FutureBuilder<Album>(
                    future: fetchAlbum(ing: inputdata),
                    builder: (context, snapshot) {
                      if (snapshot.hasData) {
                        proteins = snapshot.data!.protein;
                        carbs = snapshot.data!.carb;
                        fats = snapshot.data!.fat;
                        fibers = snapshot.data!.fiber;
                        print(proteins);
                      } else if (snapshot.hasError) {
                        return Text('-----${snapshot.error}');
                      }
                      return CircularProgressIndicator();
                    },
                  );
                });
              },
            ),
          ],
        ));
  }
}

class FactEntry extends StatelessWidget {
  FactEntry(this.label, this.data);
  late String label;
  late String data;

  @override
  Widget build(BuildContext context) {
    return Expanded(
      child: Padding(
        padding: EdgeInsets.symmetric(horizontal: 15, vertical: 20),
        child: Container(
          decoration: kContainerDecoration,
          child: Row(
            crossAxisAlignment: CrossAxisAlignment.stretch,
            mainAxisAlignment: MainAxisAlignment.spaceEvenly,
            children: [
              Center(
                  child: Text(
                label,
                style: TextStyle(fontSize: 20),
              )),
              Center(child: Text(data, style: TextStyle(fontSize: 20)))
            ],
          ),
        ),
      ),
    );
  }
}

And API handler is:

import 'dart:convert';
import 'package:http/http.dart' as http;

Future<Album> fetchAlbum({String ing = 'chicken'}) async {
  final response = await http.get(
    Uri.parse(
        "https://api.edamam.com//api//food-database//v2//parser?app_id=$api_id&app_key=$api_key&ingr=ing&nutrition-type=cooking"),
  );

  if (response.statusCode == 200) {
    return Album.fromJson(jsonDecode(response.body));
  } else {
    throw Exception('Failed to load album');
  }
}

class Album {
  final double carb;
  final double protein;
  final double fat;
  final double fiber;

  const Album({
    required this.carb,
    required this.protein,
    required this.fat,
    required this.fiber,
  });

  factory Album.fromJson(Map<String, dynamic> json) {
    return Album(
        protein: json["parsed"][0]["food"]["nutrients"]["PROCNT"],
        fat: json["parsed"][0]["food"]["nutrients"]["FAT"],
        fiber: json["parsed"][0]["food"]["nutrients"]["CHOCDF"],
        carb: json["parsed"][0]["food"]["nutrients"]["FIBTG"]);
  }
}

`

CodePudding user response:

Its seems your json["parsed"] is empty, but you try to call first item in that, to avoid this happen you can parse your list dynamically like this:

factory Album.fromJson(Map<String, dynamic> json) {
    return Album(
        protein: json["food"]["nutrients"]["PROCNT"],
        fat: json["food"]["nutrients"]["FAT"],
        fiber: json["food"]["nutrients"]["CHOCDF"],
        carb: json["food"]["nutrients"]["FIBTG"]);
  }

then change fetchAlbum to this:

Future<List<Album>> fetchAlbum({String ing = 'chicken'}) async {
  final response = await http.get(
    Uri.parse(
        "https://api.edamam.com//api//food-database//v2//parser?app_id=$api_id&app_key=$api_key&ingr=ing&nutrition-type=cooking"),
  );

  if (response.statusCode == 200) {
    
    return (jsonDecode(response.body)["parsed"] as List).map((e) => Album.fromJson(e)).toList();
  } else {
    throw Exception('Failed to load album');
  }
}

also you need to change your FutureBuilder to this:

FutureBuilder<List<Album>>(
    future: fetchAlbum(ing: inputdata),
    builder: (context, snapshot) {
      switch (snapshot.connectionState) {
        case ConnectionState.waiting:
          return CircularProgressIndicator();
        default:
          if (snapshot.hasError) {
            return Text('Error: ${snapshot.error}');
          } else {
            List<Album> data = snapshot.data ?? [];

            return ListView.builder(
              itemBuilder: (context, index) {
                proteins = data[index].protein;
                carbs = data[index].carb;
                fats = data[index].fat;
                fibers = data[index].fiber;

                print(proteins);
                return Column(children: [
                  Text('${data[index].protein ?? ""}'),
                ]);
              },
              itemCount: data.length,
            );
          }
      }
    },
  ),
  • Related