Skip to content

xhsdf/simple_state

Folders and files

NameName
Last commit message
Last commit date

Latest commit

 

History

2 Commits
 
 
 
 
 
 

Repository files navigation

simple_state

Simple state management for Flutter projects.

Usage:

Define a state class extending SimpleState

class FooState extends SimpleState { ... }

Use a SimpleStateWidget (only use one SimpleStateWidget per state type)

@override
Widget build(BuildContext context) {
  return SimpleStateWidget(
    FooState(), // the initial state
    initFunction: ..., // executed on first build
    builder: (BuildContext context) => ..., // widgets that will get rebuilt on changes
  );
}

Get the current state

FooState state = SimpleState.get<FooState>();

Update the state to rebuild the SimpleStateWidget

state.copyWith(...).update();

or

FooState(...).update();

Using sub-states

To use sub-states, the type getter must be overridden

class FooState extends SimpleState {
  @override
  Type get type => FooState;
}

class FooLoadingState extends FooState { ... }
class FooLoadedState extends FooState { ... }
class FooErrorState extends FooState { ... }

Demo: Counter

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

class CounterState extends SimpleState {
  final int counter;

  CounterState({this.counter = 0});
}

void main() {
  runApp(const CounterDemo());
}

class CounterDemo extends StatelessWidget {
  const CounterDemo({super.key});

  void _incrementCounter() {
    final CounterState currentState = SimpleState.get<CounterState>();
    CounterState(counter: currentState.counter + 1).update();
  }

  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      title: 'Counter Demo',
      theme: ThemeData(colorScheme: .fromSeed(seedColor: Colors.deepPurple)),
      home: Scaffold(
        appBar: AppBar(
          backgroundColor: Theme.of(context).colorScheme.inversePrimary,
          title: Text('Counter Demo'),
        ),
        body: SimpleStateWidget(
          CounterState(),
          builder: (context) {
            return Center(
              child: Column(
                mainAxisAlignment: .center,
                children: [
                  const Text('You have pushed the button this many times:'),
                  Text(
                    '${SimpleState.get<CounterState>().counter}',
                    style: Theme.of(context).textTheme.headlineMedium,
                  ),
                ],
              ),
            );
          },
        ),
        floatingActionButton: FloatingActionButton(
          onPressed: _incrementCounter,
          child: const Icon(Icons.add),
        ),
      ),
    );
  }
}

Demo: Loading

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

class AddressState extends SimpleState {
  final List<Address> addresses;
  final bool isLoading;
  final String? error;

  AddressState({this.addresses = const [], this.isLoading = false, this.error});

  AddressState copyWith({
    List<Address>? addresses,
    bool? isLoading,
    String? error,
  }) {
    return AddressState(
      addresses: addresses ?? this.addresses,
      isLoading: isLoading ?? this.isLoading,
      error: error ?? this.error,
    );
  }
}

class AddressDemo extends StatelessWidget {
  const AddressDemo({super.key});

  Future<void> _loadAddresses() async {
    AddressState(isLoading: true).update();

    try {
      final List<Address> addresses = await _generateAddresses();
      AddressState(addresses: addresses, isLoading: false).update();
    } catch (e) {
      AddressState(
        isLoading: false,
        error: 'Failed to load addresses: $e',
      ).update();
    }
  }

  void _addAddress() {
    final AddressState state = SimpleState.get<AddressState>();
    state.copyWith(addresses: [...state.addresses, Address.random()]).update();
  }

  Widget _buildContent(BuildContext context) {
    AddressState state = SimpleState.get<AddressState>();

    if (state.isLoading) {
      return const Center(child: CircularProgressIndicator());
    }

    if (state.error != null) {
      return Center(
        child: Text(state.error!, style: const TextStyle(color: Colors.red)),
      );
    }

    if (state.addresses.isEmpty) {
      return const Center(child: Text('No addresses loaded'));
    }

    return ListView.builder(
      itemCount: state.addresses.length,
      itemBuilder: (context, index) {
        final address = state.addresses[index];
        return Card(
          margin: const EdgeInsets.symmetric(horizontal: 8, vertical: 4),
          child: ListTile(
            title: Text(address.street),
            subtitle: Text(
              '${address.city}, ${address.state} ${address.zipCode}',
            ),
            trailing: Text(address.country),
            leading: CircleAvatar(child: Text('${index + 1}')),
          ),
        );
      },
    );
  }

  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      title: 'Address Demo',
      theme: ThemeData(colorScheme: .fromSeed(seedColor: Colors.deepPurple)),
      home: Scaffold(
        appBar: AppBar(
          backgroundColor: Theme.of(context).colorScheme.inversePrimary,
          title: Text('Address List'),
        ),
        floatingActionButton: FloatingActionButton(
          onPressed: _addAddress,
          child: const Icon(Icons.add),
        ),
        body: SimpleStateWidget(
          AddressState(),
          initFunction: _loadAddresses,
          builder: (BuildContext context) => _buildContent(context),
        ),
      ),
    );
  }
}

void main() {
  runApp(const AddressDemo());
}

Future<List<Address>> _generateAddresses() async {
  await Future.delayed(const Duration(seconds: 3));
  return <Address>[for (int i = 0; i < 6; i++) Address.random()];
}

class Address {
  final String street;
  final String city;
  final String state;
  final String zipCode;
  final String country;

  Address({
    required this.street,
    required this.city,
    required this.state,
    required this.zipCode,
    required this.country,
  });

  Address.random()
    : street = <String>[
        '123 Fake Street',
        '456 Random Street',
        '789 Real Street',
      ].random,
      city = <String>['Fake City', 'Random City', 'Real City'].random,
      state = <String>['Fake State', 'Random State', 'Real State'].random,
      zipCode = <String>['12345', '54321', '13579'].random,
      country = <String>['Fake County', 'Random County', 'Real County'].random;
}

extension RandomExt<T> on List<T> {
  T get random {
    shuffle();
    return first;
  }
}

About

Simple state management for Flutter projects.

Resources

Stars

0 stars

Watchers

0 watching

Forks

Releases

No releases published

Packages

 
 
 

Contributors

Languages