observable_pattern 1.1.5

  • Readme
  • Changelog
  • Example
  • Installing
  • 72

Observable Pattern #

Observable class #

//This is how initialize the observable class that holds a value and listen to changes and automatically close it's stream inside Observer Widget.
//you don't need to pass an initial value, but you can set an initial value
Observable<String>('Initial Value'); // you can type like T is the type of the value and E is the type of the error. Or
final variable = ''.rx; //You can declare your observable using extension methods like this. It's like Observable<String> 

//you can pass a function to validate the value and reproduce an error object.
 Observable.transformer = (event) {
      if (event == null || event.isEmpty) {
        // return the type of the error if something is wrong
        return 'Type your email';
      } else {
        //return null if everything is ok
        return null;
      }
    };

//You can listen to changes when the value change with addListener.
Observable.listen((event) {
// do something...
});

//you can change the value with two ways
Observable<String>.value  = 'New Value';
//or
Observable<String>.add('New Value');
//after this the object will listen to the new value and validate automatically

//you can validate manually by yourself using
Observable.validate();

//you can validate and listen to all objects into one using a static function from the object
final Observable<bool> canSubmit = Observable.combine<String>(<Observable<String>>[object1, object2]);
//validate all objects put in param;
canSubmit.validate();
if(canSubmit.value) {
// do something...
}

/*
this static function will return an Observable<bool,void> that listen for every elements inside and validate then using
Observable.validate(); and hold a bool value that indicate if every object inside is validated without error if false
it'll indicate that an object has an error.
*/ 

Observer Widget #

Observer Widget is a widget that listen changes when a value inside Observable class and change rebuild only where it's localized

in this example only the text widget will be rebuild.

   Observer<String>(
          stream: Observable.stream,
          builder: (BuildContext context, Reaction<String> snapshot) {
            return Text('${snapshot.hasData ? snapshot.data : ''}'); 
          },

ObserverInjection #

It is a simple way to inject dependencies using Inherited Widget and listen if the object injected change and rebuild everything that is inside it

//Inject
ObserverInjection<UserData>(
builder: (context) => UserData(),
child: Container(),
//If necessary you can dispose
dispose: (context, UserData user) => user.dispose(),
);

//Read
final UserData user = ObserverInjection.of<UserData>(context);

[1.1.4] - Bug fixes. #

  • Fixed a bug when you're validating dynamic objects on Observable.combine.

[1.1.3] - Added repository. #

  • Now you can make an issue or create a pull request!

[1.1.2] - Bug fixes. #

  • Bug fixed on Observable.combine was not validating correctly

[1.1.1] - Updated README. #

[1.1.0] - Breaking Changes and Bug fixes. #

  • Bug fixed when using hot reloading
  • Changed from Callbacks to Stream
  • ObserverBuilder parameters has a Reaction object
  • Observer parameters change from observable to stream

[1.0.6] - Updated docs, example and bug fixes. #

[1.0.5] - Bug fixes ad Breaking Changes. #

  • Bug fixed on dispatching notification with an object with Observable.combine, now it's async
  • Bug fixed on dispatching notification on validating with an object with Observable.combine, now callbacks wait for the other like an async process
  • Added DartDocs comments for better understanding
  • Fixed the error when an object is null after calling ObserverInjection.of
  • Observer Builder parameters has an AsyncSnapshot

[1.0.4] - Added MIT License. #

[1.0.3] - Added Description. #

[1.0.2] - Added Example. #

[1.0.1] - Added description. #

[1.0.0] - Initial version. #

example/lib/main.dart

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

//Create a store with variables of type Observable
class MyStore {
  final email = ''.rx;
  final password = ''.rx;
  Observable<bool> _canSubmit;
  final loading = false.rx;
  final done = false.rx;

  MyStore() {
    email.transformer = (event) {
      if (event == null || event.isEmpty) {
        return 'Type your email';
      } else {
        return null;
      }
    };
    password.transformer = (event) {
      if (event == null || event.isEmpty) {
        return 'Type your password';
      } else {
        return null;
      }
    };
  }

  void signIn() async {
    if (loading.value) return;
    canSubmit.validate();
    if (!canSubmit.value) return;
    loading.add(true);
    await Future.delayed(const Duration(seconds: 3));
    loading.add(false);
    done.add(true);
  }

  Observable<bool> get canSubmit {
    if (_canSubmit == null) {
      _canSubmit = Observable.combine<String>([email, password]);
    }
    return _canSubmit;
  }
}

//Initialize your app
class MyApp extends StatelessWidget {
  final String title;

  const MyApp({Key key, this.title}) : super(key: key);

  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      title: title,
      theme: ThemeData(primarySwatch: Colors.blue),
      home: LoginScreen(title: title),
    );
  }
}

