Flutter and Riverpod FutureBuilder never any data - flutter

I'm developing a Flutter app with Riverpod and getting a user from my postgres database as Future<AppUser?>. Anyone have any idea why my FutureBuilder never fetches my user data and always shows the CircularProgressIndicator?
My home_screen.dart
class HomeScreen extends HookConsumerWidget {
const HomeScreen({Key? key}) : super(key: key);
#override
Widget build(BuildContext context, WidgetRef ref) {
final currentUser = ref.watch(currentUserProvider);
return Scaffold(
body: Center(
child: FutureBuilder<AppUser?>(
future: currentUser,
builder: (context, snapshot) {
if(snapshot.hasData){
return Text(
'Welcome ${snapshot.data!.getNickname}',
style: const TextStyle(fontSize: 20),
);
}
return const CircularProgressIndicator();
},
),
),
);
}
}
my user_provider.dart
final authRepositoryProvider = Provider<AuthRepository>((ref) {
return AuthRepository(FirebaseAuth.instance);
});
final userRepositoryProvider = Provider<UserRepository>((ref) {
return UserRepository(ref.read(authRepositoryProvider).currentUserEmail!);
});
final currentUserProvider = Provider<Future<AppUser?>>((ref) {
return ref.read(userRepositoryProvider).getCurrentUser();
});
My user_repository.dart
class UserRepository {
final String email;
PostgreSQLConnection connection = PostgreSQLConnection(
'10.0.2.2', 5432, DatabaseAccess.databaseName,
queryTimeoutInSeconds: 3600,
timeoutInSeconds: 3600,
username: DatabaseAccess.databaseUser,
password: DatabaseAccess.databasePassword);
UserRepository(this.email);
Future<AppUser?> getCurrentUser() async {
try {
await connection.open();
final result = await connection.mappedResultsQuery(
'select * from user where email == #emailValue',
substitutionValues: {
'emailValue': email,
},
allowReuse: true,
timeoutInSeconds: 30,
);
final userFromDataBase = result[0]['user']!;
return AppUser(
email: userFromDataBase['email'],
nickname: userFromDataBase['nickname'],
role: userFromDataBase['role'],
firstname: userFromDataBase['firstname'],
lastname: userFromDataBase['lastname'],
);
} on PostgreSQLException catch(e) {
print(ErrorHandler(message: e.toString()));
return null;
}
}
}

Try changing
final currentUser = ref.watch(currentUserProvider);
to
final currentUser = ref.read(currentUserProvider);

Related

How to fix eternal loading when fetching data in flutter?

