When I try to use Provider to look at whether the user is signed in or not, I'm getting an error. If the user is signed in, I want to go to the profile page. If the user isn't signed in, I want to go to the login page.
I created a class AuthenticationWrapper
that returns to a route. I then navigate through that route.
Here is the error:
The following ProviderNotFoundException was thrown building AuthenticationWrapper(dirty):
Error: Could not find the correct Provider<User> above this AuthenticationWrapper 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 AuthenticationWrapper is under your MultiProvider/Provider<User>.
This usually happens when you are creating a provider and trying to read it immediately.
For example, instead of:
```
Widget build(BuildContext context) {
return Provider<Example>(
create: (_) => Example(),
// Will throw a ProviderNotFoundError, because `context` is associated
// to the widget that is the parent of `Provider<Example>`
child: Text(context.watch<Example>().toString()),
);
}
```
consider using `builder` like so:
```
Widget build(BuildContext context) {
return Provider<Example>(
create: (_) => Example(),
// we use `builder` to obtain a new `BuildContext` that has access to the provider
builder: (context, child) {
// No longer throws
return Text(context.watch<Example>().toString());
}
);
}
```
If none of these solutions work, consider asking for help on StackOverflow:
https://stackoverflow.com/questions/tagged/flutter
The relevant error-causing widget was
AuthenticationWrapper
Here is my main.dart:
import 'package:flutter/material.dart';
import 'package:firebase_auth/firebase_auth.dart';
import 'package:firebase_core/firebase_core.dart';
import 'firebase_options.dart';
import 'package:provider/provider.dart';
import 'Screens/login.dart';
import 'Screens/profile.dart';
import 'Screens/authentication.dart';
void main () async{
WidgetsFlutterBinding.ensureInitialized();
await Firebase.initializeApp(options: DefaultFirebaseOptions.currentPlatform,);
runApp(const MyApp());
}
class MyApp extends StatelessWidget {
const MyApp({Key? key}) : super(key: key);
@override
Widget build(BuildContext context) {
return MultiProvider(
providers: [
Provider<AuthenticationService>(create: (_) => AuthenticationService(FirebaseAuth.instance),),
StreamProvider(initialData: 5,create: (context) => context.read<AuthenticationService>().authStateChanges,)],
child: MaterialApp(
debugShowCheckedModeBanner: false,
initialRoute: '/',
routes: {
'/':(context) => const HomeScreen(),
'/login': (context) => const LoginScreen(),
'/profile': (context) => const ProfileScreen(),
'/userSignIn': (context) => const AuthenticationWrapper()
},
)
);
}
}
// Home Screen - First Run
class HomeScreen extends MyApp {
const HomeScreen({Key? key}) : super(key: key);
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
elevation: 0,
bottomOpacity: 0.0,
flexibleSpace: Container(
decoration: const BoxDecoration(gradient: LinearGradient(begin: Alignment.topCenter, end:Alignment.bottomCenter, colors: [ Color(0xFFFE6E08), Colors.white]))
),
leading: IconButton(iconSize: 40.0, icon: const Icon(Icons.menu, color: Colors.black), tooltip: "Menu",
onPressed: () {
},
),
title: Image.asset('assets/topImage.png',scale: 8),
centerTitle: true,
actions: <Widget>[
IconButton(iconSize: 40.0, icon: const Icon(Icons.account_circle, color: Colors.black,), tooltip: "Click Here to Login or View Profile",
onPressed: () {
Navigator.pushNamed(context, '/userSignIn');
}, // onPressed
),
],
),
);
}
}
class AuthenticationWrapper extends StatelessWidget {
const AuthenticationWrapper({Key? key}) : super(key: key);
@override
Widget build(BuildContext context) {
final firebaseUser = context.watch<User>();
if (firebaseUser != null) {
return const ProfileScreen();
}
return const LoginScreen();
}
}
Here is my login.dart: ``` import 'package:flutter/material.dart'; import 'package:provider/provider.dart';
import 'authentication.dart';
class LoginScreen extends StatelessWidget {
const LoginScreen({Key? key}) : super(key: key);
@override
Widget build(BuildContext context) {
return WillPopScope(onWillPop: () async => false, child:
Scaffold(
appBar: AppBar(
elevation: 0,
bottomOpacity: 0.0,
flexibleSpace: Container(
decoration: const BoxDecoration(
gradient: LinearGradient(begin: Alignment.topCenter, end:Alignment.bottomCenter, colors: [ Color(0xFFFE6E08), Colors.white]),
),
),
leading: IconButton(iconSize: 40.0, icon: const Icon(Icons.menu, color: Colors.black), tooltip: "Menu",
onPressed: () {
},
),
title: IconButton(icon:Image.asset('assets/topImage.png'), iconSize: 65,
onPressed: () {
Navigator.pop(context);
},
),
centerTitle: true,
),
body: const Inputs()
)
);
}
}
class Inputs extends StatefulWidget {
const Inputs({Key? key}) : super(key: key);
@override
State<Inputs> createState() => _InputsState();
}
class _InputsState extends State<Inputs> {
final emailController = TextEditingController();
final passwordController = TextEditingController();
@override
void dispose() {
emailController.dispose();
passwordController.dispose();
super.dispose();
}
bool userauth(email, password) {
return false;
}
@override
Widget build(BuildContext context) {
return Scaffold(
body: SingleChildScrollView(
child: Column(
children: <Widget>[
// Top Image
Padding(
padding: const EdgeInsets.only(top: 60.0),
child: Center(
child: SizedBox(
width: 200,
height: 150,
child: Image.asset('assets/topImage.png')
),
)
),
// Email Input
Padding(
padding: const EdgeInsets.symmetric(horizontal: 15),
child: TextField(
controller: emailController,
decoration: const InputDecoration(
border: OutlineInputBorder(),
labelText: 'Email',
hintText: 'Enter valid email id as abcgamil.com'
)
),
),
// Password Input
Padding(
padding: const EdgeInsets.only(
left: 15.0, right: 15.0, top: 15, bottom: 0
),
child: TextField(
controller: passwordController,
obscureText: true,
decoration: const InputDecoration(
border: OutlineInputBorder(),
labelText: 'Password',
hintText: 'Enter Secure Password'
),
)
),
// Forgot Password Text
TextButton(
onPressed: (){
// Add Forgot Password Thing Here
},
child: const Text('Forgot Password', style: TextStyle(color: Colors.black, fontSize: 15),),
),
// Submit Box
InkWell(
onTap: () {
print(emailController.text);
print(passwordController.text);
emailController.text = "";
passwordController.text = "";
context.read<AuthenticationService>().signIn(
email: emailController.text.trim(),
password: passwordController.text.trim(),
);
},
child: Ink(
height: 50,
width: 250,
decoration: BoxDecoration(
color: Colors.black,
borderRadius: BorderRadius.circular(20)
),
child: const Center(child: Text("Sign In", style: TextStyle(color: Colors.white)))
)
),
// Box for spacing
const SizedBox(height: 100),
// Text for Sign Up
const Text('New User? Create Account')
,]
),
)
);
}
}
Here is my authentication.dart:
import 'package:firebase_auth/firebase_auth.dart';
class AuthenticationService {
final FirebaseAuth _firebaseAuth;
AuthenticationService(this._firebaseAuth);
/// Changed to idTokenChanges as it updates depending on more cases.
Stream<User?> get authStateChanges => _firebaseAuth.idTokenChanges();
/// This won't pop routes so you could do something like
/// Navigator.of(context).pushNamedAndRemoveUntil('/', (Route<dynamic> route) => false);
/// after you called this method if you want to pop all routes.
Future<void> signOut() async {
await _firebaseAuth.signOut();
}
/// There are a lot of different ways on how you can do exception handling.
/// This is to make it as easy as possible but a better way would be to
/// use your own custom class that would take the exception and return better
/// error messages. That way you can throw, return or whatever you prefer with that instead.
Future<String?> signIn({required String email, required String password}) async {
try {
await _firebaseAuth.signInWithEmailAndPassword(email: email, password: password);
return "Signed in";
} on FirebaseAuthException catch (e) {
return e.message;
}
}
/// There are a lot of different ways on how you can do exception handling.
/// This is to make it as easy as possible but a better way would be to
/// use your own custom class that would take the exception and return better
/// error messages. That way you can throw, return or whatever you prefer with that instead.
Future<String?> signUp({required String email, required String password}) async {
try {
await _firebaseAuth.createUserWithEmailAndPassword(email: email, password: password);
return "Signed up";
} on FirebaseAuthException catch (e) {
return e.message;
}
}
}
```
Thanks for the help :)
CodePudding user response:
The problem seems to be while instantiating StreamProvider
. You provided a number to its initialData
argument which is wrong, as it's predicting the type to int
. The type of StreamProvider
should be User?
and initialData
can be null
StreamProvider<User?>(initialData: null,create: (context) => context.read<AuthenticationService>().authStateChanges,)