Is a good practice pass Widgets as class argument in flutter? - dart

I mean create a class like this:
class HighLightAnimationState extends State<HighLightAnimation> {
HighLightAnimationState(Card this.child, this._elevation, this._boxShadow);
final Card child;
final double _elevation;
final double _boxShadow;
#override
Widget build(BuildContext context) {
return this.child;
}
}
class HighLightAnimation extends StatefulWidget {
HighLightAnimation(Card this.child, [this._elevation = 1.0, this._boxShadow = 0.0]);
final Card child;
final double _elevation;
final double _boxShadow;
#override
createState() => new HighLightAnimationState(this.child, this._elevation, this._boxShadow);
}
It remarks on Card Widget and indicates "Don't type annotate initializing formals"
When I google it, I went redirected to https://www.dartlang.org/guides/language/effective-dart/usage, so, that's why I wanna know if the thing that I am doing is right.

It's OK to pass widgets to constructors, of course. Remove the type Card from Card this.child. That type is not wrong, just unnecessary, that's why you are getting the warning.
It should be:
HighLightAnimationState(this.child, this._elevation, this._boxShadow);
HighLightAnimation(this.child, [this._elevation = 1.0, this._boxShadow = 0.0]);

Related

How to create a mixin for advanced enum and use it in a generic widget?

My goal is to write a generic Widget that, in this case, enables the user for selecting an enum value among all the values from the enum.
So I'd like to write something like so:
class WheelPickerWidget<T extends Enum> extends StatelessWidget {
/// The initial value
final T? value;
/// The onChanged callback
final void Function(T)? onChanged;
/// Retuns the wheel enum picker
const WheelPickerWidget(
{super.key, required this.value, required this.onChanged});
#override
Widget build(BuildContext context) {
return ListWheelScrollView(
physics: const BouncingScrollPhysics(),
itemExtent: 50,
diameterRatio: 0.6,
//offAxisFraction: -0.4,
squeeze: 1.8,
//useMagnifier: true,
//overAndUnderCenterOpacity: 0.8,
clipBehavior: Clip.antiAlias,
onSelectedItemChanged: (value) => onChanged?.call(T.fromValue(value)),
children: T.values.map((c) => Text("$c")).toList());
}
}
But I see T.fromValues() and T.values are generating errors as follows:
The method 'fromValue' isn't defined for the type 'Type'.
Try correcting the name to the name of an existing method, or defining a method named 'fromValue'.
The getter 'values' isn't defined for the type 'Type'.
Try importing the library that defines 'values', correcting the name to the name of an existing getter, or defining a getter or field named 'values'.
I usually write my enums as follows:
/// Theme to use for the app
enum AppTheme {
green(0),
yellow(1),
nightBlue(2);
const AppTheme(this.value);
final int value;
factory AppTheme.fromValue(int v) => values.firstWhere((x) => x.value == v,
orElse: () => throw Exception("Unknown value $v"));
/// Returns the name corresponding to the enum
#override
String toString() {
switch (this) {
case green:
return i18n_Green.i18n;
case yellow:
return i18n_Yellow.i18n;
case nightBlue:
return i18n_Night_blue.i18n;
}
}
}
Where I make fromValue() readily available.
And I guess I could use mixin to create a specific form of enum that complies to the requirements.
/// Advanced enum
mixin EnumMixin {
}
But I didn't manage to do it: one reason is the factory cannot be supported by the mixin.
So to sum up, my questions are:
How to make my wheel picker class works with my enum?;
How to create a generic way (possibly being a mixin) to conform all my enums to a way it can be supported by my generic wheel picker?
You cannot make T.someConstructor() or T.someStaticMethod() work for some generic type T. Dart does not consider constructors and static methods to be part of the class interface, and they are not inherited.
In general, whenever you want to use something like T.someConstructor() or T.someStaticMethod(), you're probably better off using a callback instead. Similarly, instead of using T.values, you can accept a List<T> argument.
For example:
class WheelPickerWidget<T extends Enum> extends StatelessWidget {
WheelPickerWidget({required this.values, required this.fromValue});
final List<T> values;
final T Function(int) fromValue;
...
#override
Widget build(BuildContext context) {
return ListWheelScrollView(
...
onSelectedItemChanged: (value) => onChanged?.call(fromValue(value)),
children: values.map((c) => Text("$c")).toList());
}
}
and then callers would use:
WheelPickerWidget(values: AppTheme.values, fromValue: AppTheme.fromValue);
Note that fromValue is a bit redundant in principle if you already have values; you could just iterate over values to find the Enum you want. For example, you could do:
abstract class HasValue<T> {
T get value;
}
enum AppTheme implements HasValue<int> {
green(0),
yellow(1),
nightBlue(2);
const AppTheme(this.value);
#override
final int value;
...
}
class WheelPickerWidget<T extends Enum> extends StatelessWidget {
WheelPickerWidget({required this.values})
final List<T> values;
...
#override
Widget build(BuildContext context) {
return ListWheelScrollView(
...
onSelectedItemChanged: (value) => onChanged?.call(values.findValue(value)),
children: values.map((c) => Text("$c")).toList());
}
}
extension<T extends Enum> on List<T> {
T findValue<U>(U value) {
for (Object e in this) {
if (e is HasValue<U> && e.value == value) {
return e as T;
}
}
throw Exception("Unknown value $value");
}
}
Unfortunately, findValue is slightly awkward because there doesn't seem to be a good way to enforce that T derives from both Enum and HasValue, so it must perform runtime type-checking. Additionally, Dart will not perform automatic type promotion between unrelated types (in this case, Enum and HasValue), so findValue upcasts to Object first as a workaround.
If you don't want callers to pass extra arguments, one alternative would be to store those arguments in a global lookup table with the generic type as the key. This isn't a great general approach since a Map<Type, ...> depends on exact Type matches, so looking up a subtype wouldn't match a supertype in the Map. However, Enums are not allowed to be extended nor implemented, so that is not a concern. I would consider it to be less robust, however, since it would require extra work to initialize such a Map, and there's no way at compile-time that it's been initialized with all of the types you care about. As an example of how this could look:
final _fromValueMap = <Type, Enum Function(int)>{
AppTheme: AppTheme.fromValue,
};
final _lookupValuesMap = <Type, List<Enum>>{
AppTheme: AppTheme.values,
};
T fromValue<T>(int value) => _fromValueMap[T]!(value) as T;
List<T> lookupValues<T>() => _lookupValuesMap[T]! as List<T>;
class WheelPickerWidget<T extends Enum> extends StatelessWidget {
...
#override
Widget build(BuildContext context) {
return ListWheelScrollView(
...
onSelectedItemChanged: (value) => onChanged?.call(fromValue<T>(value)),
children: lookupValues<T>().map((c) => Text("$c")).toList());
}
}

