Выпадающее меню с тремя кнопками
This commit is contained in:
131
lib/screens/faq.dart
Normal file
131
lib/screens/faq.dart
Normal file
@@ -0,0 +1,131 @@
|
||||
import 'package:flutter/material.dart';
|
||||
|
||||
import 'package:checker/base_state.dart';
|
||||
import 'package:checker/consts.dart';
|
||||
import 'package:checker/common.dart';
|
||||
|
||||
/// Класс содержит заголовки и текст блоков FAQ.
|
||||
class Entry {
|
||||
Entry(this.title, this.text);
|
||||
final String title;
|
||||
final String text;
|
||||
}
|
||||
|
||||
class EntryItem extends StatelessWidget {
|
||||
|
||||
const EntryItem(this.entry);
|
||||
final Entry entry;
|
||||
|
||||
Widget _buildTiles(BuildContext context, Entry root) {
|
||||
EdgeInsets margin = new EdgeInsets.only(left: 20.0, right: 20.0);
|
||||
TextStyle titleStyle = Theme.of(context).textTheme.button.copyWith(fontWeight: FontWeight.bold, color: faqTitlesColor);
|
||||
return new Container(margin: margin, child: new Card(child: new ExpansionTile(
|
||||
key: new PageStorageKey<Entry>(root),
|
||||
title:new Text(root.title, style: titleStyle),
|
||||
children: [new Container(margin: margin, padding: new EdgeInsets.only(top: 12.0, bottom: 20.0),
|
||||
child: new Text(root.text, style: new TextStyle(fontWeight: FontWeight.w300, color: faqGrey, fontSize: 14.0)),
|
||||
decoration: new BoxDecoration(border: new Border(top: new BorderSide(color: greyTextColor, width: 0.5))))]
|
||||
)));
|
||||
}
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return _buildTiles(context, entry);
|
||||
}
|
||||
}
|
||||
|
||||
class FAQScreen extends StatefulWidget {
|
||||
|
||||
FAQScreen(this.b);
|
||||
final bool b;
|
||||
|
||||
@override State createState() => new FAQScreenState<FAQScreen>(b);
|
||||
}
|
||||
|
||||
class FAQScreenState<T> extends BaseState<FAQScreen> {
|
||||
|
||||
FAQScreenState(this.returnToScanner);
|
||||
final bool returnToScanner;
|
||||
|
||||
@override String getTitle() {
|
||||
return "FAQ";
|
||||
}
|
||||
|
||||
@override getMenuButtons() {
|
||||
return <Widget>[getLogoutButton()];
|
||||
}
|
||||
|
||||
@override String getHint() {
|
||||
return null;
|
||||
}
|
||||
|
||||
/// Метод возвращает ListView с блоками faq.
|
||||
@override Widget getScreenContent() {
|
||||
return new WillPopScope(onWillPop: onWillPop, child: new ListView.builder(
|
||||
itemBuilder: (BuildContext context, int index) => new EntryItem(data[index]),
|
||||
itemCount: data.length));
|
||||
}
|
||||
|
||||
onWillPop() {
|
||||
if(returnToScanner) {
|
||||
return startScanner(context, app, helper);
|
||||
} else {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
/// Список с контентом
|
||||
final List<Entry> data = <Entry>[
|
||||
new Entry('РЕГИСТРАЦИЯ', registrationGuide),
|
||||
new Entry('ИСПОЛЬЗОВАНИЕ', usageGuide),
|
||||
new Entry('КОНТАКТЫ ПОДДЕРЖКИ', supportGuide),
|
||||
new Entry('ОБЩАЯ ИНФОРМАЦИЯ', commonGuide)
|
||||
];
|
||||
|
||||
static const String registrationGuide = '''
|
||||
После запуска приложения вы окажетесь на странице регистрации магазина.
|
||||
|
||||
Введите DIN код магазина (выдается партнером/менеджером International Auto Club, дублируется на почту)
|
||||
|
||||
Кликните по кнопке: «Зарегистрировать»
|
||||
Дождитесь подтверждение активации программы, кликом по кнопке «Обновите статус активации» обновите статус.
|
||||
|
||||
После подтверждения запроса на активацию программы Партнером/менеджером кликните по кнопке «Завершить регистрацию», приложение готово к использованию.
|
||||
|
||||
При желании изменить номер кассы, необходимо кликнуть на «значок» верхнем правом углу и вернуться на шаг регистрации.
|
||||
''';
|
||||
|
||||
static const String usageGuide = '''
|
||||
Действие 1:
|
||||
|
||||
При предъявлении покупателем штрих-кода участника системы лояльности, запустите данное приложение.
|
||||
|
||||
На экране появится сканер штрих кодов. Отсканируйте предъявленный штрих-код сканером.
|
||||
|
||||
При успешном сканировании на вашем экране появятся данные партнера.
|
||||
|
||||
Действие 2:
|
||||
|
||||
Необходимо ввести сумму покупки данного покупателя и кликнуть по кнопке «Зафиксировать».
|
||||
|
||||
Всплывет окно подтверждения правильности ввода суммы». В случае правильного ввода суммы, кликните «ДА», сумма будет проведена и вознаграждение будет начислено участнику системы лояльности.
|
||||
|
||||
Если сумма введена с ошибкой, кликните «НЕТ» и Вы вернетесь на шаг ввода суммы и сможете её скорректировать.
|
||||
''';
|
||||
|
||||
static const String supportGuide = '''
|
||||
При некорректной работе приложения AUTO BONUS просьба сразу обратиться по телефону нашей технической поддержки: 8-800-234-6064 (звонок бесплатный) и Вас свяжут с менеджером.
|
||||
|
||||
При звонке приготовьтесь назвать ИНН и наименование вашей организации.
|
||||
|
||||
Рекомендуйте покупателям установить мобильное приложение дисконтной системы International Auto Club AUTO CLUB и получайте новых лояльных покупателей.
|
||||
|
||||
Наш сайт https://www.auto-club.biz
|
||||
''';
|
||||
|
||||
static const String commonGuide = '''
|
||||
Для эффективного считывания штрих-кода участника системы лояльности необходимо камеру сканера поднести так, чтобы в неё не попадали вертикальные полосы рамки.
|
||||
|
||||
Увеличение времени сканирования может произойти из-за черной рамки, в которую помещен штрих-код, так как вертикальные полосы этой рамки расцениваются сканером как часть штрих-кода.
|
||||
''';
|
||||
}
|
||||
107
lib/screens/finish_registration.dart
Normal file
107
lib/screens/finish_registration.dart
Normal file
@@ -0,0 +1,107 @@
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:flutter/services.dart';
|
||||
import 'dart:convert'; // Пакет для обработки json с ответом от сервера.
|
||||
|
||||
import 'package:checker/common.dart';
|
||||
import 'package:checker/consts.dart';
|
||||
import 'package:checker/network.dart';
|
||||
import 'package:checker/base_state.dart';
|
||||
import 'package:checker/strings.dart';
|
||||
|
||||
class FinishRegistrationScreen extends StatefulWidget {
|
||||
@override State createState() => new _RegistrationScreenState();
|
||||
}
|
||||
|
||||
class _RegistrationScreenState extends BaseState<FinishRegistrationScreen> {
|
||||
|
||||
bool _tokenActive = false;
|
||||
String _merchantID;
|
||||
|
||||
@override void onStart() {
|
||||
helper.getMerchantID().then((result) {
|
||||
setState(() {
|
||||
_merchantID = result;
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
@override String getTitle() {
|
||||
return StringsLocalization.registration();
|
||||
}
|
||||
|
||||
@override getHint() {
|
||||
return _merchantID;
|
||||
}
|
||||
|
||||
@override Widget getScreenContent() {
|
||||
return new Column(children: <Widget>[
|
||||
getLogo(),
|
||||
getHintLabel(),
|
||||
getInputField(),
|
||||
getMessage(),
|
||||
buildRaisedButton(_tokenActive
|
||||
? StringsLocalization.completeRegistration()
|
||||
: StringsLocalization.refreshActivationStatus(), () => handleTap())
|
||||
]);
|
||||
}
|
||||
|
||||
// Если токен активирован, то открывается экран со сканером,
|
||||
// Если нет, то отправляется запрос на проверку статуса токена.
|
||||
handleTap() async {
|
||||
if (_tokenActive) {
|
||||
startScanner(context, app, helper);
|
||||
} else {
|
||||
if (await platform.invokeMethod('isOnline')) {
|
||||
String token = await helper.getToken();
|
||||
checkTokenStatus(token).then((response) {
|
||||
|
||||
print(response.body);
|
||||
Map parsedMap = JSON.decode(response.body);
|
||||
|
||||
// Обновить экран, заменить сообщение о необходимости активации токена, на сообщние о том, что токен активен.
|
||||
setState(() {
|
||||
_tokenActive = parsedMap['active'];
|
||||
});
|
||||
|
||||
}).catchError((error) {
|
||||
print(error.toString());
|
||||
return false;
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@override getTextWidget() {
|
||||
return new Row(children: <Widget>[new Text(_merchantID != null ? _merchantID : '',
|
||||
style: new TextStyle(color: Colors.black, fontSize: 16.0))]);
|
||||
}
|
||||
|
||||
/// Метод возвращает контейнер с текстом сообщения и бэкграундом.
|
||||
getMessage() {
|
||||
return new Container(height: _tokenActive ? 72.0 : 108.0, decoration: _getDecorationForMessageField(),
|
||||
margin: new EdgeInsets.only(top: 20.0, left: 12.0, right: 12.0),
|
||||
padding: new EdgeInsets.only(bottom: 22.0, left: 14.0, right: 14.0),
|
||||
child: new Center(child: getMessageTextWidget()));
|
||||
}
|
||||
|
||||
/// Метод возвращает виджет с текстом сообщения, всеми его привязками и стилями.
|
||||
getMessageTextWidget() {
|
||||
return new Text(getMessageString(), textAlign: TextAlign.center,
|
||||
style: new TextStyle(height: 1.5, fontWeight: FontWeight.bold, fontSize: 14.0,
|
||||
color: _tokenActive ? tokenActiveTextColor : tokenActivateTextColor));
|
||||
}
|
||||
|
||||
/// Получаем текст сообщения, в зависимости от статуса активации.
|
||||
getMessageString() {
|
||||
return _tokenActive
|
||||
? StringsLocalization.completeRegistration()
|
||||
: StringsLocalization.refreshActivationStatus();
|
||||
}
|
||||
|
||||
/// Фоновое изображение для сообщения.
|
||||
Decoration _getDecorationForMessageField() {
|
||||
return new BoxDecoration(image: new DecorationImage(
|
||||
image: new ExactAssetImage(_tokenActive ? active_token_bg_png : activate_token_bg_png),
|
||||
fit: _tokenActive ? BoxFit.fitWidth : BoxFit.fill));
|
||||
}
|
||||
}
|
||||
238
lib/screens/purchase.dart
Normal file
238
lib/screens/purchase.dart
Normal file
@@ -0,0 +1,238 @@
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:flutter/services.dart';
|
||||
import 'dart:convert';
|
||||
import 'dart:core';
|
||||
|
||||
import 'package:checker/resources.dart';
|
||||
import 'package:checker/strings.dart';
|
||||
import 'package:checker/common.dart';
|
||||
import 'package:checker/consts.dart';
|
||||
import 'package:checker/network.dart';
|
||||
import 'package:checker/base_state.dart';
|
||||
import 'package:checker/screens/purchase_success.dart';
|
||||
|
||||
/// Экран проведения покупки.
|
||||
class PurchaseScreen extends StatefulWidget {
|
||||
|
||||
PurchaseScreen(this.user, this.card);
|
||||
|
||||
final String user;
|
||||
final String card;
|
||||
|
||||
@override State createState() => new PurchaseScreenState<PurchaseScreen>(user, card);
|
||||
}
|
||||
|
||||
class PurchaseScreenState<T> extends BaseState<PurchaseScreen> {
|
||||
|
||||
/// Объект, помогающий вручную изменять введенный пользователем текст.
|
||||
/// Используется для форматирования введенных пользователем данных
|
||||
/// (удаляет запрещенные символы до их отображаения).
|
||||
TextEditingController controller = new TextEditingController();
|
||||
|
||||
PurchaseScreenState(String userString, String card) {
|
||||
this.user = JSON.decode(userString);
|
||||
this.card = card;
|
||||
}
|
||||
|
||||
@override void onStart() {
|
||||
getLoyalty(user['loyalty_url']);
|
||||
}
|
||||
|
||||
bool purchaseInProgress = false;
|
||||
Map user;
|
||||
String card = '';
|
||||
String loyalty = '';
|
||||
|
||||
@override Widget getScreenContent() {
|
||||
return new Column(
|
||||
children: <Widget>[new Expanded(child: new ListView(children: <Widget>[
|
||||
getValueWithDescription(StringsLocalization.userName(), user['first_name'] == null ? '' : user['first_name']),
|
||||
getValueWithDescription(StringsLocalization.card(), card),
|
||||
getValueWithDescription(StringsLocalization.reward(), loyalty),
|
||||
getHintLabel(),
|
||||
getInputField(),
|
||||
wrapButton(getScreenMargins(36.0), getCompleteButton()),
|
||||
wrapButton(getScreenMargins(24.0), getScanButton(context, StringsLocalization.scan(), Resources.getPrimaryColor(app)))
|
||||
]))]);
|
||||
}
|
||||
|
||||
getScreenMargins(double top) {
|
||||
double side = 42.0;
|
||||
return new EdgeInsets.only(top: top, left: side, right: side);
|
||||
}
|
||||
|
||||
getCompleteButton() {
|
||||
return buildRaisedButton(StringsLocalization.completePurchase(), () => onPurchaseClick());
|
||||
}
|
||||
|
||||
Widget getScanButton(BuildContext context, String title, Color textColor) {
|
||||
return new Container(
|
||||
height: buttonHeight,
|
||||
child: new FlatButton(
|
||||
child: new Text(
|
||||
title,
|
||||
style: new TextStyle(color: textColor)),
|
||||
onPressed: () => startScanner(context, app, helper)),
|
||||
decoration: new BoxDecoration(
|
||||
border: new Border.all(color: Resources.getButtonColor(app), width: 1.0),
|
||||
borderRadius: new BorderRadius.all(new Radius.circular(4.0))));
|
||||
}
|
||||
|
||||
@override String getTitle() {
|
||||
return StringsLocalization.carryingPurchase();
|
||||
}
|
||||
|
||||
@override getHint() {
|
||||
return StringsLocalization.sum();
|
||||
}
|
||||
|
||||
@override getMenuButtons() {
|
||||
return <Widget>[getFaqButton(), getLogoutButton()];
|
||||
}
|
||||
|
||||
@override getTextWidget() {
|
||||
return new TextField(
|
||||
keyboardType: TextInputType.number,
|
||||
decoration: new InputDecoration.collapsed(
|
||||
hintText: getHint(),
|
||||
hintStyle: new TextStyle(color: greyTextColor, fontSize: 16.0)
|
||||
),
|
||||
controller: controller,
|
||||
onSubmitted: (String text) {
|
||||
setState(() {
|
||||
controller.text = _parseSum(text);
|
||||
});
|
||||
},
|
||||
textAlign: TextAlign.center,
|
||||
autofocus: true,
|
||||
);
|
||||
}
|
||||
|
||||
getLoyalty(String url) async {
|
||||
|
||||
if (await platform.invokeMethod('isOnline')) {
|
||||
|
||||
String token = await helper.getToken();
|
||||
|
||||
var headers = {
|
||||
'DM-Authorization': 'dmapptoken $appToken',
|
||||
'Authorization': 'dmtoken ${token}'
|
||||
};
|
||||
|
||||
httpClient.get(url, headers: headers).then((response) {
|
||||
|
||||
print(response.body);
|
||||
|
||||
Map bonuses = JSON.decode(response.body);
|
||||
String type = bonuses['type'];
|
||||
setState(() {
|
||||
if (type == 'amount') {
|
||||
this.loyalty = '${user['discount']}%';
|
||||
} else {
|
||||
List amountToBonus = bonuses['amount_to_bonus'];
|
||||
double loyalityVal = (double.parse(amountToBonus[1]) / amountToBonus[0]) * 100;
|
||||
this.loyalty = '${loyalityVal.toStringAsFixed(0)}%';
|
||||
}
|
||||
});
|
||||
}).catchError((error) {
|
||||
print(error.toString());
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
String _cleanupNumber(String text){
|
||||
String tmp = text
|
||||
.replaceAll(' ', '')
|
||||
.replaceAll('-', '')
|
||||
.replaceAll(',', '.')
|
||||
.replaceAll('..', '.');
|
||||
|
||||
while(tmp.indexOf('..') != -1){
|
||||
tmp = tmp.replaceAll('..', '.');
|
||||
}
|
||||
return tmp;
|
||||
}
|
||||
|
||||
_parseSum(String input) {
|
||||
num sumTotal = 0.0;
|
||||
String text = _cleanupNumber(input);
|
||||
|
||||
try {
|
||||
sumTotal = num.parse(text);
|
||||
} catch(exception) {
|
||||
print(exception);
|
||||
try {
|
||||
int idx = text.indexOf('.');
|
||||
String integerPart = text.substring(0, idx);
|
||||
String fractionalPart = text.substring(idx + 1, text.length);
|
||||
if(fractionalPart.length > 2) {
|
||||
fractionalPart = fractionalPart.substring(0, 2);
|
||||
}
|
||||
return '${integerPart}.${fractionalPart}';
|
||||
} catch(exception){
|
||||
print(exception);
|
||||
}
|
||||
}
|
||||
return sumTotal.toStringAsFixed(2);
|
||||
}
|
||||
|
||||
onPurchaseClick() {
|
||||
String val = _parseSum(controller.text);
|
||||
showDialog(context: context, child: new AlertDialog(
|
||||
title: new Text(StringsLocalization.confirmation()),
|
||||
content: new Text(StringsLocalization.confirmPurchase(val)),
|
||||
actions: <Widget>[
|
||||
new FlatButton(
|
||||
child: new Text(StringsLocalization.no()),
|
||||
onPressed: () {
|
||||
Navigator.of(context).pop();
|
||||
},
|
||||
),
|
||||
new FlatButton(
|
||||
child: new Text(StringsLocalization.yes()),
|
||||
onPressed: () {
|
||||
purchase(val);
|
||||
},
|
||||
)
|
||||
]));
|
||||
}
|
||||
|
||||
purchase(String sumTotal) async {
|
||||
if (await platform.invokeMethod('isOnline')) {
|
||||
if (!purchaseInProgress) {
|
||||
purchaseInProgress = true;
|
||||
|
||||
String token = await helper.getToken();
|
||||
helper.getMerchantID().then((result) {
|
||||
|
||||
String url = user['purchases_url'];
|
||||
|
||||
var body = {
|
||||
'doc_id': result,
|
||||
'curr_iso_code': '643',
|
||||
'commit': 'true',
|
||||
'sum_total': sumTotal
|
||||
};
|
||||
|
||||
var headers = {
|
||||
'DM-Authorization': 'dmapptoken $appToken',
|
||||
'Authorization': 'dmtoken ${token}'
|
||||
};
|
||||
|
||||
httpClient.post(url, body: body, headers: headers).then((response) {
|
||||
|
||||
print(response.body);
|
||||
helper.close().then((_) {
|
||||
Navigator.of(context).pop();
|
||||
pushRoute(context, new PurchaseSuccessScreen(sumTotal, user['first_name'] == null ? '' : user['first_name']));
|
||||
});
|
||||
|
||||
}).catchError((error) {
|
||||
purchaseInProgress = false;
|
||||
print(error.toString());
|
||||
});
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
67
lib/screens/purchase_success.dart
Normal file
67
lib/screens/purchase_success.dart
Normal file
@@ -0,0 +1,67 @@
|
||||
import 'package:flutter/material.dart';
|
||||
|
||||
import 'package:checker/common.dart';
|
||||
import 'package:checker/consts.dart';
|
||||
import 'package:checker/strings.dart';
|
||||
import 'package:checker/base_state.dart';
|
||||
|
||||
/// Экран проведения покупки.
|
||||
class PurchaseSuccessScreen extends StatefulWidget {
|
||||
|
||||
PurchaseSuccessScreen(this.val, this.name);
|
||||
final String val;
|
||||
final String name;
|
||||
|
||||
@override State createState() => new PurchaseSuccessScreenState(val, name);
|
||||
}
|
||||
|
||||
class PurchaseSuccessScreenState<T> extends BaseState<PurchaseSuccessScreen> {
|
||||
|
||||
PurchaseSuccessScreenState(this.sum, this.username);
|
||||
|
||||
String sum;
|
||||
String username;
|
||||
|
||||
@override getMenuButtons() {
|
||||
return <Widget>[getFaqButton(), getLogoutButton()];
|
||||
}
|
||||
|
||||
@override String getTitle() {
|
||||
return StringsLocalization.carryingPurchase();
|
||||
}
|
||||
|
||||
@override String getHint() {
|
||||
return null;
|
||||
}
|
||||
|
||||
@override Widget getScreenContent() {
|
||||
return new Column(children: <Widget>[
|
||||
getValueWithDescription(StringsLocalization.buyer(), username),
|
||||
getSuccessMessage(),
|
||||
new Expanded(child: new Center()),
|
||||
wrapButton(getScreenMargins(74.0), getScanButton())
|
||||
]);
|
||||
}
|
||||
|
||||
getScreenMargins(double bottom) {
|
||||
double side = 42.0;
|
||||
return new EdgeInsets.only(bottom: bottom, left: side, right: side);
|
||||
}
|
||||
|
||||
getScanButton() {
|
||||
String title = StringsLocalization.scan();
|
||||
return buildRaisedButton(title, () => startScanner(context, app, helper));
|
||||
}
|
||||
|
||||
getSuccessMessage() {
|
||||
return new Row(children: <Widget>[new Expanded(child: new Container(margin: new EdgeInsets.only(top: 20.0), height: 64.0,
|
||||
decoration: new BoxDecoration(color: greenBackground),
|
||||
child: new Center(child: new Text(getMessageTitle(), textAlign: TextAlign.center,
|
||||
style: new TextStyle(fontWeight: FontWeight.bold, color: tokenActiveTextColor)))))]);
|
||||
}
|
||||
|
||||
getMessageTitle() {
|
||||
return StringsLocalization.purchaseCompleted(sum);
|
||||
}
|
||||
|
||||
}
|
||||
103
lib/screens/registration.dart
Normal file
103
lib/screens/registration.dart
Normal file
@@ -0,0 +1,103 @@
|
||||
import 'package:checker/db.dart';
|
||||
import 'package:checker/screens/finish_registration.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
import 'dart:convert'; // Пакет для обработки json с ответом от сервера.
|
||||
|
||||
import 'package:checker/common.dart';
|
||||
import 'package:checker/consts.dart';
|
||||
import 'package:checker/network.dart';
|
||||
import 'package:checker/base_state.dart';
|
||||
import 'package:checker/strings.dart';
|
||||
|
||||
/// Экран регистрации магазина и кассы.
|
||||
class RegistrationScreen extends StatefulWidget {
|
||||
@override State createState() => new _RegistrationScreenState();
|
||||
}
|
||||
|
||||
class _RegistrationScreenState extends BaseState<RegistrationScreen> {
|
||||
|
||||
@override String getTitle() {
|
||||
return StringsLocalization.registration();
|
||||
}
|
||||
|
||||
@override getHint() {
|
||||
return StringsLocalization.idStore();
|
||||
}
|
||||
|
||||
/// Список виджетов, автоматически прокручиваемый вверх при открытии клавиатуры.
|
||||
@override Widget getScreenContent() {
|
||||
return new Container(
|
||||
child: new ListView(children: <Widget>[
|
||||
new Column(children: <Widget>[
|
||||
getLogo(),
|
||||
getHintLabel(),
|
||||
getInputField(),
|
||||
getButton()])
|
||||
]));
|
||||
}
|
||||
|
||||
@override getTextWidget() {
|
||||
return new TextField(keyboardType: TextInputType.number,
|
||||
decoration: new InputDecoration.collapsed(hintText: getHint(),
|
||||
hintStyle: new TextStyle(color: greyTextColor, fontSize: 16.0)),
|
||||
onChanged: (text) => handleUserInput(text));
|
||||
}
|
||||
|
||||
/// Возвращает кнопку регистрации.
|
||||
getButton() {
|
||||
return new Container(margin: new EdgeInsets.only(top: 36.0), child:
|
||||
buildRaisedButton(StringsLocalization.signUp(), getOnPressed()));
|
||||
}
|
||||
|
||||
// Возвращает обработчик нажатий на кнопку регистрации.
|
||||
getOnPressed() {
|
||||
return _isValidMerchantID() && !loading ? () => _registerShop() : null;
|
||||
}
|
||||
|
||||
/// Токен кассы - это DIN код. DIN код - это специальный код динекта, максимальная его длина - 25 символов.
|
||||
_isValidMerchantID() {
|
||||
print("${textFieldValue.length}");
|
||||
return textFieldValue.length > 0 && textFieldValue.length < 25;
|
||||
}
|
||||
|
||||
/// Показать progressBar, запросить токен.
|
||||
_registerShop() {
|
||||
setState(() {
|
||||
loading = true;
|
||||
_register();
|
||||
});
|
||||
}
|
||||
|
||||
/// Получение от платформы id установки, формирование запроса на получение токена, сохранение токена.
|
||||
_register() async {
|
||||
if (await platform.invokeMethod('isOnline')) {
|
||||
|
||||
String posID = await helper.getPosID();
|
||||
|
||||
createToken(textFieldValue, posID).then((response) {
|
||||
|
||||
setState(() {
|
||||
error = null;
|
||||
loading = false;
|
||||
});
|
||||
|
||||
print(response.body);
|
||||
print(response.statusCode.toString());
|
||||
Map parsedMap = JSON.decode(response.body);
|
||||
|
||||
if (response.statusCode == 201) {
|
||||
helper.insert(textFieldValue, posID, parsedMap['token']).then((_) {
|
||||
helper.close();
|
||||
pushRoute(context, new FinishRegistrationScreen());
|
||||
});
|
||||
} else {
|
||||
setState(() {
|
||||
error = parsedMap['errors'][0];
|
||||
});
|
||||
}
|
||||
}).catchError((error) {
|
||||
print(error.toString());
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
160
lib/screens/splash.dart
Normal file
160
lib/screens/splash.dart
Normal file
@@ -0,0 +1,160 @@
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:http/http.dart';
|
||||
import 'dart:convert';
|
||||
import 'dart:async';
|
||||
import 'package:checker/common.dart';
|
||||
import 'package:checker/network.dart';
|
||||
import 'package:checker/consts.dart';
|
||||
import 'package:checker/resources.dart';
|
||||
import 'package:checker/db.dart';
|
||||
import 'package:checker/base_state.dart';
|
||||
import 'package:checker/screens/registration.dart';
|
||||
import 'package:checker/screens/finish_registration.dart';
|
||||
|
||||
class SplashScreen extends StatefulWidget {
|
||||
@override State createState() => new _SplashScreenState();
|
||||
}
|
||||
|
||||
class _SplashScreenState extends BaseState<SplashScreen> {
|
||||
|
||||
@override
|
||||
Widget getScreenContent() {
|
||||
return app == null
|
||||
? getBackground()
|
||||
: new Stack(
|
||||
children: <Widget>[
|
||||
getBackground(),
|
||||
getLogo(),
|
||||
new Align(
|
||||
alignment: FractionalOffset.bottomRight,
|
||||
child: new Container(
|
||||
margin: new EdgeInsets.only(
|
||||
right: 11.0,
|
||||
bottom: 5.0),
|
||||
child: new Image.asset(powered_by_dinect_splash_png,
|
||||
height: 16.0,
|
||||
width: 122.0)))]);
|
||||
}
|
||||
|
||||
@override Widget getMainWidget() {
|
||||
return getScreenContent();
|
||||
}
|
||||
|
||||
@override
|
||||
String getTitle() {
|
||||
return null;
|
||||
}
|
||||
|
||||
@override void onStart() {
|
||||
new Future.delayed(const Duration(milliseconds: 1000), () {
|
||||
showNextScreen(context);
|
||||
});
|
||||
}
|
||||
|
||||
/// Возвращает столбец с логотипом приложения и текстом под ним.
|
||||
/// Столбец занимает не все доступное пространство, а необходимый минимум в центре экрана.
|
||||
getLogo() {
|
||||
return new Center(
|
||||
child: new Column(
|
||||
mainAxisSize: MainAxisSize.min,
|
||||
children: <Widget>[
|
||||
new Image.asset(
|
||||
Resources.getLogo(app),
|
||||
height: 112.0,
|
||||
width: 252.0),
|
||||
new Image.asset(
|
||||
splash_text_png,
|
||||
height: 40.0,
|
||||
width: 240.0)]));
|
||||
}
|
||||
|
||||
/// Запуск следующего экрана приложения.
|
||||
showNextScreen(BuildContext context) async {
|
||||
|
||||
String token = await helper.getToken();
|
||||
|
||||
// В случае, если в приложении отсутствует токен,
|
||||
// необходимо запустить регистрацию кассы.
|
||||
if (token == null) {
|
||||
await helper.close();
|
||||
pushRoute(context, new RegistrationScreen());
|
||||
} else {
|
||||
if (await platform.invokeMethod('isOnline')) {
|
||||
checkTokenStatus(token).then((statusResponse) {
|
||||
handleStatusResponse(context, statusResponse, helper);
|
||||
}).catchError((error) {
|
||||
helper.close().then((_) {
|
||||
print(error.toString());
|
||||
return false;
|
||||
});
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Обработка ответа.
|
||||
/// В случае, если токен был удален может прийти active: false, либо 404.
|
||||
/// Если токен не активен, попробовать создать его еще раз.
|
||||
handleStatusResponse(BuildContext context, var statusResponse, SqliteHelper helper) async {
|
||||
int code = statusResponse.statusCode;
|
||||
print('resp: ${code}');
|
||||
|
||||
if (code == 404) {
|
||||
helper.clear().then((result) {
|
||||
helper.close().then((_) {
|
||||
pushRoute(context, new RegistrationScreen());
|
||||
});
|
||||
});
|
||||
} else {
|
||||
|
||||
Map status = JSON.decode(statusResponse.body);
|
||||
bool active = status['active'] == null ? false : status['active'];
|
||||
|
||||
if (active) {
|
||||
startScanner(context, app, helper);
|
||||
} else {
|
||||
if (await platform.invokeMethod('isOnline')) {
|
||||
_createToken(context, helper);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Отправляется запрос на создание токена.
|
||||
///
|
||||
/// Если вернулся код 409, значит такой токен уже существует и активирован.
|
||||
/// Нужно направить пользователя на экран подтверждения активации.
|
||||
///
|
||||
/// Если вернулся код 200, значит токен был ранее удален и только что снова создался.
|
||||
/// Нужно удалить его и направить пользователя на экран регистрации.
|
||||
_createToken(BuildContext ctx, SqliteHelper helper) async {
|
||||
|
||||
String merchantID = await helper.getMerchantID();
|
||||
String posID = await helper.getPosID();
|
||||
|
||||
createToken(merchantID, posID).then((response) {
|
||||
if (response.statusCode == 409) {
|
||||
helper.close();
|
||||
pushRoute(ctx, new FinishRegistrationScreen());
|
||||
} else if (response.statusCode == 201) {
|
||||
clearToken(response, ctx, helper);
|
||||
}
|
||||
}).catchError((error) => print(error.toString()));
|
||||
}
|
||||
|
||||
/// Очищаем бд, делаем запрос на удаление токена.
|
||||
void clearToken(Response response, BuildContext ctx, SqliteHelper helper) {
|
||||
|
||||
helper.clear().then((_) {
|
||||
Map parsedMap = JSON.decode(response.body);
|
||||
deleteToken(parsedMap['token']).then((_) {
|
||||
helper.close();
|
||||
Navigator.of(ctx).pop(); // Убираем текущий route
|
||||
pushRoute(ctx, new RegistrationScreen()); // Запускаем регистрацию
|
||||
}).catchError((error) {
|
||||
helper.close();
|
||||
print(error.toString());
|
||||
});
|
||||
});
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user