arch 0.2.0+1 arch: ^0.2.0+1 copied to clipboard
MVVM architecture components for Flutter. Helps developers build loosely-coupled applications.
import 'package:arch/arch.dart';
import 'package:flutter/material.dart';
void main() => runApp(MyApp(AppInitializer()));
class AppInitializer implements Initializer {
@override
void registerTypes(ServiceRegistry registry) {
registry
..registerLazySingleton<Logger>(() => DebugLogger())
..registerView(
view: () => HomeView(),
viewModel: () => HomeViewModel(registry.resolve<NavigationService>()),
viewName: 'HomeView',
)
..registerView(
view: () => SecondView(),
viewModel: () => SecondViewModel(
registry.resolve<NavigationService>(), // NavigationService and DialogService are automatically registered
registry.resolve<DialogService>(),
registry.resolve<Logger>(),
),
// This will default to the view's type name `SecondView`.
)
..registerDialog(
dialog: () => MyCustomDialog(),
dialogModel: () => MyCustomDialogModel(registry.resolve<DialogService>()),
// This will default to the dialog's type name `MyCustomDialog`.
);
}
}
/// Log provider.
abstract class Logger {
/// Logs a message
void log(String message);
}
/// Debug log provider.
class DebugLogger implements Logger {
@override
void log(String message) => print(message);
}
class ParameterNames {
static const messageFromHome = 'messageFromHome';
static const messageFromSecond = 'messageFromSecond';
static const messageForDialog = 'messageForDialog';
static const messageFromDialog = 'messageFromDialog';
}
class MyApp extends Application {
MyApp(Initializer initializer) : super(initializer);
@override
void onInitialize() {
print('Initialized');
}
@override
Widget build(BuildContext context) {
return MaterialApp(
title: 'Arch Demo',
navigatorKey: NavigationService.navigatorKey, // IMPORTANT
theme: ThemeData(primarySwatch: Colors.blue),
home: HomeView(),
);
}
}
class HomeView extends StatefulWidget {
HomeView() : super(key: Key('HomeView'));
@override
_HomeViewState createState() => _HomeViewState();
}
class _HomeViewState extends ViewStateBase<HomeView, HomeViewModel> {
@override
Widget buildView(BuildContext context) {
return Scaffold(
appBar: AppBar(title: Text(viewModel.title)),
body: Center(
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: <Widget>[
Text('You have pushed the button this many times: ${viewModel.counter}'),
RaisedButton(
child: Text('Go to Second View'),
onPressed: viewModel.navigate,
)
],
),
),
floatingActionButton: FloatingActionButton(
onPressed: () => viewModel.counter++,
tooltip: 'Increment',
child: Icon(Icons.add),
),
);
}
}
class HomeViewModel extends ViewModelBase {
HomeViewModel(NavigationService navigationService) : super(navigationService: navigationService);
final title = 'Home View';
int _counter = 0;
int get counter => _counter;
set counter(int value) {
if (_counter != value) {
_counter = value;
notifyListeners('counter');
}
}
Future<void> navigate() async {
final result = await navigationService.push('SecondView', {ParameterNames.messageFromHome: 'Hello'});
if (result?.containsKey(ParameterNames.messageFromSecond) == true) {
print(result[ParameterNames.messageFromSecond]);
}
}
}
class SecondView extends ViewBase<SecondViewModel> {
SecondView() : super(key: Key('SecondView'));
@override
Widget buildView(BuildContext context, SecondViewModel viewModel) {
return Scaffold(
appBar: AppBar(title: Text(viewModel.title)),
body: Center(
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: <Widget>[
RaisedButton(
child: Text('Go Back'),
onPressed: viewModel.goBack,
),
RaisedButton(
child: Text('Show Alert Dialog'),
onPressed: viewModel.showAlertDialog,
),
RaisedButton(
child: Text('Show Custom Dialog'),
onPressed: viewModel.showCustomDialog,
),
],
),
), //
);
}
}
class SecondViewModel extends ViewModelBase {
SecondViewModel(NavigationService navigationService, DialogService dialogService, this._logger)
: super(navigationService: navigationService, dialogService: dialogService);
final Logger _logger;
final title = 'Second View';
@override
void init([Map<String, Object> parameters]) {
if (parameters?.containsKey(ParameterNames.messageFromHome) == true) {
_logger.log(parameters[ParameterNames.messageFromHome]);
}
}
void goBack() => navigationService.pop({ParameterNames.messageFromSecond: 'Hi!'});
void showAlertDialog() => dialogService.alert('This is an alert dialog');
Future<void> showCustomDialog() async {
final result =
await dialogService.showCustomDialog('MyCustomDialog', {ParameterNames.messageForDialog: 'Edit this'});
if (result?.containsKey(ParameterNames.messageFromDialog) == true) {
print('Edited value: ${result[ParameterNames.messageFromDialog]}');
}
}
}
class MyCustomDialog extends DialogBase<MyCustomDialogModel> {
final _controller = TextEditingController();
@override
void didInitDialogModel(MyCustomDialogModel dialogModel) {
_controller.text = dialogModel.originalInput;
}
@override
Widget buildDialog(BuildContext context, MyCustomDialogModel dialogModel) {
return AlertDialog(
title: Text('Enter an input'),
content: Column(
mainAxisSize: MainAxisSize.min,
children: <Widget>[
Text('This is an alert dialog that takes an input.'),
SizedBox(height: 8),
TextField(controller: _controller)
],
),
actions: <Widget>[
FlatButton(
child: Text('Cancel'),
onPressed: () => dialogModel.pop(),
),
FlatButton(
child: Text('Ok'),
onPressed: () => dialogModel.pop({ParameterNames.messageFromDialog: _controller.text}),
),
],
);
}
}
class MyCustomDialogModel extends DialogModelBase {
MyCustomDialogModel(DialogService dialogService) : super(dialogService);
String originalInput;
@override
void init([Map<String, Object> parameters]) {
if (parameters?.containsKey(ParameterNames.messageForDialog) == true) {
originalInput = parameters[ParameterNames.messageForDialog];
}
}
}