functional_flutter 0.0.1

  • README.md
  • CHANGELOG.md
  • Example
  • Installing
  • Versions
  • 0

Pub codecov.io

functional_flutter

Tools for composing flutter widget trees in a functional manner.

Why

Your widget tree should be a function of your applications state, so why would you write widgets as classes and not functions?

functional_flutter encourages you to separate your state and properties from your widget definition. Rather than have a class that has properties and a build function implemented, you can lift the properties into a class and write FunctionalWidgets that take a properties class and return a widget.

Properties should always be a value type. I suggest you leverage built_value or meta's @immutable annotation for your prop classes.

If everything is a pure functional widget how can my widgets have state

The withState widget enhancer lets you lift state into a functional wrapper. For example:


@immutable
class AppProps {
  final String title;
  final int count;
  final VoidCallback increment;
  final VoidCallback decrement;
  AppProps({this.title, this.count, this.increment, this.decrement});
}

FunctionalWidget<String> counterApp = withState(
  0,
  (String props, int state, SetState<int> setState) =>
      new AppProps(
        title: props,
        count: state,
        increment: () => setState((s) => s + 1),
        decrement: () => setState((s) => s - 1),
      ),
)(_appContent);

Widget _appContent(AppProps props) => new MaterialApp(
      title: props.title,
      home: new Scaffold(
        body: new Row(
          children: <Widget>[
            new RaisedButton(
              onPressed: props.increment,
              child: new Row(
                children: <Widget>[
                  new Text('Increment'),
                ],
              ),
              key: incrementButtonKey,
            ),
            new RaisedButton(
              onPressed: props.decrement,
              child: new Row(
                children: <Widget>[
                  new Text('Decrement'),
                ],
              ),
              key: decrementButtonKey,
            ),
            new Text(
              'Count: ${props.count}',
              key: counterKey,
            ),
          ],
        ),
      ),
    );

the withBuiltReduxStore enhancer also lets you subscribe to state from your built_redux store, if you are using built_redux as a state management solution.

0.0.1

initial release

example/example.dart

import 'package:functional_flutter/functional_flutter.dart';
import 'package:flutter/material.dart';
import 'package:meta/meta.dart';

void main() {
  // The following app has two views, one that shows countA and one that shows
  // countB. Depeding on which view is selected countA or countB is rendered.
  // If countA is incremented when countB is showing then the pure wrapper
  // will not rebuild the countAView widget.
  runApp(counterApp('functional flutter example'));
}

@immutable
class AppState {
  final bool showingA;
  final int countA;
  final int countB;
  AppState({
    this.showingA: true,
    this.countA: 0,
    this.countB: 0,
  });

  AppState clone({
    bool showingA,
    int countA,
    int countB,
  }) =>
      new AppState(
        showingA: showingA ?? this.showingA,
        countA: countA ?? this.countA,
        countB: countB ?? this.countB,
      );
}

@immutable
class AppProps {
  final String title;
  final AppState state;
  final VoidCallback incrementA;
  final VoidCallback incrementB;
  final VoidCallback changView;
  AppProps({
    this.title,
    this.state,
    this.incrementA,
    this.incrementB,
    this.changView,
  });
}

// counterApp is a functional component that wraps appContent
// in a statefult widget.
FunctionalWidget<String> counterApp = withState(
  // default state
  new AppState(),
  // maps the incoming props, the state from the stateful widget
  // and the setState function from the stateful widget to AppProps,
  // the result of this function is passed to appContent when counterApp
  // is invoked.
  (String props, AppState state, SetState<AppState> setState) => new AppProps(
        title: props,
        state: state,
        incrementA: () => setState((s) => s.clone(countA: s.countA + 1)),
        incrementB: () => setState((s) => s.clone(countB: s.countB + 1)),
        changView: () => setState((s) => s.clone(showingA: !s.showingA)),
      ),
)(appContent);

// appContent is a functional widget that takes AppProps
// and renders the content of the application, which is
// 3 buttons that update the app state and the current view
Widget appContent(AppProps props) => new MaterialApp(
      title: props.title,
      home: new Scaffold(
        body: new ListView(children: <Widget>[
          new RaisedButton(
            onPressed: props.changView,
            child: new Row(
              children: <Widget>[
                new Text('Change View'),
              ],
            ),
          ),
          new RaisedButton(
            onPressed: props.incrementA,
            child: new Row(
              children: <Widget>[
                new Text('Increment A'),
              ],
            ),
          ),
          new RaisedButton(
            onPressed: props.incrementB,
            child: new Row(
              children: <Widget>[
                new Text('Increment B'),
              ],
            ),
          ),
          viewBranch(props),
        ]),
      ),
    );

// viewBranch is a functional widget that gen AppProps returns
// a view that displays either counterA or counterB's value.
// Both counterView widgets are 'pure' meaning they won't rebuild
// if their props do not change. For example, if increment B is clicked
// in the parent widget and showingA is true, the build function for
// the Text widget will not be run again since the value of countA didn't change
FunctionalWidget<AppProps> viewBranch = branch(
  (props) => props.state.showingA,
  withProps<int, AppProps>((props) => props.state.countA)(
    pure(
      counterView,
    ),
  ),
  withProps<int, AppProps>((props) => props.state.countB)(
    pure(
      counterView,
    ),
  ),
);

Widget counterView(int count) => new Text('Count $count');

Use this package as a library

1. Depend on it

Add this to your package's pubspec.yaml file:


dependencies:
  functional_flutter: ^0.0.1

2. Install it

You can install packages from the command line:

with pub:


$ pub get

with Flutter:


$ flutter packages get

Alternatively, your editor might support pub get or flutter packages get. Check the docs for your editor to learn more.

3. Import it

Now in your Dart code, you can use:


import 'package:functional_flutter/functional_flutter.dart';
  
Version Uploaded Documentation Archive
0.0.1 Feb 18, 2018 Go to the documentation of functional_flutter 0.0.1 Download functional_flutter 0.0.1 archive
Popularity:
Describes how popular the package is relative to other packages. [more]
0
Health:
Code health derived from static analysis. [more]
0
Maintenance:
Reflects how tidy and up-to-date the package is. [more]
0
Overall:
Weighted score of the above. [more]
0
Learn more about scoring.

The package version is not analyzed, because it does not support Dart 2. Until this is resolved, the package will receive a health and maintenance score of 0.

Analysis issues and suggestions

Support Dart 2 in pubspec.yaml.

The SDK constraint in pubspec.yaml doesn't allow the Dart 2.0.0 release. For information about upgrading it to be Dart 2 compatible, please see https://www.dartlang.org/dart-2#migration.

Dependencies

Package Constraint Resolved Available
Direct dependencies
Dart SDK >=1.19.0 <2.0.0