Simple Flutter sqflite login->write to db->navigate->retrieve from db flow - sqlite

What is the correct way to handle this, I have done a lot of searching and most samples which use future builders use them to draw lists so maybe I should be avoiding them all together here.
I want to submit a login form, perform the network request and draw a progress bar while the login is happening, and if successful navigate to a home page. If unsuccessful it should just kill the progress bar and redraw the home page. That part seems to be working, unsure if I am using the Navigator correctly.
The login call returns a user and access token object. The Homepage needs to retrieve the access token which was written to the db by the successful login response. From what I can tell the navigation is happening too quickly and the retrieval of the access token appears to happen before the navigation to the home page.
class LoginPage extends StatefulWidget {
LoginPage({Key key, this.title}) : super(key: key);
final String title;
#override
_LoginPageState createState() => _LoginPageState();
}
class _LoginPageState extends State<LoginPage> {
bool _isValidForm = true;
Future<LoginResponse> _user;
void _submitLogin() {
setState(() {
if (_isValidForm) {
_user = login().then((_) => Navigator.push(context, MaterialPageRoute(builder: (context) => HomePage())));
}
});
}
Widget _buildLoginForm(AsyncSnapshot<LoginResponse> snapshot) {
if (snapshot.connectionState != ConnectionState.none && !snapshot.hasData) {
return new Center(child: new CircularProgressIndicator());
} else {
return SafeArea(
child: Center(
child: new ListView(
children: <Widget>[
//..more views
Padding(
padding: EdgeInsets.fromLTRB(16.0, 0.0, 16.0, 16.0),
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: <Widget>[
//..email and password fields
FlatButton(
child: new Text(
'SIGN IN',
),
onPressed: _submitLogin),
]),
)
],
),
),
);
}
}
#override
Widget build(BuildContext context) {
return new FutureBuilder(
future: _user,
builder: (context, AsyncSnapshot<LoginResponse> snapshot) {
return new Scaffold(
backgroundColor: kMyGreen,
body: _buildLoginForm(snapshot),
);
},
);
}
Future<LoginResponse> login() async {
final response = await http.post(...);
if (response.statusCode == 200) {
var loginResponse = LoginResponse.fromJson(json.decode(response.body));
//Write the user details to local db
DBProvider.db.newUser(loginResponse.user);
//Write the tokens to local db
DBProvider.db.newToken(loginResponse.tokens);
return loginResponse;
} else {
throw Exception('Failed to login');
}
}
}
Database methods:
newUser(User newUser) async {
final db = await database;
//get the biggest id in the table
var table = await db.rawQuery("SELECT MAX(id)+1 as id FROM User");
int id = table.first["id"];
//insert to the table using the new id
var raw = await db.rawInsert(
"INSERT Into User (id,first_name,last_name)"
" VALUES (?,?,?)",
[id, newUser.firstName, newUser.lastName]);
return raw;
}
newToken(Tokens newTokens) async {
final db = await database;
//await db.rawDelete("DELETE FROM Token");
//get the biggest id in the table
var table = await db.rawQuery("SELECT MAX(id)+1 as id FROM Token");
int id = table.first["id"];
//insert to the table using the new id
var raw = await db.rawInsert(
"INSERT Into Token (id,access_token,refresh_token)"
" VALUES (?,?,?)",
[id, newTokens.accessToken, newTokens.refreshToken]);
return raw;
}
Future<Tokens> getToken() async {
final db = await database;
var res = await db.query("Token", limit: 1);
return res.isNotEmpty ? Tokens.fromJson(res.first) : null;
}
Home page
class HomePage extends StatefulWidget {
HomePage({Key key, this.title}) : super(key: key);
final String title;
#override
_HomePageState createState() => _HomePageState();
}
class _HomePageState extends State<HomePage>{
#override
void initState() {
super.initState();
getHomePageStuff();
}
#override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: Text("Home Page"),
),
body: Center(
child: RaisedButton(
onPressed: () {},
child: Text('Go back!'),
),
),
);
}
}
Future<HomePageStuffResponse> getHomePageStuff() async {
Tokens token = await DBProvider.db.getToken();
//Accessing the token here throws an NPE
var accessToken = token.accessToken;
debugPrint("token = " + accessToken);
final response = await http.get(..);
if (response.statusCode == 200) {
debugPrint("FETCH SUCCESS");
return stuff;
} else {
throw Exception('Failed to fetch home page stuff');
}
}

