DEV Community

Charles Crete
Charles Crete

Posted on

7

Flutter Super State: Simple State Management for Flutter

flutter_super_state is a new state management library for Flutter which:

  • Is simple to use. Simply create modules
  • Supports async actions easily
  • Can easily be integrated with Flutter

Today we will take a look at creating a simple application with a mock authentication and a counter.

Concepts

Super State has 2 main objects:

  • Store: Which holds all of your modules
  • StoreModule: Which you can extend to implement modules

Let's take a look at a simple module:

import 'package:flutter_super_state/flutter_super_state.dart';

// Modules extend `StoreModule`
class CounterModule extends StoreModule {
  // Read only property, to avoid accidentally setting `counter` without calling `setState`
  int get counter => _counter;
  var _counter = 0;

  // This automatically registers your module to your store
  CounterModule(Store store): super(store);

  // Synchronous actions
  void increment() {
    setState(() {
      _counter++;
    });
  }

  void decrement() {
    setState(() {
      _counter--;
    });
  }

  // Asynchronous action
  Future<void> reset() async {
    // Fake async delay
    await Future.delayed(Duration(milliseconds: 10));

    setState(() {
      _counter = 0;
    });
  }
}

A store is easy to setup, here's an example:

import 'package:flutter_super_state/flutter_super_state.dart';

final store = Store();

// Register modules. Order does not matter. You should register all modules on initialization
CounterModule(store);

You can then call get modules from the store and call actions:

store.getModule<CounterModule>().increment();

await store.getModule<CounterModule>().reset();

For Flutter integration, the package provides for following widgets:

  • StoreProvider: Provides the store to all children in sub-tree
  • ModuleBuilder: Access a module inside the Flutter widget tree (is updated when module calls setState)
  • StoreBuilder: Access the store inside the Flutter widget tree (is updated when any module calls setState)

Example

Let's start by creating a new project using flutter create super_state_example and adding the package to pubspec.yaml:

dependencies:
  flutter_super_state: # Optionally put latest version from pub.dev

Next, let's clear your main.dart, and place our CounterModule from above inside lib/src/store/counter.dart. Let's create our second module, AuthModule (inside lib/src/store/auth.dart):

import 'package:flutter_super_state/flutter_super_state.dart';
import 'package:state_test/src/store/counter.dart';

class AuthModule extends StoreModule {
  int get isLoggedIn => isLoggedIn;
  var _isLoggedIn = false;

  AuthModule(Store store) : super(store);

  Future<void> login() async {
    // Do network request...
    await Future.delayed(Duration(milliseconds: 100));

    setState(() {
      _isLoggedIn = true;
    });
  }

  Future<void> logout() async {
    // Do network request...
    await Future.delayed(Duration(milliseconds: 100));

    setState(() {
      _isLoggedIn = true;
    });

    // Call action in other module
    await store.getModule<CounterModule>().reset();
  }
}

We should have our modules complete! Next, let's setup 2 screens (LoginScreen and CounterScreen):

class LoginScreen extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(
        title: Text("Login"),
      ),
      body: Center(
        // Login button
        child: ModuleBuilder<AuthModule>(
          builder: (context, authModule) {
            return RaisedButton(
              child: Text("Login"),
              onPressed: () async {
                // Call action
                await authModule.login();
                Navigator.pushReplacement(
                  context,
                  MaterialPageRoute(builder: (context) => CounterScreen()),
                );
              },
            );
          },
        ),
      ),
    );
  }
}

class CounterScreen extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(
        title: Text("Counter"),
      ),
      body: Column(
        mainAxisAlignment: MainAxisAlignment.center,
        children: <Widget>[
          // Our counter module
          ModuleBuilder<CounterModule>(
            builder: (context, counterModule) {
              return Row(
                mainAxisAlignment: MainAxisAlignment.center,
                children: <Widget>[
                  RaisedButton(
                    child: Text("Increment"),
                    onPressed: () => counterModule.increment(),
                  ),
                  Padding(
                    padding: const EdgeInsets.symmetric(horizontal: 16),
                    child: Text(counterModule.counter.toString()),
                  ),
                  RaisedButton(
                    child: Text("Decrement"),
                    onPressed: () => counterModule.decrement(),
                  ),
                ],
              );
            },
          ),
          // Logout button
          ModuleBuilder<AuthModule>(
            builder: (context, authModule) {
              return RaisedButton(
                child: Text("Logout"),
                onPressed: () async {
                  await authModule.logout();
                  Navigator.pushReplacement(
                    context,
                    MaterialPageRoute(builder: (context) => LoginScreen()),
                  );
                },
              );
            },
          ),
        ],
      ),
    );
  }
}

Next, let's setup our main.dart:

void main() {
  // Create the store
  final store = Store();

  // Register modules
  CounterModule(store);
  AuthModule(store);

  // Provide store to whole application
  runApp(StoreProvider(
    store: store,
    child: ExampleApp(),
  ));
}

class ExampleApp extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      home: LoginScreen(),
    );
  }
}

And we are done! Let's check it out:

You can view the whole project here.

Thanks for reading, don't forget to check out the package!

Hostinger image

Get n8n VPS hosting 3x cheaper than a cloud solution

Get fast, easy, secure n8n VPS hosting from $4.99/mo at Hostinger. Automate any workflow using a pre-installed n8n application and no-code customization.

Start now

Top comments (0)

Sentry blog image

The Visual Studio App Center’s retiring

But sadly….you’re not. See how to make the switch to Sentry for all your crash reporting needs.

Read more

Best practices for optimal infrastructure performance with Magento

Running a Magento store? Struggling with performance bottlenecks? Join us and get actionable insights and real-world strategies to keep your store fast and reliable.

Tune in to the full event

DEV is partnering to bring live events to the community. Join us or dismiss this billboard if you're not interested. ❤️