I am trying to make sign in in dart using cubit/bloc.
And when I try to change state of my cubit(auth) to Waiting => CircularProgressIndicator
Then my app is getting stuck when api returns statusCode == 400 and I call another emit to show dialog window
But if sign in is successful(statusCode == 200) then everything is ok, and I navigate to main_page.dart
How to fix this problem and transfer data reponse from api to widget(I want to send statusCode and message to widget)
My cubit file:
class AuthCubit extends Cubit<AuthState> {
AuthCubit() : super(AuthState(
email: "",
password: "",
));
final ApiService _apiService = ApiService();
void setEmail(String email) => emit(state.copyWith(email: email));
void setPassword(String password) => emit(state.copyWith(password: password));
Future signIn() async {
emit(WaitingSignInAuth(state.email, state.password));
try {
final data = await _apiService.signIn(
email: state.email ?? "",
password: state.password ?? ""
);
if (data['success']) {
print(data);
emit(const SuccessAutentification());
}
} on DioError catch (ex) {
print(ex.toString());
//I want to transfer to ErrorSignInAuth ex.response!.data['message'] but I don't know how
emit(ErrorSignInAuth(state.email, state.password));
} catch (e) {
print(e);
}
}
}
This is my state file:
class AuthState extends Equatable {
final String? email;
final String? password;
const AuthState({
this.email,
this.password,
});
AuthState copyWith({
String? email,
String? password,
}) {
return AuthState(
email: email ?? this.email,
password: password ?? this.password,
);
}
#override
List<Object?> get props =>
[email, password];
}
class SuccessAutentification extends AuthState {
const SuccessAutentification() : super();
}
class WaitingSignInAuth extends AuthState {
const WaitingSignInAuth(email, password) : super(email: email, password: password);
}
class ErrorSignInAuth extends AuthState {
const ErrorSignInAuth(email, password) : super(email: email, password: password);
}
And this is the widget where I use this cubit:
#override
Widget build(BuildContext context) {
return BlocConsumer<AuthCubit, AuthState>(
listener: (context, state) {
if (state is WaitingSignInAuth) {
showDialog(
context: context,
builder: (context) => Container(
width: MediaQuery.of(context).size.width,
height: MediaQuery.of(context).size.height,
color: Colors.black.withOpacity(0.6),
child: const Center(
child: CircularProgressIndicator(
strokeWidth: 1,
color: Colors.black,
backgroundColor: Colors.white,
),
),
));
}
if (state is SuccessAutentification) {
Navigator.of(context).pushReplacement(
MaterialPageRoute(
builder: (_) => const MainWidget(),
),
);
}
if (state is ErrorAuthentification) {
showDialog(
context: context,
builder: (BuildContext context) {
return AlertDialog(
title: Text("Bad request"),
content: SingleChildScrollView(
child: ListBody(
children: <Widget>[
Text("Error") // I want to show here error message
],
),
),
actions: <Widget>[
TextButton(
child: const Text("Close"),
onPressed: () {
Navigator.of(context).pop();
},
)
],
);
}
);
}
},
builder: (context, state) {
return Scaffold(
LoginButton(
color: _isValid ? AppColors.white : AppColors.whiteA3A3A3,
text: "Login in",
textColor: Colors.black,
isButtonDisabled: _isValid ? false : true,
onPressed: () {
if (_key.currentState!.validate()) {
BlocProvider.of<AuthCubit>(context).setEmail(_email.text.trim());
BlocProvider.of<AuthCubit>(context).setPassword(_password.text.trim());
BlocProvider.of<AuthCubit>(context).signIn();
_key.currentState!.save();
}
},
)
);
},
);
}
In signIn() function, after you emit WaitingSignInAuth state, in some cases you are not emitting any other state, so your page is always loading.
Future signIn() async {
emit(WaitingSignInAuth(state.email, state.password));
try {
final data = await _apiService.signIn(
email: state.email ?? "",
password: state.password ?? ""
);
if (data['success']) {
print(data);
emit(const SuccessAutentification());
} else {
// emit state
emit(ErrorSignInAuth(state.email, state.password));
}
} on DioError catch (ex) {
print(ex.toString());
emit(ErrorSignInAuth(state.email, state.password));
} catch (e) {
print(e);
// emit state
emit(ErrorSignInAuth(state.email, state.password));
}
}

Flutter app login after a logout PostgresSQLException

