simply 2.0.5 copy "simply: ^2.0.5" to clipboard
simply: ^2.0.5 copied to clipboard

Flutter made simple, Simply is a light-weight package for creating production-ready applications easily and quickly.

Simply #

Motivation #

Simply is built to help creating production-ready flutter applications faster and easier, it handles topics such as dependency injection as well as state management and let you focus on creating beautiful UIs (the main purpose of flutter).

Normal flutter application could look like the following:

void main(){
    runApp(MyApp());
}

class MyApp extends StatelessWidget{
    @override
    Widget build(BuildContext context) {
        //All code goes here!
    }
}

But this leaves us with too many logic to handle inside the app class specially as the code grows more and more, for example:

  • Execute asynchronous operations, like initialize database, log something to a remote API or intializes Firebase.
  • Rebuild the whole app when the user changes the language or the theme.
  • ...

Eventually this will lead to a mess inside that MyApp class handling both UI logic and initialization logic, what Simply does is that it helps you organize your app better.


Getting started #

All what you need to start is to use the SimpleMaterialApp class which is nothing more than a StatelessWidget that helps organizing your app.

The following is the simplest working app:

import 'package:simply/simply.dart';

void main() {
  runApp(MyApp());
}

class MyApp extends SimpleMaterialApp {
  @override
  Future<void> initialize(SimpleServiceRegistry registery) async {}
  
  @override
  MaterialApp buildApp(SimpleServiceProvider provider, String payload) 
    => MaterialApp(body: SomePage());

  @override
  Widget splashPage() => SimpleSplashPage();

  @override
  Widget startupErrorPage(String errorMessage) => SimpleStartupErrorPage(errorMessage);
}

There are 4 main methods to be overridden:

  • initialize (optional) this method handles two functionalities:
    1. Inject all the dependencies that your UIs/views will need (to be explained below).
    2. Execute all the time-consuming functionalities that needs to be called at the start of your app like preparing database, or connecting to remote API.
  • buildApp this corresponds to the normal build method where you can build your app with two main differences:
    1. It's called each time the app is reloaded not just once (to be explained below)
    2. You can consume services from the SimpleServiceProvider class which are initialized in the
  • splashPage this is a screen that the user can see while your time-consuming functionalities take place, you can use SimpleSplashPage if you don't want to implement custom splash page.
  • startupErrorPage in case any initialization error takes place, this is what the user will see, you can always use SimpleStartupErrorPage if you don't want to implement custom page for that.

Dependency Injection #

Simply passes down the dependencies along the app widget tree, however it's not directly coupled to a specific package to achieve this so it just hides the details from the consumers.

Assume we have a dependency on a certain repository responsible for fetching data from some storage, let's call it IMenuItemsRepository, we just need it to extend the ISimpleService class to be able to inject it to distinguish the services from other types.

abstract class IMenuItemsRepository extends ISimpleService {
    Future<List<String>> getMenuItemNames();
}

All what you have to do is to inject the concrete implementation of this dependency from the app level, let's say you have a class called LocalMenuItemsRepository

class MyApp extends SimpleMaterialApp {
  @override
  Future<void> initialize(SimpleServiceRegistry registery) async {
    registery.register<IMenuItemsRepository>(
        service: LocalMenuItemsRepository(),
    );
  }
  // ...
}

Then use the SimpleServiceProvider class to get the dependency from anywhere in the UI (it will be passed to the whole tree).

@override
Widget build(BuildContext context) {
    var menuItemsRepo = SimpleServiceProvider.of<IMenuItemsRepository>(context);
    // Use the dependency ..
}

This way you will keep your UI logic clean and decoupled from any implementation details.

Let's say later on you want to change the concrete implementation to another class called RemoteMenuItemsRepository all what you need to do is:

class MyApp extends SimpleMaterialApp {
  @override
  Future<void> initialize(SimpleServiceRegistry registery) async {
    registery.register<IMenuItemsRepository>(
        service: RemoteMenuItemsRepository(),
    );
  }
}

Or maybe you want to inject different dependencies based on the platform then you would do something like this:

class MyApp extends SimpleMaterialApp {
  @override
  Future<void> initialize(SimpleServiceRegistry registery) async {
    if(platform == platform.android){
        registery.register<IMenuItemsRepository>(
            service: LocalMenuItemsRepository(),
        );
    }else{
        registery.register<IMenuItemsRepository>(
            service: RemoteMenuItemsRepository(),
        );
    }
  }
}

And if you want to call time-consuming functionalities, you can do it within the same method, while users enjoy your SplashPage

class MyApp extends SimpleMaterialApp {
  @override
  Future<void> initialize(SimpleServiceRegistry registery) async {
    await Firebase.initialize();
    await SharedPrefernces.initialize();
    await prepareLocalDatabase();
    // ...
  }
}

Reloading the app #

You might want to reload the app for a global event like changing the language or the theme, you can do this easily from anywhere in the code as follows:

SimpleAppReloader.of(context).reload("Theme changed");

This will cause the whole app to be rebuilt and the method buildApp will be called with the parameter payload carrying whatever message you sent and also with passing the same service providers that you have previously registered.

class MyApp extends SimpleMaterialApp {
  @override
  MaterialApp buildApp(SimpleServiceProvider provider, String payload) {
    IThemeService themeService = provider.getService<IThemeService>();
    return CustomMaterialApp(
      widget: const HomePage(),
      theme: themeService.currentTheme,
    );
  } 
}

Simply provides navigation methods similar to the native one just to make sure that whatever dependencies are passed properly while navigation.

SimpleNavigator.of(context).push(view: TargetPage());
1
likes
140
points
103
downloads

Publisher

verified publisherwisebay.tech

Weekly Downloads

Flutter made simple, Simply is a light-weight package for creating production-ready applications easily and quickly.

Homepage

Documentation

API reference

License

BSD-3-Clause (license)

Dependencies

flutter, provider, rxdart

More

Packages that depend on simply