You can simply wrap Scaffold's body in FutureBuilder like this
#override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: Text("Home Page"),
),
body: FutureBuilder<HomePageStuffResponse>(
future: getHomePageStuff(),
builder: (context, snap) {
if(snap.hasError) {
return ErrorWidget('Error occurred while fetching data');
}
if(snap.hasData) {
return Center(
child: RaisedButton(
onPressed: () {},
child: Text('Go back!'),
),
);
}
}
),
);
}
}
Future<HomePageStuffResponse> getHomePageStuff() async {
Tokens token = await DBProvider.db.getToken();
//Accessing the token here throws an NPE
var accessToken = token.accessToken;
debugPrint("token = " + accessToken);
final response = await http.get(..);
if (response.statusCode == 200) {
debugPrint("FETCH SUCCESS");
return stuff;
} else {
throw Exception('Failed to fetch home page stuff');
}
}

Okay I was pretty close. Navigation is fine the way it is, the issue was the writing to the db was not being awaited on so that would happen simultaneously to the navigation (the newUser and newToken calls). As I would navigate to the home screen and try and read the access token the call would fail because it did not exist yet.
This was made a little harder to figure out because the debugger is a little strange in Android Studio for flutter so I just had to log everything to the console to see the issue.
If you read my question thank you for your time :)

Related

Provider in different route - Flutter

