MVVM with Provider
parent
6425f34f4f
commit
30db638877
|
|
@ -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"
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
Binary file not shown.
|
After Width: | Height: | Size: 23 KiB |
Binary file not shown.
|
After Width: | Height: | Size: 12 KiB |
Binary file not shown.
|
After Width: | Height: | Size: 7.5 KiB |
|
|
@ -0,0 +1,4 @@
|
||||||
|
{
|
||||||
|
"main.emptyList.title": "List is empty",
|
||||||
|
"main.emptyList.firstPaymentNotAdded": "Need add first payment"
|
||||||
|
}
|
||||||
|
|
@ -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": "Дополнительная информация"
|
||||||
|
}
|
||||||
|
|
@ -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";
|
|
||||||
}
|
|
||||||
|
|
@ -0,0 +1,5 @@
|
||||||
|
import 'package:equatable/equatable.dart';
|
||||||
|
|
||||||
|
abstract class BaseModel implements Equatable {
|
||||||
|
Map<String, Object> toMap();
|
||||||
|
}
|
||||||
|
|
@ -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(),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -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();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -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<void> setupLocator() async {
|
||||||
|
_log.d('Initializing Navigator Service');
|
||||||
|
locator.registerLazySingleton(() => NavigatorService());
|
||||||
|
_log.d('Initializing Api Service');
|
||||||
|
locator.registerLazySingleton(() => ApiService());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -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<String> 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));
|
||||||
|
}
|
||||||
|
|
@ -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<String, dynamic> data)
|
||||||
|
: id = data['id'],
|
||||||
|
fullName = data['fullName'],
|
||||||
|
email = data['email'],
|
||||||
|
userRole = data['userRole'];
|
||||||
|
|
||||||
|
Map<String, dynamic> toJson() {
|
||||||
|
return {
|
||||||
|
'id': id,
|
||||||
|
'fullName': fullName,
|
||||||
|
'email': email,
|
||||||
|
'userRole': userRole,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -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<SingleChildWidget> providers = [
|
||||||
|
..._independentServices,
|
||||||
|
..._dependentServices,
|
||||||
|
..._consumableServices,
|
||||||
|
];
|
||||||
|
|
||||||
|
static List<SingleChildWidget> _independentServices = [
|
||||||
|
Provider.value(value: locator<NavigatorService>()),
|
||||||
|
Provider.value(value: locator<ApiService>()),
|
||||||
|
];
|
||||||
|
|
||||||
|
static List<SingleChildWidget> _dependentServices = [
|
||||||
|
ProxyProvider<ApiService, AuthenticationService>(
|
||||||
|
update: (context, api, authenticationService) =>
|
||||||
|
AuthenticationService(api: api),
|
||||||
|
)
|
||||||
|
];
|
||||||
|
|
||||||
|
static List<SingleChildWidget> _consumableServices = [];
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,3 @@
|
||||||
|
const String LoginViewRoute = "LoginView";
|
||||||
|
const String HomeViewRoute = "HomeView";
|
||||||
|
// Generate the views here
|
||||||
|
|
@ -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<dynamic> 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<double> animation,
|
||||||
|
Animation<double> secondaryAnimation) {
|
||||||
|
return widget;
|
||||||
|
},
|
||||||
|
transitionsBuilder: (BuildContext context,
|
||||||
|
Animation<double> animation,
|
||||||
|
Animation<double> secondaryAnimation,
|
||||||
|
Widget child) {
|
||||||
|
return new SlideTransition(
|
||||||
|
position: new Tween<Offset>(
|
||||||
|
begin: const Offset(1.0, 0.0),
|
||||||
|
end: Offset.zero,
|
||||||
|
).animate(animation),
|
||||||
|
child: child,
|
||||||
|
);
|
||||||
|
},
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
@ -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<User> 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<List<Post>> getPostsForUser(int userId) async {
|
||||||
|
// var posts = List<Post>();
|
||||||
|
// // 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<dynamic>;
|
||||||
|
|
||||||
|
// // loop and convert each item to Post
|
||||||
|
// for (var post in parsed) {
|
||||||
|
// posts.add(Post.fromJson(post));
|
||||||
|
// }
|
||||||
|
|
||||||
|
// return posts;
|
||||||
|
// }
|
||||||
|
|
||||||
|
}
|
||||||
|
|
@ -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<bool> isUserLoggedIn() async {
|
||||||
|
// var user = await _firebaseAuth.currentUser();
|
||||||
|
// await _populateCurrentUser(user);
|
||||||
|
// return user != null;
|
||||||
|
// }
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,35 @@
|
||||||
|
import '../../core/base/base_service.dart';
|
||||||
|
import 'package:flutter/material.dart';
|
||||||
|
|
||||||
|
class NavigatorService extends BaseService {
|
||||||
|
final GlobalKey<NavigatorState> navigatorKey = GlobalKey<NavigatorState>();
|
||||||
|
|
||||||
|
Future<T> navigateToPage<T>(MaterialPageRoute<T> 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<T> navigateToPageWithReplacement<T>(
|
||||||
|
MaterialPageRoute<T> 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>([T result]) {
|
||||||
|
log.i('goBack:');
|
||||||
|
if (navigatorKey.currentState == null) {
|
||||||
|
log.e('goBack: Navigator State is null');
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
navigatorKey.currentState.pop(result);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -1,31 +0,0 @@
|
||||||
import 'package:flutter/material.dart';
|
|
||||||
|
|
||||||
class AvatarsStack extends StatelessWidget {
|
|
||||||
final List<String> names;
|
|
||||||
|
|
||||||
AvatarsStack(this.names);
|
|
||||||
|
|
||||||
@override
|
|
||||||
Widget build(BuildContext context) {
|
|
||||||
final avatars = List<Widget>();
|
|
||||||
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,
|
|
||||||
),
|
|
||||||
);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
@ -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: <Widget>[
|
|
||||||
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: <Widget>[
|
|
||||||
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: <Widget>[
|
|
||||||
Text(
|
|
||||||
'${game.where}',
|
|
||||||
style: TextStyle(fontSize: 20),
|
|
||||||
),
|
|
||||||
Row(
|
|
||||||
children: <Widget>[
|
|
||||||
Text('aaa')
|
|
||||||
],
|
|
||||||
),
|
|
||||||
],
|
|
||||||
),
|
|
||||||
),
|
|
||||||
Expanded(
|
|
||||||
child: AvatarsStack(
|
|
||||||
game.players.map((player) => player.name).toList())),
|
|
||||||
],
|
|
||||||
),
|
|
||||||
),
|
|
||||||
),
|
|
||||||
),
|
|
||||||
);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
@ -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<HomePage> {
|
|
||||||
List<GameListItem> _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());
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
@ -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<AppState, _ViewModel>(
|
|
||||||
distinct: true,
|
|
||||||
converter: (store) => _ViewModel.fromStore(store),
|
|
||||||
builder: (context, vm) => Row(
|
|
||||||
mainAxisAlignment: MainAxisAlignment.spaceAround,
|
|
||||||
children: <Widget>[
|
|
||||||
_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<String> route;
|
|
||||||
final Function(String) navigate;
|
|
||||||
|
|
||||||
_ViewModel({@required this.route, @required this.navigate});
|
|
||||||
|
|
||||||
static _ViewModel fromStore(Store<AppState> 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;
|
|
||||||
}
|
|
||||||
|
|
@ -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<AppState>(context)
|
|
||||||
.dispatch(NavigatePushAction(AppRoutes.addGame)),
|
|
||||||
tooltip: 'Add new game',
|
|
||||||
child: Icon(Icons.add),
|
|
||||||
),
|
|
||||||
body: body,
|
|
||||||
);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
@ -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: <Widget>[
|
|
||||||
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),
|
|
||||||
);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
@ -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());
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
106
lib/main.dart
106
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/material.dart';
|
||||||
import 'package:flutter_redux/flutter_redux.dart';
|
import 'package:provider/provider.dart';
|
||||||
import 'package:redux/redux.dart';
|
import 'views/home/home_view.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';
|
|
||||||
|
|
||||||
void main() => runApp(MyApp());
|
void main() async {
|
||||||
|
await LocatorInjector.setupLocator();
|
||||||
final GlobalKey<NavigatorState> navigatorKey = new GlobalKey<NavigatorState>();
|
runApp(MainApplication());
|
||||||
|
}
|
||||||
class MyApp extends StatelessWidget {
|
|
||||||
final store = Store<AppState>(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);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
|
class MainApplication extends StatelessWidget {
|
||||||
@override
|
@override
|
||||||
Widget build(BuildContext context) {
|
Widget build(BuildContext context) {
|
||||||
return StoreProvider(
|
return MultiProvider(
|
||||||
store: store,
|
providers: ProviderInjector.providers.toList(),
|
||||||
child: MaterialApp(
|
child: MaterialApp(
|
||||||
navigatorKey: navigatorKey,
|
navigatorKey: locator<NavigatorService>().navigatorKey,
|
||||||
navigatorObservers: [routeObserver],
|
home: HomeView(),
|
||||||
title: 'AppLocalizations.appTitle',
|
onGenerateRoute: generateRoute,
|
||||||
localizationsDelegates: [
|
|
||||||
//AppLocalizationsDelegate(),
|
|
||||||
],
|
|
||||||
theme: theme,
|
|
||||||
onGenerateRoute: (RouteSettings settings) => _getRoute(settings),
|
|
||||||
),
|
),
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
class MainRoute<T> extends MaterialPageRoute<T> {
|
|
||||||
MainRoute(Widget widget, {RouteSettings settings})
|
|
||||||
: super(
|
|
||||||
builder: (_) => RouteAwareWidget(child: widget),
|
|
||||||
settings: settings);
|
|
||||||
|
|
||||||
@override
|
|
||||||
Widget buildTransitions(BuildContext context, Animation<double> animation,
|
|
||||||
Animation<double> 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<T> extends MaterialPageRoute<T> {
|
|
||||||
FabRoute(Widget widget, {RouteSettings settings})
|
|
||||||
: super(
|
|
||||||
builder: (_) => RouteAwareWidget(child: widget),
|
|
||||||
settings: settings);
|
|
||||||
|
|
||||||
@override
|
|
||||||
Widget buildTransitions(BuildContext context, Animation<double> animation,
|
|
||||||
Animation<double> secondaryAnimation, Widget child) {
|
|
||||||
if (settings.isInitialRoute) return child;
|
|
||||||
return SlideTransition(
|
|
||||||
position: new Tween<Offset>(
|
|
||||||
begin: const Offset(0.0, 1.0),
|
|
||||||
end: Offset.zero,
|
|
||||||
).animate(animation),
|
|
||||||
child: child);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
|
||||||
|
|
@ -1,6 +0,0 @@
|
||||||
class Court {
|
|
||||||
final num number;
|
|
||||||
final bool wasReserved;
|
|
||||||
|
|
||||||
Court({this.number, this.wasReserved});
|
|
||||||
}
|
|
||||||
|
|
@ -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<Court> courts;
|
|
||||||
final List<Player> players;
|
|
||||||
|
|
||||||
Game({@required this.where, @required this.date, this.players, this.courts});
|
|
||||||
}
|
|
||||||
|
|
@ -1,5 +0,0 @@
|
||||||
class Player {
|
|
||||||
final String name;
|
|
||||||
|
|
||||||
Player(this.name);
|
|
||||||
}
|
|
||||||
|
|
@ -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';
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
@ -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<Game> games;
|
|
||||||
final List<String> route;
|
|
||||||
|
|
||||||
AppState({
|
|
||||||
this.isLoading = false,
|
|
||||||
this.games = const [],
|
|
||||||
this.route = const [AppRoutes.home],
|
|
||||||
});
|
|
||||||
|
|
||||||
factory AppState.loading() => AppState(isLoading: true);
|
|
||||||
|
|
||||||
AppState copyWith({
|
|
||||||
bool isLoading,
|
|
||||||
List<Game> 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}';
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
@ -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<Middleware<AppState>> createNavigationMiddleware() {
|
|
||||||
return [
|
|
||||||
TypedMiddleware<AppState, NavigateReplaceAction>(_navigateReplace),
|
|
||||||
TypedMiddleware<AppState, NavigatePushAction>(_navigate),
|
|
||||||
];
|
|
||||||
}
|
|
||||||
|
|
||||||
_navigateReplace(Store<AppState> 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<AppState> 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
|
|
||||||
}
|
|
||||||
|
|
@ -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)
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
@ -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<List<Game>>([
|
|
||||||
TypedReducer<List<Game>, AddGameAction>(_addGame),
|
|
||||||
]);
|
|
||||||
|
|
||||||
List<Game> _addGame(List<Game> games, AddGameAction action) {
|
|
||||||
return List.from(games)..add(action.game);
|
|
||||||
}
|
|
||||||
|
|
@ -1,5 +0,0 @@
|
||||||
import 'package:redux/redux.dart';
|
|
||||||
|
|
||||||
final loadingReducer = combineReducers<bool>([
|
|
||||||
]);
|
|
||||||
|
|
||||||
|
|
@ -1,24 +0,0 @@
|
||||||
import 'package:redux/redux.dart';
|
|
||||||
import 'package:aman_kassa_flutter/redux/actions.dart';
|
|
||||||
|
|
||||||
final navigationReducer = combineReducers<List<String>>([
|
|
||||||
TypedReducer<List<String>, NavigateReplaceAction>(_navigateReplace),
|
|
||||||
TypedReducer<List<String>, NavigatePushAction>(_navigatePush),
|
|
||||||
TypedReducer<List<String>, NavigatePopAction>(_navigatePop),
|
|
||||||
]);
|
|
||||||
|
|
||||||
List<String> _navigateReplace(
|
|
||||||
List<String> route, NavigateReplaceAction action) =>
|
|
||||||
[action.routeName];
|
|
||||||
|
|
||||||
List<String> _navigatePush(List<String> route, NavigatePushAction action) {
|
|
||||||
var result = List<String>.from(route);
|
|
||||||
result.add(action.routeName);
|
|
||||||
return result;
|
|
||||||
}
|
|
||||||
|
|
||||||
List<String> _navigatePop(List<String> route, NavigatePopAction action) {
|
|
||||||
var result = List<String>.from(route);
|
|
||||||
result.removeLast();
|
|
||||||
return result;
|
|
||||||
}
|
|
||||||
|
|
@ -1,4 +0,0 @@
|
||||||
|
|
||||||
import './app_state.dart';
|
|
||||||
|
|
||||||
List<String> currentRoute(AppState state) => state.route;
|
|
||||||
|
|
@ -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<PageRoute> routeObserver = RouteObserver<PageRoute>();
|
|
||||||
|
|
||||||
class RouteAwareWidget extends StatefulWidget {
|
|
||||||
final Widget child;
|
|
||||||
|
|
||||||
RouteAwareWidget({this.child});
|
|
||||||
|
|
||||||
State<RouteAwareWidget> createState() => RouteAwareWidgetState(child: child);
|
|
||||||
}
|
|
||||||
|
|
||||||
class RouteAwareWidgetState extends State<RouteAwareWidget> 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<AppState>(context).dispatch(NavigatePopAction());
|
|
||||||
}
|
|
||||||
|
|
||||||
@override
|
|
||||||
Widget build(BuildContext context) => Container(child: child);
|
|
||||||
}
|
|
||||||
|
|
@ -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<HomeViewModel>.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: <Widget>[
|
||||||
|
SizedBox(
|
||||||
|
width: 300,
|
||||||
|
height: 100,
|
||||||
|
child: Image.asset('assets/images/icon_large.png'),
|
||||||
|
),
|
||||||
|
CircularProgressIndicator(
|
||||||
|
strokeWidth: 3,
|
||||||
|
valueColor: AlwaysStoppedAnimation(
|
||||||
|
Colors.yellow[300],
|
||||||
|
),
|
||||||
|
)
|
||||||
|
],
|
||||||
|
),
|
||||||
|
));
|
||||||
|
}
|
||||||
|
|
@ -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<NavigatorService>();
|
||||||
|
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()));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -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<LoginViewModel>.reactive(
|
||||||
|
viewModelBuilder: () => LoginViewModel(),
|
||||||
|
onModelReady: (viewModel) {
|
||||||
|
// Do something once your viewModel is initialized
|
||||||
|
},
|
||||||
|
builder: (context, viewModel, child) {
|
||||||
|
return Scaffold(
|
||||||
|
body: Center(child: Text('LoginMobile')),
|
||||||
|
);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -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
|
||||||
|
}
|
||||||
110
pubspec.lock
110
pubspec.lock
|
|
@ -7,42 +7,42 @@ packages:
|
||||||
name: archive
|
name: archive
|
||||||
url: "https://pub.dartlang.org"
|
url: "https://pub.dartlang.org"
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "2.0.11"
|
version: "2.0.13"
|
||||||
args:
|
args:
|
||||||
dependency: transitive
|
dependency: transitive
|
||||||
description:
|
description:
|
||||||
name: args
|
name: args
|
||||||
url: "https://pub.dartlang.org"
|
url: "https://pub.dartlang.org"
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "1.5.2"
|
version: "1.6.0"
|
||||||
async:
|
async:
|
||||||
dependency: transitive
|
dependency: transitive
|
||||||
description:
|
description:
|
||||||
name: async
|
name: async
|
||||||
url: "https://pub.dartlang.org"
|
url: "https://pub.dartlang.org"
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "2.4.0"
|
version: "2.4.1"
|
||||||
boolean_selector:
|
boolean_selector:
|
||||||
dependency: transitive
|
dependency: transitive
|
||||||
description:
|
description:
|
||||||
name: boolean_selector
|
name: boolean_selector
|
||||||
url: "https://pub.dartlang.org"
|
url: "https://pub.dartlang.org"
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "1.0.5"
|
version: "2.0.0"
|
||||||
charcode:
|
charcode:
|
||||||
dependency: transitive
|
dependency: transitive
|
||||||
description:
|
description:
|
||||||
name: charcode
|
name: charcode
|
||||||
url: "https://pub.dartlang.org"
|
url: "https://pub.dartlang.org"
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "1.1.2"
|
version: "1.1.3"
|
||||||
collection:
|
collection:
|
||||||
dependency: transitive
|
dependency: transitive
|
||||||
description:
|
description:
|
||||||
name: collection
|
name: collection
|
||||||
url: "https://pub.dartlang.org"
|
url: "https://pub.dartlang.org"
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "1.14.11"
|
version: "1.14.12"
|
||||||
convert:
|
convert:
|
||||||
dependency: transitive
|
dependency: transitive
|
||||||
description:
|
description:
|
||||||
|
|
@ -56,7 +56,7 @@ packages:
|
||||||
name: crypto
|
name: crypto
|
||||||
url: "https://pub.dartlang.org"
|
url: "https://pub.dartlang.org"
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "2.1.3"
|
version: "2.1.4"
|
||||||
cupertino_icons:
|
cupertino_icons:
|
||||||
dependency: "direct main"
|
dependency: "direct main"
|
||||||
description:
|
description:
|
||||||
|
|
@ -64,30 +64,58 @@ packages:
|
||||||
url: "https://pub.dartlang.org"
|
url: "https://pub.dartlang.org"
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "0.1.3"
|
version: "0.1.3"
|
||||||
|
equatable:
|
||||||
|
dependency: "direct main"
|
||||||
|
description:
|
||||||
|
name: equatable
|
||||||
|
url: "https://pub.dartlang.org"
|
||||||
|
source: hosted
|
||||||
|
version: "1.1.1"
|
||||||
flutter:
|
flutter:
|
||||||
dependency: "direct main"
|
dependency: "direct main"
|
||||||
description: flutter
|
description: flutter
|
||||||
source: sdk
|
source: sdk
|
||||||
version: "0.0.0"
|
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:
|
flutter_test:
|
||||||
dependency: "direct dev"
|
dependency: "direct dev"
|
||||||
description: flutter
|
description: flutter
|
||||||
source: sdk
|
source: sdk
|
||||||
version: "0.0.0"
|
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:
|
image:
|
||||||
dependency: transitive
|
dependency: transitive
|
||||||
description:
|
description:
|
||||||
name: image
|
name: image
|
||||||
url: "https://pub.dartlang.org"
|
url: "https://pub.dartlang.org"
|
||||||
source: hosted
|
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:
|
matcher:
|
||||||
dependency: transitive
|
dependency: transitive
|
||||||
description:
|
description:
|
||||||
|
|
@ -102,6 +130,20 @@ packages:
|
||||||
url: "https://pub.dartlang.org"
|
url: "https://pub.dartlang.org"
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "1.1.8"
|
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:
|
path:
|
||||||
dependency: transitive
|
dependency: transitive
|
||||||
description:
|
description:
|
||||||
|
|
@ -123,20 +165,34 @@ packages:
|
||||||
url: "https://pub.dartlang.org"
|
url: "https://pub.dartlang.org"
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "2.4.0"
|
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:
|
quiver:
|
||||||
dependency: transitive
|
dependency: transitive
|
||||||
description:
|
description:
|
||||||
name: quiver
|
name: quiver
|
||||||
url: "https://pub.dartlang.org"
|
url: "https://pub.dartlang.org"
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "2.0.5"
|
version: "2.1.3"
|
||||||
redux:
|
responsive_builder:
|
||||||
dependency: "direct main"
|
dependency: "direct main"
|
||||||
description:
|
description:
|
||||||
name: redux
|
name: responsive_builder
|
||||||
url: "https://pub.dartlang.org"
|
url: "https://pub.dartlang.org"
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "3.0.0"
|
version: "0.1.9"
|
||||||
sky_engine:
|
sky_engine:
|
||||||
dependency: transitive
|
dependency: transitive
|
||||||
description: flutter
|
description: flutter
|
||||||
|
|
@ -148,7 +204,7 @@ packages:
|
||||||
name: source_span
|
name: source_span
|
||||||
url: "https://pub.dartlang.org"
|
url: "https://pub.dartlang.org"
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "1.5.5"
|
version: "1.7.0"
|
||||||
stack_trace:
|
stack_trace:
|
||||||
dependency: transitive
|
dependency: transitive
|
||||||
description:
|
description:
|
||||||
|
|
@ -156,6 +212,13 @@ packages:
|
||||||
url: "https://pub.dartlang.org"
|
url: "https://pub.dartlang.org"
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "1.9.3"
|
version: "1.9.3"
|
||||||
|
stacked:
|
||||||
|
dependency: "direct main"
|
||||||
|
description:
|
||||||
|
name: stacked
|
||||||
|
url: "https://pub.dartlang.org"
|
||||||
|
source: hosted
|
||||||
|
version: "1.5.2"
|
||||||
stream_channel:
|
stream_channel:
|
||||||
dependency: transitive
|
dependency: transitive
|
||||||
description:
|
description:
|
||||||
|
|
@ -183,7 +246,7 @@ packages:
|
||||||
name: test_api
|
name: test_api
|
||||||
url: "https://pub.dartlang.org"
|
url: "https://pub.dartlang.org"
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "0.2.11"
|
version: "0.2.15"
|
||||||
typed_data:
|
typed_data:
|
||||||
dependency: transitive
|
dependency: transitive
|
||||||
description:
|
description:
|
||||||
|
|
@ -204,6 +267,7 @@ packages:
|
||||||
name: xml
|
name: xml
|
||||||
url: "https://pub.dartlang.org"
|
url: "https://pub.dartlang.org"
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "3.5.0"
|
version: "3.6.1"
|
||||||
sdks:
|
sdks:
|
||||||
dart: ">=2.4.0 <3.0.0"
|
dart: ">=2.7.0 <3.0.0"
|
||||||
|
flutter: ">=1.16.0"
|
||||||
|
|
|
||||||
49
pubspec.yaml
49
pubspec.yaml
|
|
@ -1,52 +1,33 @@
|
||||||
name: aman_kassa_flutter
|
name: aman_kassa_flutter
|
||||||
description: A new Flutter project.
|
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
|
version: 1.0.0+1
|
||||||
|
|
||||||
environment:
|
environment:
|
||||||
sdk: ">=2.1.0 <3.0.0"
|
sdk: '>=2.3.0 <3.0.0'
|
||||||
|
|
||||||
dependencies:
|
dependencies:
|
||||||
flutter:
|
flutter:
|
||||||
sdk: 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
|
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:
|
dev_dependencies:
|
||||||
flutter_test:
|
flutter_test:
|
||||||
sdk: flutter
|
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:
|
flutter:
|
||||||
|
|
||||||
# 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
|
uses-material-design: true
|
||||||
|
|
||||||
|
|
||||||
# To add assets to your application, add an assets section, like this:
|
# To add assets to your application, add an assets section, like this:
|
||||||
# assets:
|
assets:
|
||||||
# - images/a_dot_burr.jpeg
|
- assets/images/logo.png
|
||||||
# - images/a_dot_ham.jpeg
|
- 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
|
# An image asset can refer to one or more resolution-specific "variants", see
|
||||||
# https://flutter.dev/assets-and-images/#resolution-aware.
|
# https://flutter.dev/assets-and-images/#resolution-aware.
|
||||||
|
|
|
||||||
|
|
@ -13,7 +13,7 @@ import 'package:aman_kassa_flutter/main.dart';
|
||||||
void main() {
|
void main() {
|
||||||
testWidgets('Counter increments smoke test', (WidgetTester tester) async {
|
testWidgets('Counter increments smoke test', (WidgetTester tester) async {
|
||||||
// Build our app and trigger a frame.
|
// Build our app and trigger a frame.
|
||||||
await tester.pumpWidget(MyApp());
|
await tester.pumpWidget(MainApplication());
|
||||||
|
|
||||||
// Verify that our counter starts at 0.
|
// Verify that our counter starts at 0.
|
||||||
expect(find.text('0'), findsOneWidget);
|
expect(find.text('0'), findsOneWidget);
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue