Global variable in Dart/Flutter - flutter

i wrote an function to login user by rest api in flutter. I want to use response from post but i don't know how to export my variable into another file.
I want use userID, but i dont know how,
can somebody help me?
class LoginScreenState extends State<LoginScreen>{
makeLoginRequest(String email, password) async {
SharedPreferences sharedPreferences = await SharedPreferences.getInstance();
Map data = {
'email':email,
'password':password
};
var jsonResponse;
var url = 'http://10.0.2.2:80/user/login';
var response = await http.post(url, body:data);
if(response.statusCode == 200){
jsonResponse = json.decode(response.body);
int userID = jsonResponse['id'];//HERE
if(jsonResponse != null){
setState(() {
_isLoading = false;
});
sharedPreferences.setString("token", jsonResponse['token']);
Navigator.of(context).pushAndRemoveUntil(MaterialPageRoute(builder: (BuildContext context) => UserPage()), (Route<dynamic> route) => false);
}
}

In your UserPage use a variable to get the ID.
Example:
class UserPage extends StatefulWidget {
final int userId;
UserPage({#required this.userId});
#override
_UserPageState createState() => _UserPageState();
}
class _UserPageState extends State<UserPage> {
#override
Widget build(BuildContext context) {
return Container();
}
}
While navigating to UserPage after login pass the userID:
Navigator.of(context).pushAndRemoveUntil(MaterialPageRoute(builder: (BuildContext context) => UserPage(userId: userID)), (Route<dynamic> route) => false);
When you want to get the value of userId in UserPage, you can use it in following way: widget.userId

Related

Flutter getting value from provider show null

I have a simple controller like this
class UserController with ChangeNotifier {
UserData user = UserData();
UserData get userdata => user;
void setUser(UserData user) {
user = user;
print(user.sId);
notifyListeners();
}
login(data) async {
var response = await ApiService().login(data);
final databody = json.decode(response);
if (databody['success']) {
UserData authUser = UserData.fromJson(databody['data']);
setUser(authUser);
notifyListeners();
return true;
} else {
return false;
}
}
}
I am trying to just print it like this on both widget and in initstate function but values are showing null. I can see in set function value is not null.
print('id ${context.watch<UserController>().user.sId.toString()}');
print(
'id2 ${Provider.of<UserController>(context, listen: false).user.sId.toString()}');
I already have added
ChangeNotifierProvider(create: (_) => UserController()),
],
in main.dart in MultiProvider
Also on Tap of login button I am doing this
showLoader(context);
UserController auth = Provider.of<UserController>(
context,
listen: false);
var data = {
"userEmail":
emailController.text.trim().toLowerCase(),
"userPassword": passwordController.text.trim(),
};
auth.login(data).then((v) {
if (v) {
hideLoader(context);
context.go('/homeroot');
} else {
hideLoader(context);
Fluttertoast.showToast(
backgroundColor: green,
textColor: Colors.white,
msg:
'Please enter correct email and password');
}
});
Try to include this while naming is same,
void setUser(UserData user) {
this.user = user;
print(user.sId);
notifyListeners();
}
Follow this structure
class UserController with ChangeNotifier {
UserData user = UserData();
UserData get userdata => user;
void setUser(UserData user) {
this.user = user;
print(user.sId);
notifyListeners();
}
Future<bool> login(String data) async {
await Future.delayed(Duration(seconds: 1));
UserData authUser = UserData(sId: data);
setUser(authUser);
notifyListeners();
return true;
}
}
class HPTest extends StatelessWidget {
const HPTest({super.key});
#override
Widget build(BuildContext context) {
return Scaffold(
body: Consumer<UserController>(
builder: (context, value, child) {
return Text(value.user.sId);
},
),
floatingActionButton: FloatingActionButton(onPressed: () async {
final result = await Provider.of<UserController>(context, listen: false)
.login("new ID");
print("login $result");
;
}),
);
}
}

Flutter : TypeError: Cannot read properties of null (reading 'setString')

I want to make progress tracker like if the user passed level 1 level 1 I will send to the Map level 1 is true (Finished),
I don't want to use database so I tried Shared Preferences Package then I faced the Error That in the title
... if you have a better way to do it please write it
class CheckLvl extends StatelessWidget {
static SharedPreferences sharedPreferences;
Map<String , String> Check = {
'1':'true',
'2':'false',
'3':'false',
'4':'false',
};
String encoded ;
String encodedMap;
Map<String , String> decoded;
CheckLvl(){
encoded = jsonEncode(Check);
sharedPreferences.setString('State', encoded);
}
static init () async
{
sharedPreferences = await SharedPreferences.getInstance();
}
Future<bool> isComplete (String index) async {
encodedMap = sharedPreferences.getString('State');
decoded = jsonDecode(encodedMap);
print(decoded);
if (decoded[index]=='true')
return true;
}
void Done(String index)
{
encodedMap = sharedPreferences.getString('State');
decoded = jsonDecode(encodedMap);
decoded[index]='true';
}
It is possible to get null data while reading , you can do
Future<bool> isComplete (String index) async {
final String? data = sharedPreferences.getString('State');
return data=='true' ;
}
Better using FutureBuilder for future method like
class CheckLvl extends StatefulWidget {
#override
State<CheckLvl> createState() => _CheckLvlState();
}
class _CheckLvlState extends State<CheckLvl> {
SharedPreferences? sharedPreferences;
Map<String, String> Check = {
'1': 'true',
'2': 'false',
'3': 'false',
'4': 'false',
};
Future<void> init() async {
sharedPreferences = await SharedPreferences.getInstance();
}
String? encoded;
String? encodedMap;
Map<String, String>? decoded;
Future<bool> isComplete(String index) async {
encodedMap = sharedPreferences!.getString('State');
decoded = jsonDecode(encodedMap!);
print(decoded);
if (decoded?[index] == 'true') return true;
return false;
}
void Done(String index) async {
encodedMap = sharedPreferences!.getString('State');
decoded = jsonDecode(encodedMap!);
decoded?[index] = 'true';
}
late final prefFuture = init();
#override
Widget build(BuildContext context) {
return FutureBuilder(
future: prefFuture,
builder: (context, snapshot) {
if (snapshot.hasData) {
return Text("got data");
}
return CircularProgressIndicator();
},
);
}
}
class CheckLvl extends StatelessWidget {
static SharedPreferences? sharedPreferences;
Map<String, String> Check = {
'1': 'true',
'2': 'false',
'3': 'false',
'4': 'false',
};
String? encoded;
String? encodedMap;
Map<String, String>? decoded;
static Future<SharedPreferences> init() async {
return await SharedPreferences.getInstance();
}
Future<bool> isComplete(String index) async {
sharedPreferences ??= await init();
encodedMap = sharedPreferences!.getString('State');
decoded = jsonDecode(encodedMap!);
print(decoded);
if (decoded?[index] == 'true') return true;
return false;
}
void Done(String index) async {
sharedPreferences ??= await init();
encodedMap = sharedPreferences!.getString('State');
decoded = jsonDecode(encodedMap!);
decoded?[index] = 'true';
}
#override
Widget build(BuildContext context) {
throw UnimplementedError();
}
}

SharedPreference data in TextWidget

This is a login, that catch user data and write in the other pages, like his name, etc
I set sharedPreference here:
Future<bool> login() async {
SharedPreferences sharedPreferences = await SharedPreferences.getInstance();
SharedPreferences nome = await SharedPreferences.getInstance();
var email = _emailController.text;
var senha = _senhaController.text;
var auth = 'Basic ' + base64Encode(utf8.encode('$email:$senha'));
var url = Uri.parse("http://177.70.102.109:3005/autenticacao");
var resposta = await http.get(
url,
headers: (<String, String>{'authorization': auth}),
);
// List campos = [];
if (resposta.statusCode == 200) {
await sharedPreferences.setString(
'token', "Token ${jsonDecode(resposta.body)['token']}");
await nome.setString(
'nome', "${jsonDecode(resposta.body)['result'][0]['nome']}");
print(nome);
return true;
} else {
return false;
}
}
And i want to receive and pass the 'nome' to a TextWidget in another class.
In the other page you can write something like that:
class ExamplePage extends StatefulWidget {
const ExamplePage({Key? key}) : super(key: key);
#override
State<ExamplePage> createState() => _ExamplePageState();
}
class _ExamplePageState extends State<ExamplePage> {
final _controller = TextEditingController();
#override
void initState() {
initNome();
super.initState();
}
Future<void> initNome() async {
SharedPreferences sharedPreferences = await SharedPreferences.getInstance();
String _nome = sharedPreferences.getString("nome", "");
_controller.text = _nome;
}
#override
Widget build(BuildContext context) {
return Text(_controller.text)
}
}
To read the value in some other widget you can use
getString https://pub.dev/documentation/shared_preferences/latest/shared_preferences/SharedPreferences/getString.html
Implementation would be similar to this:
SharedPreferences sharedPreferences = await SharedPreferences.getInstance();
Text(sharedPreferences.getString("nome");
See this post for example:
Flutter Shared Preference in Text Widget

how to get values stored in Shared Preference in flutter and pass those to a function?

as per the question I have to call a function through which I am calling an API which will register the user and that will take parameters as per the value which stored in Shared Preference.
So below is the fuction.
api_service.dart
class ApiService{
registerUser(String fName, String mobileNo, String lName) async{
Dio dio = new Dio(options);
FormData formData = FormData.fromMap({
'first_name' : fName,
'mobile_no' : mobileNo,
'last_name' : lName,
'source' : 'app'
});
Response response = await dio.post("user/", data: formData);
print(response.data);
}
}
and below is the page where I have stored all the formdata in Shared Preference
register.dart
class RegisterPage extends StatefulWidget{
#override
_RegisterPageState createState() => _RegisterPageState();
}
class _RegisterPageState extends State<RegisterPage>{
ApiService service = ApiService();
final GlobalKey<FormState> _formKey = new GlobalKey<FormState>();
var _key = new GlobalKey<ScaffoldState>();
UserData _data = UserData();
final setPadding = EdgeInsets.all(10.0);
submit() async{
if( this._formKey.currentState.validate()){
_formKey.currentState.save();
Map<String, dynamic> map = {
'first_name' : _data.firstName,
'last_name' : _data.lastName,
'mobile_no' : _data.mobileNo,
'email' : _data.email,
'source' : 'app',
};
var user_obj = json.encode(map);
SharedPreferences preferences = await SharedPreferences.getInstance();
preferences.setString('user_data', user_obj);
Navigator.pushNamed(context, '/otp', arguments: '${_data.mobileNo}');
/* here to pass a mobile number to otp page, I had used Navigator
because from otp page I had to call verifyOtp(). Similarly how can I
pass the Shared Preference values to otp page because again I have
to call registerUser() which requires all those Shared Preference
values. Don't know I am correct or not! */
print(_data.firstName);
print(_data.lastName);
print(_data.email);
print(_data.mobileNo);
}
else{
print('invalid credentials');
}
}
router.dart
class RouteGenerator {
static Route<dynamic> generateRoute(RouteSettings settings) {
switch (settings.name) {
case '/':
return MaterialPageRoute(builder: (_) => SplashScreenPage());
case '/register':
return MaterialPageRoute(builder: (_) => RegisterPage());
case '/login':
return MaterialPageRoute(builder: (_) => LoginPage());
case '/otp':
var mobNumber = settings.arguments as String;
var fName = settings.arguments as String;
var lName = settings.arguments as String;
return MaterialPageRoute(builder: (_) => OtpPage(mobNumber, fName, lName));
case'/home':
return MaterialPageRoute(builder: (_) => HomePage());
default:
return MaterialPageRoute(
builder: (_) => Scaffold(
body: Center(
child: Text('No route defined for ${settings.name}')),
));
}
}
}
this is the page where I am calling that function
otp_page.dart
class OtpPage extends StatelessWidget {
String mobNumber, fName, lName;
// String fName;
// String lName;
String formattedMobNo ;
OtpPage(this.mobNumber, this.fName, this.lName){
formattedMobNo = this.mobNumber.replaceRange(2, 8, 'XXXXXX');
}
ApiService service = ApiService();
final GlobalKey<FormState> _formKey = new GlobalKey<FormState>();
var _key = new GlobalKey<ScaffoldState>();
otpDecoration(double height, double width, Color color) {
return PinTheme(
shape: PinCodeFieldShape.box,
borderRadius: BorderRadius.zero,
fieldHeight: height,
fieldWidth: width,
inactiveColor: color,
activeColor: color,
selectedColor: color);
}
TextEditingController pinController = TextEditingController();
submit() {
// _formKey.currentState.save();
print(pinController.text);
print(mobNumber);
print(fName);
print(lName);
service.verifyOtp(mobNumber, pinController.text, _key);
service.registerUser(fName, mobNumber, lName);
}
can anybody please tell me how do I get those values and pass it to the function for calling an API?
actually the flow is on register page, after filling the fields and clicking on submit it will generate an otp and navigate to otp page. On otp page otp will be received and by clicking the proceed button it will verify the received otp and call the registerUser() which will call my API.
Your registerUser method is async so you can
registerUser(String fName, String mobileNo, String lName) async{
Dio dio = new Dio(options);
SharedPreferences prefs = await SharedPreferences.getInstance();
String info = await prefs.getString('user_data');
Map map= json.decode(info);
FormData formData = FormData.fromMap({
'first_name' : map['first_name'],
'mobile_no' : map['mobile_no'],
'last_name' : map['last_name'],
'source' : 'app'
});
Response response = await dio.post("user/", data: formData);
print(response.data);
}
and decode info string in this method
if you want to pass data while using route generator you need to create model such as
class User {
String first_name;
String last_name;
String mobile_no;
User({this.first_name,this.last_name,this.mobile_no});
}
then you need to add this to your route generator
final args = settings.arguments;
case '/otp':
if (args is User) {
return MaterialPageRoute(
builder: (_) => OtpPage(
user: args,
),
);
}
your otp page needs to be like that
class OtpPage extends StatelessWidget {
User user;
.
.
.
}
and your navigator must be like that
SharedPreferences prefs = await SharedPreferences.getInstance();
String info = await prefs.getString('user_data');
Map map= json.decode(info);
User user = User(first_name:map['first_name'],mobile_no:map['mobile_no'],last_name: map['last_name']);
Navigator.pushNamed(context, '/otp', arguments: user);
As you can see in the shared_preferences dependency documentation:
You can get one or more value of your shared_preference by using multiples methods depending of your value type.
SharedPreferences preferences = await SharedPreferences.getInstance();
preferences.getBool("yourKey");
preferences.getString("yourKey");
preferences.getInt("yourKey");
preferences.getDouble("yourKey");
// ...
// You can also use the dynamic getter:
preferences.get("yourKey");
Since your set your value with a .setString('user_data', user_obj); method, you should use
SharedPreferences prefs = await SharedPreferences.getInstance();
String storedString = prefs.getString('user_data');

Correct way to call an api by provider in fflutter?

I have been trying to make a app in flutter where an api is called and data is updated in TextField
Used provider for state management, here is the code for it.
class ProfileProvider with ChangeNotifier {
var profileData;
String _url = "http://10.0.2.2:3000/api/v1/user/loggedin_user";
void getData() async {
SharedPreferences prefs = await SharedPreferences.getInstance();
var token = prefs.getString('token');
var data = await http.get(
_url,
headers: {
"accept": "application/json",
"content-type": "application/json",
'Token': token,
},
);
var infoOfPerson = json.decode(data.body);
profileData = new ProfileObject(
name: infoOfPerson['name'],
mobile: infoOfPerson['mobile'],
email: infoOfPerson['email'],
role: infoOfPerson['role'],
);
notifyListeners();
}
ProfileObject get profileInfo {
return profileData;
}
}
I am getting the data fine, now i have to show it in the UI, but sometime data is populated, sometime its not. Can someone please point me the right direction why this is happening.
Here is the code for UI.
class Profile extends StatefulWidget {
#override
_ProfileState createState() => _ProfileState();
}
class _ProfileState extends State<Profile> {
final emailController = TextEditingController(text: '');
final nameController = TextEditingController(text: '');
final mobileController = TextEditingController(text: '');
var _isInit = true;
#override
void didChangeDependencies() {
if (_isInit) {
final profileData = Provider.of<ProfileProvider>(context);
profileData.getData();
if (profileData.profileInfo != null) {
emailController.text = profileData.profileInfo.name;
nameController.text = profileData.profileInfo.email;
mobileController.text = profileData.profileInfo.mobile;
}
_isInit = false;
super.didChangeDependencies();
}
}
#override
Widget build(BuildContext context) {
final profileData = Provider.of<ProfileProvider>(context);
return Scaffold(
drawer: NavigationDrawer(),
body: profileData.profileInfo == null
? Center(
child: CircularProgressIndicator(),
)
: Builder(
builder: (context) => SingleChildScrollView(
child: Padding(.....
Below the padding, there is normal TextField, can someone tell me why the data is being populated sometime and sometime its coming empty, even I wrapped it with CircularProgressIndicator() and a check the notifyListeners(); is not working there. The loader is not being shown and data is not being loaded.
Thanks
for StatelessWidget.
Inside the build method use:
Future.microtask(() async {
context.read<SomeProvider>().fetchSomething();
});
For StatefulWidgets if you want to call it once. Do this inside the initState() or didChangeDependencies (better if the latter). This will be called at the end of the frame which means after the build or rendering finishes..
void initState() {
super.initState();
WidgetsBinding.instance.addPostFrameCallback((_) {
context.read<SomeProvider>().fetchSomething();
});
}
EDIT: WidgetsBinding will also work on build. I forgot on why I used microtask lol
i've created a function which called nextTick, i call it in initState and it works for now, but want to see others method
void nextTick(Function callback, [int milliseconds = 0]) {
Future.delayed(Duration(milliseconds: 0)).then((_) {
callback();
});
}
then use it like below
#override
void initState() {
super.initState();
nextTick((){
ProfileProvider profileProvider = Provider.of<ProfileProvider>(context);
profileProvider.getProfile();
});
}
Edit: i store couple of variables to manage them on ui, like isLoading, hasError and errorMessage. Here is my provider class
class ProfileProvider extends ChangeNotifier {
bool _hasError = false;
bool _isLoading = true;
String _errorMsg = '';
Profile _profileResponse;
bool get hasError => _hasError;
bool get isLoading => _isLoading;
String get errorMsg => _errorMsg;
Profile get profileResponse => _profileResponse;
Future<void> getProfile() async {
this.setLoading = true;
this.setError = false;
this.setErrorMsg = '';
try {
await dio.post('$api/p/view', data: {}).then((res) {
print(res.data);
_profileResponse = Profile.fromJson(jsonDecode(res.data));
print(_profileResponse.data);
notifyListeners();
}).whenComplete(() {
this.setLoading = false;
});
} catch (e) {
this.setError = true;
this.setErrorMsg = '$e';
}
this.setLoading = false;
}
set setError(bool val) {
if (val != _hasError) {
_hasError = val;
notifyListeners();
}
}
set setErrorMsg(String val) {
if (val != null && val != '') {
_errorMsg = val;
notifyListeners();
}
}
set setLoading(bool val) {
_isLoading = val;
notifyListeners();
}
}