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