Skip to content

Instantly share code, notes, and snippets.

@marcguilera
Created April 11, 2018 09:19
Show Gist options
  • Save marcguilera/c5828ba2f58a11f1f61a8e3bceaabc80 to your computer and use it in GitHub Desktop.
Save marcguilera/c5828ba2f58a11f1f61a8e3bceaabc80 to your computer and use it in GitHub Desktop.
flutter lib
// The store is a flutter store
class SignInControllerImpl extends SignInControllerBase {
IStore<AppState, AppActions> store;
SignInControllerImpl(IInjector injector):
store = singletonNotNull(injector, IStore),
super(injector);
@override
Observable<String> get email => store.onLocalChange((state) => state.auth.signIn.email);
@override
Observable<String> get password => store.onLocalChange((state) => state.auth.signIn.password);
@override
Observable<bool> get isAuthenticated => store.onLocalChange((state) => state.auth.user != null);
@override
Observable<bool> get isEmailError => store.onLocalChange((state) => state.auth.signIn.isEmailError);
@override
Observable<bool> get isPasswordError => store.onLocalChange((state) => state.auth.signIn.isPasswordError);
@override
Observable<bool> get isBusy => store.onLocalChange((state) => state.auth.signIn.isBusy);
@override
void handleOnSubmit() {
store.actions.signIn(new SignInActionPayload(currentEmail, currentPassword));
}
}
Core: Code that can be usd both in the fronend and in the backend.
Shared: Misc classes.
DI: Dependency injection utilities.
Validation: Classes to validate emails, phones, etc.
Frontend: Code that will be rendered by Flutter or Dart.
Core: Code that is independent of Flutter or Dart.
Shared: Misc classes and controller / view interfaces.
Redux: Utilities for the Redux pattern.
Auth: Authentication utilities.
Mobile
Shared: Misc classes.
Auth: Authentication controllers.
// The injector is what gets passed around. Only in the setup are "new" allowed. Flutter doesn't allow for reflection so we must specify
// how to create the objects.
class InjectorSetupImpl extends InjectorSetupBaseImpl {
@override
IInjector createProductionInjector() {
return _createCommon()
// Mobile auth
..register(IFirebaseAuth, (i) => new FirebaseAuthImpl())
..register(IFacebookAuth, (i) => new FacebookAuthImpl())
..register(IGoogleAuth, (i) => new GoogleAuthImpl())
..register(ICredentialsFactory, (i) => new CredentialsFactoryImpl(i))
..register(IAuthenticator, (i) => new AuthenticatorImpl(i))
;
}
@override
IInjector createDevelopmentInjector() {
return _createCommon()
// Mobile auth
..register(ICredentialsFactory, (i) => new MockCredentialsFactoryImpl())
..register(IAuthenticator, (i) => new MockAuthenticatorImpl())
;
}
InjectorImpl _createCommon() {
// TODO add to shared the venue controller & view
return new InjectorImpl()
..register(Client, (i) => new Client())
// Auth controllers
..register(ISignInController, (i) => new SignInControllerImpl(i))
// Redux
..register(IStore, (i) => new BehaviorStoreImpl((state, action) => state,
initialState: new AppState(),
middleware: [ new LoggingMiddleware.printer() ]
))
// Venues
..register(GoogleApiKeyGetter, (i) => () => 'hello')
..register(IGooglePlaces, (i) => new GooglePlacesImpl(i))
..register(IVenueService, (i) => new GoogleVenueServiceImpl(i))
;
}
}
/// Represents a view controller AKA presenter AKA business logic component.
///
/// Rules:
/// 1) All inputs will instances of a [Subject].
/// 2) All the outputs will be instances of a [Observable].
/// 3) Do not pass instances of a [IView]. Interactions should be done through Streams!
/// 4) No flutter / web code is allowed in the BLoC (abstract).
///
/// If you follow these 3 rules build the BLoC as you want.
abstract class IController {
/// Only non-sink function of a controller. Closes all its sinks.
void closeSinks(); // Note: Sink = Subject in RxJs
}
/// Represents a view that will be rendered in the user's screen.
///
/// Rules:
/// 1) The view is the only place where there should be Flutter / Web logic.
/// 2) No business logic in the [IView], that belongs to the [IController]
/// 3) Each view complex enough must have its [IController].
abstract class IView {
}
// This is the entry point
void main() {
final setup = new InjectorSetupImpl();
final injector = setup.createInjector(Environment.development);
final app = new App(injector);
print('Running app');
runApp(app);
}
// Actual flutter view implementation
abstract class AuthFormBase <TController extends IAuthController> extends ViewBase <TController> {
AuthFormBase(IInjector injector) : super.fromInjector(injector);
@override
Widget build(BuildContext context) {
return new Column(
mainAxisAlignment: MainAxisAlignment.center,
children: <Widget>[
buildForm(context),
new Padding(
padding: const EdgeInsets.only(top: 12.0),
child: buildSubmit(context),
)
],
);
}
@protected
Widget buildForm(BuildContext context) {
return new Form (
child: buildFormColumn(context)
);
}
@protected buildFormColumn(BuildContext context);
@protected
Widget buildEmail(BuildContext context) {
return new StreamBuilder<String>(
initialData: '',
stream: controller.email,
builder: (context, snapshot) {
return new TextField(
onChanged: controller.onEmail.add,
controller: new TextEditingController(text: snapshot.data ?? ''),
decoration: const InputDecoration(
hintText: 'Email',
prefixIcon: const Padding(
padding: const EdgeInsets.only(right: 10.0),
child: const Icon(Icons.email),
)
),
);
},
);
}
@protected
Widget buildPassword(BuildContext context) {
return new StreamBuilder<String>(
initialData: '',
stream: controller.password,
builder: (context, snapshot) {
return new TextField(
onChanged: controller.onPassword.add,
controller: new TextEditingController(text: snapshot.data ?? ''),
decoration: const InputDecoration(
hintText: 'Password',
prefixIcon: const Padding(
padding: const EdgeInsets.only(right: 10.0),
child: const Icon(Icons.vpn_key),
)
),
obscureText: true
);
},
);
}
Widget buildBusy(BuildContext context) {
return new StreamBuilder(
stream: controller.isBusy,
builder: (context, snapshot) {
return snapshot.data ? new CircularProgressIndicator() : new Container();
}
);
}
@protected
Widget buildSubmit(BuildContext context) {
return new RaisedButton (
onPressed: onSubmit,
child: buildSubmitText(context),
);
}
@protected
void onSubmit() {
controller.onSubmit.add(null);
}
@protected
Widget buildSubmitText(BuildContext context);
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment