Продолжаю разбираться с локализацией

This commit is contained in:
Ivan Murashov
2017-09-05 18:59:19 +03:00
parent 671b3afe6d
commit 3bd9eb4d91
12 changed files with 279 additions and 205 deletions

View File

@@ -38,6 +38,22 @@ android {
} }
} }
productFlavors {
en {
buildConfigField "String", "locale", "\"en\""
}
ru {
buildConfigField "String", "locale", "\"ru\""
}
ua {
buildConfigField "String", "locale", "\"ua\""
}
}
sourceSets.main { sourceSets.main {
jniLibs.srcDir 'jniLibs' jniLibs.srcDir 'jniLibs'
} }

View File

@@ -6,9 +6,11 @@ import android.util.Log;
import android.widget.Toast; import android.widget.Toast;
import android.content.Context; import android.content.Context;
import android.content.SharedPreferences; import android.content.SharedPreferences;
import android.content.res.Configuration;
import android.content.res.Resources;
import com.dinect.checker.zbar.CameraActivity; import com.dinect.checker.zbar.CameraActivity;
import com.dinect.checker.zxing.ScannerActivity; import com.dinect.checker.zxing.ScannerActivity;
import java.util.Locale;
import io.flutter.app.FlutterActivity; import io.flutter.app.FlutterActivity;
import io.flutter.plugins.GeneratedPluginRegistrant; import io.flutter.plugins.GeneratedPluginRegistrant;
@@ -47,7 +49,7 @@ public class MainActivity extends FlutterActivity {
protected void onCreate(Bundle savedInstanceState) { protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState); super.onCreate(savedInstanceState);
GeneratedPluginRegistrant.registerWith(this); GeneratedPluginRegistrant.registerWith(this);
loadLocale(this);
mPreferences = getPreferences(Context.MODE_PRIVATE); mPreferences = getPreferences(Context.MODE_PRIVATE);
Log.d(TAG, "application prefs:"); Log.d(TAG, "application prefs:");
@@ -144,6 +146,29 @@ public class MainActivity extends FlutterActivity {
} }
} }
public static void loadLocale(Context context) {
Resources res = context.getResources();
Configuration configuration = new Configuration(res.getConfiguration());
switch (BuildConfig.locale) {
case "en":
configuration.locale = new Locale("en");
Locale.setDefault(configuration.locale);
res.updateConfiguration(configuration, res.getDisplayMetrics());
break;
case "ru":
configuration.locale = new Locale("ru");
Locale.setDefault(configuration.locale);
res.updateConfiguration(configuration, res.getDisplayMetrics());
break;
case "ua":
configuration.locale = new Locale("ua");
Locale.setDefault(configuration.locale);
res.updateConfiguration(configuration, res.getDisplayMetrics());
break;
}
}
public void handleItemClick() { public void handleItemClick() {
} }

View File

@@ -1,148 +1,154 @@
import 'dart:async';
import 'package:flutter/material.dart'; import 'package:flutter/material.dart';
import 'package:flutter/services.dart'; import 'package:flutter/services.dart';
import 'common.dart'; import 'common.dart';
import 'consts.dart'; import 'consts.dart';
import 'strings.dart';
abstract class BaseState<T> extends State<StatefulWidget> { abstract class BaseState<T extends StatefulWidget> extends State<T> {
/// Ожидание ответа от сервера.
bool loading = false; bool loading = false;
/// Текст ошибки, подставляется в подсказку, либо появляется над текстовым полем.
String error; String error;
/// Введенное пользователем значение.
String textFieldValue = ''; String textFieldValue = '';
TextEditingController controller = new TextEditingController(); Strings s;
@override Widget build(BuildContext context) { @override Widget build(BuildContext ctx) {
return new Scaffold(appBar: getAppBar(context), body: getBody(context)); return new Scaffold(appBar: getAppBar(ctx),
body: new Stack(children: <Widget>[
getScreenContent(ctx),
new Center(child: loading ? new CircularProgressIndicator() : null)
]));
} }
AppBar getAppBar(BuildContext context) { /// Возвращает контейнер с всеми виджетами экрана.
return new AppBar(title: new Text(getTitle(), style: new TextStyle(fontSize: 18.0)), Widget getScreenContent(BuildContext ctx);
backgroundColor: primaryColor, actions: getMenuButtons(context));
/// Возвращает заголовок для AppBar
String getTitle(BuildContext ctx);
AppBar getAppBar(BuildContext ctx) {
return new AppBar(title: new Text(getTitle(ctx), style: new TextStyle(fontSize: 18.0)),
backgroundColor: primaryColor, actions: getMenuButtons(ctx));
} }
getMenuButtons(BuildContext context) { List<Widget> getMenuButtons(BuildContext context) {
return <Widget>[getFaqButton()]; return <Widget>[getFaqButton()];
} }
getFaqButton() { Widget getFaqButton() {
return new IconButton(icon: new Icon(Icons.help_outline), onPressed: () => faq(context, false)); return new IconButton(icon: new Icon(Icons.help_outline), onPressed: () => faq(context, false));
} }
getLogoutButton() { Widget getLogoutButton() {
return new IconButton(icon: new Image.asset(logout_png, height: iconHeight, width: iconHeight), onPressed: () => logout(context)); return new IconButton(icon: new Image.asset(logout_png, height: iconHeight, width: iconHeight), onPressed: () => logout(context));
} }
Widget getBody(BuildContext context) { /// Возврвщает контейнер, внутри которого Text с подсказкой.
return new Stack(children: <Widget>[getScreenContent(), getProgressIndicator()]); Widget getHintLabel(BuildContext ctx) {
}
Widget getScreenContent();
String getTitle();
String getHint();
/// Метод возвращает контейнер с отступами, который содержит картинку с логотипом.
getLogo() {
double containerHeight = 92.0;
double imageWidth = 156.0;
return new Container(height: containerHeight, child: new Image.asset(logo_png, width: imageWidth));
}
getHintLabel() {
double horizontalMargin = 8.0; double horizontalMargin = 8.0;
return new Container(margin: new EdgeInsets.only(top: horizontalMargin, bottom: horizontalMargin, left: verticalMargin, right: verticalMargin), return new Container(margin: new EdgeInsets.only(top: horizontalMargin, bottom: horizontalMargin, left: verticalMargin, right: verticalMargin),
child: new Row(crossAxisAlignment: CrossAxisAlignment.start, child: new Row(crossAxisAlignment: CrossAxisAlignment.start,
children: <Widget>[new Text(getHintText(), textAlign: TextAlign.left, children: <Widget>[new Text(getHintString(ctx), textAlign: TextAlign.left,
style: new TextStyle(fontWeight: FontWeight.w300, color: error == null ? greyTextColor : primaryColor, fontSize: 14.0))])); style: new TextStyle(fontWeight: FontWeight.w300, color: error == null ? greyTextColor : primaryColor, fontSize: 14.0))]));
} }
getHintText() { /// Возвращает подсказку, либо ошибку, если введенные в поле ввода данные неверны.
String getHintString(BuildContext ctx) {
if (textFieldValue.length == 0 && error == null) { if (textFieldValue.length == 0 && error == null) {
return ' '; return ' ';
} else if (error != null) { } else if (error != null) {
return error; return error;
} else { } else {
return getHint(); return getHint(ctx);
} }
} }
/// Возвращает текст подсказки для поля ввода.
/// Должен быть переопределен на экранах, на которых есть поле ввода.
String getHint(BuildContext ctx) {
return null;
}
/// Смена состояния экрана при изменении текста в поле ввода. /// Смена состояния экрана при изменении текста в поле ввода.
handleUserInput(String text) { void handleUserInput(String text) {
setState(() { setState(() {
textFieldValue = text; textFieldValue = text;
}); });
} }
/// Метод возвращает контейнер с установленными отступами, в котором размещен TextField обернутый в BoxDecoration. /// Метод возвращает контейнер с полем ввода внутри.
getDecoratedTextWidget() { Widget getInputField(BuildContext ctx) {
return new Container(margin: new EdgeInsets.only(left: verticalMargin, right: verticalMargin), return new Container(margin: new EdgeInsets.only(left: verticalMargin, right: verticalMargin),
padding: getPaddingForTextWidget(), padding: getInputFieldContainerPadding(),
decoration: getDecorationForTextWidget(), decoration: getInputFieldContainerDecoration(),
child: getTextWidget()); child: new TextField(keyboardType: TextInputType.number,
decoration: new InputDecoration.collapsed(hintText: getHint(ctx),
hintStyle: new TextStyle(color: greyTextColor, fontSize: 16.0)),
onChanged: (text) => handleUserInput(text)));
} }
getPaddingForTextWidget() { /// Возвращат паддинги для поля ввода.
EdgeInsets getInputFieldContainerPadding() {
const double verticalPadding = 12.0; const double verticalPadding = 12.0;
const double horizontalPadding = 16.0; const double horizontalPadding = 16.0;
return new EdgeInsets.only(top: verticalPadding, return new EdgeInsets.only(top: verticalPadding,
bottom: verticalPadding, bottom: verticalPadding,
left: horizontalPadding, left: horizontalPadding,
right: horizontalPadding); right: horizontalPadding);
} }
/// Метод возвращает BoxDecoration для _getDecoratedInputField /// Метод возвращает BoxDecoration для _getDecoratedInputField
getDecorationForTextWidget() { BoxDecoration getInputFieldContainerDecoration() {
return new BoxDecoration(color: getTextFilledBackground(), return new BoxDecoration(color: inputFieldBackground,
border: new Border.all(color: textBorderColor, width: 1.0), border: new Border.all(color: textBorderColor, width: 1.0),
borderRadius: new BorderRadius.all(new Radius.circular(4.0))); borderRadius: new BorderRadius.all(new Radius.circular(4.0)));
} }
Color getTextFilledBackground() { /// Возвращает выпуклую залитую фирменным цветом кнопку
return const Color(0xffefefef); Widget buildRaisedButton(BuildContext context, String text, VoidCallback onPressed) {
return new RaisedButton(child: new Text(text,
style: new TextStyle(color: Colors.white)),
onPressed: onPressed,
color: primaryColor);
} }
Widget getTextWidget() { /// Метод возвращает контейнер с отступами, который содержит картинку с логотипом.
return new TextField(keyboardType: TextInputType.number, Widget getLogo() {
decoration: new InputDecoration.collapsed(hintText: getHint(), double containerHeight = 92.0;
hintStyle: new TextStyle(color: greyTextColor, fontSize: 16.0)), double imageWidth = 156.0;
onChanged: (text) => handleUserInput(text)); return new Container(height: containerHeight, child: new Image.asset(logo_png, width: imageWidth));
} }
Widget getProgressIndicator() { /// Возвращает текстовое поле, с однострочным пояснением над ним.
return new Center(child: loading ? new CircularProgressIndicator() : null); Widget getValueWithDescription(String title, String value) {
}
Widget getValueWithTitle(String title, String value) {
return new Container(padding: new EdgeInsets.only(left: verticalMargin, right: verticalMargin, top: 18.0), return new Container(padding: new EdgeInsets.only(left: verticalMargin, right: verticalMargin, top: 18.0),
child: new Column(children: <Widget>[ child: new Column(children: <Widget>[
new Row(crossAxisAlignment: CrossAxisAlignment.start, children: <Widget>[new Text(title, textAlign: TextAlign.left, style: new TextStyle(color: greyTextColor, fontSize: 14.0))]), new Row(crossAxisAlignment: CrossAxisAlignment.start, children: getDescriptionWidget(title)),
new Row(crossAxisAlignment: CrossAxisAlignment.start, children: <Widget>[new Expanded(child: new Text(value, textAlign: TextAlign.left, style: new TextStyle(color: Colors.black, fontSize: 20.0)))]) new Row(crossAxisAlignment: CrossAxisAlignment.start, children: getValueWidget(value))
])); ]));
} }
/// Возвращает список, единственный элемент которого - Text с заголовком для текстового поля.
List<Widget> getDescriptionWidget(String title) {
return <Widget>[new Text(title, textAlign: TextAlign.left, style: new TextStyle(color: greyTextColor, fontSize: 14.0))]
}
/// Возвращает список, единственный элемент которого - Text с информацией (размер скидки, сумма проведенной покупки).
List<Widget> getValueWidget(String value) {
return <Widget>[new Expanded(child: new Text(value, textAlign: TextAlign.left, style: new TextStyle(color: Colors.black, fontSize: 20.0)))];
}
/// Возвращает кнопку, обернутую набором специфичных контейнеров.
Widget buildButton(EdgeInsets margin, Widget widget) { Widget buildButton(EdgeInsets margin, Widget widget) {
return new Container(margin: margin, height: buttonHeight, child: new Row(children: <Widget>[new Expanded(child: widget)])); return new Container(margin: margin, height: buttonHeight, child: new Row(children: <Widget>[new Expanded(child: widget)]));
} }
Widget buildRaisedButton(BuildContext context, String text, VoidCallback onPressed) {
return new RaisedButton(child: new Text(text,
style: new TextStyle(color: Colors.white)),
onPressed: onPressed,
color: primaryColor);
}
Widget buildFlatButton(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)),
decoration: getDecorationForScanButton());
}
getDecorationForScanButton() {
return new BoxDecoration(
border: new Border.all(color: primaryColor, width: 1.0),
borderRadius: new BorderRadius.all(new Radius.circular(4.0)));
}
} }

View File

@@ -3,8 +3,8 @@ import 'package:flutter/material.dart';
import 'consts.dart'; import 'consts.dart';
import 'network.dart'; import 'network.dart';
import 'package:checker/registration/registration.dart'; import 'package:checker/registration.dart';
import 'package:checker/purchase/purchase.dart'; import 'package:checker/purchase.dart';
import 'faq.dart'; import 'faq.dart';
import 'strings.dart'; import 'strings.dart';

View File

@@ -26,7 +26,7 @@ const Color tokenActivateTextColor = const Color(0xff4e3a19);
const Color greenBackground = const Color(0xff8ae28a); const Color greenBackground = const Color(0xff8ae28a);
const Color faqGrey = const Color(0xff5b5b5b); const Color faqGrey = const Color(0xff5b5b5b);
const Color faqTitlesColor = const Color(0xff404040); const Color faqTitlesColor = const Color(0xff404040);
const Color inputFieldBackground = const Color(0xffefefef);
// Dimens // Dimens
const double verticalMargin = 28.0; const double verticalMargin = 28.0;
const double buttonHeight = 48.0; const double buttonHeight = 48.0;

View File

@@ -35,7 +35,7 @@ class _RegistrationScreenState extends BaseState<FinishRegistrationScreen> {
return new Column(children: <Widget>[ return new Column(children: <Widget>[
getLogo(), getLogo(),
getHintLabel(), getHintLabel(),
getDecoratedTextWidget(), getInputField(),
getMessage(), getMessage(),
buildRaisedButton(context, _tokenActive buildRaisedButton(context, _tokenActive
? Strings.of(context).completeRegistration() ? Strings.of(context).completeRegistration()

View File

@@ -3,29 +3,22 @@ import 'splash.dart';
import 'consts.dart'; import 'consts.dart';
import 'strings.dart'; import 'strings.dart';
import 'dart:async'; import 'dart:async';
import 'i18n/messages_all.dart';
import 'package:intl/intl.dart';
Future<LocaleQueryData> _onLocaleChanged(Locale locale) async {
final String localeString = locale.toString();
await initializeMessages(localeString);
Intl.defaultLocale = localeString;
return Strings.instance;
}
/// Точка входа в приложение. /// Точка входа в приложение.
void main() { void main() {
runApp(new Checker()); runApp(new Checker());
} }
class Checker extends StatelessWidget { class Checker extends StatefulWidget {
@override CheckerState createState() => new CheckerState();
}
class CheckerState extends State<Checker> {
@override Widget build(BuildContext context) { @override Widget build(BuildContext context) {
return new MaterialApp(title: appName, return new MaterialApp(
home: new SplashScreen(), title: appName,
onLocaleChanged: _onLocaleChanged, home: new SplashScreen()
theme: new ThemeData( );
primaryColor: primaryColor,
accentColor: primaryColor
));
} }
} }

View File

@@ -8,7 +8,7 @@ import 'package:checker/common.dart';
import 'package:checker/consts.dart'; import 'package:checker/consts.dart';
import 'package:checker/network.dart'; import 'package:checker/network.dart';
import 'package:checker/base_state.dart'; import 'package:checker/base_state.dart';
import 'purchase_success.dart'; import 'package:checker/purchase_success.dart';
/// Экран проведения покупки. /// Экран проведения покупки.
class PurchaseScreen extends StatefulWidget { class PurchaseScreen extends StatefulWidget {
@@ -23,6 +23,11 @@ class PurchaseScreen extends StatefulWidget {
class PurchaseScreenState<T> extends BaseState<PurchaseScreen> { class PurchaseScreenState<T> extends BaseState<PurchaseScreen> {
/// Объект, помогающий вручную изменять введенный пользователем текст.
/// Используется для форматирования введенных пользователем данных
/// (удаляет запрещенные символы до их отображаения).
TextEditingController controller = new TextEditingController();
PurchaseScreenState(String userString, String card) { PurchaseScreenState(String userString, String card) {
this.user = JSON.decode(userString); this.user = JSON.decode(userString);
this.card = card; this.card = card;
@@ -37,13 +42,13 @@ class PurchaseScreenState<T> extends BaseState<PurchaseScreen> {
@override Widget getScreenContent() { @override Widget getScreenContent() {
return new Column( return new Column(
children: <Widget>[new Expanded(child: new ListView(children: <Widget>[ children: <Widget>[new Expanded(child: new ListView(children: <Widget>[
getValueWithTitle(Strings.of(context).userName(), user['first_name'] == null ? '' : user['first_name']), getValueWithDescription(Strings.of(context).userName(), user['first_name'] == null ? '' : user['first_name']),
getValueWithTitle(Strings.of(context).card(), card), getValueWithDescription(Strings.of(context).card(), card),
getValueWithTitle(Strings.of(context).reward(), loyality), getValueWithDescription(Strings.of(context).reward(), loyality),
getHintLabel(), getHintLabel(),
getDecoratedTextWidget(), getInputField(),
buildButton(getScreenMargins(36.0), getCompleteButton()), buildButton(getScreenMargins(36.0), getCompleteButton()),
buildButton(getScreenMargins(24.0), getScanButton()) buildButton(getScreenMargins(24.0), getScanButton(context, Strings.of(context).scan(), primaryColor))
]))]); ]))]);
} }
@@ -57,8 +62,17 @@ class PurchaseScreenState<T> extends BaseState<PurchaseScreen> {
return buildRaisedButton(context, title, () => onPurchaseClick(context)); return buildRaisedButton(context, title, () => onPurchaseClick(context));
} }
getScanButton() { Widget getScanButton(BuildContext context, String title, Color textColor) {
return buildFlatButton(context, Strings.of(context).scan(), primaryColor); return new Container(height: buttonHeight, child: new FlatButton(child: new Text(title,
style: new TextStyle(color: textColor)),
onPressed: () => startScanner(context)),
decoration: getDecorationForScanButton());
}
getDecorationForScanButton() {
return new BoxDecoration(
border: new Border.all(color: primaryColor, width: 1.0),
borderRadius: new BorderRadius.all(new Radius.circular(4.0)));
} }
@override String getTitle() { @override String getTitle() {
@@ -183,7 +197,7 @@ class PurchaseScreenState<T> extends BaseState<PurchaseScreen> {
} }
getContentMessage(String val) { getContentMessage(String val) {
return Strings.of(context).confirmPurchase(val); return Strings.of(context).confirmPurchase();
} }
purchase(String sumTotal) async { purchase(String sumTotal) async {

View File

@@ -36,7 +36,7 @@ class PurchaseSuccessScreenState<T> extends BaseState<PurchaseSuccessScreen> {
@override Widget getScreenContent() { @override Widget getScreenContent() {
return new Column(children: <Widget>[ return new Column(children: <Widget>[
getValueWithTitle(Strings.of(context).buyer(), username), getValueWithDescription(Strings.of(context).buyer(), username),
getSuccessMessage(), getSuccessMessage(),
new Expanded(child: new Center()), new Expanded(child: new Center()),
buildButton(getScreenMargins(74.0), getScanButton()) buildButton(getScreenMargins(74.0), getScanButton())

View File

@@ -1,3 +1,4 @@
import 'package:checker/finish_registration.dart';
import 'package:flutter/material.dart'; import 'package:flutter/material.dart';
import 'dart:convert'; // Пакет для обработки json с ответом от сервера. import 'dart:convert'; // Пакет для обработки json с ответом от сервера.
@@ -5,7 +6,6 @@ import 'package:checker/common.dart';
import 'package:checker/network.dart'; import 'package:checker/network.dart';
import 'package:checker/base_state.dart'; import 'package:checker/base_state.dart';
import 'package:checker/strings.dart'; import 'package:checker/strings.dart';
import 'activate_token.dart';
/// Экран регистрации магазина и кассы. /// Экран регистрации магазина и кассы.
class RegistrationScreen extends StatefulWidget { class RegistrationScreen extends StatefulWidget {
@@ -14,35 +14,36 @@ class RegistrationScreen extends StatefulWidget {
class _RegistrationScreenState extends BaseState<RegistrationScreen> { class _RegistrationScreenState extends BaseState<RegistrationScreen> {
@override String getTitle() { @override String getTitle(BuildContext ctx) {
return Strings.of(context).registration(); return "registration";
} }
@override getHint() { @override getHint(BuildContext ctx) {
return Strings.of(context).idStore(); return "idStore()";
} }
// Список виджетов, автоматически прокручиваемый вверх при открытии клавиатуры. // Список виджетов, автоматически прокручиваемый вверх при открытии клавиатуры.
@override Widget getScreenContent() { @override Widget getScreenContent(BuildContext ctx) {
print(new Strings().registration());
return new Container( return new Container(
child: new ListView(children: <Widget>[ child: new ListView(children: <Widget>[
new Column(children: <Widget>[ new Column(children: <Widget>[
getLogo(), getLogo(),
getHintLabel(), getHintLabel(ctx),
getDecoratedTextWidget(), getInputField(ctx),
getButton()]) getButton(ctx)])
])); ]));
} }
// Возвращает кнопку регистрации. /// Возвращает кнопку регистрации.
getButton() { getButton(BuildContext ctx) {
return new Container(margin: new EdgeInsets.only(top: 36.0), child: return new Container(margin: new EdgeInsets.only(top: 36.0), child:
buildRaisedButton(context, Strings.of(context).signUp(), getOnPressed())); buildRaisedButton(ctx, "signUp()", getOnPressed(ctx)));
} }
// Возвращает обработчик нажатий на кнопку регистрации. // Возвращает обработчик нажатий на кнопку регистрации.
getOnPressed() { getOnPressed(BuildContext ctx) {
return _isValidMerchantID() && !loading ? () => _registerShop(context) : null; return _isValidMerchantID() && !loading ? () => _registerShop(ctx) : null;
} }
/// Токен кассы - это DIN код. DIN код - это специальный код динекта, максимальная его длина - 25 символов. /// Токен кассы - это DIN код. DIN код - это специальный код динекта, максимальная его длина - 25 символов.
@@ -52,15 +53,15 @@ class _RegistrationScreenState extends BaseState<RegistrationScreen> {
} }
/// Показать progressBar, запросить токен. /// Показать progressBar, запросить токен.
_registerShop(BuildContext context) { _registerShop(BuildContext ctx) {
setState(() { setState(() {
loading = true; loading = true;
_register(context); _register(ctx);
}); });
} }
/// Получение от платформы id установки, формирование запроса на получение токена, сохранение токена. /// Получение от платформы id установки, формирование запроса на получение токена, сохранение токена.
_register(BuildContext context) async { _register(BuildContext ctx) async {
if (await platform.invokeMethod('isOnline')) { if (await platform.invokeMethod('isOnline')) {
createToken(textFieldValue, await platform.invokeMethod('getPosID')).then((response) { createToken(textFieldValue, await platform.invokeMethod('getPosID')).then((response) {
@@ -75,7 +76,7 @@ class _RegistrationScreenState extends BaseState<RegistrationScreen> {
token = parsedMap['token']; token = parsedMap['token'];
platform.invokeMethod('saveToken', {'token' : token}); platform.invokeMethod('saveToken', {'token' : token});
platform.invokeMethod('saveMerchantID', {'merchantID' : textFieldValue}); platform.invokeMethod('saveMerchantID', {'merchantID' : textFieldValue});
pushRoute(context, new FinishRegistrationScreen()); pushRoute(ctx, new FinishRegistrationScreen());
} else { } else {
setState(() { setState(() {
error = parsedMap['errors'][0]; error = parsedMap['errors'][0];

View File

@@ -1,49 +1,54 @@
import 'package:flutter/material.dart'; import 'package:flutter/material.dart';
import 'dart:async'; import 'dart:async';
import 'dart:convert'; import 'dart:convert';
import 'common.dart'; import 'common.dart';
import 'network.dart'; import 'network.dart';
import 'consts.dart'; import 'consts.dart';
import 'package:checker/registration/registration.dart'; import 'registration.dart';
import 'package:checker/registration/activate_token.dart'; import 'finish_registration.dart';
import 'strings.dart';
class SplashScreen extends StatelessWidget { class SplashScreen extends StatelessWidget {
@override @override
Widget build(BuildContext context) { Widget build(BuildContext context) {
// Появляется splash screen, проверяется токен. // Появляется splash screen, проверяется токен.
new Future.delayed(const Duration(milliseconds: 500), () {
Strings s = new Strings();
s.load("ru").then((_) {
showNextScreen(context); showNextScreen(context);
}); });
// new Future.delayed(const Duration(milliseconds: 500), () {
// });
return new Stack(children: <Widget>[getSplashBackground(), getLogo(), return new Stack(children: <Widget>[getSplashBackground(), getLogo(),
new Align(alignment: FractionalOffset.bottomRight, child: new Align(alignment: FractionalOffset.bottomRight, child:
new Container(margin: new EdgeInsets.only(right: 11.0, bottom: 5.0), 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)))]); new Image.asset(powered_by_dinect_splash_png, height: 16.0, width: 122.0)))]);
} }
// Возвращает столбец с логотипом приложения и текстом под ним. /// Возвращает столбец с логотипом приложения и текстом под ним.
// Столбец занимает не все доступное пространство, а необходимый минимум в центре экрана. /// Столбец занимает не все доступное пространство, а необходимый минимум в центре экрана.
getLogo() { getLogo() {
return new Center(child: new Column(mainAxisSize: MainAxisSize.min, return new Center(child: new Column(mainAxisSize: MainAxisSize.min,
children: <Widget>[new Image.asset(logo_png, height: 112.0, width: 252.0), children: <Widget>[new Image.asset(logo_png, height: 112.0, width: 252.0),
new Image.asset(splash_text_png, height: 40.0, width: 240.0)])); new Image.asset(splash_text_png, height: 40.0, width: 240.0)]));
} }
// Возвращает контейнер, который содержит decoration с фоновым изображением. /// Возвращает контейнер, который содержит decoration с фоновым изображением.
getSplashBackground() { getSplashBackground() {
return new Container(decoration: return new Container(decoration:
new BoxDecoration(image: new BoxDecoration(image:
new DecorationImage(image: new ExactAssetImage(splash_png), fit: BoxFit.cover))); new DecorationImage(image: new ExactAssetImage(splash_png), fit: BoxFit.cover)));
} }
// Запуск следующего экрана приложения. /// Запуск следующего экрана приложения.
showNextScreen(BuildContext context) async { showNextScreen(BuildContext context) async {
token = await platform.invokeMethod('getToken'); token = await platform.invokeMethod('getToken');
print('token: $token');
// В случае, если в приложении отсутствует токен, // В случае, если в приложении отсутствует токен,
// необходимо запустить регистрацию кассы. // необходимо запустить регистрацию кассы.
@@ -61,9 +66,9 @@ class SplashScreen extends StatelessWidget {
} }
} }
// Обработка ответа. /// Обработка ответа.
// В случае, если токен был удален может прийти active: false, либо 404. /// В случае, если токен был удален может прийти active: false, либо 404.
// Если токен не активен, попробовать создать его еще раз. В случае успешного создания токена удалить его и перейти на экран регистрации /// Если токен не активен, попробовать создать его еще раз.
handleStatusResponse(BuildContext context, var statusResponse) async { handleStatusResponse(BuildContext context, var statusResponse) async {
int code = statusResponse.statusCode; int code = statusResponse.statusCode;
print('resp: ${code}'); print('resp: ${code}');
@@ -75,32 +80,44 @@ class SplashScreen extends StatelessWidget {
}); });
} else { } else {
Map statusResponseMap = JSON.decode(statusResponse.body); Map status = JSON.decode(statusResponse.body);
bool active = statusResponseMap['active'] == null ? false : statusResponseMap['active']; bool active = status['active'] == null ? false : status['active'];
if (active) { if (active) {
startScanner(context); startScanner(context);
} else { } else {
if (await platform.invokeMethod('isOnline')) { if (await platform.invokeMethod('isOnline')) {
createToken(await platform.invokeMethod('getMerchantID'), await platform.invokeMethod('getPosID')).then((response) { _createToken(context);
if (response.statusCode == 409) {
pushRoute(context, new FinishRegistrationScreen());
} else if (response.statusCode == 201) {
platform.invokeMethod('removeKeys').then((result) {
Map parsedMap = JSON.decode(result);
String t = parsedMap['token'];
deleteToken(t).then((response) {
print(response.body);
Navigator.of(context).pop(); // Убираем текущий route
pushRoute(context, new RegistrationScreen()); // Запускаем регистрацию
}).catchError((error) {
print(error.toString());
});
});
}
}).catchError((error) => print(error.toString()));
} }
} }
} }
} }
/// Отправляется запрос на создание токена.
///
/// Если вернулся код 409, значит такой токен уже существует и активирован.
/// Нужно направить пользователя на экран подтверждения активации.
///
/// Если вернулся код 200, значит токен был ранее удален и только что снова создался.
/// Нужно удалить его и направить пользователя на экран регистрации.
_createToken(BuildContext ctx) async {
String merchantID = await platform.invokeMethod('getMerchantID');
String posID = await platform.invokeMethod('getPosID');
createToken(merchantID, posID).then((response) {
if (response.statusCode == 409) {
pushRoute(ctx, new FinishRegistrationScreen());
} else if (response.statusCode == 201) {
platform.invokeMethod('removeKeys').then((result) {
Map parsedMap = JSON.decode(result);
deleteToken(parsedMap['token']).then((response) {
print(response.body);
Navigator.of(ctx).pop(); // Убираем текущий route
pushRoute(ctx, new RegistrationScreen()); // Запускаем регистрацию
}).catchError((error) {
print(error.toString());
});
});
}
}).catchError((error) => print(error.toString()));
}
} }

View File

@@ -1,43 +1,45 @@
import 'package:intl/intl.dart'; import 'package:intl/intl.dart';
import 'package:flutter/widgets.dart'; import 'i18n/messages_all.dart';
import 'package:sprintf/sprintf.dart'; import 'dart:async';
class Strings extends LocaleQueryData { class Strings {
static Strings of(BuildContext context) { static final Strings _singleton = new Strings._internal();
return LocaleQuery.of(context);
String _localeName;
factory Strings(){
return _singleton;
} }
static final Strings instance = new Strings(); Strings._internal();
String confirmPurchase(String val) { Future load(String locale) async {
return sprintf(Intl.message('confirm_purchase'), val); _localeName = locale;
return initializeMessages(locale);
} }
String purchaseCompleted(String val) { String registration() => Intl.message('registration', name: 'registration', locale: _localeName);
return sprintf(Intl.message('purchase_complite'), val); String idStore() => Intl.message('ID_Store', name: 'ID_Store', locale: _localeName);
} String signUp() => Intl.message('sign_up', name: 'sign_up', locale: _localeName);
String specifyDinStore() => Intl.message('specify_din_store', name: 'specify_din_store', locale: _localeName);
String idStore() => Intl.message('ID_Store'); String confirmation() => Intl.message('confirmation', name: 'confirmation', locale: _localeName);
String signUp() => Intl.message('sign_up'); String askChangeStore() => Intl.message('ask_change_store', name: 'ask_change_store', locale: _localeName);
String registration() => Intl.message('registration'); String yes() => Intl.message('yes', name: 'yes', locale: _localeName);
String specifyDinStore() => Intl.message('specify_din_store'); String no() => Intl.message('no', name: 'no', locale: _localeName);
String confirmation() => Intl.message('confirmation'); String requestSentWaitActivation() => Intl.message('request_sent_wait_activ', name: 'request_sent_wait_activ', locale: _localeName);
String askChangeStore() => Intl.message('ask_change_store'); String refreshActivationStatus() => Intl.message('update_activ_status', name: 'update_activ_status', locale: _localeName);
String yes() => Intl.message('yes'); String appActivated() => Intl.message('app_activ', name: 'app_activ', locale: _localeName);
String no() => Intl.message('no'); String completeRegistration() => Intl.message('complite_activ', name: 'complite_activ', locale: _localeName);
String requestSentWaitActivation() => Intl.message('request_sent_wait_activ'); String cardScanner() => Intl.message('card_scaner', name: 'card_scaner', locale: _localeName);
String refreshActivationStatus() => Intl.message('update_activ_status'); String userName() => Intl.message('user_name', name: 'user_name', locale: _localeName);
String appActivated() => Intl.message('app_activ'); String card() => Intl.message('card', name: 'card', locale: _localeName);
String completeRegistration() => Intl.message('complite_activ'); String reward() => Intl.message('reward', name: 'reward', locale: _localeName);
String cardScanner() => Intl.message('card_scaner'); String sum() => Intl.message('sum', name: 'sum', locale: _localeName);
String userName() => Intl.message('user_name'); String carryingPurchase() => Intl.message('carry_purchase', name: 'carry_purchase', locale: _localeName);
String card() => Intl.message('card'); String completePurchase() => Intl.message('complite_purchase', name: 'complite_purchase', locale: _localeName);
String reward() => Intl.message('reward'); String scan() => Intl.message('scan', name: 'scan', locale: _localeName);
String sum() => Intl.message('sum'); String buyer() => Intl.message('buyer', name: 'buyer', locale: _localeName);
String carryingPurchase() => Intl.message('carry_purchase'); String confirmPurchase() => Intl.message('confirm_purchase', name: 'confirm_purchase', locale: _localeName);
String completePurchase() => Intl.message('complite_purchase'); String idNotFound() => Intl.message('ID_not_found', name: 'ID_not_found', locale: _localeName);
String scan() => Intl.message('scan');
String buyer() => Intl.message('buyer');
String idNotFound() => Intl.message('ID_not_found');
} }