I wonder if i can use Navigator afther i get API data and I want to navigate to another page when i use ElevatedButton. But for some reason i cannot navigate to another page when i want to use it inside FutureBuilder.
This is the full code:
ElevatedButton(
style: ElevatedButton.styleFrom(
fixedSize: const Size.fromHeight(20),
maximumSize: const Size.fromWidth(250),
primary: clientSettings
.getColor("login_button_color"),
shape: const StadiumBorder(),
),
onPressed: () {
var credentials = {
"data[email]": _emailController.text,
"data[password]":
_passwordController.text
};
FutureBuilder(
future:
DataFeed().getDataFeed(credentials),
builder: (BuildContext context,
AsyncSnapshot<dynamic> snapshot) {
if (snapshot.hasData) {
var dataFeed = snapshot.data;
var _list = dataFeed["data"]
["views"]
.values
.toList();
Navigator.of(context)
.pushAndRemoveUntil(
MaterialPageRoute(
builder: (context) =>
AppView(
selectedMenuItems:
_list[0])),
(Route<dynamic> route) =>
false);
return dataFeed;
} else {
return const CircularProgressIndicator();
}
},
);
},
child: Text(
'Login',
style: TextStyle(
fontFamily:
clientSettings.getLogoAsset(
"login_font_family"),
fontSize: clientSettings
.getNumber("login_font_size"),
color: clientSettings
.getColor("login_text_color")),
)),
You can directly await an api call and navigate to the next page. You dont need a future builder
onPressed:() async{
var response = await DataFeed().getDataFeed(credentials);
Navigator.push();
}
Here reponse will have same data as snapshot.data.
Related
I am currently developing an App. At my registration page, I want to check the password and password confirm text box for equality and check for duplicate email or phone registration. I have a function, which calls my api in order to check, whether the email/phone number is already registered. I call the function when I press the button, so I wrapped it in a FutureBuilder and assigned it to the future property of the builder. I know, it is better to load the future element in the initState method and assign it to a variable, which then you assign to the builder property, but I can't do that, as I only get the email/phone number after the widget is built the first time.
So, now to my problem:
I assign the desired error text to the textbox in the build method. I have wrapped the assignments in a setState.
So, normally the error text should be shown, if i press the register button.
However, this only happnes after I hot-reload the flutter app. I find this very confusing and I tried several things:
I tried to wrap it only with a setState, I tried it with a StatefulBuilder, I tried to outsource the assignment into a function, which then I call in the build method. Nothing worked, so I am asking here and hope to get an answer to my specific question :)
Here is the Code of the button in the build method:
StatefulBuilder(
builder: (context, setState) => FutureBuilder(
future: checkForDuplicates(
_emailBox.controller.text, _phoneBox.controller.text),
builder: (BuildContext context,
AsyncSnapshot<String> snapshot) {
return ElevatedButton(
style: ElevatedButton.styleFrom(
backgroundColor:
Theme.of(context).colorScheme.primary,
shape: RoundedRectangleBorder(
borderRadius: BorderRadius.circular(50),
),
),
child: Text(
"Registrieren",
style: TextStyle(
fontSize: 20,
color: Theme.of(context).dividerColor),
),
onPressed: () {
setState(
() {
_dateController.text = _backupDate;
_dateController.text.isEmpty
? _validate = true
: _validate = false;
},
);
if (_pBox.controller.text !=
_confirmPasswordBox.controller.text) {
setState(() {
_pBox.errorText =
"Passwörter stimmen nicht überein!";
_confirmPasswordBox.errorText =
"Passwörter stimmen nicht überein!";
});
} else {
if (_formKey.currentState!.validate()) {
if (snapshot.hasData) {
if (snapshot.data == "email-already-exists") {
setState(() {
_emailBox.errorText =
"Email existiert beretis!";
});
} else if (snapshot.data ==
"phone-already-exists") {
setState(() {
_emailBox.errorText =
"Telefonnummer existiert beretis!";
});
} else {
//no duplicates
if (_isChecked) {
Navigator.pushAndRemoveUntil(
context,
MaterialPageRoute(
builder: (context) =>
AddPersonalDataScreen(
firstName: _fNameBox
.controller.text
.trim(),
lastName: _lNameBox
.controller.text
.trim(),
dateOfBirth:
DateTime
.parse(
_dateInput.text),
email: _emailBox
.controller.text
.trim(),
password:
_pBox.controller.text,
phoneNumber: _phoneBox
.controller.text
.trim()),
),
(route) => false,
);
}
}
} else {
showDialog(
context: context,
builder: (ctx) => AlertDialog(
title: Text(
"AGB's nicht akzeptiert!",
style: TextStyle(
fontSize: 20,
color:
Theme.of(context).dividerColor),
),
content: Text(
"Um sich zu Registrieren müssen Sie den AGB's zustimmen!",
style: TextStyle(
fontSize: 20,
color:
Theme.of(context).dividerColor),
),
actions: <Widget>[
TextButton(
onPressed: () {
Navigator.of(context).pop();
},
child: Container(
decoration: BoxDecoration(
borderRadius:
const BorderRadius.all(
Radius.circular(50),
),
color: Theme.of(context)
.colorScheme
.primary,
),
padding: const EdgeInsets.all(25),
child: Text(
"Zurück",
style: TextStyle(
fontSize: 20,
color: Theme.of(context)
.dividerColor),
),
),
),
],
),
);
}
}
}
},
);
},
),
),
Here is the function, which calls the api (but i dont think it is important to the problem):
Future<String> checkForDuplicates(String email, String phoneNumber) async {
var phpScript = "XXX";
var response = await http.post(Uri.parse(phpScript), body: {
'email': email,
'phoneNumber': phoneNumber,
});
return response.body;
}
Hi I'm newbie at coding and Flutter is my first language.
I want to delete Firestore data(Chat List) which is in the ListView from the StreamBuilder when user long tab the 'OK' button in AlertDialog.
(Process Summary:
longPress one item of the ListViewBuilder
show check AlertDialog
tab 'OK'
OK button's Function execute)
But when i longPress the item, then the deleteFunction(OK button's function) is automatically executed before i see the dialog.
How can I make it?
It is really difficult!
Here is my code
StreamBuilder(
stream: chatRoomDB,
builder: (context, AsyncSnapshot<QuerySnapshot> snapshot) {
if (snapshot.connectionState == ConnectionState.active &&
snapshot.data != null) {
return ListView(
children: snapshot.data!.docs
.map<Widget>((DocumentSnapshot ds) {
Map<String, dynamic> data =
ds.data()! as Map<String, dynamic>;
receiverProfileImageURL = data['ProfileImageURL'];
receiverUID = data['UserID'];
receiverNickName = data['NickName'];
msgContent = data['MsgContent'];
chatID = data['ChatID'];
return Container(
margin: EdgeInsets.all(3),
child: ListTile(
shape: Border(
bottom:
BorderSide(color: Colors.grey, width: 1)),
onTap: () {
Navigator.push(
context,
MaterialPageRoute(
builder: (context) => ChattingRoom(
chatID: data['ChatID'],
receiverUID: data['UserID'],
receiverImageURL:
data['ProfileImageURL'],
receiverNickName: data['NickName'],
)));
},
onLongPress: () => checkDialog2(
context: context,
msg: 'Delete?',
okButtonString: 'OK',
okFunction: chatDBDeleteFunction(
data['UserID'], data['ChatID']),
noButtonString: 'No'),
leading: Container(
child: CircleAvatar(
foregroundImage: NetworkImage(
'$receiverProfileImageURL',
),
backgroundImage:
NetworkImage('$errorImageURL'),
)),
title: Text('$receiverNickName'),
),
);
}).toList());
}
return Container();
})
checkDialog2({
required context,
required String? msg,
required String? okButtonString,
required Future<dynamic> okFunction,
required String? noButtonString,
}) {
print(1);
okFunctions(okFunc) {
print(3);
okFunction;
Navigator.pop(context);
print(4);
}
print(5);
showDialog(
context: context,
builder: (context) {
print(2);
return AlertDialog(content: Text('$msg'), actions: <Widget>[
TextButton(
onPressed: () => okFunctions(okFunction),
child: Text(
'$okButtonString',
textAlign: TextAlign.end,
),
),
TextButton(
onPressed: () {
Navigator.pop(context);
},
child: Text(
'$noButtonString',
textAlign: TextAlign.end,
),
),
]);
});
}
Future chatDBDeleteFunction(receiverUID, chatID) async {
await FirebaseFirestore.instance
.collection('ChatMessage')
.doc('${FirebaseAuth.instance.currentUser?.uid}')
.collection(chatID)
.get()
.then((QuerySnapshot qs) => {
qs.docs.forEach((DocumentSnapshot snapshot) {
snapshot.reference.delete();
})
});
Log Order is,
print 1->5>2 and automatically deleted + showDialog.
At that time(list item already disappeared), if i tab the 'OK' button, then print '3' and error(because there is nothing to delete)
(ps. although i change the okfunction to the void Function, it starts automatically. same)
Thanks for your help
I would prefer passing the UserId and ChatId and then call the chatDBDeleteFunction(receiverUID, chatID) Function and pass the values from values
TextButton(
onPressed: () {
Class.chatDBDeleteFunction(receiverUID, chatID)
}
)
The chatDBDeleteFunction(receiverUID, chatID) function can be static
I need to update the percentage var inside the AlertDialog, I know that you can't update anything inside the showDialog() which I am using here because it is built in another Widget tree, but worth asking, it would really be very helpful.
Thanks in advance. :)
Update
The uploadTask listener redo the function until sth like Navigator pushes to a different screen.
My Code
Future storageupload() async {
await checkInternet();
try {
if (controller == null) {
dialog('Error', 'Please Provide A Video Name', () => {});
} else {
StorageReference ref = FirebaseStorage.instance
.ref()
.child("Khatma 1")
.child("Videos")
.child(controller.text != null ? controller.text : "");
StorageUploadTask uploadTask = ref.putFile(
File(Variables.lastVideoPath),
StorageMetadata(contentType: 'video/mp4'));
uploadTask.events.listen((event) {
if (uploadTask.isComplete) {
AwesomeDialog(
context: context,
headerAnimationLoop: false,
dialogType: DialogType.SUCCES,
animType: AnimType.BOTTOMSLIDE,
title: 'Uploaded',
desc: 'Your Video Was Successfully Uploaded !',
btnOkOnPress: () => {print('success')},
)..show();
} else {
setState(() {
percentage = event.snapshot.bytesTransferred /
event.snapshot.totalByteCount *
100;
});
}
});
return showDialog(
context: context,
builder: (BuildContext context) {
return AlertDialog(
title: Text('Uploading'),
content: Column(
children: [
Center(
child: Text('Uploading .... Please Be Patient'),
),
SizedBox(
height: height * 0.02,
),
Text(percentage.toString()),
],
),
);
},
);
}
} catch (e) {
print(e);
}
}
code
return StatefulBuilder(
builder: (BuildContext context, void function) => AlertDialog(
title: Text('Uploading'),
content: Column(
children: [
Center(
child: Text('Uploading .... Please Be Patient'),
),
SizedBox(
height: height * 0.02,
),
Text(percentage.toString()),
],
),
),
);
all dialogs are StateLess widgets.... so u can not call setstate to update their data to show on the UI. If u want to do that then u can wrap the dialog inside a StatefullBuilder . which will allow you to call setState.
I have the code which only deals with searching words from cloud_firestore which is a firebase service. Searching is done fine and everything is working fine but i would love to convert my firebase DocumentSnapshot to a custom model. I don't wan;t it to be showing instace of 'DocumentSnapShot' of which i don't wan't. I wan't it to be showing at least instance of 'WordsSearch' when i print(data):
Full search code:
import 'package:cloud_firestore/cloud_firestore.dart';
import 'package:flutter/material.dart';
import 'package:kopala_dictionary/models/words.dart';
import 'package:kopala_dictionary/screens/author/word_details.dart';
import 'package:provider/provider.dart';
class CloudFirestoreSearch extends StatefulWidget {
#override
_CloudFirestoreSearchState createState() => _CloudFirestoreSearchState();
}
class _CloudFirestoreSearchState extends State<CloudFirestoreSearch> {
String name = "";
//words list from snappshots
List<Words> _wordsFromSnapShots(QuerySnapshot snapshot) {
return snapshot.documents.map((doc) {
return Words(
word: doc.data['word'],
englishTranslation: doc.data['english_translation'],
bembaTranslation: doc.data['bemba_translation'],
);
}).toList();
}
#override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
leading: IconButton(
icon: Icon(Icons.arrow_back_ios),
onPressed: () {
Navigator.of(context).pop();
},
),
title: Card(
child: TextField(
decoration: InputDecoration(
prefixIcon: Icon(Icons.search), hintText: 'Search...'),
onChanged: (val) {
setState(() {
name = val;
});
},
),
),
),
body: StreamBuilder<QuerySnapshot>(
stream: (name != "" && name != null)
? Firestore.instance
.collection('words')
.where("word", isGreaterThanOrEqualTo: name)
.snapshots()
: Firestore.instance.collection("words").snapshots(),
builder: (context, snapshot) {
return (snapshot.connectionState == ConnectionState.waiting)
? Center(child: CircularProgressIndicator())
: ListView.builder(
itemCount: snapshot.data.documents.length,
itemBuilder: (context, index) {
DocumentSnapshot data = snapshot.data.documents[index];
print(data);
return Card(
child: Column(
children: <Widget>[
SizedBox(
width: 25,
),
ListTile(
onTap: () {
Navigator.push(
context,
MaterialPageRoute(
builder: (context) =>
WordsDetails(word: data)),
);
},
leading: Text(
data['word'],
style: TextStyle(
fontWeight: FontWeight.w700,
fontSize: 20,
),
),
),
],
),
);
},
);
},
),
);
}
}
And this is a custom WordsSearch class:
class WordsSearch {
final String word;
final String englishTranslation;
final String bembaTranslation;
final DateTime date_posted;
Words(
{this.word,
this.englishTranslation,
this.bembaTranslation,
this.date_posted});
}
You can do this using a .fromFirestore method in your model object.
factory Words.fromFirestore(DocumentSnapshot doc) {
Map data = doc.data();
// you likely need to convert the date_posted field. something like this
Timestamp datePostedStamp = data['date_posted'];
var datePosted = new DateTime.fromMillisecondsSinceEpoch(datePostedStamp.millisecondsSinceEpoch);
return Words(
word: data['word'] ?? '',
englishTranslation: data[''] ?? '',
bembaTranslation: data['bembaTranslation'] ?? '',
date_posted: datePosted
);
}
Elsewhere...
List<Words> _wordsFromSnapShots(QuerySnapshot snapshot) {
return snapshot.documents.map((doc) {
return Words.fromFirestore(doc);
}).toList();
}
Remember to convert the date posted before sending into firebase. Here's how I've done it.
Timestamp.fromMillisecondsSinceEpoch(DateTime.now().millisecondsSinceEpoch)
I'm the newbie Flutter Developer. I got any problem when I tap the custom drawer, body content viewed the widget after that I tap add button with api and success created new data. but when back to drawer can't reload automatically. below are some of my codecs.
I user drawerbehavior version 0.0.8
MyCustomDrawer code
DrawerScaffold(
percentage: 1,
cornerRadius: 0,
appBar: AppBarProps(
title: Text(
"ToDO",
style: TextStyle(fontFamily: 'Acme'),
),
centerTitle: true,
actions: [
IconButton(
icon: Icon(Icons.add),
onPressed: () => tambahAgenda(context, '4', true))
]),
menuView: new MenuView(
menu: menu,
headerView: headerView(context),
footerView: footerView(context),
animation: false,
textStyle: TextStyle(fontFamily: 'Acme', color: Colors.white),
color: Theme.of(context).primaryColor,
selectedItemId: selectedMenuItemId,
onMenuItemSelected: (String itemId) {
selectedMenuItemId = itemId;
if (itemId == 'beranda') {
setState(() => _widget = IsiBerandaUI());
} else if (itemId == 'agenda') {
setState(() => _widget = Container());
} else if (itemId == 'kalenderagenda') {
setState(() => _widget = AgendaCalendarUI());
}
},
),
contentView: Screen(
contentBuilder: (context) => Center(child: _widget),
color: Colors.white,
),
);
_navigateAndDisplaySelection(
BuildContext context, String idKategori, bool statusLogin) async {
final result = await Navigator.push(
context,
MaterialPageRoute(builder: (context) => TambahAgendaUI()),
);
if (result != null) {
// bloc.fetchAllAgendaTGL(idKategori, statusLogin);
setState(() {
selectedMenuItemId = 'kalenderagenda';
_widget = AgendaCalendarUI();
});
Navigator.pushReplacement(
context,
MaterialPageRoute(
builder: (BuildContext context) => Beranda(
selectedMenuItemId: 'kalenderagenda',
)));
Scaffold.of(context)
..removeCurrentSnackBar()
..showSnackBar(SnackBar(
content: Text(
"$result",
style: TextStyle(fontFamily: 'googleSans'),
)));
}
}
and myadd data navigator like this
Navigator.pushReplacement(
context,
MaterialPageRoute(
builder: (BuildContext context) => Beranda(
selectedMenuItemId: 'kalenderagenda',
)
)
);
But When I pushReplacement widget get any error.
any conclusion ???