SkillAgentSearch skills...

Okito

Your best flutter coding friend. All in one; state management, navigation management(with dynamic routing), local storage, localization, dependency injection, cool extensions with best usages and with the support of best utilities!

Install / Use

/learn @ragokan/Okito

README

okito

Your best flutter coding friend. All in one; state management, navigation management(with dynamic routing), local storage, dependency injection, localization, cool extensions with best usages and with the support of best utilities!

pub points likes popularity GitHub Repo stars pub version GitHub last commit

Click here to join our discord community channel!

 

Features

Contents

 

State Management

Create Controller

Q: What should I do to make it work?
A: Just create a regular class and extend OkitoController, if you want to change the state, call update() or setState()

Q: How does these methods notify the state?
A: They have a conversation between themselves, whenever you call these methods, the notifier checks all the builders, if they are watching, they will be re-built. So the scheme is;

Model -> Controller -> Model
View -> Controller -> View
The controller is the root of them.

class CounterController extends OkitoController {
  int count = 0;

  void increment() => setState(() => count++);

  void decrement() {
    count--;
    update();
  }
}

CounterController counterController = CounterController();

You can also use setStateAsync function to update data asynchronously.


Use Controller

// That simple!
OkitoBuilder(
      controller: counterController,
      builder: () => Text('${counterController.count}'),
    );

Update Controller

    main(){
        // You can change state from anywhere without context!
        counterController.increment();
    }

    // In Flutter
    ElevatedButton(
                  onPressed: counterController.increment,
                  child: const Text('Increment'),
                )
    // Or
     ElevatedButton(
                  onPressed: () => counterController
                      .setState(() => counterController.count--),
                  child: const Text('Decrement'),
                )

Rockitos

Rockitos are our way to use state management with dependency injection!
If you don't know what is dependency injection, read Dependency Injection Guide first.

// Rockito - My favorite way to use Okito state.

Rockito<CounterController>(
  (controller) => Text('${controller.count}')
);
// Isn't it  simple ?
// To use _Rockito_, you should first inject the CounterController.

Okito.inject(CounterController);
// RockitoBuilder - Rockito but with more features and builder!
  RockitoBuilder<CounterController>(
    inject: CounterController(), // optionally, if you didn't inject it yet.
    builder: (controller) => Text('${controller.count}'),
    // You can use all of OkitoBuilder features here like otherControllers and etc.
  );

Watch Controller

    OkitoWatcher(
    watch: counterController,
    onChange: (CounterController controller) {
      // You can also update the state there.
      // onChange gives to you is the instance of controller.
      print(controller.count);
    },
  );
  counterController.increment();

  // OkitoWatcher also returns a function that stops watching which
  // reduces the memory usage, you can use it when your usage ends.

  final stopWatching = OkitoWatcher(/* code here */);
  // do what you want to do while watching, then:
  stopWatching();
// You can also watch with Rockitos.
RockitoWatcher<CounterController>(
  (controller) => print(controller.count))
// You have to inject the controller first.

State Methods

State methods are methods like the State class of Flutter.

class CounterController extends OkitoController{
  int count = 0;

  // This will be called whenever your [OkitoBuilder] is mounted
  // to the widget tree.
  @override
  void initState() {
    count++;
  }
  // This will be called whenever your [OkitoBuilder] is removed
  // from the widget tree.
  @override
  void dispose() {
    count = 0;
  }

  // This method will only be called when you use inject method of Okito.
  @override
  void onInject() {
    count++;
  }
}

I personally use State Methods when I use controllers as StatefulWidget replacers.
Example:

class EditProductController extends OkitoController {
  // other nodes here
  final priceFocusNode = FocusNode();

  void submitForm(){
    // I use my inputControllers here to get their values.
    // then I use my Okito routing without context!
    Okito.pushNamed('/productUpdatedSuccessfully/31')
  }

  @override
  void dispose() {
    // other nodes here to [dispose].
    priceFocusNode.dispose();
  }
}

