Flutter multiprovider and consumer getting null in child widget - android

Im having issues trying to get the current user from a child widget. When I login I can see the response if I print from AuthProvider. However, when I try to get the currentUser from a child widget the value is null.
Login in.
Calling query from UsersProvider.
setting the currentUser variable in said provider.
go back to AuthProvider and finish login in the user.
main
#override
Widget build(BuildContext context) {
print('build Main');
return MultiProvider(
providers: [
ChangeNotifierProvider.value(
value: AuthProvider(),
),
ChangeNotifierProvider.value(
value: UsersProvider(),
),
],
child: Consumer<AuthProvider>(
builder: (ctx, auth, _) => MaterialApp(
title: 'Wayuu',
theme: ThemeData(
primarySwatch: Colors.indigo,
accentColor: Colors.purpleAccent,
// textTheme: ThemeData.dark().textTheme.copyWith(
// title: TextStyle(
// color: Theme.of(context).accentColor,
// fontWeight: FontWeight.bold,
// fontSize: 20,
// ),
// ),
),
home: auth.isAuthenticated
? HomeState()
: FutureBuilder(
future: auth.isAuth(),
builder: (ctx, authResultSnapshot) =>
authResultSnapshot.connectionState ==
ConnectionState.waiting
? Splash()
: Login()),
routes: {
Home.routeName: (context) => Home(),
Profile.routeName: (context) => Profile(),
ForgotPassword.routeName: (context) => ForgotPassword(),
RegisterScreen.routeName: (context) => RegisterScreen(),
Favorites.routeName: (context) => Favorites(),
Podcast.routeName: (context) => Podcast(),
MyAccount.routeName: (context) => MyAccount(),
},
),
),
);
}
}
AuthProvider
bool _userAuthenticated = false;
bool get isAuthenticated {
return _userAuthenticated;
}
Future<bool> isAuth() async {
final FirebaseUser response = await FirebaseAuth.instance.currentUser();
if (response != null) {
_userAuthenticated = true;
notifyListeners();
}
return _userAuthenticated;
}
Future<void> loginUser(Map<String, String> loginData) async {
UsersProvider usersProvider = UsersProvider();
try {
final response = await FirebaseAuth.instance.signInWithEmailAndPassword(
email: loginData['usernameOrEmail'], password: loginData['password']);
// _currentUserId = response.user.uid;
await usersProvider.getUserById(response.user.uid);
await storeUserIdInSharePreferences(response.user.uid);
_userAuthenticated = true;
notifyListeners();
} catch (error) {
throw HttpException(error.toString());
}
}
}
UsersProvider
class UsersProvider with ChangeNotifier {
Map<String, dynamic> _currentUser = {};
Map<String, dynamic> get currentUser {
return {..._currentUser};
}
Future<void> getUserById(String userId) async {
try {
QuerySnapshot querySnapshot = await Firestore.instance
.collection('users')
.where('id', isEqualTo: userId)
.getDocuments();
querySnapshot.documents.forEach((documentData) {
_currentUser = {
'id': documentData.data['id'],
'fullName': documentData.data['fullName'],
'email': documentData.data['email'],
'username': documentData.data['username'],
'profilePicture': documentData.data['profilePicture'],
'bio': documentData.data['bio'],
'instagram': documentData.data['instagram'],
'facebook': documentData.data['facebook'],
'web': documentData.data['web']
};
});
notifyListeners();
} catch (error) {
print('error from query');
print(error);
throw HttpException(error.toString());
}
}
}
Profile Widget
Widget build(BuildContext context) {
print('build profile widget');
final currentUser = Provider.of<UsersProvider>(context).currentUser;
print(currentUser);
I get null from here when my "Profile" widget executes
final currentUser = Provider.of<UsersProvider>(context).currentUser;
Thank you in advance !

The instance of UsersProvider that you create inside of loginUser exists only in the scope of that method. Refactor the method to return the response and then use it to update the provider.
var response = await authProvider.loginUser(loginData);
await Provider.of<UsersProvider>(context).getUserById(response.user.uid);
Then when you call the current user it'll be the same instance that will be updated.

Related

Should I pass the google sign-in variables to other class?

I made a log-in page and made a button for google log-in like this :
Future<String> login_google() async{
FirebaseAuth auth = FirebaseAuth.instance;
GoogleSignIn googleSignIn = GoogleSignIn();
GoogleSignInAccount? account = await googleSignIn.signIn();
GoogleSignInAuthentication authentication = await account!.authentication;
AuthCredential credential = GoogleAuthProvider.credential(
idToken: authentication.idToken,
accessToken: authentication.accessToken);
final authResult = await auth.signInWithCredential(credential);
final user = authResult.user;
print (user?.uid);
print (user?.email);
print('google log-in completed');
return Future.value(user?.uid);
}
...
class _login_pageState extends State<login_page> {
#override
Widget build(BuildContext context) {
// String uid_r = await login_google();
return Scaffold(
appBar: AppBar(
backgroundColor: Colors.white,
toolbarHeight: 1.0,
),
body:Column(
mainAxisAlignment: MainAxisAlignment.center,
crossAxisAlignment: CrossAxisAlignment.center,
children: [
Image( image: AssetImage('asset/logo.png'),),
ElevatedButton(onPressed: (){
login_kakao();
Navigator.of(context).push(MaterialPageRoute(builder: (context) =>main_bone(uid:'')));
}, child: Text('Kakao Log-In')),
ElevatedButton(onPressed: (){
// login_kakao();
final uid = login_google().then(
(value){
Navigator.of(context).push(MaterialPageRoute(builder: (context) =>main_bone(uid: value))); => The button for LOG-IN .
}
);
}, child: Text('Google Log-In'))
],
),
);
}
}
Here I succeeded in log-in and passed only uid thinking that only uid is required. And My firestore rules for accessing is following :
match /post/{document=**}
{
allow read;
allow write:if
auth.uid != null;
}
On the next page, None of the data in collection(post) were shown, which means the log in has disabled on the next class .
Should I pass the whole variables for authentication to next class, for example, auth, credential ?
its called inherited widget, there is a lot method to make your entire app authenticated, not just one class. make a stream builder, listen firebase_auth.authStateChanges()
or you can use a package ( state management ) :
like
flutter_bloc
or
i prefer goRouter
check this video, its chris itself explain how to use it, its even better; work at flutter web too
take look at mine for your reference :
import 'dart:ui';
import 'package:cms_prototype/auth/auth.dart';
import 'package:cms_prototype/home/home.dart';
import 'package:cms_prototype/login/login.dart';
import 'package:cms_prototype/theme.dart';
import 'package:firebase_core/firebase_core.dart';
import 'package:flutter/material.dart';
import 'package:go_router/go_router.dart';
Future<void> main() async {
WidgetsFlutterBinding.ensureInitialized();
await Firebase.initializeApp(
options: const FirebaseOptions(
apiKey: "********",
authDomain: "",
appId: "",
measurementId: "",
storageBucket: "**.com",
messagingSenderId: "**",
projectId: "**"));
final auth = AuthRepo();
await auth.user.first;
final router = GoRouter(
urlPathStrategy: UrlPathStrategy.path,
redirect: (GoRouterState state){
if(auth.currentUser.isEmpty && state.location != '/login'){
return '/login';
}
if(auth.currentUser.isNotEmpty && state.location == '/login'){
return '/';
}
return null;
},
refreshListenable: GoRouterRefreshStream(auth.user),
routes: [
GoRoute(
name: 'home',
path: '/',
pageBuilder: (context, state) => MaterialPage(
key: state.pageKey,
child: const HomePage())),
GoRoute(
name: 'login',
path: '/login',
pageBuilder: (context, state) => MaterialPage(
key: state.pageKey,
child: LoginPage(authRepo: auth,))),
],
errorPageBuilder: (context, state) => const MaterialPage(
child: Scaffold(
body: Center(
child: Text("404 Error"),
),
)));
runApp(MyApp(router: router,authRepo: auth,));
}
class MyApp extends StatelessWidget {
final GoRouter router;
final AuthRepo authRepo;
const MyApp({Key? key, required this.router, required this.authRepo}) : super(key: key);
// This widget is the root of your application.
#override
Widget build(BuildContext context) {
return MaterialApp.router(
routeInformationProvider: router.routeInformationProvider,
routeInformationParser: router.routeInformationParser,
routerDelegate: router.routerDelegate,
debugShowCheckedModeBanner: false,
scrollBehavior: const MaterialScrollBehavior().copyWith(
dragDevices: {
PointerDeviceKind.mouse,
PointerDeviceKind.touch,
PointerDeviceKind.stylus,
PointerDeviceKind.unknown
},
),
title: 'CMS Prototype',
theme: appTheme,
);
}
}
auth repo class : ---->
class AuthRepo {
final AuthCache _cache;
final firebase_auth.FirebaseAuth _firebaseAuth;
final GoogleSignIn _googleSignIn;
AuthRepo({AuthCache? cache, firebase_auth.FirebaseAuth? firebaseAuth, GoogleSignIn? googleSignIn})
: _cache = cache?? AuthCache(),
_firebaseAuth = firebaseAuth ?? firebase_auth.FirebaseAuth.instance,
_googleSignIn = googleSignIn ?? GoogleSignIn.standard();
Stream<User> get user {
return _firebaseAuth.authStateChanges().map((fUser){
final user = fUser == null ? User.empty : fUser.toUser;
_cache.write(key: user.id, value: user);
return user;
});
}
User get currentUser {
final key = _firebaseAuth.currentUser != null? _firebaseAuth.currentUser!.uid : '';
return _cache.read(key: key) ?? User.empty;
}
Future<void> signUp({required String email, required String password}) async {
try {
await _firebaseAuth.createUserWithEmailAndPassword(
email: email,
password: password,
);
} on firebase_auth.FirebaseAuthException catch (e) {
throw SignUpWithEmailAndPasswordFailure.fromCode(e.code);
} catch (_) {
throw const SignUpWithEmailAndPasswordFailure();
}
}
//login with google (GOOGLE SIGN IN)
Future<void> loginWithGoogle() async {
try {
late final firebase_auth.AuthCredential credential;
if(kIsWeb){
final googleProvider = firebase_auth.GoogleAuthProvider();
final userCredential = await _firebaseAuth.signInWithPopup(googleProvider);
credential = userCredential.credential!;
}else{
final googleUser = await _googleSignIn.signIn();
final googleAuth = await googleUser!.authentication;
credential = firebase_auth.GoogleAuthProvider.credential(
accessToken: googleAuth.accessToken,
idToken: googleAuth.idToken
);
}
await _firebaseAuth.signInWithCredential(credential);
}
on firebase_auth.FirebaseAuthException catch (e){
throw LogInWithGoogleFailure.fromCode(e.code);
}
catch (e){
throw const LogInWithGoogleFailure();
}
}
//email dan password
Future<LogInWithEmailAndPasswordFailure?> loginWithEmailAndPassword ({required String email, required String password}) async{
try{
await _firebaseAuth.signInWithEmailAndPassword(
email: email,
password: password
);
}
on firebase_auth.FirebaseAuthException catch (e){
//return e.code;
return LogInWithEmailAndPasswordFailure.fromCode(e.code);
}
catch (_){
//return 'masalah jaringan terdeteksi';
return const LogInWithEmailAndPasswordFailure();
}
return null;
}
Future<void> logOut() async {
try {
await Future.wait([
_firebaseAuth.signOut(),
_googleSignIn.signOut(),
]);
} catch (_) {
throw LogOutFailure();
}
}
}
You should be able to access all the data for the current user using FirebaseAuth.instance.currentUser. Use that to access the current user and pass any data to backend using that user's credentials and token.
final user = FirebaseAuth.instance.currentUser;

Nullable and non-nullable values

I am using flutter and firebase for user authentication.
What I am having trouble with is the following segment of code:
import 'package:flutter/material.dart';
class AuthClass {
final FirebaseAuth auth = FirebaseAuth.instance;
//Signing in a user
Future<String> signIn({String email, String password}) async {
try {
await auth.signInWithEmailAndPassword(
email: email,
password: password
);
return "Welcome!";
} on FirebaseAuthException catch (e) {
if (e.code == 'user-not-found') {
return 'No user found for that email.';
} else if (e.code == 'wrong-password') {
return 'Wrong password provided for that user.';
}
}
}
}
The context from which I am calling the signIn function is right here:
import 'package:flutter_client_app/Provider/auth_provider.dart';
import 'package:flutter_client_app/Screens/home.dart';
class LoginPage extends StatefulWidget {
#override
_LoginPageState createState() => _LoginPageState();
}
class _LoginPageState extends State<LoginPage> {
TextEditingController _email = TextEditingController();
TextEditingController _password = TextEditingController();
bool isLoading = false;
#override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(title: Text("Login"),),
body: isLoading == false ? Padding(
padding: const EdgeInsets.all(8.0),
child: Column(
children: [
TextFormField(
controller: _email,
decoration: InputDecoration(
hintText: "Email"
)
),
const SizedBox(height: 30,),
TextFormField(
controller: _password,
decoration: InputDecoration(
hintText: "Password"
)
),
FlatButton( <=============================================================
color: Colors.blue,
onPressed: () {
print (_email.text);
print (_password.text);
setState(() {
isLoading = true;
});
AuthClass()
.signIn(
email: _email.text.trim(),
password: _password.text.trim())
.then((value) {
if (value == "Welcome!") {
setState(() {
isLoading = false;
});
Navigator.pushAndRemoveUntil(
context,
MaterialPageRoute(builder: (context) => HomePage()),
(route) => false);
} else {
setState(() {
isLoading = false;
});
ScaffoldMessenger.of(context)
.showSnackBar(SnackBar(content: Text(value)));
}
}); <==============================================================
},
child: Text("Login to account")),
SizedBox(
height: 20,
),
// GestureDetector(
// onTap: () {
// Navigator.pushAndRemoveUntil(
// context,
// MaterialPageRoute(
// builder: (context) => RegisterPage()), (route) => false);
// },
// child: Text("Don't have an account? Register"),
// ),
//
// const SizedBox(
// height: 10,
// ),
// GestureDetector(
// onTap: () {
// Navigator.pushAndRemoveUntil(
// context, MaterialPageRoute(builder: (context) => ResetPage()), (route) => false);
// },
// child: Text("Forgot Password? reset"),
//),
],
),
) : Center(
child: CircularProgressIndicator(),
)
);
}
}
The errors that I am getting are as follows:
29:33: Error: The parameter 'email' can't have a value of 'null' because of its type 'String', but the implicit default value is 'null'.
29:47: Error: The parameter 'password' can't have a value of 'null' because of its type 'String', but the implicit default value is 'null'.
29:18: Error: A non-null value must be returned since the return type 'String' doesn't allow null.
I've been successful in printing out the values that are being recorded in the TextFormField, but my problems spawn when the signIn async function is called.
You are using null-safety feature. You can disable it by change dart sdk in pubspec.yaml to environment: sdk: ">=2.7.0 <3.0.0" instead of environment: sdk: ">=2.12.0 <3.0.0" or make a your class null-safety. Also you should return value for else of if in catch part.
import 'package:flutter/material.dart';
class AuthClass {
final FirebaseAuth auth = FirebaseAuth.instance;
//Signing in a user with nullable parameters
Future<String> signIn({String? email, String? password}) async {
try {
await auth.signInWithEmailAndPassword(
email: email as String,
password: password as String,
);
return "Welcome!";
} on FirebaseAuthException catch (e) {
if (e.code == 'user-not-found') {
return 'No user found for that email.';
} else if (e.code == 'wrong-password') {
return 'Wrong password provided for that user.';
}
else return "No Clear Error"
}
}
//Signing in a user with default value parameters
Future<String> signIn({String email = "", String password = ""}) async {
try {
await auth.signInWithEmailAndPassword(
email: email,
password: password,
);
return "Welcome!";
} on FirebaseAuthException catch (e) {
if (e.code == 'user-not-found') {
return 'No user found for that email.';
} else if (e.code == 'wrong-password') {
return 'Wrong password provided for that user.';
}
else return "No Clear Error"
}
}
}

Sign In widget is rebuilt everytime sign in button is clicked

AuthProvide Class*
class AuthProvider extends ChangeNotifier {
final FirebaseAuth _auth = FirebaseAuth.instance;
bool get isSignedIn => _auth.currentUser != null;
User get currentUser => _auth.currentUser!;
String message = '';
Future<String> signIn(
{required String email, required String password}) async {
try {
await _auth.signInWithEmailAndPassword(email: email, password: password);
notifyListeners();
message = 'Successfully signed in';
return message;
} on FirebaseAuthException catch (e) {
message = getMessageFromErrorCode(e.code);
return message;
}
}
}
I call the isSignedIn getter in main using a consumer and return login screen or home screen based on the bool value.
SignIn Widget
SignInBar(
label: 'Sign in',
color: const Color(0xff092E34),
onPressed: () async {
FocusScope.of(context).unfocus();
if (_formKey.currentState!.validate()) {
_formKey.currentState!.save();
String message = await context
.read<AuthProvider>()
.signIn(email: _email, password: _password);
SnackBar snackBar = SnackBar(content: Text(message));
ScaffoldMessenger.of(context).showSnackBar(snackBar);
}
},
),
When SignIn onPressed is called my widget gets rebuilt and the form is cleared. how to stop it from rebuilding?
Ok so I don't know the specific reason for the above question but I tried with a different approach for auth provider and truly it has solved my problem.
I am posting this in the answer because I want to share this auth provider template with everyone since I faced lot problem finding one with null safety.
Auth Provider
class AuthProvider with ChangeNotifier {
bool _isLoading = false;
bool get isLoading => _isLoading;
FirebaseAuth firebaseAuth = FirebaseAuth.instance;
Future<String?> register(String email, String password) async {
setLoading(true);
try {
UserCredential authResult = await firebaseAuth
.createUserWithEmailAndPassword(email: email, password: password);
User? user = authResult.user;
setLoading(false);
notifyListeners();
return user != null ? null : 'An error Occurred';
} on SocketException {
setLoading(false);
notifyListeners();
return "No internet, please connect to internet";
} on FirebaseAuthException catch (e) {
setLoading(false);
notifyListeners();
return e.message;
}
}
Future<String?> login(String email, String password) async {
setLoading(true);
try {
UserCredential authResult = await firebaseAuth.signInWithEmailAndPassword(
email: email, password: password);
User? user = authResult.user;
setLoading(false);
notifyListeners();
return user != null ? null : 'An error Occurred';
} on SocketException {
setLoading(false);
notifyListeners();
return "No internet, please connect to internet";
} on FirebaseAuthException catch (e) {
setLoading(false);
notifyListeners();
return e.message;
}
}
Future logout() async {
await firebaseAuth.signOut();
}
void setLoading(val) {
_isLoading = val;
notifyListeners();
}
Stream<User?> get user =>
firebaseAuth.authStateChanges().map((event) => event);
}
Main.dart
MultiProvider(
providers: [
ChangeNotifierProvider<AuthProvider>.value(value: AuthProvider()),
StreamProvider<User?>.value(
value: AuthProvider().user,
initialData: null,
)
],
child: MaterialApp(
title: 'Revora',
debugShowCheckedModeBanner: false,
theme: ThemeData(
visualDensity: VisualDensity.adaptivePlatformDensity,
primarySwatch: Colors.teal,
accentColor: Colors.amber,
textTheme: AppTheme.textTheme,
appBarTheme: AppBarTheme(
backgroundColor: Colors.white,
elevation: 0.0,
),
platform: TargetPlatform.android,
),
home: Wrapper(),
),
);
If this is helpful pls upvote the answer so that everyone can gain from it.

