pass parameter to another component - null - flutter

Follow the excerpt that I send and the other that I receive the parameter. but it's coming null
Usuario usuario = Usuario();
usuario.email = email;
usuario.senha = senha;
//cadastrar ou logar
if( _cadastrar ){
print(usuario);
Navigator.of(context).pushNamed('/termos-aceite', arguments: usuario.email);
}else{
//Logar
_logarUsuario(usuario);
}
// second screen
Widget build(BuildContext context) {
setState(() {
args = ModalRoute.of(context).settings.arguments;
});
print('args');
print(args); //value is null

you need to send a map not a property
Usuario usuario = Usuario();
usuario.email = email;
usuario.senha = senha;
//cadastrar ou logar
if( _cadastrar ){
print(usuario);
Navigator.of(context).pushNamed('/termos-aceite', arguments: {"email":usuario.email});
}else{
//Logar
_logarUsuario(usuario);
}
when you try to get the values cast the arguments as Map of String Object like this and and use the property key you to get the data
setState(() {
args = ModalRoute.of(context).settings.arguments as Map<String, Object>;
});
print('args');
print(args["email"]); //value is email's value

There is a better and generic approach to do that, and I would suggest you to do that.
So we can do this via using the Constructor method, which accepts some variable with values, in your case this is the page, i.e., /termos-aceite. Now taking the Page name as Termos for this /termos-aceite. Let us see how this works.
class Termos extends StatelessWidget {
// Declare a field that holds the Todo.
final dynamic email;
// In the constructor, require a Todo.
DetailScreen({Key key, #required this.email}) : super(key: key);
//To use that, you can simply do
print(args);
setState(() {
args = this.email;
});
}
And In order to pass it to the page, in this case to Termos, we do this using your code only
Usuario usuario = Usuario();
usuario.email = email;
usuario.senha = senha;
//cadastrar ou logar
if( _cadastrar ){
print(usuario);
// Read about MaterialPageRoute() here
// https://api.flutter.dev/flutter/material/MaterialPageRoute-class.html
Navigator.push(context, MaterialPageRoute(
builder: (context) => Termos(email: email)
));
}else{
//Logar
_logarUsuario(usuario);
}
I hope you will get it what you want. Also, please keep googling these things about flutter. It is cool, and you will find great things there in the documentation itself. I am anyway listing down some of the links for you, hope that would be a learning experience for you. Keep learning
Flutter Dev Docs
Naivgate With Argument
Passing data to a new screen

Related

Flutter BLoC : how to update state with null values?

How do you deal with "accepted null values" when you update a state in BLoC ?
I use the flutter_bloc package.
I have a form in which numeric variables are nullable so that I can check their validity before the form is submitted.
But when I emit a new state, I use state.copyWith(var1?, var2?)... so when a null value is used to update a parameter, the value is not updated.
To face that I use a custom FieldStatus enum for each field. In my form submission, I can check the status of each field. But this is a bit verbose... and it needs to use 2 values instead of 1 for each field, which is not very satisfying.
I can also force the value to be null according to the new value of its FieldStatus, but it is a bit tricky and not very satisfying.
How would you manage such a case ?
Here is what I did :
States :
part of 'phhfgroup_bloc.dart';
class PhhfGroupState extends Equatable
{
final double? height;
final FieldStatus heightStatus;
const PhhfGroupState({this.height, this.heightStatus = FieldStatus.initial});
#override
List<Object?> get props => [height, heightStatus];
PhhfGroupState copyWith({double? height, FieldStatus? heightStatus})
{
return PhhfGroupState(
height: height ?? this.height,
heightStatus: heightStatus ?? this.heightStatus
);
}
}
Events :
part of 'phhfgroup_bloc.dart';
abstract class PhhfGroupEvent extends Equatable
{
const PhhfGroupEvent();
#override
List<Object> get props => [];
}
class HeightChanged extends PhhfGroupEvent
{
const HeightChanged({required this.height});
final String height;
#override
List<Object> get props => [height];
}
Handler :
import 'package:equatable/equatable.dart';
import 'package:flutter_bloc/flutter_bloc.dart';
import 'package:myapp/models/statuses.dart';
part 'phhfgroup_event.dart';
part 'phhfgroup_state.dart';
class PhhfGroupBloc extends Bloc<PhhfGroupEvent, PhhfGroupState>
{
PhhfGroupBloc() : super()
{
on<HeightChanged>(_mapHeightEventToState);
}
void _mapHeightEventToState(HeightChanged event, Emitter<PhhfGroupState> emit)
{
if(event.height.isEmpty)
{
emit(this.state.copyWith(
height: null,
heightStatus: FieldStatus.empty
));
}
else
{
double? height = double.tryParse(event.height);
if(height == null)
emit(this.state.copyWith(
height: null,
heightStatus: FieldStatus.nonnumeric
));
else emit(this.state.copyWith(
height: height,
heightStatus: FieldStatus.numeric
));
}
}
}
Thanks !
By using freeze, you could do as follow:
void main() {
var person = Person('Remi', 24);
// `age` not passed, its value is preserved
print(person.copyWith(name: 'Dash')); // Person(name: Dash, age: 24)
// `age` is set to `null`
print(person.copyWith(age: null)); // Person(name: Remi, age: null)
}
If you don't want to use another package, I suggest to add an argument for controlling nullable values.
class PhhfGroupState extends Equatable
{
final double? height;
final FieldStatus heightStatus;
const PhhfGroupState({this.height, this.heightStatus = FieldStatus.initial});
#override
List<Object?> get props => [height, heightStatus];
PhhfGroupState copyWith({double? height, FieldStatus? heightStatus, bool clearHeight = false})
{
return PhhfGroupState(
height: clearHeight == true ? null : height ?? this.height,
heightStatus: heightStatus ?? this.heightStatus
);
}
}
If you have a bunch of nullable fields, I would strongly recommend freeze, but for others, just add a flag for It.
Another way, you can use ValueGetter.
For example, password below:
#immutable
class LoginPageState {
const LoginPageState({
this.phone,
this.password,
});
final String? phone;
final String? password;
LoginPageState copyWith({
String? phone,
ValueGetter<String?>? password,
}) {
return LoginPageState(
phone: phone ?? this.phone,
password: password != null ? password() : this.password,
);
}
}
Set password to null:
void resetPassword() {
final newState = state.copyWith(password: () => null);
emit(newState);
}
You don't need any extra flag.
Thanks a lot because it is exactly what I need ! Avoid lots of boilerplate code to achieve this simple goal...
So to achieve what I need I will use :
freeze to help manage state / events
my FieldStatus enum to make some informations available in the view

Flutter Riverpod alternative to a _provider.when

Looking for an alternative to
#override
Widget build(BuildContext context) {
final _user = useProvider(MyUserProvider);
...
return _user.when(data: data, loading: loading, error: error)
for most of my components
I want to use sth like
final _user = useProvider(MyUserProvider);
// then
return Text(_user.name)
directly because the firebase user data stream is already in the tree and can never be null or have an error.
Can I use a ChangeNotifier to achieve this ?
How else can I achieve this ?
Something like this may work for you:
final authStream = StreamProvider<User?>((ref) => ref.watch(FirebaseAuth.instance).authStateChanges());
final user = Provider<User?>((ref) => ref.watch(authStream).data?.value);
Then:
final _user = useProvider(user);
return Text(_user?.name ?? '');
// or if you're sure the value is not null or your app relies on it being not null
return Text(_user!.name);
You can create a new provider which will depend from the precedent :
final connectedUserProvider = Provider<User>((ref) {
final userState = ref.watch(MyUserProvider.state);
return userState.maybeWhen(data: (User user) => user, orElse: () => throw "you should not be there");
});

Provider automatically gets updated when calling setState

I am new to Flutter and currently working with Providers. I am pulling some static array list from an api and saving it to the Provider. I am letting the user to select from this list and attach to the Item he is creating using the form.
So, Everytime the user tries to create a new Item, he/she should see the static list with the selection set to false.
But, the provider array variable gets automatically updated upon calling setState. Below is the Issue I'm facing..
main.dart
MultiProvider(
providers: [
ChangeNotifierProvider<Info1Class>(
create: (ctx) => Info1Class(),
),
ChangeNotifierProvider<Info2Class>(
create: (ctx) => Info1Class(),
),
],
child: MaterialApp(
And in my Stateful widget in the build method. I am getting the Provider Details like this.
screenArray.clear();
final t = Provider.of<Info1Class>(context, listen: false).dataArray;
screenArray.addAll(t);
Whenever I call setState to update the elements of screenArray, the provider data gets updated as well.
setState(() {screenArray[0].selected = true})
After setState(), if I print the Provider dataArray's first element, it showing as true.
print(Provider.of<Info1Class>(context, listen: false).dataArray[0].selected)
My Dependancies
provider: ^4.3.2+4
Is there a way to avoid the Provider data getting updated and only update the variable in my Stateful Widget ?
Please let me know if I am missing something.. Thanks for your help.
I tried Getx and Provider both for this problem, it's problem of referencing of object not the Provider or GetX,
I was coping reference of Objects, list or all data. to Solve this problem, I create clone of each object and then use it.
// COMMON
String? uid;
String? username;
String? fullName;
String? email;
num? timestamp;
List<ProfilePhoto>? photos;
List<String>? skills;
I add one object list and other simple String list
Clone class
MyUser.clone(MyUser? myUser) {
uid = myUser?.uid;
username = myUser?.username;
fullName = myUser?.fullName;
userType = myUser?.userType;
email = myUser?.email;
timestamp = myUser?.timestamp;
photos = ProfilePhoto.cloneList(myUser?.photos);
status = myUser?.status;
skills = [...(myUser?.skills ?? [])];
}
Constructor
MyUser({
this.uid,
this.username,
this.fullName,
this.email,
this.timestamp,
this.photos,
this.skills,)};
Photo class
class ProfilePhoto {
String? id;
String? title;
String? url;
File? file;
bool? isDefault;
ProfilePhoto({
this.id,
this.title,
this.url,
this.file,
this.isDefault,
});
ProfilePhoto.clone(ProfilePhoto profilePhoto) {
id = profilePhoto.id;
title = profilePhoto.title;
url = profilePhoto.url;
file = profilePhoto.file;
isDefault = profilePhoto.isDefault;
}
static List<ProfilePhoto>? cloneList(List<ProfilePhoto>? items) {
if (items == null) return [];
List<ProfilePhoto>? newItems = [];
for (ProfilePhoto item in items) {
newItems.add(ProfilePhoto.clone(item));
}
return newItems;
}
}
Screen layout
#override
void didChangeDependencies() {
super.didChangeDependencies();
final data = _userController.user;
MyUser? user = MyUser.clone(data);
}
void _onChangeDefault(int index) {
ProfilePhoto pp = _profilePhotos[index];
setState(() {
_profilePhotos.removeAt(index);
_profilePhotos.insert(0, pp);
});
}
this may be not a good or optimized solution, but this solve my problem of auto update data in state manager

Flutter - Retrieve MobX #action return value in screen load without click

I have MobX #action which successfully return a value. Wondering how to retrieve the value in initial screen load without click or tap anything.
abstract class _ProfileMobx with Store {
#observable
User user;
#observable
String name = '';
#action
Future getClientName() async {
SharedPreferences pref = await SharedPreferences.getInstance();
User user = User.fromJson(json.decode(pref.getString('userPref')));
name = user.name; //successfully return an username from this action
}
}
I hope I can get the username value and inserted automatically to text widget.
final ProfileMobx profileMobx = ProfileMobx();
class ProfileView extends StatelessWidget {
#override
Widget build(BuildContext context) {
return Scaffold(
body: Column(
children: [
Text(profileMobx.name)
],
),
);
}
}
Thanks !!!
Have you tried #computed?
Basically computed values are values that can be derived (computed) from the existing state or other observable/computed values.
I am not 100% sure about Dart, but in Javascript/Typescript it would look something like that:
class Profile {
#observable
user :User
#computed get name() {
return this.user ? this.user.name : '';
}
#action
getUser = async () => {
// .. your code to get user
this.user = await getUser();
// Don't need this line, just set the user, computed will be automatically recalculated
// name = user.name;
}
}
More info about computed here: https://mobx.js.org/refguide/computed-decorator.html
Or here, if you use Dart, I guess: https://pub.dev/packages/mobx#computed-observables

Passing Variables Between Two Classes in Flutter

I am making register and login pages in flutter and facing a problem as I want to use the same variables 'email' and 'password' declared inside class _MyHomePage in main.dart file
to another class SignupPage in signup.dart file.
I already imported the files but I can not use the values in both classes
class _MyHomePageState extends State<MyHomePage> {
int _counter = 0;
String _email = '';
String _password = '';
final formKey = new GlobalKey<FormState>();
FormType _formType = FormType.login;
bool validateAndSave() {
final form = formKey.currentState;
if (form.validate()) {
form.save();
return true;
// print('Form is Valid Email: $_email, Password: $_password');
}
return false;
}
You can pass the data when you navigate your screen in following way.
Navigator.push(
context,
MaterialPageRoute(
builder: (context) => SignUp(email: emailvariable,pass: passvariable),
),
in following way you can receive data
class SignUp extends StatefulWidget {
final email;
final pass;
SignUp({Key key,this.email,this.pass}) : super(key: key);
#override
_SignUpState createState() => _SignUpState();
}
now in state widget you can access email and pass variable as
widget.pass and widget.email
There are two approaches for that
Pass values through class constructor
If you don't want to go back and forth you can use this
Just in the second page use like this
class Register extends StatefulWidget {
Register({Key key, this.email, this.pass});
final String email;
final String pass;
#override
_RegisterState createState() => _RegisterState();
}
class _RegisterState extends State<Register> {
#override
Widget build(BuildContext context) {
print(widget.email);
print(widget.pass);
// to use state class values you need to use the widget as the parent class object
return Container(
);
}
}
To pass the values in constructor
Navigator.push(
context,
MaterialPageRoute(
builder: (context) => Register(email: email, pass: pass),
),
Store the values in global scope before routing to another page
If you have to route multiple times and even require these values further, you store the values in global file before routing to another page and access there
Make one file
global.dart
library my_project.global;
// set default values for the initial run
String email = '';
String pass = '';
To access the values
import 'global.dart' as global;
main() {
// import the global.dart file to access the variables across the application
global.email = 'xyz#email.com';
print(global.email);
}
If the other answers do not solve your issue, you can use the InheritedModel widget that's provided by Flutter. It allows you to share data between deeply nested widgets. The documentation is good and there's even a video from the Flutter team explaining how to use it: https://api.flutter.dev/flutter/widgets/InheritedModel-class.html