//Create your own stateful screen
class LoginScreen extends StatefulWidget {
  final String title;

  const LoginScreen({Key key, this.title}) : super(key: key);

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

//Into your state class put your store and the magic will begin
class _LoginScreenState extends State<LoginScreen> {
  final store = MyStore();

  @override
  void initState() {
    super.initState();
    Future<void>.microtask(callback);
  }

  void callback() {
    final focus = FocusScope.of(context);
    store.loading.listen((bool event) {
      if (event) {
        focus.unfocus();
        //do something...
      }
    });
    store.done.listen((bool event) {
      if (event) {
        //do something...
      }
    });
  }

  @override
  Widget build(BuildContext context) {
    return Stack(
      fit: StackFit.expand,
      children: <Widget>[
        Scaffold(
          appBar: AppBar(
            centerTitle: true,
            title: Text(widget.title),
          ),
          body: Padding(
            padding: const EdgeInsets.all(24.0),
            child: Column(
              crossAxisAlignment: CrossAxisAlignment.stretch,
              mainAxisAlignment: MainAxisAlignment.center,
              children: <Widget>[
                Observer<String>(
                  stream: store.email.stream,
                  builder: (BuildContext context, Reaction<String> snapshot) =>
                      TextField(
                    decoration: InputDecoration(
                      labelText: 'E-mail',
                      errorText: snapshot.error,
                    ),
                    onChanged: store.email.add,
                  ),
                ),
                Observer<String>(
                  stream: store.password.stream,
                  builder: (BuildContext context, Reaction<String> reaction) =>
                      TextField(
                    decoration: InputDecoration(
                      labelText: 'Password',
                      errorText: reaction.error,
                    ),
                    obscureText: true,
                    onChanged: store.password.add,
                  ),
                ),
                const SizedBox(height: 24.0),
                RaisedButton(
                  shape: StadiumBorder(),
                  color: Colors.blue,
                  onPressed: store.signIn,
                  child: Text(
                    'Sign in without reactive',
                    style: TextStyle(color: Colors.white),
                  ),
                ),
                const SizedBox(height: 24.0),
                Observer<bool>(
                  stream: store.canSubmit.stream,
                  builder: (BuildContext context, Reaction<bool> reaction) =>
                      RaisedButton(
                    shape: StadiumBorder(),
                    color: Colors.blue,
                    onPressed: reaction.hasValue && reaction.value
                        ? store.signIn
                        : null,
                    child: Text(
                      'Sign in with reactive',
                      style: TextStyle(color: Colors.white),
                    ),
                  ),
                )
              ],
            ),
          ),
        ),
        Observer<bool>(
          stream: store.loading.stream,
          builder: (BuildContext context, Reaction<bool> reaction) =>
              reaction.hasValue && reaction.value
                  ? Material(
                      color: Colors.black54,
                      child: Column(
                        mainAxisAlignment: MainAxisAlignment.center,
                        children: <Widget>[
                          CircularProgressIndicator(
                            valueColor:
                                AlwaysStoppedAnimation<Color>(Colors.white),
                          ),
                        ],
                      ),
                    )
                  : Container(),
        )
      ],
    );
  }
}

//Initialize your dart app
void main() => runApp(MyApp(title: 'Observable Pattern Example'));

Use this package as a library

1. Depend on it

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


dependencies:
  observable_pattern: ^1.1.5

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:observable_pattern/observable_pattern.dart';
  
Popularity:
Describes how popular the package is relative to other packages. [more]
44
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]
72
Learn more about scoring.

We analyzed this package on Jul 9, 2020, and provided a score, details, and suggestions below. Analysis was completed with status completed using:

  • Dart: 2.8.4
  • pana: 0.13.14
  • Flutter: 1.17.5

Analysis suggestions

Package not compatible with SDK dart

Because:

  • observable_pattern that is a package requiring null.

Dependencies

Package Constraint Resolved Available
Direct dependencies
Dart SDK >=2.7.0 <3.0.0
flutter 0.0.0
Transitive dependencies
collection 1.14.12 1.14.13
meta 1.1.8 1.2.1
sky_engine 0.0.99
typed_data 1.1.6 1.2.0
vector_math 2.0.8 2.1.0-nullsafety
Dev dependencies
flutter_test