Flutter MultiProvider Not found - flutter

Error: Could not find the correct Provider 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.
This usually happens when you are creating a provider and trying to read it immediately
import 'package:firebase_auth/firebase_auth.dart';
import 'package:firebase_core/firebase_core.dart';
import 'package:flutter/material.dart';
import 'package:flutter/services.dart';
import 'package:google_fonts/google_fonts.dart';
import 'package:park_app/app_styles.dart';
import 'package:provider/provider.dart';
import 'package:shared_preferences/shared_preferences.dart';
import './views/pages.dart';
import 'views/authentication/authentication_service.dart';
import 'Home_Page.dart';
bool? seenOnboard;
Future<void> main() async {
WidgetsFlutterBinding.ensureInitialized();
await Firebase.initializeApp();
// to show status bar
SystemChrome.setEnabledSystemUIOverlays(
[SystemUiOverlay.bottom, SystemUiOverlay.top]);
// to load onboard for the first time only
SharedPreferences pref = await SharedPreferences.getInstance();
seenOnboard = pref.getBool('seenOnboard') ?? false; //if null set to false
runApp(MyApp());
}
class MyApp extends StatelessWidget {
#override
Widget build(BuildContext context) {
return MultiProvider(
providers: [
Provider<AuthenticationService>(
create: (_) => AuthenticationService(FirebaseAuth.instance),
),
StreamProvider(
initialData: null,
create: (context) =>
context.read<AuthenticationService>().authStateChanges),
],
child: MaterialApp(
debugShowCheckedModeBanner: false,
title: 'Park App',
theme: ThemeData(
textTheme: GoogleFonts.manropeTextTheme(
Theme.of(context).textTheme,
),
primarySwatch: Colors.blue,
scaffoldBackgroundColor: kScaffoldBackground,
),
home: seenOnboard == true ? AuthenticationWrapper() : OnBoardingPage(),
),
);
}
}
class AuthenticationWrapper extends StatelessWidget {
#override
Widget build(BuildContext context) {
final firebaseUser = context.watch<User>();
if (firebaseUser != null) {
return HomePage();
}
return LoginPage();
}
}

install provider by running the command below:
flutter pub add provider and then in your main.dart file, import it import 'package:provider/provider.dart';

Make sure to specify the generic type on StreamProvider:
StreamProvider<User?>(
...
)
Note that you have set null as initialData, so your widget likely needs to handle null users. Meaning you need to do:
final user = context.watch<User?>()

Related

Unhandled Exception: PlatformException(null-error, Host platform returned null value for non-null return value., null, null)

hello I hope you are well, I have the following problem, when I run my app in flutter I get the following ERROR: E/flutter ( 6922): [ERROR:flutter/runtime/dart_vm_initializer.cc(41)] Unhandled Exception: PlatformException( null-error, Host platform returned null value for non-null return value., null, null), as I am working with firebase, but I have searched a lot and the truth is that the solutions that come up have not worked for me or I do not know if it is me who I implemented it incorrectly, soon I will show you the code, thank you very much for stopping to read this publication. I also forgot to mention I'm working on visual studio code
MAIN CODE(),
import 'package:flutter/material.dart';
import 'package:provider/provider.dart';
import 'src/Base/Views/BaseView.dart';
import 'src/Feactures/Presentation/Shared/StateProviders/LoadingStateProvider.dart';
import 'src/Feactures/Presentation/Shared/StateProviders/UserStateProvider.dart';
import 'src/Colors/colors.dart';
import 'src/Routes/routes.dart';
import 'package:flutter_localizations/flutter_localizations.dart';
import 'package:firebase_core/firebase_core.dart';
void main() => runApp(AppState());
class AppState extends StatelessWidget {
#override
Widget build(BuildContext context) {
return MultiProvider(
providers: [
ChangeNotifierProvider(create: (_) => LoadingStateProvider()),
ChangeNotifierProvider(create: (_) => UserStateProvider())
],
child: MyAppUserState(),
);
}
}
class MyAppUserState extends StatelessWidget with BaseView {
#override
Widget build(BuildContext context) {
return FutureBuilder(
future: coordinator.start(),
builder: (BuildContext context, AsyncSnapshot snapshot) {
if (snapshot.hasData) {
return MyApp(initialRoute: snapshot.data);
} else {
return CircularProgressIndicator();
}
});
}
}
class MyApp extends StatelessWidget {
final String _initialRoute;
MyApp({required String initialRoute}) : _initialRoute = initialRoute;
#override
Widget build(BuildContext context) {
Firebase.initializeApp();
return MaterialApp(
debugShowCheckedModeBanner: false,
routes: routes,
initialRoute: _initialRoute,
theme: ThemeData(
scaffoldBackgroundColor: Colors.white,
primaryColor: orange,
appBarTheme:
const AppBarTheme(iconTheme: IconThemeData(color: Colors.black))),
localizationsDelegates: const [
GlobalMaterialLocalizations.delegate,
GlobalWidgetsLocalizations.delegate,
GlobalCupertinoLocalizations.delegate,
],
supportedLocales: const [
Locale('en', ''), // English, no country code
Locale('es', ''), // Spanish, no country code
],
);
}
}
I really expected the app to run fine but it sends that error
You probably don't want to initialize Firebase in the build method. Try doing this instead (which is in the FlutterFire documentation):
void main() async {
WidgetsFlutterBinding.ensureInitialized();
await Firebase.initializeApp();
runApp();
}
Add the following plugin of the recent/latest version in the pubspec.yaml:
firebase_core :recent version
Please do the following changes in main.dart file.
Hope it works!!
import 'package:firebase_core/firebase_core.dart';
void main() async {
WidgetsFlutterBinding.ensureInitialized();
await Firebase.initializeApp();
runApp(AppState());
}

