Use preferences from the user to calculate with - flutter

Don't understand all those explanations on the internet about SHARED PREFERENCES in Flutter and I am a little (little is not true) frustrated after searching and trying for three days. So I am calling out here for my hero.
I am working at my first app in Flutter. It is for calculate volume in weight users need for their project.
I have 4 screens, actived by a bottom-bar. 1 Screen is the Setting screen, where users can put their (1)mixing ratio, (2)the weight of the liquid and (3) the Boolean if they are measuring on weight(gr) of volume (ml). If they are using volume, option 2 is not necessary is then hidden.
I want them to fill that as their settings. These settings I need for the formula on one of the other pages. If they fill in nothing, I have default values. I don't want that they have to fill in these settings every time but just once. Or twice if they use another kind of liquid. :-)
I have tried the SQFLITE package but I think the package SHARED PREFERENCES is what I need. I have watched all those video's on YouTube, I have googled and read and tried a lot of items, but I still don't get it what if have to do and where I have put the code. It always seems that if I retype what the experts are typing that I am the only one who get failure.
YES, I AM A BEGINNER (You all were once) and I am struggling with this topic now.
My questions to you:
1. Is the package SHARED PREFERENCES the best solution for my app?
2. Where do I put the setting for writing the database.
3. How do I place a default value for the first time in this database.
4. Is it possible to set a value on the Setting screen, and get the value on another page, without using the Navigation (because I use a bottom-bar).
When you read to the end: THANK YOU!
import 'package:flutter/material.dart';
import 'constants.dart';
import 'package:shared_preferences/shared_preferences.dart';
import 'dart:async';
class SettingsPage extends StatefulWidget {
#override
SettingsPageState createState() => SettingsPageState();
}
class SettingsPageState extends State<SettingsPage> {
int mixingRatio = 50;
int liquidGravity = 115;
bool weight = true;
#override
void initState() {
super.initState();
}
Future<int> _getIntFromSharedPref() async {
final prefs = await SharedPreferences.getInstance();
final mixingRatio = prefs.getInt('mixingRatio');
if (mixingRatio == null) {
return 50;
}
return mixingRatio;
}
Future<void> _resetMixingRatio() async {
final prefs = await SharedPreferences.getInstance();
await prefs.setInt('mixingRatio', 50);
}
-----------------
onPressed: () {
setState(
() {
weight = true;
weightOrVolume = 'GR';
},
);
},

Answering your questions.
Is the package SHARED PREFERENCES the best solution for my app?
For saving settings, surely this is the best package.
Where do I put the setting for writing the database.
You can save the settings anywhere using SharedPreference instance like:
sharedPrefs.putInt("key", 10);
How do I place a default value for the first time in this database
You can get it using dart syntax ??
int savedValue = sharedPrefs.getInt("key") ?? 0; // default value will be 0
Is it possible to set a value on the Setting screen, and get the value on another page, without using the Navigation.
Yes, you can do it, by either making global variable or retrieving it using getInt on the current screen.

Related

Changes in Object from Second Screen is also Changing the Value in First Screen in flutter dart

A class Object passing from First Screen to the second Screen While on the second screen when the object changed its value, it's also changing the value on first screen.
Code of First Screen. (widget.templateModel is an object class that i am passing to the second screen)
Navigator.push(context,MaterialPageRoute(builder: (context) =>
EditEmojiTextTemplateScreen(templateModel: widget.templateModel,));
Code of Second Screen (On the second screen i am receiving the object and when i am changing the value of widget.templateModel it also changing the value on the first screen for a simple understandable code below i changed the value in initState while in the gif i am changing value in TextFormField)
class EditEmojiTextTemplateScreen extends StatefulWidget {
final TemplateModel templateModel;
EditEmojiTextTemplateScreen({
Key? key,
required this.templateModel,
}) : super(key: key);
#override
State<EditEmojiTextTemplateScreen> createState() =>
_EditEmojiTextTemplateScreenState();
}
class _EditEmojiTextTemplateScreenState
extends State<EditEmojiTextTemplateScreen> {
final SharedPreferences sharedPreferences = sl();
var txtNameController = TextEditingController();
var txtColorController = TextEditingController();
_EditEmojiTextTemplateScreenState();
#override
void initState() {
widget.templateModel.emoji[0].titleTwo = "kdfff"; //here i am changing the value and it also changing the value on first screen and i dont want this behavior of this object
super.initState();
}
Note: This is happening because of widget variable as mentioned in the documentation but i don't know how to prevent this behavior.
package:flutter/src/widgets/framework.dart
The current configuration.
A [State] object's configuration is the corresponding [StatefulWidget]
instance. This property is initialized by the framework before calling
[initState]. If the parent updates this location in the tree to a new
widget with the same [runtimeType] and [Widget.key] as the current
configuration, the framework will update this property to refer to the
new widget and then call [didUpdateWidget], passing the old
configuration as an argument.
Now I see what you are trying to do.
You could initialize a NEW istance of TemplateModel in the InitState of the second screen.
Then, set the new object's properties like this (or write a cleaner method to do that):
newObject.property1 = oldObject.property1;
newObject.property2 = oldObject.property2;
...
Once the user presses the save button, change oldObject's properties again, so that the first page updates.
You might want to take a look at state management to better understand how to approach this kind of problems.
As the other answer suggests, take a look at state management solutions.
Also keep the models immutable by creating them with final fields. Then to modify, create new instances via copyWith()
Please update you code after navigation.then method
template = snapshot.data;

Flutter textfield - when and how save data to a remote server?

I want to create a multiscreen / multipart form wizard, with each screen containing:
a question
several different controls (text, radio-buttons etc.) which allow him to answer the question
I want to save the state of the answers to a remote server.
For the radio buttons that's straight-forward (whenever the user changes it's value).
But what about text-fields?
I don't want to update the server with every character change, but on the other hand I don'w want to lose data if the user exit the screen or the app closes / goes to background...
What would be the best approach to save data from text-fields?
Can Flutter's Form help me with that?
As the comment suggests you can use a debouncer for that. Creating a debouncer is pretty easy:
import 'package:flutter/foundation.dart';
import 'dart:async';
class Debouncer {
final int delay;
late VoidCallback action;
Timer? _timer;
Debouncer({required this.delay});
run(VoidCallback action) {
if (_timer != null) {
_timer?.cancel();
}
_timer = Timer(Duration(milliseconds: delay), action);
}
cancel() {
_timer?.cancel();
}
}
And to use it define it somewhere and then do:
yourDebouncer.run(() async {sendData(...)})
I suggest you send your data in the onchange method of your TextField. This way your user data is saved and your server will certainly not be overwhelmed. Don't forget to cancel the Debouncer in your dispose method.
#override
void dispose() {
_d.cancel();
yourDebouncer.dispose();
}
You might want to have a look at this if you want more details on how to implement a Debouncer.

How to test browser url route for Flutter web?

I am working on testing how my navigator 2.0 setup handles url changes in the browser in flutter web.
The closest i have come to being able to test how my app handles url changes is to manually update state in the RouterDelegate by calling the setNewRoutePath with a config from the RouteInformationParser.
I would really like to test the navigator closer to the origin of the url change.
Any ideas and pointers would be appreciated.
My current code looks like this:
//Pass routeInformation to RouterInformationParser
RouteInformation selectShopRoute = RouteInformation(location: '/selectshop?token=321');
RouterConfig selectShopConfig = await app.myRouteParser.parseRouteInformation(selectShopRoute);
await app.myRouterDelegate.setNewRoutePath(selectShopConfig);
await tester.pumpAndSettle();
//Verify that navigator state is select shop
expect(app.myRouterDelegate.currentScreen, RouterEnum.selectshop);
//Verify that navigator token is set correctly
expect(app.myRouterDelegate.token, '321');
I had the same question and could not find a good approach. I came up with a way to test our code and wanted to share it to you.
Basically, we have a custom RouteInformationParser, in which a location is added only for the testing purpose.
class MyRouteInformationParser
extends RouteInformationParser<PageConfiguration> {
String? customPath; // only use for testing
#override
Future<PageConfiguration> parseRouteInformation(
RouteInformation routeInformation,
) async {
final location = customPath ?? routeInformation.location;
// Compute the configuration based on the location
return PageConfiguration()
}
}
In the widget test, we just create the route information parser and use it with the MaterialApp. Changing the customPath during testing has similar effect as changing the URL of the web browser.
final informationParser = MyRouteInformationParser();
informationParser.customPath = "my/expected/path";

How to save data in my settings menu in Flutter?

I was wondering how to save data locally in Flutter. I am creating a Settings menu in my app, and I have a draft made of that UI, but I want to save the user's preferences when they close that menu, but I have no idea how to accomplish that.
Do you know some tutorial to do so? I have searched in Youtube, but I have no idea how to search for it. I have only found UI tutorials and I don't want that.
A picture of my UI is (I want to save that boolean option).
I would appreciate any help you could give to me!
You should use shared_preferences package. It's very simple and it's for non-critical data like this! Here is an example how to use it:
class SettingsRepository {
final SharedPreferences preferences;
SettingsRepositoryImpl(this.preferences);
Future<int> getDifficulty() {
return preferences.getInt(Keys.difficulty) ?? 1;
// this 1 in the end is a default value
}
Future setDifficulty(int difficulty) {
return preferences.setInt(Keys.difficulty, difficulty);
}
}
To learn more go to https://pub.dev/packages/shared_preferences. I assume you want to call preferences.setBool
You can save your boolean option using shared preferences.
Future<Null> saveOption(bool isSelected) async {
final SharedPreferences prefs = await SharedPreferences.getInstance();
prefs.setBool('option', isSelected);
}
Then you can get the option from there.
Future<bool> getOption() async {
final SharedPreferences prefs = await SharedPreferences.getInstance();
return prefs.getBool('option');
}
You can read more from here.

How to return data when popping multiple screens?

I know I can return data to the previous screen by using
Navigator.pop(context, 'Value');
But in my case I need to pop multiple screens by using
Navigator.popUntil(context, ModalRoute.withName('/login'));
I wonder in this case how do I pass the data back to the corresponding widget?
Thanks in advance.
you can send DATA in few ways
as a Parameter
using Shared_Preferences
using Static Variables
Only for Current Session
if you just need the DATA for Current Session you can go for Static Variables
step 1 : Create a Class and have Static Variable in it.
class Globaldata{
static String value;
}
step 2 : Initialise variable by
Globaldata.value="some_value";
step 3 : use of variable
String assigned_value = Globaldata.value;
The flutter API does not have that feature and from this https://github.com/flutter/flutter/issues/30112 discussion, that feature is not on the table yet. A walkaround was suggested though using the Page API.
However, in my opinion, it is cleaner to use the provider package https://pub.dev/packages/provider as part of your app state management to keep the data you want and make it available to any screen of interest. Follow these steps to achieve that.
Add the provider to your pubspec.yaml. Check the link above to see detailed instructions.
Create a notifier class that extends ChangeNotifier class as shown below. ChangeNotifier class is part of the flutter API.
class MyDataProvider extends ChangeNotifier {
//define your private data field(s). I'm using int here.
int _mydata;
//define a getter
int get myData => _myData;
// define a setter
set myData(newData){
_myData = newData;
notifyListeners();
}
}
Wrap your uppermost widget (or the parent of the screens where you want to pass the data) with the provider and instantiate it. I'm using main here.
void main(){
runApp(
ChangeNotifierProvider(create: (context) => MyDataProvider()),
child: MyApp(),
)
}
Assuming you have five screens: Screen1, Screen2, ..., Screen5 and you want to navigate to screen5, do some operations and return to screen1 with some data. On 1st screen, define a local variable for myData and create an instance of your myDataProvider. When a button is pressed to start the navigation, wrap up the push navigation in an asynchronous call.
//Screen1
int currentLocalData = 78;
MyDataProvider myProvider = Provider.of<MyDataProvider>(context);
onPressed: () async {
//Assign localData to myData in the provider
myProvider.myData = currentLocalData; //calls the setter define in the provider.
await Navigator.push(context, MaterialPageRoute(
builder: (context) => Screen5()
));
//Retrieve myData from the provider and assign it to currentLocalData.
//This executes after navigating back from Screen5
currentLocalData = myProvider.myData;
}
Let assume in Screen5 you retrieved the data and added 100 to it. Your aim is to return to Screen1 and make use of the new data, i.e 178. Here you will instantiate the provider again in Screen5 and assign the value 178 to myData.
//Screen5
MyDataProvider myProvider = Provider.of<MyDataProvider>(context);
myProvider.myData += 100;
//Use navigation.popUntil
Navigation.popUntil(context, ModalRoute.withName('/Screen1'));
Say you have Screen A,Screen B, Screen C. If you want to pop till Screen A and pass some data. Here is what you have to do.
1. Navigate from Screen A to Screen B
Navigator.pushNamed(context, '/screenb')
.then((value) {
//you will get return value here
});
2. To pop till Screen A.
//add thi code in Screen C
var nav = Navigator.of(context);
nav.pop('refresh');
nav.pop('refresh');