How to transfer information from one class to another in Flutter? - flutter

I just wanted to know how to pass data from one class to another in Flutter.
Like suppose, I want to transfer the text field data that is in the first class, and then pass it onto another class (which is in another file, but same folder) to display it.
Please help...

Data is held in objects, not in classes, unless it is defined as "static". To transfer data, you can do it by methods, constructors, fields, etc..
For example:
class A {
String data;
}
class B {
String data;
static String static_data;
}
somewhere in the code you have objects:
var a = A();
var b = B();
To tranfer the data you can use the public field directly:
b.data = a.data;
or in the static case:
B.data = a.data;
In Flutter, you typically have Widget with TextField. To access the text in it, use TextEditingController. Then open the other widget with constructor passing value:
class _Widget1State extends State<Widget1> {
final text_ctrl = TextEditingController();
Widget build(BuildContext context) {
return Column(children: [
TextField(controller: text_ctrl),
ElevatedButton(onPressed: () => Navigator.pushReplacement(
context, MaterialPageRoute(builder: (context) => Widget2(text_ctrl.text)));)
]);
}
}
and the Widget2 will take it like this:
class Widget2 extends StatelessWidget {
final String text;
Widget2 (this.text);
#override
Widget build(BuildContext context) {
return Text(text);
}
}

If you want to pass from one class to another you can do something like this,
Example:
Here is the first class given below which will have a TextField
class ExampleClassOne extends StatelessWidget {
final textEditingController = TextEditingController();
#override
Widget build(BuildContext context) {
return Scaffold(
body: Container(
height: MediaQuery.of(context).size.height,
width: MediaQuery.of(context).size.width,
child: Column(
children: [
TextField(
controller: textEditingController,
),
ElevatedButton(
onPressed: () {
Navigator.push(
context,
MaterialPageRoute(
builder: (_) => ExampleClassTwo(
text: textEditingController.text,
)));
},
child: Text("Enter")),
],
),
),
);
}
}
Here is the class which will receive the text value. It will receive using it's constructor.
class ExampleClassTwo extends StatelessWidget {
final String text;
const ExampleClassTwo({this.text});
#override
Widget build(BuildContext context) {
return Scaffold(
body: Container(
height: MediaQuery.of(context).size.height,
width: MediaQuery.of(context).size.width,
child: Text(text),
),
);
}
}

Related

Stateful widget inside an Provider (ChangeNotifier) widget does not get updated

