easy_stateful_builder 0.4.0

easy_stateful_builder #

pub badge

Korean

This package is under development. Please, see the changelog to get the latest API. The example may not contain the latest API.

A helper widget to manage the state of the app. Using this package, you can manage the global states easily. This package is always under the development and not fully tested, thus I highly recommend you to use this package only for your toy project. Issues and PR are always welcome.

I was highly inspired by the source code of StatefulBuilder and GlobalKey of basic flutter widget/object.

Using this package may incur high memory usage.

Install #

Installation Guide

Example #

This package provides just one important widget called EasyStatefulBuilder. EasyStatefulBuilder takes several named parameter.

  • builder (requried, Function): A build function to create widget that you want to render. Its type is Function(BuildContext, dynamic). The first argument is the usual BuildContext context. The second argument is the snapshot of the widget's state. I will describe it later in detail.
  • identifier (required, String): The ID of the state to maintain. This ID will be used as global key, so you have to distinguish this carefully.
  • initialValue (optional, dynamic): The initial value of the state. If not given, then initial state will be set to null.
  • keepAlive (options, bool default true): Whether to keep identifier state alive even though all the widget that refers identifier state are disposed.
  • key (optional, Key): The usual Key object.

And EasyStatefulBuilder provides only one simple but powerful method EasyStatefulBuilder.setState. The type of the EasyStatefulBuilder.setState is Function(String, _EasyStateHolder). Basic usage is EasyStatefulBuilder.setState('identifier', (state) { state.nextState = state.currentState + 1 });. The state.currentState and state.nextState are the same type and they can be any type you want to use.

It is really easy to use this package so that you can find the usage by yourself through the example below.

Basic Usage #

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

void main() => runApp(MyApp());

class MyApp extends StatelessWidget {
  // This widget is the root of your application.
  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      title: 'Flutter Demo',
      theme: ThemeData(
        primarySwatch: Colors.blue,
      ),
      home: MyHomePage(),
    );
  }
}

class MyHomePage extends StatelessWidget {
  const MyHomePage({Key key}) : super(key: key);

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      floatingActionButton: FloatingActionButton(
        child: Icon(Icons.plus_one),
        onPressed: () {
          // call the `setState` of the widget identified by `counter`. 
          EasyStatefulBuilder.setState('counter', (state) {
            // `state` holds the current snapshot of the `counter` state. 
            // You can set the nextState using currentState or the other value. 
            // The nextState you want to project should be sotred in `state.nextState`.
            state.nextState = state.currentState + 1;
          });
        },
      ),
      body: EasyStatefulBuilder(
        identifier: 'counter', // this will be used as ID 
        initialValue: 0, // set initial value to `int 0` if the ID `counter` was not initialized
        builder: (context, count) { // I initialized a state to `int 0`, so the type of `count` is `int`.
          return SafeArea(
            child: Text("Counter is $count"),
          );
        },
      ),
    );
  }
}

Same identifier in multiple widget #

Same as the basic usage.

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

void main() => runApp(MyApp());

class MyApp extends StatelessWidget {
  // This widget is the root of your application.
  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      title: 'Flutter Demo',
      theme: ThemeData(
        primarySwatch: Colors.blue,
      ),
      home: MyHomePage(),
    );
  }
}

class MyHomePage extends StatelessWidget {
  const MyHomePage({Key key}) : super(key: key);

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      floatingActionButton: FloatingActionButton(
        child: Icon(Icons.plus_one),
        onPressed: () {
          // call the `setState` of the widget identified by `counter`. 
          EasyStatefulBuilder.setState('counter', (state) {
            // `state` holds the current snapshot of the `counter` state. 
            // You can set the nextState using currentState or the other value. 
            // The nextState you want to project should be sotred in `state.nextState`.
            state.nextState = state.currentState + 1;
          });
        },
      ),
      body: Column(
        children: [
          EasyStatefulBuilder<int>( // `<int>` can be skipped. 
            identifier: 'counter', // this will be used as ID 
            initialValue: 0, // set initial value to `int 0` if the ID `counter` was not initialized
            builder: (context, count) { // I initialized a state to `int 0`, so the type of `count` is `int`.
              return SafeArea(
                child: Text("Counter is $count"),
              );
            },
          ),
          EasyStatefulBuilder<int>( // `<int>` can be skipped. 
            identifier: 'counter', // Use same ID
            builder: (context, count) { // The count will be same as the above. 
              return SafeArea(
                child: Text("This also use counter $count"),
              );
            },
          ),
        ],
      ),
    );
  }
}

Using Custom object as a state #

// Custom state class
class MyState {
  int _counter;
  MyState(int counter) : _counter = counter;
  int get counter => _counter;
  int get doubleCounter => _counter * 2;
  set counter(int newCount) => this._counter = newCount;
}

// ... same as basic example code, modify FloatingActionButton -> onPressed
        onPressed: () {
          // call the `setState` of the widget identified by `counter`.
          EasyStatefulBuilder.setState('counter', (state) {
            MyState next = state.currentState as MyState;
            next.counter = next.counter + 1;
            state.nextState = next;
          });
        },
        