This happens because you used a `BuildContext` that does not include the provider of your choice

I am Working on Flutter App Both for web and mobile and stuck at the Following Error:
======== Exception caught by widgets library =======================================================
The following ProviderNotFoundException was thrown building Products(dirty):
Error: Could not find the correct Provider<List<ProductsModel>> above this Products 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 Products is under your MultiProvider/Provider<List<ProductsModel>>.
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>()),
),
}
```
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) {
// No longer throws
return Text(context.watch<Example>()),
}
),
}
```
The relevant error-causing widget was:
Products file:///E:/Flutter%20Projects/flutter_web_firebase_host/lib/screens/home/home.dart:37:63
When the exception was thrown, this was the stack:
C:/b/s/w/ir/cache/builder/src/out/host_debug/dart-sdk/lib/_internal/js_dev_runtime/private/ddc_runtime/errors.dart 236:49 throw_
packages/provider/src/provider.dart 332:7 _inheritedElementOf
packages/provider/src/provider.dart 284:30 of
packages/flutter_web_firebase_host/screens/databaseScreens/products.dart 10:31 build
packages/flutter/src/widgets/framework.dart 4569:28 build
...
====================================================================================================
Main.dart
import 'package:firebase_core/firebase_core.dart';
import 'package:flutter/material.dart';
import 'package:flutter_web_firebase_host/model/users.dart';
import 'package:flutter_web_firebase_host/provider/product_provider.dart';
import 'package:flutter_web_firebase_host/screens/wrapper.dart';
import 'package:flutter_web_firebase_host/services/auth.dart';
import 'package:flutter_web_firebase_host/services/firestore_service.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) {
final firestoreServise = FirestoreService();
return MultiProvider(
providers: [
ChangeNotifierProvider(
create: (context) => ProductProvider(),
),
StreamProvider(
create: (context) => firestoreServise.getProducts(),
initialData: [],
),
StreamProvider<Users>.value(
value: AuthService().user,
initialData: null,
),
],
/* child: StreamProvider<Users>.value(
value: AuthService().user,
initialData: null*/
child: MaterialApp(
debugShowCheckedModeBanner: false,
home: Wrapper(),
),
);
// );
}
Product.dart
import 'package:flutter/cupertino.dart';
import 'package:flutter/material.dart';
import 'package:flutter_web_firebase_host/model/ProductModel.dart';
import 'package:flutter_web_firebase_host/screens/databaseScreens/edit_product.dart';
import 'package:provider/provider.dart';
class Products extends StatelessWidget {
#override
Widget build(BuildContext context) {
final products = Provider.of<List<ProductsModel>>(context, listen: false);
return Scaffold(
appBar: AppBar(
title: Text('Products'),
actions: <Widget>[
IconButton(
icon: Icon(Icons.add),
onPressed: () {
Navigator.of(context).push(
MaterialPageRoute(builder: (context) => EditProduct()));
}),
],
),
body: (products != null)
? ListView.builder(
itemCount: products.length,
itemBuilder: (context, index) {
return ListTile(
title: Text(products[index].name),
trailing: Text(products[index].price.toString()),
onTap: () {
Navigator.of(context).push(MaterialPageRoute(
builder: (context) => EditProduct(products[index])));
},
);
},
)
: Center(child: CircularProgressIndicator()));
}
}
Home.dart
import 'package:flutter/material.dart';
import 'package:flutter_web_firebase_host/screens/databaseScreens/products.dart';
import 'package:flutter_web_firebase_host/services/auth.dart';
import 'package:flutter_web_firebase_host/shared/drawer.dart';
class Home extends StatefulWidget {
#override
_HomeState createState() => _HomeState();
}
class _HomeState extends State<Home> {
final AuthService _auth = AuthService();
#override
Widget build(BuildContext context) {
return Container(
child: Scaffold(
backgroundColor: Colors.brown[100],
appBar: AppBar(
title: Text('Brew Crew'),
backgroundColor: Colors.brown[100],
elevation: 0.0,
actions: <Widget>[
FlatButton.icon(
icon: Icon(Icons.person),
label: Text('logout'),
onPressed: () async {
await _auth.signOut();
},
),
IconButton(
icon: Icon(Icons.add, color: Colors.black),
onPressed: () {
Navigator.of(context).push(
MaterialPageRoute(builder: (context) => Products()));
}),
],
),
body: SingleChildScrollView(
child: Center(
child: Column(
children: <Widget>[
Container(
child: Padding(
padding: const EdgeInsets.fromLTRB(0.0, 50, 0, 0),
child: Container(
child: Text(
'Stock Market',
style: TextStyle(fontSize: 20, fontWeight: FontWeight.bold),
)),
),
),
Container(
child: Padding(
padding: const EdgeInsets.fromLTRB(20, 0, 0, 0),
child: Image.asset(
"assets/graph.jpg",
width: 500,
height: 600,
),
),
),
],
),
),
),
drawer: MyDrawer(),
),
);
}
}
product_provider.dart
import 'package:flutter/cupertino.dart';
import 'package:flutter_web_firebase_host/model/ProductModel.dart';
import 'package:flutter_web_firebase_host/services/firestore_service.dart';
import 'package:uuid/uuid.dart';
class ProductProvider with ChangeNotifier {
final firestoreService = FirestoreService();
String _name;
double _price;
String _productId;
var uuid = Uuid();
//Geters
String get name => _name;
double get price => _price;
//Seters
changeName(String value) {
_name = value;
notifyListeners();
}
changePrice(String value) {
_price = double.parse(value);
notifyListeners();
}
loadValues(ProductsModel product) {
_name=product.name;
_price=product.price;
_productId=product.productId;
}
saveProduct() {
print(_productId);
if (_productId == null) {
var newProduct = ProductsModel(name: name, price: price, productId: uuid.v4());
firestoreService.saveProduct(newProduct);
} else {
//Update
var updatedProduct =
ProductsModel(name: name, price: _price, productId: _productId);
firestoreService.saveProduct(updatedProduct);
}
}
}
Authservise.dart
import 'package:firebase_auth/firebase_auth.dart';
import 'package:flutter_web_firebase_host/model/users.dart';
import 'package:google_sign_in/google_sign_in.dart';
class AuthService {
final FirebaseAuth _auth = FirebaseAuth.instance;
// create user obj based on firebase user
Users _userFromFirebaseUser(User user) {
return user != null ? Users(uid: user.uid) : null;
}
// auth change user stream
Stream<Users> get user {
return _auth.authStateChanges().map(_userFromFirebaseUser);
}
// sign in anon
Future signInAnon() async {
try {
UserCredential result = await _auth.signInAnonymously();
User user = result.user;
return _userFromFirebaseUser(user);
} catch (e) {
print(e.toString());
return null;
}
}
// sign in with email and password
Future signInWithEmailAndPassword(String email, String password) async {
try {
UserCredential result = await _auth.signInWithEmailAndPassword(email: email, password: password);
User user = result.user;
return user;
} catch (error) {
print(error.toString());
return null;
}
}
// register with email and password
Future registerWithEmailAndPassword(String email, String password) async {
try {
UserCredential result = await _auth.createUserWithEmailAndPassword(email: email, password: password);
User user = result.user;
return _userFromFirebaseUser(user);
} catch (error) {
print(error.toString());
return null;
}
}
// sign out
Future signOut() async {
try {
return await _auth.signOut();
} catch (error) {
print(error.toString());
return null;
}
}
//sign in with google
Future<bool> loginWithGoogle() async {
try {
GoogleSignIn googleSignIn = GoogleSignIn();
GoogleSignInAccount account = await googleSignIn.signIn();
if(account == null )
return false;
UserCredential res = await _auth.signInWithCredential(GoogleAuthProvider.credential(
idToken: (await account.authentication).idToken,
accessToken: (await account.authentication).accessToken,
));
if(res.user == null)
return false;
return true;
} catch (e) {
print(e.message);
print("Error logging with google");
return false;
}
}
}
Basically my app is connect to firebase both for web app and android app. Also i send data to firestore from my app but when i click the add button to go to textfield to send data it give the error as i mention it in start. I am using multiprovider as you can see my main.dart code
Is There anything I missing. I need Help.
the way to fix this is to put MultiProvider as parent of myApp in your main like this
runApp(
MultiProvider(
providers: [
ChangeNotifierProvider(
create: (context) => ProductProvider(),
),
StreamProvider(
create: (context) => firestoreServise.getProducts(),
initialData: [],
),
StreamProvider<Users>.value(
value: AuthService().user,
initialData: null,
),
],
child:MyApp(
));

How to implement Flutter Localization with Provider?

I wanted to make my application to work in 2 different language.
I guess problem is that I cannot handle Cosumer in multiprovider ,could it be?
I am using flutter_localization package alongside with flutter provider package;
Here is my code:
void main() async {
WidgetsFlutterBinding.ensureInitialized();
AppLanguage appLanguage = AppLanguage();
await appLanguage.fetchLocale();
SharedPreferences.getInstance().then((prefs) {
var darkModeOn = prefs.getBool('darkMode') ?? true;
runApp(
ChangeNotifierProvider<ThemeManager>(
builder: (_) => ThemeManager(lightTheme),
child: MyApp(appLanguage: appLanguage),
),
);
});
}
Class My App
class MyApp extends StatelessWidget {
final AppLanguage appLanguage;
MyApp({this.appLanguage});
#override
Widget build(BuildContext context) {
final themeNotifier = Provider.of<ThemeManager>(context);
return MultiProvider(
providers: [
ChangeNotifierProvider.value(value: ApiService()),
ChangeNotifierProxyProvider<ApiService, StudentData>(
builder: (ctx, auth, _) => StudentData(auth.token, auth.school),
)
],
child: Consumer<ApiService>(
builder: (ctx, auth, _) => ChangeNotifierProvider<AppLanguage>(
builder: (_) => appLanguage,
child: Consumer<AppLanguage>(
builder: (context, model, child) => MaterialApp(
locale: appLanguage.appLocal,
supportedLocales: [Locale('ru', 'RU'), Locale('uz', 'UZ')],
localizationsDelegates: [
AppLocalization.delegate,
GlobalMaterialLocalizations.delegate,
GlobalWidgetsLocalizations.delegate,
],
debugShowCheckedModeBanner: false,
title: "Socratus |Mobile",
theme: themeNotifier.getTheme(),
home: auth.isAuth
? MainScreen()
: FutureBuilder(
future: auth.tryAutoLogin(),
builder: (ctx, authResultSnapshot) => LoginScreen()),
routes: {
MainScreen.routeName: (ctx) => MainScreen(),
ProfilePage.routeName: (ctx) => ProfilePage(),
SettingsPage.routeName: (ctx) => SettingsPage(
appLanguage: appLanguage,
),
ChangePassword.routeName: (ctx) => ChangePassword(),
HomeworkScreen.routeName: (ctx) => HomeworkScreen(),
HWDetails.routeName: (ctx) => HWDetails(),
NewsPage.routeName: (ctx) => NewsPage(),
QuestionAndAnswers.routeName: (ctx) => QuestionAndAnswers(),
MyDownloads.routeName: (ctx) => MyDownloads(),
},
),
),
),
),
);
}
}
Here how I tried to implement
class AppLocalization {
final Locale locale;
AppLocalization(this.locale);
static AppLocalization of(BuildContext context) {
return Localizations.of<AppLocalization>(context, AppLocalization);
}
static const LocalizationsDelegate<AppLocalization> delegate =
_AppLocalizationDelegate();
Map<String, String> _localizedStrings;
Future<bool> load() async {
String jsonString = await rootBundle
.loadString('assets/translations/${locale.languageCode}.json');
Map<String, dynamic> jsonMap = json.decode(jsonString);
_localizedStrings = jsonMap.map((key, value) {
return MapEntry(key, value.toString());
});
return true;
}
String translate(String key) {
return _localizedStrings[key];
}
}
class _AppLocalizationDelegate extends LocalizationsDelegate<AppLocalization> {
// This delegate instance will never change (it doesn't even have fields!)
// It can provide a constant constructor.
const _AppLocalizationDelegate();
#override
bool isSupported(Locale locale) {
// Include all of your supported language codes here
return ['ru', 'uz'].contains(locale.languageCode);
}
#override
Future<AppLocalization> load(Locale locale) async {
AppLocalization localizations = new AppLocalization(locale);
await localizations.load();
return localizations;
}
#override
bool shouldReload(_AppLocalizationDelegate old) => false;
}
And my provider :
class AppLanguage extends ChangeNotifier {
Locale _appLocale = Locale('ru');
Locale get appLocal => _appLocale ?? Locale("ru");
fetchLocale() async {
var prefs = await SharedPreferences.getInstance();
if (prefs.getString('language_code') == null) {
_appLocale = Locale('ru');
return Null;
}
_appLocale = Locale(prefs.getString('language_code'));
return Null;
}
void changeLanguage(Locale type) async {
var prefs = await SharedPreferences.getInstance();
if (_appLocale == type) {
return;
}
if (type == Locale("uz")) {
_appLocale = Locale("uz");
await prefs.setString('language_code', 'uz');
await prefs.setString('countryCode', 'UZ');
} else {
_appLocale = Locale("ru");
await prefs.setString('language_code', 'ru');
await prefs.setString('countryCode', 'RU');
}
notifyListeners();
}
}
If this is not a correct way , how can I implement this feature ?
The way you are trying to implement is explained in this article, if you'd like a different approach I'd suggest you to check out Easy Localization from pub.dev which is currently handling well all the Flutter internationalization features.
MY SOLUTION
Note:
Used get_storage for persistent key/value storage.
The above question has been asked in reference to this tutorial
main.dart
void main() async {
...
///Initialize Storage
await GetStorage.init();
...
runApp(MyApp());
}
class MyApp extends StatelessWidget {
#override
Widget build(BuildContext context) {
...
///Check for locale
AppLanguage language = AppLanguage();
language.fetchLocale();
final providers = [
/**
* Add all providers in here like below
* ChangeNotifierProvider<ThemeModel>(create: (context) => ThemeModel(),),
*/
...
///Language provider
ChangeNotifierProvider<AppLanguage>(
create: (context) => language,
),
...
];
return MultiProvider(
providers: providers,
child: Consumer<AppLanguage>(
builder: (context, model, _) {
/**
* Access other providers apart from language like below
* Provider.of<ThemeModel>(context).theme,
*/
...
return MaterialApp(
locale: model.appLocal,
supportedLocales: [
Locale('ru', 'RU'),
Locale('uz', 'UZ'),
],
localizationsDelegates: [
AppLocalization.delegate,
GlobalMaterialLocalizations.delegate,
GlobalWidgetsLocalizations.delegate,
],
...
);
},
),
);
}
}
Here is the provider language.dart
class AppLanguage extends ChangeNotifier {
Locale _appLocale = Locale('ru');
Locale get appLocal => _appLocale;
fetchLocale() async {
var storage = GetStorage();
if (!(storage.hasData('language_code'))) {
_appLocale = Locale('ru');
return Null;
}
_appLocale = Locale(storage.read('language_code'));
return Null;
}
Future<void> updateLanguage(Locale type) async {
var storage = GetStorage();
if (_appLocale == type) {
return;
}
if (type == Locale('uz')) {
_appLocale = Locale('sw');
await storage.write('language_code', 'uz');
await storage.write('countryCode', 'UZ');
} else {
_appLocale = Locale('ru');
await storage.write('language_code', 'ru');
await storage.write('countryCode', 'RU');
}
notifyListeners();
}
}
The AppLocalization class on the above question has been implemented correctly.
USAGE
Widget build(BuildContext context) {
...
final languageModel = Provider.of<LanguageModel>(context);
...
return Scaffold(
body: ListView(
children: [
GestureDetector(
...
onTap: () {
languageModel.appLocal == Locale('ru')
? languageModel.updateLanguage(Locale('uz'))
: languageModel.updateLanguage(Locale('ru'));
},
),
],
),
);
}
Thoughts
Language provider should be consumed first and the rest to follow i.e ApiService.. e.t.c.
Any misconception on my code and bugs, don't hesitate to comment. I will reply as soon as possible.

Categories

Resources