I'm new in Flutter development, i'm making an app with Firebase Auth, in where I'm using an Authentication Wrapper class that, if user is logged in, goes to Home Screen, else goes to SignIn Screen.
The problem is that, when I want to navigate to AuthWrapper, I get this error message in a red screen:
Error: Could not find the correct Provider<UserFirebaseModel> above this Builder Widget
This happens because you used a `BuildContext` that does not include the provider
of your choice. There are a few common scenarios:
- You added a new provider in your `main.dart` and performed a hot-reload.
To fix, perform a hot-restart.
- The provider you are trying to read is in a different route.
Providers are "scoped". So if you insert of provider inside a route, then
other routes will not be able to access that provider.
- You used a `BuildContext` that is an ancestor of the provider you are trying to read.
Make sure that Builder is under your MultiProvider/Provider<UserFirebaseModel>.
This usually happens when you are creating a provider and trying to read it immediately.
Here there are the most important classes of my code, where I think the problem is.
main.dart
void main() async {
WidgetsFlutterBinding.ensureInitialized();
await Firebase.initializeApp();
runApp(MyApp());
}
class MyApp extends StatelessWidget {
#override
Widget build(BuildContext context) {
return StreamProvider.value(
value: Authentication().user,
initialData: UserFirebaseModel.initialData(),
child: MaterialApp(
title: 'AccessCity',
debugShowCheckedModeBanner: false,
theme: ThemeData(
primarySwatch: Colors.blue,
fontFamily: 'Montserrat',
),
routes: getAppRoutes(),
initialRoute: '/',
onGenerateRoute: (RouteSettings settings) {
return MaterialPageRoute(
builder: (BuildContext context) => HomeTempPage(),
);
},
),
);
}
}
authWrapper.dart
class AuthWrapper extends StatelessWidget {
#override
Widget build(BuildContext context) {
final user = Provider.of<UserFirebaseModel>(context);
print(user);
// ignore: unnecessary_null_comparison
if (user == null) {
return LoginPage();
} else {
return Home();
}
}
}
routes.dart
Map<String, WidgetBuilder> getAppRoutes() {
return <String, WidgetBuilder>{
// Home Temporal Page
'/': (BuildContext context) => HomeTempPage(),
// Components Pages
'generalBigButton': (BuildContext context) => GeneralBigButtonPage(),
'textEntryField': (BuildContext context) => TextEntryFieldPage(),
'secureTextEntryField': (BuildContext context) =>
SecureTextEntryFieldPage(),
'underlinedButton': (BuildContext context) => UnderlinedButtonPage(),
// Modules
'login': (BuildContext context) => LoginPage(),
'authWrapper': (BuildContext context) => AuthWrapper(),
};
}
homeTempPage.dart
const String _title = 'Home Temporal';
const String _goAccessCityButton = 'Ir a AccessCity';
const String _goAccessCityLabel = 'Ir a pantalla Login de la app';
class HomeTempPage extends StatefulWidget {
#override
_HomeTempPageState createState() => _HomeTempPageState();
}
class _HomeTempPageState extends State<HomeTempPage> {
final model = HomeTempModel();
#override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: Text(_title),
backgroundColor: mainBlue,
),
body: Center(
child: Column(
mainAxisAlignment: MainAxisAlignment.start,
children: [
SizedBox(height: 10),
MaterialButton(
child: Text(_goAccessCityButton),
color: Colors.blue,
textColor: Colors.white,
onPressed: () {
model.navigateToStart(context);
},
),
Text(_goAccessCityLabel),
SizedBox(height: 30),
Expanded(
child: Container(
child: ListView(
children: model.getComponents(context),
),
),
),
],
),
),
);
}
}
In this last class, the line
navigateToStart() method
goes to route 'authWrapper'.
UserFirebaseModel
class UserFirebaseModel {
final String id;
final String email;
UserFirebaseModel(this.id, this.email);
factory UserFirebaseModel.initialData() {
return UserFirebaseModel('', '');
}
}
Authentication class
class Authentication {
final FirebaseAuth _firebaseAuth = FirebaseAuth.instance;
// Create user based on User (ex FirebaseUser)
UserFirebaseModel? _userFromFirebaseUser(User? user) {
final _mail;
if (user != null) {
_mail = user.email;
if (_mail != null) {
return UserFirebaseModel(user.uid, _mail);
}
} else {
return null;
}
}
// Auth change user stream
Stream<UserFirebaseModel?> get user {
return _firebaseAuth.authStateChanges().map(_userFromFirebaseUser);
}
// Sign in with email and password
Future<String?> signIn({
required String email,
required String password,
}) async {
try {
await _firebaseAuth.signInWithEmailAndPassword(
email: email,
password: password,
);
print("Signed in");
return "";
} on FirebaseAuthException catch (e) {
return e.message;
}
}
User? getUser() {
try {
return _firebaseAuth.currentUser;
} on FirebaseAuthException {
return null;
}
}
}
Thanks for your help, i need to solve it!

Stack Overflow error when grabbing data from Firebase in Flutter

