Auth data resets whenever App is rebuild in Flutter - flutter

I have an Auth method setup that extends ChangeNotifier to give login and store tokens and other details.
and in my main.dart file I am calling a multi provider to get all providers, inside multi provider I have a Consumer as a child which decides which page to call as default. i.e if auth.isAuth returns true then MainPage.dart will be called or AuthScreen.dart.
Login works fine and everything else works fine.
But the issue which I am facing is, Whenever is reload or rebuild my application the auth data becomes null.
Any kind of help will be appreciated. Thanks in Advance.
main.dart
#override
Widget build(BuildContext context) {
return MultiProvider(
providers: [
ChangeNotifierProvider.value(
value: Auth(),
),
ChangeNotifierProvider.value(
value: Cart(),
),
ChangeNotifierProvider.value(
value: AddressBook(Auth().token),
),
ChangeNotifierProvider.value(
value: Orders(),
),
],
child: Consumer<Auth>(
builder: (ctx, auth, _) => MaterialApp(
title: 'APP NAME',
theme: ThemeData(
fontFamily: 'Roboto',
primaryColor: Colors.white,
primaryColorDark: Colors.white,
backgroundColor: Colors.white),
home: auth.isAuth ? MainPage() : AuthScreen(),
routes: {
MainPage.routeName: (ctx) => MainPage(),
CartScreen.routeName: (ctx) => CartScreen(),
OrderScreen.routeName: (ctx) => OrderScreen(),
EditAddressScreen.routeName: (ctx) => EditAddressScreen(),
}),
),
);
}
auth.dart
class Auth extends ChangeNotifier {
String _token;
String _refresh_token;
DateTime _expiryDate;
int _userId;
bool get isAuth {
return _token != null;
}
String get token {
if (_expiryDate != null &&
_expiryDate.isAfter(DateTime.now()) &&
_token != null) {
return _token;
}
return null;
}
int get userId {
return _userId;
}
Future<void> _authenticate(String email, String password, String urlSegment) async {
final url = 'http://192.168.1.120:8080/oauth/token';
Map<String, dynamic> body = {'grant_type': 'password', 'username': email, 'password': password};
var parts = [];
parts.add('${Uri.encodeQueryComponent("grant_type")}=${Uri.encodeQueryComponent("password")}');
parts.add('${Uri.encodeQueryComponent("username")}=${Uri.encodeQueryComponent(email)}');
parts.add('${Uri.encodeQueryComponent("password")}=${Uri.encodeQueryComponent(password)}');
var formData = parts.join('&');
try {
final response = await http.post(
url,
body: formData,
headers: {
'content-type': 'application/x-www-form-urlencoded',
'Authorization': 'Basic XXXXXXXXXXXXXXXXXXXXX',
}
);
final responseData = json.decode(response.body);
print(response.statusCode);
if(response.statusCode != 200){
throw HttpException(responseData['error']['error_description']);
}else{
_token = responseData['access_token'];
_userId = responseData['customerId'];
_refresh_token = responseData['refresh_token'];
_expiryDate = DateTime.now().add(
Duration(
seconds: responseData['expires_in'],
),
);
notifyListeners();
}
if (responseData['error'] != null) {
throw HttpException(responseData['error']['error_description']);
}
} catch (error) {
throw error;
}
}
Future<void> signup(String email, String password) async {
return _authenticate(email, password, 'signUp');
}
Future<void> login(String email, String password) async {
return _authenticate(email, password, 'signInWithPassword');
}
}

At the moment it looks like you are storing OAuth tokens in memory, so they are dropped upon every restart, and this provides poor usability.
MOBILE TOKEN STORAGE
OAuth tokens can be saved to secure encrypted storage to improve usability, perhaps using this Flutter library. The encryption used should be private to your app, so that no other app can use the tokens.
STRUCTURING YOUR CODE
I quite like how you've structured your code, where the rest of the app just calls Auth.dart's getToken method when calling a Web API. It looks like your Auth.dart source file needs to do more work to resolve your problem.
EXAMPLE OF MINE
This code is in Swift and uses a different flow, with AppAuth libraries. However, the general design pattern can be applied in any language, including Flutter.
Token Storage Module
OAuth Interface used by the rest of the App
OAuth Implementation uses Token Storage
API Client that calls GetToken
View that Calls an API

Related

Flutter unable to login after create new account

