Program is saying : The argument type String Function(String) can't be assigned to the parameter type String?
Function(String?)?`. İn the validator are and under line , I Wrote
error.
import 'package:asdasd/Validation/Student_Validator.dart';
import 'package:asdasd/models/Student.dart';
import 'package:flutter/material.dart';
class StudentAdd extends StatefulWidget {
#override
State<StatefulWidget> createState() {
return _StudentAddState();
}
}
class _StudentAddState extends State with StudentValidationMixin {
var student = Student.withoutinfo();
#override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: Text("Yeni öğrenci ekleme "),
),
body: Container(
margin: EdgeInsets.all(20.0),
child: Form(
child: Column(
children: <Widget>[
TextFormField(
decoration: InputDecoration(
labelText: "Öğrencinin Adı", hintText: "Musa Develi"),
validator: validateFirstName,//eror
onSaved: (String value){//eror
student.firstName=value;//eror
},
),
],
),
),
),
);
}
}
class StudentValidation {
}
i'm guessing validateFirstName is a function, right? You have to make your validator function the same as the validator,
String? Function(String?)
String is not the same as String?, the first means the value cannot be null, the second means it can be null.
this happened because null-safety is enabled
Related
I have a list of dynamic forms where I need to add and remove form fields between two fields dynamically. I am able to add/remove form fields from the bottom of the list properly.
However, when I try to add a form field in between two form fields the data for the field does not update correctly.
How can I correctly add a field in between the two fields and populate the data correctly?
import 'package:flutter/material.dart';
class DynamicFormWidget extends StatefulWidget {
const DynamicFormWidget({Key? key}) : super(key: key);
#override
State<DynamicFormWidget> createState() => _DynamicFormWidgetState();
}
class _DynamicFormWidgetState extends State<DynamicFormWidget> {
List<String?> names = [null];
#override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: const Text('Dynamic Forms'),
),
body: ListView.separated(
padding: const EdgeInsets.symmetric(horizontal: 12, vertical: 16),
itemBuilder: (builderContext, index) => Row(
children: [
Flexible(
child: TextFormField(
initialValue: names[index],
onChanged: (name) {
names[index] = name;
debugPrint(names.toString());
},
decoration: InputDecoration(
hintText: 'Enter your name',
border: OutlineInputBorder(
borderRadius: BorderRadius.circular(8))),
),
),
Padding(
padding: const EdgeInsets.all(8),
child: IconButton(
onPressed: () {
setState(() {
if(index + 1 == names.length){
names.add( null); debugPrint('Added: $names');
} else {
names.insert(index + 1, null); debugPrint('Added [${index+1}]: $names');
}
});
},
color: Colors.green,
iconSize: 32,
icon: const Icon(Icons.add_circle)),
),
Padding(
padding: const EdgeInsets.all(8),
child: IconButton(
onPressed: (index == 0&& names.length == 1)
? null
: () {
setState(() {
names.removeAt(index);
});
debugPrint('Removed [$index]: $names');
},
color: Colors.red,
iconSize: 32,
icon: const Icon(Icons.remove_circle)),
),
],
),
separatorBuilder: (separatorContext, index) => const SizedBox(
height: 16,
),
itemCount: names.length,
),
);
}
}
Basically the problem is that Flutter is confused about who is who in your TextFormField list.
To fix this issue simply add a key to your TextFormField, so that it can be uniquely identified by Flutter:
...
child: TextFormField(
initialValue: names[index],
key: UniqueKey(), // add this line
onChanged: (name) {
...
If you want to learn more about keys and its correct use take a look at this.
The widget AnimatedList solves this problem, it keep track of the widgets as a list would do and uses a build function so it is really easy to sync elements with another list. If you end up having a wide range of forms you can make use of the InheritedWidget to simplify the code.
In this sample i'm making use of the TextEditingController to abstract from the form code part and to initialize with value (the widget inherits from the ChangeNotifier so changing the value will update the text in the form widget), for simplicity it only adds (with the generic text) and removes at an index.
To make every CustomLineForm react the others (as in: disable remove if it only remains one) use a StreamBuilder or a ListModel to notify changes and make each entry evaluate if needs to update instead of rebuilding everything.
class App extends StatelessWidget {
final print_all = ChangeNotifier();
App({super.key});
#override
Widget build(BuildContext context) {
return MaterialApp(
home: Scaffold(
body: FormList(print_notifier: print_all),
floatingActionButton: IconButton(
onPressed: print_all.notifyListeners,
icon: Icon(Icons.checklist),
),
),
);
}
}
class FormList extends StatefulWidget {
final ChangeNotifier print_notifier;
FormList({required this.print_notifier, super.key});
#override
_FormList createState() => _FormList();
}
class _FormList extends State<FormList> {
final _controllers = <TextEditingController>[];
final _list_key = GlobalKey<AnimatedListState>();
void print_all() {
for (var controller in _controllers) print(controller.text);
}
#override
void initState() {
super.initState();
widget.print_notifier.addListener(print_all);
_controllers.add(TextEditingController(text: 'Inital entrie'));
}
#override
void dispose() {
widget.print_notifier.removeListener(print_all);
for (var controller in _controllers) controller.dispose();
super.dispose();
}
void _insert(int index) {
final int at = index.clamp(0, _controllers.length - 1);
_controllers.insert(at, TextEditingController(text: 'Insert at $at'));
// AnimatedList will take what is placed in [at] so the controller
// needs to exist before adding the widget
_list_key.currentState!.insertItem(at);
}
void _remove(int index) {
final int at = index.clamp(0, _controllers.length - 1);
// The widget is replacing the original, it is used to animate the
// disposal of the widget, ex: size.y -= delta * amount
_list_key.currentState!.removeItem(at, (_, __) => Container());
_controllers[at].dispose();
_controllers.removeAt(at);
}
#override
Widget build(BuildContext context) {
return AnimatedList(
key: _list_key,
initialItemCount: _controllers.length,
padding: const EdgeInsets.symmetric(horizontal: 20, vertical: 10),
itemBuilder: (ctx, index, _) {
return CustomLineForm(
index: index,
controler: _controllers[index],
on_insert: _insert,
on_remove: _remove,
);
},
);
}
}
class CustomLineForm extends StatelessWidget {
final int index;
final void Function(int) on_insert;
final void Function(int) on_remove;
final TextEditingController controler;
const CustomLineForm({
super.key,
required this.index,
required this.controler,
required this.on_insert,
required this.on_remove,
});
#override
Widget build(BuildContext context) {
return Row(
children: [
Flexible(
child: TextFormField(
controller: controler,
),
),
IconButton(
icon: Icon(Icons.add_circle),
onPressed: () => on_insert(index),
),
IconButton(
icon: Icon(Icons.remove_circle),
onPressed: () => on_remove(index),
)
],
);
}
}
import 'package:flutter/material.dart';
void main() {
runApp(MyApp());
}
class MyApp extends StatelessWidget {
const MyApp({ Key? key }) : super(key: key);
#override
Widget build(BuildContext context) {
return MaterialApp(
theme: ThemeData(
primaryColor:Colors.pink[300],
),
home: Calculator(),
debugShowCheckedModeBanner: false,
);
}
}
class Calculator extends StatefulWidget {
const Calculator({ Key? key }) : super(key: key);
#override
_CalculatorState createState() => _CalculatorState();
}
class _CalculatorState extends State<Calculator> {
#override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: Text('Calculator'),
centerTitle: true,
),
body:Container(
child: Column(
children:<Widget> [
Expanded(
child:Container(
padding: EdgeInsets.all(10.0),
alignment: Alignment.bottomRight,
child:Text(text,
style: TextStyle(
fontSize: 50.0,
fontWeight: FontWeight.w500,
),
),
)
),
Row(
children: [
customElevatedButton('9',),
customElevatedButton('8',),
customElevatedButton('7',),
customElevatedButton('+',),
],
),
Row(
children: [
customElevatedButton('6',),
customElevatedButton('5',),
customElevatedButton('4',),
customElevatedButton('-',),
],
),
Row(
children: [
customElevatedButton('3'),
customElevatedButton('2'),
customElevatedButton('1'),
customElevatedButton('X'),
],
),
Row(
children: [
customElevatedButton('C',),
customElevatedButton('0',),
customElevatedButton('=',),
customElevatedButton('/',),
],
),
],
),
)
);
}
Widget customElevatedButton(num) {
return Expanded(
child: RaisedButton(
padding:EdgeInsets.all(25.0),
color:Colors.pink[100] ,
onPressed:(){
operation(num);
},
child:Text(num,
style: TextStyle(fontSize: 40.0),
),
),
);
}
String text='';
void operation(clickedButt)
{
String result;
int first,second;
String opp;
if(clickedButt=='C'){
result='';
text='';
}
else if(clickedButt=='+'||clickedButt=='-'||clickedButt=='X'||clickedButt=='/'){
first=int.parse(text);
result='';
opp=clickedButt;
}
else if(clickedButt=='='){
second=int.parse(text);
result='';
if(opp=='+'){
result=(first+second).toString();
}
else if(opp=='-'){
result=(first-second).toString();
}
else if(opp=='*'){
result=(first*second).toString();
}
else if(opp=='/'){
result=(first/second).toString();
}
else{
result=int.parse(text+clickedButt).toString();
}
}
else{
result=text+clickedButt;
}
setState(() {
text=result;
});
}
}
The error is:
Error: Non-nullable variable 'first' must be assigned before it can be used.
Error: Non-nullable variable 'opp' must be assigned before it can be used.
I already initialized those variables before the if statement. Why am I getting this error?
Actually the text variable is should also be initialized within the operation() function. But then again the error is showing in calculator widget.
With dart null safety you cannot assign null to the String opp;
The error says you have initialized the variable opp; with null value. To avoid that either,
change String opp; to String? opp;
use late keyword before opp as late String opp; (in this case you have to make sure that you are assigning opp variable before is is used otherwise it will throw late initialization error again)
assign value to opp variable at the declaration as String opp = "someString";
I want to pass a variable name as a function parameter, but it doesn't seem to work : the content of my variable remains unchanged.
Widget Field(String changedValue, String label, bool isTextObscured) {
return TextFormField(
decoration: InputDecoration(labelText: label),
validator: checkFieldEmpty,
onChanged: (value) {
setState(() {
changedValue = value;
});
},
obscureText: isTextObscured,
);
}
Here, I want to change the value of the variable who has the name "changedValue". When I do it directly with the variable name, it works, but when I try to use the parameter, nothing happens. Here's an example of where I used it :
Widget LoginFields() {
return Column(
children: [
Field(email, Strings.emailLabel, false),
Field(password, Strings.passwordLabel, true),
ValidationButton(),
],
);
}
Thanks in advance!
There are many things to clarify here, like:
setState() is a method, that must be called inside a StatefullWidget.
if you create a function, name it with lowerCamelCase (effective dart).
for returning a Widget prefer extend a Widget, especially if you need a State.
if you seek a guide for TextField in Flutter - check cookbook here and here.
Here how you can set it up:
import 'package:flutter/material.dart';
void main() => runApp(MyApp());
class MyApp extends StatelessWidget {
#override
Widget build(BuildContext context) {
return MaterialApp(
title: 'Material App',
home: Scaffold(
appBar: AppBar(
title: Text('Material App Bar'),
),
body: Center(
child: Column(
children: [
FieldWidget(changedValueInitial: 'email', label: 'labelConstOne'),
FieldWidget(changedValueInitial: 'password', label: 'labelConstTwo', isTextObscured: true),
// ValidationButton(),
],
),
),
),
);
}
}
class FieldWidget extends StatefulWidget {
String changedValueInitial;
String label;
bool isTextObscured;
FieldWidget({
Key? key,
required this.changedValueInitial,
required this.label,
this.isTextObscured = false,
}) : super(key: key);
#override
_FieldWidgetState createState() => _FieldWidgetState();
}
class _FieldWidgetState extends State<FieldWidget> {
late String _changedValue;
#override
void initState() {
super.initState();
_changedValue = widget.changedValueInitial;
}
#override
Widget build(BuildContext context) {
return TextFormField(
decoration: InputDecoration(labelText: widget.label),
// validator: yourValidator,
initialValue: _changedValue,
onChanged: (value) {
setState(() {
_changedValue = value;
});
},
obscureText: widget.isTextObscured,
);
}
}
If that is what you need..
I'm trying to understand how to use the GetX package in a Flutter application to get a reactive update in a Text widget when the value is changed in a TextFormField.
What is displayed in the Text widget is the property of an observable object. And it is that property that is updated from the TextFormField.
The value is correctly updated in the controller but not in the widget.
If I use a string variable directly, it does update correctly. But as soon as I'm using an object, it does not update anymore.
This is a really simple sample of my application, just to be sure that that basics are understood.
Here is my code:
class User {
String name = "";
}
class TestController extends GetxController {
TestController();
final user = User().obs;
}
class MyHomePage extends StatelessWidget {
final c = Get.put(TestController());
final String title;
MyHomePage({this.title});
#override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: Text(title),
),
body: Center(
child: Container(
width: Get.width * 0.8,
height: Get.height * 0.8,
child: Column(
children: [
Obx(() => Text(c.user.value.name)),
TextFormField(
onChanged: (value) => c.user.value.name = value,
),
],
),
),
),
);
}
}
Many thanks for your help !
Ok I found the solution thanks to the CodeX youtuber.
To be able to update reactively the UI when you change the value of a property an object, even if this object is set as observable, you need to have the property observable as well.
So the correct code will look like this
class User {
final name = "".obs;
}
class TestController extends GetxController {
TestController();
final user = User().obs;
}
class MyHomePage extends StatelessWidget {
final c = Get.put(TestController());
final String title;
MyHomePage({this.title});
#override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: Text(title),
),
body: Center(
child: Container(
width: Get.width * 0.8,
height: Get.height * 0.8,
child: Column(
children: [
Obx(() => Text(c.user.value.name.value)),
TextFormField(
onChanged: (value) => c.user.value.name.value = value,
),
],
),
),
),
);
}
}
Update Your onChanged() fn to this
Obx(() => Text(c.user.value.name)),
TextFormField(onChanged: (value) {
c.user.value.name = value;
c.user.refresh();
}),
I have a following state full widget. I need to reuse it as it is by just changing two variables id and collectionName. Generally I would extract a widget, but in this case I am modifying variable firstName which wont let me extract the widget.
class IndividualSignupPage1 extends StatefulWidget {
static final id = 'idIndividualSignupPage1';
final collectionName = 'Individual';
#override
_IndividualSignupPage1State createState() => _IndividualSignupPage1State();
}
class _IndividualSignupPage1State extends State<IndividualSignupPage1> {
String firstName;
DateTime birthDate;
final firestoreObj = Firestore.instance;
#override
Widget build(BuildContext context) {
return Scaffold(
appBar: GeneralAppBar(
appBar: AppBar(),
),
body: Container(
child: Column(mainAxisAlignment: MainAxisAlignment.spaceEvenly, children: <Widget>[
TextField(
onChanged: (value) {
this.firstName = value;
},
),
Row(
mainAxisAlignment: MainAxisAlignment.end,
children: <Widget>[
Expanded(
child: Card(
child: ListTile(
title: Text(
this.birthDate == null
? 'Birthdate'
: '${this.birthDate.year}-${this.birthDate.month}-${this.birthDate.day}',
),
onTap: () {
DatePicker.showDatePicker(
context,
initialDateTime: this.birthDate,
onConfirm: (newDate, listOfIndexes) {
setState(() {
this.birthDate = newDate;
});
},
);
},
),
),
),
],
),
WFullWidthButton(
name: 'Save',
onPressedFunc: () async {
// save all info to firestore db
firestoreObj.collection(widget.collectionName).document('xyz').setData({
'firstName': this.firstName,
'birthDate': this.birthDate,
}, merge: true);
},
),
]),
),
);
}
}
Thanks
You can pass the arguments to the Class IndividualSignupPage1 and then use it in its corresponding state class _IndividualSignupPage1State with the property "widget." like,
// pass the arguments from another class.
class IndividualSignupPage1 extends StatefulWidget {
final String id;
final String collectionName;
IndividualSignupPage1(this.id,this.collectionName);
#override
_IndividualSignupPage1State createState() => _IndividualSignupPage1State();
}
Let say you want to use id and collectionName in its corresponding state class _IndividualSignupPage1State you can access it using "widget" property like,
appBar: AppBar(title: Text(widget.id)),
**OR**
appBar: AppBar(title: Text(widget.collectionName)),
Note: you can only access the widget property inside functions/methods only.
Create IndividualSignupPage1 constructor and pass data with constructor arguments.
class IndividualSignupPage1 extends StatefulWidget {
final String id;
final String collectionName;
IndividualSignupPage1(this.id,this.collectionName);