Files
eatvio_app/lib/features/auth/auth_screen/auth_screen.dart
2025-02-06 15:20:51 +07:00

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;
}
}