// ... same as multiple widget example code, modify Column -> children
          EasyStatefulBuilder(
            identifier: 'counter',
            initialValue: MyState(0),
            builder: (context, myState) {
              return Center(child: Text("Counter is ${myState.counter}"));
            },
          ),
          EasyStatefulBuilder(
            identifier: 'counter',
            builder: (context, myState) {
              return Center(
                  child: Text("Double Counter is ${myState.doubleCounter}"));
            },
          ),
    

[0.4.0] - Add immutable state getter, explicit state unregister

  • EasyStatefulBuilder.getState('identifier') returns the current state
    • You can access the current state using returnedValue.currentState
    • You can not change the current state
  • EasyStatefulBuilder.dispose('identifier') explicitly unregister the state
    • Currently, the disposing order of this method and the actual widget should be considered.

[0.3.0] - Add keepAlive parameter

  • Set state not to be disposed when keepAlive is true

[0.2.2] - Add korean description

  • Add more example
  • Add korean description
  • Fix typo

[0.2.1] - Add examples and README

[0.1.1] - Modify example and add description

[0.1.0] - First release

  • First release

[0.0.1] - TODO: Add release date.

  • TODO: Describe initial release.

example/main.dart

import 'package:flutter/cupertino.dart';
import 'package:flutter/material.dart';
import 'package:easy_stateful_builder/easy_stateful_builder.dart';

void main() => runApp(MyApp());

class MyApp extends StatelessWidget {
  // This widget is the root of your application.
  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      title: 'Flutter Demo',
      theme: ThemeData(
        primarySwatch: Colors.blue,
      ),
      home: MyHomePage(title: 'Flutter Demo Home Page'),
    );
  }
}

class MyHomePage extends StatefulWidget {
  MyHomePage({Key key, this.title}) : super(key: key);

  final String title;

  @override
  _MyHomePageState createState() => _MyHomePageState();
}

class _MyHomePageState extends State<MyHomePage> {
  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(
        title: Text(widget.title),
      ),
      floatingActionButton: FloatingActionButton(
        child: Icon(Icons.plus_one),
        onPressed: () {
          try {
            EasyStatefulBuilder.setState('counter', (state) {
              state.nextState = state.currentState + 1;
            });
            EasyStatefulBuilder.setState('counter2', (state) {
              state.nextState = state.currentState + 2;
            });
            setState(() {});
          } catch (e) {
            print(e);
          }
        },
      ),
      body: ListView(
        children: <Widget>[
          EasyStatefulBuilder(
            identifier: 'counter',
            initialValue: 0,
            builder: (context, count) {
              return Center(
                child: Text("Counter: $count"),
              );
            },
          ),
          EasyStatefulBuilder(
            identifier: 'counter2',
            initialValue: 0,
            builder: (context, count) {
              return Center(
                child: Text("Counter2: $count"),
              );
            },
          ),
          CupertinoButton(
            child: Text("Goto next page"),
            onPressed: () {
              Navigator.of(context).push(
                  MaterialPageRoute(builder: (context) => SecondScreen()));
            },
          ),
        ],
      ),
    );
  }
}

class SecondScreen extends StatefulWidget {
  SecondScreen({Key key}) : super(key: key);

  _SecondScreenState createState() => _SecondScreenState();
}

class _SecondScreenState extends State<SecondScreen> {
  @override
  Widget build(BuildContext context) {
    return Container(
      child: Center(
        child: EasyStatefulBuilder(
          identifier: 'counter2',
          builder: (context, count) {
            return Center(
              child: Text("Count2 is still $count"),
            );
          },
        ),
      ),
    );
  }
}

Use this package as a library

1. Depend on it

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


dependencies:
  easy_stateful_builder: ^0.4.0

2. Install it

You can install packages from the command line:

with Flutter:


$ flutter pub get

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

3. Import it

Now in your Dart code, you can use:


import 'package:easy_stateful_builder/easy_stateful_builder.dart';
  
Popularity:
Describes how popular the package is relative to other packages. [more]
46
Health:
Code health derived from static analysis. [more]
100
Maintenance:
Reflects how tidy and up-to-date the package is. [more]
100
Overall:
Weighted score of the above. [more]
73
Learn more about scoring.

We analyzed this package on Aug 21, 2019, and provided a score, details, and suggestions below. Analysis was completed with status completed using:

  • Dart: 2.4.0
  • pana: 0.12.19
  • Flutter: 1.7.8+hotfix.4

Platforms

Detected platforms: Flutter

References Flutter, and has no conflicting libraries.

Dependencies

Package Constraint Resolved Available
Direct dependencies
Dart SDK >=2.1.0 <3.0.0
flutter 0.0.0
Transitive dependencies
collection 1.14.11 1.14.12
meta 1.1.6 1.1.7
sky_engine 0.0.99
typed_data 1.1.6
vector_math 2.0.8
Dev dependencies
flutter_test