Assign object to any widget in Flutter - flutter

Good afternoon.
Do you know if there is in Flutter something similar to the .NET Tag property or the Qt setProperty property? Basically I want to be able to assign an object, string... to any widget in Flutter.
Thank you very much.

A basic example, as a followup to the comments:
enum DataType {
text,
number,
date,
time,
dateTime,
boolean,
}
class FieldData<T> {
final String table;
final DataType type;
final T value;
FieldData({
required this.table,
required this.type,
required this.value,
});
}
class SpecialTextField extends StatelessWidget {
const SpecialTextField({
Key? key,
required this.table,
required this.type,
required this.onChanged,
this.controller,
this.decoration,
}) : super(key: key);
final String table;
final DataType type;
final Function(FieldData) onChanged;
// Here you declare TextField's properties you need
// to use in your widget, and then pass them to TextField
final TextEditingController? controller;
final InputDecoration? decoration;
#override
Widget build(BuildContext context) {
return TextField(
controller: controller,
decoration: decoration,
onChanged: (value) => onChanged(
FieldData(
table: table,
type: type,
value: value,
),
),
);
}
}
And to use it, you can do:
SpecialTextField(
table: 'users',
type: DataType.text,
onChanged: (data) {
print('Table: ${data.table}');
print('Type: ${data.type.name}');
print('Value: ${data.value}');
},
)

