+fix
This commit is contained in:
@@ -11,27 +11,13 @@
|
|||||||
<uses-permission android:name="android.permission.RECEIVE_BOOT_COMPLETED" />
|
<uses-permission android:name="android.permission.RECEIVE_BOOT_COMPLETED" />
|
||||||
|
|
||||||
<application
|
<application
|
||||||
android:label="family_safety_frontend"
|
android:label="GeoShare"
|
||||||
android:name="${applicationName}"
|
android:name="${applicationName}"
|
||||||
android:icon="@mipmap/ic_launcher">
|
android:icon="@mipmap/ic_launcher">
|
||||||
<service
|
<service
|
||||||
android:name="com.pravera.flutter_foreground_task.service.ForegroundService"
|
android:name="com.pravera.flutter_foreground_task.service.ForegroundService"
|
||||||
android:foregroundServiceType="location"
|
android:foregroundServiceType="location"
|
||||||
android:exported="false" />
|
android:exported="false" />
|
||||||
<receiver
|
|
||||||
android:name="com.pravera.flutter_foreground_task.receiver.AlarmReceiver"
|
|
||||||
android:exported="false">
|
|
||||||
<intent-filter>
|
|
||||||
<action android:name="com.pravera.flutter_foreground_task.action.ALARM" />
|
|
||||||
</intent-filter>
|
|
||||||
</receiver>
|
|
||||||
<receiver
|
|
||||||
android:name="com.pravera.flutter_foreground_task.receiver.BootReceiver"
|
|
||||||
android:exported="false">
|
|
||||||
<intent-filter>
|
|
||||||
<action android:name="android.intent.action.BOOT_COMPLETED" />
|
|
||||||
</intent-filter>
|
|
||||||
</receiver>
|
|
||||||
<activity
|
<activity
|
||||||
android:name=".MainActivity"
|
android:name=".MainActivity"
|
||||||
android:exported="true"
|
android:exported="true"
|
||||||
|
|||||||
+2
-2
@@ -1,9 +1,9 @@
|
|||||||
class ApiConfig {
|
class ApiConfig {
|
||||||
static String _baseUrl = 'https://family-safety.onrender.com/api';
|
static String _baseUrl = 'https://test';
|
||||||
|
|
||||||
static String get baseUrl => _baseUrl;
|
static String get baseUrl => _baseUrl;
|
||||||
|
|
||||||
static void setBaseUrl(String url) {
|
static void setBaseUrl(String url){
|
||||||
_baseUrl = url.endsWith('/') ? url.substring(0, url.length - 1) : url;
|
_baseUrl = url.endsWith('/') ? url.substring(0, url.length - 1) : url;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -12,6 +12,7 @@ import 'services/settings_service.dart';
|
|||||||
void main() async {
|
void main() async {
|
||||||
WidgetsFlutterBinding.ensureInitialized();
|
WidgetsFlutterBinding.ensureInitialized();
|
||||||
final settings = SettingsService();
|
final settings = SettingsService();
|
||||||
|
await settings.initialize();
|
||||||
ApiConfig.setBaseUrl(settings.baseUrl);
|
ApiConfig.setBaseUrl(settings.baseUrl);
|
||||||
final bgGeo = BackgroundGeoService();
|
final bgGeo = BackgroundGeoService();
|
||||||
await bgGeo.init();
|
await bgGeo.init();
|
||||||
|
|||||||
@@ -30,13 +30,18 @@ class _LoginScreenState extends State<LoginScreen>
|
|||||||
_tabController.addListener(() {
|
_tabController.addListener(() {
|
||||||
setState(() {});
|
setState(() {});
|
||||||
});
|
});
|
||||||
final settings = context.read<SettingsService>();
|
WidgetsBinding.instance.addPostFrameCallback((_) {
|
||||||
_serverUrlController.text = settings.baseUrl;
|
final settings = context.read<SettingsService>();
|
||||||
_loginController.text = settings.login ?? "";
|
_serverUrlController.text = settings.baseUrl;
|
||||||
_passwordController.text = settings.pwd ?? "";
|
_loginController.text = settings.login ?? "";
|
||||||
if (settings.login != null && settings.pwd != null) {
|
_passwordController.text = settings.pwd ?? "";
|
||||||
savedAuth = true;
|
if (settings.login != null && settings.pwd != null) {
|
||||||
}
|
savedAuth = true;
|
||||||
|
final authProvider = context.read<AuthProvider>();
|
||||||
|
login(settings.login!, settings.pwd!, authProvider);
|
||||||
|
Navigator.of(context).pushReplacementNamed('/map');
|
||||||
|
}
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
@override
|
@override
|
||||||
@@ -53,15 +58,6 @@ class _LoginScreenState extends State<LoginScreen>
|
|||||||
Widget build(BuildContext context) {
|
Widget build(BuildContext context) {
|
||||||
final authProvider = context.watch<AuthProvider>();
|
final authProvider = context.watch<AuthProvider>();
|
||||||
final settings = context.read<SettingsService>();
|
final settings = context.read<SettingsService>();
|
||||||
if (savedAuth) {
|
|
||||||
login(
|
|
||||||
_loginController.text,
|
|
||||||
_passwordController.text,
|
|
||||||
context,
|
|
||||||
authProvider,
|
|
||||||
settings,
|
|
||||||
);
|
|
||||||
}
|
|
||||||
return Scaffold(
|
return Scaffold(
|
||||||
resizeToAvoidBottomInset: true,
|
resizeToAvoidBottomInset: true,
|
||||||
body: SingleChildScrollView(
|
body: SingleChildScrollView(
|
||||||
@@ -77,7 +73,7 @@ class _LoginScreenState extends State<LoginScreen>
|
|||||||
crossAxisAlignment: CrossAxisAlignment.stretch,
|
crossAxisAlignment: CrossAxisAlignment.stretch,
|
||||||
children: [
|
children: [
|
||||||
const Text(
|
const Text(
|
||||||
'Family Safety',
|
'Авторизация',
|
||||||
textAlign: TextAlign.center,
|
textAlign: TextAlign.center,
|
||||||
style: TextStyle(
|
style: TextStyle(
|
||||||
fontSize: 32,
|
fontSize: 32,
|
||||||
@@ -174,11 +170,13 @@ class _LoginScreenState extends State<LoginScreen>
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
if (_tabController.index == 0) {
|
if (_tabController.index == 0) {
|
||||||
|
print("Login...");
|
||||||
try {
|
try {
|
||||||
await authProvider.login(
|
await authProvider.login(
|
||||||
_loginController.text,
|
_loginController.text,
|
||||||
_passwordController.text,
|
_passwordController.text,
|
||||||
);
|
);
|
||||||
|
settings.saveAuth(_loginController.text, _passwordController.text);
|
||||||
if (!context.mounted) return;
|
if (!context.mounted) return;
|
||||||
Navigator.of(
|
Navigator.of(
|
||||||
context,
|
context,
|
||||||
@@ -233,15 +231,10 @@ class _LoginScreenState extends State<LoginScreen>
|
|||||||
Future<void> login(
|
Future<void> login(
|
||||||
String login,
|
String login,
|
||||||
String pwd,
|
String pwd,
|
||||||
BuildContext ctx,
|
|
||||||
AuthProvider authProvider,
|
AuthProvider authProvider,
|
||||||
SettingsService settings,
|
|
||||||
) async {
|
) async {
|
||||||
try {
|
try {
|
||||||
await authProvider.login(login, pwd);
|
await authProvider.login(login, pwd);
|
||||||
settings.saveAuth(login, pwd);
|
|
||||||
if (!ctx.mounted) return;
|
|
||||||
Navigator.of(ctx).pushReplacementNamed('/map');
|
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
// Error is handled by provider
|
// Error is handled by provider
|
||||||
}
|
}
|
||||||
|
|||||||
+48
-26
@@ -1,4 +1,5 @@
|
|||||||
import 'dart:async';
|
import 'dart:async';
|
||||||
|
import 'package:GeoShare/services/settings_service.dart';
|
||||||
import 'package:flutter/material.dart';
|
import 'package:flutter/material.dart';
|
||||||
import 'package:flutter/services.dart';
|
import 'package:flutter/services.dart';
|
||||||
import 'package:flutter_map/flutter_map.dart';
|
import 'package:flutter_map/flutter_map.dart';
|
||||||
@@ -27,7 +28,7 @@ class _MapScreenState extends State<MapScreen> {
|
|||||||
@override
|
@override
|
||||||
void initState() {
|
void initState() {
|
||||||
super.initState();
|
super.initState();
|
||||||
_initLocationAndShare();
|
//_initLocationAndShare();
|
||||||
}
|
}
|
||||||
|
|
||||||
@override
|
@override
|
||||||
@@ -93,7 +94,7 @@ class _MapScreenState extends State<MapScreen> {
|
|||||||
|
|
||||||
_geoTimer = Timer.periodic(
|
_geoTimer = Timer.periodic(
|
||||||
const Duration(seconds: 30),
|
const Duration(seconds: 30),
|
||||||
(_) => _updateGeolocation(),
|
(_) => _updateGeolocation(),
|
||||||
);
|
);
|
||||||
} catch (_) {
|
} catch (_) {
|
||||||
setState(() => _loading = false);
|
setState(() => _loading = false);
|
||||||
@@ -150,21 +151,22 @@ class _MapScreenState extends State<MapScreen> {
|
|||||||
showModalBottomSheet(
|
showModalBottomSheet(
|
||||||
context: context,
|
context: context,
|
||||||
isScrollControlled: true,
|
isScrollControlled: true,
|
||||||
builder: (context) => _WatchMenu(
|
builder: (context) =>
|
||||||
token: authProvider.token,
|
_WatchMenu(
|
||||||
onAdd: (shareId) async {
|
token: authProvider.token,
|
||||||
await shareProvider.addTracking(shareId, authProvider.token);
|
onAdd: (shareId) async {
|
||||||
if (shareProvider.trackedPeople.length == 1) {
|
await shareProvider.addTracking(shareId, authProvider.token);
|
||||||
shareProvider.startTrackingPolling(authProvider.token);
|
if (shareProvider.trackedPeople.length == 1) {
|
||||||
}
|
shareProvider.startTrackingPolling(authProvider.token);
|
||||||
},
|
}
|
||||||
onRemove: (shareId) {
|
},
|
||||||
shareProvider.removeTracking(shareId);
|
onRemove: (shareId) {
|
||||||
if (shareProvider.trackedPeople.isEmpty) {
|
shareProvider.removeTracking(shareId);
|
||||||
shareProvider.stopAllTracking();
|
if (shareProvider.trackedPeople.isEmpty) {
|
||||||
}
|
shareProvider.stopAllTracking();
|
||||||
},
|
}
|
||||||
),
|
},
|
||||||
|
),
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -207,6 +209,7 @@ class _MapScreenState extends State<MapScreen> {
|
|||||||
final authProvider = context.watch<AuthProvider>();
|
final authProvider = context.watch<AuthProvider>();
|
||||||
final _ = context.watch<MapProvider>();
|
final _ = context.watch<MapProvider>();
|
||||||
final shareProvider = context.watch<ShareProvider>();
|
final shareProvider = context.watch<ShareProvider>();
|
||||||
|
final settings = context.read<SettingsService>();
|
||||||
|
|
||||||
if (_loading) {
|
if (_loading) {
|
||||||
return Scaffold(body: const Center(child: CircularProgressIndicator()));
|
return Scaffold(body: const Center(child: CircularProgressIndicator()));
|
||||||
@@ -215,7 +218,8 @@ class _MapScreenState extends State<MapScreen> {
|
|||||||
return Scaffold(
|
return Scaffold(
|
||||||
appBar: AppBar(
|
appBar: AppBar(
|
||||||
title: Text(
|
title: Text(
|
||||||
'Family Safety Map\n${_center.latitude.toStringAsFixed(4)}, ${_center.longitude.toStringAsFixed(4)}',
|
'Карта\n${_center.latitude.toStringAsFixed(4)}, ${_center
|
||||||
|
.longitude.toStringAsFixed(4)}',
|
||||||
textAlign: TextAlign.center,
|
textAlign: TextAlign.center,
|
||||||
),
|
),
|
||||||
actions: [
|
actions: [
|
||||||
@@ -223,7 +227,10 @@ class _MapScreenState extends State<MapScreen> {
|
|||||||
icon: const Icon(Icons.refresh),
|
icon: const Icon(Icons.refresh),
|
||||||
onPressed: _updateGeolocation,
|
onPressed: _updateGeolocation,
|
||||||
),
|
),
|
||||||
IconButton(icon: const Icon(Icons.share), onPressed: _copyShareLink),
|
IconButton(icon: const Icon(Icons.share), onPressed: () {
|
||||||
|
_initLocationAndShare();
|
||||||
|
_copyShareLink();
|
||||||
|
}),
|
||||||
IconButton(
|
IconButton(
|
||||||
icon: const Icon(Icons.visibility),
|
icon: const Icon(Icons.visibility),
|
||||||
onPressed: _openWatchMenu,
|
onPressed: _openWatchMenu,
|
||||||
@@ -236,6 +243,7 @@ class _MapScreenState extends State<MapScreen> {
|
|||||||
await bgGeo.stop();
|
await bgGeo.stop();
|
||||||
bgGeo.dispose();
|
bgGeo.dispose();
|
||||||
authProvider.logout();
|
authProvider.logout();
|
||||||
|
settings.logOut();
|
||||||
Navigator.of(context).pushReplacementNamed('/');
|
Navigator.of(context).pushReplacementNamed('/');
|
||||||
},
|
},
|
||||||
),
|
),
|
||||||
@@ -287,7 +295,10 @@ class _WatchMenuState extends State<_WatchMenu> {
|
|||||||
|
|
||||||
return Padding(
|
return Padding(
|
||||||
padding: EdgeInsets.only(
|
padding: EdgeInsets.only(
|
||||||
bottom: MediaQuery.of(context).viewInsets.bottom,
|
bottom: MediaQuery
|
||||||
|
.of(context)
|
||||||
|
.viewInsets
|
||||||
|
.bottom,
|
||||||
),
|
),
|
||||||
child: Container(
|
child: Container(
|
||||||
constraints: const BoxConstraints(maxHeight: 400),
|
constraints: const BoxConstraints(maxHeight: 400),
|
||||||
@@ -297,7 +308,10 @@ class _WatchMenuState extends State<_WatchMenu> {
|
|||||||
Container(
|
Container(
|
||||||
padding: const EdgeInsets.all(16),
|
padding: const EdgeInsets.all(16),
|
||||||
decoration: BoxDecoration(
|
decoration: BoxDecoration(
|
||||||
color: Theme.of(context).colorScheme.surfaceContainerHighest,
|
color: Theme
|
||||||
|
.of(context)
|
||||||
|
.colorScheme
|
||||||
|
.surfaceContainerHighest,
|
||||||
borderRadius: const BorderRadius.vertical(
|
borderRadius: const BorderRadius.vertical(
|
||||||
top: Radius.circular(16),
|
top: Radius.circular(16),
|
||||||
),
|
),
|
||||||
@@ -354,8 +368,9 @@ class _WatchMenuState extends State<_WatchMenu> {
|
|||||||
title: Text(person.name),
|
title: Text(person.name),
|
||||||
subtitle: hasPosition
|
subtitle: hasPosition
|
||||||
? Text(
|
? Text(
|
||||||
'${person.position!['y'].toStringAsFixed(4)}, ${person.position!['x'].toStringAsFixed(4)}',
|
'${person.position!['y'].toStringAsFixed(4)}, ${person
|
||||||
)
|
.position!['x'].toStringAsFixed(4)}',
|
||||||
|
)
|
||||||
: const Text('Нет данных'),
|
: const Text('Нет данных'),
|
||||||
trailing: IconButton(
|
trailing: IconButton(
|
||||||
icon: const Icon(Icons.close, color: Colors.red),
|
icon: const Icon(Icons.close, color: Colors.red),
|
||||||
@@ -368,7 +383,10 @@ class _WatchMenuState extends State<_WatchMenu> {
|
|||||||
Container(
|
Container(
|
||||||
padding: const EdgeInsets.all(16),
|
padding: const EdgeInsets.all(16),
|
||||||
decoration: BoxDecoration(
|
decoration: BoxDecoration(
|
||||||
color: Theme.of(context).colorScheme.surfaceContainerHighest,
|
color: Theme
|
||||||
|
.of(context)
|
||||||
|
.colorScheme
|
||||||
|
.surfaceContainerHighest,
|
||||||
borderRadius: const BorderRadius.vertical(
|
borderRadius: const BorderRadius.vertical(
|
||||||
bottom: Radius.circular(16),
|
bottom: Radius.circular(16),
|
||||||
),
|
),
|
||||||
@@ -387,7 +405,9 @@ class _WatchMenuState extends State<_WatchMenu> {
|
|||||||
),
|
),
|
||||||
),
|
),
|
||||||
onSubmitted: (value) {
|
onSubmitted: (value) {
|
||||||
if (value.trim().isNotEmpty) {
|
if (value
|
||||||
|
.trim()
|
||||||
|
.isNotEmpty) {
|
||||||
widget.onAdd(value.trim());
|
widget.onAdd(value.trim());
|
||||||
_controller.clear();
|
_controller.clear();
|
||||||
}
|
}
|
||||||
@@ -397,7 +417,9 @@ class _WatchMenuState extends State<_WatchMenu> {
|
|||||||
const SizedBox(width: 8),
|
const SizedBox(width: 8),
|
||||||
ElevatedButton(
|
ElevatedButton(
|
||||||
onPressed: () {
|
onPressed: () {
|
||||||
if (_controller.text.trim().isNotEmpty) {
|
if (_controller.text
|
||||||
|
.trim()
|
||||||
|
.isNotEmpty) {
|
||||||
widget.onAdd(_controller.text.trim());
|
widget.onAdd(_controller.text.trim());
|
||||||
_controller.clear();
|
_controller.clear();
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -8,6 +8,7 @@ class AuthService {
|
|||||||
AuthService({http.Client? client}) : _client = client ?? http.Client();
|
AuthService({http.Client? client}) : _client = client ?? http.Client();
|
||||||
|
|
||||||
Future<String> login(String login, String password) async {
|
Future<String> login(String login, String password) async {
|
||||||
|
print(ApiConfig.loginUrl);
|
||||||
final response = await _client.post(
|
final response = await _client.post(
|
||||||
Uri.parse(ApiConfig.loginUrl),
|
Uri.parse(ApiConfig.loginUrl),
|
||||||
headers: {'Content-Type': 'application/json'},
|
headers: {'Content-Type': 'application/json'},
|
||||||
@@ -17,6 +18,8 @@ class AuthService {
|
|||||||
if (response.statusCode == 200) {
|
if (response.statusCode == 200) {
|
||||||
return response.body;
|
return response.body;
|
||||||
} else {
|
} else {
|
||||||
|
print(response.statusCode);
|
||||||
|
print(response.body);
|
||||||
throw Exception('Invalid credentials');
|
throw Exception('Invalid credentials');
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,7 +1,6 @@
|
|||||||
import 'package:shared_preferences/shared_preferences.dart';
|
import 'package:shared_preferences/shared_preferences.dart';
|
||||||
|
|
||||||
class SettingsService {
|
class SettingsService {
|
||||||
SettingsService(){initialize();}
|
|
||||||
static const String _baseUrlKey = 'server_base_url';
|
static const String _baseUrlKey = 'server_base_url';
|
||||||
static const String _defaultBaseUrl =
|
static const String _defaultBaseUrl =
|
||||||
'https://family-safety.onrender.com/api';
|
'https://family-safety.onrender.com/api';
|
||||||
@@ -13,6 +12,7 @@ class SettingsService {
|
|||||||
String get baseUrl => _baseUrl;
|
String get baseUrl => _baseUrl;
|
||||||
String? login;
|
String? login;
|
||||||
String? pwd;
|
String? pwd;
|
||||||
|
bool loggedIn=false;
|
||||||
|
|
||||||
Future<void> initialize() async {
|
Future<void> initialize() async {
|
||||||
if (_initialized) return;
|
if (_initialized) return;
|
||||||
@@ -34,7 +34,14 @@ class SettingsService {
|
|||||||
pwd = pass;
|
pwd = pass;
|
||||||
final prefs = await SharedPreferences.getInstance();
|
final prefs = await SharedPreferences.getInstance();
|
||||||
await prefs.setString(_loginKey, user);
|
await prefs.setString(_loginKey, user);
|
||||||
await prefs.setString(_loginKey, user);
|
await prefs.setString(_pwdKey, pass);
|
||||||
|
}
|
||||||
|
Future<void> logOut() async {
|
||||||
|
final prefs = await SharedPreferences.getInstance();
|
||||||
|
login = null;
|
||||||
|
pwd = null;
|
||||||
|
await prefs.remove(_loginKey);
|
||||||
|
await prefs.remove(_pwdKey);
|
||||||
}
|
}
|
||||||
|
|
||||||
void resetToDefault() {
|
void resetToDefault() {
|
||||||
|
|||||||
+2
-2
@@ -1,5 +1,5 @@
|
|||||||
name: family_safety_frontend
|
name: GeoShare
|
||||||
description: "A new Flutter project."
|
description: "GeoShare"
|
||||||
# The following line prevents the package from being accidentally published to
|
# The following line prevents the package from being accidentally published to
|
||||||
# pub.dev using `flutter pub publish`. This is preferred for private packages.
|
# pub.dev using `flutter pub publish`. This is preferred for private packages.
|
||||||
publish_to: 'none' # Remove this line if you wish to publish to pub.dev
|
publish_to: 'none' # Remove this line if you wish to publish to pub.dev
|
||||||
|
|||||||
Reference in New Issue
Block a user