Passing Variables Between Two Classes in Flutter - class

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

Related

How to initialized Set variable in dart?

I use dart 2.13.3 . In my flutter app I can't initialized Set variable without required annotation.I want to build constructor without final Set<V>initialSelectedValues;
or without required, because from another call this parameter is just optional. But show error and suggest me to add required annotation at the front of final Set<V>initialSelectedValues;. How to I change and solve this error?
class MultiSelectDialog<V> extends StatefulWidget {
MultiSelectDialog({required this.items,this.initialSelectedValues});
final List<MultiSelectDialogItem<V>> items;
final Set<V>initialSelectedValues;
#override
State<StatefulWidget> createState() => _MultiSelectDialogState<V>();
}
final selectedValues = await showDialog<Set<int>>(
context: context,
builder: (BuildContext context) {
return MultiSelectDialog(
items: items,
//initialSelectedValues: [1,2].toSet(),
);
},
);
Now, I can solve this. Change to default parameter with const .
MultiSelectDialog({required this.items,this.initialSelectedValues = const {}});
final List<MultiSelectDialogItem<V>> items;
final Set<V> initialSelectedValues;
Every final variable must have a value, if you want to set them through the constructor you have to put "required" in front of every parameter.
MultiSelectDialog({
required this.items,
required this.initialSelectedValues });

how to navigate data between two screens in flutter?