I'm trying to grab data from firebase (users collection -> uid document-> Summoner Info collection -> id document -> summonerName field) and display the summonerName's rank. Below is the screen that is causing the error:
import 'package:firebase_auth/firebase_auth.dart';
import 'package:first_project/network/api.dart';
import 'package:first_project/screens/summoner_search.dart';
import 'package:flutter/material.dart';
import 'set_summoner_name.dart';
class MainScreen extends StatefulWidget {
const MainScreen({Key? key}) : super(key: key);
static const id = '/mainScreen';
#override
_MainScreenState createState() => _MainScreenState();
}
class _MainScreenState extends State<MainScreen> {
CollectionReference usersCollection =
FirebaseFirestore.instance.collection('users');
final FirebaseAuth _auth = FirebaseAuth.instance;
late User loggedInUser;
bool summonerExists = false;
void getCurrentUser() {
try {
final user = _auth.currentUser;
if (user != null) {
loggedInUser = user;
print(loggedInUser.email);
}
} catch (e) {
print(e);
}
// here you write the codes to input the data into firestore
}
#override
void initState() {
getCurrentUser();
super.initState();
}
#override
Widget build(BuildContext context) {
return MaterialApp(
home: Scaffold(
appBar: AppBar(
title: Text('Main Screen'),
actions: [
IconButton(
onPressed: () => Navigator.pushNamed(context, SummonerSearch.id),
icon: Icon(Icons.search_off_rounded),
),
],
),
body: Center(
child: StreamBuilder(
stream: usersCollection
.doc(_auth.currentUser!.uid)
.collection('Summoner Info')
.snapshots(),
builder:
(BuildContext context, AsyncSnapshot<QuerySnapshot> snapshot) {
// checkIfSummonerExists(snapshot);
if (!snapshot.hasData) {
return Center(
child: CircularProgressIndicator(),
);
}
print('Reached Here!');
print(snapshot.data!.docs[0].data().toString());
return ListView(
children: snapshot.data!.docs.map((document) {
return Column(
mainAxisAlignment: MainAxisAlignment.center,
children: <Widget>[
ListTile(
title: Text('Name:' + document['summonerName']),
),
Card(
child: FutureBuilder<dynamic>(
future: DataModel()
.getWholeRank(document['summonerName']),
builder: (context, snapshot) {
String tier;
String rank;
try {
//if successful, the player is ranked and has data
if (snapshot.hasData) {
tier = snapshot.data![0]['tier'];
rank = snapshot.data![0]['rank'];
} else {
return CircularProgressIndicator();
}
if (tier == 'CHALLENGER' || tier == 'MASTER') {
rank = '';
}
return Center(
child: Row(
crossAxisAlignment: CrossAxisAlignment.center,
mainAxisAlignment: MainAxisAlignment.center,
children: <Widget>[
Text(tier),
SizedBox(width: 2.0),
Text(rank),
],
),
);
} catch (e) {
//if unsuccessful call from api, means the player is unranked and json is empty
return Center(
child: Text('Unranked'),
);
}
},
),
),
],
);
}).toList(),
);
},
),
),
),
);
}
}
In the code above, I notice I am getting the Stack Overflow error starting at the 'Card' line about 3/4 of the way down, which is where I grab the data from the database and fetch the data from the API. If I comment all of that out and just display the summonerName, I get no error.
For the API functions as reference, below here is the code of the getWholeRank method
Future<dynamic> fetchRank(String name) async {
name = removeSpaces(name);
String id = await fetchByName(name, 'id');
NetworkHelper networkHelper = NetworkHelper(
'https://na1.api.riotgames.com/lol/league/v4/entries/by-summoner/$id?api_key=$api_key');
var rankData = await networkHelper.getRankData();
return rankData;
}
Future<dynamic> getWholeRank(summonerName) async {
var rankData = await rankObj.fetchRank(summonerName);
return rankData;
}
and below this is my NetworkHelper class:
import 'package:http/http.dart' as http;
import 'dart:convert';
class NetworkHelper {
NetworkHelper(this.url);
final String url;
Future getData({String ch = 'default'}) async {
http.Response response = await http.get(Uri.parse(url));
if (response.statusCode == 200) {
String data = response.body;
// print(data);
var decodedData = jsonDecode(data);
if (ch == 'default') {
print(decodedData);
return decodedData; //returns map of data
} else {
//Options: id, accountID, name, puuid, profileIconID, revisionDate, summonerLevel,
print(decodedData[ch]);
return decodedData[ch];
}
} else {
print('Status code: ');
print(response
.statusCode); //if doesn't work, it will print status code (200 is good, 400 etc. is bad)
}
}
Future getRankData({String ch = 'default'}) async {
http.Response response = await http.get(Uri.parse(url));
if (response.statusCode == 200) {
String data = response.body;
var decodedData = jsonDecode(data);
// print(decodedData[0]['tier']);
return decodedData;
} else {
print('Failed! Status code: ');
print(response
.statusCode); //if doesn't work, it will print status code (200 is good, 400 etc. is bad)
}
}
}
If anyone could help me understand why I'm getting the stack overflow error, it would be much appreciated!!
So I fixed the error, and it was because I was passing an object into the future argument and in the stack trace, there were lots of DataModel objects being initialized, causing the stack overflow. I fixed it in the getWholeRank method by replacing rankObj, a DataModel object, to the 'this' keyword.

