Марков Сергей Викторович
10 months ago
15 changed files with 287 additions and 75 deletions
@ -0,0 +1,155 @@ |
|||||||
|
import 'dart:async'; |
||||||
|
import 'dart:math'; |
||||||
|
import 'dart:ui'; |
||||||
|
|
||||||
|
import 'package:camera/camera.dart'; |
||||||
|
import 'package:camera_aurora/camera_aurora.dart'; |
||||||
|
import 'package:flutter/material.dart'; |
||||||
|
import 'package:neon/src/models/account.dart'; |
||||||
|
import 'package:neon/src/pages/login_qr_code.dart'; |
||||||
|
import 'package:neon/src/router.dart'; |
||||||
|
import 'package:neon/widgets.dart'; |
||||||
|
|
||||||
|
class AuroraLoginQRcodePage extends StatefulWidget { |
||||||
|
const AuroraLoginQRcodePage({super.key}); |
||||||
|
|
||||||
|
@override |
||||||
|
State<AuroraLoginQRcodePage> createState() => _AuroraLoginQRcodePageState(); |
||||||
|
} |
||||||
|
|
||||||
|
class _AuroraLoginQRcodePageState extends State<AuroraLoginQRcodePage> { |
||||||
|
String? _lastErrorURL; |
||||||
|
late Future<CameraController> _controller; |
||||||
|
|
||||||
|
StreamSubscription<String?>? _cameraSearchQrSubscription; |
||||||
|
|
||||||
|
String? code; |
||||||
|
|
||||||
|
Future<CameraController> _createController() async { |
||||||
|
final cameras = await availableCameras(); |
||||||
|
if (cameras.isEmpty) { |
||||||
|
throw CameraException('not found', null); |
||||||
|
} |
||||||
|
|
||||||
|
final camera = cameras.where((final element) => element.lensDirection == CameraLensDirection.back).first; |
||||||
|
|
||||||
|
final controller = CameraController( |
||||||
|
camera, |
||||||
|
ResolutionPreset.medium, |
||||||
|
imageFormatGroup: ImageFormatGroup.jpeg, |
||||||
|
); |
||||||
|
|
||||||
|
_cameraSearchQrSubscription = cameraSearchQr?.listen((text) { |
||||||
|
if (text.isNotEmpty) { |
||||||
|
setState(() { |
||||||
|
code = text; |
||||||
|
}); |
||||||
|
} |
||||||
|
}); |
||||||
|
|
||||||
|
await controller.initialize(); |
||||||
|
return controller; |
||||||
|
} |
||||||
|
|
||||||
|
@override |
||||||
|
void initState() { |
||||||
|
_controller = _createController(); |
||||||
|
super.initState(); |
||||||
|
} |
||||||
|
|
||||||
|
@override |
||||||
|
Widget build(final BuildContext context) => Scaffold( |
||||||
|
appBar: AppBar( |
||||||
|
backgroundColor: Theme.of(context).colorScheme.inversePrimary, |
||||||
|
title: const Text('Отсканируйте qr код пароля приложения'), |
||||||
|
), |
||||||
|
body: Center( |
||||||
|
child: Column( |
||||||
|
mainAxisAlignment: MainAxisAlignment.center, |
||||||
|
children: <Widget>[ |
||||||
|
FutureBuilder( |
||||||
|
future: _controller, |
||||||
|
builder: (final context, final snapshot) { |
||||||
|
if (snapshot.hasData) { |
||||||
|
if (code != null) { |
||||||
|
String? url; |
||||||
|
try { |
||||||
|
print(code); |
||||||
|
url = code; |
||||||
|
if (url == null) { |
||||||
|
throw const InvalidQRcodeException(); |
||||||
|
} |
||||||
|
final match = LoginQRcode.tryParse(url); |
||||||
|
if (match == null) { |
||||||
|
throw const InvalidQRcodeException(); |
||||||
|
} |
||||||
|
|
||||||
|
_cameraSearchQrSubscription?.cancel(); |
||||||
|
|
||||||
|
LoginCheckServerStatusRoute.withCredentials( |
||||||
|
serverUrl: match.serverURL, |
||||||
|
loginName: match.username, |
||||||
|
password: match.password, |
||||||
|
).pushReplacement(context); |
||||||
|
} catch (e, s) { |
||||||
|
if (_lastErrorURL != url) { |
||||||
|
code = ''; |
||||||
|
debugPrint(e.toString()); |
||||||
|
debugPrint(s.toString()); |
||||||
|
|
||||||
|
_lastErrorURL = url; |
||||||
|
NeonError.showSnackbar(context, e); |
||||||
|
} |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
return Expanded( |
||||||
|
child: Stack( |
||||||
|
children: [ |
||||||
|
SizedBox( |
||||||
|
width: double.maxFinite, |
||||||
|
height: double.maxFinite, |
||||||
|
child: snapshot.data!.buildPreview(), |
||||||
|
// builder: (final context, final constraints) => AspectRatio( |
||||||
|
// aspectRatio: max(constraints.maxHeight, constraints.maxWidth) / |
||||||
|
// min(constraints.maxHeight, constraints.maxWidth), |
||||||
|
// child: snapshot.data!.buildPreview(), |
||||||
|
// ), |
||||||
|
), |
||||||
|
ClipPath( |
||||||
|
clipper: InvertedClipper(), |
||||||
|
child: Container( |
||||||
|
width: double.maxFinite, |
||||||
|
height: double.maxFinite, |
||||||
|
color: Colors.blue.withOpacity(0.7), |
||||||
|
), |
||||||
|
), |
||||||
|
], |
||||||
|
), |
||||||
|
); |
||||||
|
} else if (snapshot.hasError) { |
||||||
|
return Text(snapshot.error.toString()); |
||||||
|
} |
||||||
|
|
||||||
|
return const CircularProgressIndicator(); |
||||||
|
}, |
||||||
|
) |
||||||
|
], |
||||||
|
), |
||||||
|
), |
||||||
|
); |
||||||
|
} |
||||||
|
|
||||||
|
class InvertedClipper extends CustomClipper<Path> { |
||||||
|
@override |
||||||
|
Path getClip(final Size size) { |
||||||
|
final clipSize = min(size.width, size.height) * 0.6; |
||||||
|
return Path() |
||||||
|
..addRect(Rect.fromLTWH(0, 0, size.width, size.height)) |
||||||
|
..addRect(Rect.fromLTWH((size.width / 2) - clipSize / 2, (size.height / 2) - clipSize / 2, clipSize, clipSize)) |
||||||
|
..fillType = PathFillType.evenOdd; |
||||||
|
} |
||||||
|
|
||||||
|
@override |
||||||
|
bool shouldReclip(final CustomClipper<Path> oldClipper) => true; |
||||||
|
} |
Loading…
Reference in new issue