I have homeScreen, which every another screen returns to it.
and one of the screens have parameters from TextField, which I need in the homeScreen, how I can navigate these parameters to the home?
NOTE: when I use constructor, the other screens show an error in the line that navigate to Home because there is no parameters.
You can either use an optional parameter in your constructor:
Homepage({String textfield});
and use it on your Homepage (don't forget that this value is nullable)
Or you need to use some kind of state management with ValueNotifiers
You can pass data using parameters
class XYZ extends StatelessWidget {
final TextEditingController textController = TextEditingController();
void navigationFunction() {
//send the data from XYZ to HomePage
Navigator.push(
context,
MaterialPageRoute(
builder: (context) => HomePage(textValue: textController.text)),
);
}
}
class HomePage extends StatelessWidget {
const HomePage({this.textValue});
final String textValue; //use this textValue in HomePage
#override
Widget build(BuildContext context) {
return Scaffold();
}
}

How can I pass variable id to second screen in flutter?

I have two page and I want to use the variable 'id' in the second screen to fetch data from API.
What should I do?
Screen one: it's the product screen where user click on profile image and after that I get all information about user owner in the second screen.
Screen two: I display data for this user by id
NB: I get all the data by API
id is always Null
Screen one:
InkWell(
onTap: () {
Navigator.push(
context,
MaterialPageRoute(
builder: (context) => UserProfile(
id: id,
)),
);
// do something here
},
),
Screen two:
class UserProfile extends StatefulWidget {
final int id;
const UserProfile({Key key, #required this.id}) : super(key: key);
#override
_UserProfileState createState() => _UserProfileState();
}
class _UserProfileState extends State<UserProfile> {
#override
void initState() {
getprofile(id);
super.initState();
}
Future<List<dynamic>> getprofile(int id) async {
var response = await Network().getData('/auth/user/$id');
data = json.decode(response.body);
return data;
}
When you want to use a property from the StatefulWidget you need to use widget.propertyName. In your case it's widget.id
class _UserProfileState extends State<UserProfile> {
#override
void initState() {
getprofile(widget.id);
super.initState();
}
Future<List<dynamic>> getprofile(int id) async {
var response = await Network().getData('/auth/user/$id');
data = json.decode(response.body);
return data;
}
Either do the same that you did before,so pass the id as a parameter to the _UserProfileState class, so just call:
_UserProfileState(#required this.id) : super();
Another option to make variables available is to use the Provider widget

Storing certain value in Widget build / Flutter

I've a question:
In my Widget build(BuildContext context), I want to store a certain value,
final userName = book.owner
(book is the reference to the certain value from Firestore)
But it's done not in the right way to my lack of knowledge. I'd appreciate if someone could guide through that.
Thank you in advance!
Snippet of my code
class BookView extends StatefulWidget {
final Book book;
BookView({Key key, #required this.book}) : super(key: key);
DatabaseMethods databaseMethods = new DatabaseMethods();
var userName;
#override
_BookViewState createState() => _BookViewState(book);
}
class _BookViewState extends State<BookView> {
Book book;
_BookViewState(this.book);
String userName;
#override
void initState() {
userName = book.owner;
super.initState();
}
// final Book book;
createChatroomAndStartConversation({var userName}) {
if (userName != Constants.myName) {
String roomId = getChatRoomId(userName, Constants.myName);
List<String> users = [userName, Constants.myName];
Map<String, dynamic> chatRoomMap = {
"Users": users,
"roomId": roomId,
};
DatabaseMethods().createChatRoom(roomId, chatRoomMap);
Navigator.push(
context,
MaterialPageRoute(
builder: (context) => ConversationScreen(roomId, userName)),
);
} else {
print("You cannot send msg to your self");
}
}
#override
Widget build(BuildContext context) {
//widget.book;
return Scaffold(
resizeToAvoidBottomInset: false,
appBar: AppBar(
...
FlatButton(
child: Text(
"Get contact with",
style: TextStyle(color: Colors.white),
),
color: Colors.blue,
onPressed: () {
createChatroomAndStartConversation(
userName: userName);
...
}
Snippet of Value not in range: 1
getChatRoomId(String a, String b) {
if (a.substring(0, 1).codeUnitAt(0) > b.substring(0, 1).codeUnitAt(0)) {
return "$b\_$a";
} else {
return "$a\_$b";
}
}
It's not a good practice to store any data in build() method, because this method is invoked too many times to do the such kind of move. Consider using StatefulWidget to store any state you have in the widget, for the very beginning. When you use this widget, you can define this all in such way:
class YourWidget extends StatefulWidget {
#override
_YourWidgetState createState() => _YourWidgetState();
}
class _YourWidgetState extends State<YourWidget> {
String userName;
#override
void initState() {
userName = book.owner;
super.initState()
}
#override
Widget build(BuildContext context) {
return Container(child: Text(userName),);
}
}
Here, in initState() you can retrieve value from book and set it to userName. But for more complex and bigger applications, consider using StateManagement solutions and some kind of architectural patterns i.e. Riverpod, Provider, MobX, BLoC.. Because changing the state via setState() method will cause rebuilding whole child widget tree, which could freeze whole UI in complex app.
UPD to 'Snippet of my code':
According to your code, if you are using a 'book' from Widget, not its state - use widget.book, in such way you have access to widget members, because of this you don't need a constructor of state. So, due to these changes, your code might looks like:
class BookView extends StatefulWidget {
final Book book;
BookView({Key key, #required this.book}) : super(key: key);
// You DON'T need this here, because you are retrieving these methods
// inside your state via DatabaseMethods constructor
DatabaseMethods databaseMethods = DatabaseMethods();
#override
_BookViewState createState() => _BookViewState(book);
}
class _BookViewState extends State<BookView> {
String userName;
#override
void initState() {
// Using widget.book to retrieve Book object from state's widget
userName = widget.book.owner;
super.initState();
}
createChatroomAndStartConversation({var userName}) {
if (userName != Constants.myName) {
String roomId = getChatRoomId(userName, Constants.myName);
// Also, it's just a recommendation, try to omit local variables types
// because they are already known with List type (String). Also, this
// all is about chatRoomMap
var users = <String>[userName, Constants.myName];
final chatRoomMap = <String, dynamic>{
"Users": users,
"roomId": roomId,
};
DatabaseMethods().createChatRoom(roomId, chatRoomMap);
Navigator.push(
context,
MaterialPageRoute(
builder: (context) => ConversationScreen(roomId, userName)),
);
} else {
print("You cannot send msg to your self");
}
}
#override
Widget build(BuildContext context) {
// your widgets here
}
}
UPD 2:
Second trouble and issue with 'Snippet of Value not in range: 1'. I could to reproduce it with given value of 'a' as empty string. So, your function invocation is like getChatRoomId('', 'user123'), because of empty 'userName', substring function can't take values from range [0, 1), so exception is raised.

SignalR client flutter library shows null values in another widget when using Flutter Provider

Am trying to connect on a SignalR server with flutter, and I tried to use Provider to access the data which comes to another Widget, but am receiving a null value. This is the SignalR library for flutter am using.
Below is my Class which extends a ChangeNotifier :
class BreakingNewsSignalRClient with ChangeNotifier {
String _breakingNews;
Future<void> getBreakingNews() async {
final hubConnection = HubConnectionBuilder()
.withUrl('https://services.xxxx.com/breakingNews')
.build();
await hubConnection.start();
hubConnection.on("UpdateBreakingNews", (data){
_breakingNews = data.toString();
print(data.toString());
notifyListeners();
});
hubConnection.onclose((error) => print("Connection Closed"));
}
String get breakingNewsFunction => _breakingNews;
}
class BreakingNewsModel {
final String News;
BreakingNewsModel(this.News);
}
As you can see the code above, am having a getter String get breakingNewsFunction => _breakingNews;, this is called in another Stateful Widget to populate some data from the SignalR server, but it returns null in the other widget, however, when you try to print data here in the getBreakingNews method the data is shown.
Below is another Widget class which receives the data :
class BreakingNews extends StatefulWidget {
const BreakingNews({Key key, #required this.article, #required this.index})
: super(key: key);
final Article article;
final int index;
#override
_BreakingNewsState createState() => _BreakingNewsState();
}
class _BreakingNewsState extends State<BreakingNews> {
settings() async{
var breakingNewsInfo =
Provider.of<BreakingNewsSignalRClient>(context, listen: false);
await breakingNewsInfo.getBreakingNews();
print('lets see -- ${breakingNewsInfo.breakingNewsFunction}');
}
#override
void initState() {
// TODO: implement initState
settings();
super.initState();
}
}
So when you look at this line print('lets see -- ${breakingNewsInfo.breakingNewsFunction}');, it prints null, am still wondering what am doing wrong here.
Kindly need some help.
Did you try data[0]?
Can you write it?
_breakingNews = data[0].toString();
instead of
_breakingNews = data.toString();
This is pretty simple. Your Future<void> getBreakingNews() async returns void and therefore null. Just adjust the return type to whatever you need.
Edit:
Actually, the problem is you are not calling your getter here.
await breakingNewsInfo.getBreakingNews();
So either return your result from the function, or call the getter. Either should work.