In my screen I have a ScrollablePositionedList.builder and I'm using slidable to have slide to delete a record in db, I pop a show a confirmation dialog and after the user confirms deletion I want to show a snack bar for undo. Before adding the confirmation dialog snack bar was shown fine, but now I get the Scaffold.of() called with a context that does not contain a Scaffold.. I need to call Navigator.pop(context) just before showing it to close the confirmation dialog so thinking it was the reason I tried commenting it out but I still got the error.
Can you spot what I'm doing wrong here? Also I'd like to show the snack bar when receiving th BookingDeleted state in BlocLister, how to achieve it since it it before the Scaffold the tree??
As always many thanks for your help.
Here is the screen code:
class _BookingsScreenState extends State<BookingsScreen> {
dynamic backButton = Platform.isIOS ? CupertinoIcons.back : Icons.arrow_back;
DateFormat dateOnlyFormat =
DateFormat.yMMMMEEEEd(AppLocalizations.instance.text('Language code'));
DateFormat timeFormat = DateFormat('Hm');
AudioCache cache = new AudioCache();
ItemScrollController scrollController = ItemScrollController();
List<Booking> bookings = [];
int cellIndex = 0;
@override
Widget build(BuildContext context) {
cache.loadAll(['click.mp3', 'tableViewOpen.mp3', 'tableViewClose.mp3']);
return BlocListener<BookingBloc, BookingState>(
listener: (BuildContext context, BookingState state) {
if (state is LoadedBookings) {
setState(() {
bookings = state.bookings;
print(
'BookingsScreen BlocListener we have ${state.bookings.length} saved bookings');
print(
'BookingsScreen BlocListener saved bookings are ${state.bookings}');
print(bookings);
});
}
if (state is BookingDeleted) {
showDialog(
barrierDismissible: false,
context: context,
builder: (BuildContext context) {
return BookingDeletedConfirmationDialog();
}
);
Timer(Duration(milliseconds: 1500), (){
cache.play('tableViewClose.mp3');
Navigator.of(context, rootNavigator: false).pop(context);
});
}
},
child: Stack(
children: [
Image(
image: widget.bgImage.image,
height: MediaQuery.of(context).size.height,
width: MediaQuery.of(context).size.width,
fit: BoxFit.cover),
Scaffold(
backgroundColor: Colors.transparent,
appBar: AppBar(
elevation: 0,
centerTitle: true,
leading: IconButton(
icon: Icon(backButton),
color: Colors.redAccent,
onPressed: () {
cache.play('tableViewClose.mp3');
Navigator.pop(context);
}),
title: Text(
AppLocalizations.instance.text('BookingScreenTitle'),
style: TextStyle(
color: Colors.orange,
fontSize: 22,
fontWeight: FontWeight.w500,
letterSpacing: 1),
),
backgroundColor: Colors.transparent,
),
body: SafeArea(
minimum: EdgeInsets.symmetric(horizontal: 20),
child: ScrollablePositionedList.builder(
itemScrollController: scrollController,
itemCount: bookings.length,
itemBuilder: (BuildContext context, int index) => Slidable(
actionPane: SlidableBehindActionPane(),
actionExtentRatio: 0.25,
actions: <Widget>[
IconSlideAction(
caption:
AppLocalizations.instance.text('Booking details'),
color: Colors.transparent,
foregroundColor: Colors.blue,
icon: Icons.details,
onTap: () {
print('BookingDetailsScreen');
},
),
],
secondaryActions: <Widget>[
IconSlideAction(
caption: AppLocalizations.instance.text('Delete booking'),
color: Colors.transparent,
foregroundColor: Colors.red,
icon: Icons.delete,
onTap: () {
setState(() {
cellIndex = index;
});
var booking = bookings.elementAt(index);
if (booking.bookingState == 'Waiting' || booking.bookingState == 'Received' || booking.bookingState == 'Cancelled') {
showDialog(
context: context,
barrierDismissible: false,
builder: (BuildContext context) {
return BookingDeletionConfirmationDialog(
index: index,
booking: booking,
onPressedCancel: (){
cache.play('tableViewClose.mp3');
Navigator.pop(context);
},
onPressedDelete: (){
cache.play('tableViewClose.mp3');
Navigator.pop(context);
deleteItem(index, booking);
//Scaffold.of() called with a context that does not contain a Scaffold. ???
Scaffold.of(context).showSnackBar(SnackBar(
backgroundColor: Colors.redAccent,
content: Text(
AppLocalizations.instance
.text('Booking deleted'),
style: TextStyle(color: Colors.white),
),
action: SnackBarAction(
textColor: Colors.white,
label: AppLocalizations.instance.text('Undo'),
onPressed: () {
//To undo deletion
undoDeletion(index, booking);
})));
},
);
}
);
} else {
Scaffold.of(context).showSnackBar(SnackBar(
backgroundColor: Colors.redAccent,
content: Text(
AppLocalizations.instance
.text('Booking not deletable'),
style: TextStyle(color: Colors.white),
),
));
}
},
),
],
child: BookingCell(
isSelected: false,
borderColor: bookings[index].bookingState == 'Waiting'
? Colors.blue
: bookings[index].bookingState == 'Received'
? Colors.amber.shade200
: bookings[index].bookingState == 'Started'
? Colors.amber.shade600
: bookings[index].bookingState == 'Completed'
? Colors.green.shade400
: bookings[index].bookingState ==
'Cancelled'
? Colors.redAccent
: Colors.blueGrey.shade300,
worksList: bookings[index].worksNameList,
bookingId: bookings[index].bookingId.toString(),
bookingDate: dateOnlyFormat.format(
DateTime.fromMillisecondsSinceEpoch(
bookings[index].bookingDate)),
bookingStart: timeFormat.format(
DateTime.fromMillisecondsSinceEpoch(
bookings[index].bookingStart)),
shopName: bookings[index].shopName,
),
),
),
),
),
],
),
);
}
void deleteItem(index, Booking booking) {
setState(() {
BlocProvider.of<BookingBloc>(context).add(DeleteBooking(
booking: booking,
cityDb: widget.cityDb,
regionDb: widget.regionDb,
countryDb: widget.countryDb));
});
BlocProvider.of<BookingBloc>(context).add(UpdateBookingState(
bookingId: booking.bookingId.toString(), state: 'Cancelled'));
}
void undoDeletion(index, Booking booking) {
setState(() {
booking.bookingState ='Reactivated.'+ booking.bookingState;
BlocProvider.of<BookingBloc>(context).add(SaveBooking(
booking: booking,
cityDb: widget.cityDb,
regionDb: widget.regionDb,
countryDb: widget.countryDb));
});
}
}
Try this solution with scaffold global key