简体   繁体   中英

Flutter: RangeError (index): Invalid value: Valid value range is empty: -1

I have 2 JSON file like this:

json1 ( API ):

[
  {
    "json1Language": "English",
    "json1Icon": "https://www.countryflags.io/gb/flat/64.png",
    "json1Code": "en"
  },
  {
    "json1Language": "French",
    "json1Icon": "https://www.countryflags.io/fr/flat/64.png",
    "json1Code": "fr"
  },
  {
    "json1Language": "Spanish",
    "json1Icon": "https://www.countryflags.io/es/flat/64.png",
    "json1Code": "es"
  }
]

json2 ( API ):

[
  {
    "json2Country": "Canada",
    "json2Continent": "North American",
    "json2Language": [
      "French",
      "English"
    ]
  },
  {
    "json2Country": "Mexico",
    "json2Continent": "North American",
    "json2Language": [
      "Spanish",
      "English"
    ]
  },
  {
    "json2Country": "United Kingdom",
    "json2Continent": "Europe",
    "json2Language": [
      "English"
    ]
  },
  {
    "json2Country": "France",
    "json2Continent": "Europe",
    "json2Language": [
      "French"
    ]
  },
  {
    "json2Country": "Spain",
    "json2Continent": "Europe",
    "json2Language": [
      "Spanish"
    ]
  }
]

I tried to show the data of json1Code from Json1 , it shows an error Flutter: RangeError (index): Invalid value: Valid value range is empty: -1 for a few seconds then shows the data correctly, I'm not sure where I did wrong在此处输入图片说明

I think maybe something wrong in the Build class:

════════ Exception caught by widgets library ═══════════════════════════════════════════════════════
The following RangeError was thrown building Builder(dirty):
RangeError (index): Invalid value: Valid value range is empty: -1

The relevant error-causing widget was: 
  Builder file:///D:/Flutter/Test/load_data/lib/json2_page3.dart:80:17
When the exception was thrown, this was the stack: 
#0      List.[] (dart:core-patch/growable_array.dart:177:60)
#1      _ShowContinentState.build.<anonymous closure> (package:load_data/json2_page3.dart:83:38)
#2      Builder.build (package:flutter/src/widgets/basic.dart:7183:48)
#3      StatelessElement.build (package:flutter/src/widgets/framework.dart:4644:28)
#4      ComponentElement.performRebuild (package:flutter/src/widgets/framework.dart:4570:15)

please help me, this is main file

import 'package:flutter/material.dart';
import 'package:load_data/model/json2_model.dart';
import 'package:load_data/service/json1_service.dart';
import 'package:load_data/service/json2_service.dart';
import 'model/json1_model.dart';

class Json2Page3 extends StatefulWidget {
  @override
  _Json2Page3State createState() => _Json2Page3State();
}

class _Json2Page3State extends State<Json2Page3> {
  List<Json2> json2 = [];
  List<String> _continent = [];
  @override
  void initState() {
    super.initState();
    setState(() {
      Json2Services.getData().then((data) {
        setState(() {
          json2 = data;
          _continent = json2.map<String>((x) => x.json2Continent).toSet().toList();
        });
      });
    });
  }

  @override
  Widget build(BuildContext context) {
    return DefaultTabController(
        length: _continent.length,
        child: Scaffold(
          appBar: AppBar(
            title: Text('Page 2'),
            bottom: TabBar(tabs: _continent.map((String name) => Tab(text: name)).toList()),
          ),
          body: TabBarView(
              children: _continent.map((String name) {
            return ShowContinent(
              json2: List<Json2>.from(json2)..retainWhere((e) => e.json2Continent == name),
            );
          }).toList()),
        ));
  }
}

class ShowContinent extends StatefulWidget {
  final List<Json2> json2;
  ShowContinent({this.json2});
  @override
  _ShowContinentState createState() => _ShowContinentState(json2);
}

