Add flutter 3+ support. Update dependencies. Fix code style and format issues.
This commit is contained in:
@@ -4,7 +4,6 @@ import 'package:thingsboard_client/thingsboard_client.dart';
|
||||
TbStorage createAppStorage() => TbSecureStorage();
|
||||
|
||||
class TbSecureStorage implements TbStorage {
|
||||
|
||||
final flutterStorage = FlutterSecureStorage();
|
||||
|
||||
@override
|
||||
@@ -21,5 +20,4 @@ class TbSecureStorage implements TbStorage {
|
||||
Future<void> setItem(String key, String value) async {
|
||||
return await flutterStorage.write(key: key, value: value);
|
||||
}
|
||||
|
||||
}
|
||||
@@ -1,11 +1,10 @@
|
||||
import 'package:thingsboard_client/thingsboard_client.dart';
|
||||
import 'dart:html';
|
||||
import 'package:universal_html/html.dart' as html;
|
||||
|
||||
TbStorage createAppStorage() => TbWebLocalStorage();
|
||||
|
||||
class TbWebLocalStorage implements TbStorage {
|
||||
|
||||
final Storage _localStorage = window.localStorage;
|
||||
final html.Storage _localStorage = html.window.localStorage;
|
||||
|
||||
@override
|
||||
Future<void> deleteItem(String key) async {
|
||||
@@ -21,5 +20,4 @@ class TbWebLocalStorage implements TbStorage {
|
||||
Future<void> setItem(String key, String value) async {
|
||||
_localStorage[key] = value;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -1,25 +1,29 @@
|
||||
import 'package:thingsboard_client/thingsboard_client.dart';
|
||||
|
||||
abstract class DeviceProfileCache {
|
||||
|
||||
static final _cache = Map<String, DeviceProfileInfo>();
|
||||
|
||||
static Future<DeviceProfileInfo> getDeviceProfileInfo(ThingsboardClient tbClient, String name, String deviceId) async {
|
||||
static Future<DeviceProfileInfo> getDeviceProfileInfo(
|
||||
ThingsboardClient tbClient, String name, String deviceId) async {
|
||||
var deviceProfile = _cache[name];
|
||||
if (deviceProfile == null) {
|
||||
var device = await tbClient.getDeviceService().getDevice(deviceId);
|
||||
deviceProfile = await tbClient.getDeviceProfileService().getDeviceProfileInfo(device!.deviceProfileId!.id!);
|
||||
deviceProfile = await tbClient
|
||||
.getDeviceProfileService()
|
||||
.getDeviceProfileInfo(device!.deviceProfileId!.id!);
|
||||
_cache[name] = deviceProfile!;
|
||||
}
|
||||
return deviceProfile;
|
||||
}
|
||||
|
||||
static Future<PageData<DeviceProfileInfo>> getDeviceProfileInfos(ThingsboardClient tbClient, PageLink pageLink) async {
|
||||
var deviceProfileInfos = await tbClient.getDeviceProfileService().getDeviceProfileInfos(pageLink);
|
||||
static Future<PageData<DeviceProfileInfo>> getDeviceProfileInfos(
|
||||
ThingsboardClient tbClient, PageLink pageLink) async {
|
||||
var deviceProfileInfos = await tbClient
|
||||
.getDeviceProfileService()
|
||||
.getDeviceProfileInfos(pageLink);
|
||||
deviceProfileInfos.data.forEach((deviceProfile) {
|
||||
_cache[deviceProfile.name] = deviceProfile;
|
||||
});
|
||||
return deviceProfileInfos;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -1,7 +1,6 @@
|
||||
import 'package:thingsboard_client/thingsboard_client.dart';
|
||||
|
||||
abstract class EntityQueryApi {
|
||||
|
||||
static final activeDeviceKeyFilter = KeyFilter(
|
||||
key: EntityKey(type: EntityKeyType.ATTRIBUTE, key: 'active'),
|
||||
valueType: EntityKeyValueType.BOOLEAN,
|
||||
@@ -27,36 +26,54 @@ abstract class EntityQueryApi {
|
||||
EntityKey(type: EntityKeyType.ATTRIBUTE, key: 'active')
|
||||
];
|
||||
|
||||
static Future<int> countDevices(ThingsboardClient tbClient, {String? deviceType, bool? active}) {
|
||||
static Future<int> countDevices(ThingsboardClient tbClient,
|
||||
{String? deviceType, bool? active}) {
|
||||
EntityFilter deviceFilter;
|
||||
if (deviceType != null) {
|
||||
deviceFilter = DeviceTypeFilter(deviceType: deviceType, deviceNameFilter: '');
|
||||
deviceFilter =
|
||||
DeviceTypeFilter(deviceType: deviceType, deviceNameFilter: '');
|
||||
} else {
|
||||
deviceFilter = EntityTypeFilter(entityType: EntityType.DEVICE);
|
||||
}
|
||||
EntityCountQuery deviceCountQuery = EntityCountQuery(entityFilter: deviceFilter);
|
||||
EntityCountQuery deviceCountQuery =
|
||||
EntityCountQuery(entityFilter: deviceFilter);
|
||||
if (active != null) {
|
||||
deviceCountQuery.keyFilters = [active ? activeDeviceKeyFilter : inactiveDeviceKeyFilter];
|
||||
deviceCountQuery.keyFilters = [
|
||||
active ? activeDeviceKeyFilter : inactiveDeviceKeyFilter
|
||||
];
|
||||
}
|
||||
return tbClient.getEntityQueryService().countEntitiesByQuery(deviceCountQuery);
|
||||
return tbClient
|
||||
.getEntityQueryService()
|
||||
.countEntitiesByQuery(deviceCountQuery);
|
||||
}
|
||||
|
||||
static EntityDataQuery createDefaultDeviceQuery({int pageSize = 20, String? searchText, String? deviceType, bool? active}) {
|
||||
static EntityDataQuery createDefaultDeviceQuery(
|
||||
{int pageSize = 20,
|
||||
String? searchText,
|
||||
String? deviceType,
|
||||
bool? active}) {
|
||||
EntityFilter deviceFilter;
|
||||
List<KeyFilter>? keyFilters;
|
||||
if (deviceType != null) {
|
||||
deviceFilter = DeviceTypeFilter(deviceType: deviceType, deviceNameFilter: '');
|
||||
deviceFilter =
|
||||
DeviceTypeFilter(deviceType: deviceType, deviceNameFilter: '');
|
||||
} else {
|
||||
deviceFilter = EntityTypeFilter(entityType: EntityType.DEVICE);
|
||||
}
|
||||
if (active != null) {
|
||||
keyFilters = [active ? activeDeviceKeyFilter : inactiveDeviceKeyFilter];
|
||||
}
|
||||
return EntityDataQuery(entityFilter: deviceFilter, keyFilters: keyFilters,
|
||||
entityFields: defaultDeviceFields, latestValues: defaultDeviceAttributes, pageLink: EntityDataPageLink(pageSize: pageSize,
|
||||
return EntityDataQuery(
|
||||
entityFilter: deviceFilter,
|
||||
keyFilters: keyFilters,
|
||||
entityFields: defaultDeviceFields,
|
||||
latestValues: defaultDeviceAttributes,
|
||||
pageLink: EntityDataPageLink(
|
||||
pageSize: pageSize,
|
||||
textSearch: searchText,
|
||||
sortOrder: EntityDataSortOrder(key: EntityKey(type: EntityKeyType.ENTITY_FIELD, key: 'createdTime'),
|
||||
sortOrder: EntityDataSortOrder(
|
||||
key: EntityKey(
|
||||
type: EntityKeyType.ENTITY_FIELD, key: 'createdTime'),
|
||||
direction: EntityDataSortOrderDirection.DESC)));
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -1,3 +1,3 @@
|
||||
export '_tb_app_storage.dart'
|
||||
if (dart.library.io) 'tb_secure_storage.dart'
|
||||
if (dart.library.html) 'tb_web_local_storage.dart';
|
||||
if (dart.library.io) '_tb_secure_storage.dart'
|
||||
if (dart.library.html) '_tb_web_local_storage.dart';
|
||||
|
||||
@@ -8,7 +8,7 @@ import 'package:image_picker/image_picker.dart';
|
||||
import 'package:mime/mime.dart';
|
||||
import 'package:qr_code_scanner/qr_code_scanner.dart';
|
||||
import 'package:thingsboard_app/core/context/tb_context.dart';
|
||||
import 'package:url_launcher/url_launcher.dart';
|
||||
import 'package:url_launcher/url_launcher_string.dart';
|
||||
|
||||
class WidgetMobileActionResult<T extends MobileActionResult> {
|
||||
T? result;
|
||||
@@ -16,11 +16,17 @@ class WidgetMobileActionResult<T extends MobileActionResult> {
|
||||
String? error;
|
||||
bool hasError = false;
|
||||
|
||||
WidgetMobileActionResult.errorResult(this.error): hasError = true, hasResult = false;
|
||||
WidgetMobileActionResult.errorResult(this.error)
|
||||
: hasError = true,
|
||||
hasResult = false;
|
||||
|
||||
WidgetMobileActionResult.successResult(this.result): hasError = false, hasResult = true;
|
||||
WidgetMobileActionResult.successResult(this.result)
|
||||
: hasError = false,
|
||||
hasResult = true;
|
||||
|
||||
WidgetMobileActionResult.emptyResult(): hasError = false, hasResult = false;
|
||||
WidgetMobileActionResult.emptyResult()
|
||||
: hasError = false,
|
||||
hasResult = false;
|
||||
|
||||
Map<String, dynamic> toJson() {
|
||||
var json = <String, dynamic>{};
|
||||
@@ -33,7 +39,6 @@ class WidgetMobileActionResult<T extends MobileActionResult> {
|
||||
}
|
||||
|
||||
class MobileActionResult {
|
||||
|
||||
MobileActionResult();
|
||||
|
||||
factory MobileActionResult.launched(bool launched) {
|
||||
@@ -123,24 +128,27 @@ enum WidgetMobileActionType {
|
||||
}
|
||||
|
||||
WidgetMobileActionType widgetMobileActionTypeFromString(String value) {
|
||||
return WidgetMobileActionType.values.firstWhere((e)=>e.toString().split('.')[1].toUpperCase()==value.toUpperCase(), orElse: () => WidgetMobileActionType.unknown);
|
||||
return WidgetMobileActionType.values.firstWhere(
|
||||
(e) => e.toString().split('.')[1].toUpperCase() == value.toUpperCase(),
|
||||
orElse: () => WidgetMobileActionType.unknown);
|
||||
}
|
||||
|
||||
class WidgetActionHandler with HasTbContext {
|
||||
|
||||
WidgetActionHandler(TbContext tbContext) {
|
||||
setTbContext(tbContext);
|
||||
}
|
||||
|
||||
Future<Map<String, dynamic>> handleWidgetMobileAction(List<dynamic> args, InAppWebViewController controller) async {
|
||||
Future<Map<String, dynamic>> handleWidgetMobileAction(
|
||||
List<dynamic> args, InAppWebViewController controller) async {
|
||||
var result = await _handleWidgetMobileAction(args, controller);
|
||||
return result.toJson();
|
||||
}
|
||||
|
||||
Future<WidgetMobileActionResult> _handleWidgetMobileAction(List<dynamic> args, InAppWebViewController controller) async {
|
||||
Future<WidgetMobileActionResult> _handleWidgetMobileAction(
|
||||
List<dynamic> args, InAppWebViewController controller) async {
|
||||
if (args.isNotEmpty && args[0] is String) {
|
||||
var actionType = widgetMobileActionTypeFromString(args[0]);
|
||||
switch(actionType) {
|
||||
switch (actionType) {
|
||||
case WidgetMobileActionType.takePictureFromGallery:
|
||||
return await _takePicture(ImageSource.gallery);
|
||||
case WidgetMobileActionType.takePhoto:
|
||||
@@ -158,24 +166,26 @@ class WidgetActionHandler with HasTbContext {
|
||||
case WidgetMobileActionType.takeScreenshot:
|
||||
return await _takeScreenshot(controller);
|
||||
case WidgetMobileActionType.unknown:
|
||||
return WidgetMobileActionResult.errorResult('Unknown actionType: ${args[0]}');
|
||||
return WidgetMobileActionResult.errorResult(
|
||||
'Unknown actionType: ${args[0]}');
|
||||
}
|
||||
} else {
|
||||
return WidgetMobileActionResult.errorResult('actionType is not provided.');
|
||||
return WidgetMobileActionResult.errorResult(
|
||||
'actionType is not provided.');
|
||||
}
|
||||
}
|
||||
|
||||
Future<WidgetMobileActionResult> _takePicture(ImageSource source) async {
|
||||
try {
|
||||
final picker = ImagePicker();
|
||||
final pickedFile = await picker.getImage(source: source);
|
||||
final pickedFile = await picker.pickImage(source: source);
|
||||
if (pickedFile != null) {
|
||||
var mimeType = lookupMimeType(pickedFile.path);
|
||||
if (mimeType != null) {
|
||||
var image = File(pickedFile.path);
|
||||
List<int> imageBytes = await image.readAsBytes();
|
||||
String imageUrl = UriData.fromBytes(imageBytes, mimeType: mimeType)
|
||||
.toString();
|
||||
String imageUrl =
|
||||
UriData.fromBytes(imageBytes, mimeType: mimeType).toString();
|
||||
return WidgetMobileActionResult.successResult(
|
||||
MobileActionResult.image(imageUrl));
|
||||
} else {
|
||||
@@ -190,7 +200,8 @@ class WidgetActionHandler with HasTbContext {
|
||||
}
|
||||
}
|
||||
|
||||
Future<WidgetMobileActionResult> _launchMap(List<dynamic> args, bool directionElseLocation) async {
|
||||
Future<WidgetMobileActionResult> _launchMap(
|
||||
List<dynamic> args, bool directionElseLocation) async {
|
||||
try {
|
||||
num? lat;
|
||||
num? lon;
|
||||
@@ -213,9 +224,11 @@ class WidgetActionHandler with HasTbContext {
|
||||
|
||||
Future<WidgetMobileActionResult> _scanQrCode() async {
|
||||
try {
|
||||
Barcode? barcode = await tbContext.navigateTo('/qrCodeScan', transition: TransitionType.nativeModal);
|
||||
Barcode? barcode = await tbContext.navigateTo('/qrCodeScan',
|
||||
transition: TransitionType.nativeModal);
|
||||
if (barcode != null && barcode.code != null) {
|
||||
return WidgetMobileActionResult.successResult(MobileActionResult.qrCode(barcode.code!, describeEnum(barcode.format)));
|
||||
return WidgetMobileActionResult.successResult(MobileActionResult.qrCode(
|
||||
barcode.code!, describeEnum(barcode.format)));
|
||||
} else {
|
||||
return WidgetMobileActionResult.emptyResult();
|
||||
}
|
||||
@@ -261,19 +274,24 @@ class WidgetActionHandler with HasTbContext {
|
||||
return WidgetMobileActionResult.errorResult(
|
||||
'Location permissions are permanently denied, we cannot request permissions.');
|
||||
}
|
||||
var position = await Geolocator.getCurrentPosition(desiredAccuracy: LocationAccuracy.high);
|
||||
return WidgetMobileActionResult.successResult(MobileActionResult.location(position.latitude, position.longitude));
|
||||
var position = await Geolocator.getCurrentPosition(
|
||||
desiredAccuracy: LocationAccuracy.high);
|
||||
return WidgetMobileActionResult.successResult(
|
||||
MobileActionResult.location(position.latitude, position.longitude));
|
||||
} catch (e) {
|
||||
return _handleError(e);
|
||||
}
|
||||
}
|
||||
|
||||
Future<WidgetMobileActionResult> _takeScreenshot(InAppWebViewController controller) async {
|
||||
Future<WidgetMobileActionResult> _takeScreenshot(
|
||||
InAppWebViewController controller) async {
|
||||
try {
|
||||
List<int>? imageBytes = await controller.takeScreenshot();
|
||||
if (imageBytes != null) {
|
||||
String imageUrl = UriData.fromBytes(imageBytes, mimeType: 'image/png').toString();
|
||||
return WidgetMobileActionResult.successResult(MobileActionResult.image(imageUrl));
|
||||
String imageUrl =
|
||||
UriData.fromBytes(imageBytes, mimeType: 'image/png').toString();
|
||||
return WidgetMobileActionResult.successResult(
|
||||
MobileActionResult.image(imageUrl));
|
||||
} else {
|
||||
return WidgetMobileActionResult.emptyResult();
|
||||
}
|
||||
@@ -283,8 +301,8 @@ class WidgetActionHandler with HasTbContext {
|
||||
}
|
||||
|
||||
Future<MobileActionResult> _tryLaunch(String url) async {
|
||||
if (await canLaunch(url)) {
|
||||
await launch(url);
|
||||
if (await canLaunchUrlString(url)) {
|
||||
await launchUrlString(url);
|
||||
return MobileActionResult.launched(true);
|
||||
} else {
|
||||
log.error('Could not launch $url');
|
||||
@@ -301,5 +319,4 @@ class WidgetActionHandler with HasTbContext {
|
||||
}
|
||||
return WidgetMobileActionResult.errorResult(error);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -1,5 +1,4 @@
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:flutter/widgets.dart';
|
||||
|
||||
class FadeOpenPageTransitionsBuilder extends PageTransitionsBuilder {
|
||||
/// Constructs a page transition animation that slides the page up.
|
||||
@@ -7,12 +6,12 @@ class FadeOpenPageTransitionsBuilder extends PageTransitionsBuilder {
|
||||
|
||||
@override
|
||||
Widget buildTransitions<T>(
|
||||
PageRoute<T>? route,
|
||||
BuildContext? context,
|
||||
Animation<double> animation,
|
||||
Animation<double>? secondaryAnimation,
|
||||
Widget child,
|
||||
) {
|
||||
PageRoute<T>? route,
|
||||
BuildContext? context,
|
||||
Animation<double> animation,
|
||||
Animation<double>? secondaryAnimation,
|
||||
Widget child,
|
||||
) {
|
||||
return FadeOpenPageTransition(routeAnimation: animation, child: child);
|
||||
}
|
||||
}
|
||||
@@ -20,9 +19,11 @@ class FadeOpenPageTransitionsBuilder extends PageTransitionsBuilder {
|
||||
class FadeOpenPageTransition extends StatelessWidget {
|
||||
FadeOpenPageTransition({
|
||||
Key? key,
|
||||
required Animation<double> routeAnimation, // The route's linear 0.0 - 1.0 animation.
|
||||
required Animation<double>
|
||||
routeAnimation, // The route's linear 0.0 - 1.0 animation.
|
||||
required this.child,
|
||||
}) : _positionAnimation = routeAnimation.drive(_leftRightTween.chain(_fastOutSlowInTween)),
|
||||
}) : _positionAnimation =
|
||||
routeAnimation.drive(_leftRightTween.chain(_fastOutSlowInTween)),
|
||||
_opacityAnimation = routeAnimation.drive(_easeInTween),
|
||||
super(key: key);
|
||||
|
||||
@@ -31,8 +32,10 @@ class FadeOpenPageTransition extends StatelessWidget {
|
||||
begin: const Offset(0.5, 0.0),
|
||||
end: Offset.zero,
|
||||
);
|
||||
static final Animatable<double> _fastOutSlowInTween = CurveTween(curve: Curves.fastOutSlowIn);
|
||||
static final Animatable<double> _easeInTween = CurveTween(curve: Curves.easeIn);
|
||||
static final Animatable<double> _fastOutSlowInTween =
|
||||
CurveTween(curve: Curves.fastOutSlowIn);
|
||||
static final Animatable<double> _easeInTween =
|
||||
CurveTween(curve: Curves.easeIn);
|
||||
|
||||
final Animation<Offset> _positionAnimation;
|
||||
final Animation<double> _opacityAnimation;
|
||||
|
||||
@@ -2,22 +2,18 @@ import 'dart:io';
|
||||
import 'dart:async';
|
||||
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:flutter/widgets.dart';
|
||||
import 'package:qr_code_scanner/qr_code_scanner.dart';
|
||||
import 'package:thingsboard_app/core/context/tb_context.dart';
|
||||
import 'package:thingsboard_app/core/context/tb_context_widget.dart';
|
||||
|
||||
class QrCodeScannerPage extends TbPageWidget {
|
||||
|
||||
QrCodeScannerPage(TbContext tbContext) : super(tbContext);
|
||||
|
||||
@override
|
||||
_QrCodeScannerPageState createState() => _QrCodeScannerPageState();
|
||||
|
||||
}
|
||||
|
||||
class _QrCodeScannerPageState extends TbPageState<QrCodeScannerPage> {
|
||||
|
||||
Timer? simulatedQrTimer;
|
||||
QRViewController? controller;
|
||||
final GlobalKey qrKey = GlobalKey(debugLabel: 'QR');
|
||||
@@ -48,64 +44,63 @@ class _QrCodeScannerPageState extends TbPageState<QrCodeScannerPage> {
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return Scaffold(
|
||||
body: Stack(
|
||||
children: [
|
||||
_buildQrView(context),
|
||||
Positioned(
|
||||
body: Stack(
|
||||
children: [
|
||||
_buildQrView(context),
|
||||
Positioned(
|
||||
bottom: 0,
|
||||
left: 0,
|
||||
right: 0,
|
||||
height: kToolbarHeight,
|
||||
child: Center(child: Text('Scan a code', style: TextStyle(color: Colors.white, fontSize: 20)))
|
||||
child: Center(
|
||||
child: Text('Scan a code',
|
||||
style: TextStyle(color: Colors.white, fontSize: 20)))),
|
||||
Positioned(
|
||||
child: AppBar(
|
||||
backgroundColor: Colors.transparent,
|
||||
foregroundColor: Colors.white,
|
||||
iconTheme: IconThemeData(color: Colors.white),
|
||||
elevation: 0,
|
||||
actions: <Widget>[
|
||||
IconButton(
|
||||
icon: FutureBuilder(
|
||||
future: controller?.getFlashStatus(),
|
||||
builder: (context, snapshot) {
|
||||
return Icon(snapshot.data == false
|
||||
? Icons.flash_on
|
||||
: Icons.flash_off);
|
||||
}),
|
||||
onPressed: () async {
|
||||
await controller?.toggleFlash();
|
||||
setState(() {});
|
||||
},
|
||||
tooltip: 'Toggle flash',
|
||||
),
|
||||
IconButton(
|
||||
icon: FutureBuilder(
|
||||
future: controller?.getCameraInfo(),
|
||||
builder: (context, snapshot) {
|
||||
return Icon(snapshot.data == CameraFacing.front
|
||||
? Icons.camera_rear
|
||||
: Icons.camera_front);
|
||||
}),
|
||||
onPressed: () async {
|
||||
await controller?.flipCamera();
|
||||
setState(() {});
|
||||
},
|
||||
tooltip: 'Toggle camera',
|
||||
),
|
||||
],
|
||||
),
|
||||
Positioned(
|
||||
child:
|
||||
AppBar(
|
||||
backgroundColor: Colors.transparent,
|
||||
foregroundColor: Colors.white,
|
||||
iconTheme: IconThemeData(
|
||||
color: Colors.white
|
||||
),
|
||||
elevation: 0,
|
||||
actions: <Widget>[
|
||||
IconButton(
|
||||
icon: FutureBuilder(
|
||||
future: controller?.getFlashStatus(),
|
||||
builder: (context, snapshot) {
|
||||
return Icon(snapshot.data == false ? Icons.flash_on : Icons.flash_off);
|
||||
}
|
||||
),
|
||||
onPressed: () async {
|
||||
await controller?.toggleFlash();
|
||||
setState(() {});
|
||||
},
|
||||
tooltip: 'Toggle flash',
|
||||
),
|
||||
IconButton(
|
||||
icon: FutureBuilder(
|
||||
future: controller?.getCameraInfo(),
|
||||
builder: (context, snapshot) {
|
||||
return Icon(snapshot.data == CameraFacing.front ? Icons.camera_rear : Icons.camera_front);
|
||||
}
|
||||
),
|
||||
onPressed: () async {
|
||||
await controller?.flipCamera();
|
||||
setState(() {});
|
||||
},
|
||||
tooltip: 'Toggle camera',
|
||||
),
|
||||
],
|
||||
),
|
||||
)
|
||||
],
|
||||
)
|
||||
);
|
||||
)
|
||||
],
|
||||
));
|
||||
}
|
||||
|
||||
Widget _buildQrView(BuildContext context) {
|
||||
// For this example we check how width or tall the device is and change the scanArea and overlay accordingly.
|
||||
var scanArea = (MediaQuery.of(context).size.width < 400 ||
|
||||
MediaQuery.of(context).size.height < 400)
|
||||
MediaQuery.of(context).size.height < 400)
|
||||
? 150.0
|
||||
: 300.0;
|
||||
// To ensure the Scanner view is properly sizes after rotation
|
||||
|
||||
@@ -5,8 +5,8 @@ import 'package:thingsboard_app/core/context/tb_context.dart';
|
||||
import 'package:thingsboard_app/utils/ui/qr_code_scanner.dart';
|
||||
|
||||
class UiUtilsRoutes extends TbRoutes {
|
||||
|
||||
late var qrCodeScannerHandler = Handler(handlerFunc: (BuildContext? context, Map<String, dynamic> params) {
|
||||
late var qrCodeScannerHandler = Handler(
|
||||
handlerFunc: (BuildContext? context, Map<String, dynamic> params) {
|
||||
return QrCodeScannerPage(tbContext);
|
||||
});
|
||||
|
||||
@@ -16,5 +16,4 @@ class UiUtilsRoutes extends TbRoutes {
|
||||
void doRegisterRoutes(router) {
|
||||
router.define("/qrCodeScan", handler: qrCodeScannerHandler);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -5,13 +5,13 @@ import 'package:flutter_svg/flutter_svg.dart';
|
||||
import 'package:thingsboard_client/thingsboard_client.dart';
|
||||
|
||||
abstract class Utils {
|
||||
|
||||
static String createDashboardEntityState(EntityId entityId, {String? entityName, String? entityLabel}) {
|
||||
var stateObj = [<String, dynamic>{
|
||||
'params': <String, dynamic>{
|
||||
'entityId': entityId.toJson()
|
||||
static String createDashboardEntityState(EntityId entityId,
|
||||
{String? entityName, String? entityLabel}) {
|
||||
var stateObj = [
|
||||
<String, dynamic>{
|
||||
'params': <String, dynamic>{'entityId': entityId.toJson()}
|
||||
}
|
||||
}];
|
||||
];
|
||||
if (entityName != null) {
|
||||
stateObj[0]['params']['entityName'] = entityName;
|
||||
}
|
||||
@@ -19,14 +19,13 @@ abstract class Utils {
|
||||
stateObj[0]['params']['entityLabel'] = entityLabel;
|
||||
}
|
||||
var stateJson = json.encode(stateObj);
|
||||
var encodedUri = Uri.encodeComponent(stateJson);
|
||||
encodedUri = encodedUri.replaceAllMapped(RegExp(r'%([0-9A-F]{2})'), (match) {
|
||||
var encodedUri = Uri.encodeComponent(stateJson);
|
||||
encodedUri =
|
||||
encodedUri.replaceAllMapped(RegExp(r'%([0-9A-F]{2})'), (match) {
|
||||
var p1 = match.group(1)!;
|
||||
return String.fromCharCode(int.parse(p1, radix: 16));
|
||||
});
|
||||
return Uri.encodeComponent(
|
||||
base64.encode(utf8.encode(encodedUri))
|
||||
);
|
||||
return Uri.encodeComponent(base64.encode(utf8.encode(encodedUri)));
|
||||
}
|
||||
|
||||
static String? contactToShortAddress(ContactBased contact) {
|
||||
@@ -47,13 +46,21 @@ abstract class Utils {
|
||||
}
|
||||
}
|
||||
|
||||
static Widget imageFromBase64(String base64, {Color? color, double? width, double? height, String? semanticLabel}) {
|
||||
static Widget imageFromBase64(String base64,
|
||||
{Color? color, double? width, double? height, String? semanticLabel}) {
|
||||
var uriData = UriData.parse(base64);
|
||||
if (uriData.mimeType == 'image/svg+xml') {
|
||||
return SvgPicture.memory(uriData.contentAsBytes(), color: color, width: width, height: height, semanticsLabel: semanticLabel);
|
||||
return SvgPicture.memory(uriData.contentAsBytes(),
|
||||
color: color,
|
||||
width: width,
|
||||
height: height,
|
||||
semanticsLabel: semanticLabel);
|
||||
} else {
|
||||
return Image.memory(uriData.contentAsBytes(), color: color, width: width, height: height, semanticLabel: semanticLabel);
|
||||
return Image.memory(uriData.contentAsBytes(),
|
||||
color: color,
|
||||
width: width,
|
||||
height: height,
|
||||
semanticLabel: semanticLabel);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user