I have found a solution that I like better. It is the Extension methods, a way to add functionalities to a library knowing nothing about its internal implementation. An example in my case:
extension myWidget on Widget {
static Object _data = Object();
Object get tag => _data;
set tag(Object tag) {
_data = tag;
}
}
Widget setTag(Widget wdg, Object tag) {
wdg.tag = tag;
return wdg;
}
Usage:
Container(
width: 500,
child: setTag(
TextField(
obscureText: false,
decoration: const InputDecoration(
border: OutlineInputBorder(),
labelText: 'Nº Historia Clínica',
),
),
"###AUTOCAMPO###|HJ_07_RADIODIAGNOSTICO|HOJA_REFERENCIA|ENTEROTEXTO"
)

Related

type '(String) => void' is not a subtype of type '(dynamic) => void'

This is where I use the dropdown:
class _JobFunctionState extends State<JobFunction> {
static const jobList = <String>["Item 1", "Item 2", "Item 3"];
String dropdownValue = jobList[0];
#override
Widget build(BuildContext context) {
return Dropdown<String>(
hint: const Text("Choose job function"),
labelText: "Job Function",
value: dropdownValue,
dropdownMenuItemList: jobList
.map<DropdownMenuItem<String>>(
(String job) => DropdownMenuItem<String>(
value: job,
child: Text(job),
))
.toList(),
onChanged: (newDropdownValue) {
setState(() {
dropdownValue = newDropdownValue;
});
},
);
}
Here's the full dropdown class:
class Dropdown<T> extends StatefulWidget {
final List<DropdownMenuItem<T>> dropdownMenuItemList;
final ValueChanged<T> onChanged;
final T value;
final bool isBorder;
final double radius;
final TextStyle? textStyle;
final Color? color;
final Widget hint;
final String labelText;
const Dropdown(
{Key? key,
required this.dropdownMenuItemList,
required this.onChanged,
required this.value,
this.isBorder = true,
this.radius = 10.0,
this.textStyle,
this.color,
required this.hint,
required this.labelText})
: super(key: key);
_DropdownState<T> createState() => _DropdownState();
}
class _DropdownState<T> extends State<Dropdown> {
#override
Widget build(BuildContext context) {
return FormField<T>(
builder: (FormFieldState<T> state) {
return SingleChildScrollView(
child: DropdownButtonFormField<T>(
isExpanded: true,
itemHeight: 50.0,
items: widget.dropdownMenuItemList as List<DropdownMenuItem<T>>,
onChanged: widget.onChanged,
value: widget.value,
dropdownColor: Colors.white,
iconEnabledColor: Colors.grey,
icon: const Icon(Icons.arrow_drop_down),
hint: widget.hint,
),
);
},
);
}
}
and ValueChanged is:
typedef ValueChanged<T> = void Function(T value);
Dropdown is generic, I've made everything string in the jobFunction widget, and I get this error.
if I add dynamic to onChanged parameter(), but the type of the parameter should not be dynamic, but String. Any ideas?
adding text to fill the requirements for edit: slkdjf s;lkdjfsd jfiosdj fsdnf lksdjf klsjdfi skjldfj slkdj flksdjlkifj sf kjsdlk;fj slk;dj fisjd fiosj f;ajof hsiod jfsajfkl sjd fk jsdlf sdlkf lksjdfoijsfoi jsdlkjf lksadj flksdjflk sjdalkf jsakj fjsaoif jseij flisd jflksajflk jasdlk
I got the same issue
and what did work for me was replacing :
class _DropdownState<T> extends State<Dropdown>
by this:
class _DropdownState<T> extends State<Dropdown<T>>
Remember also to change the overrided createState method:
#override
_BasicDropdownState<T> createState() => _BasicDropdownState<T>();
Dropdown is generic, I've made everything string in the jobFunction
widget, and I get this error.
ValueChanged should be nullable value. If you specific to a String , then the onchange will specific to String? : means String or null.
Because if there is no changes, it must be null.
how to assign the String? to your variable that non-null String is by add a condition like below.
onChanged: (newDropdownValue) {
if( newDropdownValue != null){
setState(() {
dropdownValue = newDropdownValue!;
});
}
},
if no changes happend, then you dont need to set new value to you dropdownValue
hope this solve your issue.

Slider and StateFul Widget - should I create local variable or do something else?

I have custom widget (Container with some Texts and one Slider). I want this widget to reuse in various places of app. I know how to pass data via Constructor (with named arguments / parameters). This works fine. I have problem with creating currentValue variable that will show Slider's current position.
First (before widget is build and shown) I would like to initialize this variable (currentValue) with value set in different variable - initialValue.
Then, I want to store current position of slider in currentValue variable.
Finally I want to get value of currentValue (sliders value) in different part of application.
Should I use any kind of State Management libraries or local variable may be enough?
How to do this properly, with Flutter Best Practices in mind?
Thanks for suggestions. Of course sample code is welcome.
Below is my code (what I have so far achieved):
import 'package:flutter/material.dart';
class SettingsSliderWidget extends StatefulWidget {
String title;
double initialValue;
double minValue;
double maxValue;
int divisions;
SettingsSliderWidget(
{required this.title, required this.initialValue, required this.minValue, required this.maxValue, required this.divisions});
#override
State<SettingsSliderWidget> createState() => _SettingsSliderWidgetState();
}
class _SettingsSliderWidgetState extends State<SettingsSliderWidget> {
double currentValue = 15; // I don't like this solution! I would like to assign initialValue here
#override
Widget build(BuildContext context) {
// Here I can put 'local variable' - but this have not solved my problems.
return Container(
decoration: BoxDecoration(
border: Border.all(width: 0.25),
borderRadius: BorderRadius.circular(5),
color: Colors.white12),
padding: const EdgeInsets.fromLTRB(25, 10, 25, 10),
child: Column(
children: [
Row(children: [
Text(widget.title),
Spacer(),
Text(currentValue.toString())
]),
Slider(
min: widget.minValue,
max: widget.maxValue,
activeColor: Colors.lightBlue,
inactiveColor: Colors.lightGreen,
thumbColor: Colors.lightBlueAccent,
value: currentValue,
divisions: widget.divisions,
label: '${currentValue}',
onChanged: (value) {
setState(() {
currentValue = value;
});
},
),
],
),
);
}
}
You can use late double currentValue = widget.initialValue; to get value 1st time on state class. And to get changed value, use a callback method like final ValueChanged<double>? onChanged;
class SettingsSliderWidget extends StatefulWidget {
final String title;
final double initialValue;
final double minValue;
final double maxValue;
final int divisions;
/// to get value
final ValueChanged<double>? onChanged;
const SettingsSliderWidget({
Key? key,
required this.title,
required this.initialValue,
required this.minValue,
required this.maxValue,
required this.divisions,
this.onChanged,
}) : super(key: key);
#override
State<SettingsSliderWidget> createState() => _SettingsSliderWidgetState();
}
class _SettingsSliderWidgetState extends State<SettingsSliderWidget> {
late double currentValue = widget.initialValue;
....
onChanged: (value) {
if(widget.onChanged!=null)widget.onChanged!(value);
And use cases like
SettingsSliderWidget(
onChanged: (value) {
},
),
you can put currentValue in empty file beside main file like this:
double currentValue = 15;
see this :
i use this trick if i want use variable in many screen and files
then you can use this variable wherever you want in your app
You can use initState to set the the state based on widget.initialValue
class _SettingsSliderWidgetState extends State<SettingsSliderWidget> {
late double currentValue;
#override
void initState() {
currentValue = widget.initValue
super.initState();
}
...

Can Anyone solve Custom DropDownButton issue. Flutter ( i know many will not be able to )

TO SOLVE THIS U NEED TO RUN IT FIRST
Only for them who have experience with DropDownButton and T typepassing can solve this.
Please Help!
import 'package:flutter/material.dart';
// ignore: must_be_immutable
class SupDropDownButton<T> extends StatefulWidget {
FormFieldValidator<T>? validator;
ValueChanged<T> value;
final List<T> data;
SupDropDownButton(
{Key? key, required this.data, this.validator, required this.value})
: super(key: key);
#override
State<SupDropDownButton> createState() => _SupDropDownButtonState<T>();
}
class _SupDropDownButtonState<T> extends State<SupDropDownButton> {
T? _value;
List<DropdownMenuItem<T>> items() =>
widget.data.cast<T>().map<DropdownMenuItem<T>>(menuItem).toList();
DropdownMenuItem<T> menuItem(dynamic value) => DropdownMenuItem<T>(
value: value,
child: Text(value.name),
);
#override
Widget build(BuildContext context) {
return DropdownButtonFormField<T>(
decoration: const InputDecoration(border: InputBorder.none),
validator: widget.validator,
value: _value,
onChanged: (T? val) {
FocusScope.of(context).requestFocus(FocusNode());
_value = val!;
widget.value.call(val);
setState(() {});
},
items: items(),
hint: const Text('Please select Categories'),
);
}
}
THIS IS THE ERROR
Expected a value of type ((dynamic) => String?)?, but got one of type (Employee) => String?
I have worked on your code. Instead of value.name in your code, I have directly add List<String> for easy reference and it's working fine.I using null safety, that's why add late initialize to data list.if you need to create object and insert JSON data means revert back will rework on it
static data added like List<String> employeeList = ['hari','chetanPatil'] and cast it to data;
Working code :
// ignore: must_be_immutable
class SupDropDownButton<T> extends StatefulWidget {
FormFieldValidator<T>? validator;
ValueChanged<T> value;
late List<T> data;
SupDropDownButton(
{Key? key, required this.data, this.validator, required this.value})
: super(key: key);
#override
State<SupDropDownButton> createState() => _SupDropDownButtonState<T>();
}
class _SupDropDownButtonState<T> extends State<SupDropDownButton> {
T? _value;
List<String> employeeList = ['hari', 'chetanPatil'];
bool isOnLoad = true;
#override
void initState() {
super.initState();
}
List<DropdownMenuItem<T>> items() =>
widget.data.cast<T>().map<DropdownMenuItem<T>>(menuItem).toList();
DropdownMenuItem<T> menuItem(dynamic value) => DropdownMenuItem<T>(
value: value,
child: Text(value),
);
#override
Widget build(BuildContext context) {
widget.data.clear();
widget.data.addAll(employeeList);
return Scaffold(
body: Center(
child: Padding(
padding: const EdgeInsets.all(40.0),
child: DropdownButtonFormField<T>(
decoration: const InputDecoration(border: InputBorder.none),
validator: widget.validator,
value: _value,
onChanged: (T? val) {
FocusScope.of(context).requestFocus(FocusNode());
_value = val!;
print(_value);
widget.value.call(val);
setState(() {});
},
items: items(),
hint: const Text('Please select Categories'),
),
),
),
);
}
}

Pass static method and model class via contructor paramters

I have extracted the DropdownSearch<String> widget and created a independent custom widget extending the features of DropdownSearch<String>. I have done this to reduce code size and encapsulate certain functionality.
On an average a single form contains 5-6 DropdownSearch widgets. Their are multiple such forms.
I am populating the items on the widget via API call using Dio package. I have created model and serializer using built_value packages.
My question is how do I pass the fromJson static method and the model class via const constructor parameters
Code :
import 'package:dio/dio.dart';
import 'package:flutter/material.dart';
import 'package:dropdown_search/dropdown_search.dart';
import 'package:reference_wrapper/reference_wrapper.dart';
class DropDownSearch extends StatefulWidget {
final Ref<String> itemSelected;
final String url;
final String label;
const DropDownSearch({
required this.itemSelected,
required this.url,
required this.label,
Key? key,
}) : super(key: key);
#override
State<DropDownSearch> createState() => _DropDownSearchState();
}
class _DropDownSearchState extends State<DropDownSearch> {
#override
Widget build(BuildContext context) {
return DropdownSearch<String>(
mode: Mode.MENU,
showSelectedItems: true,
showSearchBox: true,
showAsSuffixIcons: true,
dropdownSearchDecoration: InputDecoration(
label: Text(widget.label),
focusColor: Colors.blue,
border: const OutlineInputBorder(
borderSide: BorderSide(
style: BorderStyle.solid,
),
),
),
validator: (value) {
if (value == null || value.isEmpty) {
return 'Please select ${widget.label}';
}
return null;
},
onFind: (text) async {
var response = await Dio().get(
widget.url,
);
if (response.statusCode != 200) {}
final ipcStatusList = IpcStatusList.fromJson(response.data); //How to pass this static method as parameter
return ipcStatusList.ipcStatus.toList(); //How to pass this as parameter
},
onChanged: (value) => setState(
() {
widget.itemSelected.ref = value ?? '';
},
),
);
}
}
I think you can make a final variable in your "DropDownSearch" which is the same type of toJson. And when using this Class, pass your static variable.

Is it okay to extend some class and use it in build()?

I have a widget that presents an error to a user. I want to have it in two variations:
the first would be a page element to show the error right in the view, replacing some portion of content
the second would be presented to a user as a dialog window.
For the second one I want to tweak the layout a little bit and use Dialog as a wrapped. So I created a separate widget, who extends my current one so I can skip adding duplicate fields in class.
class ConnectionErrorDialog extends ConnectionErrorWidget {
ConnectionErrorDialog(
{required String errorText,
required VoidCallback mainButtonOnTap,
String mainButtonText = 'Понятно'})
: super(
errorText: errorText,
mainButtonText: mainButtonText,
mainButtonOnTap: mainButtonOnTap);
#override
Widget build(BuildContext context) {
return Dialog(
elevation: 24.0,
child: Padding(
padding: EdgeInsets.all(20.0),
child: ConnectionErrorWidget(
errorText: errorText,
mainButtonOnTap: mainButtonOnTap,
mainButtonText: mainButtonText,
),
));
}
}
class ConnectionErrorWidget extends StatelessWidget {
ConnectionErrorWidget({
required this.errorText,
required this.mainButtonOnTap,
this.mainButtonText = 'Попробовать снова',
});
final String errorText;
final String mainButtonText;
final VoidCallback mainButtonOnTap;
#override
Widget build(BuildContext context) {
return UserErrorWidget(
errorText: errorText,
mainButtonText: mainButtonText,
mainButtonOnTap: mainButtonOnTap,
showAsDialog: false);
}
}
I want to understand, is it even okay to extend some class and use it in build()? Maybe there's other, better way to achieve the same result?
You can extend widgets like that, but the only benefit is omitting fields duplication. But it looks like you can do something like that:
class ConnectionErrorWidget extends StatelessWidget {
const ConnectionErrorWidget({
#required this.errorText,
#required this.mainButtonOnTap,
this.mainButtonText,
this.showAsDialog = false,
});
final bool showAsDialog;
final String errorText;
final String mainButtonText;
final VoidCallback mainButtonOnTap;
#override
Widget build(BuildContext context) {
if (showAsDialog) {
return Dialog(
elevation: 24.0,
child: Padding(
padding: const EdgeInsets.all(20.0),
child: _buildUserError(),
));
}
return _buildUserError();
}
Widget _buildUserError() {
return UserErrorWidget(
errorText: errorText,
mainButtonText: mainButtonText ?? (showAsDialog ? 'Попробовать снова' : 'Понятно'),
mainButtonOnTap: mainButtonOnTap,
showAsDialog: showAsDialog);
}
}
In that case, besides omitting fields duplication, your benefit is constructor arguments omitting.
I think that there is not much difference between these variants and you can use any variant you like more.