I'm working on a Flutter app using Riverpod for the state management and Postgres for the database. I try to do a simple thing: display a message in the home screen with the nickname of the current user. When I logout and login to check if my feature works, my user is null and my console display a PostgresSQLException :
PostgreSQLSeverity.error : Attempting to reopen a closed connection. Create a instance instead.
Any idea where I made a mistake?
My user_repository:
PostgreSQLConnection connection = PostgreSQLConnection(
'10.0.2.2', 5432, DatabaseAccess.databaseName,
queryTimeoutInSeconds: 3600,
timeoutInSeconds: 3600,
username: DatabaseAccess.databaseUser,
password: DatabaseAccess.databasePassword);
Future<AppUser?> getCurrentUser() async {
try {
await connection.open();
final result = await connection.mappedResultsQuery(
'select * from public.user where email = #emailValue',
substitutionValues: {
'emailValue': email,
},
allowReuse: true,
timeoutInSeconds: 30,
);
final userFromDataBase = result[0]['user']!;
return AppUser(
email: userFromDataBase['email'],
nickname: userFromDataBase['nickname'],
role: userFromDataBase['role'],
firstname: userFromDataBase['firstname'],
lastname: userFromDataBase['lastname'],
);
} on PostgreSQLException catch(e) {
print(ErrorHandler(message: e.toString()));
return null;
}
}
My screen:
class HomeScreen extends HookConsumerWidget {
const HomeScreen({Key? key}) : super(key: key);
#override
Widget build(BuildContext context, WidgetRef ref) {
final currentUser = ref.watch(currentUserProvider);
return Scaffold(
body: currentUser.when(
data: (user) => _buildBody(context, user, ref),
loading: () => const Center(child: CircularProgressIndicator()),
error: (error, _) => _errorBody(context, ref),
)
);
}
Widget _buildBody(BuildContext context, AppUser? user, WidgetRef ref) {
if(user == null) {
return _errorBody(context, ref);
} else {
return Center(child: Text(
'Welcome ${user.getNickname}',
style: const TextStyle(fontSize: 20),
));
}
}
Widget _errorBody(BuildContext context, WidgetRef ref) {
return const Center(child: Text(
"Error: No user found",
style: TextStyle(fontSize: 20, color: Colors.red),
));
}
}
I resolved this issue with the following code. It wasn't a postgres issue but a dark story about riverpod's providers.
My providers.dart :
final futureCurrentUserProvider = Provider<Future<AppUser?>>((ref) {
return UserRepository().getCurrentUser(ref.watch(emailChangeProvider));
});
final currentUserProvider = FutureProvider.autoDispose<AppUser?>((ref) => UserRepository().getCurrentUser(ref.watch(emailChangeProvider)));
final authChangeProvider = StreamProvider<User?>((ref) {
return ref.read(authRepositoryProvider).authUserChange;
});
final emailChangeProvider = Provider<String?>((ref) {
return ref.watch(authChangeProvider).value?.email;
});

_TypeError was thrown building NotesListView(dirty) : type 'Null' is not a subtype of type 'String'

