how to use one preference activity for multiple instances of the widgets? - android-widget

I am working on an android home screen app widget.
i am trying to add preferences to the widget. i can put multiple widgets. but i want to configure it with preferences using shared preference created dynamically.
how can i load the preference from the resource file which fetches the value from the sharedpreference and also updates it?
any suggestion?

The key to the problem is to somehow use the setting saved by PreferenceActivity as reference and create your own setting. Below is what I used to achieve having multiple setting for multiple instances of app widget with single PreferenceActivity.
private void onExitPreferenceActivity(Context context, int appWidgetId) {
// Load the user selected settings saved by PreferenceActivity
final String SETTING_PREFIX = "COLOR";
final SharedPreferences prefs = PreferenceManager.getDefaultSharedPreferences(context);
final String colorSetting = prefs.getString(SETTING_PREFIX, "");
// Save the setting of the current widget with widget ID as the postfix
final SharedPreferences.Editor prefEditor = prefs.edit();
prefEditor.putString(SETTING_PREFIX + String.valueOf(appWidgetId), colorSetting);
prefEditor.commit();
}
private void onLoadingWidgetSetting(Context context, int appWidgetId) {
// Load the setting of a particular widget given a widget ID
final String SETTING_PREFIX = "COLOR";
final SharedPreferences prefs = PreferenceManager.getDefaultSharedPreferences(context);
final String colorSetting = prefs.getString(SETTING_PREFIX + String.valueOf(appWidgetId), "");
// Use the loaded setting
// ....
}

maybe try to write your own preference system maybe on somewhere on filesys.
just suggesting

Related

How can I get a changenotifier using a futureprovider with riverpod?

