osam_flutter 3.1.5

  • Readme
  • Changelog
  • Example
  • Installing
  • 76

osam_flutter #

Navigation and UI elements which fit with Osam library

Osam Flutter based on osam.

Why should I use it? #

If you want to control navigation outside of widgets and persist UIState and Domain State, you probably found a solution.

Persist #

To start to restore all data from your app, you should be familiar with hive. Firstly, create two classes AppState and UIState for store domain and UI data. UIState can contain everything you want to store for widgets (textController values, scroll positions, navigation, and other things). To get states you can use Persist (look at example). AppState and UIState save automatically by PersistLifecycleWrapper witch works with Persist. Don't forget to start your app in the runApp with OsamProvider.

Core elements in navigation are OsamRoute, Navigation,TabNavigation.

Navigation works with OsamRoute's stack to push, pop, and others.

To use it, you should determine some class with routes. Example:

class UIState {
  @HiveField(0)
  var appNavigation = Navigation(PropertyNotifier([OsamRoute(appRoutes.first)]));
}

Presenters #

Osam Flutter contains Presenter and Navigation mixins for it. Presenter should be provided for widgets by PresenterProvider.

From osam recommendations Presenters should return IProperty's.

What Presenter can do? It can be useful to call usecases by getting it from usecaseContainer, contains init and dispose methods outside of the widgets and have access to AppState and UIState.

By mixing Presenter with NavigationPresenter you able to navigate in your app without context , and widgets.

Example:

class ScreenPresenter extends Presenter<UIState, AppState> with NavigationPresenter {
  @override
  Navigation get navigation => uiState.appNavigation;

  void setResult() => setRouteResult(result: 123, route: appRoutes.second);

  Future<String> showDialog() => push(OsamRoute(appRoutes.dialog, argument: currentRoute.value));

  void pushOther() => push(OsamRoute(appRoutes.second, argument: 'arg from other screen'));
}

OsamNavigator #

By using this widget you should have provided NavigationPresenter above.

OsamNavigator is a wrapper of default Navigator and decide what should be called by getting new routes stack from NavigationPresenter.

Notice: OsamNavigator able to push dialogs. To use this feature, MaterialApp must be determined with globally defined GlobalKey. You will not have problems with navigation if dialogs you use is Futures.

final navKey = GlobalKey<NavigatorState>();
final currentContext = navKey.currentState.overlay.context;

OsamNavigator is not able to found exact NavigationPresenter and that's why you should determinate generic type.

Example:

OsamNavigator<AppPresenter>(
      routes: {
        appRoutes.first: (ctx) => PresenterProvider(
              child: Screen(),
              presenter: ScreenPresenter(appRoutes.first),
            ),
        appRoutes.second: (ctx) => PresenterProvider(
              child: Screen(),
              presenter: ScreenPresenter(appRoutes.second),
            ),
        appRoutes.dialog: () async => showDialog(
            context: currentContext,
            child: Column(
              children: [
                PresenterProvider(
                  presenter: DialogPresenter(),
                  child: Builder(
                    builder: (ctx) {
                      return Material(
                        child: Column(
                          children: <Widget>[
                            Text('dialog'),
                            RaisedButton(
                              onPressed: () {
                              },
                            )
                          ],
                        ),
                      );
                    },
                  ),
                )
              ],
            ))
      },
    );

OsamRoute #

To get some argument from the previous screen you can provide it by OsamRoute. The result of push, pushReplacement and other methods is storing in OsamRoute and returning by it 's result completer. To set the result of the current route you able to call setResult in Presenter.

Finally #

Everything above is not easy to understand immediately. I recommend to look at the example and try write your own routes with different screens and dialogs.

[0.0.1] - TODO: Add release date. #

  • TODO: Describe initial release.

example/lib/main.dart

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

part 'main.g.dart';

final navKey = GlobalKey<NavigatorState>();
final currentContext = navKey.currentState.overlay.context;

const version = 2;

void main() async {
  WidgetsFlutterBinding.ensureInitialized();
  final persist = Persist<AppState, UIState>(appName: 'test');
  registerOsamFlutterAdapters();
  Hive.registerAdapter(AppStateAdapter());
  Hive.registerAdapter(UIStateAdapter());
  Hive.registerAdapter(CounterAdapter());

  final persistedVersion = await persist.version;

  if((persistedVersion ?? -1) < version){
    await persist.delete();
  }

  await persist.init();
  final appState = persist.appState ?? AppState();
  final uiState = persist.uiState ?? UIState();

  runApp(OsamProvider(
    appState: appState,
    uiState: uiState,
    version: version,
    child: PersistLifecycleWrapper(
      persist: persist,
      child: PresenterProvider(
        presenter: AppPresenter(),
        child: MaterialApp(
          navigatorKey: navKey,
          home: RootScreen(),
        ),
      ),
    ),
  ));
}