Flutter FutureBuilder not building with data from sqlite

I'm very new in flutter, and are trying to write the app that will take value from sqlite. From what I tried, it needs to use FutureBuilder widget.
But the following code I wrote, the FutureBuilder widget seems to get data from sqlite, but the "builder" property was never called:
import 'package:flutter/material.dart';
import 'package:flutter_spinkit/flutter_spinkit.dart';
import 'package:flutterapp/dbHelper.dart';
void main() {
runApp(MyApp());
}
class MyApp extends StatelessWidget {
#override
Widget build(BuildContext context) {
return MaterialApp(
routes:{
"/": (context) =>Test()
}
);
}
}
class Test extends StatefulWidget {
#override
_TestState createState() => _TestState();
}
class _TestState extends State<Test> {
bool nameCheck = false; // Use to check name textfield has correctly be inputed
TextEditingController nameController = TextEditingController();
#override
Widget build(BuildContext context) {
return Scaffold(
body:Column(
children: <Widget>[
SizedBox(height:300),
Row(
children: <Widget>[
Expanded(
child: TextField(
controller: nameController,
)
),
IconButton(
icon: Icon(Icons.check_circle),
onPressed:(){
return FutureBuilder(
future: getData(nameController.text),
builder: (BuildContext context, AsyncSnapshot<List<Map>>snapshot) {
print("Start future"); // never get printed
List<Widget> children;
if (snapshot.hasData) {
children = <Widget>[
Builder(
builder: (BuildContext context) {
print("got data");
final result = snapshot.data;
print(result) ; // never get printed
setState(() {
nameCheck = true;
});
return Container();
})
];
} else {
children = <Widget>[
AlertDialog(
content: SpinKitCircle(
color: Colors.white,
size: 80.0,
))
];
}
return Center(
child: Container(
color: Colors.blue,
child: Column(
mainAxisAlignment:
MainAxisAlignment
.center,
crossAxisAlignment:
CrossAxisAlignment
.center,
children: children,
)));
});}
)
],
),
Builder(
builder: (BuildContext context){
if (nameCheck == true){
return Text("test");
}
return Container();
}
)
],
)
);
}
}
Future<List<Map>> getData(String input_name) async{
final dbHelper = DBHelper.instance;
await dbHelper.database;
final result = await dbHelper.query("SELECT * FROM Guest WHERE Name = \"$input_name\"");
print(result); // This get printed
return result;
}
The DBHelper code is as follow, basically it just set up a sqlite database and some database operation:
import 'package:sqflite/sqflite.dart';
import 'package:path/path.dart';
import 'dart:io';
import 'package:path_provider/path_provider.dart';
class DBHelper {
static final _databaseName = "MonetaryDB.db";
static final _databaseVersion = 1;
static final create_table_Test = "CREATE TABLE \"Guest\" (\"Name\" TEXT NOT NULL PRIMARY KEY, \"Money\" INTEGER, \"Person\" INTEGER)";
static final String insert_guest = "INSERT INTO Guest (Name, Money, Person) VALUES (\"testname\", 1000, 1)";
DBHelper._privateConstructor();
static final DBHelper instance = DBHelper._privateConstructor();
static Database _database;
Future<Database> get database async {
if (_database != null) {
return _database;}
else{
_database = await _initDatabase();
return _database;}
}
_initDatabase() async {
Directory documentsDirectory = await getApplicationDocumentsDirectory();
String path = join(documentsDirectory.path, _databaseName);
return await openDatabase(path,
version: _databaseVersion,
onCreate: _onCreate,
);
}
Future _onCreate(Database db, int version) async {
await db.execute(create_table_Test);
await db.rawInsert(insert_guest);
}
Future<int> insert(String statement) async {
Database db = await instance.database;
return await db.rawInsert(statement);
}
Future<List<Map>> query(String statement) async {
Database db = await instance.database;
return await db.rawQuery(statement);
}
Future<int> update(String statement) async{
Database db = await instance.database;
return await db.rawUpdate(statement);
}
Future<int> delete(String statement) async{
Database db = await instance.database;
return db.rawDelete(statement);
}
}
If I simply change the IconButton onPressed function into setState((){nameCheck = true}), The Text("test") widget will show, so the problem must be the FutureBuilder. Also, the getData() function can get the correct result from the sqlite database
I have no idea why the FutureBuilder doesn't get build, did someone have any idea of it?
Thanks!
So with the help in the comment section, I change the code and it worked:
...
Widget build(BuildContext context) {
return Scaffold(
body:Column(
children: <Widget>[
SizedBox(height:300),
Row(
children: <Widget>[
Expanded(
child: TextField(
controller: nameController,
)
),
IconButton(
icon: Icon(Icons.check_circle),
onPressed:(){
return showDialog(
context: context,
builder: (BuildContext context){
return AlertDialog(
actions: <Widget>[
SizedBox(
width: 300,
height: 300,
child: FutureBuilder(
future: getData(nameController.text),
builder: (BuildContext context, AsyncSnapshot<List<Map>>snapshot) {
if (snapshot.hasData) {
final result = snapshot.data;
SchedulerBinding.instance.addPostFrameCallback((_) => setState(() {
nameCheck = true;
}));
Navigator.pop(context, true);
}
else {
return Container(
child: SpinKitCircle(
color: Colors.white,
size: 80.0,
));
}
return Container(color: Colors.blue);
})
),
],
);
}
);
}
)
],
),
Builder(
builder: (BuildContext context){
if (nameCheck == true){
return Text("test");
}
return Container();
}
)
],
)
);
}
...
The other codes are still the same.
Like the comment section above had suggested, the main problem is that there will be no place to build for the widget the FutureBuilder that is going to built. To solve this problem, I place the FutureBuilder widget into the AlertDialog widget, since that I still want to keep the SpinKitCircle widget when loading.
I also gave up the Column widget at the end of the FutureBuilder widget, and deleted the Builder widget at the beginning of the FutureBuilder widget, which it was no longer needed when there was no Column.
The above codes still throw an acceptable exception:"setState() or markNeedsBuild() called during build." But the whole things still can run, so I will try to fix that the other day.
Thanks for the suggestion in the comment section.

