I am trying to make a todo list app with Flutter. When the homepage loads, the data does not appear on program
I tried QuerySnapshot, '.then', sleep and more but unfortunately none of them worked
I tried to debug and find where is the problem. It looks like while the program loads, the data from Firestore is not ready yet that is why it does not appear on the homepage.
import 'dart:async';
import 'package:flutter/material.dart';
import 'package:cloud_firestore/cloud_firestore.dart';
import 'package:firebase_auth/firebase_auth.dart';
import 'dart:io';
class home extends StatefulWidget {
static const String id = 'home';
#override
_homeState createState() => _homeState();
}
class _homeState extends State<home> {
String userUid;
FirebaseUser loggedInUser;
final _fireStore = Firestore.instance;
final _auth = FirebaseAuth.instance;
List<Container> messageWidgets = [];
void getCurrentUser() async {
final user = await _auth.currentUser();
if (user != null) {
loggedInUser = user;
userUid = loggedInUser.uid;
}
}
getTasks() {
print('///////////////Get tasks///////////////');
return _fireStore
.document('Userss')
.collection('$userUid/Tasks')
.getDocuments();
}
bool boool = false;
var tasks;
#override
void initState() {
getCurrentUser();
getTasks().then((QuerySnapshot docs) {
if (docs.documents.isNotEmpty) {
boool = true;
tasks = docs.documents[0].data;
}
});
super.initState();
}
#override
Widget build(BuildContext context) {
return SafeArea(
child: Center(
child: Column(
children: <Widget>[
boool
? Column(
children: <Widget>[
Text(tasks['Task']),
Text(tasks['Category']),
],
)
: Container(
child: Text('No DATA'),
),
],
)));
}
I expect the data appears when homepage loads but it does not appear
My quick guess is that you need to use setState(), since the data is loaded from Firestore asynchronously.
So something like:
getTasks() {
print('///////////////Get tasks///////////////');
_fireStore
.document('Userss')
.collection('$userUid/Tasks')
.getDocuments().then((QuerySnapshot docs) {
if (docs.documents.isNotEmpty) {
boool = true;
setState(() {
tasks = docs.documents[0].data;
});
}
});
}
Related
I know this is a stupid question, but I'm asking as a newbie to flutter.
I created a getData() method to call Firebase's User data and display it on the app. And to call it, result.data() is saved as a variable name of resultData.
But as you know I can't use Text('user name: $resultData'). How do I solve this? It's a difficult problem for me, since I don't have any programming basics. thank you.
import 'dart:math';
import 'package:flutter/material.dart';
import 'package:firebase_auth/firebase_auth.dart';
import 'package:shipda/screens/login/login_screen.dart';
import 'package:get/get.dart';
import 'package:cloud_firestore/cloud_firestore.dart';
class HomeScreen extends StatefulWidget {
const HomeScreen({Key? key}) : super(key: key);
#override
State<HomeScreen> createState() => _HomeScreenState();
}
class _HomeScreenState extends State<HomeScreen> {
final _authentication = FirebaseAuth.instance;
User? loggedUser;
final firestore = FirebaseFirestore.instance;
void getData() async {
var result = await firestore.collection('user').doc('vUj4U27JoAU6zgFDk6sSZiwadQ13').get();
final resultData = result.data();
}
#override
void initState() {
super.initState();
getCurrentUser();
getData();
}
void getCurrentUser(){
try {
final user = _authentication.currentUser;
if (user != null) {
loggedUser = user;
print(loggedUser!.email);
}
} catch (e){
print(e);
}
}
#override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(),
body: Center(
child: Column(
children: [
Text('Home Screen'),
IconButton(
onPressed: () {
FirebaseAuth.instance.signOut();
Get.to(()=>LoginScreen());
},
icon: Icon(Icons.exit_to_app),
),
IconButton(
onPressed: () {
Get.to(() => LoginScreen());
},
icon: Icon(Icons.login),
),
Text('UserInfo'),
Text('user name: ')
],
),
),
);
}
}
What you are referring to is called state.
It is a complex topic and you will have to start studying it to correctly develop any web based app.
Anyway, as for your situation, you should have resultData be one of the attributes of the _HomeScreenState class.
Then change resultData in a setState method, like this:
setState(() {
resultData = result.data();
});
Then, in the Text widget, you can actually do something like:
Text("My data: " + resultData.ToString())
Instead of ToString of course, use anything you need to actually access the data.
By writing
void getData() async {
var result = await firestore.collection('user').doc('vUj4U27JoAU6zgFDk6sSZiwadQ13').get();
final resultData = result.data();
}
you make resultData only local to the function getData(). You should declare it outside. Also you need to put it in a setState to make it rebuild the screen after loading. I don't know what type it is, but if it's a String for example you could write
String? resultData;
void getData() async {
var result = await firestore.collection('user').doc('vUj4U27JoAU6zgFDk6sSZiwadQ13').get();
setState(() {
resultData = result.data();
});
}
Then you can use Text('user name: $resultData') for example
I'm try to learn how to get token. What is wrong with the code I wrote?
I'm try to learn how to get token.
import 'package:shared_preferences/shared_preferences.dart';
import '../common/constant.dart';
Use this sample:
class UtilSharedPreferences {
static Future<String> getToken() async {
var prefs = await SharedPreferences.getInstance();
return prefs.getString('Token') ?? '';
}
static Future<bool> setToken(String value) async {
var prefs = await SharedPreferences.getInstance();
return prefs.setString('Token', value);
}
}
class MyHomePage extends StatefulWidget {
MyHomePage({
Key? key,
}) : super(key: key);
#override
_MyHomePageState createState() => new _MyHomePageState();
}
class _MyHomePageState extends State<MyHomePage> {
String str = '';
gettoken() async {
str = await UtilSharedPreferences.getToken();
setState(() {});
}
#override
void initState() {
// TODO: implement initState
super.initState();
gettoken();
}
#override
Widget build(BuildContext context) {
return Scaffold(
body: Column(
mainAxisAlignment: MainAxisAlignment.center,
crossAxisAlignment: CrossAxisAlignment.center,
children: [
Text('Token :$str'),
Center(
child: InkWell(
onTap: () {
var result = UtilSharedPreferences.setToken('hi');
if(result){
print('success');
}
},
child: Text('save token'),
),
),
],
),
);
}
}
Future is asynchronous and you must use await to get the desired value.
user = await UtilSharedPreferencs();
The async keyword is required to use async. So implement it separately as a method
void setUser() async {
user = await UtilSharedPreferencs();
}
void initState(){
super.initState();
setUser();
}
Let's say I create a new screen team_screen which is the first parent of the tree.
Now for my team screen there are many widgets, some of theme have their own request, I want to show loader until every widget/request finished and ready.
I thought on 2 approaches.
All the requests are executed in team_screen with future builder and I pass the props to my widgets by demand.
Every widget with request get function that get executed in the async function in the initState function, then in my parent I make to every widget state parameter that is equal to true by the function I passed and when all is don't I stop the loader.
To sum up my problem is how to maintain a widget with many children and requests and showing one loader for entire page, making all the request on same widget? Pass isInitialize function to every widget?.
Which approach is better and if there are more approaches, I would like to hear.
Thank you for your help
Example for the second approach:
import 'package:flutter/material.dart';
import 'package:info_striker/locator.dart';
import 'package:info_striker/models/fixture/fixture.dart';
import 'package:info_striker/models/odds/bookmaker.dart';
import 'package:info_striker/models/synced-team/synced_team.dart';
import 'package:info_striker/services/fixture_service.dart';
import 'package:info_striker/utils/date_utilities.dart';
class TeamNextMatch extends StatefulWidget {
Function isInitialized;
SyncedTeam team;
TeamNextMatch({
Key? key,
required this.isInitialized,
required this.team,
}) : super(key: key);
#override
State<TeamNextMatch> createState() => _TeamNextMatchState();
}
class _TeamNextMatchState extends State<TeamNextMatch> {
Fixture? _fixture;
Bookmaker? _matchResult;
bool _isInitialized = false;
#override
void initState() {
super.initState();
init();
}
init() async {
final response = await locator<FixturesService>().getData(widget.team.id);
if (response != null) {
setState(() {
_fixture = Fixture.fromMap(response["fixture"]);
_matchResult = Bookmaker.fromMap(response["matchResultOdds"]);
});
}
widget.isInitialized(true);
}
#override
Widget build(BuildContext context) {
String? _date;
bool show = _fixture != null && _matchResult != null;
_fixture != null ? "${DateUtilities.getShortDateString(_fixture!.date)}, ${DateUtilities.getTimeString(_fixture!.date)}" : null;
return show
? Column(
children: [
Text(_fixture?.league?["name"]),
if (_date != null) Text(_date),
],
)
: const SizedBox();
}
}
You can show loader as described below -
import 'dart:convert';
import 'package:flutter/material.dart';
import 'package:flutter_application_1/data_model.dart';
import 'package:http/http.dart' as http;
class APiTest extends StatefulWidget {
const APiTest({Key? key}) : super(key: key);
#override
_APiTestState createState() => _APiTestState();
}
class _APiTestState extends State<APiTest> {
final String _url = "https://jsonplaceholder.typicode.com/todos/";
bool _isLoading = true;
final List<DataModel> _allData = [];
#override
void initState() {
super.initState();
_initData().then((value) {
setState(() {
_isLoading = false;
});
});
}
Future<void> _initData() async {
final response = await http.get(Uri.parse(_url));
final List res = jsonDecode(response.body);
res.forEach((element) {
_allData.add(DataModel.fromJson(element));
});
}
#override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: const Text("Loading Demo"),
),
body: Stack(
children: [
ListView.separated(
itemCount: _allData.length,
controller: ScrollController(),
separatorBuilder: (_, __) => const SizedBox(height: 10),
itemBuilder: ((context, index) {
return ListTile(
tileColor: Colors.grey[200],
title: Text(_allData[index].title!),
subtitle: Text(_allData[index].id.toString()),
);
}),
),
if (_isLoading)
const Center(
child: CircularProgressIndicator(),
)
],
),
);
}
}
I am trying to apply conditions on widget when user is logged in, display widget logout else display login button.How is it possible?
Before login
After Login
I used shred_preferences package saving, getting and removing used data.
Here my codes:
shared_preferences
import 'package:shared_preferences/shared_preferences.dart';
class PrefServices{
Future createCache(String username, String password) async {
SharedPreferences _preferences = await SharedPreferences.getInstance();
_preferences.setString("username", username);
_preferences.setString("password", password);
}
Future readCache(String username) async {
SharedPreferences _preferences = await SharedPreferences.getInstance();
var username = _preferences.getString("username")?? "null";
// _preferences.getString("password");
return username;
}
Future<void> removeCache(String username, String password) async {
SharedPreferences _preferences = await SharedPreferences.getInstance();
_preferences.remove("username");
_preferences.remove("password");
}
}
Profile Screen
import 'dart:async';
import 'package:clothing_roll/shred_preferences/shred_preferences_services.dart';
import 'package:clothing_roll/ui/widget/profile/login_widget.dart';
import 'package:clothing_roll/ui/widget/profile/profiles_widget.dart';
import 'package:flutter/material.dart';
import 'package:flutter/widgets.dart';
class ProfileScreen extends StatefulWidget {
const ProfileScreen({ Key? key }) : super(key: key);
#override
_ProfileScreen createState() => _ProfileScreen();
}
class _ProfileScreen extends State<ProfileScreen> {
final PrefServices _prefServices = PrefServices();
#override
void initState() {
_prefServices.readCache("username").then((value) {
print(value.toString());
if (value != null) {
return Timer(Duration(seconds: 1),
() => ProfileWidget());
} else {
return Timer(Duration(seconds: 1),
() => LoginWidget());
}
});
super.initState();
}
#override
Widget build(BuildContext context) {
return Container();
}
}
Note: I have used Getx package
create a bool variable for conmdition check
add ternary operator to bool variable and change the conditions
class Test1 extends StatelessWidget {
Controller controller = Get.put(Controller());
Test1({Key? key}) : super(key: key);
#override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(),
body: Container(
width: Get.width,
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
crossAxisAlignment: CrossAxisAlignment.center,
children: [
ElevatedButton(
onPressed: () {
controller.isLogged.value = !controller.isLogged.value;
},
child: Text("Press to change value")),
Obx(
() => controller.isLogged.value
? ElevatedButton(onPressed: () {}, child: Text("logged In"))
: ElevatedButton(onPressed: () {}, child: Text("Logged Out")),
),
],
),
),
);
}
}
I'm having a problem in my home_screen.dart file. I
have a method called pullUserData() that is called in initState() but before pullUserData() is completely finished, the build method in home_screen.dart begins. This results in null values (auth and friendsList) being sent to NavDrawer() and FriendsFeed() in the build method of home_screen.dart.
How can I prevent NavDrawer() and FriendsFeed() from being called in the build method before initState() is completely finished? Should I use FutureBuilder?
User_data.dart handles gets the values for auth and friendsList.
home_screen.dart
import 'package:flutter/cupertino.dart';
import 'package:flutter/material.dart';
import 'package:firebase_auth/firebase_auth.dart';
import 'package:mood/components/friends_feed.dart';
import 'package:mood/components/nav_drawer.dart';
import 'package:mood/services/user_data.dart';
class LandingScreen extends StatefulWidget {
static const String id = 'landing_screen';
#override
_LandingScreenState createState() => _LandingScreenState();
}
class _LandingScreenState extends State<LandingScreen> {
FirebaseAuth auth;
List<dynamic> friendsList;
#override
void initState() {
super.initState();
pullUserData();
}
Future<void> pullUserData() async {
UserData userData = UserData();
await userData.getUserData();
auth = userData.auth;
friendsList = userData.friendsList;
}
#override
Widget build(BuildContext context) {
return Scaffold(
backgroundColor: Colors.white,
appBar: AppBar(
title: Text('Mood'),
centerTitle: true,
),
drawer: NavDrawer(auth),
body: FriendsFeed(friendsList),
);
}
}
user_data.dart
import 'package:firebase_auth/firebase_auth.dart';
import 'package:cloud_firestore/cloud_firestore.dart';
class UserData {
final FirebaseAuth _auth = FirebaseAuth.instance;
User _currentUser;
String _currentUserUID;
List<dynamic> _friendsList;
FirebaseAuth get auth => _auth;
User get currentUser => _currentUser;
String get currentUserUID => _currentUserUID;
List<dynamic> get friendsList => _friendsList;
Future<void> getUserData() async {
getCurrentUser();
getCurrentUserUID();
await getFriendsList();
}
void getCurrentUser() {
_currentUser = _auth.currentUser;
}
void getCurrentUserUID() {
_currentUserUID = _auth.currentUser.uid;
}
Future<void> getFriendsList() async {
await FirebaseFirestore.instance
.collection("Users")
.doc(_currentUserUID)
.get()
.then((value) {
_friendsList = value.data()["friends"];
});
}
}
There are couple of problems in your code but it will work.
Firstly, if you want to set value of your friendslist during build, you have to use setState like this:
setState(() {
friendsList = userData.friendsList;
});
And if you want to wait until pullUserData() finish, you are looking for something called splash screen, but in your problem, you are waiting only for body to be build so I will recommend to use progress indicator in your scaffold like this:
return Scaffold(
appBar: Bars.menuAppBar('Devices'),
drawer: DrawerMenu(),
backgroundColor: ColorThemes.backgroundColor,
body: _loading
? Center(
child: CircularProgressIndicator(
valueColor: new AlwaysStoppedAnimation<Color>(
Colors.blue), //choose your own color
))
: FriendsFeed(friendsList)
);
You can see that I used _loading variable. You will have to define it before your initState() like
bool _loading = true;
Then after you set your friendsList inside of your pullUserData() function, change _loading to false inside of setState just like this:
setState(() {
friendsList = userData.friendsList;
_loading = false;
});