state_property 1.1.1 icon indicating copy to clipboard operation
state_property: ^1.1.1 copied to clipboard

A library that implements the State Property pattern for resolving the behavior of systems based on their current states.

A library that implements the State Property pattern for resolving the behavior of systems based on their current states. An existing example of this pattern includes the Material State Property used by the Flutter library.

In the example from Flutter, a TextButton can resolve its styling based on whether it's in one or more states like hovered, pressed or focused.

TextButton(
    onPressed: () {},
    child: const Text('TextButton'),
    style: ButtonStyle(
        foregroundColor: MaterialStateProperty.resolveWith((states) {
            if (states.contains(MaterialState.pressed) || states.contains(MaterialState.hovered)) {
                return Colors.blue;
            }
            return Colors.grey;
        }),
    )
);

Usage 1: Generic Loading States #

Suppose we have a system that can be in 3 states: pending, success and fail. These set of states can describe any system that can take on a task and have it work or fail, such as an API call made over a network or reading data from a file system. Each state will have corresponding behavior, which we can codify with state properties as shown below:

import 'package:state_property/state_property.dart';

enum LoadingStates {
  loading,
  success,
  fail,
}

abstract class LoadingStateProperty<ResolverType>
    implements StateProperty<LoadingStates, ResolverType> {
  @override
  ResolverType? resolve(Set<LoadingStates> states);

  static StateProperty<LoadingStates, ResolverType> loading<ResolverType>(
        StatelessResolver<ResolverType> resolver) =>
    StateProperty.resolveState<LoadingStates, ResolverType>(
      resolver,
      LoadingStates.loading,
    );

  static StateProperty<LoadingStates, ResolverType> success<ResolverType>(
        StatelessResolver<ResolverType> resolver) =>
     StateProperty.resolveState<LoadingStates, ResolverType>(
      resolver,
      LoadingStates.success,
    );

  static StateProperty<LoadingStates, ResolverType> fail<ResolverType>(
        StatelessResolver<ResolverType> resolver) =>
     StateProperty.resolveState<LoadingStates, ResolverType>(
      resolver,
      LoadingStates.fail,
    );

  static StateProperty<LoadingStates, ResolverType> resolveWith<ResolverType>(
        StatefulResolver<LoadingStates, ResolverType> resolver) =>
    StateProperty.resolveWith<LoadingStates, ResolverType>(
      resolver,
    );

  static StateProperty<LoadingStates, ResolverType> all<ResolverType>(
        StatelessResolver<ResolverType> resolver) =>
    StateProperty.all<LoadingStates, ResolverType>(
      resolver,
    );

  static StateProperty<LoadingStates, Null> never<ResolverType>(
        StatelessResolver<ResolverType> resolver) =>
    StateProperty.never();
}

Our LoadingStatePropery class implements the StateProperty interface provided by the library to define a system that can be in any of our three loading states. It reuses the 4 core state properties from the library to build its loading state properties:

  1. resolveWith - The most flexible state property that allows for dynamically resolving behavior based on the provided set of states.
  final isLoadingStateProperty = LoadingStateProperty.resolveWith<bool>((states) {
    return states.contains(LoadingStates.loading);
  });

  isLoadingStateProperty.resolve({LoadingStates.loading}); // true
  isLoadingStateProperty.resolve({LoadingStates.success}); // false
  1. resolveState - Resolves the given behavior when in the provided state, otherwise null.
  final successStateProperty = LoadingStateProperty.resolveWith<bool>(() {
    return true;
  }, LoadingStates.done);

  successStateProperty.resolve({LoadingStates.done}); // true
  successStateProperty.resolve({LoadingStates.loading}); // false
  1. all - Resolves the given behavior independent of the current state of the system.
  final trueStateProperty = LoadingStateProperty.all<bool>(() {
    return true;
  });

  trueStateProperty.resolve({LoadingStates.done}); // true
  trueStateProperty.resolve({LoadingStates.loading}); // true
  1. never Resolves null as the behavior independent of the current state of the system.
  final nullStateProperty = LoadingStateProperty.all<bool>(() {
    return true;
  });

  nullStateProperty.resolve({LoadingStates.done}); // null
  nullStateProperty.resolve({LoadingStates.loading}); // null

Usage 2: Using Loading States in Widgets #

Now that we've built a state property that describes of loading states, we can apply it to a Flutter widget that supports different builders based on the current loading state.

import 'package:flutter/material.dart';
import './loading_state_property.dart';

class Loader extends StatefulWidget {
  final Future<void> Function() load;
  LoadingStateProperty<Widget> builder;

  @override
}

class _LoaderState extends State<_LoaderState> {
  LoadingState _state;

  @override
  initState() {
    super.initState();

    // Kick off loading data when the widget is first built.
    _state = LoadingStates.loading;
    widget.load().then((_resp) {
      setState(() {
        _state = LoadingStates.success;
      })
    }).catchError((e) {
      setState(() {
        _state = LoadingStates.fail;
      });
    });
  }

  @override
  build(context) {
    return widget.builder.resolve(_state) ?? SizedBox();
  }
}

// A widget that has different loading behavior based on the loading state.
class MyLoadingWidget extends StatelessWidget {
  @override
  build(context) {
    return Loader(
      builder: LoadingStateProperty.resolveWith<Widget>((states) {
        if (states.contains(LoadingStates.success)) {
          return Text('Done!');
        }

        if (states.contains(LoadingStates.fail)) {
          return Text('Something went wrong');
        }

        return const CircularProgressIndicator();
      });
    )
  }
}

// A widget that only has UI when the loading has finished successfully.
class SuccessfulLoadingWidget extends StatelessWidget {
  @override
  build(context) {
    return Loader(
      builder: LoadingStateProperty.resolveState<Widget>(() => Text('Done!'), LoadingStates.success);
    )
  }
}
0
likes
120
pub points
65%
popularity

A library that implements the State Property pattern for resolving the behavior of systems based on their current states.

Repository (GitHub)
View/report issues

Documentation

API reference

License

MIT (LICENSE)

Dependencies

flutter

More

Packages that depend on state_property