Could not find the correct Provider<User> above this AuthWrapper Widget

I am trying to fix this error
Error: Could not find the correct Provider above this AuthWrapper Widget.
Following this tutorial but it doesnt seem to work for me.https://www.youtube.com/watch?v=yyD_VqSrKd8
here is the code:
import 'package:firebase_core/firebase_core.dart';
import 'package:firebase_auth/firebase_auth.dart';
import 'package:untitled2/firbaseauth.dart';
import 'package:untitled2/homescreen.dart';
import 'package:untitled2/startpage.dart';
import 'package:flutter/material.dart';
import 'package:provider/provider.dart';
void main() async {
WidgetsFlutterBinding.ensureInitialized();
await Firebase.initializeApp();
runApp(MyApp());
}
class MyApp extends StatelessWidget {
// This widget is the root of your application.
#override
Widget build(BuildContext context) {
return MultiProvider(
providers: [
Provider<Authservis>(
create: (_) => Authservis(FirebaseAuth.instance),
),
StreamProvider(create: (context) => context.read<Authservis>().authStateChanges,
initialData: null,),
],
child: MaterialApp(
title: "APP",
home: AuthWrapper(),
),
);
}
}
class AuthWrapper extends StatelessWidget{
#override
Widget build(BuildContext context) {
final user = context.watch<User>();
if(user != null){
return Homescreen();
}
return Startpage();
}
}
The error exactly tells you that there is no Provider named User above the widget that calls such provider.
Your MultiProvider at the top of your tree includes just a Auhtservis provider, but in order to use your User provider, the list should include the User provider, too.
Also, youtube tutorials aren't perfect, if you lecturer uses this precise code you might want to notify them.
I can't see any Provider with User type above your AuthWrapper. I think the correct code should be this:
final authService = context.watch<AuthService>();
// check if user is logged in
try giving your StreamProvider a type of User
StreamProvider<User>(create: (context) => context.read<Authservis>().authStateChanges,
initialData: null,),

Flutter/Dart variables lost / keep getting reinitialized when trying to get from different class

The variables keep getting reinitialized when accessed from another class.
I have a class, in its separate dart source file, declared as such:
helper_provider.dart
import 'package:flutter/material.dart';
class BuilderHelperProvider extends ChangeNotifier {
var _builderDetails;
void setBuilderDetails(inputDetails) {
this._builderDetails = inputDetails;
}
get builderDetails {
return _builderDetails;
}
}
and I initialize the variable in main.dart
main.dart
import 'package:builder_project/helpers/helper_provider.dart';
import 'package:cloud_firestore/cloud_firestore.dart';
import 'package:firebase_core/firebase_core.dart';
import 'package:flutter/material.dart';
import 'package:provider/provider.dart';
void main() async {
WidgetsFlutterBinding.ensureInitialized();
await Firebase.initializeApp();
runApp(MyApp());
}
class MyApp extends StatelessWidget {
#override
Widget build(BuildContext context) {
return MultiProvider(
providers: [
ChangeNotifierProvider(
create: (context) => BuilderHelperProvider(),
),
],
child: MaterialApp(
debugShowCheckedModeBanner: false,
title: 'Flutter Demo',
theme: ThemeData(
primarySwatch: Colors.blue,
visualDensity: VisualDensity.adaptivePlatformDensity,
),
home: StreamBuilder(
stream: FirebaseFirestore.instance
.collection('builders')
.doc('builder id')
.snapshots(),
builder: (context, builderSnap) {
if (builderSnap.connectionState == ConnectionState.waiting) {
return Center(child: CircularProgressIndicator());
}
final _builderDetails = builderSnap.data;
BuilderHelperProvider().setBuilderDetails(_builderDetails);
print('builder Details: ${BuilderHelperProvider().builderDetails}');
# *This keeps returning null*
return BuilderHomepage();
})
// home: BuilderHomepage(),
),
);
}
}
The output of the above code comes as:
I/flutter (31260): builder Details: null
My application did not quit or stop between the two operations. Under what circumstances would get reinitalised?
just make them static and you will be fine
static var _builderDetails;

StreamProvider with RiverPod not working (try to migrate from Provider)

