RangeError (index): Invalid value: Valid value range is empty: 0 error is coming - flutter

import 'package:flutter/cupertino.dart';
import 'package:flutter/material.dart';
import 'package:flutter/rendering.dart';
import 'package:flutter/widgets.dart';
import 'package:greycell_app/src/commons/actions/dialog_handler.dart';
import 'package:greycell_app/src/commons/themes/textstyle.dart';
import 'package:greycell_app/src/commons/widgets/error_data.dart';
import 'package:greycell_app/src/manager/main_model.dart';
import 'package:greycell_app/src/models/payment/due_detail.dart';
import 'package:greycell_app/src/models/payment/payable_fee.dart';
import 'package:greycell_app/src/models/response/response.dart';
import 'package:greycell_app/src/models/response/success.dart';
import 'package:greycell_app/src/views/accountViews/account_header.dart';
import 'package:greycell_app/src/views/accountViews/pay_option.dart';
import 'package:greycell_app/src/views/paymentViews/payable_tile.dart';
import 'package:greycell_app/src/views/paymentViews/payment_confirmation_screen.dart';
import 'package:greycell_app/src/views/paymentViews/payment_edit_field_view.dart';
import 'package:scoped_model/scoped_model.dart';
class PaymentBodyView extends StatefulWidget {
final DueDetail dueDetail;
final MainModel model;
//
const PaymentBodyView({this.dueDetail, this.model});
#override
_MyPaymentScreenState createState() => _MyPaymentScreenState();
}
class _MyPaymentScreenState extends State<PaymentBodyView> {
final TextEditingController _paymentEditController = TextEditingController();
final TextEditingController _paymentSearchController =
TextEditingController();
final GlobalKey<FormState> _formKey = GlobalKey<FormState>();
final double minValue = 8.0;
DueDetail _dueDetail;
double _totalFeeAmount = 0.0;
/// Is user is Scrolling
bool isScrollDown = false;
ScrollController _scrollController;
// Current Index
int _currentIndex = -999999;
void setUp() {
if (_dueDetail.getPayAmtFrom == 'FA') {
// There will be one custom payable fee in the list: for more info check Account Service for FA
widget.model.addPayableFee(_dueDetail.payableFeeDetails.first);
}
}
void _onCreated() async {
_dueDetail = widget.dueDetail;
if (_dueDetail == null) {
// Make Api Call and Get Data
final ResponseMania _result = await widget.model.getAccountDueDetail();
if (_result is Success) {
_dueDetail = _result.success;
setUp();
_calculateAmount();
}
} else {
// You have transaction data
setUp();
_calculateAmount();
}
}
void _calculateAmount() {
if (_dueDetail != null && _dueDetail.payableFeeDetails != null) {
_dueDetail.payableFeeDetails.forEach((PayableFee fee) {
_totalFeeAmount += fee.feeAmount;
});
}
setState(() {});
}
void _onPayableTap(int index) {
if (!mounted) return;
setState(() {
if (_currentIndex == index) {
_currentIndex = -99999;
}
else {
_currentIndex = index;
}
});
}
void _onAddPayableFee(int index, PayableFee selectedFee) {
widget.model.addPayableFee(selectedFee);
_showSnackBar("Added to the payment");
setState(() {
_currentIndex = -99999;
});
}
void _onEditPayableFee(int index, PayableFee selectedFee) async {
_paymentEditController.text = selectedFee.feeAmount.toString();
DialogHandler.onCustomAlertDialog(
title: "Edit amount",
context: context,
content: PaymentEditTextFiled(
context: context,
key: _formKey,
fee: selectedFee,
paymentEditController: _paymentEditController,
).build(),
onSubmit: () {
if (_formKey.currentState.validate()) {
selectedFee.netPayAbleAmount =
double.parse(_paymentEditController.text);
widget.model.editPayableAmount(index, selectedFee);
Navigator.of(context).pop();
_showSnackBar("Updated and added to the payment");
}
});
}
void _onRemovePayableFee(int index, PayableFee selectedFee) {
print("remove Called");
widget.model.removePayableFee(selectedFee);
_showSnackBar("Successfully removed");
setState(() {
_currentIndex = -99999;
});
}
void _onProceedToPay() {
Navigator.of(context)
.push(MaterialPageRoute(
builder: (_) => PaymentConfirmationScreen(
model: widget.model,
dueDetail: _dueDetail,
totalFeeAmount: _totalFeeAmount,
totalPayAbleAmount: widget.model.totalPayableAmount,
payableFees: widget.model.selectedPayableFees
// payableFees: <PayableFee>[_dueDetail.payableFeeDetails[0]],
)))
.then((value) {});
}
void _reset() {
widget.model.clearSelectedPayable();
}
#override
void dispose() {
_scrollController.dispose();
_reset();
super.dispose();
}
#override
void initState() {
super.initState();
_onCreated();
_scrollController = ScrollController(initialScrollOffset: 0.0);
}
bool get isNotDueFound =>
_dueDetail.payableFeeDetails == null ||
_dueDetail.payableFeeDetails.length == 0;
void onAllSelectChanged(bool value) {}
void _showSnackBar(String msg) {
Scaffold.of(context).showSnackBar(SnackBar(
content: Text(
"$msg",
style: CustomTextStyle(context).caption.apply(color: Colors.white),
),
backgroundColor: Colors.green[400],
));
}
Widget _buildPaymentButton() {
return Container(
width: MediaQuery.of(context).size.width,
height: 50.0,
decoration: BoxDecoration(
gradient: LinearGradient(
begin: Alignment.centerLeft,
end: Alignment.centerRight,
colors: [Colors.blue, Theme.of(context).primaryColor])),
child: MaterialButton(
padding: EdgeInsets.all(15.0),
onPressed: _onProceedToPay,
child: Text(
"Proceed to pay",
style: CustomTextStyle(context)
.subtitle1
.apply(color: Colors.white, fontWeightDelta: 1),
),
textColor: Colors.white,
),
);
}
Widget _buildTotalAmount() {
return Container(
padding: EdgeInsets.symmetric(horizontal: 12.0, vertical: 15.0),
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: <Widget>[
Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: <Widget>[
Text("Total fee amount"),
Text(
"₹${_totalFeeAmount}",
style: CustomTextStyle(context)
.subtitle1
.apply(fontWeightDelta: 1),
)
],
),
SizedBox(
height: 12.0,
),
Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: <Widget>[
Text("Net payable amount"),
ScopedModelDescendant(builder: (_, __, MainModel model) {
return Text(
"₹${model.totalPayableAmount}",
style: CustomTextStyle(context)
.subtitle1
.apply(fontWeightDelta: 1),
);
})
],
),
],
),
);
}
Widget _buildFinalAmount() {
return ScopedModelDescendant(builder: (_, __, MainModel model) {
return Container(
padding: EdgeInsets.all(16),
child: Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: <Widget>[
Text(
"Final Payable Amount",
style: CustomTextStyle(context).subtitle2,
),
Flexible(
child: Text(
"₹${model.totalPayableAmount}",
style: CustomTextStyle(context).headline6,
),
),
],
),
);
});
}
Widget _buildFAbody() {
final PayableFee _fee = _dueDetail.payableFeeDetails.first;
return Container(
child: Card(
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: <Widget>[
PayableTile(
isEditable: false,
isSelected: false,
payableFee: _fee,
),
_dueDetail.getPolicyValue != "P"
? Container()
: Row(
crossAxisAlignment: CrossAxisAlignment.center,
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: <Widget>[
Padding(
padding: const EdgeInsets.only(left: 15.0),
child: Text(
"Partially Pay",
style: CustomTextStyle(context).subtitle2,
),
),
MaterialButton(
onPressed: () => _onEditPayableFee(0, _fee),
child: Text("Change"),
padding: EdgeInsets.symmetric(vertical: 15.0),
)
],
),
Divider(),
_buildFinalAmount(),
SizedBox(
height: 20.0,
),
Padding(
padding: const EdgeInsets.all(16.0),
child: _buildPaymentButton(),
),
],
),
),
);
}
#override
Widget build(BuildContext context) {
return Container(
color: Colors.grey[50],
child: _dueDetail == null
? Center(
child: CircularProgressIndicator(),
)
: Column(
children: <Widget>[
Expanded(
child: Container(
width: MediaQuery.of(context).size.width,
child: SingleChildScrollView(
child: Column(
crossAxisAlignment: CrossAxisAlignment.center,
children: <Widget>[
// PaymentSearchFilter(),
MyAccountInfoHeader(
dueDetail: _dueDetail,
),
MyOnlinePayOption(
dueDetail: _dueDetail,
),
isNotDueFound
? MyErrorData(
errorMsg: "No dues found",
subtitle: "No payable fees available",
)
: _dueDetail.getPayAmtFrom == 'FA'
? _buildFAbody()
: ListView.builder(
controller: _scrollController,
shrinkWrap: true,
physics: ClampingScrollPhysics(),
itemCount:
_dueDetail.payableFeeDetails.length.compareTo(0),
itemBuilder:
(BuildContext context, int index) {
final PayableFee _fee =
_dueDetail.payableFeeDetails[index];
return PayableTile(
payableFee: _fee,
isEditable:
_dueDetail.getPolicyValue == "P",
onChanged: () {
_onPayableTap(index);
},
isSelected: _currentIndex == index,
onBottomAction: (String action) {
if (action == 'EDIT')
_onEditPayableFee(index, _fee);
else if (action == 'DELETE')
_onRemovePayableFee(index, _fee);
else
_onAddPayableFee(index, _fee);
},
);
}),
],
),
),
),
),
isNotDueFound || _dueDetail.getPayAmtFrom == 'FA'
? Container()
: Align(
alignment: Alignment.bottomCenter,
child: Container(
decoration: BoxDecoration(
color: Colors.blue[50],
borderRadius: BorderRadius.only(
topLeft: Radius.circular(12),
topRight: Radius.circular(12.0))),
// padding: EdgeInsets.symmetric(horizontal: 12.0),
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: <Widget>[
_buildTotalAmount(),
_buildPaymentButton(),
],
),
),
)
],
),
);
}
}
RangeError (index): Invalid value: Valid value range is empty: 0
This error is coming when I try to press a button to open this page. How to fix it please help. It's taking too much of my time... Please help me out solving this.
Most probably it is a ListView problem or something... Check it out, please. I am fetching data from
API here on the page & doing some calculations.