I have a Stateless-Provider widget along with its ChangeNotifier-model. Inside the Provider, there is a Stateful widget. When notifyListeners is called, all widgets in the stateless widget get updated, except the Stateful one. What am I missing here, and how do I go about it? Providing a simplified example here: Upon pressing the button, the expected result is First: The value is 1st, but the actual output is First: The value is 2nd
import 'package:flutter/material.dart';
import 'package:provider/provider.dart';
class Model extends ChangeNotifier {
final List<ListElement> elements;
Model({required this.elements});
void add() {
elements.insert(0, ListElement(name: "First", value: "1st"));
notifyListeners();
}
}
class ListElement {
final String name;
var value;
ListElement({required this.name, required this.value});
}
class ValueWidget extends StatefulWidget {
final String value;
ValueWidget({required this.value});
#override
State<StatefulWidget> createState() => _ValueWidget(value: value);
}
class _ValueWidget extends State<ValueWidget> {
String value;
_ValueWidget({required this.value});
#override
Widget build(BuildContext context) {
return Text("The value is ${value}.");
}
}
class StatelessPage extends StatelessWidget {
final model = Model(elements: [
ListElement(name: "Second", value: "2nd"),
ListElement(name: "Third", value: "3rd")]);
#override
Widget build(BuildContext context) {
return Scaffold(
body: ChangeNotifierProvider(
create: (context) => model,
child: ConsumerWidget())
);
}
}
class ConsumerWidget extends StatelessWidget {
#override
Widget build(BuildContext context) {
return Consumer<Model>(builder: (context, model, _) {
return SingleChildScrollView(
child: Container(
padding: EdgeInsets.fromLTRB(10, 30, 10, 10000),
child: Column(
children: [Column(
children: model.elements.map((element) {
return Row(children: [
Text("${element.name}: "),
ValueWidget(value: element.value)
]);
}).toList(),
),
TextButton(onPressed: model.add,
child: Text("Add element to beginning")),
],
),
),
);
});
}
}
Please consider that this is simplified version of my production code, and changing the whole Provider class to a Stateful one would be difficult.
Edit: Thanks Aimen for showing the path. What finally worked was using only the index of the list elements in the Stateful wiget (ValueWidget). And fetch the data from the model. I think the reason for this is that if the Stateful-widget in independece is not affected, it will not rebuild. We need to affect the build part of the widget. Pasting the changed working code.
import 'package:flutter/material.dart';
import 'package:provider/provider.dart';
class Model extends ChangeNotifier {
final List<ListElement> elements;
Model({required this.elements});
void add() {
elements.insert(0, ListElement(name: "First", value: "1st"));
notifyListeners();
}
}
class ListElement {
final String name;
var value;
ListElement({required this.name, required this.value});
}
class ValueWidget extends StatefulWidget {
final int ind;
final Model model;
ValueWidget({required this.ind, required this.model});
#override
State<StatefulWidget> createState() => _ValueWidget(
ind: ind, model: model);
}
class _ValueWidget extends State<ValueWidget> {
final int ind;
final Model model;
_ValueWidget({required this.ind, required this.model});
#override
Widget build(BuildContext context) {
// Can also use Provider like this so that it does not need to be passed
// final model = Provider.of<Model>(context, listen: true);
// This is the part because of which widget is getting rebuilt
final element = model.elements[ind];
return Text("The value is ${element.value}.");
}
}
class StatelessPage extends StatelessWidget {
final model = Model(
elements: [
ListElement(name: "Second", value: "2nd"),
ListElement(name: "Third", value: "3rd")]
);
#override
Widget build(BuildContext context) {
return Scaffold(
body: ChangeNotifierProvider(
create: (context) => model,
child: ConsumerWidget())
);
}
}
class ConsumerWidget extends StatelessWidget {
#override
Widget build(BuildContext context) {
return Consumer<Model>(builder: (context, model, _) {
return SingleChildScrollView(
child: Container(
padding: EdgeInsets.fromLTRB(10, 30, 10, 10000),
child: Column(
children: [Column(
children:
model.elements.asMap().entries.map((ele) {
return Row(children: [
Text("${ele.value.name}: "),
ValueWidget(ind: ele.key, model: model),
]);
}).toList(),
),
TextButton(onPressed: model.add,
child: Text("Add element to beginning")),
],
),
),
);
});
}
}
you are not implementing provider in the stateful widget you are just passing a value through a parameter you need to call a provider and set the listen to true
inside the statful widget
like
var model = Model.of(context, listen = true);
List elements = model.elements;
here the elements variable will change when the elements in the provider will have a new value

Reusing a Widget with Setters

New to Dart/Flutter and unsure if this is the proper way of going about this, but I want to make a class for an ElevatedButton widget that I can use over-and-over and only need to set the text and callback (onPressed) for each instance of ElevatedButton.
For now I'm just trying to get to the point where I can make a list of Widgets (ElevatedButtons) where I set each button text, but am struggling. This is what I have:
class AElevatedButton extends StatefulWidget
{
AElevatedButton({Key? key}) : super(key:key);
#override
State<AElevatedButton> createState() => ElevatedButtonState();
}
class ElevatedButtonState extends State<AElevatedButton>
{
String buttonText = "Button";
void setText(String buttonText) {
setState(() {
this.buttonText = buttonText;
});
}
#override
Widget build(BuildContext context) {
return Container(
margin: const EdgeInsets.all(5),
child: ElevatedButton(
onPressed: null,
child: Text(buttonText)
)
);
}
}
In my "home page" State I have below but am not able to access the "setText" method in ElevatedButtonState, which somewhat makes sense as I'm creating a ElevatedButton object and not a ElevatedButtonState. Unsure if there is a way to get the state so I can call the method?
class _MyHomePageState extends State<MyHomePage>
{
AElevatedButton firstButton = new AElevatedButton();
AElevatedButton secondButton = new AElevatedButton();
void initiateButtons()
{
firstButton.setText("Button 1"); <---- error
secondButton.setText("Button 2"); <---- error
}
#override
Widget build(BuildContext context) {
initiateButtons();
return Scaffold(
appBar: AppBar(
title: const Text("Test Buttons")
),
body:
Column(
children: <Widget>[
firstButton,
secondButton
])
);
}
}
It seems that what you're making is more or less a wrapper of an existing widget with your own customization. This is rather common in Flutter, and actually its how a lot of material widgets are implemented, just setting up a bunch of properties with a predefined state.
In your case the correct way to achieve what you want, is to make a new class just as you did, but you don't need to create setters to change the state/attributes of your new widgets. Simply pass them in the constructors of your new class, for example:
class AElevatedButton extends StatelessWidget {
final String text;
final VoidCallback? onTap;
const AElevatedButton({required this.text,this.onTap});
#override
Widget build(BuildContext context) {
return Container(
margin: const EdgeInsets.all(5),
child: ElevatedButton(
onPressed: onTap,
child: Text(text)
)
);
}}
Here there is only text and onTap, but you could add more attributes as your necessities change, and to use them is just like any other flutter widget:
class _MyHomePageState extends State<MyHomePage>
{
#override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: const Text("Test Buttons")
),
body:
Column(
children: <Widget>[
AElevatedButton(text:"Button 1",onTap:testcallback1),
AElevatedButton(text:"Button 2",onTap:testcallback2)
])
);
}
}
If I missed any point or could clarify something more, do comment.
After some experimenting, I got a solution but unsure if this is the best way:
class AElevatedButton extends StatefulWidget
{
AElevatedButton({Key? key}) : super(key:key);
String buttonText = "Default";
Function() cb = nothingfunc;
#override
State<AElevatedButton> createState() => ElevatedButtonState();
}
class ElevatedButtonState extends State<AElevatedButton>
{
#override
Widget build(BuildContext context) {
return Container(
margin: const EdgeInsets.all(5),
child: ElevatedButton(
onPressed: widget.cb,
child: Text(widget.buttonText)
)
);
}
}
And the homepage State:
AElevatedButton firstButton = new AElevatedButton();
AElevatedButton secondButton = new AElevatedButton();
void initiateButtons()
{
firstButton.buttonText = "Button 1";
firstButton.cb = testcallback1;
secondButton.buttonText = "Button 2";
secondButton.cb = testcallback2;
}
Some misc. functions added for testing above:
void nothingfunc()
{
}
void testcallback1()
{
print("Button 1 pressed");
}
void testcallback2()
{
print("Button 2 pressed");
}

