i'm trying to make a singleton class from MyDatabase with default constructor to access to getUserDao on this class, implemented code work fine but i have some other issue with that as
i have to use async and await in outside of class
i have to use that
only on main function because main method should be async
for example:
my singleton class:
class MydbModel {
UserDao _userDao;
MydbModel._(this._userDao);
static Future<MydbModel> create() async => MydbModel._(await initialDatabase());
static Future<UserDao> initialDatabase() async {
var db = await $FloorAppDatabase.databaseBuilder('flutter_database.db').build();
return db.userDao;
}
UserDao get userDao=>_userDao;
}
and main class:
main() async {
var mydbModel = await MydbModel.create();
print(mydbModel.userDao);
}
i have to define that only on main method and i can't use that on StatefulWidget or State<Classname classes and when i try to use this instance i have to pass it for all class
how can i resolve this problem to use simply class in all part of application?
for example:
main() {
var userDao = MydbModel.create().then((dao){
return dao;
});
print(userDao);
}
Try this:
class MydbModel {
static UserDao _userDao;
MydbModel._();
static UserDao get userDao =>_userDao;
static Future<void> create() async => _userDao = await initialDatabase();
static Future<UserDao> initialDatabase() async {
var db = await $FloorAppDatabase.databaseBuilder('flutter_database.db').build();
return db.userDao;
}
}
You should be able to access _userDao from anywhere using MydbModel.userDao.
If your app requires data to be loaded asynchronously before being ready for user input, you'll have to show some UI while it loads. FutureBuilder handles such a case.
Here's an example that shows a spinner until prepareData is finished. Another option would be to show a splash screen.
Future prepareData() async => null;
...
#override
Widget build(BuildContext context) {
return FutureBuilder(
future: prepareData(),
builder: (context, snapshot) {
if (snapshot.hasData) {
return Text('${snapshot.data}');
} else if (snapshot.hasError) {
return Text('${snapshot.error}');
} else
return Center(child: CircularProgressIndicator());
},
);
}
Related
I am getting imageUrl from another page. and I want to use the "imageUrl" inside of "theimage" but I cannot access it. I am facing this error.
The instance member 'widget' can't be accessed in an initializer.
Try replacing the reference to the instance member with a different expression
class ProductCard extends StatefulWidget {
final String imageUrl;
ProductCard(
{this.imageUrl,});
#override
State<ProductCard> createState() => _ProductCardState();
}
class _ProductCardState extends State<ProductCard> {
// I can not access widget.imageUrl in here
final theImage = Image.network("${widget.imageUrl}", fit: BoxFit.cover);
/// Did Change Dependencies
#override
void didChangeDependencies() {
precacheImage(theImage.image, context);
super.didChangeDependencies();
}
#override
Widget build(BuildContext context) {
return Container(
child:theImage;
);
}
}
You are using named constructor ProductCard make imageUrl required nullable ,or provide default value.
const ProductCard(
{required this.imageUrl,});
And use initState for initialization.
late final Image theImage;
#override
void initState() {
theImage = Image.network("${widget.imageUrl}", fit: BoxFit.cover);
super.initState();
}
Next remove ; from end of theImage or use ,.
#override
Widget build(BuildContext context) {
return Container(child: theImage);
}
I have a FirebaseActions class which is doing signup and signin works for me. I added a getCurrentUser method in it. I'm calling that function from my HomeScreen widget. I need to put the returned value(type= FirebaseUser) into a variable in my HomeScreen to reach loggedInUser.email. But I get this error. My question; is there any way to get a FirebaseUser type data into a Future type variable? When I write this function in HomeScreen instead of FirebasAction class, it works but is it the best practice?
FirebaseActions class
static getCurrentUser(context) async {
final user = await _auth.currentUser().catchError((error) {
Scaffold.of(context).showSnackBar(AppSnackBar.appSnackBar(error.message));
});
if (user != null) {
return user;
}
}
HomeScreen widget
class _HomeScreenState extends State<HomeScreen> {
FirebaseUser loggedInUser;
#override
void initState() {
super.initState();
loggedInUser = FirebaseActions.getCurrentUser(context);
print(loggedInUser.toString());
}
You are getting this error because you didn't specify a return type in your getCurrentUser() function.
Replace your code with the one below and everything will work fine.
To solve this issue:
Check the code below: It works perfectly fine:
Firebase Action Class
// give your function a return type
static Future<FirebaseUser> getCurrentUser(context) async {
final user = await _auth.currentUser().catchError((error) {
Scaffold.of(context).showSnackBar(AppSnackBar.appSnackBar(error.message));
});
if (user != null) {
return user;
}
}
Home Screen Widget
class _HomeScreenState extends State<HomeScreen> {
FirebaseUser loggedInUser;
#override
void initState() {
super.initState();
call the function here
logInUser();
print(loggedInUser.toString());
}
I hope this helps
UPDATE
// create a new function to log in user
void logInUser() async {
// await the result because you are invoking a future method
loggedInUser = await FirebaseActions.getCurrentUser(context);
}
You are not specified return type of method and also method is async, so you have to put await where you are calling.
static Future<FirebaseUser> getCurrentUser(context) async {
also, add await where you are calling.
loggedInUser = await FirebaseActions.getCurrentUser(context);
update:
create new function and call in that function.
callme() async{
loggedInUser = FirebaseActions.getCurrentUser(context);
print(loggedInUser.toString());
}
initstate:
#override
void initState() {
super.initState();
callme();
}
Here's how I handle async functions in my initstate:
FirebaseActions.getCurrentUser(context).then((val){loggedInUser = val});
Also make sure you specify the return type in your asynchronous function.
Future<FirebaseUser> getCurrentUser(context) async {...
My implementation repository
in main method i have this lines of code to connect to database and work fine
Future<void> main() async {
final database = await $FloorAppDatabase.databaseBuilder('flutter_database.db').build();
final userDao = database.userDao;
runApp(MaterialApp(...);
}
now i'm trying to use this codes
final database = await $FloorAppDatabase.databaseBuilder('flutter_database.db').build();
final userDao = database.userDao;
from class, for example:
Future<void> main() async {
MyDatabase myDatabase = MyDatabase();
final userDao = myDatabase.userDao;
runApp(MaterialApp(...);
}
unfortunately i get null for userDao in this implementation, i think in that witch i use async i should be change that and use then()
class MyDatabase {
UserDao userDao;
Future<UserDao> initialDatabase() async {
final database = await $FloorAppDatabase.databaseBuilder('flutter_database.db').build();
return database.userDao;
}
}
#dao
abstract class UserDao{
#Query('SELECT * FROM User LIMIT 1')
Stream<User> getUserInfo();
#insert
Future<void> insertUserInformation(User user);
}
UPDATED: implementation solution on scope model
class MydbModel extends Model {
MyDatabase myDatabase = MyDatabase();
Future _doneFuture;
MydbModel() {
_doneFuture= myDatabase.initialDatabase();
}
Future get initializationDone => _doneFuture;
}
class MyDatabase {
AppDatabase db;
UserDao userDao;
Future<void> initialDatabase() async {
db = await $FloorAppDatabase.databaseBuilder('flutter_database.db').build();
}
UserDao getUserDao() {
return db.userDao;
}
}
main() {
runApp(MaterialApp(
title: 'floor sample',
home: App(),
));
}
class App extends StatefulWidget {
App({Key key}) : super(key: key);
#override
State<App> createState() => _AppState();
}
class _AppState extends State<App> {
#override
Widget build(BuildContext context) {
return Scaffold(
body: ScopedModel(
model: MydbModel(),
child: ScopedModelDescendant<MydbModel>(
builder: (context, _, model) => StreamBuilder<User>(
stream: model.myDatabase.userDao.getUserInfo(),
builder: (_, snapshot) {
if (!snapshot.hasData) {
return Text('user not found');
} else {
return Text('user found');
}
},
),
),
),
);
}
}
With given class implementation, you should call the function and wait until it completes:
MyDatabase myDatabase = MyDatabase();
final userDao = await myDatabase.initialDatabase();
Alternatively, if you don't want to recreate databse instance each time, consider assigning it to a class member
class MyDatabase {
AppDatabase db;
Future<void> initDb() async {
db = await $FloorAppDatabase.databaseBuilder('flutter_database.db').build();
}
UserDao getUserDao() {
return db.userDao;
}
// here may be other functions that use db
}
And the use it like this
MyDatabase myDatabase = MyDatabase();
await myDatabase.initDb();
final userDao = myDatabase.getUserDao();
// here you can call other functions from class
I am trying to get the length of a collection in Firebase using Flutter. I have a profile page that loads how many hikes you have done from a database, but I keep getting a Future<int> when I print the variable. How do I solve this problem?
class ProfilePage extends StatefulWidget {
#override
_ProfilePage createState() => new _ProfilePage();
}
int doneHikes;
Database temp = new Database();
class _ProfilePage extends State<ProfilePage> {
#override
void initState() {
super.initState();
_numOfDoneHikes();
}
Future<void> _numOfDoneHikes() async{
var asyncResult = await temp.numOfDoneHikes();
setState(() => doneHikes = asyncResult);
print(doneHikes);
}
numOfDoneHikes function in the Database object:
Future<int> numOfDoneHikes() async {
return Firestore.instance.collection(globalUserName)
.document("Done Hikes").collection("Hike List").snapshots().length;
}
With this code, I keep getting doneHikes printing
instance of Future<int>
You can use await:
Future<int> numOfDoneHikes() async {
return await Firestore.instance.collection(globalUserName)
.document("Done Hikes").collection("Hike List").snapshots().length;
}
in fact, you don't even need await, you can do this instead:
Future<int> numOfDoneHikes() {
return Firestore.instance.collection(globalUserName)
.document("Done Hikes").collection("Hike List").snapshots().length;
}
I want to declare a user object, that I will instantiate with an http request, and I want it to be global. How can I do it? With a Singleton? But how can I make this class also a Singleton? Or is there another way?
That is what I've done so far:
class User{
String username;
String password;
int id;
User({this.username, this.id});
factory User.fromJson(Map<String, dynamic> json){
return User(
username: json['name'],
id: json['id']
);
}
}
and then:
var user = await login(username, password, context);
In flutter, you should not make singletons. Instead, you should store it into a widget that exposes these data to all of its descendants.
Usually InheritedWidget
The reason being, with such architecture all the descendants are automatically aware of any change made to your "singleton".
A typical example would be the following:
#immutable
class User {
final String name;
User({this.name});
}
class Authentificator extends StatefulWidget {
static User currentUser(BuildContext context) {
final _AuthentificatorScope scope = context.inheritFromWidgetOfExactType(_AuthentificatorScope);
return scope.user;
}
final Widget child;
Authentificator({this.child, Key key}): super(key: key);
#override
_AuthentificatorState createState() => _AuthentificatorState();
}
class _AuthentificatorState extends State<Authentificator> {
#override
Widget build(BuildContext context) {
return _AuthentificatorScope(
child: widget.child,
);
}
}
class _AuthentificatorScope extends InheritedWidget {
final User user;
_AuthentificatorScope({this.user, Widget child, Key key}) : super(child: child, key: key);
#override
bool updateShouldNotify(_AuthentificatorScope oldWidget) {
return user != oldWidget.user;
}
}
which you have to instantiate like this:
new MaterialApp(
title: 'Flutter Demo',
builder: (context, child) {
return Authentificator(
child: child,
);
},
home: Home(),
);
and then use inside your pages like this:
#override
Widget build(BuildContext context) {
User user = Authentificator.currentUser(context);
...
}