Related

Error : Failed to detect image file format using the file header. File header was [0x3c 0x68 0x74 0x6d 0x6c 0x3e 0x0a 0x20 0x20 0x20]

I'm trying to display an image from Firestore in to my UI. But it returns the error above, and the file format also changes when I run it on Edge (not on physical device or Emulator). I can't figure out how. Here's my code.
class ViewPola extends StatefulWidget {
const ViewPola({Key? key}) : super(key: key);
static String id = 'view_pola';
#override
State<ViewPola> createState() => _ViewPolaState();
}
class _ViewPolaState extends State<ViewPola> {
Function getSnaps = () async {
final polaMap = {
'Kode Pola': '',
'Bagian Pola': '',
'image_url': '',
};
FirebaseFirestore.instance
.collection('PolaWillyJKT')
.limit(1)
.get()
.then((snapshot) {
if (snapshot.size == 0) {
FirebaseFirestore.instance.collection('PolaWillyJKT').add(polaMap);
FirebaseFirestore.instance.collection('PolaWillyJKT');
print('add');
} else {
print('disini nge get');
var a = FirebaseFirestore.instance.collection('PolaWillyJKT').get();
Map<String, dynamic> data = a as Map<String, dynamic>;
print(data);
}
});
};
var _selectedItem;
var _showList = false;
#override
void initState() {
getSnaps();
super.initState();
}
final Stream<QuerySnapshot> _polaWilly =
FirebaseFirestore.instance.collection('PolaWillyJKT').snapshots();
#override
Widget build(BuildContext context) {
final screenHeight = ScreenInfo.screenHeight(context);
final screenWidth = ScreenInfo.screenWidth(context);
return StreamBuilder<QuerySnapshot>(
stream: _polaWilly,
builder: (BuildContext context, AsyncSnapshot<QuerySnapshot> snapshot) {
if (snapshot.hasError) {
return Text('Something went wrong');
}
if (snapshot.connectionState == ConnectionState.waiting) {
return Text("Loading");
}
return SafeArea(
child: Scaffold(
body: Padding(
padding: EdgeInsets.all(25),
child: Column(
mainAxisSize: MainAxisSize.min,
crossAxisAlignment: CrossAxisAlignment.center,
children: [
const Text(
'Pilih Bagian Pola',
style: TextStyle(fontSize: 25),
),
const SizedBox(
height: 20,
),
DropdownButton(
isExpanded: true,
value: _selectedItem,
items: snapshot.data?.docs
.map(
(value) => DropdownMenuItem(
value: value.get("Bagian Pola"),
child: Text('${value.get("Bagian Pola")}'),
),
)
.toList(),
onChanged: (newValue) {
setState(() {
_selectedItem = newValue.toString();
_showList = true;
});
}),
Padding(
padding: const EdgeInsets.all(25),
child: Visibility(
visible: _showList,
child: Container(
height: screenHeight * 0.4,
child: ListView(
children: snapshot.data!.docs
.where(
(e) => e.get("Bagian Pola") == _selectedItem)
.map((DocumentSnapshot document) {
Map<String, dynamic> data =
document.data()! as Map<String, dynamic>;
return Center(
child: Column(
children: [
Text(
'Bagian Pola: ${data["Bagian Pola"]}',
style: TextStyle(fontSize: 15),
),
const SizedBox(
height: 15,
),
Text(
'Kode Pola : ${data["Kode Pola"]}',
style: TextStyle(fontSize: 15),
),
const SizedBox(
height: 15,
),
Image(
image:
NetworkImage(document.get("Foto Pola")),
height: 200,
width: 200,
), // this is the Image widget where I tried to put the image from firestore
Text(document.get('Foto Pola').toString()),
],
),
);
}).toList(),
),
),
),
),
],
),
),
),
);
},
);
}
}
and here's the class that uploads the image to Firestore
class Input_Pola extends StatefulWidget {
const Input_Pola({Key? key}) : super(key: key);
static String id = 'input_pola';
#override
State<Input_Pola> createState() => _Input_PolaState();
}
class _Input_PolaState extends State<Input_Pola> {
final GlobalKey<ScaffoldState> _scaffoldKey = GlobalKey<ScaffoldState>();
void _checkDuplicate() {
if (options.contains(_textfieldValue.text)) {
// Display bottom sheet that says item already exists
_scaffoldKey.currentState?.showBottomSheet((context) => Container(
height: 300,
child: Column(
children: [
const Text('Item already exists'),
TextButton(
onPressed: () {
Navigator.pop(context);
},
child: Text('Dismiss'))
],
),
));
} else {
// Add item to options list
setState(() {
options.add(_textfieldValue.text);
});
}
}
XFile? image;
final ImagePicker picker = ImagePicker();
String _image_url = '';
void _addToFirebase(String dropdownValue, String _kodePolaController) {
var imageFile = File(image!.path);
String fileName = pth.basename(imageFile.path);
FirebaseStorage storage = FirebaseStorage.instance;
Reference ref = storage.ref().child("WillyJKT/polaWillyJKT");
UploadTask uploadTask = ref.putFile(imageFile);
uploadTask.whenComplete(() async {
var url = await ref.getDownloadURL();
_image_url = url.toString();
});
FirebaseFirestore.instance
.collection('PolaWillyJKT')
.doc(_selectedOption)
.set({
'Bagian Pola': _selectedOption,
'Kode Pola': _kodePolaController,
'Foto Pola': _image_url
});
}
String _dropdownValue = 'kg';
String _property1 = '';
String _property2 = '';
String _property3 = '';
bool _isOptionSelected = false;
final TextEditingController _kodePolaController = TextEditingController();
var _selectedOption;
final TextEditingController _textfieldValue = TextEditingController();
final List<String> options = [];
#override
void initState() {
super.initState();
_selectedOption = options.isNotEmpty ? options[0] : null;
}
//we can upload image from camera or from gallery based on parameter
Future getImage(ImageSource media) async {
var img = await picker.pickImage(source: media);
setState(() {
image = img;
});
}
void myAlert() {
showDialog(
context: context,
builder: (BuildContext context) {
return AlertDialog(
shape:
RoundedRectangleBorder(borderRadius: BorderRadius.circular(8)),
title: Text('Please choose media to select'),
content: Container(
height: MediaQuery.of(context).size.height / 6,
child: Column(
children: [
ElevatedButton(
//if user click this button, user can upload image from gallery
onPressed: () {
Navigator.pop(context);
getImage(ImageSource.gallery);
},
child: Row(
children: [
Icon(Icons.image),
Text('From Gallery'),
],
),
),
ElevatedButton(
//if user click this button. user can upload image from camera
onPressed: () {
Navigator.pop(context);
getImage(ImageSource.camera);
},
child: Row(
children: [
Icon(Icons.camera),
Text('From Camera'),
],
),
),
],
),
),
);
});
}
#override
Widget build(BuildContext context) {
final screenHeight = ScreenInfo.screenHeight(context);
final screenWidth = ScreenInfo.screenWidth(context);
return SafeArea(
child: Scaffold(
key: _scaffoldKey,
floatingActionButton: FloatingActionButton(
child: const Icon(Icons.add),
onPressed: () {
_addToFirebase(_dropdownValue, _kodePolaController.text);
},
),
body: SingleChildScrollView(
child: Padding(
padding: const EdgeInsets.all(25),
child: Container(
height: screenHeight,
child: Column(
mainAxisAlignment: MainAxisAlignment.start,
crossAxisAlignment: CrossAxisAlignment.center,
children: [
TextFormField(
decoration: const InputDecoration(
hintText: 'Input Pola',
border: UnderlineInputBorder(),
),
onChanged: (value) {
setState(() {
_textfieldValue.text = value;
});
},
),
DropdownButton<String>(
value: _selectedOption,
onChanged: (value) {
setState(() {
_selectedOption = value!;
_isOptionSelected = true;
_kodePolaController.clear();
});
},
hint: const Text('Input from Text Field Above'),
items: options.map((option) {
return DropdownMenuItem<String>(
value: option,
child: Text(option),
);
}).toList(),
),
TextButton(
onPressed: _checkDuplicate,
child: const Text("Add Option"),
),
Visibility(
visible: _isOptionSelected,
child: Expanded(
child: Column(
mainAxisAlignment: MainAxisAlignment.start,
crossAxisAlignment: CrossAxisAlignment.center,
children: <Widget>[
TextField(
controller: _kodePolaController,
decoration: const InputDecoration(
labelText: "Kode Pola"),
onChanged: (value) {
setState(() {
_property1 = value;
});
},
),
const SizedBox(height: 20,),
ElevatedButton(
onPressed: () {
myAlert();
},
child: Text('Upload Photo'),
),
SizedBox(
height: 10,
),
//if image not null show the image
//if image null show text
image != null
? Padding(
padding:
const EdgeInsets.symmetric(horizontal: 20),
child: ClipRRect(
borderRadius: BorderRadius.circular(8),
child: Image.file(
//to show image, you type like this.
File(image!.path),
fit: BoxFit.cover,
width: MediaQuery.of(context).size.width,
height: 300,
),
),
)
: const Text(
"No Image",
style: TextStyle(fontSize: 20),
),
],
),
),
)
],
),
),
),
),
),
);
}
}
It also displays the error on Edge, but when I build the apk and run it on my physical device, it just shows nothing on the screen except for the data that's got above it. Any idea on how I can fix this?
var a = FirebaseFirestore.instance.collection('PolaWillyJKT').get();
This returns a promise but not the actual data. In fact it could still be fetching the data when you attempt to convert it to a map.
You can await
var a = await FirebaseFirestore.instance.collection('PolaWillyJKT').get();
myDoc = a.docs.first();
Or use a callback that will run once the collection fetch completes:
FirebaseFirestore.instance.collection('PolaWillyJKT').get().then((response) => {
print(response);
});