I am using shared_preferences for my app and I have made a Settings class with helper methods.
As part of the Settings method I use Settings.create to generate my SharedPreferences.
It looks like this:
class Settings extends ChangeNotifier {
final SharedPreferences prefs;
Settings(this.prefs);
static Future<Settings> create() async {
var prefs = await SharedPreferences.getInstance();
return Settings(prefs);
}
// ### Helper Methods ###
}
I have used this post to try a solution and have come up with this:
FutureProvider<Settings> createSettings = FutureProvider<Settings>((ref) {
return Settings.create();
});
ChangeNotifierProvider<Settings> settingsProvider = ChangeNotifierProvider<Settings> ((ref) {
final settingsInstance = ref.watch(createSettings).maybeWhen(
data: (value) => value,
orElse: () => null,
);
return Settings(settingsInstance.prefs);
});
The problem I have now run into is that I get an Error because null is returned as long as the future has not completed.
I have hit a wall and am out of ideas. Does anyone have an idea on how to solve this?
Yeah, two options. Depending on the trade-offs you want to make.
Options
Option 1 (just wait)
In main.dart inside the Future<void> main() async {} function, just wait for the call to get shared prefs and then manually set the state in a your providers (using a state provider).
So that looks like:
(providers.dart)
final settingsProvider = StateProvider<Settings>((_) => defaultValue);
// ^ You can also make the above nullable if you don't have a reasonable default. But to be fair, this default will never be called if you're always setting it in main.
(main.dart)
Future<void> main() async {
final settings = await Settings.create();
final container = ProviderContainer();
container.read(settingsProvider.state).state = settings;
}
Option 2 (same idea, just don't wait)
The same as the above code, but don't wait in main. Just don't wait. Here's the difference:
(main.dart)
Future<void> main() async {
final container = ProviderContainer();
Settings.create().then((settings){
container.read(settingsProvider.state).state = settings;
});
// The rest of your code...
runApp();
}
Overview / Comparison
Option #1 is more simple to work with, but you may not be okay with waiting if you want a fast startup. But I doubt that it matters. In that case, if you have reasonable defaults, then use option #2
For the code on using Riverpod in main(), please reference this Github issue comment:
https://github.com/rrousselGit/riverpod/issues/202#issuecomment-731585273

How can I save widget's data to device in Flutter?

when a file add I want to do an easy way to access that file in my app. to do that I am using ListView.builder() widget. but I can't save the data in the app
I worked on the shared_preferences package. At least I don't know how I must do
I solved my problem with this block, I am using ListviewBuilder to create widgets so I needed an array. after this, I wanted to save this data on my device. I am using shared preferences but shared preferences only allow to add string array. then, I decoded all arrays to String array then, when I start the widget in initstate. I take data then, encode the array data then, add value to my variable.
SharedPreferences? sharedPref;
List<Map<String, dynamic>> documents = [];
List<String> documentsEncode = [];
#override
void initState() {
super.initState();
getInstance();
}
Future<void> getInstance() async {
final sharedPref = await SharedPreferences.getInstance();
documentsEncode = sharedPref.getStringList(widget.collection!) ?? [];
documentsEncodeToDecode();
// main();
}
documentsEncodeToDecode() {
documents.clear();
documentsEncode.forEach((documentEncode) {
Map<String, dynamic> document = jsonDecode(documentEncode);
documents.add(document);
});
setState(() {});
}

Whats the best practise for using variables in many different classes in Flutter?

Lets say a User in my App can note his weight. Now I want to use this weight in many other Widgets all over my App for example to calculate some data, which depends on the user weight.
Is it a good practise to use a static variable like this:
class UserManager {
static double weight;
}
So now I have access to the User weight in every Class and can make calculations for example:
double value = UserManager.weight * 0.4;
Is this a good practise or are there some better solutions?
You can use GetStorage()
final appData = GetStorage();
appData.writeIfNull("data", false);
bool yourVar = appData.read("data");
or SharedPreferences
var yourData;
final Future<SharedPreferences> _prefs = SharedPreferences.getInstance();
void _getSomeTh() async {
final prefs = await _prefs;
final result = prefs.getBool("data");
yourData = result;
}
It would be better not to work with static variables, but to have an object that you distribute to all widgets in the widget tree. You can achieve this, for example, with an InheritedWidget or with packages such as BLoC or Provider. Making such data static seems to me to be an anti-pattern.
With the BLoC pattern:
class User {
final double weight;
User({required this.weight});
}
class UserManager extends Cubit<User>{
...
}
This way you have a state managing system with which you could very easily rebuild all the widgets concerned when the weight changes.
For a simple requirement as this, the static variable approach might be fine. However, for a more complex object that you wish to share in a hierarchy of widgets, you should use packages like provider
For example, you have a User object that you wish to share app-wide, then
class User with ChangeNotifier {
late String uuid;
late String name;
// Our one and only one static instance on which we operate
static late User? instance;
// There is a good chance that the init functions like below are async too
static void initUser(String uuid, String name) {
instance = User._init(uuid, name);
}
// It is usually the 'instance' that calls the below function
void changeFunction(/*params*/){
// Change the user instance, say, assign it to null if a user logs out
// More importanly, notify the listeners about the change
notifyListeners();
}
// Note this constructor is not exposed to the public.
User._init(this.uuid,this.name);
}
Then propagate the User object using a ChangeNotifierProvider using the guidelines mentioned here

Provider not updating custom widget when value changes

My provider is used inside a custom widget that is used as ListTile inside the parent widget .
The provider is inside a stream that fetches data from a Firebase database, when the stream is triggered it stores the new data inside the provider as a Map.
The provider is called LastMessage and the Map is called messageMap, new data gets added using the function updateMap :
var messageInstance = Provider.of<global.LastMessage>(context, listen: false);
StreamSubscription<Event> updates;
// roomId is a string that is used as a key
updates = ref.child(RoomId).onChildAdded.listen((event) async{ // the stream works correctly and the new data gets stored
String _x = await getLastMessage();// a function that gets the data from the database
messageInstance.updateMap(RoomId, _x);
});
class LastMessage extends ChangeNotifier{
Map<String, String> messageMap = {};
void updateMap(String RoomID, String lastMessage){
messageMap[RoomID] = lastMessage;
notifyListeners();
}
Now the problem is that even after messageMap is updated the widget doesn't get rebuilt when listen is set to false, although it works when listen is true but it makes the whole app very slow
Widget will be rebuilt only if the listener is true.
If the listener is false then the widget will not be rebuilt when you call notifyListeners();
The solution was using StreamBuilder instead of provider to update the widget

How to display a dialog from ChangeNotifier model

In my project, when ChangeNotifier class receives a status it sets up a boolean and calls notifyListeners(). In my main build() function, I then check the pending-boolean and display the dialog accordingly - but I am only able to display the dialog by giving it a small delay in the build method - it seems the context is missing.
TL;DR:
Is there any way to display a dialog from within the ChangeNotifier class?
Even if you could do that by just passing a BuildContext, you shouldn't, because you'd be coupling your ChangeNotifier to specific cases only.
Let's say this is your model:
class Model extends ChangeNotifier {
bool _loading = false;
bool get loading => _loading;
void update(bool value) {
_loading = value;
notifyListeners();
}
}
And say, you're updating loading value on a button press using:
final model = Provider.of<Model>(context, listen: false);
model.update(true);
You should perform your logic here itself, or maybe you are listening to this model somewhere else in your project with:
final model = Provider.of<Model>(context);
You then should show dialog by checking:
if (model.loading) {
// show dialog
}