flutter_control 0.98.7
flutter_control: ^0.98.7 copied to clipboard
Flutter Control is complex library to maintain App and State management, Dependency Injection, Navigation with Routing, Localization and more..
Flutter Control is complex library to maintain App and State management.
Library merges multiple functionality under one hood. This approach helps to tidily bound separated logic into complex solution.
- App State Management - Managing application state, localization and theme changes.
- Widget State Management - UI / Logic separation. Controlling State and UI updates.
- Dependency Injection - Factory, Singleton and Lazy initialization.
- Navigation and Routing - Routes, transitions and passing arguments to other pages and Models.
- Localization - Json based localization with basic formatting.
- Event System - Global event/data stream to easily notify app events.
Simplified structure of core classes in Flutter Control. Full diagram is at bottom of this page.
Control
with ControlFactory
is main gate and bounds everything together.
ControlWidget
holds UI and ControlModel
solves Business Logic.
Flutter Control Core
Control
Main static class. InitializesControlFactory
and provides easy access to most of core [Control] objects likeBaseLocalization
,RouteStore
,ControlBroadcast
, etc..ControlFactory
Initializes and can store Controls, Models and other objects. Dependency Injection is provided during object initialization and also on demand.
Factory has own Storage. Objects in this storage are accessible via custom key or Type. Best practice is to use Type as a key.
Factory is one and only singleton in this library.
Core objects of Flutter Control are stored in Factory's Storage by default (Control.initControl
) and are accessible by theirType
or via Providers.ControlRoot
Wraps App and initializes [Control]. It's just shortcut to start with Flutter Control. ViaControlScope
is possible to maintainState
of this root widget and control whole app state (localization, theme, etc.).
Control.initControl(
localization: LocalizationConfig(
defaultLocale: 'en',
locales: LocalizationAsset.build(locales: ['en_US', 'es_ES']),
),
entries: {
CounterListControl: CounterListControl(),
},
initializers: {
CounterModel: (_) => CounterModel(),
CounterDetailControl: (args) => CounterDetailControl(model: Parse.getArg<CounterModel>(args)),
},
routes: [
ControlRoute.build<DetailPage>(builder: (_) => DetailPage()),
],
initAsync: () async {
loadPreAppConfig();
},
);
ControlRoot
additionally offers App State management - home scree, localization and theme changes.
ControlRoot(
localization: LocalizationConfig(locales: [...]),
theme: ThemeConfig<MyThemne>(
builder: (context) => MyTheme(context),
themes: {...},
),
entries: {...},
initializers: {...},
routes: [...],
states: [
AppState.init.build(builder: (_) => LoadingPage()),
AppState.main.build(
builder: (_) => DashboardPage(),
transition: TransitionToDashboard(),
),
],
app: (setup, home) => MaterialApp(
key: setup.key,
title: setup.title('app_name', 'Example App'),
theme: setup.theme,
home: home,
locale: setup.locale,
supportedLocales: setup.supportedLocales,
localizationsDelegates: [
...
],
),
);
-
ControlWidget
is base abstract class (StatefulWidget) to maintain larger UI parts of App (Pages or complex Widgets). Widget is created with defaultControlState
to correctly reflect lifecycle of Widget to Models. So there is no need to create custom [State].
Widget will init all containing Models and pass arguments to them.
ControlWidget
is immutable so all logic parts (even UI logic and animations) must be controlled from outside. This helps truly separate all code from pure UI (also helps to reuse this code). AlsoLocalizationProvider
is part of this Widget and it's possible to fully use library's localization without delegate. This Widget comes with fewmixin
classes:RouteControl
to abstract navigation and easily pass arguments and init other Pages.TickerControl
andSingleTickerControl
to create [State] withTicker
and provide access tovsync
.
SingleControlWidget
- Focused to single ControlModel. But still can handle multiple Controls. -
StateboundWidget
- Subscribes to just oneStateControl
- a mixin class typically used with [ControlModel] - [BaseControl] or [BaseModel].
Whenever state of [StateControl] is changed, this Widget is rebuild. -
ControlModel
is base class to maintain Business Logic parts of App.
BaseControl
is extended version of [ControlModel] with more functionality. Mainly used for Pages or complex Widgets and also to separate robust Logic parts.
BaseModel
is extended but lightweight version of [ControlModel]. Mainly used to control smaller Widgets like Items in dynamic List or to separate/reuse Logic parts.
This Controls comes with fewmixin
classes to extend base functionality:RouteControlProvider
to provide navigation outside of Widget.StateControl
to control state of whole Widget.TickerComponent
passesTicker
to Model and enables to control animations outside of Widget.
ActionControl
is one type of Observable used in this Library. It's quite lightweight and is used to notify Widgets and to provide events about value changes.
Has two variants - Single (just one listener), Broadcast (multiple listeners).
On the Widget side isActionBuilder
to dynamically build Widgets. It's also possible to useActionBuilderGroup
to group values of multiple Observables.
ActionControlSub
provides read-only version of ActionControl.
Upon dismiss of ActionControl, everyControlSubscription
is closed.
final counter = ActionControl.broadcast<int>(0);
ActionBuilder<int>(
control: counter,
builder: (context, value) => Text(value.toString()),
);
FieldControl
is more robust Observable solution aroundStream
andStreamController
. Primarily is used to notify Widgets and to provide events about value changes.
Can listenStream
,Future
or subscribe to another [FieldControl] with possibility to filter and convert values.
[FieldControl] comes with pre-build primitive variants asStringControl
,DoubleControl
, etc., where is possible to use validation, regex or value clamping. And alsoListControl
to work with Iterables.
On the Widget side isFieldBuilder
andFieldStreamBuilder
to dynamically build Widgets. AlsoFieldBuilderGroup
for use with multiple Observables. It's also possible to use standardStreamBuilder
.
Value is set directly, but property can bu used privately and to public provide just sink -FieldSink
orFieldSinkConverter
and stream -FieldControlSub
interface to provide subscription to public.
Upon dismiss of FieldControl, everyFieldSubscription
is closed.
final counter = FieldControl<int>(0);
FieldBuilder<int>(
control: counter,
builder: (context, value) => Text(value.toString()),
);
Check Counter Example and TODO List Example at Git repository.
Structure below shows how data and events flows between UI and Model. ControlWidget
can use multiple `ControlModel]s** - for example one for Business Logic and one for UI/animation part.
With this approach is really easy to reuse UI/animation logic on multiple widgets and mainly separate Business Logic of Models from UI.
Other Important classes
BaseLocalization
Json based localization, that supports simple strings, plurals and dynamic structures.
Easy access viaLocalizationProvider
mixin. Localization object is stored in Factory, so is accessible without context and can be used even in Models, Entities, etc. viaControl.localization()
Localization is initialized and loaded inControl
by default.
And by defaultControlWidget
uses this localization with mixin.
Control.initControl(
localization: LocalizationConfig(
defaultLocale: 'en',
locales: LocalizationAsset.build(locales: ['en_US', 'es_ES']),
),
);
ControlRoot(
localization: LocalizationConfig(
locales: {
'en': 'assets/localization/en.json',
'es': 'assets/localization/es.json',
},
),
);
Check Localization Example and Localization Delegate Example at Git repository.
ControlBroadcast
Event stream across whole App. Default broadcaster is part ofControlFactory
and is stored there.
Every subscription is bound to it'skey
andType
so notification arrives only for expected data.
WithBroadcastProvider
is possible to subscribe to any stream and send data or events from one end of App to another, even to Widgets and their States. Also custom broadcaster can be created to separate events from global/default stream.
BroadcastProvider.subscribe<int>('on_count_changed', (value) => updateCount(value));
BraodcastProvider.broadcast('on_count_changed', 10);
ControlRoute
SpecifiesRoute
withTransition
and [WidgetBuilder] settings forRouteHandler
. WithWidgetInitializer
passingargs
to Widgets and Models during navigation.
UseRouteControl
mixin to enable this navigation with Widget andRouteControlProvider
mixin with [ControlModel]. Routes can be stored inRouteStore
and Route builder is accessible statically viaControlRoute.of
.
Control.initControl(
routes: [
ControlRoute.build<DetailPage>(builder: (_) => DetailPage()),
ControlRoute.build(key: 'detail_super', builder: (_) => DetailPage()).path('super').viaTransition(_transitionBuilder),
],
);
class ListPage extends ControlWidget with RouteControl {
Widget build(BuildContext context){
...
routeOf<DetailPage>().openRoute();
routeOf<DetailPage>().viaTransition(_transitionBuilder).openRoute();
};
}
Check Navigation Example and Navigation Stack Example at Git repository.
Other util classes
-
ControlTheme
andThemeProvider
Wraps [ThemeData], [MediaQuery] and asset path helper. -
InputField
Wrapper of [TextField] to provide more functionality and control viaInputController
. -
DisposeHandler
- mixin for any class, helps with object disposing. -
PrefsProvider
- mixin for any class, helps to store user preferences. -
FutureBlock
Retriggerable delay. -
DelayBlock
Delay to wrap a block of code to prevent 'super fast' completion and UI jiggles. -
Parse
Helps to parse json primitives and Iterables. Also helps to look up Lists and Maps for objects. -
WidgetInitializer
Helps to initialize Widgets with init data. -
UnitId
Unique Id generator based on Time, Index or just Random. -
and more..
Check set of Flutter Control Examples at Git repository for more complex solutions and how to use this library.
Full Core Structure