I don't know where this error is coming from
enter image description here
the debug console says the error is in the returning line and the return line return just returning a widget but the error is about string is null i don't from where this error is coming
this is notes_view.dart file
class NotesView extends StatefulWidget {
const NotesView({super.key});
#override
State<NotesView> createState() => _NotesViewState();
}
class _NotesViewState extends State<NotesView> {
late final FirebaseCloudStorage _notesService;
String get userId => AuthService.firebase().currentUser!.id;
#override
void initState() {
_notesService = FirebaseCloudStorage();
super.initState();
}
#override
Widget build(BuildContext context) {
return Scaffold(
backgroundColor: Colors.blue[100],
appBar: AppBar(
title: const Text("Your Notes"),
actions: [
IconButton(
onPressed: () {
Navigator.of(context).pushNamed(createOrUpdateNoteRoute);
},
icon: const Icon(Icons.add),
),
PopupMenuButton<MenuActions>(
onSelected: (value) async {
switch (value) {
case MenuActions.logout:
final shouldLogout = await showLogoutDialog(context);
if (shouldLogout) {
await AuthService.firebase().logOut();
Navigator.of(context)
.pushNamedAndRemoveUntil(loginRoute, (_) => false);
}
break;
}
},
itemBuilder: (context) {
return const [
PopupMenuItem<MenuActions>(
value: MenuActions.logout, child: Text("Log out")),
];
},
)
],
),
body: StreamBuilder(
stream: _notesService.allNotes(ownerUserId: userId),
builder: (context, snapshot) {
switch (snapshot.connectionState) {
case ConnectionState.waiting:
case ConnectionState.active:
if (snapshot.hasData) {
final allNotes = snapshot.data as Iterable<CloudNote>;
return NotesListView(
notes: allNotes,
onDeleteNote: (note) async {
await _notesService.deleteNote(
documentId: note.documentId);
print(note.documentId);
},
onTap: (note) {
Navigator.of(context).pushNamed(
createOrUpdateNoteRoute,
arguments: note,
);
},
);
} else {
return const CircularProgressIndicator();
}
default:
return const CircularProgressIndicator();
}
},
));
}
}
this is notes_list_view.dart file
typedef NoteCallback = void Function(CloudNote note);
class NotesListView extends StatelessWidget {
final Iterable<CloudNote> notes;
final NoteCallback onDeleteNote;
final NoteCallback onTap;
const NotesListView({
Key? key,
required this.notes,
required this.onDeleteNote,
required this.onTap,
}) : super(key: key);
#override
Widget build(BuildContext context) {
return ListView.builder(
itemCount: notes.length,
itemBuilder: (context, index) {
final note = notes.elementAt(index);
return ListTile(
onTap: () {
onTap(note);
},
textColor: Colors.black87,
title: Text(
note.text,
maxLines: 1,
softWrap: true,
overflow: TextOverflow.ellipsis,
),
trailing: IconButton(
icon: Icon(
Icons.delete,
color: Colors.red[200],
),
onPressed: () async {
final shouldDelete = await deleteDialog(context);
if (shouldDelete) {
onDeleteNote(note);
}
},
),
);
},
);
}
}
this is the firebase_cloud_storage.dart file
import 'package:cloud_firestore/cloud_firestore.dart';
import 'package:mynotes/services/cloud/cloud_note.dart';
import 'package:mynotes/services/cloud/cloud_storage_constants.dart';
import 'package:mynotes/services/cloud/cloud_storage_exceptions.dart';
class FirebaseCloudStorage {
final notes = FirebaseFirestore.instance.collection('notes');
Future<void> deleteNote({required String documentId}) async {
try {
await notes.doc(documentId).delete();
} catch (e) {
throw CouldNotDeleteNoteException();
}
}
Future<void> updateNote({
required String documentId,
required String text,
}) async {
try {
await notes.doc(documentId).update({textFieldName: text});
} catch (_) {
throw CouldNotUpdateNotesException();
}
}
Stream<Iterable<CloudNote>> allNotes({required String ownerUserId}) {
return notes.snapshots().map((event) => event.docs
.map((doc) => CloudNote.fromSnapshot(doc))
.where((note) => note.ownerUserId == ownerUserId));
}
Future<Iterable<CloudNote>> getNotes({required String ownerUserId}) async {
try {
return await notes
.where(ownerUserIdFieldName, isEqualTo: ownerUserId)
.get()
.then(
(value) => value.docs.map((doc) => CloudNote.fromSnapshot(doc)),
);
} catch (e) {
throw CouldNotGetAllNotesException();
}
}
Future<CloudNote> createNewNote({required String ownerUserId}) async {
final document = await notes.add({
ownerUserIdFieldName: ownerUserId,
textFieldName: '',
});
final fetchedNote = await document.get();
return CloudNote(
documentId: fetchedNote.id,
ownerUserId: ownerUserId,
text: '',
);
}
static final FirebaseCloudStorage _shared =
FirebaseCloudStorage._sharedInstance();
FirebaseCloudStorage._sharedInstance();
factory FirebaseCloudStorage() => _shared;
}
#immutable
class CloudNote {
final String documentId;
final String ownerUserId;
final String text;
const CloudNote({
required this.documentId,
required this.ownerUserId,
required this.text,
});
CloudNote.fromSnapshot(QueryDocumentSnapshot<Map<String, dynamic>> snapshot)
: documentId = snapshot.id,
ownerUserId = snapshot.data()[ownerUserIdFieldName],
text = snapshot.data()[textFieldName] as String;
}
I will like to accept null data while reading map, try
text = snapshot.data()[textFieldName] ?? "got null value";
CloudNote.fromSnapshot(QueryDocumentSnapshot<Map<String, dynamic>> snapshot)
: documentId = snapshot.id,
ownerUserId = snapshot.data()[ownerUserIdFieldName] ?? "default value",
text = snapshot.data()[textFieldName] ?? "Default value";