class RootScreen extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return OsamNavigator<AppPresenter>(
      routes: {
        appRoutes.first: (ctx) => PresenterProvider(
              child: Screen(),
              presenter: ScreenPresenter(appRoutes.first),
            ),
        appRoutes.second: (ctx) => PresenterProvider(
              child: Screen(),
              presenter: ScreenPresenter(appRoutes.second),
            ),
        appRoutes.dialog: () async => showDialog(
            context: currentContext,
            child: PresenterProvider(
              presenter: DialogPresenter(),
              child: Builder(
                builder: (ctx) {
                  final presenter = ctx.presenter<DialogPresenter>();
                  return Material(
                    child: Column(
                      children: <Widget>[
                        Text(presenter.cameFrom),
                        RaisedButton(
                          onPressed: () {
                            presenter.currentRoute.result = 'result from dialog';
                          },
                        )
                      ],
                    ),
                  );
                },
              ),
            ))
      },
    );
  }
}

class Screen extends StatefulWidget {
  @override
  _ScreenState createState() => _ScreenState();
}

class _ScreenState extends State<Screen> with AfterLayoutMixin {
  TextEditingController textEditingController;

  @override
  void initState() {
    textEditingController = TextEditingController();
    super.initState();
  }

  @override
  void afterFirstLayout(BuildContext context) {
    final presenter = context.presenter<ScreenPresenter>();
    textEditingController.addListener(() {
      presenter.uiState.login = textEditingController.text;
    });
    textEditingController.text = presenter.uiState.login;
  }

  @override
  Widget build(BuildContext context) {
    final presenter = context.presenter<ScreenPresenter>();
    return Scaffold(
      appBar: AppBar(
        title: Text('Argument is : ${presenter.currentRoute.argument}'),
      ),
      body: Center(
        child: Column(
          mainAxisAlignment: MainAxisAlignment.center,
          children: <Widget>[
            if (presenter.route == appRoutes.first)
              CupertinoSwitch(
                value: presenter.uiState.isSelected,
                onChanged: (value) {
                  setState(() {
                    presenter.uiState.isSelected = !presenter.uiState.isSelected;
                  });
                },
              ),
            if (presenter.route == appRoutes.first)
              TextField(
                controller: textEditingController,
              ),
            if (presenter.route == appRoutes.first)
              RaisedButton(
                child: Text('to other screen'),
                onPressed: () => presenter.pushOther(),
              ),
            if (presenter.route == appRoutes.second)
              RaisedButton(
                child: Text('set result'),
                onPressed: () => presenter.setResult(),
              ),
            RaisedButton(
              onPressed: presenter.increment,
              child: ValueListenableBuilder(
                valueListenable: presenter.counterValue,
                builder: (_, value, __) => Text(value.toString()),
              ),
            )
          ],
        ),
      ),
      floatingActionButton: FloatingActionButton(
        onPressed: () {
          presenter.showDialog();
        },
      ),
    );
  }
}

// presenters

class AppPresenter extends Presenter<UIState, AppState> with NavigationPresenter {
  @override
  Navigation get navigation => uiState.appNavigation;
}

class ScreenPresenter extends Presenter<UIState, AppState> with NavigationPresenter {
  final String route;

  ScreenPresenter(this.route);

  @override
  Navigation get navigation => uiState.appNavigation;

  IProperty<int> get counterValue => appState.counter.value;

  void increment() => appState.counter.increment();

  @override
  void init() {
    if (route == appRoutes.first)
      findRoute(appRoutes.second)?.whenComplete?.then(handleRouteResult);
    super.init();
  }

  void pushOther() =>
      push(OsamRoute(appRoutes.second, argument: 'arg from other screen')).then(handleRouteResult);

  void handleRouteResult(Object value) {
    push(OsamRoute(appRoutes.dialog, argument: value)).then((value) {
      print('got result from dialog $value');
    });
  }

  void setResult() {
    if (currentRoute.value == appRoutes.second)
      setRouteResult(result: 123, route: appRoutes.second);
  }

  Future<String> showDialog() => push(OsamRoute(appRoutes.dialog, argument: currentRoute.value));
}

class DialogPresenter extends Presenter<UIState, AppState> with NavigationPresenter {
  @override
  Navigation get navigation => uiState.appNavigation;

  String get cameFrom {
    return findRoute(appRoutes.dialog).argument.toString();
  }
}

@HiveType(typeId: hiveID.appState)
class AppState {
  @HiveField(0)
  var counter = Counter();
}

@HiveType(typeId: hiveID.counter)
class Counter {
  @HiveField(0)
  PropertyNotifier<int> _value;

  Counter([this._value]) {
    _value ??= PropertyNotifier(0);
  }

  IProperty<int> get value => _value;

  void increment() => _value.value++;
}

@HiveType(typeId: hiveID.UIState)
class UIState {
  @HiveField(0)
  var appNavigation = Navigation(PropertyNotifier([OsamRoute(appRoutes.first)]));
  @HiveField(1)
  var appTabNavigation = TabNavigation(PropertyNotifier(tabs.first));
  @HiveField(2)
  var isSelected = false;
  @HiveField(3)
  var login = '';
}