Flutter: image messing up Flexible/Expanded sizes

Im trying to make something in flutter that looks like a twitter clone. The Tweet, called a wave, can be seen in the following:
import 'package:font_awesome_flutter/font_awesome_flutter.dart';
import 'package:timeago/timeago.dart' as timeago;
import '../../../../../../blocs/vote/vote_bloc.dart' as vote;
import 'package:cached_network_image/cached_network_image.dart';
import 'package:flutter/material.dart';
import 'package:flutter_bloc/flutter_bloc.dart';
import 'package:like_button/like_button.dart';
import '../../../../../../blocs/profile/profile_bloc.dart';
import '../../../../../../blocs/wave_liking/wave_liking_bloc.dart';
import '../../../../../../models/user_model.dart';
import '../../../../../../models/wave_model.dart';
import '../../../../../../widgets/text_splitter.dart';
import '../../generic_view.dart';
import '../../photo_view/photo_view.dart';
class WaveTile extends StatelessWidget {
const WaveTile({
Key? key,
required this.poster,
required this.wave,
this.extendBelow = false,
}) : super(key: key);
final User poster;
final Wave wave;
final bool extendBelow;
#override
Widget build(BuildContext context) {
User user =
(BlocProvider.of<ProfileBloc>(context).state as ProfileLoaded).user;
return IntrinsicHeight(
child: Row(
children: [
waveColumn(context),
Expanded(
flex: 5,
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
waveHeader(poster, wave, context, user),
waveText(wave, context),
if (wave.imageUrl != null) waveImage(wave, context),
waveButtons(wave),
],
))
],
),
);
}
Expanded waveColumn(BuildContext context) {
return Expanded(
child: Column(
mainAxisAlignment: MainAxisAlignment.start,
crossAxisAlignment: CrossAxisAlignment.center,
children: [
SizedBox(
width: 40,
height: 40,
child: InkWell(
child: Hero(
tag: 'wave${wave.id}',
child: CircleAvatar(
backgroundImage:
CachedNetworkImageProvider(poster.imageUrls[0])),
),
onTap: () {
BlocProvider.of<vote.VoteBloc>(context)
.add(vote.LoadUserEvent(user: poster));
Navigator.pushNamed(
context,
'/votes',
);
},
),
),
if (extendBelow)
Expanded(
child: VerticalDivider(
color: Color.fromARGB(255, 207, 207, 207),
thickness: 2,
width: 10,
),
),
//add a grey line
]),
);
}
}
Widget waveHeader(User poster, Wave wave, BuildContext context, User user) {
return Row(
children: [
Container(
margin: const EdgeInsets.only(right: 5.0),
child: Text(poster.name,
style: Theme.of(context)
.textTheme
.headline4!
.copyWith(fontWeight: FontWeight.bold)),
),
Text(
'${poster.handle} · ${timeago.format(wave.createdAt)}',
style: Theme.of(context).textTheme.subtitle1,
),
Spacer(),
WaveTilePopup(poster: poster, wave: wave, user: user),
],
);
}
Widget waveText(Wave wave, BuildContext context) {
return Flexible(
child: TextSplitter(
wave.message,
context,
Theme.of(context).textTheme.subtitle2!,
));
}
Widget waveImage(Wave wave, BuildContext context) {
return Flexible(
child: ClipRRect(
borderRadius: BorderRadius.circular(10),
child: InkWell(
child: CachedNetworkImage(
imageUrl: wave.imageUrl!,
fit: BoxFit.fill,
),
onTap: () {
Navigator.pushNamed(
context,
MyPhotoView.routeName,
arguments: {
'imageUrls': [wave.imageUrl!],
'index': 0
},
);
},
),
),
);
}
Widget waveButtons(Wave wave) {
return BlocBuilder<ProfileBloc, ProfileState>(
builder: (context, profileState) {
if (profileState is ProfileLoading) {
return Container();
}
if (profileState is ProfileLoaded) {
User user = profileState.user;
return Container(
margin: const EdgeInsets.only(top: 10.0, right: 20.0),
child: Row(
mainAxisAlignment: MainAxisAlignment.spaceEvenly,
children: [
Row(
children: [
//font awesome comment icon
Icon(
FontAwesomeIcons.comment,
size: 14.0,
color: Colors.grey,
),
SizedBox(width: 5),
Text(wave.comments.toString()),
],
),
BlocBuilder<WaveLikingBloc, WaveLikingState>(
builder: (context, waveLikingState) {
//set waveLikingState to be WaveLikingLoaded
waveLikingState = waveLikingState as WaveLikingLoaded;
bool inShortTermLikes =
waveLikingState.shortTermLikes.contains(wave.id);
bool inShortTermDislikes =
waveLikingState.shortTermDislikes.contains(wave.id);
bool inLikes = waveLikingState.likes.contains(wave.id);
bool inDislikes = waveLikingState.dislikes.contains(wave.id);
bool likedBy = wave.likedBy.contains(user.id);
bool isLiked = ((likedBy || inLikes || inShortTermLikes) &&
!(inDislikes || inShortTermDislikes));
int likeCount = (inLikes || inShortTermLikes)
? wave.likes + 1
: (inDislikes || inShortTermDislikes)
? wave.likes - 1
: wave.likes;
return LikeButton(
isLiked: isLiked,
size: 30,
circleColor: CircleColor(
start: Colors.red[300]!, end: Colors.red[900]!),
bubblesColor: BubblesColor(
dotPrimaryColor: Colors.red[300]!,
dotSecondaryColor: Colors.red[900]!,
),
likeBuilder: (bool isLiked) {
return Icon(
Icons.favorite,
color: isLiked ? Colors.red[900] : Colors.grey,
size: 20,
);
},
likeCount: likeCount,
countBuilder: (int? count, bool isLiked, String text) {
var color = isLiked ? Colors.red[900] : Colors.grey;
Widget result;
result = Text(
text,
style: TextStyle(color: color),
);
return result;
},
onTap: (bool isLiked) async {
(isLiked)
? BlocProvider.of<WaveLikingBloc>(context).add(
DislikeWave(waveId: wave.id, userId: user.id!))
: BlocProvider.of<WaveLikingBloc>(context)
.add(LikeWave(waveId: wave.id, userId: user.id!));
return !isLiked;
},
);
},
),
],
),
);
}
return Container();
},
);
}
When creating a wave with just text, it will end up looking like this:
This is the ideal situation right now. However, when an image is added, it looks like this:
Going through widget inspector does not seem to help me much, and the only way I can change the size of the wave is by deleting the image, leading me to believe the image is causing some weird interaction with the Flexible/Expanded widgets near it. Anyone got any ideas?
Thanks!
As you have 2 Flexible in a column , it will take up all the space. You can try removing Flexible from Text.