How can I implement listview.builder in FutureBuilder in Scaffold block?

I want to add my data in listview.
also tried using this https://flutter.dev/docs/cookbook/lists/long-lists
I got data in futureAlbum, and snapshot has data. but i can't convert these data in listView.builder. so, how to implement listview in FutureBuilder?
body: Center(
child: FutureBuilder<ListAlbum>(
future: futureAlbum,
builder: (context, snapshot) {
if (snapshot.hasData) {
print(snapshot.data);
return Text(snapshot.data!.idEmployee.toString()); // in this section i want to add listview
} else if (snapshot.hasError) {
return Text('${snapshot.error}');
}
return const CircularProgressIndicator();
},
),
),
full code of calling api
class OrganizationList extends StatefulWidget {
const OrganizationList({Key? key}) : super(key: key);
#override
_OrganizationListState createState() => _OrganizationListState();
}
class _OrganizationListState extends State<OrganizationList> {
late Future<ListAlbum> futureAlbum;
#override
void initState() {
super.initState();
futureAlbum = listData();
}
#override
Widget build(BuildContext context) {
return Scaffold(
body: Center(
child: FutureBuilder<ListAlbum>(
future: futureAlbum,
builder: (context, snapshot) {
if (snapshot.hasData) {
print(snapshot.data);
return Text(snapshot.data!.idEmployee.toString()); // in this section i want to add listview
} else if (snapshot.hasError) {
return Text('${snapshot.error}');
}
return const CircularProgressIndicator();
},
),
),
);
}
}
class ListAlbum {
final int idEmployee;
final String avatar;
final String fullName;
final String officeID;
final String email;
final String designation;
final String department;
final String mobileNumber;
final String workStation;
final String businessUnit;
ListAlbum({
required this.idEmployee,
required this.avatar,
required this.fullName,
required this.officeID,
required this.email,
required this.designation,
required this.department,
required this.mobileNumber,
required this.workStation,
required this.businessUnit,
});
factory ListAlbum.fromJson(Map<String, dynamic> json) {
return ListAlbum(
idEmployee: json['idEmployee'],
avatar: json['avatar'],
fullName: json['fullName'],
officeID: json['officeID'],
email: json['email'],
designation: json['designation'],
department: json['department'],
mobileNumber: json['mobileNumber'],
workStation: json['workStation'],
businessUnit: json['businessUnit'],
);
}
}
Future<ListAlbum> listData() async {
final token =
'eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJzdWIiOjI4OTksImlzcyI6Imh0dHBzOi8vcG9ydGFsLWFwaS5qb21ha2hhdGEuY29tL2FwaS9hdXRoL2xvZ2luIiwiaWF0IjoxNjI5NTI2OTc1LCJleHAiOjE2Mjk2MTMzNzUsIm5iZiI6MTYyOTUyNjk3NSwianRpIjoiRktiT295eEYwaEpDUXMxdiJ9.o4eM_C4hlluHe9Azk0MspPJtYZ7agdpFA6xwKiijLj8';
String url =
'https://portal-api.jomakhata.com/api/getOrganizationData?token=${token}';
Dio dio = new Dio();
dio.options.headers['Content-Type'] = 'application/json';
final body = {'limit': 5, 'orderBy': 'idEmployee', 'orderType': 'DESC'};
final response = await dio.post(url, data: body);
if (response.statusCode == 200) {
print(response.statusCode);
print(response.data);
var data = ListAlbum.fromJson(response.data["data"]["data"][0]);
return data;
} else {
throw Exception('Failed!');
}
}
What list I want to implement!
First, you have all members is ListAlbum marked as required, but some of your results in the response doesn't have all of these, for example second row has no avatar. You can overcome this by marking these fields as not required, like this (I made here all members optional - adjust it as you need):
class ListAlbum {
final int? idEmployee;
final String? avatar;
final String? fullName;
final String? officeID;
final String? email;
final String? designation;
final String? department;
final String? mobileNumber;
final String? workStation;
final String? businessUnit;
ListAlbum({
this.idEmployee,
this.avatar,
this.fullName,
this.officeID,
this.email,
this.designation,
this.department,
this.mobileNumber,
this.workStation,
this.businessUnit,
});
factory ListAlbum.fromJson(Map<String, dynamic> json) {
return ListAlbum(
idEmployee: json['idEmployee'],
avatar: json['avatar'],
fullName: json['fullName'],
officeID: json['officeID'],
email: json['email'],
designation: json['designation'],
department: json['department'],
mobileNumber: json['mobileNumber'],
workStation: json['workStation'],
businessUnit: json['businessUnit'],
);
}
}
Next, convert your listData function so that it will return a list of ListAlbum objects. You can grab the data from your response, convert and return like this:
Future<List<ListAlbum>> listData() async {
final token =
'eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJzdWIiOjI4OTksImlzcyI6Imh0dHBzOi8vcG9ydGFsLWFwaS5qb21ha2hhdGEuY29tL2FwaS9hdXRoL2xvZ2luIiwiaWF0IjoxNjI5NTI2OTc1LCJleHAiOjE2Mjk2MTMzNzUsIm5iZiI6MTYyOTUyNjk3NSwianRpIjoiRktiT295eEYwaEpDUXMxdiJ9.o4eM_C4hlluHe9Azk0MspPJtYZ7agdpFA6xwKiijLj8';
String url =
'https://portal-api.jomakhata.com/api/getOrganizationData?token=${token}';
Dio dio = new Dio();
dio.options.headers['Content-Type'] = 'application/json';
final body = {'limit': 5, 'orderBy': 'idEmployee', 'orderType': 'DESC'};
final response = await dio.post(url, data: body);
if (response.statusCode == 200) {
print(response.statusCode);
print(response.data);
return response.data["data"]["data"]
.map<ListAlbum>((json) => ListAlbum.fromJson(json))
.toList();
} else {
throw Exception('Failed!');
}
}
Finally, change the future return type and create the ListView from this list, this is an example, adjust it:
class _OrganizationListState extends State<OrganizationList> {
late Future<List <ListAlbum>> futureAlbum;
#override
void initState() {
super.initState();
futureAlbum = listData();
}
#override
Widget build(BuildContext context) {
return Scaffold(
body: Center(
child: FutureBuilder<List<ListAlbum>>(
future: futureAlbum,
builder: (context, snapshot) {
if (snapshot.hasData) {
print(snapshot.data);
return ListView.builder(
itemCount: snapshot.data!.length,
itemBuilder: (context, index) {
final ListAlbum item = snapshot.data![index];
return ListTile(
leading: Text(item.idEmployee.toString()),
title: Text(item.fullName!),
subtitle: Text(item.designation!),
trailing: Text(item.businessUnit!),
);
},
);
} else if (snapshot.hasError) {
return Text('${snapshot.error}');
}
return const CircularProgressIndicator();
},
),
));
}
}
...and I don't know whether token is a secret or not, but if it is, you should revoke it.
Try this:
Future<List<ListAlbum>> listData() async {
final token =
'eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJzdWIiOjI4OTksImlzcyI6Imh0dHBzOi8vcG9ydGFsLWFwaS5qb21ha2hhdGEuY29tL2FwaS9hdXRoL2xvZ2luIiwiaWF0IjoxNjI5NTI2OTc1LCJleHAiOjE2Mjk2MTMzNzUsIm5iZiI6MTYyOTUyNjk3NSwianRpIjoiRktiT295eEYwaEpDUXMxdiJ9.o4eM_C4hlluHe9Azk0MspPJtYZ7agdpFA6xwKiijLj8';
String url =
'https://portal-api.jomakhata.com/api/getOrganizationData?token=${token}';
Dio dio = new Dio();
dio.options.headers['Content-Type'] = 'application/json';
final body = {'limit': 5, 'orderBy': 'idEmployee', 'orderType': 'DESC'};
final response = await dio.post(url, data: body);
if (response.statusCode == 200) {
print(response.statusCode);
print(response.data);
List<ListAlbum> _list=response.data["data"]["data"].map((e)=>ListAlbum.fromJson(e)).toList();
return _list;
} else {
throw Exception('Failed!');
}
}
And your widget to this:
class OrganizationList extends StatefulWidget {
const OrganizationList({Key? key}) : super(key: key);
#override
_OrganizationListState createState() => _OrganizationListState();
}
class _OrganizationListState extends State<OrganizationList> {
late Future<List<ListAlbum>> futureAlbum;
#override
void initState() {
super.initState();
futureAlbum = listData();
}
#override
Widget build(BuildContext context) {
return Scaffold(
body: Center(
child: FutureBuilder<List<ListAlbum>>(
future: futureAlbum,
builder: (context, snapshot) {
if (snapshot.hasData) {
print(snapshot.data);
return Column(
children:[
for(ListAlbum item in snapshot.data)
Text(item.idEmployee.toString())]);
//Or you can also use listview.builder here instead of column, but this will work for proof of concept.
} else if (snapshot.hasError) {
return Text('${snapshot.error}');
}
return const CircularProgressIndicator();
},
),
),
);
}
}

