diff --git a/.vscode/launch.json b/.vscode/launch.json new file mode 100644 index 0000000..3287bb6 --- /dev/null +++ b/.vscode/launch.json @@ -0,0 +1,13 @@ +{ + // Use IntelliSense to learn about possible attributes. + // Hover to view descriptions of existing attributes. + // For more information, visit: https://go.microsoft.com/fwlink/?linkid=830387 + "version": "0.2.0", + "configurations": [ + { + "name": "Flutter", + "request": "launch", + "type": "dart" + } + ] +} \ No newline at end of file diff --git a/assets/images/icon_large.png b/assets/images/icon_large.png new file mode 100644 index 0000000..eabb25a Binary files /dev/null and b/assets/images/icon_large.png differ diff --git a/assets/images/logo.png b/assets/images/logo.png new file mode 100644 index 0000000..eec3735 Binary files /dev/null and b/assets/images/logo.png differ diff --git a/assets/images/splash.png b/assets/images/splash.png new file mode 100644 index 0000000..e7f4164 Binary files /dev/null and b/assets/images/splash.png differ diff --git a/assets/lang/en.json b/assets/lang/en.json new file mode 100644 index 0000000..af564aa --- /dev/null +++ b/assets/lang/en.json @@ -0,0 +1,4 @@ +{ + "main.emptyList.title": "List is empty", + "main.emptyList.firstPaymentNotAdded": "Need add first payment" +} \ No newline at end of file diff --git a/assets/lang/ru.json b/assets/lang/ru.json new file mode 100644 index 0000000..8a0e3a6 --- /dev/null +++ b/assets/lang/ru.json @@ -0,0 +1,21 @@ +{ + "main.emptyList.title": "Список пуст", + "main.emptyList.firstPaymentNotAdded": "Необходимо добавить первый платеж", + "edit.period": "Периодичность платежей", + "edit.year": "Ежегодный", + "edit.quarter": "Ежеквартальный", + "edit.month": "Ежемесячный", + "edit.week": "Еженедельный", + "edit.periodCount": "Всего периодов", + "edit.perPeriodSum": "Сумма платежа за период", + "edit.firstPayDate": "Дата первого платежа", + "edit.weekendWork": "Учитывать выходные дни недели", + "edit.saturdayWork": "Суббота рабочий день", + "edit.name": "Наименование кредита", + "edit.nameBank": "Наименование банка", + "edit.descriptionLabel": "Дополнительная информация", + "edit.calendar": "Календарь", + "edit.calendarDesc": "Календарь платежей", + "edit.description": "Описание", + "edit.descriptionSubtitle": "Дополнительная информация" +} \ No newline at end of file diff --git a/lib/app_routes.dart b/lib/app_routes.dart deleted file mode 100644 index a1a8ef7..0000000 --- a/lib/app_routes.dart +++ /dev/null @@ -1,7 +0,0 @@ -class AppRoutes { - static const home = "/"; - static const addGame = "/addGame"; - static const history = "/history"; - static const money = "/money"; - static const profile = "/profile"; -} diff --git a/lib/core/base/base_model.dart b/lib/core/base/base_model.dart new file mode 100644 index 0000000..505e851 --- /dev/null +++ b/lib/core/base/base_model.dart @@ -0,0 +1,5 @@ +import 'package:equatable/equatable.dart'; + +abstract class BaseModel implements Equatable { + Map toMap(); +} \ No newline at end of file diff --git a/lib/core/base/base_service.dart b/lib/core/base/base_service.dart new file mode 100644 index 0000000..03b422c --- /dev/null +++ b/lib/core/base/base_service.dart @@ -0,0 +1,13 @@ +import 'package:logger/logger.dart'; + +import '../logger.dart'; + +class BaseService { + Logger log; + + BaseService({String title}) { + this.log = getLogger( + title ?? this.runtimeType.toString(), + ); + } +} \ No newline at end of file diff --git a/lib/core/base/base_view_model.dart b/lib/core/base/base_view_model.dart new file mode 100644 index 0000000..630c082 --- /dev/null +++ b/lib/core/base/base_view_model.dart @@ -0,0 +1,49 @@ +import 'package:flutter/foundation.dart'; +import 'package:logger/logger.dart'; +import '../logger.dart'; + +class BaseViewModel extends ChangeNotifier { + String _title; + bool _busy; + Logger log; + bool _isDisposed = false; + + BaseViewModel({ + bool busy = false, + String title, + }) : _busy = busy, + _title = title { + log = getLogger(title ?? this.runtimeType.toString()); + } + + bool get busy => this._busy; + bool get isDisposed => this._isDisposed; + String get title => _title ?? this.runtimeType.toString(); + + set busy(bool busy) { + log.i( + 'busy: ' + '$title is entering ' + '${busy ? 'busy' : 'free'} state', + ); + this._busy = busy; + notifyListeners(); + } + + @override + void notifyListeners() { + if (!isDisposed) { + super.notifyListeners(); + } else { + log.w('notifyListeners: Notify listeners called after ' + '${title ?? this.runtimeType.toString()} has been disposed'); + } + } + + @override + void dispose() { + log.i('dispose'); + _isDisposed = true; + super.dispose(); + } +} \ No newline at end of file diff --git a/lib/core/locator.dart b/lib/core/locator.dart new file mode 100644 index 0000000..fba2128 --- /dev/null +++ b/lib/core/locator.dart @@ -0,0 +1,19 @@ +import '../core/services/ApiService.dart'; + +import '../core/logger.dart'; +import '../core/services/navigator_service.dart'; +import 'package:get_it/get_it.dart'; +import 'package:logger/logger.dart'; + +GetIt locator = GetIt.instance; + +class LocatorInjector { + static Logger _log = getLogger('LocatorInjector'); + + static Future setupLocator() async { + _log.d('Initializing Navigator Service'); + locator.registerLazySingleton(() => NavigatorService()); + _log.d('Initializing Api Service'); + locator.registerLazySingleton(() => ApiService()); + } +} \ No newline at end of file diff --git a/lib/core/logger.dart b/lib/core/logger.dart new file mode 100644 index 0000000..620f8fc --- /dev/null +++ b/lib/core/logger.dart @@ -0,0 +1,43 @@ +import 'dart:developer' as prefix0; +import 'package:logger/logger.dart'; + +class SimpleLogPrinter extends LogPrinter { + static int counter = 0; + final String className; + + SimpleLogPrinter(this.className); + + @override + List log(LogEvent event) { + prefix0.log( + event.message, + time: DateTime.now(), + level: () { + switch (event.level) { + case Level.verbose: + return 0; + case Level.debug: + return 500; + case Level.info: + return 0; + case Level.warning: + return 1500; + case Level.error: + return 2000; + case Level.wtf: + return 2000; + default: + return 2000; + } + }(), + name: className, + error: event.error, + sequenceNumber: counter += 1, + ); + return []; + } +} + +Logger getLogger(String className) { + return Logger(printer: SimpleLogPrinter(className)); +} \ No newline at end of file diff --git a/lib/core/models/user.dart b/lib/core/models/user.dart new file mode 100644 index 0000000..b632465 --- /dev/null +++ b/lib/core/models/user.dart @@ -0,0 +1,23 @@ +class User { + final String id; + final String fullName; + final String email; + final String userRole; + + User({this.id, this.fullName, this.email, this.userRole}); + + User.fromData(Map data) + : id = data['id'], + fullName = data['fullName'], + email = data['email'], + userRole = data['userRole']; + + Map toJson() { + return { + 'id': id, + 'fullName': fullName, + 'email': email, + 'userRole': userRole, + }; + } +} diff --git a/lib/core/providers.dart b/lib/core/providers.dart new file mode 100644 index 0000000..92bf4ad --- /dev/null +++ b/lib/core/providers.dart @@ -0,0 +1,30 @@ +import 'package:provider/single_child_widget.dart'; + +import '../core/locator.dart'; +import '../core/services/navigator_service.dart'; +import 'package:provider/provider.dart'; + +import 'services/ApiService.dart'; +import 'services/authentication_service.dart'; + +class ProviderInjector { + static List providers = [ + ..._independentServices, + ..._dependentServices, + ..._consumableServices, + ]; + + static List _independentServices = [ + Provider.value(value: locator()), + Provider.value(value: locator()), + ]; + + static List _dependentServices = [ + ProxyProvider( + update: (context, api, authenticationService) => + AuthenticationService(api: api), + ) + ]; + + static List _consumableServices = []; +} \ No newline at end of file diff --git a/lib/core/route_names.dart b/lib/core/route_names.dart new file mode 100644 index 0000000..cd86836 --- /dev/null +++ b/lib/core/route_names.dart @@ -0,0 +1,3 @@ +const String LoginViewRoute = "LoginView"; +const String HomeViewRoute = "HomeView"; +// Generate the views here diff --git a/lib/core/router.dart b/lib/core/router.dart new file mode 100644 index 0000000..e6ee8c7 --- /dev/null +++ b/lib/core/router.dart @@ -0,0 +1,61 @@ +import './route_names.dart'; +import 'package:aman_kassa_flutter/views/home/home_view.dart'; +import 'package:aman_kassa_flutter/views/login/login_view.dart'; +import 'package:flutter/material.dart'; + +Route generateRoute(RouteSettings settings) { + switch (settings.name) { + case LoginViewRoute: + return _getPageRoute( + routeName: settings.name, + viewToShow: LoginView(), + ); + case HomeViewRoute: + return _getPageRoute( + routeName: settings.name, + viewToShow: HomeView(), + ); + // case AddAndEditViewRoute: + // var documentToEdit = settings.arguments as Document; + // return SlideRightRoute(widget:AddAndEditView( + // edittingDocument: documentToEdit, + // )); + default: + return MaterialPageRoute( + builder: (_) => Scaffold( + body: Center( + child: Text('No route defined for ${settings.name}')), + )); + } +} + +PageRoute _getPageRoute({String routeName, Widget viewToShow}) { + return MaterialPageRoute( + settings: RouteSettings( + name: routeName, + ), + builder: (_) => viewToShow); +} + +class SlideRightRoute extends PageRouteBuilder { + final Widget widget; + SlideRightRoute({this.widget}) + : super( + pageBuilder: (BuildContext context, Animation animation, + Animation secondaryAnimation) { + return widget; + }, + transitionsBuilder: (BuildContext context, + Animation animation, + Animation secondaryAnimation, + Widget child) { + return new SlideTransition( + position: new Tween( + begin: const Offset(1.0, 0.0), + end: Offset.zero, + ).animate(animation), + child: child, + ); + }, + ); +} diff --git a/lib/core/services/ApiService.dart b/lib/core/services/ApiService.dart new file mode 100644 index 0000000..43b5e32 --- /dev/null +++ b/lib/core/services/ApiService.dart @@ -0,0 +1,37 @@ +import 'dart:convert'; + +import 'package:aman_kassa_flutter/core/base/base_service.dart'; +import 'package:aman_kassa_flutter/core/models/user.dart'; +import 'package:http/http.dart' as http; + +/// The service responsible for networking requests +class ApiService extends BaseService { + static const endpoint = 'https://jsonplaceholder.typicode.com'; + + var client = new http.Client(); + + Future getUserProfile(int userId) async { + // Get user profile for id + var response = await client.get('$endpoint/users/$userId'); + + // Convert and return + return User.fromData(json.decode(response.body)); + } + + // Future> getPostsForUser(int userId) async { + // var posts = List(); + // // Get user posts for id + // var response = await client.get('$endpoint/posts?userId=$userId'); + + // // parse into List + // var parsed = json.decode(response.body) as List; + + // // loop and convert each item to Post + // for (var post in parsed) { + // posts.add(Post.fromJson(post)); + // } + + // return posts; + // } + +} \ No newline at end of file diff --git a/lib/core/services/authentication_service.dart b/lib/core/services/authentication_service.dart new file mode 100644 index 0000000..2e2063f --- /dev/null +++ b/lib/core/services/authentication_service.dart @@ -0,0 +1,35 @@ + +import 'package:aman_kassa_flutter/core/base/base_service.dart'; +import 'package:aman_kassa_flutter/core/models/user.dart'; +import 'package:flutter/foundation.dart'; + +import 'ApiService.dart'; + +class AuthenticationService extends BaseService { + + final ApiService _api; + + AuthenticationService({ApiService api}) : _api = api; + + User _currentUser; + User get currentUser => _currentUser; + + Future loginWithEmail({ + @required String email, + @required String password, + }) async { + try { + User result = await _api.getUserProfile(123); + _currentUser = result; + return result != null; + } catch (e) { + return e.message; + } + } + + // Future isUserLoggedIn() async { + // var user = await _firebaseAuth.currentUser(); + // await _populateCurrentUser(user); + // return user != null; + // } +} diff --git a/lib/core/services/navigator_service.dart b/lib/core/services/navigator_service.dart new file mode 100644 index 0000000..7e94494 --- /dev/null +++ b/lib/core/services/navigator_service.dart @@ -0,0 +1,35 @@ +import '../../core/base/base_service.dart'; +import 'package:flutter/material.dart'; + +class NavigatorService extends BaseService { + final GlobalKey navigatorKey = GlobalKey(); + + Future navigateToPage(MaterialPageRoute pageRoute) async { + log.i('navigateToPage: pageRoute: ${pageRoute.settings.name}'); + if (navigatorKey.currentState == null) { + log.e('navigateToPage: Navigator State is null'); + return null; + } + return navigatorKey.currentState.push(pageRoute); + } + + Future navigateToPageWithReplacement( + MaterialPageRoute pageRoute) async { + log.i('navigateToPageWithReplacement: ' + 'pageRoute: ${pageRoute.settings.name}'); + if (navigatorKey.currentState == null) { + log.e('navigateToPageWithReplacement: Navigator State is null'); + return null; + } + return navigatorKey.currentState.pushReplacement(pageRoute); + } + + void pop([T result]) { + log.i('goBack:'); + if (navigatorKey.currentState == null) { + log.e('goBack: Navigator State is null'); + return; + } + navigatorKey.currentState.pop(result); + } +} \ No newline at end of file diff --git a/lib/features/home/avatars_stack.dart b/lib/features/home/avatars_stack.dart deleted file mode 100644 index b977875..0000000 --- a/lib/features/home/avatars_stack.dart +++ /dev/null @@ -1,31 +0,0 @@ -import 'package:flutter/material.dart'; - -class AvatarsStack extends StatelessWidget { - final List names; - - AvatarsStack(this.names); - - @override - Widget build(BuildContext context) { - final avatars = List(); - final max = names.length - 1 > 8 ? 8 : names.length - 1; - for (num i = max; i >= 0; i--) { - avatars.add(Positioned( - right: (5.0 + (25 - i) * i), - child: CircleAvatar( - backgroundColor: (i % 2 == 0 ? Colors.yellow : Colors.grey), - radius: 20, - child: Text(names[i].substring(0, 1), - style: TextStyle(fontSize: 30)), - ), - )); - } - - return Container( - height: 40, - child: Stack( - children: avatars, - ), - ); - } -} diff --git a/lib/features/home/game_list_item.dart b/lib/features/home/game_list_item.dart deleted file mode 100644 index 36f18be..0000000 --- a/lib/features/home/game_list_item.dart +++ /dev/null @@ -1,72 +0,0 @@ -import 'package:flutter/material.dart'; -import 'package:aman_kassa_flutter/features/home/avatars_stack.dart'; -import 'package:aman_kassa_flutter/model/Game.dart'; - -class GameListItem extends StatelessWidget { - final Game game; - - const GameListItem(this.game, {Key key}) : super(key: key); - - @override - Widget build(BuildContext context) { - return Padding( - padding: EdgeInsets.all(5.0), - child: Center( - child: Card( - child: IntrinsicHeight( - child: Row( - mainAxisSize: MainAxisSize.max, - children: [ - Container( - padding: EdgeInsets.all(10.0), - decoration: BoxDecoration( - color: Colors.grey.shade900, - borderRadius: BorderRadius.horizontal( - left: Radius.circular(4.0), - right: Radius.elliptical(15.0, 25.0))), - child: Column( - mainAxisSize: MainAxisSize.max, - children: [ - Padding( - padding: const EdgeInsets.only(bottom: 8.0), - child: Text( - 'DateFormat', - style: TextStyle(color: Colors.white, fontSize: 20), - ), - ), - Text( - 'DateToday', - style: TextStyle(color: Colors.white), - ), - ], - ), - ), - Padding( - padding: const EdgeInsets.all(8.0), - child: Column( - mainAxisSize: MainAxisSize.max, - mainAxisAlignment: MainAxisAlignment.spaceAround, - children: [ - Text( - '${game.where}', - style: TextStyle(fontSize: 20), - ), - Row( - children: [ - Text('aaa') - ], - ), - ], - ), - ), - Expanded( - child: AvatarsStack( - game.players.map((player) => player.name).toList())), - ], - ), - ), - ), - ), - ); - } -} diff --git a/lib/features/home/home_page.dart b/lib/features/home/home_page.dart deleted file mode 100644 index 685afde..0000000 --- a/lib/features/home/home_page.dart +++ /dev/null @@ -1,33 +0,0 @@ -import 'package:flutter/material.dart'; -import 'package:aman_kassa_flutter/features/home/game_list_item.dart'; -import 'package:aman_kassa_flutter/features/menu/main_menu.dart'; -//import 'package:aman_kassa_flutter/testdata/test_data.dart'; - -class HomePage extends StatefulWidget { - HomePage({Key key}) : super(key: key); - - @override - _HomePageState createState() => _HomePageState(); -} - -class _HomePageState extends State { - List _games; - - _HomePageState() { - //var games = [TestData.getRandomGames(5); - // games.sort((a, b) => a.date.compareTo(b.date)); - // _games = games.map((game) => GameListItem(game)) - // .toList()]; - _games=[]; - } - - Widget _getBody() => ListView.builder( - itemBuilder: (BuildContext context, int index) => _games[index], - itemCount: _games.length, - ); - - @override - Widget build(BuildContext context) { - return MainMenu(_getBody()); - } -} diff --git a/lib/features/menu/bottom_nav_bar.dart b/lib/features/menu/bottom_nav_bar.dart deleted file mode 100644 index 84463a9..0000000 --- a/lib/features/menu/bottom_nav_bar.dart +++ /dev/null @@ -1,84 +0,0 @@ - -import 'package:flutter/material.dart'; -import 'package:flutter_redux/flutter_redux.dart'; -import 'package:redux/redux.dart'; -import 'package:aman_kassa_flutter/app_routes.dart'; -import 'package:aman_kassa_flutter/redux/actions.dart'; -import 'package:aman_kassa_flutter/redux/app_state.dart'; -import 'package:aman_kassa_flutter/redux/selectors.dart'; - -class BottomNavBar extends StatelessWidget { - Widget _addPadding(Widget child) => Padding( - padding: const EdgeInsets.symmetric(horizontal: 8), - child: child, - ); - - Widget _getMenuItem(BuildContext context, - {Icon icon, String routeName, @required _ViewModel vm}) { - if (!vm.route.contains(routeName)) - return _addPadding( - IconButton(icon: icon, onPressed: () => vm.navigate(routeName))); - else - return _addPadding(IconButton( - icon: icon, - onPressed: () => vm.navigate(routeName), - color: Theme.of(context).accentColor.withOpacity(0.7))); - } - - @override - Widget build(BuildContext context) { - return BottomAppBar( - notchMargin: 8, - color: Theme.of(context).primaryColor, - shape: CircularNotchedRectangle(), - child: StoreConnector( - distinct: true, - converter: (store) => _ViewModel.fromStore(store), - builder: (context, vm) => Row( - mainAxisAlignment: MainAxisAlignment.spaceAround, - children: [ - _getMenuItem(context, - icon: Icon(Icons.home), routeName: AppRoutes.home, vm: vm), - _getMenuItem(context, - icon: Icon(Icons.history), - routeName: AppRoutes.history, - vm: vm), - SizedBox(width: 16), - _getMenuItem(context, - icon: Icon(Icons.monetization_on), - routeName: AppRoutes.money, - vm: vm), - _getMenuItem(context, - icon: Icon(Icons.supervised_user_circle), - routeName: AppRoutes.profile, - vm: vm), - ], - ), - ), - ); - } -} - -class _ViewModel { - final List route; - final Function(String) navigate; - - _ViewModel({@required this.route, @required this.navigate}); - - static _ViewModel fromStore(Store store) { - return _ViewModel( - route: currentRoute(store.state), - navigate: (routeName) => - store.dispatch(NavigateReplaceAction(routeName))); - } - - @override - bool operator ==(Object other) => - identical(this, other) || - other is _ViewModel && - runtimeType == other.runtimeType && - route == other.route; - - @override - int get hashCode => route.hashCode; -} diff --git a/lib/features/menu/main_menu.dart b/lib/features/menu/main_menu.dart deleted file mode 100644 index 14922a3..0000000 --- a/lib/features/menu/main_menu.dart +++ /dev/null @@ -1,31 +0,0 @@ -import 'package:flutter/material.dart'; -import 'package:flutter_redux/flutter_redux.dart'; -import 'package:aman_kassa_flutter/app_routes.dart'; -import 'package:aman_kassa_flutter/features/menu/bottom_nav_bar.dart'; -import 'package:aman_kassa_flutter/redux/actions.dart'; -import 'package:aman_kassa_flutter/redux/app_state.dart'; - -class MainMenu extends StatelessWidget { - final Widget body; - - MainMenu(this.body); - - Widget _getInfoBarWorkaround() => - PreferredSize(child: Container(), preferredSize: Size(0.0, 0.0)); - - @override - Widget build(BuildContext context) { - return Scaffold( - appBar: _getInfoBarWorkaround(), - floatingActionButtonLocation: FloatingActionButtonLocation.centerDocked, - bottomNavigationBar: BottomNavBar(), - floatingActionButton: FloatingActionButton( - onPressed: () => StoreProvider.of(context) - .dispatch(NavigatePushAction(AppRoutes.addGame)), - tooltip: 'Add new game', - child: Icon(Icons.add), - ), - body: body, - ); - } -} diff --git a/lib/features/newgame/new_game.dart b/lib/features/newgame/new_game.dart deleted file mode 100644 index 2eba8be..0000000 --- a/lib/features/newgame/new_game.dart +++ /dev/null @@ -1,34 +0,0 @@ -import 'package:flutter/material.dart'; - -class NewGame extends StatelessWidget { - Widget _getBody(BuildContext context) => Center( - child: Center( - child: Column( - mainAxisSize: MainAxisSize.min, - children: [ - Padding( - padding: const EdgeInsets.all(8.0), - child: Text('New Game', style: TextStyle(fontSize: 20, color: Colors.white),), - ), - RaisedButton( - child: Text('Show Alert'), - onPressed: () { - showDialog( - context: context, - builder: (ctx) => AlertDialog( - shape: RoundedRectangleBorder(borderRadius: BorderRadius.all(Radius.circular(20.0))), - title: Text('Alert Title'), - content: Text('Content of alert.'))); - }) - ], - ), - )); - - @override - Widget build(BuildContext context) { - return Scaffold( - appBar: AppBar(title: Text('Add Game')), - body: _getBody(context), - ); - } -} diff --git a/lib/features/stub_screen.dart b/lib/features/stub_screen.dart deleted file mode 100644 index 907f877..0000000 --- a/lib/features/stub_screen.dart +++ /dev/null @@ -1,16 +0,0 @@ - -import 'package:flutter/material.dart'; -import 'package:aman_kassa_flutter/features/menu/main_menu.dart'; - -class StubScreen extends StatelessWidget { - - Widget _getBody() => Center( - child: Text('Stub Screen'), - ); - - @override - Widget build(BuildContext context) { - return MainMenu(_getBody()); - } - -} diff --git a/lib/main.dart b/lib/main.dart index 6c00a6c..881541a 100644 --- a/lib/main.dart +++ b/lib/main.dart @@ -1,98 +1,28 @@ +import 'package:aman_kassa_flutter/views/login/login_view.dart'; + +import 'core/locator.dart'; +import 'core/providers.dart'; +import 'core/router.dart'; +import 'core/services/navigator_service.dart'; import 'package:flutter/material.dart'; -import 'package:flutter_redux/flutter_redux.dart'; -import 'package:redux/redux.dart'; -import 'package:aman_kassa_flutter/app_routes.dart'; -import 'package:aman_kassa_flutter/features/home/home_page.dart'; -import 'package:aman_kassa_flutter/features/newgame/new_game.dart'; -import 'package:aman_kassa_flutter/features/stub_screen.dart'; -import 'package:aman_kassa_flutter/redux/app_state.dart'; -import 'package:aman_kassa_flutter/redux/navigation_middleware.dart'; -import 'package:aman_kassa_flutter/redux/reducers/app_reducer.dart'; -import 'package:aman_kassa_flutter/route_aware_widget.dart'; +import 'package:provider/provider.dart'; +import 'views/home/home_view.dart'; -void main() => runApp(MyApp()); - -final GlobalKey navigatorKey = new GlobalKey(); - -class MyApp extends StatelessWidget { - final store = Store(appReducer, - initialState: AppState.loading(), - middleware: createNavigationMiddleware()); - - final theme = ThemeData( - primaryColor: Colors.grey.shade900, - //primaryColorLight: Colors.grey.shade800, - //primaryColorDark: Colors.black, - //scaffoldBackgroundColor: Colors.grey.shade800, -// textTheme: TextTheme( -// body1: TextStyle(color: Colors.white), -// display1: TextStyle(color: Colors.white), -// title: TextStyle(color: Colors.white), -// ), - //iconTheme: IconThemeData(color: Colors.white), - //accentColor: Colors.yellow[500], - ); - - MaterialPageRoute _getRoute(RouteSettings settings) { - switch (settings.name) { - case AppRoutes.home: - return MainRoute(HomePage(), settings: settings); - case AppRoutes.addGame: - return FabRoute(NewGame(), settings: settings); - default: - return MainRoute(StubScreen(), settings: settings); - } - } +void main() async { + await LocatorInjector.setupLocator(); + runApp(MainApplication()); +} +class MainApplication extends StatelessWidget { @override Widget build(BuildContext context) { - return StoreProvider( - store: store, + return MultiProvider( + providers: ProviderInjector.providers.toList(), child: MaterialApp( - navigatorKey: navigatorKey, - navigatorObservers: [routeObserver], - title: 'AppLocalizations.appTitle', - localizationsDelegates: [ - //AppLocalizationsDelegate(), - ], - theme: theme, - onGenerateRoute: (RouteSettings settings) => _getRoute(settings), + navigatorKey: locator().navigatorKey, + home: HomeView(), + onGenerateRoute: generateRoute, ), ); } -} - -class MainRoute extends MaterialPageRoute { - MainRoute(Widget widget, {RouteSettings settings}) - : super( - builder: (_) => RouteAwareWidget(child: widget), - settings: settings); - - @override - Widget buildTransitions(BuildContext context, Animation animation, - Animation secondaryAnimation, Widget child) { - if (settings.isInitialRoute) return child; - // Fades between routes. (If you don't want any animation, - // just return child.) - return FadeTransition(opacity: animation, child: child); - } -} - -class FabRoute extends MaterialPageRoute { - FabRoute(Widget widget, {RouteSettings settings}) - : super( - builder: (_) => RouteAwareWidget(child: widget), - settings: settings); - - @override - Widget buildTransitions(BuildContext context, Animation animation, - Animation secondaryAnimation, Widget child) { - if (settings.isInitialRoute) return child; - return SlideTransition( - position: new Tween( - begin: const Offset(0.0, 1.0), - end: Offset.zero, - ).animate(animation), - child: child); - } -} +} \ No newline at end of file diff --git a/lib/model/Court.dart b/lib/model/Court.dart deleted file mode 100644 index 9bf9906..0000000 --- a/lib/model/Court.dart +++ /dev/null @@ -1,6 +0,0 @@ -class Court { - final num number; - final bool wasReserved; - - Court({this.number, this.wasReserved}); -} diff --git a/lib/model/Game.dart b/lib/model/Game.dart deleted file mode 100644 index e10f158..0000000 --- a/lib/model/Game.dart +++ /dev/null @@ -1,12 +0,0 @@ -import 'package:meta/meta.dart'; -import 'package:aman_kassa_flutter/model/Court.dart'; -import 'package:aman_kassa_flutter/model/Player.dart'; - -class Game { - final String where; - final DateTime date; - final List courts; - final List players; - - Game({@required this.where, @required this.date, this.players, this.courts}); -} diff --git a/lib/model/Player.dart b/lib/model/Player.dart deleted file mode 100644 index 7ba20fd..0000000 --- a/lib/model/Player.dart +++ /dev/null @@ -1,5 +0,0 @@ -class Player { - final String name; - - Player(this.name); -} diff --git a/lib/redux/actions.dart b/lib/redux/actions.dart deleted file mode 100644 index 18d6ec5..0000000 --- a/lib/redux/actions.dart +++ /dev/null @@ -1,44 +0,0 @@ - - -import 'package:aman_kassa_flutter/model/Game.dart'; - -class AddGameAction { - final Game game; - - AddGameAction(this.game); - - @override - String toString() { - return 'AddGameAction{game: $game}'; - } -} - -class NavigateReplaceAction { - final String routeName; - - NavigateReplaceAction(this.routeName); - - @override - String toString() { - return 'MainMenuNavigateAction{routeName: $routeName}'; - } -} - -class NavigatePushAction { - final String routeName; - - NavigatePushAction(this.routeName); - - @override - String toString() { - return 'NavigatePushAction{routeName: $routeName}'; - } -} - -class NavigatePopAction { - - @override - String toString() { - return 'NavigatePopAction'; - } -} diff --git a/lib/redux/app_state.dart b/lib/redux/app_state.dart deleted file mode 100644 index 00e5a47..0000000 --- a/lib/redux/app_state.dart +++ /dev/null @@ -1,46 +0,0 @@ -import 'package:meta/meta.dart'; -import 'package:aman_kassa_flutter/app_routes.dart'; -import 'package:aman_kassa_flutter/model/Game.dart'; - -@immutable -class AppState { - final bool isLoading; - final List games; - final List route; - - AppState({ - this.isLoading = false, - this.games = const [], - this.route = const [AppRoutes.home], - }); - - factory AppState.loading() => AppState(isLoading: true); - - AppState copyWith({ - bool isLoading, - List games, - }) => - AppState( - isLoading: isLoading ?? this.isLoading, - games: games ?? this.games, - route: route ?? this.route - ); - - @override - int get hashCode => - isLoading.hashCode ^ games.hashCode ^ route.hashCode; - - @override - bool operator ==(Object other) => - identical(this, other) || - other is AppState && - runtimeType == other.runtimeType && - isLoading == other.isLoading && - games == other.games && - route == other.route; - - @override - String toString() { - return 'AppState{isLoading: $isLoading, games: $games, route: $route}'; - } -} diff --git a/lib/redux/navigation_middleware.dart b/lib/redux/navigation_middleware.dart deleted file mode 100644 index 709e880..0000000 --- a/lib/redux/navigation_middleware.dart +++ /dev/null @@ -1,27 +0,0 @@ -import 'package:redux/redux.dart'; -import 'package:aman_kassa_flutter/main.dart'; -import 'package:aman_kassa_flutter/redux/actions.dart'; -import 'package:aman_kassa_flutter/redux/app_state.dart'; - -List> createNavigationMiddleware() { - return [ - TypedMiddleware(_navigateReplace), - TypedMiddleware(_navigate), - ]; -} - -_navigateReplace(Store store, action, NextDispatcher next) { - final routeName = (action as NavigateReplaceAction).routeName; - if (store.state.route.last != routeName) { - navigatorKey.currentState.pushReplacementNamed(routeName); - } - next(action); //This need to be after name checks -} - -_navigate(Store store, action, NextDispatcher next) { - final routeName = (action as NavigatePushAction).routeName; - if (store.state.route.last != routeName) { - navigatorKey.currentState.pushNamed(routeName); - } - next(action); //This need to be after name checks -} diff --git a/lib/redux/reducers/app_reducer.dart b/lib/redux/reducers/app_reducer.dart deleted file mode 100644 index 224f23d..0000000 --- a/lib/redux/reducers/app_reducer.dart +++ /dev/null @@ -1,14 +0,0 @@ - - -import 'package:aman_kassa_flutter/redux/app_state.dart'; -import 'package:aman_kassa_flutter/redux/reducers/games_reducer.dart'; -import 'package:aman_kassa_flutter/redux/reducers/loading_reducer.dart'; -import 'package:aman_kassa_flutter/redux/reducers/navigation_reducer.dart'; - -AppState appReducer(AppState state, action) { - return AppState( - isLoading: loadingReducer(state.isLoading, action), - games: gamesReducer(state.games, action), - route: navigationReducer(state.route, action) - ); -} diff --git a/lib/redux/reducers/games_reducer.dart b/lib/redux/reducers/games_reducer.dart deleted file mode 100644 index 0b38fca..0000000 --- a/lib/redux/reducers/games_reducer.dart +++ /dev/null @@ -1,11 +0,0 @@ -import 'package:redux/redux.dart'; -import 'package:aman_kassa_flutter/model/Game.dart'; -import 'package:aman_kassa_flutter/redux/actions.dart'; - -final gamesReducer = combineReducers>([ - TypedReducer, AddGameAction>(_addGame), -]); - -List _addGame(List games, AddGameAction action) { - return List.from(games)..add(action.game); -} diff --git a/lib/redux/reducers/loading_reducer.dart b/lib/redux/reducers/loading_reducer.dart deleted file mode 100644 index f494b67..0000000 --- a/lib/redux/reducers/loading_reducer.dart +++ /dev/null @@ -1,5 +0,0 @@ -import 'package:redux/redux.dart'; - -final loadingReducer = combineReducers([ -]); - diff --git a/lib/redux/reducers/navigation_reducer.dart b/lib/redux/reducers/navigation_reducer.dart deleted file mode 100644 index 9239234..0000000 --- a/lib/redux/reducers/navigation_reducer.dart +++ /dev/null @@ -1,24 +0,0 @@ -import 'package:redux/redux.dart'; -import 'package:aman_kassa_flutter/redux/actions.dart'; - -final navigationReducer = combineReducers>([ - TypedReducer, NavigateReplaceAction>(_navigateReplace), - TypedReducer, NavigatePushAction>(_navigatePush), - TypedReducer, NavigatePopAction>(_navigatePop), -]); - -List _navigateReplace( - List route, NavigateReplaceAction action) => - [action.routeName]; - -List _navigatePush(List route, NavigatePushAction action) { - var result = List.from(route); - result.add(action.routeName); - return result; -} - -List _navigatePop(List route, NavigatePopAction action) { - var result = List.from(route); - result.removeLast(); - return result; -} diff --git a/lib/redux/selectors.dart b/lib/redux/selectors.dart deleted file mode 100644 index e3761cd..0000000 --- a/lib/redux/selectors.dart +++ /dev/null @@ -1,4 +0,0 @@ - -import './app_state.dart'; - -List currentRoute(AppState state) => state.route; diff --git a/lib/route_aware_widget.dart b/lib/route_aware_widget.dart deleted file mode 100644 index 678feb4..0000000 --- a/lib/route_aware_widget.dart +++ /dev/null @@ -1,46 +0,0 @@ -import 'package:flutter/material.dart'; -import 'package:flutter_redux/flutter_redux.dart'; -import 'package:aman_kassa_flutter/redux/actions.dart'; -import 'package:aman_kassa_flutter/redux/app_state.dart'; - -final RouteObserver routeObserver = RouteObserver(); - -class RouteAwareWidget extends StatefulWidget { - final Widget child; - - RouteAwareWidget({this.child}); - - State createState() => RouteAwareWidgetState(child: child); -} - -class RouteAwareWidgetState extends State with RouteAware { - final Widget child; - - RouteAwareWidgetState({this.child}); - - @override - void didChangeDependencies() { - super.didChangeDependencies(); - routeObserver.subscribe(this, ModalRoute.of(context)); - } - - @override - void dispose() { - routeObserver.unsubscribe(this); - super.dispose(); - } - - @override - void didPush() { - // Route was pushed onto navigator and is now topmost route. - } - - @override - void didPopNext() { - // Covering route was popped off the navigator. - StoreProvider.of(context).dispatch(NavigatePopAction()); - } - - @override - Widget build(BuildContext context) => Container(child: child); -} diff --git a/lib/views/home/home_view.dart b/lib/views/home/home_view.dart new file mode 100644 index 0000000..122697a --- /dev/null +++ b/lib/views/home/home_view.dart @@ -0,0 +1,41 @@ +library home_view; + +import 'package:flutter/material.dart'; +import 'package:stacked/stacked.dart'; +import 'home_view_model.dart'; + +class HomeView extends StatelessWidget { + @override + Widget build(BuildContext context) { + return ViewModelBuilder.reactive( + viewModelBuilder: () => HomeViewModel(), + onModelReady: (viewModel) { + //viewModel.busy = true; + }, + builder: (context, viewModel, child) { + return buildWaitingLogo(context, viewModel, child); + }); + } + + Widget buildWaitingLogo( + BuildContext context, HomeViewModel viewModel, Widget child) => + new Scaffold( + body: Center( + child: Column( + mainAxisSize: MainAxisSize.min, + children: [ + SizedBox( + width: 300, + height: 100, + child: Image.asset('assets/images/icon_large.png'), + ), + CircularProgressIndicator( + strokeWidth: 3, + valueColor: AlwaysStoppedAnimation( + Colors.yellow[300], + ), + ) + ], + ), + )); +} diff --git a/lib/views/home/home_view_model.dart b/lib/views/home/home_view_model.dart new file mode 100644 index 0000000..4a6710d --- /dev/null +++ b/lib/views/home/home_view_model.dart @@ -0,0 +1,25 @@ +import 'package:aman_kassa_flutter/core/base/base_view_model.dart'; +import 'package:aman_kassa_flutter/core/locator.dart'; +import 'package:aman_kassa_flutter/core/route_names.dart'; +import 'package:aman_kassa_flutter/core/services/navigator_service.dart'; +import 'package:aman_kassa_flutter/views/login/login_view.dart'; +import 'package:flutter/material.dart'; + +class HomeViewModel extends BaseViewModel { + final NavigatorService _navigationService = locator(); + int _counter; + + HomeViewModel({int counter = 0}) : this._counter = counter; + + int get counter => this._counter; + set counter(int value) { + this._counter = value; + notifyListeners(); + } + + void increment() => this.counter += 1; + + void goToLogin() { + _navigationService.navigateToPage(MaterialPageRoute(builder: (context) => LoginView())); + } +} \ No newline at end of file diff --git a/lib/views/login/login_view.dart b/lib/views/login/login_view.dart new file mode 100644 index 0000000..6545751 --- /dev/null +++ b/lib/views/login/login_view.dart @@ -0,0 +1,22 @@ +library login_view; + +import 'package:flutter/material.dart'; +import 'package:stacked/stacked.dart'; +import 'login_view_model.dart'; + +class LoginView extends StatelessWidget { + @override + Widget build(BuildContext context) { + //LoginViewModel viewModel = LoginViewModel(); + return ViewModelBuilder.reactive( + viewModelBuilder: () => LoginViewModel(), + onModelReady: (viewModel) { + // Do something once your viewModel is initialized + }, + builder: (context, viewModel, child) { + return Scaffold( + body: Center(child: Text('LoginMobile')), + ); + }); + } +} diff --git a/lib/views/login/login_view_model.dart b/lib/views/login/login_view_model.dart new file mode 100644 index 0000000..087f76d --- /dev/null +++ b/lib/views/login/login_view_model.dart @@ -0,0 +1,7 @@ +import 'package:aman_kassa_flutter/core/base/base_view_model.dart'; + +class LoginViewModel extends BaseViewModel { + LoginViewModel(); + + // Add ViewModel specific code here +} \ No newline at end of file diff --git a/pubspec.lock b/pubspec.lock index 84dfaf5..9caeade 100644 --- a/pubspec.lock +++ b/pubspec.lock @@ -7,42 +7,42 @@ packages: name: archive url: "https://pub.dartlang.org" source: hosted - version: "2.0.11" + version: "2.0.13" args: dependency: transitive description: name: args url: "https://pub.dartlang.org" source: hosted - version: "1.5.2" + version: "1.6.0" async: dependency: transitive description: name: async url: "https://pub.dartlang.org" source: hosted - version: "2.4.0" + version: "2.4.1" boolean_selector: dependency: transitive description: name: boolean_selector url: "https://pub.dartlang.org" source: hosted - version: "1.0.5" + version: "2.0.0" charcode: dependency: transitive description: name: charcode url: "https://pub.dartlang.org" source: hosted - version: "1.1.2" + version: "1.1.3" collection: dependency: transitive description: name: collection url: "https://pub.dartlang.org" source: hosted - version: "1.14.11" + version: "1.14.12" convert: dependency: transitive description: @@ -56,7 +56,7 @@ packages: name: crypto url: "https://pub.dartlang.org" source: hosted - version: "2.1.3" + version: "2.1.4" cupertino_icons: dependency: "direct main" description: @@ -64,30 +64,58 @@ packages: url: "https://pub.dartlang.org" source: hosted version: "0.1.3" + equatable: + dependency: "direct main" + description: + name: equatable + url: "https://pub.dartlang.org" + source: hosted + version: "1.1.1" flutter: dependency: "direct main" description: flutter source: sdk version: "0.0.0" - flutter_redux: - dependency: "direct main" - description: - name: flutter_redux - url: "https://pub.dartlang.org" - source: hosted - version: "0.5.4" flutter_test: dependency: "direct dev" description: flutter source: sdk version: "0.0.0" + get_it: + dependency: "direct main" + description: + name: get_it + url: "https://pub.dartlang.org" + source: hosted + version: "3.1.0" + http: + dependency: "direct main" + description: + name: http + url: "https://pub.dartlang.org" + source: hosted + version: "0.12.1" + http_parser: + dependency: transitive + description: + name: http_parser + url: "https://pub.dartlang.org" + source: hosted + version: "3.1.4" image: dependency: transitive description: name: image url: "https://pub.dartlang.org" source: hosted - version: "2.1.4" + version: "2.1.12" + logger: + dependency: "direct main" + description: + name: logger + url: "https://pub.dartlang.org" + source: hosted + version: "0.9.1" matcher: dependency: transitive description: @@ -102,6 +130,20 @@ packages: url: "https://pub.dartlang.org" source: hosted version: "1.1.8" + nested: + dependency: transitive + description: + name: nested + url: "https://pub.dartlang.org" + source: hosted + version: "0.0.4" + observable_ish: + dependency: transitive + description: + name: observable_ish + url: "https://pub.dartlang.org" + source: hosted + version: "2.1.4" path: dependency: transitive description: @@ -123,20 +165,34 @@ packages: url: "https://pub.dartlang.org" source: hosted version: "2.4.0" + provider: + dependency: "direct main" + description: + name: provider + url: "https://pub.dartlang.org" + source: hosted + version: "4.1.2" + provider_architecture: + dependency: "direct main" + description: + name: provider_architecture + url: "https://pub.dartlang.org" + source: hosted + version: "1.1.1+1" quiver: dependency: transitive description: name: quiver url: "https://pub.dartlang.org" source: hosted - version: "2.0.5" - redux: + version: "2.1.3" + responsive_builder: dependency: "direct main" description: - name: redux + name: responsive_builder url: "https://pub.dartlang.org" source: hosted - version: "3.0.0" + version: "0.1.9" sky_engine: dependency: transitive description: flutter @@ -148,7 +204,7 @@ packages: name: source_span url: "https://pub.dartlang.org" source: hosted - version: "1.5.5" + version: "1.7.0" stack_trace: dependency: transitive description: @@ -156,6 +212,13 @@ packages: url: "https://pub.dartlang.org" source: hosted version: "1.9.3" + stacked: + dependency: "direct main" + description: + name: stacked + url: "https://pub.dartlang.org" + source: hosted + version: "1.5.2" stream_channel: dependency: transitive description: @@ -183,7 +246,7 @@ packages: name: test_api url: "https://pub.dartlang.org" source: hosted - version: "0.2.11" + version: "0.2.15" typed_data: dependency: transitive description: @@ -204,6 +267,7 @@ packages: name: xml url: "https://pub.dartlang.org" source: hosted - version: "3.5.0" + version: "3.6.1" sdks: - dart: ">=2.4.0 <3.0.0" + dart: ">=2.7.0 <3.0.0" + flutter: ">=1.16.0" diff --git a/pubspec.yaml b/pubspec.yaml index 46769ae..9d452e8 100644 --- a/pubspec.yaml +++ b/pubspec.yaml @@ -1,59 +1,40 @@ name: aman_kassa_flutter description: A new Flutter project. - -# The following defines the version and build number for your application. -# A version number is three numbers separated by dots, like 1.2.43 -# followed by an optional build number separated by a +. -# Both the version and the builder number may be overridden in flutter -# build by specifying --build-name and --build-number, respectively. -# In Android, build-name is used as versionName while build-number used as versionCode. -# Read more about Android versioning at https://developer.android.com/studio/publish/versioning -# In iOS, build-name is used as CFBundleShortVersionString while build-number used as CFBundleVersion. -# Read more about iOS versioning at -# https://developer.apple.com/library/archive/documentation/General/Reference/InfoPlistKeyReference/Articles/CoreFoundationKeys.html version: 1.0.0+1 - environment: - sdk: ">=2.1.0 <3.0.0" - + sdk: '>=2.3.0 <3.0.0' dependencies: - flutter: - sdk: flutter - - redux: ^3.0.0 - flutter_redux: ^0.5.0 - - # The following adds the Cupertino Icons font to your application. - # Use with the CupertinoIcons class for iOS style icons. - cupertino_icons: ^0.1.2 - + flutter: + sdk: flutter + cupertino_icons: ^0.1.2 + stacked : ^1.5.2 + provider_architecture: ^1.0.3 + responsive_builder: ^0.1.4 + provider: ^4.1.2 + logger: ^0.9.1 + get_it: ^3.0.3 + equatable: ^1.1.1 + http: ^0.12.1 dev_dependencies: - flutter_test: - sdk: flutter - - -# For information on the generic Dart part of this file, see the -# following page: https://dart.dev/tools/pub/pubspec - -# The following section is specific to Flutter. + flutter_test: + sdk: flutter flutter: + uses-material-design: true - # The following line ensures that the Material Icons font is - # included with your application, so that you can use the icons in - # the material Icons class. - uses-material-design: true # To add assets to your application, add an assets section, like this: - # assets: - # - images/a_dot_burr.jpeg - # - images/a_dot_ham.jpeg - + assets: + - assets/images/logo.png + - assets/images/icon_large.png + - assets/lang/en.json + - assets/lang/ru.json + # An image asset can refer to one or more resolution-specific "variants", see # https://flutter.dev/assets-and-images/#resolution-aware. - + # For details regarding adding assets from package dependencies, see # https://flutter.dev/assets-and-images/#from-packages - + # To add custom fonts to your application, add a fonts section here, # in this "flutter" section. Each entry in this list should have a # "family" key with the font family name, and a "fonts" key with a @@ -72,4 +53,4 @@ flutter: # weight: 700 # # For details regarding fonts from package dependencies, - # see https://flutter.dev/custom-fonts/#from-packages + # see https://flutter.dev/custom-fonts/#from-packages \ No newline at end of file diff --git a/test/widget_test.dart b/test/widget_test.dart index 1a601cb..858b009 100644 --- a/test/widget_test.dart +++ b/test/widget_test.dart @@ -13,7 +13,7 @@ import 'package:aman_kassa_flutter/main.dart'; void main() { testWidgets('Counter increments smoke test', (WidgetTester tester) async { // Build our app and trigger a frame. - await tester.pumpWidget(MyApp()); + await tester.pumpWidget(MainApplication()); // Verify that our counter starts at 0. expect(find.text('0'), findsOneWidget);