I'm trying to understand RiverPod by migrating my simple FireStore auth Provider example to RiverPod.
This is my AuthenticationService:
import 'package:firebase_auth/firebase_auth.dart';
class AuthenticationService {
final FirebaseAuth _firebaseAuth;
AuthenticationService(this._firebaseAuth);
// with StreamProvider we listen to these changes
Stream<User> get authStateChanges => _firebaseAuth.authStateChanges();
Future<String> signIn({String email, String password}) async {
try {
await _firebaseAuth.signInWithEmailAndPassword(
email: email, password: password);
return 'Signed in';
} on FirebaseAuthException catch (e) {
return e.message;
}
}
Future<String> signUp({String email, String password}) async {
try {
await _firebaseAuth.createUserWithEmailAndPassword(
email: email, password: password);
return 'Signed up ';
} on FirebaseAuthException catch (e) {
return e.message;
}
}
Future<void> signOut() async {
await _firebaseAuth.signOut();
}
}
In main.dart I made 2 providers so I can use the service and listen to the property inside of the AuthenticationService
import 'package:firebase_auth/firebase_auth.dart';
import 'package:firebase_core/firebase_core.dart';
import 'package:flutter/material.dart';
import 'package:meditatie_app/authentication_service.dart';
import 'package:meditatie_app/home_page.dart';
import 'package:meditatie_app/signin_page.dart';
import 'package:provider/provider.dart';
Future<void> main() async {
// initalize Firebase and before that we need to initialize the widgets.
WidgetsFlutterBinding.ensureInitialized();
await Firebase.initializeApp();
runApp(MyApp());
}
class MyApp extends StatelessWidget {
// This widget is the root of your application.
#override
Widget build(BuildContext context) {
return MultiProvider(
providers: [
// Normal provider to serve the AuthenticationService in the widgettree
// so the login form can use this provider to use .singIn()
Provider<AuthenticationService>(
create: (_) => AuthenticationService(FirebaseAuth.instance),
),
// also a StreamProvider that serves the AuthenticationSerivce authStateChanges
// this stream is updated by the FirebaseAuth package when users signin or out
// this provider use context.read<AuthenticationService> to find the
// provider dived here above
StreamProvider(
create: (context) =>
context.read<AuthenticationService>().authStateChanges,
)
],
child: MaterialApp(
title: 'Flutter Demo',
theme: ThemeData(
primarySwatch: Colors.blue,
visualDensity: VisualDensity.adaptivePlatformDensity,
),
home: AuthenticationWrapper(),
),
);
}
}
class AuthenticationWrapper extends StatelessWidget {
#override
Widget build(BuildContext context) {
final firebaseUser = context.watch<User>();
if (firebaseUser != null) {
return HomePage();
}
return SignInPage();
}
}
Here the SingIn page:
import 'package:flutter/material.dart';
import 'package:meditatie_app/authentication_service.dart';
import 'package:provider/provider.dart';
class SignInPage extends StatelessWidget {
final TextEditingController emailController = TextEditingController();
final TextEditingController passwordController = TextEditingController();
...
RaisedButton(
onPressed: () {
// Sign in code
context.read<AuthenticationService>().signIn(
email: emailController.text.trim(),
password: passwordController.text.trim(),
);
},
...
This works fine with normal Provider, but I can't get it to work with RiverPod
What I did was:
These providers I made global in providers.dart
import 'package:firebase_auth/firebase_auth.dart';
import 'package:flutter_riverpod/all.dart';
import 'authentication_service.dart';
final authenticationSeriviceProvider =
Provider((ref) => AuthenticationService(FirebaseAuth.instance));
final authStateChangeProvider = StreamProvider.autoDispose<User>((ref) {
return ref
.watch(authenticationSeriviceProvider)
.authStateChanges;
});
Is this correct? The authStateChangeProvider is using the authenticationSeriviceProvider
When is use it like:
import 'package:firebase_core/firebase_core.dart';
import 'package:flutter/material.dart';
import 'package:meditatie_app/home_page.dart';
import 'package:meditatie_app/signin_page.dart';
import 'package:flutter_riverpod/all.dart';
import 'providers.dart';
Future<void> main() async {
// initialize Firebase and before that we need to initialize the widgets.
WidgetsFlutterBinding.ensureInitialized();
await Firebase.initializeApp();
runApp(
// riverpod needs at toplevel a Provider container
// for storing state of different providers.
ProviderScope(
child: MyApp(),
),
);
}
class MyApp extends StatelessWidget {
// This widget is the root of your application.
#override
Widget build(BuildContext context) {
return MaterialApp(
title: 'Flutter Demo',
theme: ThemeData(
primarySwatch: Colors.blue,
visualDensity: VisualDensity.adaptivePlatformDensity,
),
home: AuthenticationWrapper(),
);
}
}
// Riverpods ConsumerWidget
// which can consume a provider
// rebuild if the value of the provider changes
class AuthenticationWrapper extends ConsumerWidget {
#override
Widget build(BuildContext context, ScopedReader watch) {
final firebaseUser = watch(authStateChangeProvider);
if (firebaseUser != null) {
return HomePage();
}
return SignInPage();
}
}
My 'firebaseUser' is not a User anymore, but an AsyncValue
When I change it to:
class AuthenticationWrapper extends ConsumerWidget {
#override
Widget build(BuildContext context, ScopedReader watch) {
final User firebaseUser = watch(authStateChangeProvider).data?.value;
if (firebaseUser != null) {
return HomePage();
}
return SignInPage();
}
}
It is working, but what am I doing wrong that I now work with AsyncValue
Expanding the previous answer AsyncValue<T> is a sealed class, think of it as StreamBuilder in Flutter having AsyncSnapshot<T> which wraps the value returned from the stream and gives you options to check if its connecting, waiting, withError or withData. In your case
class AuthenticationWrapper extends ConsumerWidget {
#override
Widget build(BuildContext context, ScopedReader watch) {
return watch(authStateChangeProvider).when(
data: (user) => user == null ? SignInPage() : HomePage(),
loading: () => CircularProgressIndicator(),
error: (err, stack) => SignInPage(),
);
}
}
should handle all the options, now when loading it will show a progress indicator, if there is an error (connection, bad result, etc) it will display the SignInPage, and finally when there is a value you still will need to check if the value returned from the Stream is null (As far as I understand Firebase returns null when there is no user signed in, it doesn't mean the stream is empty) and display the right widget if its null or not.
Just like Provider, after retrieving the user you still have to do the logic with that
See the documentation.
You should use AsyncValue's exposed states to decide what to render. Your code could look something like the following:
class AuthenticationWrapper extends ConsumerWidget {
#override
Widget build(BuildContext context, ScopedReader watch) {
return watch(authStateChangeProvider).when(
data: (user) => user == null ? SignInPage() : HomePage(),
loading: () => CircularProgressIndicator(),
error: (err, stack) => SignInPage(),
);
}
}
So adjust your return logic to what you'd like for the data, loading, and error states, but this should give you a general idea on how to use AsyncValue.
Another way I found was to use it the way this tutorial did, but with the new riverpod changes:
import 'dart:async';
import 'package:firebase_auth/firebase_auth.dart';
import 'package:flutter_shopping_list/repositories/auth_repository.dart';
import 'package:hooks_riverpod/hooks_riverpod.dart';
final authControllerProvider = StateNotifierProvider<AuthController, User?>(
(ref) => AuthController(ref.read)..appStarted(),
);
class AuthController extends StateNotifier<User?> {
final Reader _read;
StreamSubscription<User?>? _authStateChangesSubscription;
AuthController(this._read) : super(null) {
_authStateChangesSubscription?.cancel();
_authStateChangesSubscription = _read(authRepositoryProvider)
.authStateChanges
.listen((user) => state = user);
}
#override
void dispose() {
_authStateChangesSubscription?.cancel();
super.dispose();
}
void appStarted() async {
final user = _read(authRepositoryProvider).getCurrentUser();
if (user == null) {
await _read(authRepositoryProvider).signInAnonymously();
}
}
}
And then I used it like this:
#override
Widget build(BuildContext context, WidgetRef ref) {
User? user = ref.watch<User?>(authControllerProvider);
return user != null
? MaterialApp(
title: 'My App',
builder: (context, child) => _Unfocus(child: child!),
home: MainNavigation(),
debugShowCheckedModeBanner: false,
)
: const MaterialApp(
title: 'My App,
home: LoginPage(),
debugShowCheckedModeBanner: false,
);
}

Class 'List<AnimeAll>' has no instance getter 'title'

I'm building an app in flutter for watching anime using provider pattern.
However, I have a hard time working with it.
The error shows up in pages/tabs/search.dart
I can't access the "title" parameter fetching from provider.
I got this error:
Code:
main.dart
import 'package:anime_go/pages/home.dart';
import 'package:easy_localization/easy_localization.dart';
import 'package:flutter/material.dart';
import 'package:provider/provider.dart';
import 'package:anime_go/providers/anime.dart';
void main() {
runApp(
EasyLocalization(
supportedLocales: [Locale('en', 'US')],
path: 'lib/assets/translations',
fallbackLocale: Locale('en', 'US'),
child: AnimeGo()),
);
}
class AnimeGo extends StatelessWidget {
#override
Widget build(BuildContext context) {
return MultiProvider(
providers: [
ChangeNotifierProvider(
create: (context) => AnimeModel()..addAnimeList(),
)
],
child: MaterialApp(
localizationsDelegates: context.localizationDelegates,
supportedLocales: context.supportedLocales,
locale: context.locale,
debugShowCheckedModeBanner: false,
theme: ThemeData(
primaryColor: Colors.blueGrey[800],
),
title: 'title'.tr(),
home: HomeScreen(),
),
);
}
}
services/anime_twist.dart
import 'package:anime_go/models/anime_all.dart';
import 'package:anime_go/.env.dart';
import 'package:http/http.dart' as http;
class AnimeTwistApiService {
static const baseUrl = 'https://twist.moe/api/anime';
Future<List<AnimeAll>> getAllAnime() async {
final http.Response response = await http
.get(baseUrl, headers: {'x-access-token': EnvironmentVariables.token});
return animeAllFromJson(response.body);
}
}
providers/anime.dart
import 'package:anime_go/models/anime_all.dart';
import 'package:anime_go/services/anime_twist.dart';
import 'package:flutter/material.dart';
class AnimeModel extends ChangeNotifier {
final AnimeTwistApiService api = AnimeTwistApiService();
final List allAnimeList = [];
void addAnimeList() async {
final List<AnimeAll> animeList = await api.getAllAnime();
allAnimeList.add(animeList);
notifyListeners();
}
}
pages/tabs/search.dart
import 'package:anime_go/providers/anime.dart';
import 'package:flutter/material.dart';
import 'package:provider/provider.dart';
class SearchTab extends StatelessWidget {
#override
Widget build(BuildContext context) {
return Consumer<AnimeModel>(
builder: (context, animeList, child) {
print(animeList.allAnimeList.toString());
return Column(
children: [
for (var anime in animeList.allAnimeList) Text(anime.title) // here is the problem - the error
],
);
},
);
}
}
Your allAnimeList is a List<List<AnimeAll>>. So when you iterate over it in your for loop, one anime is now of type List<AnimeAll>. This type does indeed not have a title property.
I don't know what you wanted. Maybe you want to iterate over this list again and then use the title property of each AnimeAll?
To be honest it's quite confusing to me, you have a list of a list of something called an ...All (that to me would signal that this is a collection type as well), it seems you got confused yourself, maybe it's time to think about a better structure and naming of your data.