Using Type Function and pass it into ElevatedButton onPressed, Flutter

I am new to Flutter + Dart. I basically have class in the following.
First I have a clas called BottomForm where it have build function that returns ElevatedButton problem when I call Function type variable in onPressed I have an issue saying that:
The argument type 'Function' can't be assigned to the parameter type 'void Function()?'.dartargument_type_not_assignable
import 'formbutton.dart';
// Define a corresponding State class.
// This class holds the data related to the Form.
class _MyCustomFormState extends State<MyCustomForm> {
// Create a text controller and use it to retrieve the current value
// of the TextField.
final email = TextEditingController();
final password = TextEditingController();
void _logIn() {
print("Logged In.");
}
#override
void dispose() {
// Clean up the controller when the widget is disposed.
email.dispose();
password.dispose();
super.dispose();
}
#override
Widget build(BuildContext context) {
return Scaffold(
body: Column(
children: [
Padding(
padding: const EdgeInsets.all(16.0),
child: TextFormField(
autocorrect: true,
controller: email,
),
),
ButtonForm(_logIn, "Hello"),
],
),
floatingActionButton: FloatingActionButton(
onPressed: () {
showDialog(
context: context,
builder: (context) {
return AlertDialog(
content: Text(email.text),
);
});
},
tooltip: "Show me the value",
child: Icon(Icons.text_fields),
),
);
}
}
//Define a Custom Widget
class MyCustomForm extends StatefulWidget {
#override
_MyCustomFormState createState() => _MyCustomFormState();
}
Than I have a problem in the main class for our Button . When I pass the Function functionApply;
import 'package:flutter/cupertino.dart';
import 'package:flutter/material.dart';
class ButtonForm extends StatelessWidget {
final Function functionApply;
final String textButton;
ButtonForm(this.functionApply, this.textButton);
#override
Widget build(BuildContext context) {
return Container(
width: double.infinity,
child: ElevatedButton(
child: Text(this.textButton),
onPressed: this.functionApply, // I have a problem here!!
),
);
}
}
onPressed is a type of VoidCallback
typedef VoidCallback = void Function()
So instead of using
final Function functionApply;
use
final VoidCallback functionApply;
So your ButtonForm will be
import 'package:flutter/cupertino.dart';
import 'package:flutter/material.dart';
class ButtonForm extends StatelessWidget {
final VoidCallback functionApply;
final String textButton;
ButtonForm(this.functionApply, this.textButton);
#override
Widget build(BuildContext context) {
return Container(
width: double.infinity,
child: ElevatedButton(
child: Text(textButton),
onPressed: functionApply, // Problem Solved!!
),
);
}
}
Try this:
ElevatedButton(
child: Text(this.textButton),
onPressed: () {
functionApply();
},
)
Give the return type of your function. If you don't give any return type then by default the return type will be dynamic. But onPressed function 's return type is void. So just change the function deceleration and it will work nicely.
final void Function() functionApply;

Flutter, calling a function inside the button

I am new to flutter and I make some practices. I have a StatelessWidget called doChange and makeChange and one StatefulWidget. This class which is statefulwidget I made child of the home page of the app also. But, I think that it is unnecessary to define here. My purpose in this case is that, I want to change the state of the button make open,make closed and at the same time the text open and close will also change. I think that class changeText has not problem but in makeChange class I have some trouble with creating constructor and function to call into onPress. The states do not change. How can i solve this or is that any way to do this without function ?
class changeText extends StatelessWidget{
final doChange;
changeText({#required this.doChange});
#override
Widget build(BuildContext context){
return Container(
//some codes
//some codes
child: doChange ? Text("open") : Text("close"),
);
}
}
class makeChange extends StatelessWidget{
final changeState;
makeChange({#required this.changeState}); // I want to add constructor here lets say onPressButton
whenPressed(){ // I want to create a function with the that constructor that I have add.
}
#override
Widget build(BuildContext context){
return Container(
//some codes
//
child: Column(
children: [
MaterialButton(
//some codes
//
onPressed: () {} // Here I want to call a function when press the button.
child: changeState ? Text("make open") : Text("make close"),
),
],
),
);
}
}
class Mainarea extends StatefulWidget{
#override
_MainareaState createState() => _mainAreaState();
}
class _MainareaState extends State<Mainarea> {
bool isChange= false;
#override
Widget build(BuildContext context){
return Container(
//some codes
//
child: Column(
children: <Widget>[
changeText(doChange: !this.isChange),
makeChange(changeState: !this.isChange),
],
),
);
}
}
I just added a final Function(bool) callback as a parameter, which can be called inside from the stateless widget, and returns to the calling function. From there you can call setState
class changeText extends StatelessWidget {
final bool doChange;
changeText({#required this.doChange});
#override
Widget build(BuildContext context) {
return Container(
//some codes
//some codes
child: doChange ? Text("open") : Text("close"),
);
}
}
class makeChange extends StatelessWidget {
final bool changeState;
final Function(bool) callback;
makeChange(
{#required
this.changeState,
#required
this.callback}); // You can rename this to onPressed or whatever
#override
Widget build(BuildContext context) {
return Container(
//some codes
//
child: Column(
children: [
MaterialButton(
//some codes
//
onPressed: () => callback( changeState),
child: changeState ? Text("make close") : Text("make open"), //I had to swap these around to make the text correct
),
],
),
);
}
}
class Mainarea extends StatefulWidget {
#override
_MainareaState createState() => _MainareaState();
}
class _MainareaState extends State<Mainarea> {
bool isChange = false;
#override
Widget build(BuildContext context) {
return Container(
//some codes
//
child: Column(
children: <Widget>[
changeText(doChange: !this.isChange),
makeChange(
changeState: !this.isChange,
callback: (bool val) {
setState(() => isChange = val); //this is your function that returns and resetst the value in the parent widget
},
),
],
),
);
}
}

how to get the TextFiled value outside the widget

the TextFiled class
class InputContainer extends StatefulWidget{
final _InputContainerInner input = new _InputContainerInner();
#override
State<StatefulWidget> createState() {
return input;
}
}
class _InputContainerInner extends State<InputContainer>{
TextEditingController controller = TextEditingController();
String num = '';
#override
Widget build(BuildContext context) {
return Container(
...
child: TextField(
...
controller: controller,
...
)
)
}
i use it in another file
Widget build(BuildContext context) {
InputContainer passWord = new InputContainer();
return Scaffold(
body: new Column(
children: <Widget>[
passWord,
new MaterialButton(
onPressed: () {
print(passWord.input);
print(passWord.input.num);
}
)
]
)
}
i click the button , but got nothing of this TextFiled, print result is
flutter: InputContainerInner#9818c(lifecycle state: created, no widget, not mounted)
flutter:
maybe it is the matter of lifecycle, but i have made it in the widget , what happen ?
Maybe U can save it in some variable e.g.
String password = controller.text
then call it from other class by creating object or something
I think you shouldn't create a new instance of InputContainer in the other widget class, it's wrong.
The ideal way is to use inheritedwidget or scopedmodel,provider but that is more complicated
You can try creating a globalkey inside the other class and access the InputContainerInner text controller from there
Note: Change your _InputContainerInner to InputContainerInner
//put this inside your class widget, where u declare all the variables
final GlobalKey<InputContainerInner> containerKey;
Widget build(BuildContext context) {
return Scaffold(
body: new Column(
children: <Widget>[
//import the inputContainer class
InputContainer(),
new MaterialButton(
onPressed: () {
print(widget.containerKey.currentState.controller.text);
print(widget.containerKey.currentState.controller.input.num);
}
)
]
)
}
full example
TextEditingController completeAddressController = new TextEditingController();
TextFormField(
controller: completeAddressController,
),
get value of TextFormField
String text = completeAddressController.text.toString(),