Проблема при первом отображении извлеченных данных в приложении flutter

#flutter #dart

Вопрос:

В своем приложении я получаю данные из API и сохраняю их в списке. Когда я пытаюсь отобразить данные на экране, он выдает красный экран с предупреждением в течение первых нескольких секунд, а затем загружает данные на экран.

компилятор выдает это предупреждение консоли

 
════════ Exception caught by widgets library ═══════════════════════════════════
The following StateError was thrown building StatusScreen(dirty, dependencies: [_InheritedProviderScope<CovidData>], state: _StatusScreenState#54e51):
Bad state: No element

The relevant error-causing widget was
StatusScreen
When the exception was thrown, this was the stack
#0      List.first (dart:core-patch/growable_array.dart:332:5)
#1      _StatusScreenState.build
#2      StatefulElement.build
#3      ComponentElement.performRebuild
#4      StatefulElement.performRebuild
...
════════════════════════════════════════════════════════════════════════════════

 

Это экран состояния

 import 'package:flutter/material.dart';
import 'package:provider/provider.dart';
import 'package:bubble_tab_indicator/bubble_tab_indicator.dart';

import 'package:Health_app/data/covidData.dart';  /* ---> the fetched data method and List of data is from here  */

import 'package:Health_app/screens/covidBarChart.dart';   /*-----> stateless widget that renders a barchart from provided data   */

import 'package:Health_app/config/palatte.dart';  
import 'package:Health_app/config/styles.dart';

import 'package:Health_app/widgets/custom_app_bar.dart';
import 'package:Health_app/widgets/statusGridViewer.dart';  /*----->stateless widget that provide information based on the bool values provided */

class StatusScreen extends StatefulWidget {
  static const routeName = '/statusScreen';
  @override
  _StatusScreenState createState() => _StatusScreenState();
}

class _StatusScreenState extends State<StatusScreen> {
  var _isInit = true;
  var _isLoading = false;


  var _local = true;
  var _today = true;

  get local {
    return _local;
  }

  get today {
    return _today;
  }

  @override
  void initState() {
    // TODO: implement initState
    super.initState();
  }

  @override
  void didChangeDependencies() {
    print("Didchangedependanciescalled");
    if (_isInit) {
      setState(() {
        _isLoading = true;
      });
      Provider.of<CovidData>(context, listen: true)
      .fetchAndSetDataCovid()
      .then((_) => {
      setState(() {
        _isLoading = false;
      }),
      });
    }
    _isInit = false;
    super.didChangeDependencies();
  }

  @override
  Widget build(BuildContext context) {

/* ----> I think the problem occurs from here, 
 when I'm calling the Provider to obtain the fetched data, the data obtained is stored in a `List<CovidProperties>` named **data**. 
    

    final covidData = Provider.of<CovidData>(context, listen: false);
    final dataInstance = covidData.data;

    final List<String> numList = [];
    final List<String> numList2 = [];
    final List<int> newNumList;

/*Here I'm selecting the 'pcrData' list, it consist of key value pairs of data,
 where value is another list of values with 'pcr_count' and 'date'     */
   
    dataInstance.first.pcrData.forEach((element) {
      numList.add(element['pcr_count']);
      numList2.add(element['date']);
    });

    newNumList = numList.map((e) => int.parse(e)).toList();

    return Scaffold(
      appBar: CustomAppBar(),
      backgroundColor: Palette.primaryColor,
      body: _isLoading
          ? Center(
              child: CircularProgressIndicator(),
            )
          : CustomScrollView(
              physics: ClampingScrollPhysics(),
              slivers: [
                _buildHeader(),
                _buildRegionTabBar(),
                _buildStateTabBar(),
                SliverPadding(
                  padding: const EdgeInsets.symmetric(horizontal: 10.0),
                  sliver: SliverToBoxAdapter(
                    child: StatusGridViewer(local, today),
                  ),
                ),
                SliverPadding(
                  padding: const EdgeInsets.symmetric(horizontal: 10.0),
                  sliver: SliverToBoxAdapter(
                    child: CovidBarChart(
                      covidCases: newNumList.sublist(1, 8),
                      covidDates: numList2,
                    ),
                  ),
                ),
              ],
            ),
    );
  }

  SliverPadding _buildHeader() {
    return SliverPadding(
      padding: const EdgeInsets.all(20.0),
      sliver: SliverToBoxAdapter(
        child: const Center(
          child: const Text(
            'Statistics',
            style: const TextStyle(
              color: Colors.white,
              fontSize: 25.0,
              fontWeight: FontWeight.bold,
            ),
          ),
        ),
      ),
    );
  }

  SliverToBoxAdapter _buildRegionTabBar() {
    return SliverToBoxAdapter(
      child: DefaultTabController(
        length: 2,
        child: Container(
          margin: EdgeInsets.symmetric(horizontal: 20.0),
          height: 50.0,
          decoration: BoxDecoration(
            color: Colors.white24,
            borderRadius: BorderRadius.circular(25.0),
          ),
          child: TabBar(
            indicator: const BubbleTabIndicator(
              tabBarIndicatorSize: TabBarIndicatorSize.tab,
              indicatorHeight: 40.0,
              indicatorColor: Colors.white60,
            ),
            labelStyle: Styles.tabTextStyle,
            labelColor: Colors.black,
            unselectedLabelColor: Colors.white,
            tabs: [
              const Text('Local'),
              const Text('Global'),
            ],
            onTap: (index) {
              print('Statmeaer changed '   index.toString());

              setState(() {
                _local = !_local;
              });
            },
          ),
        ),
      ),
    );
  }

  SliverPadding _buildStateTabBar() {
    return SliverPadding(
      padding: const EdgeInsets.all(20.0),
      sliver: SliverToBoxAdapter(
        child: DefaultTabController(
          length: 2,
          child: TabBar(
            indicatorColor: Colors.transparent,
            labelStyle: Styles.tabTextStyle,
            labelColor: Colors.white,
            unselectedLabelColor: Colors.white54,
            tabs: [
              const Text('Today'),
              const Text('Total'),
            ],
            onTap: (index) {
              print('Date to show details selected '   index.toString());
              setState(() {
                _today = !_today;
              });
            },
          ),
        ),
      ),
    );
  }
}
 

Это класс свойств covid

 class CovidProperties {
  final String updateDate;
  final int localNewCases;
  final int localTotalCases;
  final int totalHospitalized;
  final int localDeaths;
  final int localNewDeaths;
  final int localRecovered;
  final int localActiveCases;
  final int globalNewCases;
  final int globalTotalCases;
  final int globalDeaths;
  final int globalNewDeaths;
  final int globalRecovered;
  final int totalPCR;
  final List<dynamics> pcrData;

  CovidProperties(
      {required this.updateDate,
      required this.localNewCases,
      required this.localTotalCases,
      required this.totalHospitalized,
      required this.localDeaths,
      required this.localNewDeaths,
      required this.localRecovered,
      required this.localActiveCases,
      required this.globalNewCases,
      required this.globalTotalCases,
      required this.globalDeaths,
      required this.globalNewDeaths,
      required this.globalRecovered,
      required this.totalPCR,
      required this.pcrData
      });
}
 

Ссылка на API, из которого я получаю данные
канал передачи данных

Ответ №1:

Если вы получаете данные в начале виджета, я думаю, что для этого вам следует использовать FutureBuilder. На самом деле, вы избежите ошибок, пытаясь получить доступ к своим извлеченным данным, если вы еще не получили данные из API.

       class _StatusScreenState extends State<StatusScreen> {
      var _isLoading = false;   
      var _local = true;
      var _today = true;
      Future _initFuture;
    
      get local {
        return _local;
      }
    
      get today {
        return _today;
      }
    
      @override
      void initState() {
        _initFuture = Provider.of<CovidData>(context, listen: false).fetchAndSetDataCovid();  
        super.initState();
      }
    
      @override
      void didChangeDependencies() {     
        super.didChangeDependencies();
      }
    
      @override
      Widget build(BuildContext context) {
        final covidDataProvider = Provider.of<CovidData>(context, listen: false);
        
        //Move this logic to the provider
        //final List<String> numList = [];
        //final List<String> numList2 = [];
        //final List<int> newNumList;
  
        //dataInstance.first.pcrData.forEach((element) {
        // numList.add(element['pcr_count']);
        // numList2.add(element['date']);
        //});
    
        //newNumList = numList.map((e) => int.parse(e)).toList();
    
        return Scaffold(
          appBar: CustomAppBar(),
          backgroundColor: Palette.primaryColor,
          body: FutureBuilder(
          future: _initFuture,
          builder: (ctx, snapshot) {
          if(snapshot.connectionState != ConnectionState.done)
           return Center(
                  child: CircularProgressIndicator(),
                )
              else 
           return CustomScrollView(
                  physics: ClampingScrollPhysics(),
                  slivers: [
                    _buildHeader(),
                    _buildRegionTabBar(),
                    _buildStateTabBar(),
                    SliverPadding(
                      padding: const EdgeInsets.symmetric(horizontal: 10.0),
                      sliver: SliverToBoxAdapter(
                        child: StatusGridViewer(local, today),
                      ),
                    ),
                    SliverPadding(
                      padding: const EdgeInsets.symmetric(horizontal: 10.0),
                      sliver: SliverToBoxAdapter(
                        child: CovidBarChart(
                          covidCases: covidDataProvider.newNumList.sublist(1, 8),
                          covidDates: covidDataProvider.numList2,
                        ),
                      ),
                    ),
                  ],
                ),
        );
      }
 

Комментарии:

1. Чем вы t00n, я смог решить свою проблему, переместив реализацию списка pcr_data и дат поставщику и реализовав их в методе извлечения данных

Ответ №2:

Я смог решить эту проблему,переместив методы, которые я использую для создания numList, NumList1, newNumList, внутрь метода fetchAndSetDataCovid моих поставщиков.

сначала я реализовал List<int> pcrData и List<string> date отдельно и создал эту функцию для их заполнения. и после этого запускаем метод внутри fetchAndSetCovidData() метода.

   void getPcrCount() {
    final List<String> pcrCount1 = [];

    data.first.pcrData.forEach((element) {
      pcrCount1.add(element['pcr_count']);
      date.add(element['date']);
    });

    pcrCount = pcrCount1.map((e) => int.parse(e)).toList();
  }