So i want to implement role based authentication to my app in flutter. Only users with the permissions should get to this site. Im getting the error:
The method 'then' isn't defined for the type 'User'.
Try correcting the name to the name of an existing method, or defining a method named 'then'.
I've tried to solve it for 2 hours now, but every tutorial is outdated or not for my usecase.
Full code of Authentication Handler:
import 'package:cloud_firestore/cloud_firestore.dart';
import 'package:flutter/material.dart';
import 'package:flutter/widgets.dart';
import 'package:firebase_auth/firebase_auth.dart';
import 'package:learnon/screens/lehrerstartpage.dart';
import '../screens/startscreen.dart';
import '../screens/auth_screen.dart';
class AutoLoginHandler extends StatelessWidget {
#override
Widget build(BuildContext context) {
return StreamBuilder<User?>(
//Streambuilder looks if data is avaliable
stream: FirebaseAuth.instance.authStateChanges(),
builder: (context, snapshot) {
if (snapshot.hasData && snapshot.data != null) {
return MainPage(); //when data here goto Startscreen
}
return LoginScreen(); //when no data is here goto Login
},
);
}
final FirebaseAuth auth = FirebaseAuth.instance;
authorizeAccess(BuildContext context) {
FirebaseAuth.instance.currentUser!.then((user) {
FirebaseFirestore.instance
.collection('/users')
.where('uid', isEqualTo: user.uid)
.get()
.then((results) {
if (results.size > 0) {
if (results.docs[0].data()['role'] == 'admin')
{
Navigator.of(context).push(new
MaterialPageRoute(
builder: (BuildContext context) => new
LehrerMainPage()));
}
}
});
});
}
}
Related
So i want to implement role based authentication to my app in flutter. Only users with the permissions should get to this site. Im getting the error:
The getter 'documents' isn't defined for the type 'QuerySnapshot<Map<String, dynamic>>'.
Try importing the library that defines 'documents', correcting the name to the name of an existing getter, or defining a getter or field named 'documents'.
Can anybody help me here?
Code:
class AutoLoginHandler extends StatelessWidget {
#override
authorizeAccess(BuildContext context) {
FirebaseAuth.instance.currentUser!().then((user) {
FirebaseFirestore.instance
.collection('/users')
.where('uid', isEqualTo: user.uid)
.get()
.then((docs) {
if (docs.documents[0].data.exists) {
if (docs.documents[0].data['role'] == 'admin') {
Navigator.of(context).push(new MaterialPageRoute(
builder: (BuildContext context) => new LehrerMainPage()));
}
}
});
});
}
}
try this. But I suggest to you to use for role management access always use Custom Claims.
.then((results) {
if (results.size > 0) {
if (results.docs[0].data()['role'] == 'admin')
{
Navigator.of(context).push(new
MaterialPageRoute(
builder: (BuildContext context) => new
LehrerMainPage()));
}
}
}
Hello fellow flutter developers,
I have a bug that's been making my life pretty complicated while developing my own app using Flutter. It goes like this:
User opens app
if they're not signed in, they're redirected to USP page.
If they click next, they're redirected to the sign up page.
Sign-up is provided by Firebase and it's anonymous
If sign-up is successful, a Provider should be triggered and a new page is loaded
The bug is that sometimes the user is sent back to the USP page (meaning their user_id is null) despite no exception between thrown during sign-up. If I force the navigation to the signed-in page, then the user doesn't have an user_id and that's an issue for me.
Any one experienced and fixed the same issue? Below you can see how I built my code, maybe this can help?
This is my main file:
import 'dart:async';
import 'package:flutter/material.dart';
import 'package:provider/provider.dart';
Future<void> main() async {
WidgetsFlutterBinding.ensureInitialized();
await SignIn.initializeFirebase();
runApp(MyApp());
}
class MyApp extends StatelessWidget {
getSignInUser() {
return SignIn().user;
}
Widget getMaterialApp() {
return MaterialApp(
title: 'app_title',
home: HomePagePicker(),
onGenerateRoute: RouteGenerator.generateRoute,
debugShowCheckedModeBanner: false,
);
}
#override
Widget build(BuildContext context) {
return MultiProvider(
providers: [
StreamProvider<MyUser?>.value(value: SignIn().user, initialData: null),
],
child: getMaterialApp(),
);
}
}
class HomePagePicker extends StatefulWidget {
#override
_HomePagePickerState createState() => _HomePagePickerState();
}
class _HomePagePickerState extends State<HomePagePicker> {
#override
Widget build(BuildContext context) {
MyUser? myUser = Provider.of<MyUser?>(context);
if (myUser == null) return IntroScreen(); // this shows the USPs
else {
// this takes you to the signed-in part of the app
return AnotherScreen();
}
}
}
The IntroScreen is a very simple screen with a few USPs and a button to open the registration page. It goes something like
import 'package:flutter/material.dart';
import 'package:flutter/services.dart';
import 'login.dart';
class IntroScreen extends StatelessWidget {
static const routeName = '/introScreen';
#override
Widget build(BuildContext context) {
analytics.setScreenName("introScreen");
return Scaffold(
body: AnnotatedRegion<SystemUiOverlayStyle>(
value: SystemUiOverlayStyle(statusBarColor: ThemeConfig.darkPrimary),
child: Column(...), // show the USPs
floatingActionButton: getFloatingButton(context)
);
}
Widget getFloatingButton(BuildContext buildContext) {
return FloatingActionButton(
backgroundColor: ThemeConfig.primary,
foregroundColor: Colors.white,
child: Icon(Icons.arrow_forward),
onPressed: () {
navigateToScreen(MyLogin.routeName, buildContext, null);
},
);
}
// this is in another file normally but putting it here for completeness
navigateToScreen(String routeName, BuildContext context, Object? arguments) {
Navigator.pushNamed(
context,
routeName,
arguments: arguments
);
}
The important bit in the registration page is this
Future<void> finalizeRegistration(String userName, String userToken) async {
await usersCollection.add({'userName': userName, "userToken": userToken});
}
Future<void> registerUser(String userName) {
return SignIn()
.anonymousSignIn(userName)
.timeout(Duration(seconds: 2))
.then((userToken) {
finalizeRegistration(userName, userToken)
.then((value) => Navigator.pop(context));
})
.catchError((error) {
registrationErrorDialog();
});
}
The SignIn class is the following
import 'package:cloud_firestore/cloud_firestore.dart';
import 'package:firebase_core/firebase_core.dart';
import 'package:firebase_auth/firebase_auth.dart';
import 'package:house_party/models/MyUser.dart';
class SignIn {
final FirebaseAuth _auth = FirebaseAuth.instance;
static Future<FirebaseApp> initializeFirebase() async {
FirebaseApp firebaseApp = await Firebase.initializeApp();
return firebaseApp;
}
Stream<MyUser?> get user {
return _auth
.authStateChanges()
.asyncMap(getUser);
}
Future<String> anonymousSignIn(String userName) async {
var authResult = await _auth.signInAnonymously();
return authResult.user!.uid;
}
Future<MyUser?> getUser(User? user) async {
if (user == null) {
return Future.value(null);
}
return FirebaseFirestore.instance
.collection('users')
.where('userToken', isEqualTo: user.uid)
.get()
.then((res) {
if (res.docs.isNotEmpty) {
return MyUser.fromFireStore(res.docs.first.data());
} else {
return null;
}
});
}
}
Finally, I'm using these versions of firebase
firebase_core: ^1.0.0
cloud_firestore: ^1.0.0
firebase_dynamic_links: ^2.0.0
firebase_auth: 1.1.2
firebase_analytics: ^8.1.1
I hope the problem statement is clear enough!
Thanks in advance!
I fixed this problem (or at least I'm not able to reproduce it anymore) by upgrading the firebase libraries as follows
firebase_core: ^1.4.0
cloud_firestore: ^2.4.0
firebase_dynamic_links: ^2.0.7
firebase_auth: 3.0.1
firebase_analytics: ^8.2.0
void _myMatches() {
if (SignUp.userUid != null) {
FirebaseFirestore.instance
.collection("posts")
.where(
'owner id',
isEqualTo: SignUp.userUid,
)
.where("User Id", isNotEqualTo: [])
.where("rental status", isEqualTo: false)
.get()
.then((value) {
value.docs.forEach((result) {
print(result.data());
});
});
} else {
FirebaseFirestore.instance
.collection("posts")
.where(
'owner id',
isEqualTo: Loginpage.userUid,
)
.where("User Id", isNotEqualTo: [])
.where("rental status", isEqualTo: false)
.get()
.then((value) {
value.docs.forEach((result) {
print(result.data());
});
});
}
}
}
Hi, I am using flutter and firestore to write a program. My function that reads the data is as follows:(mentioned above)
which i call when a specific button is pressed. This leads to the data being read from firestore to be printed on the console. What do I do to display it on my emulator. How do I wrap this data in a widget so I can display it on the screen on whichever page i want?
The key is to use a FutureBuilder to render UI after you get the data, and show loading before that. Then inside builder of FutureBuilder, use ListView and ListTile(or anything you like) to render list items.
A minimum example might looks like this:
import 'package:cloud_firestore/cloud_firestore.dart';
import 'package:firebase_core/firebase_core.dart';
import 'package:flutter/material.dart';
void main() async {
WidgetsFlutterBinding.ensureInitialized();
await Firebase.initializeApp();
runApp(MaterialApp(
home: App(),
));
}
class App extends StatelessWidget {
Future<QuerySnapshot<Map<String, dynamic>>> getData() {
// Handle any data retrieval logic you want
return FirebaseFirestore.instance.collection('posts').get();
}
#override
Widget build(BuildContext context) {
return FutureBuilder<QuerySnapshot<Map<String, dynamic>>>(
// plug your future snapshot here
future: getData(),
builder: (context, snapshot) {
// Check loading
if (snapshot.connectionState == ConnectionState.waiting) {
return CircularProgressIndicator();
}
// Check error
final queryData = snapshot.data;
if (snapshot.hasError || queryData == null) {
return Icon(Icons.error);
}
return Scaffold(
// Use ListView.builder to render only visible items
body: ListView.builder(
itemCount: queryData.docs.length,
itemBuilder: (context, index) {
// Get data inside docs
final docData = queryData.docs[index].data();
return ListTile(
title: docData['title'],
subtitle: docData['subtitle'],
);
},
),
);
});
}
}
I'm getting a weird error, can anyone explain why this is and what would be the solution?
I need to compare different collection fields in firestore. I was thinking it might be because I'm using both streambuilder and provider? so one of the context gets 'confused'. If this is so, is there any alternative to what I'm trying to do?
import 'package:flutter/material.dart';
import 'package:cloud_firestore/cloud_firestore.dart';
import 'package:firebase_auth/firebase_auth.dart';
import 'package:carpoolapp/shared/loading.dart';
import 'package:provider/provider.dart';
import 'package:carpoolapp/models/chatdata.dart';
import 'package:carpoolapp/screens/home/chat_tile.dart';
final _firestore = FirebaseFirestore.instance;
User user = FirebaseAuth.instance.currentUser;
class preMessageTab extends StatefulWidget {
#override
_preMessageTabState createState() => _preMessageTabState();
}
class _preMessageTabState extends State<preMessageTab> {
int count = 0;
var chats;
void prov(){
chats = Provider.of<List<ChatData>>(context);
}
#override
Widget build(BuildContext context) {
prov();
return StreamBuilder(
stream: _firestore.collection('users').doc(user.uid).collection('messages').snapshots(),
builder: (context, snapshot) {
if (!snapshot.hasData) {
return Center(
child: Loading(),
);
}
else{
return ListView.builder(
itemCount: chats.length,
itemBuilder: (context, index) {
snapshot.data.docs.forEach((doc) {
if(doc.data()['sender'] == chats[index].uid){
++count;
}
});
if(count>=1){
return ChatTile(chat: chats[index],);
}
else{
return SizedBox(height: 20.0,);
}
}
);
}
}
);
}
}
The error indicates that your widget can't access the provider. This could be due to the multiple reasons mentioned in the red screen and debug console.
Mainly, always check that your provider is above your widget.
If you are using more than one provider, use multiProvider.
If the problem still persists, it would mean that the widget you are calling provider from, and your multiprovider, are in two separate material route.
Fixing this issue would solve the problem usually, as you suggested also, using a middleMan widget.
Created another dart file and used that as a 'middleman' to get to the original tab. The route was the issue.
#Huthaifa Thank you for your help.
class prepreMessageTab extends StatefulWidget {
#override
_prepreMessageTabState createState() => _prepreMessageTabState();
}
class _prepreMessageTabState extends State<prepreMessageTab> {
#override
Widget build(BuildContext context) {
return StreamProvider<List<ChatData>>.value(
value: DatabaseService().chatData,
child: Scaffold(
body: preMessageTab(),
),
);
}
}
I'm new to flutter and trying to create a login app.
I have 2 screens.
Login (If user enters correct credentials, store user information to local db(sqflite) and navigate to home).
Home (have logout option).
I'm trying to achieve auto login i.e when user closes the app without logging out, the app should navigate to home automatically without logging again when app reopens.
My logic:
If user enters valid credentials, clear the db table and insert newly entered credentials.
Auto login - when app starts, check if record count in db table is 1, then navigate to home else login.
Here's the code which I have tried:
class MyApp extends StatefulWidget {
#override
_MyAppState createState() => _MyAppState();
}
class _MyAppState extends State<MyApp> {
final dbHelper = DatabaseHelper.instance;
bool logged = false;
#override
void initState() {
super.initState();
autoLogIn();
}
void autoLogIn() async {
if (await dbHelper.queryRowCount() == 1) {
setState(() {
logged = true;
});
return;
}
}
#override
Widget build(BuildContext context) {
return logged ? HomeScreen(): LoginScreen();
}
}
It makes me as if, the widget is build before the state of logged is changed.
How can I achieve auto login assuming there is no issue with database(sqflite) implementation.
I have used SharedPreferences instead of local database and that worked.
Below is the minimal implementation
import 'package:IsBuddy/Screens/Dashboard/dashboard.dart';
import 'package:IsBuddy/Screens/Login/login_screen.dart';
import 'package:flutter/material.dart';
import 'package:shared_preferences/shared_preferences.dart';
class AutoLogin extends StatefulWidget {
#override
_AutoLoginState createState() => _AutoLoginState();
}
class _AutoLoginState extends State<AutoLogin> {
Future<SharedPreferences> _prefs = SharedPreferences.getInstance();
Future<bool> logged;
#override
void initState() {
logged = _prefs.then((SharedPreferences prefs) {
return (prefs.getBool('logged') ?? false);
});
super.initState();
}
#override
Widget build(BuildContext context) {
return FutureBuilder<bool>(
future: logged,
builder: (BuildContext context, AsyncSnapshot<bool> snapshot) {
switch (snapshot.connectionState) {
case ConnectionState.waiting:
return Container(
child: Center(
child: CircularProgressIndicator(),
));
default:
if (snapshot.hasError) {
return Text('Error: ${snapshot.error}');
} else {
return snapshot.data ? Dashboard() : LoginScreen();
}
}
},
);
}
}