Flutter generic function callback - flutter

can you comment?
typedef ButtonChangedCallback = void Function<T>(T value);
class MyWidget<T> extends StatefulWidget {
ButtonChangedCallback? onCallback;
const MyWidget(
{required Key key,
this.onCallback})
: super(key: key);
I would like to create such template widget to be used with different enums. So it can signal what value from the enum was selected.
But I am unable to find how to later assign to the "onCallback" method.
MyWidget(
key: const Key("RadioControls"),
onCallback: <MyEnum>(MyEnum value) =>
setState(() {
someSettings.value = value;
}),
)
This does not work with Value of type MyEnum can not be assigned to variable of type MyEnum By some experiments I discovered that inside the lambda does not seem to correspond to MyEnum as defined before.
EDIT: Solution
typedef ButtonChangedCallback<T> = void Function(T value);
class MyWidget<T> extends StatefulWidget {
ButtonChangedCallback<T>? onCallback;
used as
MyWidget<MyEnum>(
key: const Key("RadioControls"),
onCallback: (MyEnum value) =>
setState(() {
someSettings.value = value;
}),
)

When you do:
typedef ButtonChangedCallback = void Function<T>(T value);
class MyWidget<T> extends StatefulWidget {
ButtonChangedCallback? onCallback;
You're declaring that ButtonChangedCallback must be a generic function. Whatever callback is assigned to MyWidget.onCallback must itself be generic. That is, you would only be able to use it as:
MyWidget(onCallback: <T>(T x) { ... });
In the above, T is the name of the type parameter to your generic, anonymous function. It is not a type argument. T could be named anything, and in your attempt, you happened to name it MyEnum, so you ended up in a confusing situation where a generic type parameter had the same name as an actual type.
Additionally, the type parameter for the function would be unrelated to the type parameter for MyWidget.
What you probably want is to for the typedef to be generic and for the Function object to not be:
typedef ButtonChangedCallback<T> = void Function(T value);
class MyWidget<T> extends StatefulWidget {
ButtonChangedCallback<T>? onCallback;
and now you can use it as:
MyWidget<MyEnum>(
onCallback: (value) =>
setState(() {
someSettings.value = value;
}),

This way should work:
class MyWidget<T> extends StatefulWidget {
final Function<T>? callback;
const MyWidget({required this.key, this.callback}) : super(key: key);
}
And you instantiate the widget this way:
MyWidget<MyEnum>(
key: const Key("RadioControls"),
callback: (MyEnum value) {
setState((){
someSettings.value = value;
});
}
)

Related

Enum Generic not allow typed enum

I created a class that accepts a generic enum as one of it's parameters. However when I then go to pass in a specified enum type, it throws the error Expected a value of type '(Enum?) => void', but got one of type '(GradeLevel?) => void'.
My classes are:
Radio Group Presentable
class RadioGroupPresentable<T extends Enum> implements Presentable {
final List<T> values;
final T groupValue;
void Function(T? value) onChanged;
RadioGroupPresentable(this.values, this.groupValue, this.onChanged);
}
RadioGroup
class RadioGroup<T extends Enum> extends StatelessWidget {
final List<T> _values;
final T _groupValue;
ValueChanged<T?> onChanged;
RadioGroup(this._values, this._groupValue, this.onChanged, {super.key});
RadioGroup.fromPresentable(RadioGroupPresentable<T> p): this(p.values, p.groupValue, p.onChanged);
#override
Widget build(BuildContext context) {
print("building radio grouo");
return Column(
children: _values.map((e) {
return ListTile(
title: Text(e.name),
leading: Radio<T>(
value: e,
groupValue: _groupValue,
onChanged: onChanged,
),
);
}).toList(),
);
}
}
Enum
enum GradeLevel {
tenth, eleventh, twelfth
}
Presentable creation
RadioGroupPresentable(GradeLevel.values, GradeLevel.tenth, (value) {
if (value != null) {
gradeLevel.setValue(value, action: "Update Grade Level");
_application.gradeLevel = value;
print(value);
}
})
View (where error occurs)
case RadioGroupPresentable<GradeLevel>:
RadioGroupPresentable<GradeLevel> presentable = p as RadioGroupPresentable<GradeLevel>;
RadioGroup widget = RadioGroup.fromPresentable(presentable);
content.add(widget);
break;
I have tried to make the class generic so it would accept any enum, however it crashes when I pass it an enum.
The issue is caused by this line:
RadioGroup widget = RadioGroup.fromPresentable(presentable);
Type inference is failing here, so this is being interpreted as RadioGroup<Enum>.fromPresentable(presentable) rather than RadioGroup<GradeLevel>.fromPresentable(presentable).
When the RadioGroup.fromPresentable structure attempts to store the onChanged function, it finds a function that has the wrong type. It wants a function that is capable of taking an argument of any Enum type, but the function it finds is only capable of taking arguments of the GradeLevel type.
Remember, a function is only a subtype of another if its argument types are equally or more permissive than its supertype.
(Enum value) {} is void Function(GradeLevel);
(GradeLevel value) {} is! void Function(Enum);

The argument type 'Container Function(int, dynamic)' can't be assigned to the parameter type 'dynamic Function(int)'

import 'dart:ffi';
import 'package:flutter/material.dart';
import 'package:concentric_transition/concentric_transition.dart';
class ConcentricTransition extends StatefulWidget {
const ConcentricTransition({Key? key}) : super(key: key);
#override
State<ConcentricTransition> createState() => _ConcentricTransitionState();
}
class _ConcentricTransitionState extends State<ConcentricTransition> {
#override
Widget build(BuildContext context) {
return Scaffold(
body: ConcentricPageView(
colors: [Colors.white, Colors.blueAccent, Colors.pinkAccent],
itemBuilder: (index, value) {
return Container();
The argument type 'Container Function(int, dynamic)' can't be assigned to the parameter type 'dynamic Function(int)'. tam olarak burada sorunu alıyorum.
},
),
);
}
}
Your itemBuilder callback only takes one argument of type int (I'm guessing an index); however, your callback takes two arguments here.
Change the callback to take one argument, and it should resolve the issue:
itemBuilder: (index) {
// If you need the value, you can get it this way
final value = values[index];
return Container();
}
If you need the value to be made available to the callback, you'll need to change the type of the callback in the definition of ConcentricPageView, for example:
class ConcentricPageView extends StatelessWidget {
final Widget Function(int, dynamic) itemBuilder;
ConcentricPageView({required this.itemBuilder});
}

How can I use variable of CubitState inside Cubit? Flutter/Bloc

so I don't have any idea how to take argument from mine Cubit state which is AnswerPicked in this case, there is a code from states file.
part of 'answer_cubit.dart';
abstract class AnswerState extends Equatable {
const AnswerState();
#override
List<Object> get props => [];
}
class AnswerInitial extends AnswerState {}
class AnswerPicked extends AnswerState {
final String answer;
AnswerPicked({
this.answer,
});
String toString() => '{AnswerPicked: $answer}';
}
I want to use it in Cubit function right there:
part 'answer_state.dart';
class AnswerCubit extends Cubit<AnswerState> {
final ExamScoreCubit scoreCubit;
AnswerCubit({
#required this.scoreCubit,
}) : super(AnswerInitial());
List<String> userAnswersList = [];
void pickAnswer(String answer) {
emit(AnswerInitial());
emit(AnswerPicked(answer: answer));
}
void takeAnswer(String questionAnswer, int type) {
if(state is AnswerPicked){
userAnswersList.add(state.answer); // state.answer don't work
scoreCubit.checkAnswer(AnswerPicked().answer, questionAnswer, type); // AnswerPicked().answer don't work
}
emit(AnswerInitial());
}
}
In void takeAnswer() I don't want to pass it throw argument inside the widget tree using context. Any ideas how to do it?
userAnswersList.add((state as AnswerPicked) .answer);

How to define a default function with parameters in Dart?

I'm developing a mobile app in Flutter and have encountered a problem while trying to pass a function as a parameter to a widget.
To be more precise:
class Test extends StatefulWidget {
final Function(bool) onChanged;
const Test({Key key, this.onChanged}) : super(key: key);
#override
_TestState createState() => _TestState();
}
class _TestState extends State<Test> {
bool switchValue = false;
#override
Widget build(BuildContext context) {
return Container(
child: Switch(
value: switchValue,
onChanged: (bool value) {
setState(() => switchValue = value);
widget.onChanged(value);
}));
}
}
It throws NoSuchMethodError: "The method 'call' was called on null" when the widget is used without defining the onChanged function.
How to define a default function for the onChanged parameter? The parameter should be optional.
I have tried with:
() {} - A value of type 'Null Function( )' can't be assigned to a variable of type 'dynamic Function(bool)'.
(bool) {} - The default value of an optional parameter must be constant.
Solutions without using default value are:
to check if onChange parameter is not null before calling it, or
to define it every time when the widget is used - onChanged: (bool val) {}
Any suggestions would be appreciated.
You can define a default function like this.
void emptyFunction(bool value) {
if (value) {
// do something
} else {
// do something
}
}
const Test({Key key, this.onChanged = emptyFunction}) : super(key: key);
you can check a sample code showing this in action on dartpad. https://dartpad.dev/30fc0fdc02bec673779eebc733753c05

what's the use of `State<T extends StatefulWidget>`

I looked at the docs of dart for generics.
abstract class StringCache {
String getByKey(String key);
void setByKey(String key, String value);
}
abstract class ObjectCache {
Object getByKey(String key);
void setByKey(String key, Object value);
}
The above two is replaceed by one single generic type T with below code
abstract class Cache<T> {
T getByKey(String key);
void setByKey(String key, T value);
}
Where the use of T is seen clearly. but not sure where the state class uses
class _CounterState extends State<Counter> {
int _counter = 0;
void _increment() {
setState(() {
_counter++;
});
}
#override
Widget build(BuildContext context) {
return Row(
children: <Widget>[
RaisedButton(
onPressed: _increment,
child: Text('Increment'),
),
Text('Count: $_counter'),
],
);
}
}
T is a generic type parameter and extends StatefulWidget is a constraint for what types T can be set to.
With
class _CounterState extends State<Counter> {
T is set to type Counter (which has to be a StatefulWidget).
Passing Counter as type allows you to use
widget.foo
to reference field foo in Counter from _CounterState and you get autocompletion and static type checking.