I wants to reload and update the widget in flutter when clicked on action button of snack bar - flutter

This is my code where I am showing list of pending invoices.But when i accept the it should reload the pending invoice widget after clicking on snack bar action button.When user clicks on accept button its showing snack bar and when pressed accept action button snack bar it navigate back to same widget where I needs to update that widget along with new list of pending invoices without the invoice which we have accepted
class PendingInvoiceWidget extends StatelessWidget {
final double maxWidth;
final double maxHeight;
final bool isOverdue;
final String amount;
final String savedAmount;
final String invoiceDate;
final String dueDate;
Invoice fullDetails;
int index;
bool userConsentGiven = false;
final String companyName;
PendingInvoiceWidget(
{required this.fullDetails,
required this.maxWidth,
required this.maxHeight,
required this.amount,
required this.savedAmount,
required this.index,
required this.invoiceDate,
required this.dueDate,
required this.companyName,
required this.isOverdue});
#override
Widget build(BuildContext context) {
TextEditingController acceptController = TextEditingController();
TextEditingController rejectController = TextEditingController();
List<Invoice> pendingInvoice =
Provider.of<TransactionManager>(context).pendingInvoice;
String? invoiceId = fullDetails.sId;
String invAmt = double.parse(amount).toStringAsFixed(2);
DateTime id = DateTime.parse(invoiceDate);
DateTime dd = DateTime.parse(dueDate);
DateTime currentDate = DateTime.now();
Duration dif = dd.difference(currentDate);
int daysLeft = dif.inDays;
String idueDate = DateFormat("dd-MMM-yyyy").format(dd);
String invDate = DateFormat("dd-MMM-yyyy").format(id);
double h1p = maxHeight * 0.01;
double h10p = maxHeight * 0.1;
double w10p = maxWidth * 0.1;
return ProgressHUD(child: Builder(builder: (context) {
final progress = ProgressHUD.of(context);
return ExpandableNotifier(
child: Padding(
padding: EdgeInsets.symmetric(horizontal: w10p * .5, vertical: 10),
child: Expandable(
collapsed: ExpandableButton(
child: Card(
child: Container(
padding: EdgeInsets.all(10),
decoration: BoxDecoration(
borderRadius: BorderRadius.circular(10),
color: Colours.offWhite,
),
child: Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: [
Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
isOverdue
? Padding(
padding: EdgeInsets.symmetric(
vertical: h1p * 1),
child: Container(
// height: h1p * 4.5,
// width: w10p * 1.7,
decoration: BoxDecoration(
borderRadius:
BorderRadius.circular(5),
color: Colours.failPrimary,
),
child: const Center(
child: Padding(
padding: EdgeInsets.all(4),
child: Text(
"Overdue",
style: TextStyles.overdue,
),
),
),
),
)
: Container(),
Row(
children: [
Text(
"${fullDetails.invoiceNumber}",
style: TextStyles.textStyle6,
),
SvgPicture.asset(
"assets/images/home_images/arrow-circle-right.svg"),
],
),
expanded: Column(
children: [
ExpandableButton(
child: Card(
child: Container(
padding: EdgeInsets.all(10),
decoration: BoxDecoration(
borderRadius: BorderRadius.circular(10),
color: Colours.offWhite,
),
Column(
crossAxisAlignment: CrossAxisAlignment.end,
children: [
isOverdue
? Text(
"$daysLeft days Overdue",
style: TextStyles.textStyle57,
)
: Text(
"$daysLeft days left",
style: TextStyles.textStyle57,
),
Text(
"₹ $invAmt",
style: TextStyles.textStyle58,
),
],
)
]),
),
),
),
Card(
elevation: .5,
child: Padding(
padding: const EdgeInsets.all(10.0),
child: Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: [
Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
const SizedBox(
height: 4,
),
const Text(
"Invoice Date",
style: TextStyles.textStyle62,
),
Text(
invDate,
style: TextStyles.textStyle63,
),
// Text(
// companyName,
// style: TextStyles.companyName,
// ),
],
),
SvgPicture.asset("assets/images/arrow.svg"),
Column(
crossAxisAlignment: CrossAxisAlignment.end,
children: [
const SizedBox(
height: 4,
),
const Text(
"Due Date",
style: TextStyles.textStyle62,
),
Text(
idueDate,
style: TextStyles.textStyle63,
),
// Text(
// companyName,
// style: TextStyles.companyName,
// ),
],
),
],
),
),
),
Card(
child: Container(
padding: const EdgeInsets.all(10),
decoration: const BoxDecoration(),
child: Column(
children: [
Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: [
Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
const SizedBox(
height: 4,
),
const Text(
"Invoice Amount",
style: TextStyles.textStyle62,
),
Text(
"₹ $invAmt",
style: TextStyles.textStyle65,
)
// Text("Asian Paints",style: TextStyles.textStyle34,),
],
),
Column(
crossAxisAlignment: CrossAxisAlignment.end,
children: [
const SizedBox(
height: 4,
),
isOverdue
? const Text(
"Payabe Amount",
style: TextStyles.textStyle62,
)
: const Text(
"Pay Now",
style: TextStyles.textStyle62,
),
Text(
"₹ $invAmt",
style: TextStyles.textStyle66,
),
],
),
],
),
SizedBox(
height: h1p * 1.5,
),
// Row(
// mainAxisAlignment: MainAxisAlignment.start,
// children: [
// Column(
// crossAxisAlignment: CrossAxisAlignment.start,
// children: [
// isOverdue?
// const Text(
// "Interest",
// style: TextStyles.textStyle62,
// ):
// const Text(
// "You Save",
// style: TextStyles.textStyle62,
// ),
// Text(
// "₹ $savedAmount",
// style:isOverdue?
// TextStyles.textStyle73:
// TextStyles.textStyle77,
// ),
// ],
// ),
// ],
// ),
Padding(
padding: const EdgeInsets.symmetric(vertical: 10.0),
child: GestureDetector(
onTap: () {
progress!.show();
getIt<TransactionManager>()
.changeSelectedInvoice(fullDetails);
progress.dismiss();
Navigator.pushNamed(context, savemoreDetails);
},
child: Image.asset(
"assets/images/viewetails.png")),
),
Row(
children: [
Expanded(
child: InkWell(
onTap: () async {
showDialog(
context: context,
builder: (context) => Padding(
padding:
const EdgeInsets.symmetric(
// vertical: h10p * 5,
),
child: AlertDialog(
shape:
const RoundedRectangleBorder(
borderRadius:
BorderRadius.all(
Radius.circular(
10.0))),
title: Row(
children: const [
Center(
child: Text("Comment"),
),
],
),
const SizedBox(
width: 10,
),
Expanded(
child: InkWell(
onTap: () async {
showDialog(
context: context,
builder: (context) => Padding(
padding:
const EdgeInsets.symmetric(
// vertical: h10p * 4,
),
child: StatefulBuilder(
builder: (context, setState) {
return AlertDialog(
shape:
const RoundedRectangleBorder(
borderRadius:
BorderRadius.all(
Radius.circular(
10.0))),
title: const Center(
child: Text("Consent"),
),
content: Column(
mainAxisSize:
MainAxisSize.min,
children: [
Text(
"I agree and approve Xuriti and NBFC Ditya Finance Private Limited to disburse funds to yhe seller $companyName for invoice number -${fullDetails.invoiceNumber} on my behalf"),
TextField(
controller:
acceptController,
decoration:
const InputDecoration(
hintText:
"Leave a comment *"),
onChanged: (_) {
print(acceptController
.text);
acceptController
.text.isEmpty
? Row(
children: const [
Text(
"Please write a reason",
style: TextStyle(
color:
Colors.red),
),
],
)
: Container();
},
),
SizedBox(
height: h1p * 4,
),
InkWell(
onTap: () async {
userConsentGiven =
true;
String timeStamp =
DateTime.now()
.toString();
if (acceptController
.text
.isNotEmpty) {
progress!.show();
String? message = await getIt<
TransactionManager>()
.changeInvoiceStatus(
invoiceId,
"Confirmed",
index,
fullDetails,
timeStamp,
userConsentGiven,
acceptController
.text,
"This invoice has been confirmed and Xuriti and its financing partner is authorised to disburse funds to the seller as per the invoice generated on my behalf");
progress.dismiss();
ScaffoldMessenger
.of(context)
.showSnackBar(
SnackBar(
behavior:
SnackBarBehavior
.floating,
content:
Text(
message!,
style:
const TextStyle(color: Colors.green),
)));
} else {
Fluttertoast.showToast(
msg:
"Please write a reason",
textColor:
Colors.red);
}
Navigator.pop(
context,
);
},
child: Container(
height: h1p * 8,
width: w10p * 7.5,
decoration: BoxDecoration(
borderRadius:
BorderRadius
.circular(
6),
color: Colours
.pumpkin),
child: const Center(
child: Text(
"Accept",
style: TextStyles
.subHeading,
)),
),
),
],
),
);
}),
));
},
child: Container(
height: h1p * 9,
decoration: BoxDecoration(
color: Colours.successPrimary,
borderRadius: BorderRadius.circular(5)),
child: const Center(
child: Text(
"Accept",
style: TextStyles.textStyle46,
),
),
),
),
),
],
)
],
),
),
),
],
)),
),
);
}));
}
}

Please try to use this code as SnackBar and decorate you want,
ScaffoldMessenger.of(context).showSnackBar(SnackBar(
behavior: SnackBarBehavior.floating,
content: Text(
message!,
style: const TextStyle(color: Colors.green),
),
backgroundColor: Colors.black,
action: SnackBarAction(
label: "Reload",
onPressed: () {
setState(() {});
})));
I Hope these things are solve your issue,

Related

How to test this GestureDetector in Flutter with Patrol?

In my app, the widget tree is DeleteItem GestureDetector -> InvoiceTotal Widget -> ItemListWidget -> ListView -> EditInvoiceClass
I tried to use PatrolTest
await $(#InvoiceTotal).$(#DeleteItem).scrollTo().tap();
$.pumpAndSettle;
to test the DeleteItem GestureDetector but I got an Exception --
WaitUntilVisibleTimeoutException (TimeoutException after 0:00:10.000000: Finder exactly one widget with key \[\<'DeleteItem'\>\] that has ancestor(s) with key \[\<'InvoiceTotal'\>\] (ignoring all but first) (ignoring offstage widgets): GestureDetector-\[\<'DeleteItem'\>\](startBehavior: start, dependencies: \[MediaQuery, \_ScrollableScope\]) did not find any visible widgets)
I want to know why the DeleteItem GestureDetector is invisible? How can I fix the problem?
My Flutter code attached below.
// Code Segment of EditInvoice Class
return Consumer<ItemContents>(
builder: (context, value, child) {
return Consumer<DarkThemeProvider>(
builder: (context, value1, child1) {
return Expanded(
child: Column(
children: [
Padding(
padding:
EdgeInsets.symmetric(horizontal: myTheme.normalPadding),
child: const goBack(),
),
Expanded(
child: Scrollbar(
child: ListView(
padding: EdgeInsets.zero,
children: [
Padding(
padding: EdgeInsets.all(myTheme.normalPadding),
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
mainAxisSize: MainAxisSize.min,
children: [
ItemListWidget(
key: const Key('ItemListWidget'),
context: context,
myTextTheme: myTextTheme,
myTextColor: myTextColor,
value: value,
myTheme: myTheme,
invoicesContents: vm.invoicesData(
vm,
newAddModel,
editData,
index,
),
),
vm.isEmptyInvoice
? RichText(
text: TextSpan(
children: <TextSpan>[
TextSpan(
text: "New Invoice",
style: myTheme.textTheme(
"labelLarge", isDark),
),
],
),
)
: RichText(
text: TextSpan(
children: <TextSpan>[
TextSpan(
text: "Edit ",
style: myTheme.textTheme(
"labelLarge", isDark),
),
TextSpan(
text: "#",
style: myTheme.textTheme(
"labelSmall", isDark),
),
TextSpan(
text: vm.selectedItem.id,
style: myTheme.textTheme(
"labelLarge", isDark),
),
],
),
),
Padding(
padding: const EdgeInsets.symmetric(
vertical: 24.0),
child: Text(
'Bill From',
style: myTheme.textTheme("caption", isDark),
),
),
Form(
key: const Key('form'),
child: Column(
crossAxisAlignment:
CrossAxisAlignment.start,
children: [
// InvoiceFadeWidget(
// myTheme: myTheme,
// key: const Key('fadeWidget'),
// ),
InvoicesPageTextField(
class ItemListWidget extends StatelessWidget {
const ItemListWidget({
Key? key = const Key('ItemListWidget'),
required this.context,
required this.myTextTheme,
required this.myTextColor,
required this.value,
required this.myTheme,
required this.invoicesContents,
}) : super(key: const Key('ItemListWidget'),);
final BuildContext context;
final TextTheme myTextTheme;
final ThemeData myTextColor;
final ItemContents value;
final DarkThemeProvider myTheme;
final InvoicesContents invoicesContents;
#override
Widget build(BuildContext context) {
return Column(
children: [
for (var i = 0; i < invoicesContents.items.length; i++)
Padding(
padding: EdgeInsets.only(bottom: myTheme.normalPadding * 2),
child: Column(
children: [
Padding(
padding: EdgeInsets.only(bottom: myTheme.normalPadding),
child: InvoicesPageTextField(
key: const Key('ItemName'),
labelText: 'Item Name',
controller: TextEditingController(
text: invoicesContents.items[i].name,
),
onChanged: ((p0) {
invoicesContents.items[i].name = p0;
}),
),
),
Row(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
SizedBox(
width: myTheme.normalPadding * 3 - 8,
child: InvoicesPageTextField(
key: const Key('Qty'),
labelText: 'Qty.',
controller: TextEditingController(
text: invoicesContents.items[i].quantity.toString(),
),
onChanged: (p0) {
int? qty = int.tryParse(p0);
if (qty != null && qty > 0) {
invoicesContents.items[i].quantity = qty;
invoicesContents.items[i].total =
// ignore: unnecessary_cast
(qty *
invoicesContents.items[i].price.toDouble());
invoicesContents.total = invoicesContents.items
.map((e) => e.total)
.reduce((a, b) => a + b);
context.read<ItemContents>().refresh();
} else {
showDialog(
context: context,
builder: (context) => AlertDialog(
backgroundColor: Theme.of(context).canvasColor,
title: Text(
'Ops! =v=',
style: myTheme.textTheme(
"bodyMedium", myTheme.darkTheme),
),
content: Text(
'Natural Number Required !',
style: myTheme.textTheme(
"displayLarge", myTheme.darkTheme),
),
actionsPadding: EdgeInsets.symmetric(
horizontal: myTheme.normalPadding / 2 - 2,
vertical: myTheme.normalPadding / 2 - 2,
),
actions: [
Container(
decoration: BoxDecoration(
borderRadius: BorderRadius.all(
Radius.circular(myTheme.normalPadding),
),
color: myTextColor.highlightColor,
),
width: myTheme.normalPadding * 4 - 7,
height: myTheme.normalPadding * 2,
child: GestureDetector(
key: const Key('EnterNaturalNum'),
behavior: HitTestBehavior.opaque,
onTap: () => Navigator.pop(context),
child: Container(
alignment: Alignment.center,
child: Text(
'OK',
style: myTheme.textTheme(
"headlineMedium", myTheme.darkTheme),
),
),
),
),
],
),
);
}
},
),
),
InvoicePriceWidget(
key: const Key('InvoicePrice'),
context: context,
i: i,
invoicesContents: invoicesContents,
myTheme: myTheme),
InvoiceTotalWidget(
key: const Key('InvoiceTotal'),
myTextTheme: myTextTheme,
i: i,
context: context,
myTheme: myTheme,
invoicesContents: invoicesContents),
],
)
],
),
),
],
);
}
}
class InvoiceTotalWidget extends StatelessWidget {
const InvoiceTotalWidget({
Key? key = const Key('InvoiceTotal'),
required this.myTextTheme,
required this.i,
required this.context,
required this.myTheme,
required this.invoicesContents,
}) : super(
key: const Key('InvoiceTotal'),
);
final TextTheme myTextTheme;
final int i;
final BuildContext context;
final DarkThemeProvider myTheme;
final InvoicesContents invoicesContents;
#override
Widget build(BuildContext context) {
return Expanded(
key: const Key('TotalWidget'),
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Padding(
padding: EdgeInsets.only(bottom: myTheme.normalPadding),
child: Text(
'Total',
style: myTheme.textTheme("captionSmall", myTheme.darkTheme),
),
),
Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: [
Text(
context.read<ItemContents>().parseTotal(
invoicesContents.items[i].total.toDouble(),
showCurrency: false),
style: myTheme.textTheme("displayLarge", myTheme.darkTheme),
),
GestureDetector(
key: const Key('DeleteItem'),
onTap: () {
//const Key('DeleteItemOnTap');
invoicesContents.total = invoicesContents.total -
invoicesContents.items[i].quantity *
invoicesContents.items[i].price;
invoicesContents.items[i].quantity = 0;
invoicesContents.items[i].price = 0;
context.read<ItemContents>().refresh();
invoicesContents.items.removeAt(i);
context
.read<ItemContents>()
.data[context.read<ItemContents>().data.length - 1] =
invoicesContents;
context.read<ItemContents>().refresh();
},
child: Padding(
key: const Key('DeleteIcon'),
padding: EdgeInsets.only(right: myTheme.normalPadding * 0.9),
child: SvgPicture.asset(
'assets/images/icon-delete.svg',
),
),
),
],
),
],
),
);
}
}
I have tried to move the position of this button to the front of the ListView to ensure that the button has been generated. But this still does not make the button visible.

Text overflow flutter

I have the next widget, which is rendered with overflow. I have tried to solve, but i don't know. Can anyone help me? The aim is to do a custom card inside listview.
I have tried to wrap with expanded buth then, the error is referenced with constraints.
import 'package:flutter/material.dart';
import '../../shared/AppTheme.dart';
class ComandaScreen extends StatefulWidget {
const ComandaScreen({Key? key}) : super(key: key);
#override
State<ComandaScreen> createState() => _ComandaScreenState();
}
class _ComandaScreenState extends State<ComandaScreen> {
bool expanded = false;
int unidades = 0;
final List<Map<String, dynamic>> _items = List.generate(
10, (index) => {'id': index, 'Nombre': 'Nuggets $index',
'isExpanded': false, "unidades": 8});
#override
Widget build(BuildContext context) {
final ButtonStyle flatButtonStyle = TextButton.styleFrom(
shape: const RoundedRectangleBorder(
borderRadius: BorderRadius.all(Radius.circular(4.0)),
),
);
return Scaffold(
appBar: AppBar(
title: const Text('Comanda'),
backgroundColor: AppTheme.backgroundColor,
foregroundColor: AppTheme.primaryTextColor,
elevation: 0,
),
body: SingleChildScrollView(
child: ExpansionPanelList(
elevation: 3,
// expandedHeaderPadding: const EdgeInsets.all(10),
expansionCallback: (index, isExpanded) {
setState(() {
_items[index]['isExpanded'] = !isExpanded;
});
},
animationDuration: const Duration(milliseconds: 200),
children: _items
.map(
(item) => ExpansionPanel(
canTapOnHeader: true,
// backgroundColor: item['isExpanded'] == true ? Colors.cyan[100] : Colors.white,
headerBuilder: (context, isExpanded) {
return Container(
margin: const EdgeInsets.all(10),
child: Row(children: [
const CircleAvatar(
child: Text(
'1',
textAlign: TextAlign.center,
)),
const SizedBox(
width: 10,
),
Expanded(
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
const Text(
'Nuggets',
style: TextStyle(color: Colors.black, fontWeight: FontWeight.w600),
),
Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: [
Column(
crossAxisAlignment: CrossAxisAlignment.start,
mainAxisAlignment: MainAxisAlignment.start,
children: const [
Text(
'Unidades: ${7}',
style: TextStyle(color: Colors.black),
),
Text(
'Pendientes: 400',
style: TextStyle(color: Colors.black),
),
],
),
const SizedBox(
width: 20,
),
Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: const [
Text(
'Precio: 10 €',
maxLines: 1,
overflow: TextOverflow.ellipsis,
style: TextStyle(color: Colors.black),
),
Text(
'Total: 70 €',
maxLines: 1,
overflow: TextOverflow.ellipsis,
style: TextStyle(color: Colors.black),
),
],
),
],
),
],
),
),
]),
);
},
body: ButtonBar(
alignment: MainAxisAlignment.spaceAround,
buttonHeight: 52.0,
buttonMinWidth: 90.0,
children: <Widget>[
TextButton(
style: flatButtonStyle,
onPressed: () {
setState(() {
item['unidades'] += 1;
});
},
child: Column(
children: const <Widget>[
Icon(
Icons.add,
color: AppTheme.grismedio,
),
// Padding(
// padding: EdgeInsets.symmetric(vertical: 2.0),
// ),
// Text('Más'),
],
),
),
TextButton(
style: flatButtonStyle,
onPressed: () {
setState(() {
item['unidades'] -= 1;
});
},
child: Column(
children: const <Widget>[
Icon(
Icons.remove,
color: AppTheme.grismedio,
),
// Padding(
// padding: EdgeInsets.symmetric(vertical: 2.0),
// ),
// Text('Menos'),
],
),
),
TextButton(
style: flatButtonStyle,
onPressed: () {},
child: Column(
children: const <Widget>[
Icon(
Icons.edit_outlined,
color: AppTheme.grismedio,
),
// Padding(
// padding: EdgeInsets.symmetric(vertical: 2.0),
// ),
// Text('Editar'),
],
),
),
TextButton(
style: flatButtonStyle,
onPressed: () {},
child: Column(
children: const <Widget>[
Icon(
Icons.delete_outline_outlined,
color: AppTheme.grismedio,
),
// Padding(
// padding: EdgeInsets.symmetric(vertical: 2.0),
// ),
// Text('Eliminar'),
],
),
),
TextButton(
style: flatButtonStyle,
onPressed: () {},
child: Column(
children: const <Widget>[
Icon(
Icons.card_giftcard_outlined,
color: AppTheme.grismedio,
),
// Padding(
// padding: EdgeInsets.symmetric(vertical: 2.0),
// ),
// Text('Invitar'),
],
),
)
],
),
isExpanded: item['isExpanded'],
),
)
.toList(),
// Card_lineaComanda(flatButtonStyle),
),
),
);
}
}
I 've edited the code to show all screen widget.
Image of result of code before:
For desktop applications, you can prevent the resize with breakpoint, so the error won't happen. In the pubsec.yaml file, add the following dependency.
window_size:
git:
url: https://github.com/google/flutter-desktop-embedding.git
path: plugins/window_size
And in your main method before runapp add this code with min-width and min-height below which the app won't resize.
const double desktopMinWidth = 800.0;
const double desktopMinHeight = 600.0;
if (Platform.isMacOS || Platform.isWindows) {
setWindowMinSize(const Size(desktopMinWidth, desktopMinHeight));
setWindowMaxSize(Size.infinite);
}
Note: Once done restart your app.
For mobile, it is entirely a different case. You might need to restructure the design

Flutter getX not updating UI after ading items to obs list

I'm using GetX to manage the state of my ecommerce app, and I'm stuck with the shopping cart.
I'm using Sqflite package to store carts locally.
issue: when i add a product to cart list it's not updating it automatically untill i restart the app i see the added items
Cart controller
addProductToCart(ProductModel product) async {
List<Map> items =
await _repository.getLocalByCondition('carts', 'productId', product.id);
if (items.length > 0) {
product.quantity = items.first['productQuantity'] + 1;
return await _repository.updateLocal(
'carts', 'productId', product.toMap());
}
product.quantity = 1;
return await _repository.saveLocal('carts', product.toMap());
}
#override
void onInit() {
super.onInit();
getCarts();
}
Add to cart product details
_addToCart(BuildContext context, ProductModel product) async {
int result = await _cartController.addProductToCart(product);
if (result > 0) {
Fluttertoast.showToast(msg: 'Item added to cart successfully!');
} else {
Fluttertoast.showToast(msg: 'Error to add product!');
}
}
Cart screen
Obx(() {
return Scaffold(
appBar: AppBar(
title: Text('Cart (${_cartController.cartList.length} items)',
style: TextStyle(color: Colors.black))),
bottomNavigationBar: InkWell(
onTap: () {
//_checkout(_cartController.cartList);
},
child: Container(
color: Colors.black,
height: 55,
margin: EdgeInsets.all(8),
child: Row(
mainAxisAlignment: MainAxisAlignment.center,
children: [
Text(
'Checkout',
style: TextStyle(color: Colors.white),
),
SizedBox(
width: 10,
),
Text(
'£' + _cartController.total.toString(),
style: TextStyle(color: Colors.white),
)
],
),
)),
body: _cartController.loading.isTrue
? Center(child: CircularProgressIndicator())
: _cartController.cartList.length > 0
? ListView.builder(
padding:
EdgeInsets.symmetric(horizontal: 8, vertical: 12),
itemBuilder: (context, index) {
return Dismissible(
key: Key(this
._cartController
.cartList[index]
.id
.toString()),
onDismissed: (val) async {
var x= await _cartController.deleteCartItem(
index, this._cartController.cartList[index].id);
if(x!>0) Fluttertoast.showToast(msg: 'deleted');
else{ Fluttertoast.showToast(msg: x.toString());
}
},
background: Container(
child: Align(
alignment: Alignment.centerRight,
child: Padding(
padding: const EdgeInsets.only(right: 28.0),
child: Icon(
Icons.delete_outline_outlined,
size: 36,
color: Colors.white,
),
)),
color: Colors.redAccent),
child: Card(
elevation: 5,
shape: OutlineInputBorder(
borderRadius: BorderRadius.all(
Radius.circular(7),
)),
child: Container(
margin: EdgeInsets.all(4),
height: 120,
child: Row(
children: [
Expanded(
flex: 3,
child: ClipRRect(
borderRadius: BorderRadius.only(
topLeft: Radius.circular(5),
topRight: Radius.circular(5)),
child: Image.network(
_cartController.cartList[index].photo,
width: 150,
height: 120,
fit: BoxFit.cover,
),
),
),
Expanded(
flex: 5,
child: Column(
mainAxisAlignment:
MainAxisAlignment.center,
crossAxisAlignment:
CrossAxisAlignment.start,
children: [
Padding(
padding: const EdgeInsets.all(8.0),
child: Text(
_cartController
.cartList[index].name,
overflow: TextOverflow.clip,
style: TextStyle(
fontWeight: FontWeight.w500,
fontSize: 16),
),
),
Padding(
padding: const EdgeInsets.all(8.0),
child: Text(
'${_cartController.cartList[index].price - _cartController.cartList[index].discount} ' +
" x " +
_cartController
.cartList[index].quantity
.toString(),
style: TextStyle(
fontWeight: FontWeight.w500),
),
),
],
)),
Expanded(
child: Column(
mainAxisAlignment: MainAxisAlignment.start,
children: [
IconButton(
icon: Icon(Icons.keyboard_arrow_up),
onPressed: () {
_cartController.total.value += this
._cartController
.cartList[index]
.price -
this
._cartController
.cartList[index]
.discount;
this
._cartController
.cartList[index]
.quantity++;
},
),
Text(_cartController
.cartList[index].quantity
.toString()),
IconButton(
icon: Icon(Icons.keyboard_arrow_down),
onPressed: () {
if (_cartController
.cartList[index].quantity >
1) {
_cartController.total.value -= (this
._cartController
.cartList[index]
.price -
this
._cartController
.cartList[index]
.discount);
this
._cartController
.cartList[index]
.quantity--;
}
},
),
],
))
],
),
),
),
);
},
itemCount: _cartController.cartList.length,
)
: Center(
child: Text('Your Shopping cart is empty'),
));
});
You need to add product in _cartController.cartList.
_addToCart(BuildContext context, ProductModel product) async {
cartList.add(product);
....
}

ListView.builder returns Null check operator used on a null value

I'm developing a job search app that scrapes data from Indeed using Python which is being sent back to my Flutter UI as JSON data. The JSON data is being received successfully, however, Im getting an error of Null check operator used on a null value. The error appears to be stemming from the _jobSearch widget.
The relevant error-causing widget was ListView lib/ui/home_page.dart:256
Exception caught by scheduler library
Null check operator used on a null value
Here is the code:
import 'package:flutter/material.dart';
import 'package:http/http.dart' as http;
import 'dart:convert';
import 'package:flutter_job_portal/theme/colors.dart';
import 'package:flutter_job_portal/theme/images.dart';
import 'package:flutter_job_portal/ui/bottom_menu_bar.dart';
import 'package:flutter_job_portal/ui/job_detail_page.dart';
String job = ""; //user's response will be assigned to this variable
String final_response = "";
final _formkey = GlobalKey<FormState>(); //key created to interact with the form
//function to validate and save user form
Future<void> _savingData() async {
final validation = _formkey.currentState.validate();
if (!validation) {
return;
}
_formkey.currentState.save();
}
Future<List<Job>> _getJobs() async {
final url = 'http://127.0.0.1:5000/job';
final response1 = await http.post(Uri.parse(url), body: json.encode({'job': job}));
final response2 = await http.get(Uri.parse(url));
final decoded = json.decode(response2.body);
List<Job> jobs = [];
for (var i in decoded) {
Job job = Job(i['Title'], i['Company'], i['Location'], i['Salary']);
jobs.add(job);
}
return jobs;
}
class Job {
final String title;
final String company;
final String location;
final String salary;
Job(this.title, this.company, this.location, this.salary);
}
class HomePage extends StatelessWidget {
const HomePage({Key key}) : super(key: key);
Widget _appBar(BuildContext context) {
return Container(
padding: EdgeInsets.symmetric(horizontal: 16, vertical: 10),
child: Row(
children: [
CircleAvatar(
backgroundImage: AssetImage(Images.user1),
),
Spacer(),
IconButton(
icon: Icon(Icons.notifications_none_rounded),
onPressed: () {},
)
],
),
);
}
Widget _header(BuildContext context) {
return Container(
margin: EdgeInsets.symmetric(vertical: 12),
padding: EdgeInsets.symmetric(horizontal: 16, vertical: 10),
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Text("Hello, Alex!",
style: TextStyle(
fontSize: 15,
color: KColors.subtitle,
fontWeight: FontWeight.w500,
)),
SizedBox(
height: 6,
),
Text("Swipe to find your future",
style: TextStyle(
fontSize: 20,
color: KColors.title,
fontWeight: FontWeight.bold)),
SizedBox(
height: 10,
),
Row(
children: [
Expanded(
child: Container(
height: 45,
padding: EdgeInsets.symmetric(horizontal: 10, vertical: 5),
decoration: BoxDecoration(
color: KColors.lightGrey,
borderRadius: BorderRadius.circular(10)),
child: Form(
key: _formkey,
child: TextFormField(
decoration: InputDecoration(
hintText: 'Search job title or keywords',
),
onSaved: (value) {
job =
value; //getting data from the user form and assigning it to job
},
),
),
),
),
SizedBox(
width: 16,
),
Container(
decoration: BoxDecoration(
color: KColors.primary,
borderRadius: BorderRadius.circular(10),
),
height: 40,
child: IconButton(
color: KColors.primary,
icon: Icon(Icons.search, color: Colors.white),
onPressed: () async {
_savingData();
_getJobs();
},
),
)
],
)
],
),
);
}
Widget _recommendedSection(BuildContext context) {
return Container(
padding: EdgeInsets.symmetric(horizontal: 16, vertical: 10),
margin: EdgeInsets.symmetric(vertical: 12),
height: 200,
width: MediaQuery.of(context).size.width,
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Text(
"Recommended",
style: TextStyle(fontWeight: FontWeight.bold, color: KColors.title),
),
SizedBox(height: 10),
Expanded(
child: ListView(
scrollDirection: Axis.horizontal,
children: [
_recommendedJob(context,
company: "Google",
img: Images.google,
title: "UX Designer",
sub: "\$45,000 Remote",
isActive: true),
_recommendedJob(context,
company: "DropBox",
img: Images.dropbox,
title: "Research Assist",
sub: "\$45,000 Remote",
isActive: false)
],
),
),
],
),
);
}
Widget _recommendedJob(
BuildContext context, {
String img,
String company,
String title,
String sub,
bool isActive = false,
}) {
return Padding(
padding: const EdgeInsets.only(right: 10),
child: GestureDetector(
onTap: () {
Navigator.push(context, JobDetailPage.getJobDetail());
},
child: AspectRatio(
aspectRatio: 1.3,
child: Container(
decoration: BoxDecoration(
color: isActive ? KColors.primary : Colors.white,
borderRadius: BorderRadius.circular(7),
),
padding: EdgeInsets.all(16),
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Container(
height: 40,
width: 40,
padding: EdgeInsets.all(10),
decoration: BoxDecoration(
color: isActive ? Colors.white : KColors.lightGrey,
borderRadius: BorderRadius.circular(7),
),
child: Image.asset(img),
),
SizedBox(height: 16),
Text(
company,
style: TextStyle(
fontSize: 12,
color: isActive ? Colors.white38 : KColors.subtitle,
),
),
SizedBox(height: 6),
Text(
title,
style: TextStyle(
fontSize: 14,
color: isActive ? Colors.white : KColors.title,
fontWeight: FontWeight.bold,
),
),
SizedBox(height: 6),
Text(
sub,
style: TextStyle(
fontSize: 12,
color: isActive ? Colors.white38 : KColors.subtitle,
),
),
],
),
),
),
),
);
}
Widget _jobSearch(BuildContext context) {
return new Container(
child: FutureBuilder(
future: _getJobs(),
builder: (context, snapshot) {
if (snapshot.data == null) {
return Container(
child: Center(
child: Text('Loading...'),
));
} else {
return ListView.builder(
itemCount: snapshot.data.length,
itemBuilder: (context, index) {
return ListTile(
title: Text(snapshot.data[index].location),
);
},
);
}
},
),
);
}
#override
Widget build(BuildContext context) {
return Scaffold(
backgroundColor: KColors.background,
bottomNavigationBar: BottomMenuBar(),
body: SafeArea(
child: Container(
width: MediaQuery.of(context).size.width,
child: SingleChildScrollView(
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
_appBar(context),
_header(context),
_recommendedSection(context),
_jobSearch(context)
],
),
),
),
),
);
}
}

How to solve "a build function has returned null" in flutter app?

App was working perfectly before and then I had to make some changes to allow or restrict calling feature in the app based on subscription level of the user, by passing the variable value from one screen to another using provider.
one Screen 1 i am using :
Future<void> _verifyPuchase(String id) async {
PurchaseDetails purchase = _hasPurchased(id);
if (purchase != null && purchase.status == PurchaseStatus.purchased) {
print(purchase.productID);
if (Platform.isIOS) {
await _iap.completePurchase(purchase);
print('Achats antérieurs........$purchase');
isPuchased = true;
}
isPuchased = true;
checkIsPurchsed(isPurchsed: isPuchased);
} else {
isPuchased = false;
checkIsPurchsed(isPurchsed: isPuchased);
}
}
and have i have a class on screen 1:
class checkIsPurchsed with ChangeNotifier{
bool isPurchsed;
checkIsPurchsed({this.isPurchsed});
notifyListeners();
}
and on screen 5 I have:
Consumer<checkIsPurchsed>(
builder: (context,isPurchsed,child){
return isPurchsed.isPurchsed ? IconButton(
icon: Icon(Icons.call), onPressed: () => onJoin("AudioCall"),
):Center(child: Text(
'Sorry you have not subscribe the package',
),);
},
),
Consumer<checkIsPurchsed>(
builder: (context,isPurchsed,child){
return isPurchsed.isPurchsed ? IconButton(
icon: Icon(Icons.video_call), onPressed: () => onJoin("VideoCall"),
):Center(child: Text(
'Sorry you have not subscribe the package',
),);
},
),
on screen 5 when i try to open a chat this is what i am getting :
the icon buttons on which i am checking the purchase status appear when chat is opened , but after adding the above mentioned modifications the chat itself isnt opening and instead i am getting the red screen.
Update 1:
class ChatPage extends StatefulWidget {
final User sender;
final String chatId;
final User second;
ChatPage({this.sender, this.chatId, this.second});
#override
_ChatPageState createState() => _ChatPageState();
}
class _ChatPageState extends State<ChatPage> {
bool isBlocked = false;
final db = Firestore.instance;
CollectionReference chatReference;
final TextEditingController _textController = new TextEditingController();
bool _isWritting = false;
final _scaffoldKey = GlobalKey<ScaffoldState>();
//Ads _ads = new Ads();
#override
void initState() {
//_ads.myInterstitial()
//..load()
//..show();
print("object -${widget.chatId}");
super.initState();
chatReference =
db.collection("chats").document(widget.chatId).collection('messages');
checkblock();
}
var blockedBy;
checkblock() {
chatReference.document('blocked').snapshots().listen((onData) {
if (onData.data != null) {
blockedBy = onData.data['blockedBy'];
if (onData.data['isBlocked']) {
isBlocked = true;
} else {
isBlocked = false;
}
if (mounted) setState(() {});
}
// print(onData.data['blockedBy']);
});
}
List<Widget> generateSenderLayout(DocumentSnapshot documentSnapshot) {
return <Widget>[
Expanded(
child: Column(
crossAxisAlignment: CrossAxisAlignment.end,
children: <Widget>[
Container(
child: documentSnapshot.data['image_url'] != ''
? InkWell(
child: Column(
crossAxisAlignment: CrossAxisAlignment.end,
children: <Widget>[
new Container(
margin: EdgeInsets.only(
top: 2.0, bottom: 2.0, right: 15),
child: Stack(
children: <Widget>[
CachedNetworkImage(
placeholder: (context, url) => Center(
child: CupertinoActivityIndicator(
radius: 10,
),
),
errorWidget: (context, url, error) =>
Icon(Icons.error),
height:
MediaQuery.of(context).size.height * .65,
width: MediaQuery.of(context).size.width * .9,
imageUrl:
documentSnapshot.data['image_url'] ?? '',
fit: BoxFit.fitWidth,
),
Container(
alignment: Alignment.bottomRight,
child:
documentSnapshot.data['isRead'] == false
? Icon(
Icons.done,
color: secondryColor,
size: 15,
)
: Icon(
Icons.done_all,
color: primaryColor,
size: 15,
),
)
],
),
height: 150,
width: 150.0,
color: secondryColor.withOpacity(.5),
padding: EdgeInsets.all(5),
),
Padding(
padding: const EdgeInsets.only(right: 10),
child: Text(
documentSnapshot.data["time"] != null
? DateFormat.yMMMd()
.add_jm()
.format(documentSnapshot.data["time"]
.toDate())
.toString()
: "",
style: TextStyle(
color: secondryColor,
fontSize: 13.0,
fontWeight: FontWeight.w600,
)),
)
],
),
onTap: () {
Navigator.of(context).push(
CupertinoPageRoute(
builder: (context) => LargeImage(
documentSnapshot.data['image_url'],
),
),
);
},
)
: Container(
padding: EdgeInsets.symmetric(
horizontal: 15.0, vertical: 10.0),
width: MediaQuery.of(context).size.width * 0.65,
margin: EdgeInsets.only(
top: 8.0, bottom: 8.0, left: 80.0, right: 10),
decoration: BoxDecoration(
color: primaryColor.withOpacity(.1),
borderRadius: BorderRadius.circular(20)),
child: Column(
children: <Widget>[
Row(
crossAxisAlignment: CrossAxisAlignment.end,
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: <Widget>[
Expanded(
child: Container(
child: Text(
documentSnapshot.data['text'],
style: TextStyle(
color: Colors.black87,
fontSize: 16.0,
fontWeight: FontWeight.w600,
),
),
),
),
Row(
mainAxisAlignment: MainAxisAlignment.end,
children: <Widget>[
Text(
documentSnapshot.data["time"] != null
? DateFormat.MMMd()
.add_jm()
.format(documentSnapshot
.data["time"]
.toDate())
.toString()
: "",
style: TextStyle(
color: secondryColor,
fontSize: 13.0,
fontWeight: FontWeight.w600,
),
),
SizedBox(
width: 5,
),
documentSnapshot.data['isRead'] == false
? Icon(
Icons.done,
color: secondryColor,
size: 15,
)
: Icon(
Icons.done_all,
color: primaryColor,
size: 15,
)
],
),
],
),
],
)),
),
],
),
),
];
}
_messagesIsRead(documentSnapshot) {
return <Widget>[
Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: <Widget>[
InkWell(
child: CircleAvatar(
backgroundColor: secondryColor,
radius: 25.0,
child: ClipRRect(
borderRadius: BorderRadius.circular(90),
child: CachedNetworkImage(
imageUrl: widget.second.imageUrl[0] ?? '',
useOldImageOnUrlChange: true,
placeholder: (context, url) => CupertinoActivityIndicator(
radius: 15,
),
errorWidget: (context, url, error) => Icon(Icons.error),
),
),
),
onTap: () => showDialog(
barrierDismissible: false,
context: context,
builder: (context) {
return Info(widget.second, widget.sender, null);
}),
),
],
),
Expanded(
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: <Widget>[
Container(
child: documentSnapshot.data['image_url'] != ''
? InkWell(
child: Column(
crossAxisAlignment: CrossAxisAlignment.end,
children: <Widget>[
new Container(
margin: EdgeInsets.only(
top: 2.0, bottom: 2.0, right: 15),
child: CachedNetworkImage(
placeholder: (context, url) => Center(
child: CupertinoActivityIndicator(
radius: 10,
),
),
errorWidget: (context, url, error) =>
Icon(Icons.error),
height: MediaQuery.of(context).size.height * .65,
width: MediaQuery.of(context).size.width * .9,
imageUrl:
documentSnapshot.data['image_url'] ?? '',
fit: BoxFit.fitWidth,
),
height: 150,
width: 150.0,
color: Color.fromRGBO(0, 0, 0, 0.2),
padding: EdgeInsets.all(5),
),
Padding(
padding: const EdgeInsets.only(right: 10),
child: Text(
documentSnapshot.data["time"] != null
? DateFormat.yMMMd()
.add_jm()
.format(documentSnapshot.data["time"]
.toDate())
.toString()
: "",
style: TextStyle(
color: secondryColor,
fontSize: 13.0,
fontWeight: FontWeight.w600,
)),
)
],
),
onTap: () {
Navigator.of(context).push(CupertinoPageRoute(
builder: (context) => LargeImage(
documentSnapshot.data['image_url'],
),
));
},
)
: Container(
padding: EdgeInsets.symmetric(
horizontal: 15.0, vertical: 10.0),
width: MediaQuery.of(context).size.width * 0.65,
margin: EdgeInsets.only(top: 8.0, bottom: 8.0, right: 10),
decoration: BoxDecoration(
color: secondryColor.withOpacity(.3),
borderRadius: BorderRadius.circular(20)),
child: Column(
children: <Widget>[
Row(
crossAxisAlignment: CrossAxisAlignment.end,
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: <Widget>[
Expanded(
child: Container(
child: Text(
documentSnapshot.data['text'],
style: TextStyle(
color: Colors.black87,
fontSize: 16.0,
fontWeight: FontWeight.w600,
),
),
),
),
Row(
mainAxisAlignment: MainAxisAlignment.end,
children: <Widget>[
Text(
documentSnapshot.data["time"] != null
? DateFormat.MMMd()
.add_jm()
.format(documentSnapshot
.data["time"]
.toDate())
.toString()
: "",
style: TextStyle(
color: secondryColor,
fontSize: 13.0,
fontWeight: FontWeight.w600,
),
),
],
),
],
),
],
)),
),
],
),
),
];
}
List<Widget> generateReceiverLayout(DocumentSnapshot documentSnapshot) {
if (!documentSnapshot.data['isRead']) {
chatReference.document(documentSnapshot.documentID).updateData({
'isRead': true,
});
return _messagesIsRead(documentSnapshot);
}
return _messagesIsRead(documentSnapshot);
}
generateMessages(AsyncSnapshot<QuerySnapshot> snapshot) {
return snapshot.data.documents
.map<Widget>((doc) => Container(
margin: const EdgeInsets.symmetric(vertical: 10.0),
child: new Row(
mainAxisAlignment: MainAxisAlignment.center,
children: doc.data['type'] == "Call"
? [
Text(doc.data["time"] != null
? "${doc.data['text']} : " +
DateFormat.yMMMd()
.add_jm()
.format(doc.data["time"].toDate())
.toString() +
" by ${doc.data['sender_id'] == widget.sender.id ? "You" : "${widget.second.name}"}"
: "")
]
: doc.data['sender_id'] != widget.sender.id
? generateReceiverLayout(
doc,
)
: generateSenderLayout(doc)),
))
.toList();
}
#override
Widget build(BuildContext context) {
ChangeNotifierProvider<checkIsPurchsed>(
create: (context)=>checkblock(),
child: Scaffold(
key: _scaffoldKey,
backgroundColor: Colors.white,
appBar: AppBar(
centerTitle: true,
elevation: 0,
title: Text(widget.second.name),
leading: IconButton(
icon: Icon(Icons.arrow_back_ios),
color: Colors.white,
onPressed: () => Navigator.pop(context),
),
actions: <Widget>[
Consumer<checkIsPurchsed>(
builder: (context,isPurchsed,child){
return isPurchsed.isPurchsed ? IconButton(
icon: Icon(Icons.call), onPressed: () => onJoin("AudioCall"),
):Center(child: Text(
'Sorry you have not subscribe the package',
),);
},
),
Consumer<checkIsPurchsed>(
builder: (context,isPurchsed,child){
return isPurchsed.isPurchsed ? IconButton(
icon: Icon(Icons.video_call), onPressed: () => onJoin("VideoCall"),
):Center(child: Text(
'Sorry you have not subscribe the package',
),);
},
),
}
The problematic area is:
#override
Widget build(BuildContext context) {
ChangeNotifierProvider<checkIsPurchsed>(
There's no return in front of ChangeNotifierProvider, so it doesn't return a Widget. Correct would be:
#override
Widget build(BuildContext context) {
return ChangeNotifierProvider<checkIsPurchsed>(