Error when using StreamProvider and StreamBuilder

I am trying to use StreamProvider and StreamBuilder to pull data from firestore into my app with the code below. I am getting the error "streamusers and "userslist" are not defined as well as "testuser" is not a type. Here is a picture of my firestore databasefirestore setup]1
does anyone know how I can fix this so that it pulls the data from firestore and updates dynamically when new users are added?
Main.dart:
class _MyHomePageState extends State<MyHomePage> {
final auth = FirebaseAuth.instance;
final db = DatabaseService();
#override
Widget build(BuildContext context) {
var user = Provider.of<FirebaseUser>(context);
bool loggedIn = user != null;
final _width = MediaQuery.of(context).size.width;
final _height = MediaQuery.of(context).size.height;
StreamProvider<List<User>>.value(
value: db.streamUsers(user),
child: UsersList(),
),
StreamBuilder<TestUser>(
stream: db.streamTestUser(user.uid),
builder: (context, snapshot) {
var user = snapshot.data;
if (user != null) {
return Stack(...
I also have my db.dart file as so:
class DatabaseService {
final Firestore _db = Firestore.instance;
Future<User> getUser(String id) async {
var snap = await _db.collection('users').document(id).get();
return User.fromMap(snap.data);
}
Stream<User> streamTestUser(String id) {
return _db
.collection('users')
.document(id)
.snapshots()
.map((snap) => User.fromMap(snap.data));
}
}
And finally my user_model.dart file:
class User {
final String name;
final String photourl;
final int totalquestions;
User({this.name, this.photourl, this.totalquestions});
factory User.fromMap(Map data) {
return User(
name: data['name'] ?? '',
photourl: data['photourl'] ?? '',
totalquestions: data['totalquestions'] ?? '',
);
}
}
Try using Builder inside StreamProvider instead of StreamBuilder.
Mine is working using this approach.
class MyHomePage extends StatelessWidget {
#override
Widget build(BuildContext context) {
var user = Provider.of<FirebaseUser>(context);
return StreamProvider<User>.value(
value: db.getUser(user?.uid),
catchError: (_, __) => null,
child: Builder(
builder: (context) {
///Passing UserData Down the Builder
var _userSnapshot = Provider.of<UserData>(context);
///Check UserData Availability
if (_userSnapshot == null) {
return Center(
child: Text('User Empty'),
);
} else {
return Scaffold(
body: Column(
children: <Widget>[
Text(_userSnapshot?.name),
Text(_userSnapshot?.photourl),
Text(_userSnapshot?.totalquestions),
],
),
);
}
},
),
);
}