How to make the function type safe?

I am pretty new in Dart and I would like to know, how to make Function defined as a property more type safe:
class NewTransaction extends StatelessWidget {
final titleController = TextEditingController();
final amountController = TextEditingController();
final Function addNewTx;
NewTransaction(this.addNewTx);
With type safety I mean, that I can determine what are the inputs and outputs. Now I can pass anything to Function object.
When declaring a Function parameter, declare the return type and parameters.
In your example, I will imagine a void return type, and a String parameter. As a result:
class NewTransaction extends StatelessWidget {
final titleController = TextEditingController();
final amountController = TextEditingController();
final void Function(String) addNewTx;
NewTransaction(this.addNewTx);
}

Update widget from other widget if they are in 2 different dart file

Creating a sample calculator app in flutter. But no idea how to communicate each widgets, which are different dart files.
Main.dart ---------- ( starting point of the application)
CalculatorFrame.dart --------- ( importing numberboards , entry textbox and
forms calculator view)
NumberButton.dart -------- ( represent the button widget)
NumberBoards.dart ---------- ( import numberbutton and creates the buttons ( 0-9) portion of the calculator)
EntryTextbox.dart ----------- ( represent the textbox entry for button pressed action and calculated values)
I have a difficulty to implement the interaction with these widgets. On button pressed, need to update the EntryTextbox widget. How can it be done if the widgets are in different dart files.
Please suggest.
1.) You need to make your CalculatorFrame a Stateful widget.
2.) You need to create a state for your EntryTextbox widget like a userInput state to track what the current user input was.
3.) You need to create a method to update the state by using the setState method that is built in when using the Stateful widget.
4.) You need to pass that method to the NumberBoards then create a constructor to receive that method then pass the method to the NumberButton widget.
To change the state of EntryTextbox widget when a button was pressed, declare a containing widget (I suppose it's CalculatorFrame) as StatefullWidget with String or whatever field, which you will initialize EntryTextbox from. Implement a function that changes that field when a button is pressed, and pass it as a constructor parameter to a NumberButton widget.
Here is a schematic example:
import 'package:flutter/material.dart';
class CalculatorFrame extends StatefulWidget {
#override
_CalculatorFrameState createState() => _CalculatorFrameState();
}
class _CalculatorFrameState extends State<CalculatorFrame> {
String _text;
#override
Widget build(BuildContext context) => ...( // container widget of your choice
EntryTextbox(text: _text),
NumberButton(text: '0', onPressed: _onPressed,),
...,
NumberButton(text: '9', onPressed: _onPressed,),
);
void _onPressed(String buttonText) =>
setState(() => _text += buttonText);
}
class EntryTextbox extends StatelessWidget {
final String text;
const EntryTextbox({Key key, this.text}) : super(key: key);
#override
Widget build(BuildContext context) => ...(text: text); // text entry widget of your choice
}
class NumberButton extends StatelessWidget {
final String text;
final Function(String) onPressed;
const NumberButton({Key key, this.onPressed, this.text}) : super(key: key);
#override
Widget build(BuildContext context) => ...Button( // button widget of your choice
child: Text(text),
onPressed: () => onPressed(text),
);
}
Since in your example NumberButton widgets sit inside NumberBoards widget, it's NumberBoards responsibility to accept onPressed as a constructor parameter and pass it to each NumberButton constructor.
In my example, onPressed accepts String parameter - text of the button which was pressed. You might decide to pass int or perhaps a more complex data class.
This non-architectural approach works fine for tiny apps and samples. However, I'd rather recommend using BLoC or ScopedModel for your task. Simple app state management section of the official documentation might be helpful.

What does the Object<type> syntax mean in Dart?

In the following code example, from the flutter docs:
class RandomWords extends StatefulWidget {
#override
createState() => RandomWordsState();
}
class RandomWordsState extends State<RandomWords> {
#override
Widget build(BuildContext context) {
final wordPair = WordPair.random();
return Text(wordPair.asPascalCase);
}
}
What exactly does the State<RandomWords> syntax mean?
I understand that you can specify the type for the objects contained in a collection, like lists, using this syntax - List <String>
But I cannot understand the motive behind State<RandomWords>.
Moreover, how can you reference RandomWordsState in RandomWords declaration and also reference RandomWords in RandomWordsState declaration? Shouldn't that cause a circular reference error or something?
I come from dynamically typed languages like python, and this looks a little odd to me, can someone please point me to the right place?
<RandomWords> is a generic type parameter passed to the State class.
The State class looks like
abstract class State<T extends StatefulWidget> extends Diagnosticable {
and RandomWords will be passed to the T type parameter which has a constraint that T needs to be a subclass of StatefulWidget.
State also has a field and getter where the type parameter is used
T get widget => _widget;
T _widget;
This results in a property of the type of the widget
which provides proper autocompletion and type checks in its subclass RandomWordsState
Assume you have
class RandomWords extends StatefulWidget {
RandomWords({this.fixed});
final WordPair fixed;
#override
createState() => RandomWordsState();
}
class RandomWordsState extends State<RandomWords> {
#override
Widget build(BuildContext context) {
// vvvv here we can access `fixed` in a strongly typed manner
final wordPair = widget.fixed ?? WordPair.random();
return Text(wordPair.asPascalCase);
}
}
See also https://www.dartlang.org/guides/language/language-tour#generics

In Flutter, how can I define a baseline for a Stateless widget?

I have created a stateless widget, and I want to define a baseline for it, so that I can use it with the Baseline widget (https://docs.flutter.io/flutter/widgets/Baseline-class.html).
How can I do that?
If you want to define a baseline you can't do it directly in the stateless widget. You need to mess around its corresponding RenderBox that needs to implement the computeDistanceToActualBaseline() method.
Give a look to the ListTile implementation here. You will see that the _RenderListTile RenderBox implements the above method returning the baseline of the title widget.
#override
double computeDistanceToActualBaseline(TextBaseline baseline) {
assert(title != null);
final BoxParentData parentData = title.parentData;
return parentData.offset.dy + title.getDistanceToActualBaseline(baseline);
}
In this case, the baseline of the title is the bottom of the Text widget.
All this is needed because the Baseline widget tries to get the baseline of the child widget. If you don't provide a explicit baseline with the above method, it just uses its bottom position.
You can find below an example of a BaselineBox where you can set an arbitrary baseline from top.
import 'package:flutter/widgets.dart';
import 'package:flutter/foundation.dart';
import 'package:flutter/rendering.dart';
class BaselineBox extends SingleChildRenderObjectWidget {
const BaselineBox({Key key, #required this.baseline, Widget child})
: assert(baseline != null),
super(key: key, child: child);
final double baseline;
#override
RenderBaselineBox createRenderObject(BuildContext context) =>
new RenderBaselineBox(baseline: baseline);
#override
void updateRenderObject(
BuildContext context, RenderBaselineBox renderObject) {
renderObject.baseline = baseline;
}
#override
void debugFillProperties(DiagnosticPropertiesBuilder properties) {
super.debugFillProperties(properties);
properties.add(new DoubleProperty('baseline', baseline));
}
}
class RenderBaselineBox extends RenderProxyBox {
RenderBaselineBox({
RenderBox child,
#required double baseline,
}) : assert(baseline != null),
assert(baseline >= 0.0),
assert(baseline.isFinite),
_baseline = baseline,
super(child);
double get baseline => _baseline;
double _baseline;
set baseline(double value) {
assert(value != null);
assert(value >= 0.0);
assert(value.isFinite);
if (_baseline == value) return;
_baseline = value;
markNeedsLayout();
}
#override
double computeDistanceToActualBaseline(TextBaseline baselineType) {
return _baseline;
}
#override
void debugFillProperties(DiagnosticPropertiesBuilder properties) {
super.debugFillProperties(properties);
properties.add(new DoubleProperty('baseline', baseline));
}
}

Resources