forked from eatvio/eatvio_app
370 lines
13 KiB
Dart
370 lines
13 KiB
Dart
import 'dart:convert';
|
|
|
|
import 'package:dio/dio.dart';
|
|
import 'package:eatvio_app/core/const.dart';
|
|
import 'package:eatvio_app/core/locator_service.dart';
|
|
import 'package:eatvio_app/core/token_storage.dart';
|
|
import 'package:eatvio_app/core/utils/image_constant.dart';
|
|
import 'package:eatvio_app/core/widgets/custom_text_form_field.dart';
|
|
import 'package:eatvio_app/features/bottom_navigation_bar/presentation/bloc/profile_avatar_cubit/profile_avatar_cubit.dart';
|
|
import 'package:eatvio_app/generated/l10n.dart';
|
|
import 'package:eatvio_app/core/theme/theme_helper.dart';
|
|
import 'package:flutter/gestures.dart';
|
|
import 'package:flutter/material.dart';
|
|
import 'package:flutter_bloc/flutter_bloc.dart';
|
|
import 'package:flutter_native_splash/flutter_native_splash.dart';
|
|
import 'package:flutter_svg/flutter_svg.dart';
|
|
import 'package:go_router/go_router.dart';
|
|
import 'package:http/http.dart' as http;
|
|
import 'package:url_launcher/url_launcher.dart';
|
|
import 'package:validators/validators.dart';
|
|
import 'package:webview_flutter/webview_flutter.dart';
|
|
|
|
import 'bloc/auth_bloc.dart';
|
|
import 'models/auth_model.dart';
|
|
|
|
class AuthScreen extends StatefulWidget {
|
|
static Widget builder(BuildContext context) {
|
|
return BlocProvider<AuthBloc>(
|
|
create: (context) => AuthBloc(
|
|
AuthState(
|
|
authModelObj: AuthModel(),
|
|
),
|
|
)..add(
|
|
AuthInitialEvent(),
|
|
),
|
|
child: AuthScreen(),
|
|
);
|
|
}
|
|
|
|
@override
|
|
State<AuthScreen> createState() => _AuthScreenState();
|
|
}
|
|
|
|
class _AuthScreenState extends State<AuthScreen> {
|
|
bool checkboxState = false;
|
|
bool hidePassword = true;
|
|
final baseUrlController = TextEditingController();
|
|
late final WebViewController _controller;
|
|
|
|
@override
|
|
void initState() {
|
|
super.initState();
|
|
|
|
_controller = WebViewController()
|
|
..setJavaScriptMode(JavaScriptMode.unrestricted)
|
|
..addJavaScriptChannel(
|
|
'FlutterChannel',
|
|
onMessageReceived: (JavaScriptMessage message) {
|
|
// Handle messages from JavaScript
|
|
final data = jsonDecode(message.message);
|
|
final email = data['email'];
|
|
final password = data['password'];
|
|
print('Email: $email, Password: $password');
|
|
},
|
|
)
|
|
..setNavigationDelegate(
|
|
NavigationDelegate(
|
|
onPageFinished: (String url) async {
|
|
await _controller.runJavaScript('''
|
|
function initializeListeners() {
|
|
const emailInput = document.getElementById("inputEmail_main");
|
|
const passwordInput = document.getElementById("inputPassword_main");
|
|
|
|
if (emailInput && passwordInput) {
|
|
function sendToFlutter() {
|
|
const email = emailInput.value;
|
|
const password = passwordInput.value;
|
|
const data = JSON.stringify({ email: email, password: password });
|
|
if (window.FlutterChannel) {
|
|
window.FlutterChannel.postMessage(data);
|
|
}
|
|
}
|
|
|
|
emailInput.addEventListener("input", sendToFlutter);
|
|
passwordInput.addEventListener("input", sendToFlutter);
|
|
} else {
|
|
console.error("Email or Password input not found.");
|
|
}
|
|
}
|
|
|
|
// Ensure DOM is loaded before attaching listeners
|
|
document.addEventListener("DOMContentLoaded", initializeListeners);
|
|
initializeListeners();
|
|
''');
|
|
},
|
|
),
|
|
)
|
|
..loadRequest(Uri.parse(baseUrl));
|
|
}
|
|
|
|
@override
|
|
Widget build(BuildContext context) {
|
|
FlutterNativeSplash.remove();
|
|
final s = S.of(context);
|
|
|
|
// return Scaffold(
|
|
// appBar: AppBar(
|
|
// toolbarHeight: 0,
|
|
// ),
|
|
// body: WebViewWidget(controller: _controller),
|
|
// );
|
|
|
|
return Scaffold(
|
|
appBar: AppBar(
|
|
centerTitle: true,
|
|
title: GestureDetector(
|
|
onLongPress: () {
|
|
showDialog(
|
|
context: context,
|
|
builder: (context) => AlertDialog(
|
|
title: Text('Base url без https:// и /api'),
|
|
content: Column(
|
|
mainAxisSize: MainAxisSize.min,
|
|
children: [
|
|
CustomTextFormField(
|
|
controller: baseUrlController,
|
|
hintText: 'dev2.eatvio.com',
|
|
),
|
|
const SizedBox(height: 12),
|
|
InkWell(
|
|
onTap: () async {
|
|
baseUrl = 'https://${baseUrlController.text}';
|
|
sl<Dio>().options.baseUrl = baseUrl;
|
|
},
|
|
borderRadius: BorderRadius.circular(20),
|
|
child: Ink(
|
|
decoration: BoxDecoration(
|
|
borderRadius: BorderRadius.circular(20),
|
|
color: appTheme.blueA400,
|
|
),
|
|
child: Container(
|
|
padding: EdgeInsets.symmetric(horizontal: 16, vertical: 12),
|
|
alignment: Alignment.center,
|
|
child: Text(
|
|
'Сохранить',
|
|
style: TextStyle(
|
|
color: Colors.white,
|
|
fontWeight: FontWeight.w600,
|
|
),
|
|
),
|
|
),
|
|
),
|
|
),
|
|
],
|
|
),
|
|
),
|
|
);
|
|
},
|
|
child: SvgPicture.asset(
|
|
ImageConstant.imgLogoEatvio,
|
|
height: 70,
|
|
fit: BoxFit.contain,
|
|
),
|
|
),
|
|
),
|
|
body: SafeArea(
|
|
child: Padding(
|
|
padding: EdgeInsets.all(16),
|
|
child: Column(
|
|
crossAxisAlignment: CrossAxisAlignment.start,
|
|
children: [
|
|
Text(s.email),
|
|
const SizedBox(height: 4),
|
|
BlocSelector<AuthBloc, AuthState, TextEditingController?>(
|
|
selector: (state) => state.emailController,
|
|
builder: (context, emailController) {
|
|
return CustomTextFormField(
|
|
controller: emailController,
|
|
textInputAction: TextInputAction.next,
|
|
keyboardType: TextInputType.emailAddress,
|
|
textInputType: TextInputType.emailAddress,
|
|
hintText: 'Введите email',
|
|
);
|
|
},
|
|
),
|
|
const SizedBox(height: 16),
|
|
Text(s.password),
|
|
const SizedBox(height: 4),
|
|
BlocSelector<AuthBloc, AuthState, TextEditingController?>(
|
|
selector: (state) => state.passwordController,
|
|
builder: (context, passwordController) {
|
|
return CustomTextFormField(
|
|
obscureText: hidePassword,
|
|
controller: passwordController,
|
|
hintText: 'Введите пароль',
|
|
suffix: IconButton(
|
|
onPressed: () {
|
|
setState(() {
|
|
hidePassword = !hidePassword;
|
|
});
|
|
},
|
|
icon: Icon(
|
|
hidePassword ? Icons.visibility_outlined : Icons.visibility_off_outlined,
|
|
color: Colors.grey,
|
|
),
|
|
),
|
|
);
|
|
},
|
|
),
|
|
const SizedBox(height: 16),
|
|
Row(
|
|
mainAxisAlignment: MainAxisAlignment.center,
|
|
children: [
|
|
Text('Нет аккаунта? '),
|
|
GestureDetector(
|
|
onTap: () => GoRouter.of(context).goNamed('registrationScreen'),
|
|
child: Text(
|
|
'Зарегистрироваться',
|
|
style: TextStyle(
|
|
color: appTheme.blueA400,
|
|
decorationColor: appTheme.blueA400,
|
|
decoration: TextDecoration.underline,
|
|
),
|
|
),
|
|
),
|
|
],
|
|
),
|
|
Spacer(),
|
|
Center(
|
|
child: RichText(
|
|
textAlign: TextAlign.center,
|
|
text: TextSpan(
|
|
text: 'Нажимая "Войти", Вы соглашаетесь \n',
|
|
style: TextStyle(
|
|
fontSize: 12,
|
|
color: Colors.black,
|
|
),
|
|
children: [
|
|
TextSpan(text: 'c '),
|
|
TextSpan(
|
|
text: ' Публичной офертой',
|
|
style: TextStyle(
|
|
fontSize: 12,
|
|
color: appTheme.blueA400,
|
|
decorationColor: appTheme.blueA400,
|
|
decoration: TextDecoration.underline,
|
|
),
|
|
recognizer: TapGestureRecognizer()..onTap = () async => await launchUrl(Uri.parse(baseUrl + '/oferta_bs.pdf')),
|
|
),
|
|
],
|
|
),
|
|
),
|
|
),
|
|
const SizedBox(height: 16),
|
|
BlocBuilder<AuthBloc, AuthState>(
|
|
builder: (context, state) {
|
|
return Container(
|
|
height: 48,
|
|
width: MediaQuery.of(context).size.width - 32,
|
|
child: FloatingActionButton.extended(
|
|
heroTag: 'authButton',
|
|
highlightElevation: 0,
|
|
elevation: 0,
|
|
focusElevation: 0,
|
|
shape: RoundedRectangleBorder(
|
|
borderRadius: BorderRadius.circular(20),
|
|
),
|
|
backgroundColor: appTheme.blueA400,
|
|
onPressed: onTapAuth(state, context),
|
|
label: Text(
|
|
'Войти',
|
|
style: theme.textTheme.titleSmall!.copyWith(color: Colors.white),
|
|
),
|
|
),
|
|
);
|
|
},
|
|
),
|
|
],
|
|
),
|
|
),
|
|
),
|
|
);
|
|
}
|
|
|
|
onTapAuth(AuthState state, BuildContext context) {
|
|
final s = S.of(context);
|
|
return () async {
|
|
List<String> errors = validateFields(state.emailController!.text, state.passwordController!.text);
|
|
if (errors.isEmpty) {
|
|
final Map<String, dynamic> credentials = {
|
|
'email': state.emailController!.text,
|
|
'password': state.passwordController!.text,
|
|
};
|
|
final response = await http.post(
|
|
Uri.parse(baseUrl + '/api/login'),
|
|
headers: {'Content-Type': 'application/json'},
|
|
body: json.encode(credentials),
|
|
encoding: utf8,
|
|
);
|
|
|
|
if (response.statusCode == 200) {
|
|
var res = await jsonDecode(response.body);
|
|
final token = res['token'];
|
|
await TokenStorage.saveTokenInStorage(token);
|
|
GoRouter.of(context).go('/my-profile-screen');
|
|
sl<ProfileAvatarCubit>().getMyProfile();
|
|
} else {
|
|
showDialog(
|
|
context: context,
|
|
builder: (context) => AlertDialog(
|
|
title: Text('Ошибка'),
|
|
content: Text(s.wrong_credentials),
|
|
actions: [
|
|
TextButton(
|
|
onPressed: () => Navigator.pop(context),
|
|
child: Text('OK'),
|
|
),
|
|
],
|
|
),
|
|
);
|
|
}
|
|
} else {
|
|
String errorMessage = '';
|
|
|
|
for (var error in errors) {
|
|
errorMessage += error + '\n\n';
|
|
}
|
|
|
|
showDialog(
|
|
context: context,
|
|
builder: (context) => AlertDialog(
|
|
title: Text('Ошибка'),
|
|
content: Text(errorMessage),
|
|
actions: [
|
|
TextButton(
|
|
onPressed: () => Navigator.pop(context),
|
|
child: Text('OK'),
|
|
),
|
|
],
|
|
),
|
|
);
|
|
}
|
|
};
|
|
}
|
|
|
|
List<String> validateFields(String email, String password) {
|
|
final s = S.of(context);
|
|
|
|
List<String> errors = [];
|
|
|
|
if (!isEmail(email)) {
|
|
errors.add(s.email_invalid);
|
|
}
|
|
|
|
if (email.isEmpty) {
|
|
errors.add(s.email_empty);
|
|
}
|
|
|
|
if (password.isEmpty) {
|
|
errors.add(s.password_empty);
|
|
}
|
|
|
|
if (password.length < 6) {
|
|
errors.add(s.password_short);
|
|
}
|
|
|
|
return errors;
|
|
}
|
|
}
|