Марков Сергей Викторович
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