Is creating a stateful widget with a generic data type <T> passed to the state in Flutter a good practice? - flutter

I have created a widget in Flutter as follows:
import 'package:flutter/material.dart';
import 'package:flutter_bloc/flutter_bloc.dart';
class WidgetA<T> extends StatefulWidget {
const WidgetA({
super.key,
required this.errorSelector,
...
});
final Function errorSelector;
...
#override
State<WidgetA> createState() =>
_WidgetAState<T>();
}
class _WidgetAState<T> extends State<WidgetA> {
...
#override
Widget build(BuildContext context) {
...
return Builder(
builder: (ctx) {
final String foo =
ctx.select(widget.errorSelector as String Function(T));
return Text(foo);
}
);
}
}
Is this practice okay?
Are there better ways to accomplish this?
Can this cause any issues?
FYI - T is being used to pass a Class that extends a Bloc.

it's not bad and it's not good unless you have a good reason to do it (for example if you want to customize a data type based on that generic ).
so before doing it, ask yourself why so I need to make it generic, all the good patterns in the code are there to add some value to the code.
as you can see the only place where the generic is important is to set it in the State object, this prevents conflicting your StatefulWidget in your app with others and specify it to one StatefulWidget

Related

what does it mean when we see people calling widget in dart?

I have seen many times people calling widget. sth inside the code.
May I know what it is actually doing?
For example code below, (highlighted part is my confusion)
class _MyOwnClassState extends State<MyOwnClass> {
#override
Widget build(BuildContext context) {
return ListTile(
title: Container(
child: Column(children: makeWidgetChildren(**widget.jsonObject)**),
),
);
}
}
In flutter's StatefulWidget, we have the following architecture.
You have a StatefulWidget like this,
class MyOwnClass extends StatefulWidget {
State createState () => _MyOwnClassState();
}
And you have a State class for your StatefulWidget like this,
class _MyOwnClassState extends State<MyOwnClass> {
}
Now, State class is meant to house variables that tend to change in order for your UI to be rebuilt.
So you can have variables in your State that you can update using setState.
But what if you had some data that doesn't change and you want to avoid putting them inside the State class.
That's where your StatefulWidget comes to play.
You can store variables in your MyOwnClass and the widget variable inside the State class gives you a way to access them.
For example,
class MyOwnClass extends StatefulWidget {
int numberThatDoesntChange = 1;
State createState () => _MyOwnClassState();
}
You can access them in your State class like this,
class _MyOwnClassState extends State<MyOwnClass> {
Widget build(BuildContext context) {
return Text('$widget.numberThatDoesntChange');
}
}
Apart from this, your StatefulWidget has many more internal instance members that you can access inside of your State class using the widget variable.
The widget refers to the actual view that renders on the screen. It extends the StatefulWidget class of the flutter framework and overrides the createState() method. The createState() method is used to create the instance of state class. We will look into createState().
The state class is used to maintain the state of the widget so that it can be rebuilt again. It extends the State class of the flutter framework and overrides the build method.
The framework calls build() method again and again whenever setState() method is called. The setState() method notifies the framework that the internal state of this object has changed and it should be rebuilt. Suppose we change the value of text in StatefulWidget then we need to call setState().
Edit As Nisanth pointed outh in his comment - I missed your question completely; please ignore the below....
Let me try my answer, I don't think others are getting your point.
In your exapmle, Column(children: x) expect a list of Widgets.
You have two options - either provide this list directly:
class _MyOwnClassState extends State<MyOwnClass> {
#override
Widget build(BuildContext context) {
return ListTile(
title: Container(
child: Column(children: <Widget>[SomeWidget()]),
),
);
}
}
Or if you have more complex code that generates widget - based on input parameters, or you have the same widget generated multiple times and you want to avoid the code duplication - you would create the separate function to do the job.
Something like:
class _MyOwnClassState extends State<MyOwnClass> {
List<Widget> makeWidgetChildren(int param) {
/*
some very complex logic here
/*
if (param>3 && param<4) {
return List<Widget>.generate(4, (index)=>SomeWidget1(index));
} else {
return <Widget>[Center()];
}
}
#override
Widget build(BuildContext context) {
return ListTile(
title: Container(
child: Column(children: makeWidgetChildren(**widget.jsonObject)**),
),
);
}
}
So basically, it is just to make the code nicer; and to avoid having code repeated over and over again in the build function.

How to use Provider/Consumer when extending a class

To start, I'm new to Flutter, so I am completely open to the possibility that my problem stems from a fundamental misunderstanding, but here is my question:
I am trying to get a good understanding of how to use Provider in conjunction with with the get_it package.
I think I understand how to use the Provider pattern in the standard case, by which I mean creating a unique class with a view and a view_model. Where I seem to have become lost is when I design a custom widget as a base template class and then extend that widget so that it can be tailored for use in a specific class view, I'm not seeing how to connect it to the Provider pattern because the base class doesn't know in advance which view_model it needs to listen to.
Below I will provide short example of what I am doing in the standard case, where things seem to work fine, and then I will show a short example of how I am trying to build the custom widget and extend it...
Here is the sample standard way in which I am using the Provider pattern with get_it, in which everything seems to work just fine:
class MyScreenView extends StatefulWidget{
#override
_ProfileEditScreenViewState createState() => _ProfileEditScreenViewState();
}
class _MyScreenViewState extends State<MyScreenView>{
final MyScreenViewModel model = serviceLocator<MyScreenViewModel>();
#override
Widget build(BuildContext context) {
return ChangeNotifierProvider<MyScreenViewModel>(
create: (context) => model,
child: Material(
color: Colors.white,
child: Consumer<MyScreenViewModel>(
builder: (context,model,child) => Text(model.someText),
),
),
);
}
}
class MyScreenViewModel extends ChangeNotifier{
String? _someText;
MyScreenViewModel() {
this._someText= 'Sample Text';
}
String get someText=> _someText;
set someText(String value) {
_someText= value;
notifyListeners();
}
}
Here is an example of how I am trying to build a base class, but am uncertain as to how I go about connecting it to Provider: (The idea here is that the below widget would be part of a more complex widget that would have a view_model where the state for the overall widget would be maintained)
class BaseCheckBoxTile extends StatefulWidget{
bool isChecked;
Function(bool) checkBoxOnChanged;
BaseCheckBoxTile({this.isChecked = false, required this.checkBoxOnChanged});
#override
_BaseCheckBoxTileState createState() => _BaseCheckBoxTileState();
}
class _BaseCheckBoxTileState extends State<BaseCheckBoxTile>{
#override
Widget build(BuildContext context) {
return SizedBox(
child: Checkbox(value: widget.isChecked,onChanged: widget.checkBoxOnChanged,),
);
}
}
class CustomCheckBoxTile extends BaseCheckBoxTile{
bool isChecked;
Function(bool) checkBoxOnChanged;
CustomCheckBoxTile({this.isChecked =false, required this.checkBoxOnChanged})
:super(isChecked: isChecked, checkBoxOnChanged: checkBoxOnChanged);
}
My instinct is to want to put something in my _BaseCheckBoxTileState that gives me access to the larger widget's view_model, like what I do in the first example with:
"MyScreenViewModel model = serviceLocator<MyScreenViewModel>(); "
If I had that, then I could assign the values in my _BaseCheckBoxTileState by referring to the model instead of widget (e.g., model.isChecked instead of widget.isChecked). The model would obviously extend ChangeNotifier, and the view that is making use of the custom widget would wrap the widget in a Consumer. However, the _BaseCheckBoxTileState doesn't know what view_model to listen to.
Would I accomplish this by putting some generic Type or Object in for my View_Model which could be assigned when the class is built? Or am I approaching this in a completely wrong way?

Multiple questions related to Flutter basic

I am coming from React and trying to understand Flutter. There are some differences between dart vs JS and React vs Flutter. However, it seems a bit similar to each other. However, there is some confusion toward Flutter.
void
It does not return the value. Therefore, it is used to void main(). Does it mean like the function 'main()' will be executed and I will never be able to reuse void main()?
<>
In the code, I have here
class LoginScreen extends StatefulWidget {
createState() {
return new LoginScreenState();
}
}
class LoginScreenState extends State<LoginScreen> {
Widget build(context) {
return Container(
margin: EdgeInsets.all(20.0),
child: Column(
children: [
emailField(),
// passwordField(),
// submitButton(),
],
));
}
}
I can see that State<LoginScreen> is related to class LoginScreen. It is an extension of the LoginScreen State?
Future<int> successor = future.then((int value) {
},
However, in here Future <----- I do not understand why it has to state 'int' in this code. What is the proper use of <>?
Stateful vs Stateless
Can I just use the Stateful all the time even if I do not have any state like React? Where there is no difference?
You need to learn basic dart.
void main() is same as main(). The only difference is declaring the return type. main() is called only once by the flutter engine.
Read about generics. <> is the called the diamond operator and is used to specify the type of something. Future<int> means the function will return something in future and it will strictly be of int type. If you don't specify the type flutter will treat it as dynamic.
You can use stateful classes if you are using state management packages like provider or rxdart to maintain state.
class LoginScreen extends StatefulWidget {
createState() {
return new LoginScreenState();
}
}
enter code here
changed to
class LoginScreen extends StatefulWidget {
LoginScreenState createState()=>LoginScreenState();
}
}
enter code here
Stateful vs Stateless`enter code here`
stateful has state object and stateless has no state object.using stateful you can changes widget data .on other hand in stateful you cant

Flutter: How can I avoid recreating the model for parts of the application?

My app uses a set of Card()s inside a PageView(), each card has multiple text lines.
The user gives input for each line (e.g. modifying the text etc.).
I want to keep this input for a while.
This is how my code looks at the moment, abstractly speaking:
class MyCard extends StatefulWidget {
final List<Widget> _myLines = [];
#override
State<StatefulWidget> createState() => MyCardState();
}
class MyCardState extends State<MyCard> {
...
#override
Widget build(BuildContext context) {
...
widget._myLines.add(ChangeNotifierProvider(
create: (context) => MyLineModel(context, lineText),
child: RecipeLine())
...
}
}
This doesn't work well:
As soon as I swipe left / right through the PageView onto other cards and then swipe back, the Card is being built again. This also leads to a rebuild of MyLineModel, which in turn erases all the user's input.
How can I avoid the rebuild of MyLineModel and keep the user's input?
You can solve this in 2 ways:
Create the model outside the widget and pass the model for the widget, as a variable to a constructor for example, or using Provider or any other technique of Dependency Injection.
Using any of the KeepAlive APIs, such as the AutomaticKeepAliveClientMixin as:
class MyCardState extends State<MyCard> with AutomaticKeepAliveClientMixin {
...
#override
bool get wantKeepAlive => true;
}
As the official documentation states, we can simply use the ChangeNotifierProvider.value() constructor...
In the original code:
class MyCardState extends State<MyCard> {
...
#override
Widget build(BuildContext context) {
...
widget._myLines.add(ChangeNotifierProvider.value(
value: MyLineModel(context, lineText),
child: RecipeLine())
...
}
}

Why build method isn't defined inside StatefulWidget?

I'm currently learning Flutter. I tried to deep dive into Flutter Widget life-cycle, and I wonder why StatefulWidget are written like this :
class Example extends StatefulWidget {
#override
_ExampleState createState() => _ExampleState();
}
class _ExampleState extends State<Example> {
// initState
// setState
// ...
#override
Widget build(BuildContext build) {
...
}
}
but not :
class Example extends StatefulWidget {
// initState
// setState
// ...
#override
Widget build(BuildContext build) {
...
}
}
I think the latter makes the source simple. But I don't know why they're using the former style ?
The reason why StatefulWidget uses a separate State class and not having build method inside its body is because all fields inside a Widget are immutable, and this includes all its sub-classes.
You might have noticed that StatelessWidget has its build and other associated methods defined inside it, but that was possible due to the nature of StatelessWidget which is rendered completely using the provided info, and doesn't expect any future change in its State.
In the case of StatefulWidget, State information occasionally change (or expected to change) during the course of the app, thus this information isn't suitable for storage in a final field (build) to satisfy Widget class conditions (all fields are immutable). That's why State class is introduced. You just have to override the createState function to attach your defined State to your StatefulWidget, and let all that change happens in a separate class.