Solving the exception: The getter 'iterator' was called on null

I'm trying to solve an issue where I get the runtime exception:
════════ Exception caught by widgets library ═══════════════════════════════════════════════════════
The getter 'iterator' was called on null.
Receiver: null
Tried calling: iterator
The relevant error-causing widget was:
RecordTile AndroidStudioProjects/abc/lib/screens/clinical_records_screen.dart:80:31
════════════════════════════════════════════════════════════════════════════════════════════════════
The exception is in the line:
return loading
? LoadingAnimation()
: RecordTile(
records: clinical_records,
position: position,
);
and is being generated even after I have wrapped it in a try/catch block. How do I get to the root of this? I tried checking whether an element in the list is null.
My code is as follows:
class PreviousRecordsSummaryScreen extends StatefulWidget {
static const String id = 'ClinicalRecordsScreen';
List<CheckinNew> clinical_records;
Customer customer;
PreviousRecordsSummaryScreen({
this.clinical_records,
this.customer,
});
#override
_PreviousRecordsSummaryScreenState createState() =>
_PreviousRecordsSummaryScreenState();
}
class _PreviousRecordsSummaryScreenState
extends State<PreviousRecordsSummaryScreen> {
List<CheckinNew> clinical_records;
Customer customer;
bool loading;
List<CheckinNew> sortedRecords(List<CheckinNew> initialList) {
print("Sorting by date.. newest first");
List<CheckinNew> sortList = initialList;
sortList.sort((b, a) => a.actualDate.compareTo(b.actualDate));
print(sortList); // [two, four, three]
return sortList;
}
#override
void initState() {
clinical_records = widget.clinical_records;
customer = widget.customer;
print("Customer: ${customer.name} ${customer.cstid}");
loading = false;
super.initState();
}
#override
Widget build(BuildContext context) {
print("In ClinicalRecordsScreen");
print("clinical_records is $clinical_records");
clinical_records = sortedRecords(clinical_records);
return Scaffold(
appBar: AppBar(title: Text("${widget.customer.name}'s visits")),
body: Container(
child: ListView(
shrinkWrap: true,
children: [
clinical_records == null
? Container()
: ListView.builder(
shrinkWrap: true,
itemCount: (clinical_records == null)
? 0
: clinical_records.length,
itemBuilder: (BuildContext context, int position) {
print(
"Before calling RecordTile, position:$position clinical_records:$clinical_records ");
print("Printing each element");
int index = 0;
for (CheckinNew el in clinical_records) {
print("el: $el $index");
index++;
}
try {
return loading
? LoadingAnimation()
: RecordTile(
records: clinical_records,
position: position,
);
} catch (e) {
return Container();
}
},
),
],
),
),
);
}
}
class RecordTile extends StatefulWidget {
RecordTile({
this.records,
this.position,
this.expanded = false,
});
List<CheckinNew> records;
int position;
bool expanded;
#override
_RecordTileState createState() => _RecordTileState();
}
class _RecordTileState extends State<RecordTile> {
bool expanded;
List<CheckinNew> records;
int position;
#override
void initState() {
expanded = widget.expanded;
records = widget.records;
position = widget.position;
super.initState();
}
Widget medicines(List<MedsResponse> meds) {
List<Widget> mylist = [];
for (MedsResponse presc in meds) {
print(presc.brand);
mylist.add(Divider(
height: 10,
));
mylist.add(
Wrap(
alignment: WrapAlignment.start,
crossAxisAlignment: WrapCrossAlignment.start,
children: [
CircleAvatar(
radius: 10,
backgroundColor: Colors.grey.shade800,
child: Text((meds.indexOf(presc) + 1).toString()),
),
Padding(
padding: EdgeInsets.symmetric(horizontal: 2),
),
Text(
presc.brand,
style: TextStyle(color: Colors.black),
),
],
),
);
mylist.add(
Padding(
padding: EdgeInsets.symmetric(vertical: 2),
),
);
mylist.add(
Row(
children: [
Padding(padding: EdgeInsets.symmetric(horizontal: 10)),
Text(
'${presc.dose} ${presc.unit} ${presc.freq} x ${presc.durn} ${presc.durnunit}',
style: TextStyle(color: Colors.black),
),
],
),
);
mylist.add(
Padding(
padding: EdgeInsets.symmetric(vertical: 2),
),
);
mylist.add(
Wrap(
children: [
Padding(padding: EdgeInsets.symmetric(horizontal: 10)),
Text(
'(${presc.generic})',
style: TextStyle(
color: Colors.black,
),
),
],
),
);
mylist.add(
Padding(
padding: EdgeInsets.symmetric(vertical: 2),
),
);
}
return Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: mylist,
);
}
#override
Widget build(BuildContext context) {
print("Is expanded");
return Card(
child: InkWell(
onTap: () {
print("expanded is $expanded");
setState(() {
expanded = !expanded;
});
},
child: ListTile(
title: Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: [
Text(
'${widget.records[widget.position].dateLong()} ${widget.records[widget.position].time}'),
Text('Checkin #${records[widget.position].checkinno}'),
],
),
subtitle: !expanded
? Text(
records[position].clinicalRecord.diagnosis_as_string(),
textAlign: TextAlign.left,
style: TextStyle(
// color: Colors.white,
),
)
: Container(
// color: Colors.pink,
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Padding(padding: EdgeInsets.symmetric(vertical: 3)),
Text(
records[position].clinicalRecord.diagnosis_as_string(),
textAlign: TextAlign.left,
style: TextStyle(
color: Colors.black,
decorationThickness: 10,
),
),
Padding(padding: EdgeInsets.symmetric(vertical: 3)),
Text(records[position].clinicalRecord.history),
Padding(padding: EdgeInsets.symmetric(vertical: 3)),
Text(records[position].clinicalRecord.examination),
Padding(padding: EdgeInsets.symmetric(vertical: 3)),
Text("Treatment:"),
medicines(records[position].prescribedDrugs),
],
),
),
),
),
);
}
}