After building the app, if I try to login it's working fine, but if I create a new account then try to login again, login is not working. I have seen after debugging that I'm getting token in _authenticate method. I think notifyListeners not working here but I don't know why?
I'm using the provider package in my flutter app. I have an Auth provider class where I'm saving data in firebase and also login by firebase. Below it's my provider class.
class Auth with ChangeNotifier {
String? _token;
late DateTime _expiryDate = DateTime.now();
late String _userId;
bool get isAuth {
return token != null;
}
String? get token {
if ((_expiryDate).isAfter(DateTime.now())) {
return _token;
}
return null;
}
Future _authenticate(String email, String password, String urlSegment) async {
var urlParse = Uri.parse(urlSegment);
try {
final response = await http.post(urlParse,
body: jsonEncode({
'email': email,
'password': password,
'returnSecureToken': true
})
);
final responseData = jsonDecode(response.body);
if (responseData['error'] != null) {
throw HttpException(responseData['error']['message']);
}
// set token and user id from firebase response
_token = responseData['idToken'];
_userId = responseData['localId'];
_expiryDate = DateTime.now()
.add(Duration(seconds: int.parse(responseData['expiresIn'])));
notifyListeners();
return responseData['idToken'];
} catch (error) {
rethrow;
}
}
Future login(String email, String password) async {
print(email);
String url = Constants.firebaseLoginUrl;
return _authenticate(email, password, url);
}
Future signup(String email, String password) async {
String url = Constants.firebaseSignupUrl;
return _authenticate(email, password, url);
}
}
In signUp page I have tried below code to create a new user
Future<void> signUpSubmit() async {
if (_formKey.currentState!.validate()) {
await Provider.of<Auth>(context, listen: false).signup(_email.text, _pass.text);
}
I have checked new data is saving perfectly.
In signUp page there has a login button, after click on login button I have redirect in login page,
TextButton(
onPressed: () {
Navigator.push(
context,
MaterialPageRoute(
builder: (context) => const LoginPage()
),
);
},
child: const Text('Log in to access'),
)
After click on login I have redirect in login page, then tried login again but it's not working. In debug I'm getting token if I print(token) in _authenticate method.
In main.dart my consumer is looks like
child: Consumer<Auth>(
builder: (ctx,auth, _) => MaterialApp(
home: auth.isAuth ? const HomePage():const LoginPage(),
)
After create account if I rebuild app again then login is working? How I will solve this problem?
See you are redirecting directly in loginPage rather than via the main page ! Just redirect the login button to main page, in your main page there has condition
home: auth.isAuth ? const HomePage(): const LoginPage()
So, if it is auth false it will always redirect to the login page.
Change
MaterialPageRoute( builder: (context) => const LoginPage() ),
To
MaterialPageRoute( builder: (context) => const MainPage() ),

How to use Sessions with Flutter and Nestjs?

I'm trying to keep users logged in with nestjs backend, when I use Postman the process works very smoothly, but with Flutter I don't know how to do it. I don't think that I actually understand how sessions work for mobiles, I tried looking for some proper explaining but I couldn't find anything so far.
Nestjs Code
#Controller('users')
#Serialize(UserDto)
export class UsersController {
constructor(
private usersService: UsersService,
private authService: AuthService,
) {}
#Get('/whoami')
#UseGuards(AuthGuard)
whoAmI(#currentUser() user: User) {
return user;
}
#Get()
getUsers(#Query('email') email: string) {
return this.usersService.find(email);
}
#Post('/signup')
async sendUser(#Body() body: UserDto, #Session() session: any) {
console.log(body);
const user = await this.authService.signup(body.email, body.password);
session.userId = user.id;
console.log(session.userId);
return user;
}
#Post('/signin')
async signin(#Body() body: UserDto, #Session() session: any) {
const user = await this.authService.signin(body.email, body.password);
session.userId = user.id;
console.log(session.userId);
return user;
}
#Post('/signout')
async signout(#Session() session: any) {
console.log(session.userId);
if (!session.userId) {
throw new NotFoundException('user not found');
}
session.userId = null;
}
}
Flutter Code
Future<void> signin(
String username, String password, BuildContext context) async {
try {
var url = 'https://example-app.herokuapp.com/users/signin';
var dio = Dio();
var response =
await dio.post(url, data: {'email': username, 'password': password}, options: Options(headers: {'Accept': 'application/json'}));
print(response.headers);
// response;
Navigator.of(context).pushNamed(CategoryDetailScreen.routeName);
} catch (err) {
print(err);
throw err;
}
}
Future<void> signout() async {
try {
var url = 'https://example-app.herokuapp.com/users/signout';
var dio = Dio();
var response = await dio.post(url,
options: Options(headers: {
'cookie':
'key'
}
)
);
print(response.headers);
response;
// return response;
} catch (err) {
print(err);
throw err;
}
}
Thanks to #RichardHeap 's comment I managed to solve my issue.
Check out session management with cookies:
Flutter http Maintain PHP session
I used FlutterSecureStorage package to store the incoming cookie and then decide which screen to show as a home screen using FutureBuilder as seen below:
I used these functions to write cookies and delete them from the device:
Future<void> signin(
String username, String password, BuildContext context) async {
try {
// String cookie = '';
var url = 'https://daleel-app.herokuapp.com/users/signin';
var storage = FlutterSecureStorage();
var dio = Dio();
var response =
await dio.post(url, data: {'email': username, 'password': password});
List<String>? cookies = response.headers['set-cookie'];
for (int i = 0; i <= cookies!.length - 1; i++) {
var cokIndex = cookies[i].indexOf(';');
var subCookies = cookies[i].substring(0, cokIndex + 1);
cookie += subCookies + ' ';
}
var subbedCookie = cookie.substring(0, cookie.length - 2);
print(subbedCookie);
storage.write(key: 'cookie', value: subbedCookie);
loggedIn = true;
// print(response.headers['set-cookie']);
// [express:sess=eyJ1c2VySWQiOjU1fQ==; path=/; httponly, express:sess.sig=Zy_Lc7kXM1BqZKIZRRt7ygpCTrM; path=/; httponly]
Navigator.of(context).pushNamed(CategoryDetailScreen.routeName);
} catch (err) {
print(err);
throw err;
}
}
Future<void> signout(BuildContext context) async {
try {
var url = 'https://daleel-app.herokuapp.com/users/signout';
var dio = Dio();
var fStorage = FlutterSecureStorage();
var header = await fStorage.read(key: 'cookie');
await dio.post(url, options: Options(headers: {'cookie': header}));
fStorage.delete(key: 'cookie');
Navigator.of(context).pushReplacementNamed(LoginScreen.routeName);
print('you reached here');
} catch (err) {
print(err);
throw err;
}
}
And here I used the FutureBuilder to decide which screen to show:
class MyApp extends StatefulWidget {
#override
_MyAppState createState() => _MyAppState();
}
class _MyAppState extends State<MyApp> {
var cookie = FlutterSecureStorage().read(key: 'cookie');
#override
Widget build(BuildContext context) {
return MultiProvider(
providers: [
ChangeNotifierProvider(
create: (ctx) => Places(),
),
ChangeNotifierProvider(
create: (ctx) => Offers(),
),
],
child: FutureBuilder(
future: cookie,
builder: (BuildContext context, AsyncSnapshot snapshot) => MaterialApp(
debugShowCheckedModeBanner: true,
title: 'Daleel',
theme: ThemeData(
primarySwatch: Colors.blue,
),
home: snapshot.hasData ? HomeScreen() : LoginScreen(),
routes: {
LoginScreen.routeName: (ctx) => LoginScreen(),
TestScreen3.routeName: (ctx) => TestScreen3(),
TestScreen2.routeName: (ctx) => TestScreen2(),
HomeScreen.routeName: (ctx) => HomeScreen(),
DetailsScreen.routeName: (ctx) => DetailsScreen(),
ExploreScreen.routeName: (ctx) => ExploreScreen(),
CategoryDetailScreen.routeName: (ctx) => CategoryDetailScreen(),
SearchDetailsScreen.routeName: (ctx) => SearchDetailsScreen(),
}),
),
);
}
}

Flutter: ChangeNotifierProxyProvider: not providing updated correct state

I am trying to get state from one ChangeNotifier Auth.dart into another ChangeNotifier ProductsProvider.dart. But ChangeNotifierProxyProvider is providing incorrect state data for Auth.
main.dart
void main() => runApp(
MultiProvider(
providers: [
ChangeNotifierProvider<Auth>(create: (ctx) => Auth()),
ChangeNotifierProxyProvider<Auth, ProductsProvider>(
create: (context) => ProductsProvider(
Provider.of<Auth>(context, listen: false),
productList: [],
),
update: (ctx, auth, preProducts) {
print("ChangeNotifierProxyProvider Token ${auth.isAuth}");
print("ChangeNotifierProxyProvider Token ${auth.token}");
print("ChangeNotifierProxyProvider Test ${auth.test}");
return ProductsProvider(
auth,
productList:
preProducts == null ? [] : preProducts.getProductList,
);
},
),
ChangeNotifierProvider(create: (ctx) => Cart()),
ChangeNotifierProvider(create: (ctx) => Order()),
ChangeNotifierProvider(create: (ctx) => Auth()),
],
child: MyApp(),
),
);
class MyApp extends StatelessWidget {
#override
Widget build(BuildContext context) {
return Consumer<Auth>(
builder: (ctx, auth, _) {
print("Builder Token ${auth.isAuth}");
print("Builder Token ${auth.token}");
print("Builder Test ${auth.test}");
return MaterialApp(
debugShowCheckedModeBanner: false,
title: 'Flutter Demo',
theme: ThemeData(
primarySwatch: Colors.purple,
accentColor: Colors.orange,
fontFamily: "Lato",
),
routes: {
"/": (ctx) => auth.isAuth ? ProductOverviewScreen() : AuthScreen(),
ProductDetailScreen.PRODUCT_DETAIL_ROUTE: (ctx) =>
ProductDetailScreen(),
CartScreen.CART_SCREEN_ROUTE: (ctx) => CartScreen(),
OrderScreen.ORDER_SCREEN_ROUTE: (ctx) => OrderScreen(),
UserProductScreen.USER_PRODUCT_ROUTE: (ctx) => UserProductScreen(),
EditProductScreen.EDIT_PRODUCT_ROUTE: (ctx) => EditProductScreen(),
},
);
},
// ),
);
}
}
Auth.dart
import 'dart:convert';
import 'package:flutter/widgets.dart';
import 'package:http/http.dart' as http;
import 'package:shop_app/models/HttpException.dart';
class Auth with ChangeNotifier {
String _token;
String _userId;
DateTime _expiryDate;
String test;
bool get isAuth => token != null;
String get token {
if (_token == null
// && _expiryDate != null &&
// _expiryDate.isAfter(DateTime.now())
) {
return null;
}
return _token;
}
Future<void> _authenticate(String email, String password, String url) async {
final uri = Uri.parse(url);
final response = await http.post(
uri,
body: jsonEncode({
"email": email,
"password": password,
"returnSecureToken": true,
}),
);
Map<String, dynamic> responseData = jsonDecode(response.body);
if (responseData["error"] != null) {
throw HttpException(responseData["error"]["message"]);
}
_token = responseData["idToken"];
_userId = responseData["localId"];
test = "_token";
// _expiryDate = DateTime.now()
// .add(Duration(seconds: int.parse(responseData["expiresIn"])));
notifyListeners();
}
Future<void> signUp(String email, String password) async {
const url =
"https://identitytoolkit.googleapis.com/v1/accounts:signUp?key=[KEY]";
return await _authenticate(email, password, url);
}
Future<void> login(String email, String password) async {
const url =
"https://identitytoolkit.googleapis.com/v1/accounts:signInWithPassword?key=[KEY]";
return await _authenticate(email, password, url);
}
}
ProductsProvider.dart
import 'dart:convert';
import 'package:flutter/material.dart';
import 'package:http/http.dart' as http;
import 'package:shop_app/models/HttpException.dart';
import 'package:shop_app/models/Product.dart';
import 'package:shop_app/providers/Auth.dart';
class ProductsProvider with ChangeNotifier {
List<Product> _productList = [];
Auth _auth;
ProductsProvider(this._auth, {List<Product> productList = const []})
: _productList = productList;
Future<void> fetchAndSetProducts() async {
var uri = Uri.parse(
"https://flutter-shop-app-3035a-default-rtdb.europe-west1.firebasedatabase.app/products.json?auth=${_auth.token}");
try {
final response = await http.get(uri);
if (response.body != "null") {
final Map<String, dynamic> decodedJSON = jsonDecode(response.body);
final List<Product> loadedProductList = [];
decodedJSON.forEach((prodId, prodData) {
loadedProductList.add(Product(
id: prodId,
title: prodData["title"],
description: prodData["description"],
price: prodData["price"],
imageUrl: prodData["imageUrl"],
isFavourite: prodData["isFavourite"],
));
});
_productList = loadedProductList;
} else {
_productList = [];
}
notifyListeners();
} catch (error) {
throw error;
}
}
List<Product> get getProductList {
return _productList;
}
Product findById(String id) =>
_productList.firstWhere((element) => element.id == id);
Future<void> addProduct(Product product) async {
var uri = Uri.parse(
"https://flutter-shop-app-3035a-default-rtdb.europe-west1.firebasedatabase.app/products.json");
try {
final responseProduct =
await http.post(uri, body: jsonEncode(product.toJSon()));
final finalProduct = Product(
id: jsonDecode(responseProduct.body)["name"],
title: product.title,
description: product.description,
price: product.price,
imageUrl: product.imageUrl);
_productList.add(finalProduct);
notifyListeners();
return Future.value();
} catch (error) {
throw error;
}
}
Future<void> updateProduct(String id, Product product) async {
var productIndex = _productList.indexWhere((element) => element.id == id);
if (productIndex >= 0) {
var uri = Uri.parse(
"https://flutter-shop-app-3035a-default-rtdb.europe-west1.firebasedatabase.app/products/$id.json");
try {
await http.patch(uri,
body: jsonEncode({
"title": product.title,
"description": product.description,
"price": product.price,
"imageUrl": product.imageUrl,
"isFavourite": product.isFavourite,
}));
_productList[productIndex] = product;
notifyListeners();
} catch (error) {
throw error;
}
}
}
Future<void> deleteProduct(String id) async {
final uri = Uri.parse(
"https://flutter-shop-app-3035a-default-rtdb.europe-west1.firebasedatabase.app/products/$id.json");
final existingProductIndex =
_productList.indexWhere((element) => element.id == id);
var existingProduct = _productList[existingProductIndex];
_productList.removeAt(existingProductIndex);
final response = await http.delete(uri);
if (response.statusCode >= 400) {
_productList.insert(existingProductIndex, existingProduct);
notifyListeners();
throw HttpException("Could not delete product");
}
existingProduct = null;
notifyListeners();
}
}
After I click the login button the login method from the Auth.dart is trigged. After fetching the token the from firebase the screen is updated to ProductOverviewScreen. But the ProductsProvider.dart is not able to fetch the items because the ChangeNotifierProxyProvider update is returning an incorrect state for Auth.
Output:
Builder Token true
Builder Token eyJhbGciOiJSUzI1NiIsImtpZCI6IjNkOWNmYWE4OGVmMDViNDI0YmU2MjA1ZjQ2YjE4OGQ3MzI1N2JjNDIiLCJ0eXAiOiJKV1QifQ.eyJpc3MiOiJodHRwczovL3NlY3VyZXRva2VuLmdvb2dsZS5jb20vZmx1dHRlci1zaG9wLWFwcC0zMDM1YSIsImF1ZCI6ImZsdXR0ZXItc2hvcC1hcHAtMzAzNWEiLCJhdXRoX3RpbWUiOjE2MjE3NzUxMDYsInVzZXJfaWQiOiJ5Y0dVVWhwYTFvY2EwMThlYUx4VGZkQnRNbmsyIiwic3ViIjoieWNHVVVocGExb2NhMDE4ZWFMeFRmZEJ0TW5rMiIsImlhdCI6MTYyMTc3NTEwNiwiZXhwIjoxNjIxNzc4NzA2LCJlbWFpbCI6InRlc3RAdGVzdC5jb20iLCJlbWFpbF92ZXJpZmllZCI6ZmFsc2UsImZpcmViYXNlIjp7ImlkZW50aXRpZXMiOnsiZW1haWwiOlsidGVzdEB0ZXN0LmNvbSJdfSwic2lnbl9pbl9wcm92aWRlciI6InBhc3N3b3JkIn19.t1xosbzllt79NV6FQ79mTQ2J3VCR5fILKMxE5-ObOxMI2DtD_kMg2AP9NXm_f1IsLF9AT5xeXeVU36goVDLQuKSWmbejOANDn7hsF6VzZMyBV1P9qehXpWgSmCscjXT8FRlKViZzxOZwCWSHS1M94n92YYhwaZltiDcQ87hhv7ZdKyLrlDsUPfr7IjNBeSVDmzws_9uBoZwYKRYCW1veWPc7HPWtdP8QT7K_vkCEvGLHfxbmVHOgUkDMzLqhgusZl34GCPVKr_PSpQ9SgC7Mg95QeZyzYzPmhasGUptq5pQsjEoqTxgYHnmEuMRRjksZT5lbfsQQFOJMsXBTIC7RDQ
Builder Test _token
ChangeNotifierProxyProvider Token false
ChangeNotifierProxyProvider Token null
ChangeNotifierProxyProvider Test null
Error: Expected a value of type 'int', but got one of type 'String'
at Object.throw_ [as throw] (http://localhost:1344/dart_sdk.js:5333:11)
at ProductProvider.ProductsProvider.new.fetchAndSetProducts (http://localhost:1344/packages/shop_app/providers/ProductProvider.dart.lib.js:84:21)
at fetchAndSetProducts.next (<anonymous>)
at http://localhost:1344/dart_sdk.js:39031:33
at _RootZone.runUnary (http://localhost:1344/dart_sdk.js:38888:58)
at _FutureListener.thenAwait.handleValue (http://localhost:1344/dart_sdk.js:33874:29)
at handleValueCallback (http://localhost:1344/dart_sdk.js:34434:49)
at Function._propagateToListeners (http://localhost:1344/dart_sdk.js:34472:17)
at _Future.new.[_completeWithValue] (http://localhost:1344/dart_sdk.js:34314:23)
at async._AsyncCallbackEntry.new.callback (http://localhost:1344/dart_sdk.js:34337:35)
at Object._microtaskLoop (http://localhost:1344/dart_sdk.js:39175:13)
at _startMicrotaskLoop (http://localhost:1344/dart_sdk.js:39181:13)
at http://localhost:1344/dart_sdk.js:34688:9
I am learning dart and flutter. I am not sure what I am missing. Can someone could help me fix this issue.
I was able to find what was causing this issue.
It appears I was reinitializing the Auth ChangeNotifierProvider. If you see the last line in the MultiProvider constructor providers argument.
If anyone else comes across such an issue ensure that you have specified the providers in the correct order.
Product Id defined int/Integer, but the parser from firebase is String. You need change in Product Id to String or use int.parse(prodId)

Riverpod FutureProvider keeps on firiging again and again

I am using Riverpod's FutureProvider with family. The FutureProvider keeps on running again and again. It shows the loading dialog only. Also the hot reload stops working. FutureProvider is working fine without family. Please help in finding what's wrong.
final ephemerisProvider =
Provider((ref) => ApiService("https://localhost"));
final ephemerisFutureProvider = FutureProvider.family
.autoDispose<EpheModel, Map<String, dynamic>>((ref, data) async {
var response = await ref.read(ephemerisProvider).getData(data);
print(EpheModel.fromJSON(response));
return EpheModel.fromJSON(response);
});
class Kundlis extends ConsumerWidget {
static const routeName = "/kundlis";
#override
Widget build(BuildContext context, ScopedReader watch) {
final AsyncValue<EpheModel> kundlis = watch(ephemerisFutureProvider({}));
return Scaffold(
appBar: AppBar(
title: Text("Kundlis"),
),
drawer: AppDrawer(),
body: kundlis.when(
data: (kundli) => Center(child: Text(kundli.toString())),
loading: () => ProgressDialog(message: "Fetching Details..."),
error: (message, st) =>
CustomSnackBar.buildErrorSnackbar(context, '$message')));
}
}
class ApiService {
final String url;
ApiService(this.url);
Future<Map<String, dynamic>> getData(Map<String, dynamic> data) async {
try {
http.Response response = await http.post(url + "/ephe",
headers: <String, String>{'Content-Type': 'application/json'},
body: jsonEncode(data));
if (response.statusCode == 200) {
return data;
} else {
throw Exception("Error Fetching Details");
}
} on SocketException {
throw Exception("No Internet Connection");
} on HttpException {
throw Exception("Error Fetching Details");
}
}
}
{} != {}. Because of .family, you are creating a completely new provider every time you call watch(ephemerisFutureProvider({})). To select a previously-built provider via family, you must pass an identical value. And {} is never identical to {}, guaranteed. :)

Flutter Multiprovider with dependent streams

I'm want to make the User document from Firestore available to different widgets in my application. I'm listening to the authstate changes and getting the user ID, however, I'm not sure how to use that within a subsequent stream in my multiprovider :
return MultiProvider(
providers: [
Provider<FirebaseAuthService>(
create: (_) => FirebaseAuthService(),
),
//I'm looking to get another provider based on the provider above^^^ i.e the user id,
],
child: getScreen(),
);
class FirebaseAuthService {
final _firebaseAuth = FirebaseAuth.instance;
User _userFromFirebase(FirebaseUser user) {
return user == null ? null : User(uid: user.uid);
}
Stream<User> get onAuthStateChanged {
return _firebaseAuth.onAuthStateChanged.map(_userFromFirebase);
}
Future<User> signInAnonymously() async {
final authResult = await _firebaseAuth.signInAnonymously();
return _userFromFirebase(authResult.user);
}
Future<void> signOut() async {
return await _firebaseAuth.signOut();
}
}