functional_widget 0.7.2 functional_widget: ^0.7.2 copied to clipboard
A code generator that generates widget classes from their implementation as a function.
Widgets are cool. But classes are quite verbose:
class Foo extends StatelessWidget {
final int value;
final int value2;
const Foo({Key key, this.value, this.value2}) : super(key: key);
@override
Widget build(BuildContext context) {
return Text('$value $value2');
}
}
So much code for something that could be done much better using a plain function:
Widget foo(BuildContext context, { int value, int value2 }) {
return Text('$value $value2');
}
The problem is, using functions instead of classes is not recommended:
- https://stackoverflow.com/questions/53234825/what-is-the-difference-between-functions-and-classes-to-create-widgets/53234826#53234826
- https://github.com/flutter/flutter/issues/19269
... Or is it?
functional_widgets, is an attempt to solve this issue, using a code generator.
Simply write your widget as a function, decorate it with a @widget
, and then this library will generate a class for you to use.
Example #
You write:
@widget
Widget foo(BuildContext context, int value) {
return Text('$value');
}
It generates:
class Foo extends StatelessWidget {
final int value;
const Foo(this.value, {Key key}) : super(key: key);
@override
Widget build(BuildContext context) {
return foo(context, value);
}
}
And then you use it:
runApp(
Foo(42)
);
How to use #
Install (builder) #
There are a few separate packages you need to install:
functional_widget_annotation
, a package containing decorators. You must install it asdependencies
.functional_widget
, a code-generator that uses the decorators from the previous packages to generate your widget. Install it insidebuilders
, a Flutter specific field on yourpubspec.yaml
made for code-generators.
Your pubspec.yaml
should looks like:
dependencies:
functional_widget_annotation: ^0.5.0
builders:
functional_widget: ^0.6.0
That's it! Flutter will automatically run the code generator when executing flutter build
, flutter run
or similar.
Install (build_runner) #
If your version of Flutter is too old, the previous installation method may not work.
In that case it is possible to work with functional_widget
by using build_runner
package.
First add the following to your pubspec.yaml
:
dependencies:
functional_widget_annotation: ^0.5.0
dev_dependencies:
functional_widget: ^0.6.0
build_runner: ^1.3.1
Then to run the generator, you must use build_runner
:
flutter pub pub run build_runner watch
This will watch your source folder and run the code-generator whenever something changes.
Customize the output #
It is possible to customize the output of the generator by using different decorators or configuring default values in build.yaml
file.
build.yaml
change the default behavior of a configuration.
# build.yaml
targets:
$default:
builders:
functional_widget:
options:
# Default values:
debugFillProperties: false
widgetType: stateless # or 'hook'
equality: none # or 'identical'/'equal'
FunctionalWidget
decorator will override the default behavior for one specific widget.
@FunctionalWidget(
debugFillProperties: true,
widgetType: FunctionalWidgetType.hook,
equality: FunctionalWidgetEquality.identical,
)
Widget foo() => Container();
debugFillProperties override #
Widgets can be override debugFillProperties
to display custom fields on the widget inspector. functional_widget
offer to generate these bits for your, by enabling debugFillProperties
option.
For this to work, it is required to add the following import:
import 'package:flutter/foundation.dart';
Example:
(You write)
import 'package:flutter/foundation.dart';
@widget
Widget example(int foo, String bar) => Container();
(It generates)
class Example extends StatelessWidget {
const Example(this.foo, this.bar, {Key key}) : super(key: key);
final int foo;
final String bar;
@override
Widget build(BuildContext _context) => example(foo, bar);
@override
void debugFillProperties(DiagnosticPropertiesBuilder properties) {
super.debugFillProperties(properties);
properties.add(IntProperty('foo', foo));
properties.add(StringProperty('bar', bar));
}
}
Generate different type of widgets #
By default, the generated widget is a StatelessWidget
.
It is possible to generate a HookWidget
instead (from https://github.com/rrousselGit/flutter_hooks)
There are a few ways to do so:
- Through
build.yaml
:
# build.yaml
targets:
$default:
builders:
functional_widget:
options:
widgetType: hook
- With
@FunctionalWidget
decorator:
@FunctionalWidget(widgetType: FunctionalWidgetType.hook)
Widget example(int foo, String bar) => Container();
- With the shorthand
@hwidget
decorator:
@hwidget
Widget example(int foo, String bar) => Container();
class Example extends HookWidget {
const Example(this.foo, this.bar, {Key key}) : super(key: key);
final int foo;
final String bar;
@override
Widget build(BuildContext _context) => example(foo, bar);
}
In any cases, flutter_hooks
must be added as a separate dependency in the pubspec.yaml
dependencies:
flutter_hooks: # some version number
operator== override #
It can be interesting for Widget
to override operator==
for performance optimizations.
functional_widget
optionally allows overriding both operator==
and hashCode
based on the field used.
There are two different configurations:
none
(default): Don't override anythingidentical
, overrideshashCode
andoperator==
with the latter being implemented usingidentical
to compare fields.equal
, similar toidentical
, but using==
to compare fields.
It can be configured both through build.yaml
:
# build.yaml
targets:
$default:
builders:
functional_widget:
options:
equility: identical
or using @FunctionalWidget
decorator:
@FunctionalWidget(equality: FunctionalWidgetEquality.identical)
Widget example(int foo, String bar) => Container();
All the potential function prototypes #
functional_widget will inject widget specific parameters if you ask for them. You can potentially write any of the following:
Widget foo();
Widget foo(BuildContext context);
Widget foo(Key key);
Widget foo(BuildContext context, Key key);
Widget foo(Key key, BuildContext context);
You can then add however many arguments you like after the previously defined arguments. They will then be added to the class constructor and as a widget field:
- positional
@widget
Widget foo(int value) => Text(value.toString());
// USAGE
Foo(42);
- named:
@widget
Widget foo({int value}) => Text(value.toString());
// USAGE
Foo(value: 42);
- A bit of everything:
@widget
Widget foo(BuildContext context, int value, { int value2 }) {
return Text('$value $value2');
}
// USAGE
Foo(42, value2: 24);