I have implemented a function to form submitting.I want to have SnackBar Alert to after submitted. I have tried but it doesn't work.After I added SnackBar routing also doesn't work.
addTicket() async {
if (_formKey.currentState.validate()) {
_formKey.currentState.save();
try{
DocumentReference ref = await db.collection('CostalLineTicketDetails').
document(ticketCato).collection("Tickets").add(
{
'startStation':startStation,
'endStation':endStation,
'price':price,
'ticketType':ticketCato,
'contactNo':contactNo,
'dateTime':dateTime,
});
setState(() => id = ref.documentID);
Navigator.push(context, new MaterialPageRoute(builder: (context) => CostalLine()));
Scaffold.of(context).showSnackBar(SnackBar(content: Text('Ticket Added Sucessfully')));
}catch(e){
print(e);
}
}
}
}
You cannot show showSnackBar on same page after going to another screen.
You can declare _scaffoldKey and pass it to Scaffold like this
final GlobalKey<ScaffoldState> _scaffoldKey = new GlobalKey<ScaffoldState>();
Scaffold(
key: _scaffoldKey,
then open snackbar like this
_scaffoldKey.currentState.showSnackBar(SnackBar(
content: Text(
'Welcome',
),
duration: Duration(seconds: 2),
));
Output:
Edit
You can also use flash where you don't need to pass _scaffoldKey every time.
example:
void _showBasicsFlash({
Duration? duration,
flashStyle = FlashBehavior.floating,
}) {
showFlash(
context: context,
duration: duration,
builder: (context, controller) {
return Flash(
controller: controller,
behavior: flashStyle,
position: FlashPosition.bottom,
boxShadows: kElevationToShadow[4],
horizontalDismissDirection: HorizontalDismissDirection.horizontal,
child: FlashBar(
content: Text('This is a basic flash'),
),
);
},
);
}
try this,
addTicket() async {
if (_formKey.currentState.validate()) {
_formKey.currentState.save();
try{
DocumentReference ref = await
db.collection('CostalLineTicketDetails').
document(ticketCato).collection("Tickets").add(
{
'startStation':startStation,
'endStation':endStation,
'price':price,
'ticketType':ticketCato,
'contactNo':contactNo,
'dateTime':dateTime,
});
setState(() => id = ref.documentID);
// Navigator.push(context, new MaterialPageRoute(builder: (context) => CostalLine()));
Scaffold.of(context).showSnackBar(SnackBar(content:
Text('Ticket Added Sucessfully')));
}catch(e){
print(e);
}
}
}
}
Define this code in any of the generalized dart file, and you can call this function at any place and will display a generic type scaffold.
import 'package:flutter/material.dart';
void showWarningSnackBar(BuildContext context, String message) {
// Find the Scaffold in the widget tree and use it to show a SnackBar.
ScaffoldFeatureController<Widget, dynamic> _scaffold;
// Find the Scaffold in the widget tree and use it to show a SnackBar.
_scaffold = Scaffold.of(context).showSnackBar(SnackBar(
content: InkWell(
onTap: () {
_scaffold.close();
},
child: Row(
mainAxisAlignment: MainAxisAlignment.start,
children: <Widget>[
AppImage.asset(
assetName: YOUR_IMAGE_NAME,
fit: BoxFit.contain,
width: 20,
color: COLOR),
const SizedBox(
width: 10,
),
Text(
'$message',
maxLines: 2,
),
],
),
),
duration: const Duration(seconds: 10),
backgroundColor: COLOR,
));
}
Related
I using qr_code_scanner for scan a qr code & barcodes. Its scanning perfectly. But when i want to use dialog after scan for ask how much product did you scan and after that check it and control it.But when i use textfield inside of dialog and when i tap textfield camera stops working and its stays in black screen. What should i do ? What is wrong ? My codes for scan :
void _onQRViewCreated(QRViewController controller) {
this.controller = controller;
controller.scannedDataStream.listen((scanData) {
controller.pauseCamera();
player.play("scanner_sound.mp3");
inspect(args);
if (args.Barcode == scanData.code) {
showDialog(
context: context,
builder: (context) {
return WillPopScope(
onWillPop: () async {
Navigator.pop(context);
controller.resumeCamera();
return true;
},
child: AlertDialog(
title: const Text('Ürün Giriş'),
content: Column(
children: [
const Text('Bu üründen kaç adet okutuldu ?'),
TextField(
keyboardType: TextInputType.number,
controller: _controller,
),
],
),
actions: <Widget>[
TextButton(
onPressed: () {
bool isTrue = checkScannedCount(int.parse(_controller.text));
if (isTrue) {
var model = args.copyWith(ScannedCount: args.Count);
context.read<ProductCubit>().updateProduct(model);
Navigator.pop(context);
Navigator.pop(context);
} else {
ScaffoldMessenger.of(context).showSnackBar(
const SnackBar(
content: Text("Lütfen sayımı tekrarlayınız."),
),
);
Navigator.pop(context);
controller.resumeCamera(); //Its not starting camera again.
}
},
child: const Text('Tamam')),
],
),
);
});
} else {
showDialog(
context: context,
builder: (context) {
return WillPopScope(
onWillPop: () async {
Navigator.pop(context);
controller.resumeCamera();
return true;
},
child: AlertDialog(
title: const Text('Hatalı Barkod veya Ürün'),
content: const Text('Yanlış ürünü veya barkodu okutuyor olabilirsiniz. Kontrol edip tekrar ediniz.'),
actions: <Widget>[
TextButton(
onPressed: () {
Navigator.pop(context);
controller.resumeCamera();
},
child: const Text('Tamam')),
],
),
);
});
}
});
}
qr_code_scanner no longer supported . Since the underlying frameworks of this package, zxing for android and MTBBarcodescanner for iOS are both not longer maintaned . use mobile_scanner
When I tried to change the state from outside the function the state is not changing.
void _showAlertDialog(BuildContext context) {
// flutter defined function
showDialog(
context: context,
builder: (BuildContext context) {
// return object of type Dialog
return AlertDialog(
title: const Text("Diabetes Prediction"),
content: StatefulBuilder(
return _predictedResult == "" ? Column(
mainAxisSize: MainAxisSize.min,
children: [
const CircularProgressIndicator(),
Text(loadingText),
],
) : Text(_predictedResult);
},
),
actions: <Widget>[
// usually buttons at the bottom of the dialog
TextButton(
child: const Text("OK"),
onPressed: () {
Navigator.of(context).pop();
},
),
],
);
},
);
}
Elevated button helps in calling the _showAlertDialog the loadingText is declared inside the class
ElevatedButton(
child: const Text("Predict"),
onPressed: () async {
// Pregnancies, Glucose, Blood Pressure, Insulin, Skin Thickness, Pedigree Function, Weight,
// Height, Age
_predictedResult = "";
loadingText = "";
var data = _formatData();
var result = Serializer().serialize(data);
_showAlertDialog(context);
setState(() {
loadingText = "Sending data to server...";
});
await Future.delayed(const Duration(seconds: 2), (){
});
setState(() {
loadingText = "Analyzing data...";
});
// await Future.delayed(const Duration(seconds: 2), (){
// print("data received");
// });
await _predict(result);
},
),
The output comes as Sending data to server...
String _predictedResult = '';
StreamController<String>? controller;
String loadingText = '';
#override
Widget build(BuildContext context) {
return Scaffold(
backgroundColor: Colors.grey[900],
body: Center(
child: ElevatedButton(
child: const Text("Predict"),
onPressed: () async {
controller = StreamController<String>();
// Pregnancies, Glucose, Blood Pressure, Insulin, Skin Thickness, Pedigree Function, Weight,
// Height, Age
_predictedResult = "";
loadingText = "";
_showAlertDialog(context);
controller!.add("Sending data to server...");
await Future.delayed(const Duration(seconds: 2), () {});
controller!.add("Analyzing data...");
await Future.delayed(const Duration(seconds: 2), () {
print("data received");
});
controller!.add("data received!");
},
),
),
);
}
void _showAlertDialog(BuildContext context) {
// flutter defined function
showDialog(
context: context,
builder: (BuildContext context) {
// return object of type Dialog
return AlertDialog(
title: const Text("Diabetes Prediction"),
content: StreamBuilder(
stream: controller!.stream,
builder: (context, AsyncSnapshot<String> snap) {
return _predictedResult == ""
? Column(
mainAxisSize: MainAxisSize.min,
children: [
const CircularProgressIndicator(),
Text(snap.data ?? "Loading..."),
],
)
: Text(_predictedResult);
},
),
actions: <Widget>[
// usually buttons at the bottom of the dialog
TextButton(
child: const Text("OK"),
onPressed: () {
Navigator.of(context).pop();
},
),
],
);
},
);
}
Use Stream
Flutter Stream Basics for Beginners
I'm writing an alertDialog where the user can type a name.
The alertDialog has a "OK" button and a "Annulla" button. I want the "OK" button to be disabled while the textField is empty, and then enabled when the user types something.
I'm using a statefulBuilder as recommended by some answers here on StackOverflow, but clearly my implementation is not working.
// Function to display a dialog to insert a new item to the list
Future<void> _displayItemAddDialog(BuildContext context, provider) async {
String itemName;
// clear the textField and add the item to the list
Future<void> onOKPressed() {
_textFieldController.clear();
Navigator.pop(context);
provider.addItem(itemName);
}
showDialog(
context: context,
builder: (context) {
return StatefulBuilder(
builder: (BuildContext context, StateSetter setState) {
// used to check if to enable the OK button
bool okButtonEnabled = false;
return AlertDialog(
title: Text('Inserisci Nome Oggetto'),
content: TextField(
onChanged: (value) {
itemName = value;
print(value);
// if the TextField is not empty then enable the button
if (value != "") {
// not working :(
setState() => okButtonEnabled = true;
}
},
controller: _textFieldController,
decoration: InputDecoration(hintText: 'Nome'),
),
actions: <Widget>[
TextButton(
onPressed: () {
_textFieldController.clear();
Navigator.pop(context);
},
child: Text('Annulla'),
),
TextButton(
// if button enabled then I change the assigned function
onPressed: okButtonEnabled ? onOKPressed : null,
child: Text('OK')),
],
);
});
});
}
You should move your okButtonEnabled outside StatefulBuilder, so right above it.
showDialog(
context: context,
builder: (context) {
// Move okButtonEnabled here
bool okButtonEnabled = false;
return StatefulBuilder(
builder: (BuildContext context, StateSetter setState) {
return AlertDialog(
title: Text('Inserisci Nome Oggetto'),
content: TextField(
onChanged: (value) {
itemName = value;
print(value);
// if the TextField is not empty then enable the button
if (value != "") {
setState(() => okButtonEnabled = true);
}
},
controller: _textFieldController,
decoration: InputDecoration(hintText: 'Nome'),
),
actions: <Widget>[
TextButton(
onPressed: () {
_textFieldController.clear();
Navigator.pop(context);
},
child: Text('Annulla'),
),
TextButton(
// if button enabled then I change the assigned function
onPressed: okButtonEnabled ? onOKPressed : null,
child: Text('OK')),
],
);
},
);
},
);
i have an issue with the widget unmounted with dismissdirection action on flutter. When I left swipe the dismissible item with the deleted action confirmed, the error occured as following:
The following assertion was thrown while notifying status listeners for AnimationController:
This widget has been unmounted, so the State no longer has a context (and should be considered
defunct).
Consider canceling any active work during "dispose" or using the "mounted" getter to determine if
the State is still active.
The full error codes are here
My code:
home_page.dart. The homepage I use statefulwidget and redirect to ExpensesCategoryHistory() Screen.
class HomePage extends StatefulWidget {
const HomePage({Key key, this.database, this.budget}) : super(key: key);
final DatabaseService database;
final Budget budget;
#override
_HomePageState createState() => _HomePageState();
}
class _HomePageState extends State<HomePage> {
#override
Widget build(BuildContext context) {
final database = Provider.of<DatabaseService>(context, listen: false);
PersistentTabController _controller;
_controller = PersistentTabController(initialIndex: 0);
return PersistentTabView(
context,
controller: _controller,
screens: _buildScreens(),
items: _navBarsItems(),
confineInSafeArea: true,
backgroundColor: Colors.white,
handleAndroidBackButtonPress: true,
resizeToAvoidBottomInset: true,
stateManagement: true,
hideNavigationBarWhenKeyboardShows: true, // Recommended to set 'resizeToAvoidBottomInset' as true while using this argument. Default is true.
decoration: NavBarDecoration(
borderRadius: BorderRadius.circular(10.0),
colorBehindNavBar: Colors.white,
),
popAllScreensOnTapOfSelectedTab: true,
popActionScreens: PopActionScreensType.all,
itemAnimationProperties: ItemAnimationProperties( // Navigation Bar's items animation properties.
duration: Duration(milliseconds: 200),
curve: Curves.ease,
),
screenTransitionAnimation: ScreenTransitionAnimation( // Screen transition animation on change of selected tab.
animateTabTransition: true,
curve: Curves.ease,
duration: Duration(milliseconds: 200),
),
navBarStyle: NavBarStyle.style15, // Choose the nav bar style with this property.
);
}
}
List<Widget> _buildScreens() {
return [
Home(),
ExpensesCategoryHistory(),
BudgetPage(),
Container(),
Container()
];
}
Then, in the ExpensesCategoryHistory() class. So, user can select the category and prompt expenses based on the category (when the list item is tapped.) Refer Steps 4 of error occured with images
class ExpensesCategoryHistory extends StatefulWidget {
#override
_ExpensesCategoryHistoryState createState() => _ExpensesCategoryHistoryState();
}
class _ExpensesCategoryHistoryState extends State<ExpensesCategoryHistory> {
var categoryList = ["Beauty", "Entertainment", "Food & Drinks", "Groceries", "Medical", "Transport", "Others"];
#override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
backgroundColor: kPrimaryColor,
title: Text('Expenses History By Category'),
),
body: _buildContents(context),
floatingActionButton: FloatingActionButton(
backgroundColor: kPrimaryColor,
child: Icon(Icons.add),
onPressed: () => EditExpensesPage.show(context,
database: Provider.of<DatabaseService>(context, listen: false),
),
),
);
}
Widget _buildContents(BuildContext context) {
final database = Provider.of<DatabaseService>(context, listen: false);
return ListView.builder(
itemCount: categoryList.length,
itemBuilder: (context, index){
return ListTile(
title: Text('${categoryList[index]}'),
onTap: () {
if(mounted){
Navigator.push(
context,
MaterialPageRoute<Widget>(
builder: (context) => ExpensesHistory(Category: categoryList[index]),
),
);
}
},
);
},
);
}
}
Then, lasty the main issue is here. When user select on delete action after left swipe of item. The error code occurred and did not perform Navigator.of(context).pop(true)
ExpensesHistory class
class ExpensesHistory extends StatelessWidget {
// Declare a field that holds the Todo.
final String Category;
// In the constructor, require a Todo.
ExpensesHistory({Key key, #required this.Category}) : super(key: key);
Future<void> _delete(BuildContext context, Expense expense) async {
try {
final database = Provider.of<DatabaseService>(context, listen: false);
await database.deleteExpenses(expense);
} on PlatformException catch (e) { //handle error
PlatformExceptionAlertDialog(
title: 'Operation failed',
exception: e,
).show(context);
}
}
#override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
backgroundColor: kPrimaryColor,
title: Text('$Category'),
),
body: _buildContents(context),
floatingActionButton: FloatingActionButton(
backgroundColor: kPrimaryColor,
child: Icon(Icons.add),
onPressed: () => EditExpensesPage.show(context,
database: Provider.of<DatabaseService>(context, listen: false),
),
),
);
}
Widget _buildContents(BuildContext context) {
final database = Provider.of<DatabaseService>(context, listen: false);
List myExpenses = [];
return StreamBuilder<List<Expense>>(
stream: database.expensesStream(),
builder: (context, snapshot) {
return ListItemsBuilder<Expense>(
snapshot: snapshot,
itemBuilder: (context, expense) =>
Dismissible(
confirmDismiss: (DismissDirection direction) async {
return await showDialog(
context: context,
builder: (BuildContext context) {
return AlertDialog(
title: const Text("Delete Confirmation"),
content: const Text(
"Are you sure you want to delete this item?"),
actions: <Widget>[
FlatButton(
onPressed: () => Navigator.of(context).pop(true),
child: const Text("Delete"),
),
FlatButton(
onPressed: () => Navigator.of(context).pop(false),
child: const Text("Cancel"),
),
],
);
},
);
},
key: UniqueKey(),
background: slideRightBackground(),
direction: DismissDirection.endToStart,
onDismissed: (direction) => _delete(context, expense),
child: ExpensesListTile(
expense: expense,
onTap: () => ExpensesCategoryPage.show(context, expense),
category: Category,
),
),
);
},
);
}
}
Steps of error occured with images
To ExpensesCategoryHistory class, render page here
Select category Beauty here
Render Beauty category item here
Confirm Delete action on category item after left swipe of dismissible here
Selected delete, prompt error code + item not deleted
Return stay step 3
Sorry for the full codes, but I wonder if my implementation issue on flutter/dart? I have been solving for days but still same issue, even I've read couple related issues on stackoverflow. Any help is greatly appreciated !
Tested
All widgets are mounted within 3 classes after routing.
Error occurred when deleted action is tapped but widget is mounted still.
I am trying to write a program to check if the time selected by the user already exists in the firebase firestore or not. If it does then I navigate back to the page where they select time again.
But as of now, I am succeeded in sending the date and time to firebase and but not the latter part.
DateTime _eventDate;
bool processing;
String _time;
bool conditionsStatisfied ;
#override
void initState() {
super.initState();
_eventDate = DateTime.now();
processing = false ;
}
inside showDatePicker()
setState(() {
print('inside the setState of listTile');
_eventDate = picked ;
});
inside the button (SAVE):
onPressed: () async {
if (_eventDate != null) {
final QuerySnapshot result = await FirebaseFirestore
.instance
.collection('events')
.where('event_date', isEqualTo: this._eventDate)
.where('selected_time', isEqualTo: this._time)
.get();
final List <DocumentSnapshot> document = result.docs;
if (document.length > 0) {
setState(() {
print('inside the method matching conditions');
showAlertDialogue(context);
});
}else{
final data = {
// "title": _title.text,
'selected_time ': this._time,
"event_date": this._eventDate
};
if (widget.note != null) {
await eventDBS.updateData(widget.note.id, data);
} else {
await eventDBS.create(data);
}
Navigator.pop(context);
setState(() {
processing = false;
});
}
};
some guidance needed on how do I resolve this issue!
Also, because of the else statement now the program won't write the date into firestore.
After Alot of research, I came to realize that if you send the data from calendar in DateTime format then, because of the timestamp at the end of the Date it becomes impossible to match to dates. Hence I formatted the DateTime value into (DD/MM/YYYY).
Here is the rest of the code for reference:
class _AddEventPageState extends State<AddEventPage> {
String _eventDate;
bool processing;
String _time;
#override
void initState() {
super.initState();
// _eventDate = DateTime.now();
processing = false ;
}
#override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(title: Text('Please select a date'),),
body: Column(
children: [
hourMinute30Interval(),
Text('$_time'),
ListView(
scrollDirection: Axis.vertical,
shrinkWrap: true,
children: <Widget>[
ListTile(
title: Text(
'$_eventDate'),
onTap: () async {
DateTime picked = await showDatePicker(context: context,
initialDate: DateTime.now(),
firstDate: DateTime(DateTime.now().year - 1),
lastDate: DateTime(DateTime.now().year + 10),);
if (picked != null) {
setState(() {
print('inside the setState of listTile');
_eventDate = DateFormat('dd/MM/yyyy').format(picked) ;
});
}
},
),
SizedBox(height: 10.0),
ListTile(
title: Center(
child: Text('Select time for appointment!', style: TextStyle(
fontWeight: FontWeight.bold,
fontSize: 20,
),
),
),
),
processing
? Center(child: CircularProgressIndicator())
: Padding(
padding: const EdgeInsets.symmetric(horizontal: 16.0),
child: Material(
elevation: 5.0,
borderRadius: BorderRadius.circular(30.0),
color: Theme
.of(context)
.primaryColor,
child:MaterialButton(
child: Text('SAVE', style: TextStyle(
fontSize: 20,
color: Colors.white,
fontWeight: FontWeight.bold,
)),
onPressed: () async {
if (_eventDate != null) {
AddingEventsUsingRajeshMethod().getAvailableSlots(
_eventDate, _time).then((QuerySnapshot docs) async {
if (docs.docs.length == 1) {
showAlertDialogue(context);
}
else{
final data = {
// "title": _title.text,
'selected_time': this._time,
"event_date": _eventDate,
};
if (widget.note != null) {
await eventDBS.updateData(widget.note.id, data);
} else {
await eventDBS.create(data);
}
Navigator.pop(context);
setState(() {
processing = false;
});
}
});
}
}
),
),
),
],
),
],
),
);
}
showAlertDialogue method :
showAlertDialogue(BuildContext context) {
Widget okButton = FlatButton(onPressed: (){
Timer(Duration(milliseconds: 500), () {
Navigator.push(
context,
MaterialPageRoute(builder: (context) => datePicker()),
);
});
}, child: Text(' OK! '));
AlertDialog alert = AlertDialog(
title: Text('Slot unavailable'),
content: Text('This slot is already booked please select another slot'),
actions: [
okButton,
],
);
showDialog(context: context ,
builder: (BuildContext context){
return alert ;
}
);
}
The hourMinute30Interval() is nothing but a Widget that returns a timePickerSpinner which is a custom Widget. Tap here for that.
The Query that is run after passing the _eventDate and _time is in another class, and it goes as follows :
class AddingEventsUsingRajeshMethod {
getAvailableSlots(String _eventDate , String _time){
return FirebaseFirestore.instance
.collection('events')
.where('event_date', isEqualTo: _eventDate )
.where('selected_time', isEqualTo: _time)
.get();
}
}
You can name it something prettier ;)