class _ShowContinentState extends State<ShowContinent> {
  final List<Json2> json2;
  List<Json1> json1 = [];

  _ShowContinentState(this.json2);

  @override
  void initState() {
    super.initState();
    Json1Services.getData().then((data) {
      setState(() {
        json1 = data;
      });
    });
  }

  @override
  Widget build(BuildContext context) {
    return Column(
      children: [
        for (Json2 j2 in json2)
          Row(
            mainAxisAlignment: MainAxisAlignment.spaceAround,
            children: [
              Text(j2.json2Country.toUpperCase()),
              for (int i = 0; i < j2.json2Language.length; i++)
                Builder(
                  builder: (_) {
                    int index = json1.indexWhere((e) => e.json1Language == j2.json2Language[i]);
                    return Row(
                      children: [
                        Image.network(json1[index].json1Icon),
                        Text(json1[index].json1Code),
                      ],
                    );
                  },
                )
            ],
          ),
      ],
    );
  }
}


You can copy paste run full code below
You can use addPostFrameCallback and bool isLoading to check loading status
when isLoading == true , return CircularProgressIndicator()
code snippet

bool isLoading = true;

  @override
  void initState() {
    super.initState();
    WidgetsBinding.instance.addPostFrameCallback((_) {
      Json1Services.getData().then((data) {
        setState(() {
          json1 = data;
          isLoading = false;
        });
      });
    });
  }

  @override
  Widget build(BuildContext context) {
    return isLoading
        ? Center(child: CircularProgressIndicator())
        : Column(

working demo

在此处输入图片说明

full code

import 'package:flutter/material.dart';

import 'dart:convert';

List<Json2> json2FromJson(String str) =>
    List<Json2>.from(json.decode(str).map((x) => Json2.fromJson(x)));

String json2ToJson(List<Json2> data) =>
    json.encode(List<dynamic>.from(data.map((x) => x.toJson())));

List<Json1> json1FromJson(String str) =>
    List<Json1>.from(json.decode(str).map((x) => Json1.fromJson(x)));

String json1ToJson(List<Json1> data) =>
    json.encode(List<dynamic>.from(data.map((x) => x.toJson())));

class Json1 {
  Json1({
    this.json1Language,
    this.json1Icon,
    this.json1Code,
  });

  String json1Language;
  String json1Icon;
  String json1Code;

  factory Json1.fromJson(Map<String, dynamic> json) => Json1(
        json1Language: json["json1Language"],
        json1Icon: json["json1Icon"],
        json1Code: json["json1Code"],
      );

  Map<String, dynamic> toJson() => {
        "json1Language": json1Language,
        "json1Icon": json1Icon,
        "json1Code": json1Code,
      };
}

class Json2 {
  Json2({
    this.json2Country,
    this.json2Continent,
    this.json2Language,
  });

  String json2Country;
  String json2Continent;
  List<String> json2Language;

  factory Json2.fromJson(Map<String, dynamic> json) => Json2(
        json2Country: json["json2Country"],
        json2Continent: json["json2Continent"],
        json2Language: List<String>.from(json["json2Language"].map((x) => x)),
      );

  Map<String, dynamic> toJson() => {
        "json2Country": json2Country,
        "json2Continent": json2Continent,
        "json2Language": List<dynamic>.from(json2Language.map((x) => x)),
      };
}

class Json2Services {
  static Future<List<Json2>> getData() async {
    await Future.delayed(Duration(seconds: 5), () {});

    String jsonString = '''
    [
  {
    "json2Country": "Canada",
    "json2Continent": "North American",
    "json2Language": [
      "French",
      "English"
    ]
  },
  {
    "json2Country": "Mexico",
    "json2Continent": "North American",
    "json2Language": [
      "Spanish",
      "English"
    ]
  },
  {
    "json2Country": "United Kingdom",
    "json2Continent": "Europe",
    "json2Language": [
      "English"
    ]
  },
  {
    "json2Country": "France",
    "json2Continent": "Europe",
    "json2Language": [
      "French"
    ]
  },
  {
    "json2Country": "Spain",
    "json2Continent": "Europe",
    "json2Language": [
      "Spanish"
    ]
  }
]
    ''';

    return Future.value(json2FromJson(jsonString));
  }
}

class Json1Services {
  static Future<List<Json1>> getData() async {
    await Future.delayed(Duration(seconds: 5), () {});
    String jsonString = '''
    [
  {
    "json1Language": "English",
    "json1Icon": "https://www.countryflags.io/gb/flat/64.png",
    "json1Code": "en"
  },
  {
    "json1Language": "French",
    "json1Icon": "https://www.countryflags.io/fr/flat/64.png",
    "json1Code": "fr"
  },
  {
    "json1Language": "Spanish",
    "json1Icon": "https://www.countryflags.io/es/flat/64.png",
    "json1Code": "es"
  }
]
    ''';

    return Future.value(json1FromJson(jsonString));
  }
}

class Json2Page3 extends StatefulWidget {
  @override
  _Json2Page3State createState() => _Json2Page3State();
}

class _Json2Page3State extends State<Json2Page3> {
  List<Json2> json2 = [];
  List<String> _continent = [];
  @override
  void initState() {
    super.initState();
    setState(() {
      Json2Services.getData().then((data) {
        setState(() {
          json2 = data;
          _continent =
              json2.map<String>((x) => x.json2Continent).toSet().toList();
        });
      });
    });
  }

  @override
  Widget build(BuildContext context) {
    return DefaultTabController(
        length: _continent.length,
        child: Scaffold(
          appBar: AppBar(
            title: Text('Page 2'),
            bottom: TabBar(
                tabs:
                    _continent.map((String name) => Tab(text: name)).toList()),
          ),
          body: TabBarView(
              children: _continent.map((String name) {
            return ShowContinent(
              json2: List<Json2>.from(json2)
                ..retainWhere((e) => e.json2Continent == name),
            );
          }).toList()),
        ));
  }
}

class ShowContinent extends StatefulWidget {
  final List<Json2> json2;
  ShowContinent({this.json2});
  @override
  _ShowContinentState createState() => _ShowContinentState(json2);
}

class _ShowContinentState extends State<ShowContinent> {
  final List<Json2> json2;
  List<Json1> json1 = [];

  _ShowContinentState(this.json2);

  bool isLoading = true;
  @override
  void initState() {
    super.initState();
    WidgetsBinding.instance.addPostFrameCallback((_) {
      Json1Services.getData().then((data) {
        setState(() {
          json1 = data;
          isLoading = false;
        });
      });
    });
  }

  @override
  Widget build(BuildContext context) {
    return isLoading
        ? Center(child: CircularProgressIndicator())
        : Column(
            children: [
              for (Json2 j2 in json2)
                Row(
                  mainAxisAlignment: MainAxisAlignment.spaceAround,
                  children: [
                    Text(j2.json2Country.toUpperCase()),
                    for (int i = 0; i < j2.json2Language.length; i++)
                      Builder(
                        builder: (_) {
                          int index = json1.indexWhere(
                              (e) => e.json1Language == j2.json2Language[i]);
                          return Row(
                            children: [
                              Image.network(json1[index].json1Icon),
                              Text(json1[index].json1Code),
                            ],
                          );
                        },
                      )
                  ],
                ),
            ],
          );
  }
}

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

class MyApp extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      title: 'Flutter Demo',
      theme: ThemeData(
        primarySwatch: Colors.blue,
        visualDensity: VisualDensity.adaptivePlatformDensity,
      ),
      home: Json2Page3(),
    );
  }
}

When build() is called, json data is not ready.
So you need to fix code asynchronous.
I suggest one solution 'FutureBuilder' https://api.flutter.dev/flutter/widgets/FutureBuilder-class.html

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM