How to call a function only once while the page is active in flutter - flutter

I have a dart page voterhome(like dashboard),at first when I arrive at voterhome it will call the getuserdetails() function and get the adhar of user to pass it to other pages so I can navigate to other pages (like vote,voteregister ..etc)
when I return back to voterhome from other pages it will call the getuserdetails()function again because the function is inside initstate() I don't want this to happen how can I do that
my code :
voterhome.dart
import 'package:cloud_firestore/cloud_firestore.dart';
import 'package:election/services/IntoLogin.dart';
import 'package:election/pages/Voter/Vote.dart';
import 'package:election/pages/Voter/VoteRegister.dart';
import 'package:firebase_auth/firebase_auth.dart';
import 'package:flutter/cupertino.dart';
import 'package:flutter/foundation.dart';
import 'package:flutter/material.dart';
import 'package:http/http.dart';
import 'package:web3dart/web3dart.dart';
import '../../services/Auth.dart';
import '../../utils/Constants.dart';
import '../../services/Electioninfo.dart';
import '../../services/VerifyEmail.dart';
class VoterHome extends StatefulWidget {
//getting required parameters to pass on to vote and authorize
final Web3Client ethClient;
final String electionName;
final String electionaddress;
const VoterHome({Key? key, required this.ethClient, required this.electionName, required this.electionaddress}) : super(key: key);
#override
State<VoterHome> createState() => _VoterHomeState();
}
class _VoterHomeState extends State<VoterHome> {
//creating clients
late Client? httpClient;//http client
late Web3Client? ethclient;// eth client
//sign out user
final User? user = Auth().currentuser;
Future<void>signOut()async{
await Auth().signOut();
if(!mounted)return;
Navigator.pushAndRemoveUntil(context, MaterialPageRoute(builder: (context)=>IntroLogin()), (route) => false);
}
// voters info
var email;
var adhar;
var name;
var phone;
//checking if voter authorized or voted // dont have to do this because we does this on the respected pages
late bool isAuth = false;//if he is authorized
late bool isVoted = false;//if he is voted
Future<void>getUserDetail() async {
try {
final DocumentSnapshot voters = await FirebaseFirestore.instance
.collection('voters')
.doc(user?.email)
.get();
if (voters.data() != null) {
email = voters.get('email');
name = voters.get('name');
phone = voters.get('phone');
adhar = voters.get('adharnum');
print('adhar is $adhar');
}else{
print('cannot find details');
}
showSnackBar(succesdetailsnackSnack);
} catch (e) {
if (kDebugMode) {
print('get check user ::::: $e');
showSnackBar(errordetailsnackSnack);
}
}
}//function to check ends
#override
void initState() {
httpClient = Client();
ethclient = Web3Client(infura_url, httpClient!);
WidgetsBinding.instance.addPostFrameCallback((_) async {
await getUserDetail();
setState(() { });
});
super.initState();
}
#override
Widget build(BuildContext context) {
Map<String,dynamic> voterdata = {'name':name,'adharnum':adhar.toString(),'email':email,};
if(user!.emailVerified){
return Scaffold(
appBar: AppBar(
leading: IconButton(onPressed: () { signOut(); }, icon: const Icon(Icons.logout),),
actions: [IconButton(onPressed:(){setState(() {});}, icon: const Icon(Icons.refresh))],
title: const Text('Voter DASHBOARD'),backgroundColor: Colors.cyan,),
body: SingleChildScrollView(
child: Column(
children: [
Container(
padding: const EdgeInsets.all(24),
margin: const EdgeInsets.only(bottom: 16),
child: InkWell(
onTap: () {
Navigator.push(
context,
MaterialPageRoute(
builder: (context) => VoteRegister(electionName:widget.electionName,
ethClient: widget.ethClient, electionaddress:widget.electionaddress,adhar:adhar,)));
},
child: Card(borderOnForeground: true,elevation: 4,
child: Column(
children: [
Container(height: 200,
decoration: BoxDecoration(
borderRadius: BorderRadius.circular(15),
image: const DecorationImage(
image: AssetImage('assets/undraw/electionday.png')))),
Container(decoration: const BoxDecoration(color: Colors.cyan),width: double.infinity,
child: const Center(
child: Text('Register to Vote',style: TextStyle(
fontWeight: FontWeight.bold,fontSize:16,color: Colors.white),),
),
)
],
),
),
),
),
Container(
padding: const EdgeInsets.all(24),
margin: const EdgeInsets.only(bottom: 16),
child: InkWell(
onTap: () {
Navigator.push(
context,
MaterialPageRoute(
builder: (context) => VoterVote(ethClient:ethclient!,electionName:widget.electionName,
electionaddress:widget.electionaddress ,votermap:voterdata,)));
},
child: Card(borderOnForeground: true,elevation: 4,
child: Column(
children: [
Container(height: 200,
decoration: BoxDecoration(
borderRadius: BorderRadius.circular(15),
image: const DecorationImage(
image: AssetImage('assets/undraw/upvote.png')))),
Container(decoration: const BoxDecoration(color: Colors.cyan),width: double.infinity,
child: const Center(
child: Text('Vote',style: TextStyle(fontWeight: FontWeight.bold,fontSize:16,color: Colors.white),),
),
)
],
),
),
),
),
Container(
padding: const EdgeInsets.all(24),
margin: const EdgeInsets.only(bottom: 16),
child: InkWell(
onTap: () {
Navigator.push(
context,
MaterialPageRoute(
builder: (context) => ElectionInfo(ethClient:ethclient!,electionName:widget.electionName,
electionAddress:widget.electionaddress,)));
},
child: Card(borderOnForeground: true,elevation: 4,
child: Column(
children: [
Container(height: 200,
decoration: BoxDecoration(
borderRadius: BorderRadius.circular(15),
image: const DecorationImage(
image: AssetImage('assets/undraw/electionday.png')))),
Container(decoration: const BoxDecoration(color: Colors.cyan),width: double.infinity,
child: const Center(
child: Text('Election details',style: TextStyle(
fontWeight: FontWeight.bold,fontSize:16,color: Colors.white),),
),
)
],
),
),
),
),
],
),
)
);
}else{
return Scaffold(
appBar:AppBar( ///app bar
backgroundColor: Colors.cyan,
leading: IconButton(
onPressed: () {
signOut();
},
icon: const Icon(Icons.logout_sharp),
),
title: const Text('Verify Voter email'),
actions: [
IconButton(
onPressed: () {
refresh();
},
icon: const Icon(Icons.refresh))
],
),
body: Container(margin: const EdgeInsets.only(top: 56),
child: Center(
child: Column(
children: [
Text('Your Email ${user?.email} is not verified'),
const SizedBox(height: 24,),
ElevatedButton(
onPressed: () {
Navigator.pushAndRemoveUntil(
context,
MaterialPageRoute(builder: (context) => const VerifyEmail()),
(route) => false);
},
child: const Text('Verify Email'))
],
),
),
),
);
}
}
//function to refresh using setstate
void refresh() {
setState(() {});
}
//snackbar
SnackBar errordetailsnackSnack = const SnackBar(content: Text('You are not logged in if you are please check your internet connection'));
SnackBar succesdetailsnackSnack = const SnackBar(content: Text('successfull'));
SnackBar votedSnack = const SnackBar(content: Text('You have already voted'));
SnackBar RegisterSnack = const SnackBar(content: Text('You have already registered'));
// SnackBar errorSnack = const SnackBar(content: Text('Fill all the details'));
// SnackBar datanullSnack = const SnackBar(content: Text('No users registerd yet'));
//function to show snackbar
ScaffoldFeatureController<SnackBar, SnackBarClosedReason> showSnackBar(SnackBar snackBar) {
return ScaffoldMessenger.of(context).showSnackBar(snackBar);
}
}
vote.dart //when i go to voterhome() from this it again calls the function
import 'package:cloud_firestore/cloud_firestore.dart';
import 'package:election/pages/Voter/VoterHome.dart';
import 'package:election/utils/Constants.dart';
import 'package:firebase_auth/firebase_auth.dart';
import 'package:flutter/cupertino.dart';
import 'package:flutter/foundation.dart';
import 'package:flutter/material.dart';
import 'package:web3dart/web3dart.dart';
import '../../services/Auth.dart';
import '../../services/functions.dart';
import '../../services/IntoLogin.dart';
class VoterVote extends StatefulWidget {
final Web3Client ethClient;
final String electionName;
final String electionaddress;
final votermap;
const VoterVote({Key? key, required this.ethClient, required this.electionName, required this.electionaddress, this.votermap,}) : super(key: key);
#override
State<VoterVote> createState() => _VoterVoteState();
}
class _VoterVoteState extends State<VoterVote> {
final User? user = Auth().currentuser;//fi// rebase auth current user initialization
//sign out user function
Future<void> signOut() async {
if (!mounted) return;
await Auth().signOut();
if (!mounted) return;
Navigator.pushAndRemoveUntil(
context,
MaterialPageRoute(builder: (context) => IntroLogin()),
(route) => false);
}
//checking if the voter is already voted
late bool isAuth = false;
late bool isVoted = false;
Future<void>getUserDetail() async {
var voterdetails = widget.votermap;
try {
final DocumentSnapshot voters = await FirebaseFirestore.instance
.collection('Election')
.doc(widget.electionName).collection('voterAuth').doc(voterdetails['adharnum'])
.get();
if (voters.data() != null) {
isAuth = voters.get('isAuth');
isVoted = voters.get('isVoted');
}else{
isAuth = false;
isVoted= false;
print('cannot find details');
}
} catch (e) {
if (kDebugMode) {
print('get check user ::::: $e');
}
}
}//function to check ends
TextEditingController privatekeyController = TextEditingController();
#override
void initState() {
WidgetsBinding.instance.addPostFrameCallback((_) async {
await getUserDetail();
setState(() { });
});
super.initState();
}
#override
Widget build(BuildContext context) {
if(isVoted== true && isAuth == true){
return Scaffold(
appBar: AppBar(
backgroundColor: Colors.cyan,
leading: IconButton(onPressed: (){signOut();},icon: const Icon(Icons.logout_sharp),),
title:const Text('Vote'),
actions: [
IconButton(onPressed:(){
refresh();
}, icon: const Icon(Icons.refresh))
],
),
body: const Center(child: Text('you have already voted sir'),),
);
}else if(isVoted == false&&isAuth == true){
return Scaffold(
appBar:AppBar(
backgroundColor: Colors.cyan,
leading: IconButton(onPressed: (){signOut();},icon: const Icon(Icons.logout_sharp),),
title:const Text('Vote'),
actions: [
IconButton(onPressed:(){
refresh();
}, icon: const Icon(Icons.refresh))
],
),
body: Container(margin:const EdgeInsets.only(bottom: 56,top: 24),alignment: Alignment.topCenter,
child:SingleChildScrollView(
child: Column(
children: [
Center(
child: SelectableText("$voter_private_key && $voter_key2 && $voter_key3")
),
const SizedBox(height: 24,),
Container(padding: const EdgeInsets.all(16),
child: TextField(
controller: privatekeyController,
decoration:
const InputDecoration(hintText: 'Private key for voting',border: OutlineInputBorder(
borderRadius:
BorderRadius.all(Radius.circular(8))))),
),
const SizedBox(height: 24,),
SingleChildScrollView(
child: StreamBuilder<List>(stream:getCandidatesNum(widget.ethClient,widget.electionaddress).asStream(),
builder: (context, snapshot) {
if (snapshot.connectionState == ConnectionState.waiting) {
return const Center(
child: CircularProgressIndicator(),
);
} else {
return Column(
children: [
for (int i = 0; i < snapshot.data![0].toInt(); i++)
FutureBuilder<List>(
future: candidateInfo(i, widget.ethClient,widget.electionaddress),
builder: (context, candidatesnapshot) {
if (candidatesnapshot.connectionState ==
ConnectionState.waiting) {
return const Center(
child: CircularProgressIndicator(),
);
} else {
return Card( //card to represent the candidate
color: Colors.cyan,
child: SizedBox(height: 100,
child: Center(
child: ListTile(
title: Text('Name: ${candidatesnapshot.data![0][0]}'),
subtitle: Text('Votes: ${candidatesnapshot.data![0][1]}'),
leading: ConstrainedBox(
constraints: const BoxConstraints(
minHeight: 90,
minWidth: 90,
maxHeight: 100,
maxWidth: 100,
),
child:const Image(image: AssetImage('assets/undraw/electionday.png')),
),
trailing: ElevatedButton(
onPressed: ()async {
try{
await vote(i,widget.ethClient,privatekeyController.text,widget.electionaddress);
await registerAuth();
gotoDashboard();
showSnackBar(succesdetailsnackSnack);
}catch(e){
if (kDebugMode) {
print(e);
}
showSnackBar(errordetailsnackSnack);
gotoDashboard();
}
}, child: const Text('Vote'),),
),
),
),
);
}
})
],
);
}
},
),
),
],
),
),
),
);
}else{
return Scaffold(
appBar: AppBar(
backgroundColor: Colors.cyan,
leading: IconButton(onPressed: (){signOut();},icon: const Icon(Icons.logout_sharp),),
title:const Text('Vote'),
actions: [
IconButton(onPressed:(){
refresh();
}, icon: const Icon(Icons.refresh))
],
),
body: const Center(child: Text('you are not authorized please authorize first'),),
);
}
}
//snackbar
SnackBar errordetailsnackSnack = const SnackBar(content: Text(' already voted or please check your internet connection'));
SnackBar succesdetailsnackSnack = const SnackBar(content: Text('successfull'));
SnackBar voterSnack = const SnackBar(content: Text('you are a voter logout from voter account'));
SnackBar adminSnack = const SnackBar(content: Text('you are an admin logout from Admin account'));
// SnackBar errorSnack = const SnackBar(content: Text('Fill all the details'));
// SnackBar datanullSnack = const SnackBar(content: Text('No users registerd yet'));
//function to show snackbar
ScaffoldFeatureController<SnackBar, SnackBarClosedReason> showSnackBar(SnackBar snackBar) {
return ScaffoldMessenger.of(context).showSnackBar(snackBar);
}
void refresh() {
setState(() {});
}
Future<void> registerAuth() async {
var voterdetails = widget.votermap;
try {
await FirebaseFirestore.instance.collection('Election').doc(widget.electionName).collection('voterAuth').doc(voterdetails['adharnum']).update({'isVoted':true});
print('updated data aaaaaaaaaaaaaaa');
} catch (e) {
if (kDebugMode) {
print('failed to register on firebase $e');
}
}
}
void gotoDashboard(){
Navigator.pushAndRemoveUntil(context,MaterialPageRoute(builder:(context)=>VoterHome(ethClient: widget.ethClient,
electionName: widget.electionName, electionaddress: widget.electionaddress)), (route) => false);
}
}

One way to achieve what you want is by making your page retain its state.
Try this code
You have to add with AutomaticKeepAliveClientMixin to the screen like in the snippet below.
Override this #override bool get wantKeepAlive => true;.
Inside build method, add this super.build(context);.
class _TestScreen extends State<TestScreen> with AutomaticKeepAliveClientMixin<TestScreen> {
#override
bool get wantKeepAlive => true;
Widget build(BuildContext context) {
super.build(context);
return Scaffold();
}
}
After doing this, your page will not call initState() everytime that page is loaded.

When you navigate from VoteHome page to VoteRegister page, you use the push method. The push method returns a Future object. You can use the then method of Future to execute your code after VoteRegister was popped from the navigation stack or in other words the user pressed back button in appbar.
Navigator.of(context)
.push(MaterialPageRoute(
builder: (context) => VoteRegister(electionName:widget.electionName,ethClient: widget.ethClient, electionaddress:widget.electionaddress,adhar:adhar,)))
.then((value) {
// you can do what you need here
// setState etc.
// also reload your data here
});

Related

Everything matches but I still get "Bad state: field does not exist within the DocumentSnapshotPlatform"

So i'm pretty lost trying to get my app to fetch not only the desired text from firestore but also the image i placed there. I know the error "Bad state: field does not exist within the DocumentSnapshotPlatform" implies that either I'm missing field or there's something wrong with at least one of those fields. There's only four things I'm trying to fetch from firestore "imgUrl, title, desc, organizer" i have checked for spelling and order and i can't figure it out. I have also checked firestore to see if the order and spelling was correct and i dont see what's the issue. Please help, Thank you so much in advanced.
////////////////////////////// News Class Starts\\\\\\\\\\\\\\
import 'package:myfuji/screens/CrudMethods.dart';
import 'package:cloud_firestore/cloud_firestore.dart';
import 'package:flutter/material.dart';
import 'CrudMethods.dart';
import 'add_blog.dart';
class News extends StatefulWidget {
#override
_NewsState createState() => _NewsState();
}
class _NewsState extends State<News> {
CrudMethods crudMethods = CrudMethods();
late QuerySnapshot blogSnapshot;
#override
void initState() {
crudMethods.getData().then((result) {
blogSnapshot = result;
setState(() {});
});
super.initState();
}
Widget blogsList() {
return ListView.builder(
padding: const EdgeInsets.only(top: 24),
itemCount: blogSnapshot.docs.length,
itemBuilder: (context, index) {
return BlogTile(
organizer: blogSnapshot.docs[index].get('Organizer'),
desc: blogSnapshot.docs[index].get('desc'),
imgUrl: blogSnapshot.docs[index].get('imgUrl'),
title: blogSnapshot.docs[index].get('title'),
);
},
);
}
#override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: const Text("Events"),
),
body: Container(
child: blogSnapshot != null
? blogsList()
: const Center(
child: CircularProgressIndicator(),
)),
floatingActionButton: FloatingActionButton(
child: const Icon(Icons.add),
onPressed: () {
Navigator.push(
context, MaterialPageRoute(builder: (context) => AddBlog()));
},
),
);
}
}
class BlogTile extends StatelessWidget {
final String imgUrl, title, desc, organizer;
const BlogTile(
{required this.organizer,
required this.desc,
required this.imgUrl,
required this.title});
#override
Widget build(BuildContext context) {
return Container(
margin: const EdgeInsets.only(bottom: 24, right: 16, left: 16),
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Container(
child: ClipRRect(
borderRadius: const BorderRadius.all(Radius.circular(8)),
child: Image.network(
imgUrl,
width: MediaQuery.of(context).size.width,
fit: BoxFit.cover,
height: 200,
),
),
),
const SizedBox(height: 16),
Text(
title,
style: const TextStyle(fontSize: 17),
),
const SizedBox(height: 2),
Text(
'$desc - By $organizer',
style: const TextStyle(fontSize: 14),
)
],
),
);
}
}
///////////////////////////////////////////// class AddBlog begins here \\\\\\\\\\\
import 'dart:io';
import 'package:myfuji/screens/CrudMethods.dart';
import 'package:firebase_storage/firebase_storage.dart';
import 'package:flutter/material.dart';
import 'package:image_picker/image_picker.dart';
import 'package:random_string/random_string.dart';
class AddBlog extends StatefulWidget {
#override
_AddBlogState createState() => _AddBlogState();
}
class _AddBlogState extends State<AddBlog> {
//
late File selectedImage;
final picker = ImagePicker();
bool isLoading = false;
CrudMethods crudMethods = new CrudMethods();
Future getImage() async {
final pickedFile = await picker.getImage(source: ImageSource.gallery);
setState(() {
if (pickedFile != null) {
selectedImage = File(pickedFile.path);
} else {
print('No image selected.');
}
});
}
Future<void> uploadBlog() async {
if (selectedImage != null) {
// upload the image
setState(() {
isLoading = true;
});
Reference firebaseStorageRef = FirebaseStorage.instance
.ref()
.child("events/")
.child("${randomAlphaNumeric(9)}.jpg");
final UploadTask task = firebaseStorageRef.putFile(selectedImage);
var imageUrl;
await task.whenComplete(() async {
try {
imageUrl = await firebaseStorageRef.getDownloadURL();
} catch (onError) {
print("Error");
}
print(imageUrl);
});
// print(downloadUrl);
Map<String, dynamic> blogData = {
"imgUrl": imageUrl,
"Organizer": authorTextEditingController.text,
"title": titleTextEditingController.text,
"desc": descTextEditingController.text
};
crudMethods.addData(blogData).then((value) {
setState(() {
isLoading = false;
});
Navigator.pop(context);
});
// upload the blog info
}
}
//
TextEditingController titleTextEditingController =
new TextEditingController();
TextEditingController descTextEditingController = new TextEditingController();
TextEditingController authorTextEditingController =
new TextEditingController();
#override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: Text("Create Blog"),
actions: [
GestureDetector(
onTap: () {
uploadBlog();
},
child: Padding(
padding: EdgeInsets.symmetric(horizontal: 16),
child: Icon(Icons.file_upload)),
)
],
),
body: isLoading
? Container(
child: Center(
child: CircularProgressIndicator(),
))
: SingleChildScrollView(
child: Container(
margin: EdgeInsets.symmetric(horizontal: 16),
child: Column(
children: [
GestureDetector(
onTap: () {
getImage();
},
child: selectedImage != null
? Container(
height: 150,
margin: EdgeInsets.symmetric(vertical: 24),
width: MediaQuery.of(context).size.width,
child: ClipRRect(
borderRadius:
BorderRadius.all(Radius.circular(8)),
child: Image.file(
selectedImage,
fit: BoxFit.cover,
),
),
)
: Container(
height: 150,
decoration: BoxDecoration(
color: Colors.grey,
borderRadius:
BorderRadius.all(Radius.circular(8))),
margin: EdgeInsets.symmetric(vertical: 24),
width: MediaQuery.of(context).size.width,
child: Icon(
Icons.camera_alt,
color: Colors.white,
),
),
),
TextField(
controller: titleTextEditingController,
decoration: InputDecoration(hintText: "enter title"),
),
TextField(
controller: descTextEditingController,
decoration: InputDecoration(hintText: "enter desc"),
),
TextField(
controller: authorTextEditingController,
decoration:
InputDecoration(hintText: "enter author name"),
),
],
)),
),
);
}
}
///////////////////////////////////////////// class CrudMethods begins here \\\\\\\\\\\
import 'package:cloud_firestore/cloud_firestore.dart';
class CrudMethods {
Future<void> addData(blogData) async {
print(blogData);
FirebaseFirestore.instance
.collection("events/")
.add(blogData)
.then((value) => print(value))
.catchError((e) {
print(e);
});
}
getData() async {
return await FirebaseFirestore.instance.collection("events/").get();
}
}
/////////////////////////////////////////////firestore\\\\\\\\\\\\\
This maybe related to having “/“ after collection name here:
.collection("events/")
Instead try this:
.collection("events")
Also it may be best to change child to collection here:
Reference firebaseStorageRef = FirebaseStorage.instance
.ref()
.child("events/")
Try to see if you get data back by running this:
itemCount: blogSnapshot.docs.length,
itemBuilder: (context, index) {
QuerySnapshot snap = blogSnapshot.data; // Snapshot
List<DocumentSnapshot> items = snap.documents; // List of Documents
DocumentSnapshot item = items[index]; Specific Document
return BlogTile(
organizer: item.data['Organizer'],
desc: item.data['desc'],
imgUrl: item.data['imgUrl'],
title: item.data['title'],
);
},
I think you need to utilize a QueryDocumentSnapshot to access the data in the document.

getx obx not updating avatar image - Flutter GetX

what I want to achieve is to change the image in CircleAvatar when I'm selecting an image, here is the code:
ProfileController:
class ProfileController extends GetxController {
final TextEditingController emailController = TextEditingController();
final ImagePicker _picker = ImagePicker();
Rx<String?> avatarPath = null.obs;
avatarFromCamera() async {
var localAvatar = await _picker.pickImage(
source: ImageSource.camera, imageQuality: 50
);
if (localAvatar != null) {
avatarPath = localAvatar.path.obs;
update();
}
}
avatarFromGallery() async {
var localAvatar = await _picker.pickImage(
source: ImageSource.gallery, imageQuality: 50
);
if (localAvatar != null) {
avatarPath = localAvatar.path.obs;
update();
}
}
String? emailValidator(String? value) {
if (value == null || value.isEmpty) {
return null;
}
if (!EmailValidator.validate(value, false)) {
return 'Invalid email address';
}
}
#override
void onClose() {
emailController.dispose();
super.onClose();
}
String? emailValidator(String? value) {
if (value == null || value.isEmpty) {
return null;
}
if (!EmailValidator.validate(value, false)) {
return 'Invalid email address';
}
}
void save(GlobalKey<FormState> profileFormKey) {
if (profileFormKey.currentState!.validate()) {
print('valid');
}
}
}
and here is the ProfileScreen widget:
lass ProfileScreen extends StatelessWidget {
final ProfileController _profileController = Get.put<ProfileController>(ProfileController());
GlobalKey<FormState> profileFormKey = GlobalKey<FormState>();
ProfileScreen({Key? key}) : super(key: key);
#override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: const Text('Update user details'),
),
body: SingleChildScrollView(
child: Form(
key: profileFormKey,
child: Column(
children: [
Padding(
padding: const EdgeInsets.all(30.0),
child: TextFormField(
keyboardType: TextInputType.text,
controller: _profileController.emailController,
decoration: const InputDecoration(
labelText: 'Enter email',
),
validator: _profileController.emailValidator,
),
),
Center(
child: GestureDetector(
onTap: () {
showModalBottomSheet(
context: context,
builder: (BuildContext bc) {
return SafeArea(
child: Wrap(
children: <Widget>[
ListTile(
leading: const Icon(Icons.photo_library),
title: const Text('Photo Library'),
onTap: () {
_profileController.avatarFromGallery();
Navigator.of(context).pop();
}),
ListTile(
leading: const Icon(Icons.photo_camera),
title: const Text('Camera'),
onTap: () {
_profileController.avatarFromCamera();
Navigator.of(context).pop();
},
),
],
),
);
}
);
},
child: CircleAvatar(
radius: 55,
backgroundColor: Colors.pink,
child: Obx(() =>(_profileController.avatarPath.value != null)
? ClipRRect(
borderRadius: BorderRadius.circular(50),
child: Image.file(
File(_profileController.avatarPath.value!),
width: 100,
height: 100,
fit: BoxFit.fitHeight
),
)
: Container(
decoration: BoxDecoration(
color: Colors.grey[200],
borderRadius: BorderRadius.circular(50)),
width: 100,
height: 100,
child: Icon(
Icons.camera_alt,
color: Colors.grey[800],
),
),
),
),
)),
Container(
margin: const EdgeInsets.all(10),
width: double.infinity,
child: MaterialButton(
color: Colors.blue,
onPressed: () => _profileController.save(profileFormKey),
child: const Text(
'Submit',
style: TextStyle(color: Colors.white),
),
),
),
],
),
),
),
);
}
}
as you can see, I have Obx and my avatarPath reactive, and I'm running update everywhere I changing it, but it's not udpated. I also tried to use empty string as initial value of imagePath like this Rx<String> avatarPath = ''.obs; and it's not working. What I'm doing wrong??? Thank you in advice!!!
There are two things to revise for it. Firstly, change avatarPath = localAvatar.path.obs; with avatarPath.value = localAvatar.path;. Because localAvatar.path.obs create the new observable and changes will not be reflected to previous observers.
Secondly, create a new stateless widget having the widget tree of bottom sheet's builder like
showModalBottomSheet(
context: context,
builder: (BuildContext bc) {
return CustomBottomView();
}
);
Then inside the CustomBottomView copy your bottom sheet widget tree.
class CustomBottomView extends GetView<ProfileController> {
return YourBottomSheetWidgetTreeHere();
}
Dont worry about ProfileController here. You have already put it in DI in previous route. First follow the first step if you still face the problem second step will definitely resolve it.

RenderFlex children have non-zero flex but incoming height constraints are unbounded in flatter

I have a page code written in an android studio. On this page, I display a list of objects, as well as a widget to search for these objects. When I clicked on the search widget, I had the following problem: "The following statement was thrown during layout:
RenderFlex is overcrowded 31 pixels below.
The corresponding widget that caused the errors was: "But then I found the answer to this question and added" SingleChildScrollView ".
But then I had this problem: "RenderFlex have non-zero flexibility, but the input height limits are unlimited." . And I can't solve it in any way. I will be grateful for your help. Here is my code:
import 'package:flutter/material.dart';
import 'package:flutter_app_seals/model/post/form_unseals.dart';
import 'package:flutter_app_seals/model/post/form_seals.dart';
import 'package:flutter_app_seals/model/setting/globalvar.dart' as global;
import 'dart:async';
import 'dart:io';
import 'dart:convert';
void main() => runApp(JsonParseObjectSts_UN());
class JsonParseObjectSts_UN extends StatefulWidget {
JsonParseObjectSts_UN() : super();
#override
_JsonParseObjectsState createState() => _JsonParseObjectsState();
}
class _JsonParseObjectsState extends State <StatefulWidget> {
List<UserDetails> _searchResult = [];
List<UserDetails> _userDetails = [];
TextEditingController controller = new TextEditingController();
final String url = global.urlVar ;
// Get json result and convert it to model. Then add
Future<Null> getUserDetails() async {
HttpClient client = new HttpClient();
client.badCertificateCallback = ((X509Certificate cert, String host, int port) => true);
final request = await client
.getUrl(Uri.parse(url))
.timeout(Duration(seconds: 5));
HttpClientResponse response = await request.close();
var responseBody = await response.transform(utf8.decoder).join();
final responseJson = json.decode(responseBody);
setState(() {
for (Map user in responseJson) {
_userDetails.add(UserDetails.fromJson(user));
}
});
}
#override
void initState() {
super.initState();
getUserDetails();
}
Widget _buildUsersList() {
return new ListView.builder(
itemCount: _userDetails.length,
itemBuilder: (context, index) {
return new Card(
color: (_userDetails[index].sealed == "Так") ? Colors.redAccent : Colors.greenAccent,
margin: EdgeInsets.symmetric(vertical: 7),
child: ListTile(
title: Text(
_userDetails[index].name,
style: TextStyle(fontSize: 25),
),
subtitle: Text("Запломбований:${_userDetails[index].sealed}"),
leading: Icon(
Icons.home_outlined,
size: 30,
color: Colors.black87,
),
onTap: () =>
{
if ('Так' == _userDetails[index].sealed) {
global.nameObj = _userDetails[index].name,
global.sealsNumb = _userDetails[index].seal_number,
global.typesOp = 'Так',
Navigator.push(
context,
MaterialPageRoute(builder: (context) => Unseals()),
)
}
else{
{
global.nameObj = _userDetails[index].name,
global.typesOp = 'Ні',
Navigator.push(
context,
MaterialPageRoute(builder: (context) => Form_seals()),
)
}
}
}
),
);
},
);
}
Widget _buildSearchResults() {
return new ListView.builder(
itemCount: _searchResult.length,
itemBuilder: (context, i) {
return new Card(
color: (_searchResult[i].sealed == "Так") ? Colors.redAccent : Colors.greenAccent,
margin: EdgeInsets.symmetric(vertical: 7),
child: ListTile(
title: Text(
_searchResult[i].name,
style: TextStyle(fontSize: 25),
),
subtitle: Text("Запломбований:${_searchResult[i].sealed}"),
leading: Icon(
Icons.home_outlined,
size: 30,
color: Colors.black87,
),
onTap: () =>
{
if ('Так' == _searchResult[i].sealed) {
global.nameObj = _searchResult[i].name,
global.sealsNumb = _searchResult[i].seal_number,
global.typesOp = 'Так',
Navigator.push(
context,
MaterialPageRoute(builder: (context) => Unseals()),
)
}
else{
{
global.nameObj = _searchResult[i].name,
global.typesOp = 'Ні',
Navigator.push(
context,
MaterialPageRoute(builder: (context) => Form_seals()),
)
}
}
}
),
);
},
);
}
Widget _buildSearchBox() {
return new Padding(
padding: const EdgeInsets.all(8.0),
child: new Card(
child: new ListTile(
leading: new Icon(Icons.search),
title: new TextField(
controller: controller,
decoration: new InputDecoration(
hintText: 'Пошук', border: InputBorder.none),
onChanged: onSearchTextChanged,
),
trailing: new IconButton(
icon: new Icon(Icons.cancel),
onPressed: () {
controller.clear();
onSearchTextChanged('');
},
),
),
),
);
}
Widget _buildBody() {
return new Scaffold(
body:Container(
decoration: BoxDecoration(
gradient: LinearGradient(
colors: [Colors.blue, Colors.white],
begin: Alignment.topCenter,
end: Alignment.bottomCenter),
),
child: SingleChildScrollView(
child:Column(
children: <Widget>[
new Container(
color: Theme.of(context).primaryColor, child: _buildSearchBox()),
new Expanded(
child: _searchResult.length != 0 || controller.text.isNotEmpty
? _buildSearchResults()
: _buildUsersList()),
],
),
),
),
);
}
#override
Widget build(BuildContext context) {
return new Scaffold(
body: _buildBody()
);
}
onSearchTextChanged(String text) async {
_searchResult.clear();
if (text.isEmpty) {
setState(() {});
return;
}
_userDetails.forEach((userDetail) {
if (userDetail.name.contains(text) ) _searchResult.add(userDetail);
});
setState(() {});
}
}
class UserDetails {
final String name, seal_number,sealed;
UserDetails({this.name, this.sealed, this.seal_number});
factory UserDetails.fromJson(Map<String, dynamic> json) {
return new UserDetails(
sealed: json['sealed'],
name: json['name'],
seal_number: json['seal_number'],
);
}
}
My scrin:
My scrin when I want to use search:

how to increase the size of the window with the search result in flutter?

I have a page code written in an android studio. On this page, I display a list of objects, as well as a widget to search for these objects. When I clicked on the search widget,my list of objects is shrinking, and it is almost invisible (it is at the top). Can this be fixed somehow so that it can be seen on the whole page ??
import 'package:flutter/material.dart';
import 'package:flutter_app_seals/model/post/form_unseals.dart';
import 'package:flutter_app_seals/model/post/form_seals.dart';
import 'package:flutter_app_seals/model/setting/globalvar.dart' as global;
import 'dart:async';
import 'dart:io';
import 'dart:convert';
void main() => runApp(JsonParseObjectSts_UN());
class JsonParseObjectSts_UN extends StatefulWidget {
JsonParseObjectSts_UN() : super();
#override
_JsonParseObjectsState createState() => _JsonParseObjectsState();
}
class _JsonParseObjectsState extends State <StatefulWidget> {
List<UserDetails> _searchResult = [];
List<UserDetails> _userDetails = [];
TextEditingController controller = new TextEditingController();
final String url = global.urlVar ;
// Get json result and convert it to model. Then add
Future<Null> getUserDetails() async {
HttpClient client = new HttpClient();
client.badCertificateCallback = ((X509Certificate cert, String host, int port) => true);
final request = await client
.getUrl(Uri.parse(url))
.timeout(Duration(seconds: 5));
HttpClientResponse response = await request.close();
var responseBody = await response.transform(utf8.decoder).join();
final responseJson = json.decode(responseBody);
setState(() {
for (Map user in responseJson) {
_userDetails.add(UserDetails.fromJson(user));
}
});
}
#override
void initState() {
super.initState();
getUserDetails();
}
Widget _buildUsersList() {
return new ListView.builder(
itemCount: _userDetails.length,
itemBuilder: (context, index) {
return new Card(
color: (_userDetails[index].sealed == "Так") ? Colors.redAccent : Colors.greenAccent,
margin: EdgeInsets.symmetric(vertical: 7),
child: ListTile(
title: Text(
_userDetails[index].name,
style: TextStyle(fontSize: 25),
),
subtitle: Text("Запломбований:${_userDetails[index].sealed}"),
leading: Icon(
Icons.home_outlined,
size: 30,
color: Colors.black87,
),
onTap: () =>
{
if ('Так' == _userDetails[index].sealed) {
global.nameObj = _userDetails[index].name,
global.sealsNumb = _userDetails[index].seal_number,
global.typesOp = 'Так',
Navigator.push(
context,
MaterialPageRoute(builder: (context) => Unseals()),
)
}
else{
{
global.nameObj = _userDetails[index].name,
global.typesOp = 'Ні',
Navigator.push(
context,
MaterialPageRoute(builder: (context) => Form_seals()),
)
}
}
}
),
);
},
);
}
Widget _buildSearchResults() {
return new ListView.builder(
itemCount: _searchResult.length,
itemBuilder: (context, i) {
return new Card(
color: (_searchResult[i].sealed == "Так") ? Colors.redAccent : Colors.greenAccent,
margin: EdgeInsets.symmetric(vertical: 7),
child: ListTile(
title: Text(
_searchResult[i].name,
style: TextStyle(fontSize: 25),
),
subtitle: Text("Запломбований:${_searchResult[i].sealed}"),
leading: Icon(
Icons.home_outlined,
size: 30,
color: Colors.black87,
),
onTap: () =>
{
if ('Так' == _searchResult[i].sealed) {
global.nameObj = _searchResult[i].name,
global.sealsNumb = _searchResult[i].seal_number,
global.typesOp = 'Так',
Navigator.push(
context,
MaterialPageRoute(builder: (context) => Unseals()),
)
}
else{
{
global.nameObj = _searchResult[i].name,
global.typesOp = 'Ні',
Navigator.push(
context,
MaterialPageRoute(builder: (context) => Form_seals()),
)
}
}
}
),
);
},
);
}
Widget _buildSearchBox() {
return new Padding(
padding: EdgeInsets.all(7.0),
child: new Card(
child: new ListTile(
leading: new Icon(Icons.search),
title: new TextField(
controller: controller,
decoration: new InputDecoration(
hintText: 'Пошук', border: InputBorder.none),
onChanged: onSearchTextChanged,
),
trailing: new IconButton(
icon: new Icon(Icons.cancel),
onPressed: () {
controller.clear();
onSearchTextChanged('');
},
),
),
),
);
}
Widget _buildBody() {
return new Scaffold(
body:Container(
decoration: BoxDecoration(
gradient: LinearGradient(
colors: [Colors.blue, Colors.white],
begin: Alignment.topCenter,
end: Alignment.bottomCenter),
),
child:Column(
children: <Widget>[
new Expanded( flex: 1, child: _buildSearchBox()),
new Expanded(flex:8,
child: _searchResult.length != 0 || controller.text.isNotEmpty
? _buildSearchResults()
: _buildUsersList()),
],
),
),
);
}
#override
Widget build(BuildContext context) {
return new Scaffold(
body: _buildBody()
);
}
onSearchTextChanged(String text) async {
_searchResult.clear();
if (text.isEmpty) {
setState(() {});
return;
}
_userDetails.forEach((userDetail) {
if (userDetail.name.contains(text) ) _searchResult.add(userDetail);
});
setState(() {});
}
}
class UserDetails {
final String name, seal_number,sealed;
UserDetails({this.name, this.sealed, this.seal_number});
factory UserDetails.fromJson(Map<String, dynamic> json) {
return new UserDetails(
sealed: json['sealed'],
name: json['name'],
seal_number: json['seal_number'],
);
}
}
My page:
My page with search:
I have looked at your code several times, I just noticed a small error.
For the results of your searches, you try to display the "List" of searches in a ListView.builder, which is in a column in the parent container does not have a defined height.
Solution: Set a height for the main container and the problem will be solved

I'm implementing google sign in using flutter and firestore. On click, user should should sign in with google and navigate user to a create user page

I have a google_sign_in button which when clicked should present the google sign in then the user should be redirected to a CreateAccountPage. The page has an input and a submit button. When the user submits their preferred username, the google account data together with the created username should be stored as a document in users collection cloud firestore database and the user redirected to another page, say TimelinePage.
Below is my home.dart file:
import 'package:cloud_firestore/cloud_firestore.dart';
import 'package:flutter/cupertino.dart';
import 'package:flutter/material.dart';
import 'package:google_sign_in/google_sign_in.dart';
import 'package:musagram/pages/activity_feed.dart';
import 'package:musagram/pages/create_account.dart';
import 'package:musagram/pages/profile.dart';
import 'package:musagram/pages/search.dart';
import 'package:musagram/pages/timeline.dart';
import 'package:musagram/pages/upload.dart';
final GoogleSignIn googleSignIn = GoogleSignIn();
final usersRef = FirebaseFirestore.instance.collection('users');
final DateTime timestamp = DateTime.now();
class Home extends StatefulWidget {
#override
_HomeState createState() => _HomeState();
}
class _HomeState extends State<Home> {
bool isAuth = false;
PageController pageController;
int pageIndex = 0;
#override
void initState() {
super.initState();
pageController = PageController();
// Detects when user signed in
googleSignIn.onCurrentUserChanged.listen((account) {
handleSignIn(account);
}, onError: (err) {
print('Error signing in: $err');
});
// Reauthenticate user when app is opened
googleSignIn.signInSilently(suppressErrors: false).then((account) {
handleSignIn(account);
}).catchError((err) {
print('Error signing in: $err');
});
}
handleSignIn(GoogleSignInAccount account) {
if (account != null) {
createUserInFirestore();
setState(() {
isAuth = true;
});
} else {
setState(() {
isAuth = false;
});
}
}
createUserInFirestore() async {
// 1) check if user exists in users collection in database (according to their id)
final GoogleSignInAccount user = googleSignIn.currentUser;
final DocumentSnapshot doc = await usersRef.doc(user.id).get();
if (!doc.exists) {
// 2) if the user doesn't exist, then we want to take them to the create account page
final username = await Navigator.push(
context, MaterialPageRoute(builder: (context) => CreateAccount()));
// 3) get username from create account, use it to make new user document in users collection
usersRef.doc(user.id).set({
"id": user.id,
"username": username,
"photoUrl": user.photoUrl,
"email": user.email,
"displayName": user.displayName,
"bio": "",
"timestamp": timestamp
});
}
}
#override
void dispose() {
pageController.dispose();
super.dispose();
}
login() {
googleSignIn.signIn();
}
logout() {
googleSignIn.signOut();
}
onPageChanged(int pageIndex) {
setState(() {
this.pageIndex = pageIndex;
});
}
onTap(int pageIndex) {
pageController.animateToPage(
pageIndex,
duration: Duration(milliseconds: 300),
curve: Curves.easeInOut,
);
}
Scaffold buildAuthScreen() {
return Scaffold(
body: PageView(
children: <Widget>[
// Timeline(),
ElevatedButton(
child: Text('Logout'),
onPressed: logout,
),
ActivityFeed(),
Upload(),
Search(),
Profile(),
],
controller: pageController,
onPageChanged: onPageChanged,
physics: NeverScrollableScrollPhysics(),
),
bottomNavigationBar: CupertinoTabBar(
currentIndex: pageIndex,
onTap: onTap,
activeColor: Theme.of(context).primaryColor,
items: [
BottomNavigationBarItem(icon: Icon(Icons.whatshot)),
BottomNavigationBarItem(icon: Icon(Icons.notifications_active)),
BottomNavigationBarItem(
icon: Icon(
Icons.photo_camera,
size: 35.0,
),
),
BottomNavigationBarItem(icon: Icon(Icons.search)),
BottomNavigationBarItem(icon: Icon(Icons.account_circle)),
]),
);
// return RaisedButton(
// child: Text('Logout'),
// onPressed: logout,
// );
}
Scaffold buildUnAuthScreen() {
return Scaffold(
body: Container(
decoration: BoxDecoration(
gradient: LinearGradient(
begin: Alignment.topRight,
end: Alignment.bottomLeft,
colors: [
Theme.of(context).accentColor,
Theme.of(context).primaryColor,
],
),
),
alignment: Alignment.center,
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
crossAxisAlignment: CrossAxisAlignment.center,
children: <Widget>[
Text(
'MusaGram',
style: TextStyle(
fontFamily: "Signatra",
fontSize: 90.0,
color: Colors.white,
),
),
GestureDetector(
onTap: login,
child: Container(
width: 260.0,
height: 60.0,
decoration: BoxDecoration(
image: DecorationImage(
image: AssetImage(
'assets/images/google_signin_button.png',
),
fit: BoxFit.cover,
),
),
),
)
],
),
),
);
}
#override
Widget build(BuildContext context) {
return isAuth ? buildAuthScreen() : buildUnAuthScreen();
}
}
And here's the create_account.dart
import 'package:flutter/material.dart';
import 'package:musagram/widgets/header.dart';
class CreateAccount extends StatefulWidget {
#override
_CreateAccountState createState() => _CreateAccountState();
}
class _CreateAccountState extends State<CreateAccount> {
final _formKey = GlobalKey<FormState>();
String username;
submit() {
_formKey.currentState.save();
Navigator.pop(context, username);
}
#override
Widget build(BuildContext parentContext) {
return Scaffold(
appBar: header(context, titleText: "Set up your profile"),
body: ListView(
children: <Widget>[
Container(
child: Column(
children: <Widget>[
Padding(
padding: EdgeInsets.only(top: 25.0),
child: Center(
child: Text(
"Create a username",
style: TextStyle(fontSize: 25.0),
),
),
),
Padding(
padding: EdgeInsets.all(16.0),
child: Container(
child: Form(
key: _formKey,
child: TextFormField(
onSaved: (val) => username = val,
decoration: InputDecoration(
border: OutlineInputBorder(),
labelText: "Username",
labelStyle: TextStyle(fontSize: 15.0),
hintText: "Must be at least 3 characters",
),
),
),
),
),
GestureDetector(
onTap: submit,
child: Container(
height: 50.0,
width: 350.0,
decoration: BoxDecoration(
color: Colors.blue,
borderRadius: BorderRadius.circular(7.0),
),
child: Center(
child: Text(
"Submit",
style: TextStyle(
color: Colors.white,
fontSize: 15.0,
fontWeight: FontWeight.bold,
),
),),
),
),
],
),
),
],
),
);
}
}
method of google sign in looks like this. It will first authenticate the user then it will check is users data present on the firestore. if it's present data will not be overridden and else data will be added.
also at the end you can navigate to your createAccountPage. and you can pass the required values to the constructor. directly map of data or values.
Future<UserCredential> signInWithGoogle() async {
//first trigger authentication
final GoogleSignInAccount googleUser = await GoogleSignIn().signIn();
//obtain the auth details
final GoogleSignInAuthentication googleAuth =
await googleUser.authentication;
//create a new credentials
final GoogleAuthCredential credential = GoogleAuthProvider.credential(
accessToken: googleAuth.accessToken, idToken: googleAuth.idToken);
//saving the user data to shared preferences
SharedPrefsHelper _sharedpref = new SharedPrefsHelper();
// // sign in method
try {
UserCredential firebaseUser =
await FirebaseAuth.instance.signInWithCredential(credential);
if (firebaseUser != null) {
// Check is already sign up
final QuerySnapshot result = await FirebaseFirestore.instance
.collection('users')
.where('id', isEqualTo: firebaseUser.user.uid)
.get();
//userdata to update at firebase
//passing this via constructor to the next page
Map<String, dynamic> userdata = {
'useremail': firebaseUser.user.email,
'displayname': firebaseUser.user.displayName,
'photoUrl': firebaseUser.user.photoURL,
'userid': firebaseUser.user.uid,
};
// navigate . if you don't have context here you can pass this as a paramater to the
method. no issue if you call inside build or init
//userdata is map. just for handling easily
//you can also create simple variables and pass each value to them.
Navigator.pushReplacement(context, CreateUserAccount(userdata));
}
return firebaseUser;
} catch (error) {
print(error);
}
}
Inside the CreateUserAccount
class CreateUserAccount extends StatefulWidget {
final Map<String, dynamic> userdata;
CreateUserAccount(this.userdata);
#override
CreateUserAccountState createState() => CreateUserAccountState();
}
class CreateUserAccountState extends State<CreateUserAccount> {
TextEditingController _textController = new TextEditingController();
#override
Widget build(BuildContext context) {
return Scaffold(
body: Container(
child: Column(
children: [
Container(
margin: const EdgeInsets.symmetric(horizontal: 16, vertical: 20),
child: TextField(
controller: _textController,
),
),
ElevatedButton(
onPressed: () async{
//get the username from textfield
//and update data to firebase
widget.userdata['username'] = _textController.text;
await FirebaseFirestore.instance
.collection('users')
.doc(widget.userdata['userId'])
.set(
widget.userdata,
SetOptions(merge: true),
);
Navigator.pushReplacement(context, MaterialPageRoute(builder: (context) => TimeLinePage());
},
child: Text("submit"),
),
],
),
));
}
}
Inside the CreateUserAccount we simply added the username in map and added record in firebase. Then you can navigate to the timeline after this.