How to implement a network data fetch and then display in checkbox in alert inside showDialog with setState

I couldn't find a way to fetch data from network API inside showDialog > StatefulBuilder > AlertDialog. After fetching, this data should display in checkboxes and then finally on click ok, the selected checkboxes data is returned to the parent widget. There are more states other than these checkbox states in the alert. But the Navigator.of(context).pop() can return only single value.
Is there a way to rebuild the StatefulBuilder with setState on parent widget. Or some easy hack to rebuild the StatefulBuilder from an outside function like fetchOrderStatus() in the below code. (might be possible with a key on StatefulBuilder, but don't know how).
Below is my code
import 'package:flutter/material.dart';
import 'package:flutter/services.dart';
import 'package:flutter_spinkit/flutter_spinkit.dart';
import 'package:http/http.dart' as http;
import 'package:intl/intl.dart';
import 'dart:convert';
import 'package:recase/recase.dart';
import 'package:woocommerceadmin/src/orders/widgets/OrderDetailsPage.dart';
import 'package:barcode_scan/barcode_scan.dart';
class OrdersListPage extends StatefulWidget {
final String baseurl;
final String username;
final String password;
OrdersListPage({
Key key,
#required this.baseurl,
#required this.username,
#required this.password,
}) : super(key: key);
#override
_OrdersListPageState createState() => _OrdersListPageState();
}
class _OrdersListPageState extends State<OrdersListPage> {
String baseurl;
String username;
String password;
List ordersListData = List();
int page = 1;
bool hasMoreToLoad = true;
bool isListLoading = false;
bool isSearching = false;
String searchValue = "";
String sortOrderByValue = "date";
String sortOrderValue = "desc";
bool isOrderStatusOptionsReady = false;
bool isOrderStatusOptionsError = false;
String orderStatusOptionsError;
Map<String, bool> orderStatusOptions = {};
final scaffoldKey = new GlobalKey<ScaffoldState>();
final GlobalKey<RefreshIndicatorState> _refreshIndicatorKey =
new GlobalKey<RefreshIndicatorState>();
#override
void initState() {
super.initState();
baseurl = widget.baseurl;
username = widget.username;
password = widget.password;
fetchOrdersList();
}
#override
Widget build(BuildContext context) {
return Scaffold(
key: scaffoldKey,
appBar: _myAppBar(),
body: RefreshIndicator(
key: _refreshIndicatorKey,
onRefresh: handleRefresh,
child: Column(
children: <Widget>[
Expanded(
child: NotificationListener<ScrollNotification>(
onNotification: (ScrollNotification scrollInfo) {
if (hasMoreToLoad &&
!isListLoading &&
scrollInfo.metrics.pixels ==
scrollInfo.metrics.maxScrollExtent) {
handleLoadMore();
}
},
child: ListView.builder(
itemCount: ordersListData.length,
itemBuilder: (BuildContext context, int index) {
return Card(
child: InkWell(
onTap: () {
Navigator.push(
context,
MaterialPageRoute(
builder: (context) => OrderDetailsPage(
id: ordersListData[index]["id"],
),
),
);
},
child: Row(
mainAxisAlignment: MainAxisAlignment.start,
mainAxisSize: MainAxisSize.max,
crossAxisAlignment: CrossAxisAlignment.center,
children: <Widget>[
Expanded(
child: Padding(
padding: const EdgeInsets.all(10.0),
child: Column(
mainAxisAlignment:
MainAxisAlignment.start,
mainAxisSize: MainAxisSize.min,
crossAxisAlignment:
CrossAxisAlignment.start,
children: <Widget>[
_orderDate(ordersListData[index]),
_orderIdAndBillingName(
ordersListData[index]),
_orderStatus(ordersListData[index]),
_orderTotal(ordersListData[index])
]),
),
)
]),
),
);
}),
),
),
if (isListLoading)
Container(
height: 60.0,
color: Colors.white,
child: Center(
child: SpinKitFadingCube(
color: Colors.purple,
size: 30.0,
)),
),
],
),
),
);
}
Future<void> fetchOrderStatus() async {
String url =
"$baseurl/wp-json/wc/v3/reports/orders/totals?consumer_key=$username&consumer_secret=$password";
setState(() {
isOrderStatusOptionsReady = false;
isOrderStatusOptionsError = false;
});
dynamic response;
try {
response = await http.get(url);
if (response.statusCode == 200) {
if (json.decode(response.body) is List &&
!json.decode(response.body).isEmpty) {
json.decode(response.body).forEach((item) {
if (item is Map) {
orderStatusOptions.putIfAbsent(item["slug"], () => false);
}
});
setState(() {
isOrderStatusOptionsReady = true;
});
} else {
setState(() {
isOrderStatusOptionsReady = false;
isOrderStatusOptionsError = true;
orderStatusOptionsError = "Failed to fetch order status options";
});
}
} else {
String errorCode = "";
if (json.decode(response.body) is Map &&
json.decode(response.body).containsKey("code") &&
json.decode(response.body)["code"] is String) {
errorCode = json.decode(response.body)["code"];
}
setState(() {
isOrderStatusOptionsReady = false;
isOrderStatusOptionsError = true;
orderStatusOptionsError =
"Failed to fetch order status options. Error: $errorCode";
});
}
} catch (e) {
setState(() {
isOrderStatusOptionsReady = false;
isOrderStatusOptionsError = true;
orderStatusOptionsError =
"Failed to fetch order status options. Error: $e";
});
}
}
Widget _myAppBar() {
Widget myAppBar;
myAppBar = AppBar(
title: Text("Orders List"),
actions: <Widget>[
GestureDetector(
child: Padding(
padding: const EdgeInsets.only(left: 10),
child: Icon(Icons.search),
),
onTap: () {
setState(() {
isSearching = !isSearching;
});
},
),
GestureDetector(
child: Padding(
padding: const EdgeInsets.symmetric(horizontal: 10),
child: Icon(Icons.filter_list),
),
onTap: _orderFilter,
),
],
);
}
return myAppBar;
}
void _orderFilter() async {
showDialog(
context: context,
barrierDismissible: false,
builder: (BuildContext context){
// fetchOrderStatus();
return StatefulBuilder(builder: (context, StateSetter setState) {
return AlertDialog(
title: Text("Sort & Filter"),
titlePadding: EdgeInsets.fromLTRB(15, 20, 15, 0),
content: Container(
width: 300,
height: 400,
child: SingleChildScrollView(
child: Column(
mainAxisAlignment: MainAxisAlignment.start,
crossAxisAlignment: CrossAxisAlignment.start,
children: <Widget>[
Text(
"Sort by",
style: Theme.of(context).textTheme.subhead,
),
Row(
children: <Widget>[
Expanded(
child: Container(
child: DropdownButton<String>(
underline: SizedBox.shrink(),
value: sortOrderByValue,
onChanged: (String newValue) {
FocusScope.of(context)
.requestFocus(FocusNode());
setState(() {
sortOrderByValue = newValue;
});
},
items: <String>[
"date",
"id",
"title",
"slug",
"include"
].map<DropdownMenuItem<String>>((String value) {
return DropdownMenuItem<String>(
value: value,
child: Text(
value.titleCase,
textAlign: TextAlign.center,
style: Theme.of(context).textTheme.body1,
),
);
}).toList(),
),
),
),
InkWell(
child: Padding(
padding:
const EdgeInsets.symmetric(horizontal: 10),
child: Icon(
Icons.arrow_downward,
color: (sortOrderValue == "desc")
? Theme.of(context).primaryColor
: Colors.black,
),
),
onTap: () {
setState(() {
sortOrderValue = "desc";
});
},
),
InkWell(
child: Padding(
padding:
const EdgeInsets.symmetric(horizontal: 10),
child: Icon(
Icons.arrow_upward,
color: (sortOrderValue == "asc")
? Theme.of(context).primaryColor
: Colors.black,
),
),
onTap: () {
setState(() {
sortOrderValue = "asc";
});
},
),
],
),
Text(
"Filter by",
style: Theme.of(context).textTheme.subhead,
),
Text(
"Order Status",
style: Theme.of(context).textTheme.body1.copyWith(
fontWeight: FontWeight.bold, fontSize: 16),
),
isOrderStatusOptionsReady
? ListView(
children:
orderStatusOptions.keys.map((String key) {
return new CheckboxListTile(
title: Text(key),
value: orderStatusOptions[key],
onChanged: (bool value) {
setState(() {
orderStatusOptions[key] = value;
});
},
);
}).toList(),
)
: Container(
child: Center(
child: SpinKitFadingCube(
color: Theme.of(context).primaryColor,
size: 30.0,
),
),
)
],
),
),
),
contentPadding: EdgeInsets.fromLTRB(15, 10, 15, 0),
actions: <Widget>[
FlatButton(
child: Text("Close"),
onPressed: () {
Navigator.of(context).pop();
},
),
FlatButton(
child: Text("Ok"),
onPressed: () {
Navigator.of(context).pop();
},
)
],
);
});
});
}
}
You have a couple of options
Fetch the data before showing the dialog. Using either async/await keywords or .then, you wait for the fetching of data to complete, then use the data in the dialog
void _orderFilter() async {
await fetchOrderStatus();
showDialog(...); //Use the response in the dialog
}
Create a new stateful widget for the dialog and have fetchOrderStatus() be a method in that class. This allows you to have more control over what to display as well as state changes in the dialog.
#gaurav-jain I have followed your question from Github where you asked a question about my answer to this problem. How I did it is i have a button that when clicked immediately opens the dialog that then waits for the Future to load data from the API: The async function _showOptions() renders the dialog that renders a list of checkboxes with options fetched from API:
new RaisedButton(
color: Colors.green,
padding: EdgeInsets.all(20.0),
onPressed: () {
if (state._isLoading){
// don't do anything when form is submitting and this button is pressed again
return null;
}
else {
if (state._formKey.currentState.validate()) {
state._showOptions().then((selected){
print(state.selectedOptions);
if (state.selectedOptions.isNotEmpty) {
_submitForm();
}
else {
showDialog(
context: context,
builder: (context) {
return AlertDialog(
content: Text('You have not selected any option'),
actions: <Widget>[
FlatButton(
child: Text('OK'),
onPressed: () {
Navigator.of(context, rootNavigator: true).pop();
},
),
],
);
}
);
}
});
}
else {
Scaffold.of(context).showSnackBar(
SnackBar(
content: Text('Your form has errors. Rectify them and submit again'),
)
);
}
}
},
child: Text(state._isLoading ? 'Submitting...' : 'Submit', style: new TextStyle(color: Colors.white)),
),
The state here is the parent widget's state, but the async dialog has it's own state to make it work. You can refer back to the github comment for the other pieces of the code here https://github.com/flutter/flutter/issues/15194#issuecomment-450490409
As suggested by #wxker, 2nd approach, I have implemented other stateful widget which returns AlertDialog.
Parent Widget Calling showDialog on tap:
IconButton(
icon: Icon(Icons.filter_list),
onPressed: () {
showDialog(
context: context,
barrierDismissible: false,
builder: (BuildContext context) {
return OrdersListFiltersModal(
baseurl: widget.baseurl,
username: widget.username,
password: widget.password,
sortOrderByValue: sortOrderByValue,
sortOrderValue: sortOrderValue,
orderStatusOptions: orderStatusOptions,
onSubmit:
(sortOrderByValue, sortOrderValue, orderStatusOptions) {
setState(() {
this.sortOrderByValue = sortOrderByValue;
this.sortOrderValue = sortOrderValue;
this.orderStatusOptions = orderStatusOptions;
});
handleRefresh();
},
);
},
);
},
),
Child stateful widget returning alert with default value and function callback in constructor to change parent widget state.
Widget build(BuildContext context) {
return AlertDialog(
title: Text("Sort & Filter"),
titlePadding: EdgeInsets.fromLTRB(15, 20, 15, 0),
content: Container(
height: 400,
child: SingleChildScrollView(
child: Column(
mainAxisAlignment: MainAxisAlignment.start,
crossAxisAlignment: CrossAxisAlignment.start,
children: <Widget>[
Text(
"Sort by",
style: Theme.of(context).textTheme.subhead,
),
Padding(
padding: const EdgeInsets.fromLTRB(10, 0, 10, 0),
child: Row(
children: <Widget>[
Expanded(
child: Container(
child: DropdownButton<String>(
underline: SizedBox.shrink(),
value: sortOrderByValue,
onChanged: (String newValue) {
FocusScope.of(context).requestFocus(FocusNode());
setState(() {
sortOrderByValue = newValue;
});
},
items: <String>[
"date",
"id",
"title",
"slug",
"include"
].map<DropdownMenuItem<String>>((String value) {
return DropdownMenuItem<String>(
value: value,
child: Text(
value.titleCase,
textAlign: TextAlign.center,
style: Theme.of(context).textTheme.body1,
),
);
}).toList(),
),
),
),
InkWell(
child: Padding(
padding: const EdgeInsets.symmetric(horizontal: 10),
child: Icon(
Icons.arrow_downward,
color: (sortOrderValue == "desc")
? Theme.of(context).primaryColor
: Colors.black,
),
),
onTap: () {
setState(() {
sortOrderValue = "desc";
});
},
),
InkWell(
child: Padding(
padding: const EdgeInsets.symmetric(horizontal: 10),
child: Icon(
Icons.arrow_upward,
color: (sortOrderValue == "asc")
? Theme.of(context).primaryColor
: Colors.black,
),
),
onTap: () {
setState(() {
sortOrderValue = "asc";
});
},
),
],
),
),
Text(
"Filter by",
style: Theme.of(context).textTheme.subhead,
),
SizedBox(
height: 10,
),
Padding(
padding: const EdgeInsets.only(left: 10),
child: Text(
"Order Status",
style: Theme.of(context)
.textTheme
.body1
.copyWith(fontWeight: FontWeight.bold, fontSize: 16),
),
),
SizedBox(
height: 10,
),
isOrderStatusOptionsError
? Row(
children: <Widget>[
Expanded(
child: Padding(
padding: const EdgeInsets.fromLTRB(10, 10, 10, 10),
child: Text(
orderStatusOptionsError,
style: Theme.of(context).textTheme.body1,
),
),
),
],
)
: isOrderStatusOptionsReady
? Column(
children: orderStatusOptions.keys.map((String key) {
return GestureDetector(
onTap: () {
setState(() {
orderStatusOptions[key] =
!orderStatusOptions[key];
});
},
child: Container(
color: Colors.transparent,
height: 30,
child: Row(
children: <Widget>[
Checkbox(
value: orderStatusOptions[key],
onChanged: (bool value) {
setState(() {
orderStatusOptions[key] = value;
});
},
),
Expanded(
child: Text(
key.titleCase,
style:
Theme.of(context).textTheme.body1,
),
),
],
),
),
);
}).toList(),
)
: Container(
padding: EdgeInsets.fromLTRB(0, 20, 0, 0),
child: Center(
child: SpinKitPulse(
color: Theme.of(context).primaryColor,
size: 50,
),
),
)
],
),
),
),
contentPadding: EdgeInsets.fromLTRB(15, 10, 15, 0),
actions: <Widget>[
FlatButton(
child: Text("Close"),
onPressed: () {
Navigator.of(context).pop();
},
),
FlatButton(
child: Text("Ok"),
onPressed: () {
widget.onSubmit(
sortOrderByValue, sortOrderValue, orderStatusOptions);
Navigator.of(context).pop();
},
)
],
);
}
Future<void> fetchOrderStatusOptions() async {
String url =
"${widget.baseurl}?consumer_key=${widget.username}&consumer_secret=${widget.password}";
setState(() {
isOrderStatusOptionsReady = false;
isOrderStatusOptionsError = false;
});
http.Response response;
try {
response = await http.get(url);
if (response.statusCode == 200) {
if (json.decode(response.body) is List &&
!json.decode(response.body).isEmpty) {
Map<String, bool> tempMap = orderStatusOptions;
json.decode(response.body).forEach((item) {
if (item is Map &&
item.containsKey("slug") &&
item["slug"] is String &&
item["slug"].isNotEmpty) {
tempMap.putIfAbsent(item["slug"], () => false);
}
});
setState(() {
isOrderStatusOptionsReady = true;
orderStatusOptions = tempMap;
});
} else {
setState(() {
isOrderStatusOptionsReady = false;
isOrderStatusOptionsError = true;
orderStatusOptionsError = "Failed to fetch order status options";
});
}
} else {
String errorCode = "";
if (json.decode(response.body) is Map &&
json.decode(response.body).containsKey("code") &&
json.decode(response.body)["code"] is String) {
errorCode = json.decode(response.body)["code"];
}
setState(() {
isOrderStatusOptionsReady = false;
isOrderStatusOptionsError = true;
orderStatusOptionsError =
"Failed to fetch order status options. Error: $errorCode";
});
}
} catch (e) {
setState(() {
isOrderStatusOptionsReady = false;
isOrderStatusOptionsError = true;
orderStatusOptionsError =
"Failed to fetch order status options. Error: $e";
});
}
}
}