Streams

You might have streams in your app and you might want to watch them, update state on changes etc.
This streams can also be Firestore streams, you can use it, too.

You might want to check examples folder for an example of it.

Firstly, we have to use OkitoStreamMixin

class YourController extends OkitoController with OkitoStreamMixin{
  // your code, totally same.
}
// For regular streams.
class CounterController extends OkitoController with OkitoStreamMixin {
   int count = 0;
  @override
   void onInject(){
     initStream<int>(stream: yourIntStream, onData(data) {
         count = data; // You don't need to do anything or update.
    });
  }
}
// For Firestore query stream
// To use, you have to get the snapshot as Stream of query.
class PeopleController extends OkitoController with OkitoStreamMixin {
  List<Person> people = [];
  @override  // Alternatively, you can use the constructor.
  void onInject(){
     initFirestoreQueryStream(querySnapshot: querySnapshot, onData(data) {
        // This data is a list of documents(it is a map) of your collection.
        people = data;
    });
  }
}
// For Firestore query stream
// To use, you have to get the snapshot as Stream of query.
class PersonController extends OkitoController with OkitoStreamMixin{
  Person person = Person();
  @override  // Alternatively, you can use the constructor.
  void onInject(){
     initFirestoreDocumentStream(
        documentSnapshot: documentSnapshot,
        onData(data) {
          // This data is your document as map.
          person = Person.fromMap(data);
     });
   }
}

Utilities

Navigation and using widgets without context.

Firstly, we should wrap our app with Okito or provide Okito

// Basically, you should add *Okito* to the beginning of your app or provide key/observers manually.

OkitoMaterialApp(/*  Everything is same with [MaterialApp] */);

// Or
OkitoCupertinoApp(/*  Everything is same with [CupertinoApp] */);

// Or
Material/CupertinoApp(
    navigatorKey: Okito.navigatorKey,
    navigatorObservers: [OkitoObserver()]);

Then you can use all of Okito Benefits!

All of the properities has same usages with its long usage

For example: Okito.pushNamed('/secondPage') = Navigator.of(context).pushNamed('secondPage')

Okito.width;
Okito.height;
Okito.aspectRatio;
Okito.devicePixelRatio;
Okito.isLandscape;
Okito.isPortrait;
Okito.theme;

Okito.showSnackBar();
Okito.showToast(); // Snackbar without widget, usefull for simple usage.
Okito.showModal();
Okito.showDialog();

Okito.push();
Okito.pushReplacement();
Okito.pushNamed();
Okito.pushReplacementNamed();
Okito.pop();
Okito.arguments;
Okito.routeName;

Routing Management

Lets say that you want dynamic urls like

/posts/:id
/posts/23

And this id is a dynamic variable, right?
With Okito, you can do that easily!

// You don't need to add something like OkitoPage or etc.
// Okito lets you do your job without code changes.
OkitoMaterialApp(
      routes: {
        '/': (ctx) => FirstPage(),
        '/second/:id': (ctx) => SecondPage(),
      }
);

Now, whenever you try this:

ElevatedButton(
  onPressed: () => Okito.pushNamed(
    // You can add any kind of arguments
    '/second/33?name=Rago&postId=123&isMaterial=true',
    arguments: 'This is an extra argument'),
    child: const Text('Go to second page'),
  )

It will push to second page with the argument [id] : [33]

Moreover, you will see your arguments like this:

print(Okito.arguments);
// result
{'id' : '33', 'name' : 'Rago', 'postId' : 123, 'isMaterial' : true, 'arguments': 'This is an extra argument'};
// Yes, you can even get extra arguments manually.

Check example/flutter_dynamic_routing/lib/main.dart for a live example.

Would you like to have more benefits? Of course!

Theme Management

// Firstly, the bottom method gives you the app c
View on GitHub
GitHub Stars36
CategoryCustomer
Updated4mo ago
Forks6

Languages

Dart

Security Score

92/100

Audited on Nov 17, 2025

No findings