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.
Related
I have a statefull widget named Hamburger I have a class named _HamburgerState connected to this class. I have an integer value called _count inside my class named HamburgerState. I want the _count value at the time the add to cart button is pressed inside my HamburgerState class to be imported into my StatelessWidget named "CartPage". I heard it was done but I couldn't do it. When I write the code below, I get an error like this. '_HamburgerState' doesn't conform to the bound 'ChangeNotifier?' of the type parameter 'T'.
Try using a type that is or is a subclass of 'ChangeNotifier?'.
class Hamburger extends StatefulWidget {
const Hamburger({super.key});
#override
State<Hamburger> createState() => _HamburgerState();
}
class _HamburgerState extends State<Hamburger> {
int _count = 1;
void _incrementCount() {...
void _decrementCount() {...
#override
Widget build(BuildContext context) {
Color coloricon = Provider.of<iconcolor>(context).coloricon;
return ChangeNotifierProvider.value(
value: this,
child: Scaffold(...
);
}
}
class AppIcon2 extends ChangeNotifier {...
class CountNotifier extends ChangeNotifier {
int _count;
int get count => _count;
set count(int value) {
_count = value;
notifyListeners();
}
}
I tried with Provider but I don't know if it works
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;
});
}
)
I have a custom widget that uses a ListTile. I would like to set the Leading: property to a Checkbox if the Class A is building the widget, but set the Leading property to Null if Class B is building the widget.
Is it possible for the ListTile to know the name of the class that is building it?
Or is there a better way to approach this type of problem?
You can either use the is operator or use obj.runtimeType to check the type of object.
Refer to this link to understand the difference between them.
Here's an example snippet.
class CustomListTile{
var obj;
CustomListTile(this.obj);
void isSameClass(){
// if(obj.runtimeType == Truck)
if(obj is Truck){
print("Building checkbox");
}else{
print("returning Null");
}
}
}
class Chopper{
void test(){
CustomListTile obj = CustomListTile(this);
obj.isSameClass();
}
}
class Truck{
void test(){
CustomListTile obj = CustomListTile(this);
obj.isSameClass();
}
}
void main(){
Chopper objChop = Chopper();
objChop.test();
Truck objTruck = Truck();
objTruck.test();
}
Would passing a boolean like this do the job for you?
class CustomListTile extends StatelessWidget {
const CustomListTile({Key? key, this.hasLeading = false}) : super(key: key);
final bool hasLeading;
#override
Widget build(BuildContext context) {
return ListTile(
leading: hasLeading ? const Icon(Icons.person) : null,
);
}
}
class MyApp extends StatelessWidget {
#override
Widget build(BuildContext context) {
return ListView(
children: [
CustomListTile(hasLeading: true), // This one has leading
CustomListTile(), // This one does not
],
);
}
}
I am unable to access a public static boolean from a different class, eg. I have a boolean isFull in my StudyjiosListviewScreen class as shown:
class StudyjiosListviewScreen extends StatefulWidget {
#override
_StudyjiosListviewScreenState createState() => _StudyjiosListviewScreenState();
}
class _StudyjiosListviewScreenState extends State<StudyjiosListviewScreen> {
static bool isFull = false;
...
I want to use this boolean isFull in another class JoinStudyjio.
I created an instance of the StudyjiosListviewScreen class in the JoinStudyjio class like this:
StudyjiosListviewScreen listviewScreen = StudyjiosListviewScreen();
But when I try to use the boolean isFull like this:
if (listviewScreen.isFull) {
...
I get an error. I have already imported the file for the StudyjiosListviewScreen class inside the file for the JoinStudyjio class.
This is because StudyjiosListviewScreen and _StudyjiosListviewScreenState are 2 different classes.
The static variable isFull which you are trying to access is of the later one and you are trying to access it by creating an instance of the first one. If it had been a static variable of the class StudyjiosListviewScreen, you could have accessed it without even creating an instance of that class like this StudyjiosListviewScreen.isFull
If I understood your issue correctly, and following the suggestion I made in my comment, here is a code example of sharing a variable and a method to change it's value, down to two classes from a parent class:
class VariableSharing62951032 extends StatefulWidget {
#override
_VariableSharing62951032State createState() => _VariableSharing62951032State();
}
class _VariableSharing62951032State extends State<VariableSharing62951032> {
bool isFull = false;
#override
Widget build(BuildContext context) {
return Column(
children: <Widget>[
ClassA62951032(isFull: isFull, swapIsFull: swapIsFull,),
ClassB62951032(isFull: isFull, swapIsFull: swapIsFull,),
],
);
}
void swapIsFull(){
setState(() {
isFull = !isFull;
});
}
}
class ClassA62951032 extends StatefulWidget {
final bool isFull;
final Function swapIsFull;
ClassA62951032({
this.isFull,
this.swapIsFull
});
#override
_ClassA62951032State createState() => _ClassA62951032State();
}
class _ClassA62951032State extends State<ClassA62951032> {
#override
Widget build(BuildContext context) {
return Column(
children: <Widget>[
Text('Class A'),
Text(widget.isFull.toString()),
RaisedButton(
child: Text('Swap isFull'),
onPressed: () => widget.swapIsFull(),
),
],
);
}
}
class ClassB62951032 extends StatefulWidget {
final bool isFull;
final Function swapIsFull;
ClassB62951032({
this.isFull,
this.swapIsFull
});
#override
_ClassB62951032State createState() => _ClassB62951032State();
}
class _ClassB62951032State extends State<ClassB62951032> {
#override
Widget build(BuildContext context) {
return Column(
children: <Widget>[
Text('Class B'),
Text(widget.isFull.toString()),
RaisedButton(
child: Text('Swap isFull'),
onPressed: () => widget.swapIsFull(),
),
],
);
}
}
Sharing variables and methods between classes it's a huge deal in Flutter.
First of all, you are passing it in the wrong way. That variable is saved in your state widget, which is defined as private.
So, or you define it as public and than you pass a key associated with your state, or you change complitelly approach. I don't like passing keys and it is not good for production, so I will give you a better example using providers:
add provider library to your pubspec.yaml:
provider: ^4.3.1 // Or latest version
Create a class where you can save that value:
class valuesHelper {
//In this class we are storing global, dynamic values
bool _isSeen;
valuesHelper() {
this._isSeen = false;
}
void setValue(bool value) {
this._isSeen = value;
}
bool getValue(){
return this._isSeen;
}
}
Now wrap your main with the provider and pass the valuesHelper();
class MyApp extends StatelessWidget {
// This widget is the root of your application.
#override
Widget build(BuildContext context) {
return Provider(
create: (_) => valuesHelper(),
child: MaterialApp(
home: MyHomePage(),
),
);
}
}
Now call the Provider.of(context) wherever you want.
//Somwhere in your code when you have access to context:
ValueHelper helper = Provider.of<valueHelper>(context);
helper.setValue(true);
//Somwhereelse in your code when you have access to context:
ValueHelper helper = Provider.of<valueHelper>(context);
bool theValueIWant = helper.getValue();
If you have asynchronous stuff and huge state managment Blocs are even better and fancier, but for this kind of things Providers are more than enough.
I understand that StatefulWidget subclasses are intended to be immutable, and that we tend to store state in a State subclass. (Not talking advanced state management techniques here.)
What then, are the perils of storing our model/state in the StatefulWidget class instead of the State class? Since the State instance has a widget property referring to the StatefulWidget, why not store the state there?
For example:
class Foo extends StatefulWidget {
int count = 0; // state
State createState() => FooState();
}
class FooState extends State<Foo> {
Widget build(BuildContext context) {
return Column(children:[
Text(widget.count.toString()),
FloatingActionButton(
onPressed: doIt
)
]);
}
void doIt() {
setState( () {
widget.count += 1;
});
}
}
Would those concerns still apply when state is an object, passed down to the State instance?
For example:
class Foo extends StatefulWidget {
final counter = Counter(0); // state
State createState() => FooState(counter);
}
class FooState extends State<Foo> {
final counter;
FooState(this.counter);
Widget build(BuildContext context) {
return Column(children:[
Text(counter.stringValue()),
FloatingActionButton(
onPressed: doIt
)
]);
}
void doIt() {
setState( () {
counter.increment();
});
}
}
Immutability is used for performance reasons. If the widget needs to change, create a new instance set up accordingly. It's quicker to check if two instances are identical than if their state is the same.