// ignore: camel_case_types
abstract class appRoutes {
  static const first = 'first';
  static const second = 'second';
  static const dialog = 'dialog';
}

// ignore: camel_case_types
abstract class tabs {
  static const first = 'first';
  static const second = 'second';
}

// ignore: camel_case_types
abstract class hiveID {
  static const appState = 0;
  static const counter = 1;
  static const UIState = 2;
}

Use this package as a library

1. Depend on it

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


dependencies:
  osam_flutter: ^3.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:osam_flutter/osam_flutter.dart';
  
Popularity:
Describes how popular the package is relative to other packages. [more]
54
Health:
Code health derived from static analysis. [more]
100
Maintenance:
Reflects how tidy and up-to-date the package is. [more]
95
Overall:
Weighted score of the above. [more]
76
Learn more about scoring.

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

  • Dart: 2.8.4
  • pana: 0.13.13
  • Flutter: 1.17.5

Analysis suggestions

Package does not support Flutter platform android

Because of import path [package:osam_flutter/osam_flutter.dart, package:osam_flutter/src/presentation/osam_provider.dart, package:osam/osam.dart, package:osam/src/domain/persist.dart, package:osam/src/persist/persist_repository.dart, package:path_provider/path_provider.dart, package:path_provider_linux/path_provider_linux.dart] that declares support for platforms: linux

Package does not support Flutter platform ios

Because of import path [package:osam_flutter/osam_flutter.dart, package:osam_flutter/src/presentation/osam_provider.dart, package:osam/osam.dart, package:osam/src/domain/persist.dart, package:osam/src/persist/persist_repository.dart, package:path_provider/path_provider.dart, package:path_provider_linux/path_provider_linux.dart] that declares support for platforms: linux

Package does not support Flutter platform macos

Because of import path [package:osam_flutter/osam_flutter.dart, package:osam_flutter/src/presentation/osam_provider.dart, package:osam/osam.dart, package:osam/src/domain/persist.dart, package:osam/src/persist/persist_repository.dart, package:path_provider/path_provider.dart, package:path_provider_linux/path_provider_linux.dart] that declares support for platforms: linux

Package does not support Flutter platform web

Because of import path [package:osam_flutter/osam_flutter.dart, package:osam_flutter/src/presentation/osam_provider.dart, package:osam/osam.dart, package:osam/src/domain/persist.dart, package:osam/src/persist/persist_repository.dart, package:path_provider/path_provider.dart] that declares support for platforms: android, ios, linux, macos

Package does not support Flutter platform windows

Because of import path [package:osam_flutter/osam_flutter.dart, package:osam_flutter/src/presentation/osam_provider.dart, package:osam/osam.dart, package:osam/src/domain/persist.dart, package:osam/src/persist/persist_repository.dart, package:path_provider/path_provider.dart] that declares support for platforms: android, ios, linux, macos

Package not compatible with SDK dart

because of import path [osam_flutter] that is in a package requiring null.

Health suggestions

Fix lib/osam_flutter.dart. (-0.50 points)

Analysis of lib/osam_flutter.dart reported 1 hint:

line 3 col 8: Unused import: 'package:flutter/cupertino.dart'.

Format lib/src/legasy/osam_builder.dart.

Run flutter format to format lib/src/legasy/osam_builder.dart.

Format lib/src/navigation/navigation_presenter.dart.

Run flutter format to format lib/src/navigation/navigation_presenter.dart.

Fix additional 3 files with analysis or formatting issues.

Additional issues in the following files:

  • lib/src/navigation/osam_navigator.dart (Run flutter format to format lib/src/navigation/osam_navigator.dart.)
  • lib/src/presentation/loading/loading_wrapper.dart (Run flutter format to format lib/src/presentation/loading/loading_wrapper.dart.)
  • lib/src/presentation/osam_provider.dart (Run flutter format to format lib/src/presentation/osam_provider.dart.)

Maintenance suggestions

The package description is too short. (-5 points)

Add more detail to the description field of pubspec.yaml. Use 60 to 180 characters to describe the package, what it does, and its target use case.

Dependencies

Package Constraint Resolved Available
Direct dependencies
Dart SDK >=2.8.0 <3.0.0
after_layout ^1.0.7+2 1.0.7+2
flutter 0.0.0
osam ^6.2.1 6.2.1
provider ^4.1.3 4.1.3
Transitive dependencies
charcode 1.1.3
collection 1.14.12 1.14.13
convert 2.1.1
crypto 2.1.5
equatable 1.2.0
file 5.2.1
hive 1.4.1+1
intl 0.16.1
meta 1.1.8
nested 0.0.4
path 1.7.0
path_provider 1.6.11
path_provider_linux 0.0.1+2
path_provider_macos 0.0.4+3
path_provider_platform_interface 1.0.2
platform 2.2.1
plugin_platform_interface 1.0.2
process 3.0.13
sky_engine 0.0.99
typed_data 1.1.6 1.2.0
vector_math 2.0.8
xdg_directories 0.1.0
Dev dependencies
build_runner any
flutter_test
hive_generator