functional_widget

A code generator to write widgets as function without loosing the benefits of classes.

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');
  }
}
Dart

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');
}
Dart

The problem is, using functions instead of classes is not recommended:

... 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 @swidget, and then
this library will generate a class for you to use.

As the added benefit, you also get for free the ability to inspect the parameters
passed to your widgets in the devtool

Example

You write:

@swidget
Widget foo(BuildContext context, int value) {
  return Text('$value');
}
Dart

It generates:

class Foo extends StatelessWidget {
  const Foo(this.value, {Key key}) : super(key: key);

  final int value;

  @override
  Widget build(BuildContext context) {
    return foo(context, value);
  }

  @override
  void debugFillProperties(DiagnosticPropertiesBuilder properties) {
    super.debugFillProperties(properties);
    properties.add(IntProperty('value', value));
  }
}
Dart

And then you use it:

runApp(
  Foo(42)
);
Dart

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 as dependencies.
  • functional_widget, a code-generator that uses the decorators from the previous
    packages to generate your widget.
  • build_runner, a dependency that all applications using code-generation should have

Your pubspec.yaml should look like:

dependencies:
  functional_widget_annotation: ^0.8.0

dev_dependencies:
  functional_widget: ^0.8.0
  build_runner: ^1.9.0
YAML

That's it!

You can then start the code-generator with:

flutter pub run build_runner watch
Sh

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'
YAML

FunctionalWidget decorator will override the default behavior for one specific widget.

@FunctionalWidget(
  debugFillProperties: true,
  widgetType: FunctionalWidgetType.hook,
)
Widget foo() => Container();
Dart

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';
Dart

Example:

(You write)

import 'package:flutter/foundation.dart';

@swidget
Widget example(int foo, String bar) => Container();
Dart

(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));
  }
}
Dart

Generate different type of widgets

By default, the generated widget by @FunctionalWidget() 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
    
    YAML

    then used as:

    @FunctionalWidget()
    Widget example(int foo, String bar) => Container();
    
    Dart
  • With parameters on the @FunctionalWidget decorator:

    @FunctionalWidget(widgetType: FunctionalWidgetType.hook)
    Widget example(int foo, String bar) => Container();
    
    Dart
  • With the shorthand @hwidget decorator:

    @hwidget
    Widget example(int foo, String bar) => Container();
    
    Dart

In any cases, flutter_hooks must be added as a separate dependency in the pubspec.yaml

dependencies:
  flutter_hooks: # some version number
YAML

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);
Dart

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
@swidget
Widget foo(int value) => Text(value.toString());

// USAGE

Foo(42);
Dart
  • named:
@swidget
Widget foo({int value}) => Text(value.toString());

// USAGE

Foo(value: 42);
Dart
  • A bit of everything:
@swidget
Widget foo(BuildContext context, int value, { int value2 }) {
  return Text('$value $value2');
}

// USAGE

Foo(42, value2: 24);
Dart

GitHub

https://github.com/rrousselGit/functional_widget