wolt_modal_sheet 0.11.0

SDKflutter
Platformandroidioswindowslinuxmacosweb

This package provides a responsive modal with multiple pages, motion animation for page transitions, and scrollable content within each page.

Wolt State Management Package

Welcome to the Wolt State Management package! This internal package provides a simple and efficient way to manage and represent the state of asynchronous data in your Flutter applications.

Table of Contents


Getting Started

Adding the Package to Your Project

Since this is an internal package and not published on pub.dev, you can add it to your project using a local path. In your pubspec.yaml file, add:

dependencies:
  wolt_state_management:
    path: ./packages/state_management

Importing the Package

Import the package in your Dart code:

import 'package:wolt_state_management/wolt_state_management.dart';

Core Concepts

ValueState

ValueState<T> is a sealed class that represents the current state of a value of type T. It can be in one of three states:

  • Idle: The value is successfully loaded or initialized.
  • Loading: The value is in the process of being loaded or processed.
  • Error: An error occurred during loading or processing.

IdleValueState

Represents the successful state with an optional value.

ValueState<int> state = ValueState.idle(42);

LoadingValueState

Represents the loading state with an optional last known value.

ValueState<int> loadingState = ValueState.loading(lastKnownValue: 42);
ValueState<int> loadingState = ValueState.loading();

ErrorValueState

Represents the error state with an exception and an optional last known value.

ValueState<int> errorState = ValueState.error(Exception('An error occurred'), 42);
ValueState<int> errorState = ValueState.error(Exception('An error occurred'));

StatefulValueNotifier

StatefulValueNotifier<T> extends ValueNotifier<ValueState<T>> and implements StatefulValueListenable<T>. It provides methods to update the state and retains the last known value automatically during state transitions.

final notifier = StatefulValueNotifier<int>(0);

Methods

  • setIdle([T? value]): Sets the state to idle.
  • setLoading({T? lastKnownValue}): Sets the state to loading.
  • setError([Exception? error, T? lastKnownValue]): Sets the state to error.

StatefulValueListenableBuilder

A widget that listens to a StatefulValueListenable and rebuilds itself based on the current state.

StatefulValueListenableBuilder<int>(
  valueListenable: listenable,
  idleBuilder: (context, value, child) => Text('Value: $value'),
  loadingBuilder: (context, lastKnownValue, child) => CircularProgressIndicator(),
  errorBuilder: (context, error, lastKnownValue, child) => Text('Error: $error'),
);

Usage Examples

Basic Usage

Step 1: Create a `StatefulValueNotifier

final notifier = StatefulValueNotifier<String>();

Step 2: Update the State

// Set to idle state with a value
notifier.setIdle('Data loaded');

// Transition to loading state
notifier.setLoading();

// Transition to error state
notifier.setError(Exception('Failed to load data'));

Step 3: Listen to State Changes

notifier.addListener(() {
  final state = notifier.value;
  if (state.isIdle) {
    print('Idle: ${state.currentValue}');
  } else if (state.isLoading) {
    print('Loading, last known value: ${state.currentValue}');
  } else if (state.isError) {
    print('Error: ${(state as ErrorValueState).error}');
  }
});

Using StatefulValueListenableBuilder

ViewModel

class CounterViewModel {
  final StatefulValueNotifier<int> _counterNotifier = StatefulValueNotifier<int>(0);

  StatefulValueListenable<int> get counter => _counterNotifier;

  void increment() {
    _counterNotifier.setLoading();
    Future.delayed(Duration(seconds: 1), () {
      _counterNotifier.setIdle((_counterNotifier.currentValue ?? 0) + 1);
    });
  }

  void causeError() {
    _counterNotifier.setError(Exception('An error occurred'));
  }
}

UI

class CounterView extends StatelessWidget {
  final CounterViewModel viewModel;

  CounterView(this.viewModel);

  @override
  Widget build(BuildContext context) {
    return StatefulValueListenableBuilder<int>(
      valueListenable: viewModel.counter,
      idleBuilder: (context, value) {
        return Column(
          mainAxisAlignment: MainAxisAlignment.center,
          children: [
            Text('Value: $value'),
            ElevatedButton(
              onPressed: viewModel.increment,
              child: Text('Increment'),
            ),
            ElevatedButton(
              onPressed: viewModel.causeError,
              child: Text('Cause Error'),
            ),
          ],
        );
      },
      loadingBuilder: (context, lastKnownValue) {
        return Center(child: CircularProgressIndicator());
      },
      errorBuilder: (context, error, value) {
        return Column(
          mainAxisAlignment: MainAxisAlignment.center,
          children: [
            Text('Error: ${error.toString()}'),
            ElevatedButton(
              onPressed: viewModel.increment,
              child: Text('Retry'),
            ),
          ],
        );
      },
    );
  }
}

Additional Notes

Handling Last Known Values

The lastKnownValue is used in the Loading and Error states to retain the most recent successful value. This allows you to display or use the previous value while new data is being fetched or when an error occurs, improving user experience.

// Loading with last known value
notifier.setLoading();

// Accessing the last known value
final lastValue = notifier.currentValue;

Optimizing with the child Parameter

The child parameter in StatefulValueListenableBuilder allows you to pass a widget that does not depend on the valueListenable. This widget is not rebuilt when the state changes, improving performance.

final staticButton = ElevatedButton(
  onPressed: () {},
  child: Text('Static Button'),
);

StatefulValueListenableBuilder<int>(
  valueListenable: notifier,
  child: staticButton,
  idleBuilder: (context, value) {
    return Column(
      children: [
        Text('Value: $value'),
        child!, // Use the child here
      ],
    );
  },
  // Other builders...
);

Important:

  • Ensure that any widget passed as child does not depend on the valueListenable.
  • Use the child parameter to improve performance by avoiding unnecessary rebuilds.