this is my first provider class. I'm trying to retrieve the name of my item from my list
before getting this error I got another error that said '_internallinkedhashmap<string, dynamic>' I search that online and add .cast<String, dynamic>().
now I'm getting this one. is this something to do with my list length
import 'package:ecnomic/provider/provider.dart';
import 'package:flutter/material.dart';
import 'package:flutter/src/widgets/container.dart';
import 'package:flutter/src/widgets/framework.dart';
import 'package:provider/provider.dart';
import '../service/colors.dart';
class ItemProperties extends StatefulWidget {
const ItemProperties({super.key});
#override
State<ItemProperties> createState() => _ItemPropertiesState();
}
class _ItemPropertiesState extends State<ItemProperties> {
#override
Widget build(BuildContext context) {
List items = [];
final itemprovider =
Provider.of<onlineShopingProvider>(context, listen: false);
items = itemprovider.getOnlineItem;
return Container(
color: Colors.blue,
height: 500,
width: double.infinity,
child: GridView.builder(
gridDelegate: const SliverGridDelegateWithFixedCrossAxisCount(
crossAxisCount: 2,
crossAxisSpacing: 5,
// mainAxisSpacing: 10,
// childAspectRatio: 3 / 2,
),
itemCount: 20,
itemBuilder: ((context, index) {
return
// Card(
// color: Colors.amber,
// child:
Column(
children: [
Image.network(
'https://hips.hearstapps.com/hmg-prod/images/2020-bmw-750i-xdrive-252-1566180109.jpg?crop=1.00xw:0.927xh;0,0.0366xh&resize=2048:*'),
Text('$index'),
Text(
'${itemprovider.getOnlineItem[index].item_name.cast<String, dynamic>()}'),
],
);
//);
})));
}
}
and this is my provider class
import 'package:flutter/material.dart';
class onlineShopingProvider with ChangeNotifier {
final dynamic _onlineitem = [
{
"item_id": 2,
"item_name": "Jeep Wrangler Sahara",
"item_price": "80000",
"item_image":
"https://www.motortrend.com/uploads/sites/11/2020/01/2020-Jeep-Wrangler-Unlimited-Sahara-4x4-25958.jpg?fit=around%7C875:492",
"category_id": 22,
"category_name": "Jeep",
"item_color": "#FF0000"
},
{
"item_id": 2,
"item_name": "Jeep Wrangler Sahara",
"item_price": "80000",
"item_image":
"https://www.motortrend.com/uploads/sites/11/2020/01/2020-Jeep-Wrangler-Unlimited-Sahara-4x4-25958.jpg?fit=around%7C875:492",
"category_id": 22,
"category_name": "Jeep",
"item_color": "#FF0000"
},
{
"item_id": 18,
"item_name": "Jeep Grand Cherokee Summit",
"item_price": "80000",
"item_image":
"https://hips.hearstapps.com/hmg-prod/images/2023-jeep-grand-cherokee-summit-4x4-101-1667328305.jpeg?crop=0.617xw:0.520xh;0.353xw,0.451xh&resize=2048:*",
"category_id": 22,
"category_name": "Jeep",
"item_color": "#FF0000"
},
{
"item_id": 3,
"item_name": "BMW X5",
"item_price": "100000",
"item_image":
"https://www.motortrend.com/uploads/2022/11/2023-BMW-X5-xDrive40i-front-three-quarter-view-11.jpg?fit=around%7C875:492.1875",
"category_id": 23,
"category_name": "BMW",
"item_color": "#000000"
},
{
"item_id": 4,
"item_name": "BMW 7 Series",
"item_price": "100000",
"item_image":
"https://hips.hearstapps.com/hmg-prod/images/2020-bmw-750i-xdrive-252-1566180109.jpg?crop=1.00xw:0.927xh;0,0.0366xh&resize=2048:*",
"category_id": 23,
"category_name": "BMW",
"item_color": "#000000"
},
{
"item_id": 4,
"item_name": "BMW 8 Series",
"item_price": "107000",
"item_image":
"https://www.caranddriver.com/photos/g27011971/2020-bmw-7-series-drive-gallery/",
"category_id": 23,
"category_name": "BMW",
"item_color": "#000000"
}
];
final dynamic shopingCatagory = [
{
"category_id": 22,
"category_name": "Jeep",
"category_image": "https://pngimg.com/uploads/jeep/jeep_PNG95.png"
},
{
"category_id": 24,
"category_name": "Toyota",
"category_image":
"https://www.freepnglogos.com/uploads/toyota-logo-png/toyota-logos-brands-logotypes-0.png"
},
{
"category_id": 23,
"category_name": "BMW",
"category_image":
"https://pngimg.com/uploads/bmw_logo/bmw_logo_PNG19714.png"
}
];
get getOnlineItem => _onlineitem;
get getShopingCatagory => shopingCatagory;
}
Your itemCount should be the size of the list, try changing to
itemCount: itemprovider.getOnlineItem.length,
And also change your text to this
Text(itemprovider.getOnlineItem[index]['item_name']),
You are setting hard-code value itemCount: 20, but list doesn't contains this much data. You can do
itemCount: items.lenght,
itemCount: items.length,
use size of your list.when you give item count greater than size of your list it will throw an error
also you dont have to access list in provider because you already assigned it to block variable items
items[index].item_name
Related
I have an API and what I am trying to do is to display the 'CourseTitle' from the JSON into the ExpansionTile title and the corresponding 'Title', 'EndDate', 'QuizStatus' in the children of ExpansionTile.
If data is not available for corresponding 'Title', 'EndDate', 'QuizStatus' etc., only 'CourseTitle' should be added to the ExpansionTile title, and children should be empty.
The first tile is built as expected but the remaining screen shows a red screen with RangeError (index): Invalid value: Valid value range is empty: 1.
I'm aware that this is because of empty data from JSON but couldn't solve the issue.
Here's JSON response:
{
"QuizzesData": [
{
"request_status": "Successful",
"CourseCode": "ABC101",
"CourseTitle": "ABC Course",
"UnReadStatus": [],
"Quizzes": [
{
"QuizID": "542",
"Title": "Test Quiz 01",
"StartDate": "Oct 20, 2022 12:00 AM",
"EndDate": "Oct 31, 2022 11:59 PM",
"IsDeclared": "False",
"Questions": "5",
"TotalMarks": "5",
"MarksObtained": "1",
"MarksObtainedTOCheckQuizResult": "1",
"QuizIsDeclared": "Un-Declared",
"StudentSubmitStatus": "true",
"IsRead": "1",
"QuizStatus": "Attempted"
}
]
},
{
"CourseCode": "DEF101",
"CourseTitle": "DEF Course",
"UnReadStatus": [],
"Quizzes": []
},
{
"CourseCode": "GHI101",
"CourseTitle": "GHI Course",
"UnReadStatus": [],
"Quizzes": []
},
{
"CourseCode": "JKL101",
"CourseTitle": "JKL Course",
"UnReadStatus": [],
"Quizzes": []
},
]
}
Here's the API data:
var listofdata ;
Future quizListingApi() async {
final response = await http.get(Uri.parse('json url'));
if(response.statusCode == 200){
listofdata = jsonDecode(response.body.toString());
}
else{
print(response.statusCode);
}
and the build method:
#override
Widget build(BuildContext context) {
return Scaffold(
body: Column(
children: [
Expanded(child: FutureBuilder(
future: quizListingApi(),
builder: (context, AsyncSnapshot snapshot){
if(snapshot.connectionState == ConnectionState.waiting){
return Text('Loading');
}
else{
return ListView.builder(
itemCount: listofdata['QuizzesData'].length,
itemBuilder: (context, index){
return Card(
child: Column(
children: [
ExpansionTile(
title: Text(listofdata['QuizzesData'][index]['CourseTitle']),
children: [
Text(listofdata['QuizzesData'][index]['Quizzes'][index]['Title']),
Text(listofdata['QuizzesData'][index]['Quizzes'][index]['EndDate']),
Text(listofdata['QuizzesData'][index]['Quizzes'][index]['QuizStatus']),
],
),
],
),
);
}
);
}
},
))
],
),
);
}
I also tried answers from other similar threads but couldn't find the solution for this specific type of problem.
Your Quizzes's index are not same as your listofdata, so you need and other for loop for your Quizzes's items:
ExpansionTile(
title: Text(listofdata['QuizzesData'][index]['CourseTitle']),
children: List<Widget>.generate(listofdata['QuizzesData'][index]['Quizzes'].length, (i) => Column(
children: [
Text(listofdata['QuizzesData'][index]['Quizzes'][i]
['Title']),
Text(listofdata['QuizzesData'][index]['Quizzes'][i]
['EndDate']),
Text(listofdata['QuizzesData'][index]['Quizzes'][i]
['QuizStatus']),
],
),),
),
I have an API, I want to show this API live. When the user removes any data delete live and also add any data from the backend I want to show it live.
Now how can I achieve it in flutter?
I am using Getx(Obx not working) , ListView.builder, and Dio package.
My Page.dart
import 'package:clipboard/clipboard.dart';
import 'package:dio/dio.dart';
import 'package:eva_icons_flutter/eva_icons_flutter.dart';
import 'package:flutter/material.dart';
import 'package:flutter_phone_direct_caller/flutter_phone_direct_caller.dart';
import 'package:get/get.dart';
import 'package:intl/intl.dart';
import 'package:isp_app/src/configs/appColors.dart';
import 'package:isp_app/src/configs/appConfigs.dart';
import 'package:isp_app/src/controllers/baseController.dart';
import 'package:isp_app/src/widgets/customButton.dart';
import 'package:isp_app/src/widgets/customRawText.dart';
import 'package:isp_app/src/widgets/kText.dart';
import 'package:shared_preferences/shared_preferences.dart';
class NewCollectionTab extends StatefulWidget with BaseController {
#override
State<NewCollectionTab> createState() => _NewCollectionTabState();
}
class _NewCollectionTabState extends State<NewCollectionTab>
with BaseController {
final keyRefresh = GlobalKey<RefreshIndicatorState>();
#override
Widget build(BuildContext context) {
return Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
SizedBox(height: 20),
Padding(
padding: EdgeInsets.symmetric(horizontal: 20),
child: KText(
text: 'Main',
fontSize: 18,
fontWeight: FontWeight.w600,
color: Colors.black,
),
),
SizedBox(height: 10),
Obx( ()=>
ListView.builder(
shrinkWrap: true,
primary: false,
physics: BouncingScrollPhysics(),
itemCount: connectionDataC.connection.length,
itemBuilder: (context, index) {
final item = connectionDataC.connection[index];
return item.connectionStatus == true
? Container()
: InkWell( child: Padding(
padding: EdgeInsets.only(bottom: 10),
child: ListTile(
leading: CircleAvatar(
radius: 30,
backgroundColor: black12,
backgroundImage: NetworkImage(
item.catImg.toString(),
),
),
title: Column(
mainAxisAlignment: MainAxisAlignment.start,
children: [
Row(
mainAxisAlignment: MainAxisAlignment.start,
children: [
KText(
text: item.userName.toString(),
fontSize: 16,
maxLines: 2,
fontWeight: FontWeight.w600,
),
],
),
SizedBox(height: 5),
Row(
mainAxisAlignment: MainAxisAlignment.start,
children: [
Icon(
Icons.date_range,
size: 12,
color: black12,
),
SizedBox(width: 5),
KText(
text: DateFormat('dd-MM-yyyy').format(
DateTime.parse(
item.confrmDate.toString(),
),
),
fontSize: 12,
fontWeight: FontWeight.w600,
),
],
),
SizedBox(height: 5),
],
),
subtitle: KText(
text: item.description.toString(),
fontSize: 13,
color: black54,
maxLines: 3,
textAlign: TextAlign.justify,
fontWeight: FontWeight.normal,
),
),
),
);
},
),
),
],
);
}
}
My Controller.dart
import 'package:dio/dio.dart';
import 'package:get/get.dart';
import 'package:isp_app/src/configs/appConfigs.dart';
import 'package:isp_app/src/models/connections.dart';
import 'package:shared_preferences/shared_preferences.dart';
class ConnectionController extends GetxController {
// ignore: unused_field
final _dio = Dio();
// final RxList<dynamic> connectionList = RxList();
final connection = RxList<Connection>();
getconnectionList() async {
print('++++++++++++++++++++++++++++++++++');
try {
final sharedPreferences = await SharedPreferences.getInstance();
final token = sharedPreferences.get('accessToken');
final userName = sharedPreferences.get('loginUserName');
final res = await _dio.get(
'$baseUrl/new_connection/?search=$userName',
options: Options(
headers: {
"Content-Type": "application/json",
"Accept": "application/json",
"Authorization": 'Token $token'
},
),
);
print(res.statusCode);
print(res.headers);
// print(res.body);
// print(res.unauthorized);
print(res.realUri);
print(res.statusMessage);
print(res.data);
print(res.realUri);
print(res.requestOptions);
print('++++++++++++++++++++++++++++++++++');
if (res.statusCode == 200) {
if (res.statusCode! >= 200 && res.statusCode! < 300) {
connection.addAll(
(res.data as List).map((e) => Connection.fromJson(e)).toList());
}
}
} catch (e) {
print(e);
}
}
}
My Api Data demo :
[
{
"id": 1,
"confrm_date": "2022-01-17T07:14:06",
"User_name": "গণপরিবহনে অঘোষিত নিয়ম",
"Phone_Number": "0185455545",
"Description": "পরিবহন খাতের সঙ্গে মো. রুবেল হোসেন যুক্ত সেই ১৯৯৭ সাল থেকে। শুরু থেকেই তিনি ‘পুলিশ পাস’-এর কথা শুনে আসছেন। আদৌ এ নিয়ে কোনো আইন বা নিয়ম আছে কি না, তা জানেন না তিনি। তাঁর মতো অনেক পরিবহনমালিক ও শ্রমিকের একই অবস্থা।\r\n\r\nএত দিন শিক্ষার্থীদের জন্য বাসে অর্ধেক ভাড়ার (হাফ পাস) কথা শোনা গেলেও গণপরিবহনে ভাড়াসংক্রান্ত আরেকটি বিষয় প্রচলিত আছে। সেটিই হলো ওই ‘পুলিশ পাস’। এ ক্ষেত্রে অর্ধেক ভাড়া নয়, কোনো ভাড়াই দিচ্ছেন না অনেক পুলিশ সদস্য।",
"Connection_status": false,
"cat_img": "http://103.137.75.74:82/media/cat_img/LOGO-01.png",
"Connection_by": 1
},
{
"id": 2,
"confrm_date": "2022-01-17T07:14:06",
"User_name": "অবৈধ রসিদে টোল",
"Phone_Number": "0185455545",
"Description": "অবৈধ রসিদ তৈরি করে রাজধানীর যাত্রাবাড়ীর মেয়র হানিফ উড়ালসড়ক থেকে টোল আদায় করার অভিযোগ উঠেছে সংঘবদ্ধ একটি চক্রের বিরুদ্ধে। বিভিন্ন যানবাহনের চালকদের ভয় দেখিয়ে টোলের নামে চাঁদা আদায় করছিল চক্রটি। না দিলে প্রাণনাশের হুমকি দিচ্ছিল তারা।",
"Connection_status": false,
"cat_img": "http://103.137.75.74:82/media/cat_img/LOGO-01.png",
"Connection_by": 1
},
{
"id": 3,
"confrm_date": "2022-01-17T07:14:06",
"User_name": "User",
"Phone_Number": "0185455545",
"Description": "নরমাল ডেলিভারি হসপিটাল",
"Connection_status": false,
"cat_img": "http://103.137.75.74:82/media/cat_img/LOGO-01.png",
"Connection_by": 1
},
{
"id": 4,
"confrm_date": "2022-01-17T07:14:06",
"User_name": "User",
"Phone_Number": "0185455545",
"Description": "কাশেম মঞ্জিল, তানযীমুল উম্মাহ হিফয মাদ্রাসা।",
"Connection_status": false,
"cat_img": "http://103.137.75.74:82/media/cat_img/LOGO-01.png",
"Connection_by": 1
},
{
"id": 5,
"confrm_date": "2022-01-17T10:42:57",
"User_name": "User",
"Phone_Number": "0185455545",
"Description": "হাউজিং মারকাজ মসজিদের পিছনে - nstu teacher 2300 tk router +\r\nline charge 1000+ 360",
"Connection_status": true,
"cat_img": "http://103.137.75.74:82/media/cat_img/LOGO-01.png",
"Connection_by": 1
},
{
"id": 6,
"confrm_date": "2022-01-17T10:43:04",
"User_name": "User",
"Phone_Number": "0185455545",
"Description": "মাষ্টার পাড়া - হালিমা ভিলা ৩ তলায়",
"Connection_status": true,
"cat_img": "http://103.137.75.74:82/media/cat_img/LOGO-01.png",
"Connection_by": 1
},
{
"id": 7,
"confrm_date": "2022-01-17T10:44:25",
"User_name": "User",
"Phone_Number": "0185455545",
"Description": "মিঠুর কানেকশন আছে এখন , ১ তারিখ নিবে।",
"Connection_status": true,
"cat_img": "http://103.137.75.74:82/media/cat_img/LOGO-01.png",
"Connection_by": 1
},
{
"id": 8,
"confrm_date": "2022-01-17T10:44:49",
"User_name": "User",
"Phone_Number": "0185455545",
"Description": "এতিম খানার পিছনে।",
"Connection_status": true,
"cat_img": "http://103.137.75.74:82/media/cat_img/LOGO-01.png",
"Connection_by": 1
},
{
"id": 10,
"confrm_date": "2022-01-20T07:09:03",
"User_name": "User123",
"Phone_Number": "0185455545",
"Description": "54155",
"Connection_status": true,
"cat_img": "http://103.137.75.74:82/media/cat_img/LOGO-01.png",
"Connection_by": 1
},
{
"id": 11,
"confrm_date": "2022-01-20T07:09:48",
"User_name": "12345",
"Phone_Number": "0185455545",
"Description": "546",
"Connection_status": true,
"cat_img": "http://103.137.75.74:82/media/cat_img/LOGO-01.png",
"Connection_by": 1
}
]
Thing is that APIs do not send you "updates" so the client in this case your application does not receive updates when the server, this would require another mechanism like a socket, in which you connect to a server, and listen for active updates, In terms of a normal web api it works as request vs response, the server only responds to a request, so if you want to have a behavior that simulates a "real time" you will need to code a timer, which makes the request every 5 seconds for example, in that case, you will try to fetch new information every certain amount of time. Make sense?
final variationMap = HashMap<String, List>();
In this map, I have
key -> ["color"] = value -> ["White", "Black"];
key -> ["ram"] = value -> ["128GB", "256GB"];
Based on this information I have designed the below UI.
**I want -> If I select white, white will be selected and black will remain unselected. And If I select black white will become unselected.
The same goes for Ram. Selecting one will make the other unselected. Two list view selections will work independently. **
For a single list view, we can achieve this using a selectedIndex variable.
Here is the API response. Here attribute value can be multiple. But I need to show one value in UI. So after some logic, I store the label and value into a map.
"productVariation": [
{
"price": 406089.25,
"qty": 449,
"variationAttribute": [
{
"attribute_value": "White",
"attributeDetails": {
"attributeLabel": [
{
"label": "Color"
}
]
}
},
{
"attribute_value": "128GB",
"attributeDetails": {
"attributeLabel": [
{
"label": "Ram"
}
]
}
}
]
},
{
"price": 292561.69,
"qty": 246,
"variationAttribute": [
{
"attribute_value": "White",
"attributeDetails": {
"attributeLabel": [
{
"label": "Color"
}
]
}
},
{
"attribute_value": "256GB",
"attributeDetails": {
"attributeLabel": [
{
"label": "Ram"
}
]
}
}
]
},
{
"price": 951456.88,
"qty": 828,
"variationAttribute": [
{
"attribute_value": "Black",
"attributeDetails": {
"attributeLabel": [
{
"label": "Color"
}
]
}
},
{
"attribute_value": "128GB",
"attributeDetails": {
"attributeLabel": [
{
"label": "Ram"
}
]
}
}
]
},
{
"price": 930735.09,
"qty": 321,
"variationAttribute": [
{
"attribute_value": "Black",
"attributeDetails": {
"attributeLabel": [
{
"label": "Color"
}
]
}
},
{
"attribute_value": "256GB",
"attributeDetails": {
"attributeLabel": [
{
"label": "Ram"
}
]
}
}
]
}
]
Here is the UI code. This code is for the bottom sheet dialog.
variationView() {
final widgets = <Widget>[];
var i = 1; // maintain vertical dot line between variation
for (var key in widget.controller.variationMap.keys) {
final list = widget.controller.variationMap[key];
widgets.add(
GlobalText(
str: "Select $key",
fontSize: 18,
fontWeight: FontWeight.w300,
),
);
widgets.add(
const SizedBox(
height: 20,
),
);
widgets.add(
SizedBox(
height: 60,
child: ListView.builder(
itemCount: list!.length,
shrinkWrap: true,
scrollDirection: Axis.horizontal,
itemBuilder: (ctx, index) {
return GestureDetector(
onTap: () {
setState(() {
isSelectedIndex = index;
isSelectedIndexForListView = i;
});
},
child:Container(
margin: EdgeInsets.only(right: 11),
padding: EdgeInsets.all(4),
width: 60,
height: 55,
decoration: BoxDecoration(
color: Color(0xfff8f8f8),
borderRadius: BorderRadius.circular(10),
border: Border.all(
color: isSelectedIndex == index && isSelectedIndexForListView == i
? Colors.black
: Color(0xffe2e2e2),
width: 1,
),
),
child: Center(
child: GlobalText(
str: list[index],
color: Color(0xff535960),
fontSize: 13,
fontWeight: FontWeight.w400,
maxLines: 2,
),
),
),
);
},
),
),
);
if (i < widget.controller.variationMap.keys.length) {
widgets.add(
const SizedBox(
height: 30,
),
);
}
i++;
}
return Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: widgets,
);
}
I have tried multiple ways but failed to hold or manage the state of the selected item.
In this code, I have tried to hold the index of the list view and another for item selected index. but When I select a ram, So same index color also goes selected and vice versa.
I have also tried using Unique key. But failed to solve the problem.
First you can create a model class for Value which will have to fields one for the value name another for checking if it's selected or not.
class Value{
String valueName;
bool isSelected;
}
Then create another class which will have one field of String type that is the label and another field of type List of Value object.
class Model {
String label;
List<Value> valueList;
}
From your controller or viewmodel class or the class you are using to update the states you will just have to update the value of isSelected field.
I'm trying to fetch some history data from this rest API in a FutureBuilder. It seems the calls are too many to be handled although there must be a way to fetch the data.
A few info about the api call I'm making:
Average server latency: 632ms
Ideal remote api Calls: 5
Endpoint: https://covid-193.p.rapidapi.com/history?country=greece&day=2020-12-27}
Json Response
{
"get": "history",
"parameters": {
"country": "greece",
"day": "2020-12-27"
},
"errors": [],
"results": 1,
"response": [
{
"continent": "Europe",
"country": "Greece",
"population": 10398156,
"cases": {
"new": "+262",
"active": 120572,
"critical": 473,
"recovered": 9989,
"1M_pop": "12994",
"total": 135114
},
"deaths": {
"new": "+46",
"1M_pop": "438",
"total": 4553
},
"tests": {
"1M_pop": "314492",
"total": 3270139
},
"day": "2020-12-27",
"time": "2020-12-27T15:45:07+00:00"
}
]
}
How time scales to 37,9 sec
I'm making 5 API calls for 5 different dates (all 5 days before today) and I'm parsing the data to a Country Object. The call will approximately take 5 x 632ms = 3610ms = 3,6 sec which sounds reasonable for a delay.
But, I have 6 different types of data to parse, for example the total deaths, I need 5 of these calls to make for 1 type, multiply it by 6 and I get a total of 30 calls. Also note that my top level StreamBuilder can be called twice, so I'm ending up with 60 calls which translates to 37,920 ms = 37,9 sec
StreamBuilder called twice and I can't avoid it
Related Post: Streambuilder will be called twice
This is the layout inside my stream builder:
Layouts from Top to Bottom
Calling CountryScreen Widget to create the layout with the first parsed data
StreamBuilder(
stream: bloc.getCountryStream,
builder: (context, AsyncSnapshot<String> snapshot) {
if (snapshot.hasData) {
//Do stuff to show the total variables for each box (note this is a call to another endpoint)
return CountryScreen(
country: country,
),
}
)
CountryScreen
Map country // initialized from above
Widget build (context){
return column(children:[
InfoCard(
title: "Total Cases",
iconColor: Color(0xFFFF8C00),
effectedNum: "${country['totalcases']}",
country: country['country'],
searchFor: "Total Cases",
),
InfoCard(...),
...
...
InfoCard(
title: "New Deaths",
iconColor: Colors.red,
effectedNum: "${country['newdeaths']}",
country: country['country'],
searchFor: "Total Cases",
)
]);
}
Info Card
Widget build(context)
{
return Expanded(
child: LineReportChart(
country: country, searchFor: searchFor),
);
}
LineReportChart
class LineReportChart extends StatefulWidget {
final String country;
final String searchFor;
LineReportChart({this.country, this.searchFor});
#override
_LineReportChart createState() =>
_LineReportChart(country: country, searchFor: searchFor);
}
class _LineReportChart extends State<LineReportChart> {
String country;
String searchFor;
_LineReportChart({this.country, this.searchFor});
List<FlSpot> spots = [];
#override
Widget build(BuildContext context) {
return FutureBuilder(
future: getSpots(),
builder: (context, AsyncSnapshot<dynamic> snapshot) {
if (!snapshot.hasData) {
return AspectRatio(
aspectRatio: 2.2,
child: SizedBox(
child: CircularProgressIndicator(),
));
}
spots = snapshot.data;
return AspectRatio(
aspectRatio: 2.2,
child: LineChart(
LineChartData(
gridData: FlGridData(
show: false,
),
borderData: FlBorderData(
show: false,
),
titlesData: FlTitlesData(show: false),
lineBarsData: [
LineChartBarData(
spots: spots,
isCurved: true,
dotData: FlDotData(show: false),
belowBarData: BarAreaData(show: false),
colors: [kPrimaryColor],
barWidth: 3),
]),
),
);
});
}
Future<dynamic> getSpots() async {
return FetchCountry.getNewCasesChart(country, searchFor);
}
Rest API Call
getNewCasesChart method
static Future<List<FlSpot>> getNewCasesChart(country, searchFor) async {
//do 5 calls and the parsing for each one. I didn't include it as the post is already long.
print('called function for fetch data');
List<FlSpot> chartPoints;
switch (searchFor) {
case "Total Cases":
await Future.delayed(Duration(seconds: 2));
chartPoints = [
FlSpot(0, .5),
FlSpot(1, 1.5),
FlSpot(2, .5),
FlSpot(1, 1),
];
break;
case "Total Deaths":
await Future.delayed(Duration(seconds: 4));
chartPoints = [
FlSpot(0, .5),
FlSpot(1, 1.5),
FlSpot(2, .5),
];
break;
case "Recovered":
await Future.delayed(Duration(seconds: 3));
chartPoints = [
FlSpot(0, .5),
FlSpot(1, 1.5),
FlSpot(2, .5),
FlSpot(3, 2.1),
];
break;
case "New Cases":
await Future.delayed(Duration(seconds: 6));
chartPoints = [
FlSpot(1, 1.5),
FlSpot(0, .5),
FlSpot(2, .5),
];
break;
case "Critical":
await Future.delayed(Duration(seconds: 5));
chartPoints = [
FlSpot(0, .5),
FlSpot(1, 1.5),
FlSpot(2, .5),
FlSpot(3, .5),
FlSpot(5, .5),
];
break;
case "New Deaths":
await Future.delayed(Duration(seconds: 3));
chartPoints = [
FlSpot(0, .5),
FlSpot(1, 1.5),
FlSpot(2, .5),
FlSpot(0, .5),
FlSpot(1, 1.5),
FlSpot(2, .5),
];
break;
default:
}
return chartPoints;
}
Is there any way to reduce the total calls on the top level layout?
I am trying to implement Dismissible to swipe and remove the item from the list in flutter, but I am getting the below error on implementation of the same
type '_InternalLinkedHashMap<String, dynamic>' is not a subtype of
type 'String'
at this line of the code key: Key(item)
How should I resolve it ?
ListView.separated(
separatorBuilder: (context, index){
return Divider();
},
controller: _scrollController,
itemCount: noteItems,
shrinkWrap: true,
itemBuilder: (context, index) {
final item = firstdata[index];
return
Dismissible(
direction: DismissDirection.endToStart,
key: Key(item),
onDismissed: (direction) {
setState(() {
firstdata.removeAt(index);
});
Scaffold.of(context)
.showSnackBar(SnackBar(content: Text("$item dismissed")));
},
background: Container(color: Colors.red)
,
child: Padding(
padding: const EdgeInsets.fromLTRB(8.0, 7.0, 8.0, 0.0),
child: Column(
children: <Widget>[
ListTile(
leading:ClipRRect(
borderRadius: BorderRadius.circular(10),
child: Image.asset('images/appstore.png', width: 50, height: 50)
) ,
title:
Row(children: [
Flexible(
child: firstdata[index]['id']!= null?AutoSizeText(
firstdata[index]['id'],
maxLines: 2,
style: TextStyle(fontWeight: FontWeight.bold),) :Text(''),
),
],),
),
],
),
),
);
},
),
The JSON data structure for the list view is here below
{
"error": "false",
"notification": [
{
"rn": "1",
"id": "224",
"company_details": {
"code": "2",
}
},
{
"rn": "2",
"id": "219",
"company_details": {
"code": "3",
}
},
{
"rn": "3",
"id": "213",
"company_details": {
"code": "3",
}
},
{
"rn": "4",
"id": "209",
"company_details": {
"code": "4",
}
},
{
"rn": "5",
"id": "204",
"company_details": {
"code": "3",
}
},
{
"rn": "6",
"id": "199",
"company_details": {
"code": "3",
}
},
{
"rn": "7",
"id": "193",
"company_details": {
"code": "3",
}
}
],
}
How should I implement the same and get it resolved?
As stated in the other answer, the Key function expects a string to create a key based on that. If you can identify an item based on one of its parameters (for example id), then you could use item.id and it would be fine.
However, to make sure it will be truly unique key for any combination of parameters (in your case id, rn and company_details) you can use ObjectKey:
Replace the following line:
key: Key(item)
With the following:
key:ObjectKey(item)
This way Flutter can identify your item's parameters and create a key based on the combination of them.
Other options include ValueKey and UniqueKey.
type '_InternalLinkedHashMap<String, dynamic>' is not a subtype of type 'String'
means that it crashed because it was expecting a String and flutter did not find one.
This means that:
key: Key(item)
Key(item)-> Is not a String. I don´t know how are you creating Key/where is it.
My best guess is to try to find some method like...:
`key: Key(item).aMethodthatgivesaString()`
`key: Key(item).toString()`
Let me know if this was useful.