RangeError (index): Invalid value: Only valid value is 0: 1

I am new to flutter. I am trying to add the list of data to the view. The list of data have different sets of order items with different lengths. I am getting the data from the API but due to the different length of order data, I am getting the error as the image below. I have the json api as follows:
{
"status":"success",
"message":"Data Fetched",
"data":{
"list":[
{
"id":27,
"order_code":"7wfelnkhuodlbvdseley1",
"chef_id":1,
"user_id":1,
"order_status":1,
"status":1,
"order_datetime":"2020-01-21 18:05:00",
"user_location_id":1,
"price":1600,
"coupon_id":null,
"use_coupon":0,
"discount":0,
"final_price":1600,
"vat_amt":208,
"delivery_charge_amt":0,
"payment_id":1,
"delivery_time":null,
"delivery_user_id":null,
"payment_status":0,
"payment_price":null,
"payment_time":null,
"reject_message":null,
"created_at":"2020-01-21 18:05:00",
"updated_at":"2020-01-21 18:07:46",
"orderdata":[
{
"id":30,
"order_code_id":27,
"chef_id":1,
"food_id":17,
"user_id":1,
"additional_info":null,
"food_qty":4,
"instruction":null,
"price":400,
"food":{
"id":17,
"name":"Prawns Chilli",
"description":"<p>Seared prawns smothered in a spicy, sticky Asian sauce,
these Asian Chilli Garlic Prawns will have you smacking your
lips in utter satisfaction, feeling like you’ve just dined at a fancy modern
Thai restaurant.<wbr /> </p>",
"ingredient_detail":null,
"price":500,
"discount_price":400,
"ribbon_text":null,
"image":"1576657695-prawn chilli3.jpg",
"banner_image":null,
"published_date":"2019-12-18",
"is_offer":1
}
}
]
},
{
"id":29,
"order_code":"lzquyrmthdahxmjm81ja1",
"chef_id":1,
"user_id":1,
"order_status":1,
"status":1,
"order_datetime":"2020-01-21 19:17:52",
"user_location_id":1,
"price":280,
"coupon_id":null,
"use_coupon":0,
"discount":0,
"final_price":280,
"vat_amt":36.4,
"delivery_charge_amt":50,
"payment_id":1,
"delivery_time":null,
"delivery_user_id":null,
"payment_status":0,
"payment_price":null,
"payment_time":null,
"reject_message":null,
"created_at":"2020-01-21 19:17:52",
"updated_at":"2020-01-21 19:18:30",
"orderdata":[
{
"id":33,
"order_code_id":29,
"chef_id":1,
"food_id":11,
"user_id":1,
"additional_info":null,
"food_qty":2,
"instruction":null,
"price":250,
"food":{
"id":11,
"name":"Chicken burger",
"description":"<p>The juicyness of the meat will make you feel heaven.</p>",
"ingredient_detail":null,
"price":300,
"discount_price":250,
"ribbon_text":null,
"image":"1576654603-chick burger.jpg",
"banner_image":null,
"published_date":"2019-12-18",
"is_offer":1
}
},
{
"id":34,
"order_code_id":29,
"chef_id":1,
"food_id":4,
"user_id":1,
"additional_info":null,
"food_qty":2,
"instruction":null,
"price":140,
"food":{
"id":4,
"name":"Momo",
"description":"<p>This juicy steamed momos are prepared from the ground water buffalo meat and are called \"Buff momo\". The wrappers are very thinly rolled and the filling is deliciously spicy and juicy, served along with tangy yellow chutney and classic spicy tomato sauce that compliments the taste of steamed momos.</p>",
"ingredient_detail":"<p>Tomato, Ground meat ,Flour, Chilli pepper, Garlic, Ginger, Scallion, Black pepper and Soy sauce</p>",
"price":150,
"discount_price":140,
"ribbon_text":null,
"image":"1576651666-momo.jpg",
"banner_image":null,
"published_date":"2019-11-18",
"is_offer":1
}
}
]
}
]
}
}
My Full Widget Code:
class OnProcessPage extends StatefulWidget {
#override
State<StatefulWidget> createState() {
return _OnProcessTile();
}
}
class _OnProcessTile extends State<OnProcessPage> {
bool _isLoading = false;
List<ListD> foodData = [];
List<Orderdata> orderData = [];
SharedPreferences sharedPreferences;
#override
void initState() {
super.initState();
setState(() {
_isLoading = true;
});
getPrefs();
getOnProcessRequest();
}
#override
Widget build(BuildContext context) {
return Center(
child: Stack(
children: <Widget>[
Opacity(
opacity: _isLoading
? 0.3
: 1, // You can reduce this when loading to give different effect
child: AbsorbPointer(
absorbing: _isLoading,
child: _buildCardList(context),
),
),
Opacity(
opacity: _isLoading ? 1.0 : 0,
child: Center(
child: CircularProgressIndicator(
backgroundColor: Theme.of(context).primaryColor,
),
)),
],
));
}
Widget _buildCardList(BuildContext context) {
return ListView.builder(
itemCount: foodData == null ? 0 : foodData.length,
itemBuilder: (context, int index) {
return Wrap(
children: <Widget>[
Container(
margin: EdgeInsets.only(bottom: 10),
child: Card(
child: Column(
children: <Widget>[
_buildCardView(context, index),
_cardBottomView(context, index)
],
)))
],
);
});
}
Widget _buildCardView(BuildContext context, int index) {
return Wrap(
children: <Widget>[
Container(
child: Container(
margin: EdgeInsets.all(10.0),
child: Column(
children: <Widget>[
_cardTopSection(context, index),
_cardMiddleSection(context, index),
_cardTotalPrice(context, index),
Container(
height: 1,
color: Color.fromRGBO(232, 232, 232, 1),
),
],
),
),
),
],
);
}
Widget _cardTotalPrice(BuildContext context, int i) {
return Container(
margin: EdgeInsets.only(bottom: 5.0),
child: Padding(
padding: EdgeInsets.only(top: 3, bottom: 3),
child: Row(
children: <Widget>[
Expanded(
child: Text(""),
),
Expanded(
child: Text("Total",
style: TextStyle(
fontWeight: FontWeight.bold,
fontSize: 18,
)),
),
Expanded(
child: Text(
"${foodData[i].finalPrice}",
style: TextStyle(
fontWeight: FontWeight.bold,
fontSize: 18,
),
),
)
],
),
),
);
}
Widget _cardTopSection(BuildContext context, int index) {
return Container(
color: Color.fromRGBO(232, 232, 232, 1),
child: Row(
children: <Widget>[
_topLeftSection(index),
_topmiddleSection(index),
_toprightSection(index)
],
),
);
}
Widget _cardMiddleSection(BuildContext context, int i) {
return Container(
margin: EdgeInsets.only(top: 10.0),
child: ListView.builder(
shrinkWrap: true,
physics: const NeverScrollableScrollPhysics(),
itemCount:
foodData[i].orderdata == null ? 0 : foodData[i].orderdata.length,
itemBuilder: (context, i) {
print("Item Builder");
print(i);
print("Item Builder");
return _cardMiddleItems(i);
}),
);
}
Widget _cardMiddleItems(int i) {
print("Middle");
print(i);
print("Middle");
return Container(
margin: EdgeInsets.only(bottom: 5.0),
child: Padding(
padding: EdgeInsets.only(top: 3, bottom: 3),
child: Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: <Widget>[
Expanded(
child: Text("${orderData[i].food.name}"),
),
Expanded(
child: Text("${orderData[i].foodQty}"),
),
Expanded(
child: Text("${orderData[i].price}"),
),
],
),
),
);
}
Widget _topLeftSection(int index) {
return Container(
child: CircleAvatar(
backgroundImage: AssetImage('assets/images/momo.jpg'),
backgroundColor: Colors.lightGreen,
radius: 24.0,
),
);
}
Widget _topmiddleSection(int i) {
return Expanded(
child: Container(
child: Column(
children: <Widget>[
Text("Coldplay "),
Text("${foodData[i].createdAt}")
// new Text("Hi whatsup?"),
],
),
),
);
}
Widget _toprightSection(int index) {
return Expanded(
child: Container(
child: Column(
mainAxisAlignment: MainAxisAlignment.spaceAround,
children: <Widget>[
Text(
"#" + "${foodData[index].id}",
style: TextStyle(color: Colors.black, fontSize: 18.0),
),
],
),
),
);
}
Widget _cardBottomView(BuildContext context, int index) {
return Row(
mainAxisAlignment: MainAxisAlignment.spaceEvenly,
crossAxisAlignment: CrossAxisAlignment.center,
children: <Widget>[
Text(
"Order Status",
style: TextStyle(fontSize: 18),
),
RaisedButton(
onPressed: () => {},
color: Colors.green,
child: Text(
"Cooking",
style: TextStyle(color: Colors.white),
),
),
RaisedButton(
onPressed: () => {
sharedPreferences.setBool('process', true),
sharedPreferences.setBool('new', false),
Navigator.push(
context,
MaterialPageRoute(
builder: (BuildContext context) => RequestDetails(),
settings: RouteSettings(
arguments: foodData[index],
),
))
},
color: Theme.of(context).primaryColor,
child: Text("Details", style: TextStyle(color: Colors.white)),
),
],
);
}
Future<NewRequestResponse> getOnProcessRequest() async {
print("OnProcess");
SharedPreferences sharedPreferences = await SharedPreferences.getInstance();
String token = sharedPreferences.getString("api_token");
Map<String, String> headers = {"Authorization": token};
var jsonResponse;
NewRequestResponse newRequestResponse;
var response = await http.post(
"url",
headers: headers);
if (response.statusCode == 200) {
print("Onprocess Inside 200");
jsonResponse = json.decode(response.body);
print(jsonResponse);
if (jsonResponse != null) {
newRequestResponse = new NewRequestResponse.fromJson(jsonResponse);
print(newRequestResponse);
setState(() {
foodData = newRequestResponse.data.list;
for (int i = 0; i < foodData.length; i++) {
orderData = foodData[i].orderdata;
}
});
setState(() {
_isLoading = false;
});
return newRequestResponse;
} else {
setState(() {
_isLoading = false;
});
return null;
}
} else {
setState(() {
_isLoading = false;
});
jsonResponse = json.decode(response.body.toString());
newRequestResponse = new NewRequestResponse.fromJson(jsonResponse);
print("onProcessRequest outside 200");
return newRequestResponse;
}
}
void getPrefs() async {
sharedPreferences = await SharedPreferences.getInstance();
}
}
It's a little bit tricky, so I hope I can understand the bug, but to me the problem seems to be on your getOnProcessRequest at this line:
for (int i = 0; i < foodData.length; i++) {
orderData = foodData[i].orderdata;
}
you're updating (and overwriting) orderData at each cycle of foodData. I don't think it is the right thing to do.
As much as I understood what should happen is
for each foodData, get the list of orderData.
But foodData is a list, and orderData too. You should, I think, link these items, so that for each foodData you can access to its own orderData list.
EDIT:
I cannot provide a full solution since would take me too much honestly.
But I came up with an example
final Map<String, dynamic> myApi = {
"list": [
{
"id": 1,
"orderdata": [
{"title": "food1"},
{"title": "food2"}
]
},
{
"id": 2,
"orderdata": [
{"title": "food3"},
{"title": "food4"}
]
},
]
};
class OrderData {
final String title;
OrderData(this.title);
#override
String toString() {
return title;
}
}
class FoodData {
final int id;
final List<OrderData> orderData;
FoodData(this.id, this.orderData);
}
void main() {
final tmpMap = (myApi['list'] as List<Map<String, Object>>);
print(tmpMap);
List<FoodData> myList = tmpMap.map<FoodData>((elem) {
final value = elem;
final _id = value['id'] as int;
final List<OrderData> _orderData =
(value['orderdata'] as List<Map<String, String>>)
.map<OrderData>((order) => OrderData(order['title']))
.toList();
return FoodData(_id, _orderData);
}).toList();
print(myList.length);
for (int i = 0; i < myList.length; i++) {
for (int j = 0; j < myList[i].orderData.length; j++) {
print("i: $i, j: $j, elem: ${myList[i].orderData[j]}");
}
}
}
Now at the end what you need is to change your getOnProcessRequest, using a single list, so delete your orderdata, and use only fooddata, and now your foodData would have for each element (food) also a internal rappresentation of the order.
When you want to instanciate the ListView.builder:
For the first (outer ListView) you would use fooddata.length
for the second (inner ListView) you would use foodata[i].orders.length
Hope this would help you with find your right solution