Site icon Flutter Packages | Pub dev Packages – Flutter Mobile App World

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

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:

… 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:

Your pubspec.yaml should looks like:

dependencies:
  functional_widget_annotation: ^0.5.1

dev_dependencies:
  functional_widget: ^0.7.2
  build_runner: ^1.9.0

Then indicate that you are using generated file using Dart’s part keyword. For example, my_file.dart might look like this

import 'package:flutter/material.dart';

part 'my_file.g.dart';

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

// now you can use Foo(42)

That’s it! Flutter will automatically run the code generator when executing flutter buildflutter 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.1

dev_dependencies:
  functional_widget: ^0.7.2
  build_runner: ^1.9.0

Then to run the generator, you must use build_runner:

flutter 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:

# build.yaml
targets:
  $default:
    builders:
      functional_widget:
        options:
          widgetType: hook
@FunctionalWidget(widgetType: FunctionalWidgetType.hook)
Widget example(int foo, String bar) => Container();
@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:

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:

@widget
Widget foo(int value) => Text(value.toString());

// USAGE

Foo(42);
@widget
Widget foo({int value}) => Text(value.toString());

// USAGE

Foo(value: 42);
@widget
Widget foo(BuildContext context, int value, { int value2 }) {
  return Text('$value $value2');
}

// USAGE

Foo(42, value2: 24);

Download code generator widget source code on GitHub

https://github.com/rrousselGit/functional_widget

Exit mobile version