Is there a best practice to call a specific function every time before any function in Dart/Flutter? - flutter

I am working with singleton pattern, and I have managed the dependency and parameter injections like this:
LocalizationClientComponent._();
/// Singleton instance of the [LocalizationClientComponent] class.
static final instance = LocalizationClientComponent._();
static bool _injectedDependencies = false;
static bool _injectedParams = false;
late final DeviceInfoRepository _deviceInfoRepository;
late final MapapiRepository _mapapiRepository;
late final SensorsRepository _sensorsRepository;
late final LocationRepository _locationRepository;
late final BluetoothRepository _bluetoothRepository;
late final WifiRepository _wifiRepository;
void injectDependencies({
required DeviceInfoRepository deviceInfoRepository,
required MapapiRepository mapapiRepository,
required SensorsRepository sensorsRepository,
required LocationRepository locationRepository,
required BluetoothRepository bluetoothRepository,
required WifiRepository wifiRepository,
}) {
if (_injectedDependencies) return;
_deviceInfoRepository = deviceInfoRepository;
_mapapiRepository = mapapiRepository;
_sensorsRepository = sensorsRepository;
_locationRepository = locationRepository;
_bluetoothRepository = bluetoothRepository;
_wifiRepository = wifiRepository;
_injectedDependencies = true;
}
Then the same thing for parameters:
void injectParams({
required String email,
required String password,
required Duration postFrequency,
required int sensorListLength,
}) {
if (_injectedParams) return;
_postFrequency = postFrequency;
_sensorListLength = sensorListLength;
_telemetryEvent = TelemetryEvent(_sensorListLength);
_injectedParams = true;
}
After that, I made this method to control whether params and dependencies are injected:
void _handleInitError() {
final StringBuffer errorMessage = StringBuffer();
if (!_injectedDependencies) {
errorMessage
..write(
'Dependencies are not injected. Use injectDependencies() before using any other methods.',
)
..write('\n');
}
if (!_injectedParams) {
errorMessage.write(
'Parameters are not injected. Use injectParams() before using any other methods.',
);
}
if (errorMessage.isNotEmpty) {
throw Exception(errorMessage.toString());
}
}
Now, I am adding _handleInitError() to all other methods like this:
Future<bool> openAppSettings() async {
_handleInitError();
return _locationRepository.openAppSettings();
}
Future<bool> openLocationSettings() async {
_handleInitError();
return _locationRepository.openLocationSettings();
}
Future<bool> requestLocationPermission() {
_handleInitError();
return _locationRepository.requestLocationPermission();
}
Future<bool> activateLocationService() async {
_handleInitError();
return _locationRepository.activateLocationService();
}
Future<bool> turnOnWifi() async {
_handleInitError();
return _wifiRepository.turnOnWifi();
}
Is there a better way than adding all methods to _handleInitError()?
Is there a way to automatically execute this method before any method?

You could define a base function that your functions get passed as parameter and before each call you do your _handleInitError
void call(Function f){
_handleInitError();
f.call();
}
Call it like this
call(requestLocationPermission);

Related

I can't assign a value to a variable from an asyncron function inside the class

I cannot assign a value to the result variable of type Map<String,dynamic>. I get an error when I define it with late . I think the problem is that I cannot change the value of the variable in the class from within the asyncron function. I couldn't find the reason for this.
I'm so confused. I couldn't find a project or an example that shows how to assign a value to a variable from an asyncron function in any class. You can also suggest a sample project where the http package is used close to the clean code logic. Thank you from now.
void main() {
String urlx = 'http://localhost:8000/process/get_all';
HttpRouter allitem = HttpRouter(url: urlx);
HttpResponser itemyol = HttpResponser();
itemyol.setUrl(urlx);
itemyol.getProcess();
print(itemyol.getItem());
print(itemyol.result);
}
class HttpRouter {
final String url;
HttpRouter({required this.url});
Future<http.Response> getProcess() {
return http.get(Uri.parse(this.url));
}
Future<http.Response> fetchFollowing() {
return http.post(
Uri.parse(this.url),
body: jsonEncode(<String, String>{
'title': "title",
}),
);
}
}
class HttpResponser {
List<dynamic>? result;
late String errorMessage;
late String url;
Future<bool> getProcess() async {
await HttpRouter(url: url).getProcess().then((data) {
if (data.statusCode == 200) {
//There is no problem in this section. The data is coming.
//and I can write with print .
//The problem is that I cannot assign the data to the result variable.
//It always returns null.
setResult(RequestModel.fromJson(json.decode(data.body)).result);
} else {
Map<String, dynamic> result = json.decode(data.body);
setMessage(result['message']);
}
});
return true;
}
setResult(value) {
this.result = value;
}
void setMessage(String value) {
errorMessage = value;
}
List? getItem() {
return this.result;
}
void setUrl(String url) {
this.url = url;
}
}
I tried assigning a value to a variable in a class from an asyncron function in a class, but I couldn't.

displaying only the current user data

I protected data_service with current user to only display the current user's habits.
data_service.dart:
class DataService {...
late final Database db;
Users? _user;
late final StreamData<Map<int, Habit>> habits;
Future<void> init() async {
db = await HabitsDb.connectToDb();
habits = StreamData(initialValue: await _getAllHabits(), broadcast: true);
}
String get userEmail => AuthService.firebase().currentUser!.email;
Future<Map<int, Habit>> _getAllHabits() async {
getOrCreateUser(email: userEmail); //issue
final habits = await _getAllHabitsFromDb();
final map = Map<int, Habit>();
final currentUser = _user;
print(currentUser);
for (final habit in habits) {
if (currentUser != null) {
print(currentUser.id);
print(habit.userId);
if (habit.userId == currentUser.id) {
map[habit.id] = habit;
}
}
//map[habit.userId] = currentUser?.id;
}
return map;
}
Future<List<Habit>> _getAllHabitsFromDb() async {
final habitsMap = await HabitsDb.getAllHabits(db);
final habitsList = habitsMap.map((e) => Habit.fromDb(e)).toList();
return habitsList;
}
Future<Users> getOrCreateUser({
required String email,
bool setAsCurrentUser = true,
}) async {
try {
//we found the user
final user = await getUser(email: email);
if (setAsCurrentUser) {
_user = user;
}
print(_user?.email);
return user;
} on CouldNotFindUser {
//we didn't find the user
final createdUser = await createUser(email: email);
if (setAsCurrentUser) {
_user = createdUser;
}
return createdUser;
} catch (e) {
rethrow;
}
}
...}
in main class:
void main() async {
WidgetsFlutterBinding.ensureInitialized();
await Firebase.initializeApp();
final dataService = DataService();
await dataService.init();
GetIt.I.registerSingleton(dataService);
... }
StreamData class:
class StreamData<T> {
List<Habit> _notes = [];
User? _user;
late final StreamController<T> _controller;
Stream<T> get stream => _controller.stream;
late T _value;
T get value => _value;
StreamData({required T initialValue, bool broadcast = true}) {
if (broadcast) {
_controller = StreamController<T>.broadcast();
} else {
_controller = StreamController<T>();
}
_value = initialValue;
}
the problem is that the line getOrCreateUser(email: userEmail); is only called once and it does not work when I switch user and I need to Hot Restart to fix it. I think using Futurebuilder will fix it. but if yes, how do I use it when there is a need to call dataService.init at the beginning of the main?
Since your getOrCreateUser function is declared as async, you'll want to use await when you call it in _getAllHabits:
await getOrCreateUser(email: userEmail)
This ensures the getOrCreateUser code has completed before the rest of the code in _getAllHabits (that depends on the result of getOrCreateUser) executes.

Flutter Null Safe Config Class with shared_preferences

In flutter 1.x, I implemented a Config class using the Flutter shared_preferences package; the code looks like this:
import 'package:shared_preferences/shared_preferences.dart';
class Config {
static final Config _config = Config._internal();
factory Config() => _config;
final accessTokenKey = 'accessToken';
String _accessToken;
SharedPreferences prefs;
Config._internal() {
loadData();
}
void loadData() async {
prefs = await SharedPreferences.getInstance();
_accessToken = prefs.getString(accessTokenKey) ?? '';
}
String get accessToken {
return _accessToken;
}
set accessToken(String accessToken) {
_accessToken = accessToken;
_saveString(accessTokenKey, accessToken);
}
_saveString(String key, String value, {String printValue = ''}) {
String printVal = printValue.length > 0 ? printValue : value;
prefs.setString(key, value);
}
}
I’m creating a new project in Flutter 2.x and trying to use the same code, but due to changes associated with null safety I’m having some difficulty getting the updated code just right.
The updated documentation for the package says to initialize the _prefs object like this:
Future<SharedPreferences> _prefs = SharedPreferences.getInstance();
Then create a local prefs object using:
final SharedPreferences prefs = await _prefs;
This is fine, but I don’t want to have to make every class method that uses shared_preferences async then recreate the variable. At the same time I can’t create it as a class variable without initializing it first. Can someone please show me a cleaner way to do this, or do I just have to redeclare it every time I use it?
Also, how do I initialize the config object in my other classes? In my 1.x code, I would just do this:
final Config config = new Config();
then start accessing the properties of the config object. How do I initialize it with all of the async code in the class now?
Here’s where the updated code is today:
import 'package:shared_preferences/shared_preferences.dart';
import '../models/device.dart';
class Config {
static final Config _config = Config._internal();
factory Config() => _config;
final accessTokenKey = 'accessToken';
String _accessToken = '';
Future<SharedPreferences> _prefs = SharedPreferences.getInstance();
Config._internal() {
print('Config constructor');
loadData();
}
Future<void> loadData() async {
final SharedPreferences prefs = await _prefs;
_accessToken = prefs.getString(accessTokenKey) ?? '';
}
String get accessToken {
return _accessToken;
}
set accessToken(String accessToken) {
_accessToken = accessToken;
_saveString(accessTokenKey, accessToken);
}
_saveString(String key, String value, {String printValue = ''}) {
String printVal = printValue.length > 0 ? printValue : value;
print('Config: _saveString("$key", "$printVal")');
final SharedPreferences prefs = await _prefs;
prefs.setString(key, value);
}
}
You can get instance of SharedPreferences as static field in init method:
static SharedPreferences? _prefs; //or: static late SharedPreferences _prefs;
static init() async {
_prefs = await SharedPreferences.getInstance();
}
And call init() somewhere like in build() method of first widget run, for once.Now you can use _prefs everywhere as you want.
If I want to show you a complete class to use SharedPreferences, it looks like this:
import 'package:shared_preferences/shared_preferences.dart';
class SharedPreferencesRepository {
static SharedPreferences? _prefs;
static init() async {
_prefs = await SharedPreferences.getInstance();
}
static putInteger(String key, int value) {
if (_prefs != null) _prefs!.setInt(key, value);
}
static int getInteger(String key) {
return _prefs == null ? 0 : _prefs!.getInt(key) ?? 0;
}
static putString(String key, String value) {
if (_prefs != null) _prefs!.setString(key, value);
}
static String getString(String key) {
return _prefs == null ? 'DEFAULT_VALUE' : _prefs!.getString(key) ?? "";
}
static putBool(String key, bool value) {
if (_prefs != null) _prefs!.setBool(key, value);
}
static bool getBool(String key) {
return _prefs == null ? false : _prefs!.getBool(key) ?? false;
}
}
I hope this useful for you.
If you need to wait for some async work to finish before getting an instance of a class, consider using a static method (not a factory constructor, since constructors must always return the base type).
You can use late fields to allow them to be non-null before you initialize them:
class Config {
late String _accessToken;
String get accessToken => _accessToken;
Config._(); // private constructor to prevent accidental creation
static Future<Config> create() async {
final config = Config();
final preferences = await SharedPreferences.getInstance();
config._accessToken = await preferences.getString('<your key>');
return config;
}
}
If you want to make sure this is initialized before running your app, you can initialize it in your main() method before you call runApp() to give control to the Flutter framework:
Future<void> main() async {
WidgetsFlutterBinding.ensureInitialized(); // make sure all plugins loaded etc.
final config = await Config.create();
print(config.accessToken);
runApp(MyApp());
}

How to save data of type bool in shared_preferences flutter

I created a separate calss page to working with shared preferences from all the different application pages. Save or edit data. I can save String data with ease, but I am facing a problem saving data of type bool. I try to save data of type bool to store the status of the user logged in or not. I searched for solutions for a long time, but couldn't find.
full code:
import 'package:shared_preferences/shared_preferences.dart';
class MyPreferences {
static const ID = "id";
static const STATE = "state";
static final MyPreferences instance = MyPreferences._internal();
static SharedPreferences _sharedPreferences;
String id = "";
String state = "";
MyPreferences._internal() {}
factory MyPreferences() => instance;
Future<SharedPreferences> get preferences async {
if (_sharedPreferences != null) {
return _sharedPreferences;
} else {
_sharedPreferences = await SharedPreferences.getInstance();
state = _sharedPreferences.getString(STATE);
id = _sharedPreferences.getString(ID);
return _sharedPreferences;
}
}
Future<bool> commit() async {
await _sharedPreferences.setString(STATE, state);
await _sharedPreferences.setString(ID, id);
}
Future<MyPreferences> init() async {
_sharedPreferences = await preferences;
return this;
}
}
Can somebody help me to make bool data.
thank you
Just add a couple methods to your class.
void updateLoggedIn(bool value) {
_sharedPreferences.setBool('logged_in', value);
}
bool isLoggedIn() => _sharedPreferences.getBool('logged_in') ?? false;
Then on login just run
MyPreferences.instance.updateLoggedIn(true)
And the same thing passing in false on logout.
Then whenever you want to check logged in status just run
if(MyPreferences.instance.isLoggedIn()) {
// whatever needs to happen
}

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