How do I initialize a dart class's private property with an async value without initstate? - class

Every time this class is instantiated, I wish a call to be made to firebase and the _datapoint given the retrieved value. Otherwise, I have to assign the datapoint in each function in the class (see functionOneExample), and that's just prone to errors. Note: I cannot use initstate as this function is not a widget (I do not need or want a build method). If I could, I would call _getThis in the initstate. Thanks for your help!!
class AsyncInitExample {
AsyncInitExample(this.enterThis);
String enterThis;
String _datapoint;
_getThis() async {
var firebaseRetrieved = await //Firebase get this;
this._datapoint = firebaseRetrieved;
}
Future<dynamic> functionOneExample {
this._datapoint ?? await _getThis();
}
// etc. etc. etc.
}

I can recommend making a private constructor together with a static method to fetch all the async values and then use the private constructor to return a object:
class AsyncInitExample {
AsyncInitExample._(this.enterThis, this._datapoint);
String enterThis;
String _datapoint;
static Future<AsyncInitExample> getInstance(String enterThis) async {
var firebaseRetrieved = await //Firebase get this;
return AsyncInitExample._(enterThis, firebaseRetrieved);
}
String functionOneExample() => _datapoint;
// etc. etc. etc.
}
By doing it this way, you just need to await the Future from getInstance() and after this, you can access all variables in the class without awaiting.

Related

Flutter SharedPreferences Null check operator used on a null value