How to retrieve a Firebase Storage image stream in flutter?

I've got a few photo's I've uploaded into my firebase storage under a file called 'photos' and I want to be able to retrieve them onto my app through a stream. I have done this before through Firebase cloud database by tapping into the Firestore.instance.collection('messages').snapshots() property in my StreamBuilder, but I don't know how to access the firebase storage snapshots and upload them as a stream into my app.
This was my code for the messages snapshot, I hope it helps:
final _firestore = Firestore.instance;
void messagesStream() async {
await for (var message in _firestore.collection('messages').snapshots()){
for (var snapshot in message.documents){
print(snapshot.data);
}
}
}
#override
Widget build(BuildContext context) {
return Scaffold(
body: SafeArea(
child: Column(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
crossAxisAlignment: CrossAxisAlignment.stretch,
children: <Widget>[
StreamBuilder<QuerySnapshot>(
stream: Firestore.instance.collection('messages').snapshots(),
builder: (context, snapshot){
if (!snapshot.hasData){
return Center(
child: CircularProgressIndicator(backgroundColor: Colors.lightBlueAccent,),
);
} else {
final messages = snapshot.data.documents;
List<Text> messageWidgets = [];
for (var message in messages){
final messageText = message.data['text'];
final messageSender = message.data['sender'];
final messageWidget = Text('$messageText from $messageSender');
messageWidgets.add(messageWidget);
}
return Column(children: messageWidgets,);
}
}
),
),
},
So I figured out you can't create a stream from the firebase storage, but what I could do was, in my firebase cloud database, start a new collection called 'my_collection' and in a new document, create an auto-ID, with a field called 'image' which is a string, with an http reference to an image that is on the internet, or one you can upload to the internet (this is what I did on imgur.com, credit to them)! Here is my code below, I hope it helps others! If it doesn't, have a look at this code written by iampawan, he helped me a tonne!
https://github.com/iampawan/FlutterWithFirebase
class MyList extends StatefulWidget {
#override
_MyListState createState() => _MyListState();
}
class _MyListState extends State<MyList> {
StreamSubscription<QuerySnapshot> subscription;
List <DocumentSnapshot> myList;
final CollectionReference collectionReference = Firestore.instance.collection('my_collection');
final DocumentReference documentReference = Firestore.instance.collection('my_collection').document('GFWRerw45DW5GB54p');
#override
void initState() {
super.initState();
subscription = collectionReference.snapshots().listen((datasnapshot) {
setState(() {
myList = datasnapshot.documents;
});
});
}
#override
void dispose() {
subscription?.cancel();
super.dispose();
}
#override
Widget build(BuildContext context) {
return myList != null ?
ListView.builder(
itemCount: myList.length,
itemBuilder: (context, index){
String imgPath = myList[index].data['image'];
return MyCard(assetImage: Image.network(imgPath), function:
(){
if (imgPath == myList[0].data['image']){
Navigator.pushNamed(context, MyMenu.id);
} else if (imgPath == myList[1].data['image']){
Navigator.pushNamed(context, YourMenu.id);
} else if (imgPath == myList[2].data['image']){
Navigator.pushNamed(context, HisMenu.id);
} else if (imgPath == myList[3].data['image']){
Navigator.pushNamed(context, HerMenu.id);
}
},);
})
: Center(child: CircularProgressIndicator(),
);
}
}
Just to note, MyCard is it's own page with it's own constructor that requires an assetImage and a function for the user to be pushed to a new screen:
MyCard({#required this.assetImage, #required this.function});
final Image assetImage;
final Function function;

Getting null value (Firestore query result) in Flutter app

I have an application and In this i'm making a query for get user details by the e-mail account.
I'm using Future class to get data and fill my variable but the widget Text always show null value.
Please let me now if i am doing something wrong.
class _HomePageAppState extends State<HomePageApp> {
String _emailUsuario;
Usuario usuario;
void initState() {
super.initState();
Autenticacao().getCurrentUser().then((user) {
setState(() {
if (user != null) {
_emailUsuario = user.email.toString(); //the user email is returnig correctly
recuperarDadosUsuarioFirebase().then((ds) {
usuario = Usuario(
email: _emailUsuario,
nome: ds['nome'] != null ? ds['nome'] : null,
);
});
}
});
});
}
Future<DocumentSnapshot> recuperarDadosUsuarioFirebase() async {
DocumentSnapshot ds;
await Firestore.instance
.collection('usuarios')
.document(_emailUsuario)
.get()
.then((DocumentSnapshot _ds) {
ds = _ds;
});
return ds;
}
#override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(),
body: Container(
color: Colors.white10,
child: ListView(
children: <Widget>[
Text('Bem vindo ${usuario.nome} !!!'),
],
),
),
);
}
}
U might want to use Future Builder for such async work cause build method was called before usuario is assign so like this :
FutureBuilder(
future: getCurrentUser(),
builder: (context, AsyncSnapshot snapshot) {
if (snapshot.data == null) {
return Center(child: CircularProgressIndicator());
}
// after getting data
},
);
Method getCurrentUser() needs to be created :)

Resources