Add flutter 3+ support. Update dependencies. Fix code style and format issues.

This commit is contained in:
Igor Kulikov
2022-08-12 13:55:27 +03:00
parent 1a07bcd7a0
commit 944c36ce7b
94 changed files with 3167 additions and 3173 deletions

View File

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

View File

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

View File

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

View File

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

View File

@@ -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';

View File

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

View File

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

View File

@@ -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

View File

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

View File

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