I've made a class for shared preferences. The class is as follows
class StorageUtil {
static StorageUtil? _storageInstance;
static SharedPreferences? _preferences;
static Future<StorageUtil?> getInstance() async {
if (_storageInstance == null) {
_storageInstance = StorageUtil();
}
if (_preferences == null) {
_preferences = await SharedPreferences.getInstance();
}
return _storageInstance;
}
addStringtoSF(String key, String value) async {
print(' inside sharedPreferences file $key $value'); // Receives data here
await _preferences!.setString(key,
value); //Unhandled Exception: Null check operator used on a null value
}
When ever i try to store the values i'm getting a error 'Null check operator used on a null value'
This is how i'm passing down the values to the store function. I'm receiving the data inside the function. But cannot store the values inside it. What causes this?
String? userResponse = json.encode(authResponse);
print('This is userResponse type');
_storageUtil.addStringtoSF('userData', userResponse);
Try adding this WidgetsFlutterBinding.ensureInitialized();
in the very first line of you main() method in main.dart file if not present.
The problem over here is that
The class has a static function that is responsible for the initialization of variables and can be accessed without an object of class StorageUtil.
When the nonstatic function is called you need to create an object of StorageUtil class and then access that function due to which the static variables are not initialized which are initialized in the static function hence null.
From the snippet of code it seems you are willing to make a singleton class here is the correct code for it:
class StorageUtil {
static StorageUtil storageInstance = StorageUtil._instance();
static SharedPreferences? _preferences;
StorageUtil._instance(){
getPreferences();
}
void getPreferences()async{
_preferences = await SharedPreferences.getInstance();
}
addStringtoSF(String key, String value) async {
print(' inside sharedPreferences file $key $value'); // Receives data here
await _preferences!.setString(key,
value);
}
}
Where ever you want the preference to be used just call:
final StorageUtil storage = StorageUtil.storageInstance;
storage.AnyNonStaticFunctionName()// call for methods in the StorageUtil Class
this is the one and only object that will exist throughout the application.
Or
if you don't want to change your class then just add this in all the nonstatic functions at the top which uses _preferences
And also add this null check
if (_preferences == null) {
_preferences = await SharedPreferences.getInstance();
}
because you may be having multiple instances of the StorageUtil making the _preferences variable null each time.
Add this line also under your print line, before calling _preferences!.
if (_preferences == null) {
_preferences = await SharedPreferences.getInstance();
}

How to read StateNotifierProvider state inside FutureProvider

I have a StateNotifierProvider which has getter and setter method of a class variable.
I'm trying to access the class variable value via getter method inside a FutureProvider
I've tried multiple ways to access the value inside the FutureProvider but unable to get any success.
I am getting the below error
errror snapshot
Let me know the correct approach.
class SelectedPost extends StateNotifier<String> {
SelectedPost() : super("");
var uuid;
void setPostId(String id) {
uuid = id;
}
String getPostId() {
return uuid;
}
}
Here I'm trying to access the getter method to fetch uuid value
final selectedPostProvider = StateNotifierProvider((ref) => new SelectedPost());
final detailPostFuture = FutureProvider<Post>((ref) async {
final _selectedPostProvider = ref.watch(selectedPostProvider);
return fetchDetailPost(_selectedPostProvider.getPostId());
});
If you want to use the state itself, use this.
final detailPostFuture = FutureProvider<Post>((ref) async {
final _selectedPostProvider = ref.watch(selectedPostProvider);
});
If you want to access only the members (like functions, variables), use this.
final detailPostFuture = FutureProvider<Post>((ref) async {
final _selectedPostProvider = ref.watch(selectedPostProvider.notifier);
});
If you want to use both, use this.
final detailPostFuture = FutureProvider<Post>((ref) async {
final _selectedPostState = ref.watch(selectedPostProvider);
final _selectedPostProvider = ref.watch(selectedPostProvider.notifier);
});
See this migration doc for reference.
You need to watch the Notifier itself. It can be done by using:
final _selectedPostNotifier = ref.watch(selectedPostProvider.notifier);

Try replacing the reference to the instance member with a different expression

I am using Riverpod to fetch Api and display in the app, and my method , "getMovieList()" requires a String, but in the below code I am getting this Error :
"The instance member 'pageNumber' can't be accessed in an initializer.
Try replacing the reference to the instance member with a different expressiondartimplicit_this_reference_in_initializer"
class StateManager {
final String pageNumber;
StateManager(this.pageNumber);
static final movieStateFuture = FutureProvider<List<Movie>>((ref) async {
return ApiSetup.getMovieList(pageNumber); // The error is Here "The instance member 'pageNumber' can't be accessed in an initializer."
});
}
class ApiSetup {
static List<Movie> parsePhotos(String responseBody) {
List<Movie> listMovies = [];
for (var mov in jsonDecode(responseBody)['results']) {
final movie = Movie.fromJson(mov);
listMovies.add(movie);
}
return listMovies;
}
static Future<List<Movie>> getMovieList(String pageNum) async {
final response = await http.get(Uri.parse(
"https://api.themoviedb.org/3/movie/now_playing?api_key=${Constants.apiKey}&language=en-US&page=$pageNum"));
if (response.statusCode == 200) {
return compute(parsePhotos, response.body);
} else {
print("Error here");
}
throw Exception("Some Random Error");
}
}
You can not refer a non static member from inside a static method. Your pageNumber is an attribute that belongs to the instance/object of StateManager whereas static methods belongs to the class.
If you want to use pageNumber while accessing the future try using family provider instead:
static final movieStateFuture = FutureProvider.family<List<Movie>,int>( //<-- Add '.family' modifer and 'datatype' of the argument
(ref, pageNum) async { //<-- Second argument to create method is the parameter you pass
return ApiSetup.getMovieList(pageNum);
}
);
Now while calling movieStateFuture, pass in the argument like this:
watch(movieStateFuture(/*PAGE_NUMBER_HERE*/));

Why the function is returning an empty list in Flutter

So Here's the code. The error is when calling loadSounds function from outside the class returns an empty list. But when loadSounds is called from loadcategories it works fine and returns a list containing instances of Sound. Even priniting the sounds variable in the loadSounds function prints an empty list.
class AudioRepository {
List<Category> categories = <Category>[];
List<Sound> sounds = <Sound>[];
Future<String> _loadCategoriesAsset() async =>
await rootBundle.loadString(Assets.soundsJson);
Future<List<Category>> loadCategories() async {
if (categories.isNotEmpty) {
return categories;
}
String jsonString = await _loadCategoriesAsset();
categories.clear();
categories.addAll(categoryFromJson(jsonString));
categories.map((c) => sounds.addAll(c.sounds)).toList();
loadSounds('1');
return categories;
}
Future<List<Sound>> loadSounds(String categoryId) async {
print(sounds);
return sounds
.where((sound) => sound.id.substring(0, 1) == categoryId)
.toList();
}
}
Output when called from loadCategories is as follows:
[Instance of 'Sound', Instance of 'Sound', Instance of 'Sound', Instance of 'Sound', Instance of 'Sound', Instance of 'Sound']
I'm accessing it outside the class as follows:
final _sounds = await repository.loadSounds(event.categoryId);
Output when called from outside or printing from loadSounds function is as follows:
[]
So what's the problem here. I'm not able to figure out why the loadSounds fuction work when called from loadCategories inside the class and not otherwise in any way.
If you don't call repository.loadCategories() before calling loadSounds(), you won't have anything in your sounds variable since you are assigning values only in your loadCateogries() function.
Is your repository variable a singleton and did you call loadCategories on it?
Also, I would not write this line like this :
categories.map((c) => sounds.addAll(c.sounds)).toList();
The toList() method is not really usefull and the map function should be used more to convert something (a String to Int mapping for instance).
I would use :
categories.forEach((c)=> sounds.addAll(c.sounds));
use provider and ChangeNotifier is the best way.
your code will be like this
class AudioRepository extends ChangeNotifier {
List<Category> categories = <Category>[];
List<Sound> sounds = <Sound>[];
Future<String> _loadCategoriesAsset() async =>
await rootBundle.loadString(Assets.soundsJson);
Future<List<Category>> loadCategories() async {
if (categories.isNotEmpty) {
return categories;
}
String jsonString = await _loadCategoriesAsset();
categories.clear();
categories.addAll(categoryFromJson(jsonString));
categories.map((c) => sounds.addAll(c.sounds)).toList();
//notify listeners
notifyListeners();
////
loadSounds('1');
return categories;
}
Future<List<Sound>> loadSounds(String categoryId) async {
print(sounds);
return sounds
.where((sound) => sound.id.substring(0, 1) == categoryId)
.toList();
}
}
to retrieve data from outside, the following code should be placed in Build() methode.
final _arp = Provider.of<AudioRepository>(context, listen: true);
print(_arp._sounds);
It may be an error in the where clause. Because the condition isn't correct.
try this:
return sounds
.where((sound) => sound.id[0] == categoryId)
.toList();

Is there any way we can inject provider dependancy to normal class in flutter?

I have one normal dart class in which I want to provide two provider dependency.
So I can access that class though out of my application. I can pass that dependency from the build method of the widget and then I can use this class but I don't want to do that, like 100 times I have to pass that dependency if I used that class 100 times.
I also want to use this class from every lifecycle of flutter as it's generating different types of events for application.
I also want to initialize genrateUserProerties() method only once when the dependency is ready and when the user opens the application.
This is initialized before any provider initializes and it's not always used from the method where we have context available.
I need a way to provide that dependency in a way we can initialize genrateUserProerties() only once.
User _user; and BrandCofiguration _activeBrand; I need these two be pass here when it's ready.
User _user; and BrandCofiguration _activeBrand; both are coming from two different Providers when I received a valid response from the server.
class FireBaseAnalyticsBase {
static FirebaseAnalytics _analytics;
static FirebaseAnalyticsObserver _observer;
**User _user;**
BuildContext _context;
**BrandCofiguration _activeBrand;**
int _seconds;
Stopwatch _stopwatch;
String _eventName;
Map<String, dynamic> _userProperties = {};
bool _isTimeTrackEvent;
FireBaseAnalyticsBase(BuildContext context, UserProvider userProvider,
BrandSelectionProvider brandSelectionProvider) {
this._context = context;
_analytics = FirebaseAnalytics();
_observer = FirebaseAnalyticsObserver(analytics: _analytics);
_activeBrand = brandSelectionProvider.activeBrand;
_user = userProvider.authenticatedUser;
if (_user != null) {
genrateUserProerties();
}
}
void startFirebaseEventWithoutTime(String eventName) {
this._eventName = eventName;
_isTimeTrackEvent = false;
logFirebaseEvent();
}
void startFireBaseEventWithTime(String eventName) {
_stopwatch = Stopwatch();
_stopwatch.start();
_isTimeTrackEvent = true;
_eventName = eventName;
}
void stopFireBaseTimeEvent() {
_stopwatch.stop();
_seconds = (_stopwatch.elapsedMilliseconds / 1000) as int;
_stopwatch.reset();
logFirebaseEvent();
}
Future<void> logFirebaseEvent() async {
if (_isTimeTrackEvent) {
_userProperties
.addAll({FirebaseAnalyticsEnum.time_spent.value: _seconds});
}
print("firebase test");
await _analytics.logEvent(
name: _eventName,
parameters: _userProperties,
);
}
Future<void> genrateUserProerties() async {
print("firebase properties initilize");
var _packageInfo = await PackageInfo.fromPlatform();
_userProperties = {
FirebaseAnalyticsEnum.user_id.value: _user.id.toString(),
FirebaseAnalyticsEnum.platform.value: Platform.operatingSystem,
FirebaseAnalyticsEnum.device_language.value:
Localizations.localeOf(_context).languageCode,
FirebaseAnalyticsEnum.application.value: _packageInfo.appName,
FirebaseAnalyticsEnum.current_api.value: Config.CURRENT_API,
FirebaseAnalyticsEnum.device_type.value: _user.id.toString(),
FirebaseAnalyticsEnum.app_version.value: Config.CURRENT_VERSION,
FirebaseAnalyticsEnum.is_admin.value: _user.isAdmin,
FirebaseAnalyticsEnum.is_educator.value: _user.educator,
FirebaseAnalyticsEnum.is_brand_ambassador.value: _user.brandAmbassador,
FirebaseAnalyticsEnum.salon_role.value: _user.salongroup,
FirebaseAnalyticsEnum.brand.value: _activeBrand.brandName,
FirebaseAnalyticsEnum.school_role.value: _user.schoolgroup,
};
}
}
I think you should use Singleton pattern for these classes consume a lot of resources.
Example:
static FireBaseAnalyticsBase _instance;
static FireBaseAnalyticsBase getInstance(BuildContext context, UserProvider userProvider,
BrandSelectionProvider brandSelectionProvider){
if(_instance == null){
_instance = FireBaseAnalyticsBase(context,userProvider,brandSelectionProvider);
}
return _instance;
}
Or if you passing BuildContext you can get Provider
Provider.of<>(context) in FireBaseAnalyticsBase's Constructor