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

@@ -1,15 +1,11 @@
import 'package:flutter/material.dart';
import 'package:flutter/widgets.dart';
import 'package:thingsboard_app/core/context/tb_context.dart';
import 'package:thingsboard_app/core/entity/entity_details_page.dart';
import 'package:thingsboard_client/thingsboard_client.dart';
class DeviceDetailsPage extends EntityDetailsPage<DeviceInfo> {
DeviceDetailsPage(TbContext tbContext, String deviceId):
super(tbContext,
entityId: deviceId,
defaultTitle: 'Device');
DeviceDetailsPage(TbContext tbContext, String deviceId)
: super(tbContext, entityId: deviceId, defaultTitle: 'Device');
@override
Future<DeviceInfo?> fetchEntity(String deviceId) {
@@ -23,5 +19,4 @@ class DeviceDetailsPage extends EntityDetailsPage<DeviceInfo> {
subtitle: Text('${device.type}'),
);
}
}

View File

@@ -2,7 +2,6 @@ import 'dart:async';
import 'package:auto_size_text/auto_size_text.dart';
import 'package:flutter/material.dart';
import 'package:flutter/widgets.dart';
import 'package:flutter_svg/flutter_svg.dart';
import 'package:thingsboard_app/constants/assets_path.dart';
import 'package:thingsboard_app/core/context/tb_context.dart';
@@ -14,7 +13,6 @@ import 'package:thingsboard_app/utils/utils.dart';
import 'package:thingsboard_client/thingsboard_client.dart';
mixin DeviceProfilesBase on EntitiesBase<DeviceProfileInfo, PageLink> {
final RefreshDeviceCounts refreshDeviceCounts = RefreshDeviceCounts();
@override
@@ -48,7 +46,8 @@ mixin DeviceProfilesBase on EntitiesBase<DeviceProfileInfo, PageLink> {
}
@override
Widget buildEntityGridCard(BuildContext context, DeviceProfileInfo deviceProfile) {
Widget buildEntityGridCard(
BuildContext context, DeviceProfileInfo deviceProfile) {
return DeviceProfileCard(tbContext, deviceProfile);
}
@@ -56,7 +55,6 @@ mixin DeviceProfilesBase on EntitiesBase<DeviceProfileInfo, PageLink> {
double? gridChildAspectRatio() {
return 156 / 200;
}
}
class RefreshDeviceCounts {
@@ -64,20 +62,20 @@ class RefreshDeviceCounts {
}
class AllDevicesCard extends TbContextWidget {
final RefreshDeviceCounts refreshDeviceCounts;
AllDevicesCard(TbContext tbContext, this.refreshDeviceCounts) : super(tbContext);
AllDevicesCard(TbContext tbContext, this.refreshDeviceCounts)
: super(tbContext);
@override
_AllDevicesCardState createState() => _AllDevicesCardState();
}
class _AllDevicesCardState extends TbContextState<AllDevicesCard> {
final StreamController<int?> _activeDevicesCount = StreamController.broadcast();
final StreamController<int?> _inactiveDevicesCount = StreamController.broadcast();
final StreamController<int?> _activeDevicesCount =
StreamController.broadcast();
final StreamController<int?> _inactiveDevicesCount =
StreamController.broadcast();
@override
void initState() {
@@ -103,9 +101,12 @@ class _AllDevicesCardState extends TbContextState<AllDevicesCard> {
Future<void> _countDevices() {
_activeDevicesCount.add(null);
_inactiveDevicesCount.add(null);
Future<int> activeDevicesCount = EntityQueryApi.countDevices(tbClient, active: true);
Future<int> inactiveDevicesCount = EntityQueryApi.countDevices(tbClient, active: false);
Future<List<int>> countsFuture = Future.wait([activeDevicesCount, inactiveDevicesCount]);
Future<int> activeDevicesCount =
EntityQueryApi.countDevices(tbClient, active: true);
Future<int> inactiveDevicesCount =
EntityQueryApi.countDevices(tbClient, active: false);
Future<List<int>> countsFuture =
Future.wait([activeDevicesCount, inactiveDevicesCount]);
countsFuture.then((counts) {
if (this.mounted) {
_activeDevicesCount.add(counts[0]);
@@ -117,20 +118,19 @@ class _AllDevicesCardState extends TbContextState<AllDevicesCard> {
@override
Widget build(BuildContext context) {
return
GestureDetector(
behavior: HitTestBehavior.opaque,
child:
Container(
child: Card(
margin: EdgeInsets.zero,
shape: RoundedRectangleBorder(
borderRadius: BorderRadius.circular(4),
),
elevation: 0,
child: Column(
children: [
Padding(padding: EdgeInsets.fromLTRB(16, 12, 16, 15),
return GestureDetector(
behavior: HitTestBehavior.opaque,
child: Container(
child: Card(
margin: EdgeInsets.zero,
shape: RoundedRectangleBorder(
borderRadius: BorderRadius.circular(4),
),
elevation: 0,
child: Column(
children: [
Padding(
padding: EdgeInsets.fromLTRB(16, 12, 16, 15),
child: Row(
mainAxisSize: MainAxisSize.max,
mainAxisAlignment: MainAxisAlignment.spaceBetween,
@@ -139,120 +139,126 @@ class _AllDevicesCardState extends TbContextState<AllDevicesCard> {
style: TextStyle(
fontWeight: FontWeight.w500,
fontSize: 14,
height: 20 / 14
)
),
height: 20 / 14)),
Icon(Icons.arrow_forward, size: 18)
],
)
),
Divider(height: 1),
Padding(padding: EdgeInsets.all(0),
child: Row(
mainAxisSize: MainAxisSize.max,
children: [
Flexible(fit: FlexFit.tight,
child: GestureDetector(
behavior: HitTestBehavior.opaque,
child: Container(
height: 40,
decoration: BoxDecoration(
color: Colors.white,
borderRadius: BorderRadius.circular(4),
),
child: StreamBuilder<int?>(
stream: _activeDevicesCount.stream,
builder: (context, snapshot) {
if (snapshot.hasData) {
var deviceCount = snapshot.data!;
return _buildDeviceCount(context, true, deviceCount);
} else {
return Center(child:
Container(height: 20, width: 20,
child: CircularProgressIndicator(
valueColor: AlwaysStoppedAnimation(Theme.of(tbContext.currentState!.context).colorScheme.primary),
strokeWidth: 2.5)));
}
},
)
),
onTap: () {
navigateTo('/deviceList?active=true');
}
),
),
// SizedBox(width: 4),
Container(width: 1,
height: 40,
child: VerticalDivider(width: 1)
),
Flexible(fit: FlexFit.tight,
child: GestureDetector(
behavior: HitTestBehavior.opaque,
child: Container(
height: 40,
decoration: BoxDecoration(
color: Colors.white,
borderRadius: BorderRadius.circular(4),
),
child: StreamBuilder<int?>(
stream: _inactiveDevicesCount.stream,
builder: (context, snapshot) {
if (snapshot.hasData) {
var deviceCount = snapshot.data!;
return _buildDeviceCount(context, false, deviceCount);
} else {
return Center(child:
Container(height: 20, width: 20,
child: CircularProgressIndicator(
valueColor: AlwaysStoppedAnimation(Theme.of(tbContext.currentState!.context).colorScheme.primary),
strokeWidth: 2.5)));
}
},
)
),
onTap: () {
navigateTo('/deviceList?active=false');
}
),
)
],
)
)
],
)
),
decoration: BoxDecoration(
boxShadow: [
BoxShadow(
color: Colors.black.withAlpha((255 * 0.05).ceil()),
blurRadius: 6.0,
offset: Offset(0, 4)
)
],
),
)),
Divider(height: 1),
Padding(
padding: EdgeInsets.all(0),
child: Row(
mainAxisSize: MainAxisSize.max,
children: [
Flexible(
fit: FlexFit.tight,
child: GestureDetector(
behavior: HitTestBehavior.opaque,
child: Container(
height: 40,
decoration: BoxDecoration(
color: Colors.white,
borderRadius: BorderRadius.circular(4),
),
child: StreamBuilder<int?>(
stream: _activeDevicesCount.stream,
builder: (context, snapshot) {
if (snapshot.hasData) {
var deviceCount = snapshot.data!;
return _buildDeviceCount(
context, true, deviceCount);
} else {
return Center(
child: Container(
height: 20,
width: 20,
child: CircularProgressIndicator(
valueColor:
AlwaysStoppedAnimation(
Theme.of(tbContext
.currentState!
.context)
.colorScheme
.primary),
strokeWidth: 2.5)));
}
},
)),
onTap: () {
navigateTo('/deviceList?active=true');
}),
),
// SizedBox(width: 4),
Container(
width: 1,
height: 40,
child: VerticalDivider(width: 1)),
Flexible(
fit: FlexFit.tight,
child: GestureDetector(
behavior: HitTestBehavior.opaque,
child: Container(
height: 40,
decoration: BoxDecoration(
color: Colors.white,
borderRadius: BorderRadius.circular(4),
),
child: StreamBuilder<int?>(
stream: _inactiveDevicesCount.stream,
builder: (context, snapshot) {
if (snapshot.hasData) {
var deviceCount = snapshot.data!;
return _buildDeviceCount(
context, false, deviceCount);
} else {
return Center(
child: Container(
height: 20,
width: 20,
child: CircularProgressIndicator(
valueColor:
AlwaysStoppedAnimation(
Theme.of(tbContext
.currentState!
.context)
.colorScheme
.primary),
strokeWidth: 2.5)));
}
},
)),
onTap: () {
navigateTo('/deviceList?active=false');
}),
)
],
))
],
)),
decoration: BoxDecoration(
boxShadow: [
BoxShadow(
color: Colors.black.withAlpha((255 * 0.05).ceil()),
blurRadius: 6.0,
offset: Offset(0, 4))
],
),
onTap: () {
navigateTo('/deviceList');
}
);
),
onTap: () {
navigateTo('/deviceList');
});
}
}
class DeviceProfileCard extends TbContextWidget {
final DeviceProfileInfo deviceProfile;
DeviceProfileCard(TbContext tbContext, this.deviceProfile) : super(tbContext);
@override
_DeviceProfileCardState createState() => _DeviceProfileCardState();
}
class _DeviceProfileCardState extends TbContextState<DeviceProfileCard> {
late Future<int> activeDevicesCount;
late Future<int> inactiveDevicesCount;
@@ -269,8 +275,10 @@ class _DeviceProfileCardState extends TbContextState<DeviceProfileCard> {
}
_countDevices() {
activeDevicesCount = EntityQueryApi.countDevices(tbClient, deviceType: widget.deviceProfile.name, active: true);
inactiveDevicesCount = EntityQueryApi.countDevices(tbClient, deviceType: widget.deviceProfile.name, active: false);
activeDevicesCount = EntityQueryApi.countDevices(tbClient,
deviceType: widget.deviceProfile.name, active: true);
inactiveDevicesCount = EntityQueryApi.countDevices(tbClient,
deviceType: widget.deviceProfile.name, active: false);
}
@override
@@ -292,99 +300,95 @@ class _DeviceProfileCardState extends TbContextState<DeviceProfileCard> {
imageFit = BoxFit.cover;
padding = 0;
}
return
ClipRRect(
borderRadius: BorderRadius.circular(4),
child: Column(
children: [
Expanded(
child: Stack (
children: [
SizedBox.expand(
child: Padding(
padding: EdgeInsets.all(padding),
child: FittedBox(
clipBehavior: Clip.hardEdge,
fit: imageFit,
child: image
)
)
)
]
)
),
Container(
height: 44,
child: Padding(
padding: EdgeInsets.symmetric(horizontal: 6),
child: Center(
child: AutoSizeText(entity.name,
textAlign: TextAlign.center,
maxLines: 1,
minFontSize: 12,
overflow: TextOverflow.ellipsis,
style: TextStyle(
fontWeight: FontWeight.w500,
fontSize: 14,
height: 20 / 14
),
)
)
)
),
Divider(height: 1),
GestureDetector(
behavior: HitTestBehavior.opaque,
child: FutureBuilder<int>(
future: activeDevicesCount,
builder: (context, snapshot) {
if (snapshot.hasData && snapshot.connectionState == ConnectionState.done) {
var deviceCount = snapshot.data!;
return _buildDeviceCount(context, true, deviceCount);
} else {
return Container(height: 40,
child: Center(
child: Container(
height: 20, width: 20,
child:
CircularProgressIndicator(
valueColor: AlwaysStoppedAnimation(Theme.of(tbContext.currentState!.context).colorScheme.primary),
strokeWidth: 2.5))));
}
},
),
onTap: () {
navigateTo('/deviceList?active=true&deviceType=${entity.name}');
}
),
Divider(height: 1),
GestureDetector(
behavior: HitTestBehavior.opaque,
child: FutureBuilder<int>(
future: inactiveDevicesCount,
builder: (context, snapshot) {
if (snapshot.hasData && snapshot.connectionState == ConnectionState.done) {
var deviceCount = snapshot.data!;
return _buildDeviceCount(context, false, deviceCount);
} else {
return Container(height: 40,
child: Center(
child: Container(
height: 20, width: 20,
child:
CircularProgressIndicator(
valueColor: AlwaysStoppedAnimation(Theme.of(tbContext.currentState!.context).colorScheme.primary),
strokeWidth: 2.5))));
}
},
),
onTap: () {
navigateTo('/deviceList?active=false&deviceType=${entity.name}');
}
)
]
)
);
return ClipRRect(
borderRadius: BorderRadius.circular(4),
child: Column(children: [
Expanded(
child: Stack(children: [
SizedBox.expand(
child: Padding(
padding: EdgeInsets.all(padding),
child: FittedBox(
clipBehavior: Clip.hardEdge,
fit: imageFit,
child: image)))
])),
Container(
height: 44,
child: Padding(
padding: EdgeInsets.symmetric(horizontal: 6),
child: Center(
child: AutoSizeText(
entity.name,
textAlign: TextAlign.center,
maxLines: 1,
minFontSize: 12,
overflow: TextOverflow.ellipsis,
style: TextStyle(
fontWeight: FontWeight.w500,
fontSize: 14,
height: 20 / 14),
)))),
Divider(height: 1),
GestureDetector(
behavior: HitTestBehavior.opaque,
child: FutureBuilder<int>(
future: activeDevicesCount,
builder: (context, snapshot) {
if (snapshot.hasData &&
snapshot.connectionState == ConnectionState.done) {
var deviceCount = snapshot.data!;
return _buildDeviceCount(context, true, deviceCount);
} else {
return Container(
height: 40,
child: Center(
child: Container(
height: 20,
width: 20,
child: CircularProgressIndicator(
valueColor: AlwaysStoppedAnimation(Theme.of(
tbContext.currentState!.context)
.colorScheme
.primary),
strokeWidth: 2.5))));
}
},
),
onTap: () {
navigateTo('/deviceList?active=true&deviceType=${entity.name}');
}),
Divider(height: 1),
GestureDetector(
behavior: HitTestBehavior.opaque,
child: FutureBuilder<int>(
future: inactiveDevicesCount,
builder: (context, snapshot) {
if (snapshot.hasData &&
snapshot.connectionState == ConnectionState.done) {
var deviceCount = snapshot.data!;
return _buildDeviceCount(context, false, deviceCount);
} else {
return Container(
height: 40,
child: Center(
child: Container(
height: 20,
width: 20,
child: CircularProgressIndicator(
valueColor: AlwaysStoppedAnimation(Theme.of(
tbContext.currentState!.context)
.colorScheme
.primary),
strokeWidth: 2.5))));
}
},
),
onTap: () {
navigateTo(
'/deviceList?active=false&deviceType=${entity.name}');
})
]));
}
}
@@ -402,26 +406,27 @@ Widget _buildDeviceCount(BuildContext context, bool active, int count) {
Stack(
children: [
Icon(Icons.devices_other, size: 16, color: color),
if (!active) CustomPaint(
size: Size.square(16),
painter: StrikeThroughPainter(color: color, offset: 2),
)
if (!active)
CustomPaint(
size: Size.square(16),
painter: StrikeThroughPainter(color: color, offset: 2),
)
],
),
SizedBox(width: 8.67),
Text(active ? 'Active' : 'Inactive', style: TextStyle(
fontSize: 12,
fontWeight: FontWeight.w500,
height: 16 / 12,
color: color
)),
SizedBox(width: 8.67),
Text(count.toString(), style: TextStyle(
fontSize: 12,
fontWeight: FontWeight.w500,
height: 16 / 12,
color: color
))
SizedBox(width: 8.67),
Text(active ? 'Active' : 'Inactive',
style: TextStyle(
fontSize: 12,
fontWeight: FontWeight.w500,
height: 16 / 12,
color: color)),
SizedBox(width: 8.67),
Text(count.toString(),
style: TextStyle(
fontSize: 12,
fontWeight: FontWeight.w500,
height: 16 / 12,
color: color))
],
),
Icon(Icons.chevron_right, size: 16, color: Color(0xFFACACAC))
@@ -431,7 +436,6 @@ Widget _buildDeviceCount(BuildContext context, bool active, int count) {
}
class StrikeThroughPainter extends CustomPainter {
final Color color;
final double offset;
@@ -441,7 +445,8 @@ class StrikeThroughPainter extends CustomPainter {
void paint(Canvas canvas, Size size) {
final paint = Paint()..color = color;
paint.strokeWidth = 1.5;
canvas.drawLine(Offset(offset, offset), Offset(size.width - offset, size.height - offset), paint);
canvas.drawLine(Offset(offset, offset),
Offset(size.width - offset, size.height - offset), paint);
paint.color = Colors.white;
canvas.drawLine(Offset(2, 0), Offset(size.width + 2, size.height), paint);
}
@@ -450,5 +455,4 @@ class StrikeThroughPainter extends CustomPainter {
bool shouldRepaint(covariant StrikeThroughPainter oldDelegate) {
return color != oldDelegate.color;
}
}

View File

@@ -5,8 +5,9 @@ import 'package:thingsboard_client/thingsboard_client.dart';
import 'device_profiles_base.dart';
class DeviceProfilesGrid extends BaseEntitiesWidget<DeviceProfileInfo, PageLink> with DeviceProfilesBase, EntitiesGridStateBase {
DeviceProfilesGrid(TbContext tbContext, PageKeyController<PageLink> pageKeyController) : super(tbContext, pageKeyController);
class DeviceProfilesGrid extends BaseEntitiesWidget<DeviceProfileInfo, PageLink>
with DeviceProfilesBase, EntitiesGridStateBase {
DeviceProfilesGrid(
TbContext tbContext, PageKeyController<PageLink> pageKeyController)
: super(tbContext, pageKeyController);
}

View File

@@ -9,24 +9,28 @@ import 'device_details_page.dart';
import 'devices_list_page.dart';
class DeviceRoutes extends TbRoutes {
late var devicesHandler = Handler(handlerFunc: (BuildContext? context, Map<String, dynamic> params) {
late var devicesHandler = Handler(
handlerFunc: (BuildContext? context, Map<String, dynamic> params) {
return MainPage(tbContext, path: '/devices');
});
late var devicesPageHandler = Handler(handlerFunc: (BuildContext? context, Map<String, dynamic> params) {
late var devicesPageHandler = Handler(
handlerFunc: (BuildContext? context, Map<String, dynamic> params) {
return DevicesPage(tbContext);
});
late var deviceListHandler = Handler(handlerFunc: (BuildContext? context, Map<String, dynamic> params) {
late var deviceListHandler = Handler(
handlerFunc: (BuildContext? context, Map<String, dynamic> params) {
var searchMode = params['search']?.first == 'true';
var deviceType = params['deviceType']?.first;
String? activeStr = params['active']?.first;
bool? active = activeStr != null ? activeStr == 'true' : null;
return DevicesListPage(tbContext, searchMode: searchMode, deviceType: deviceType, active: active);
return DevicesListPage(tbContext,
searchMode: searchMode, deviceType: deviceType, active: active);
});
late var deviceDetailsHandler = Handler(handlerFunc: (BuildContext? context, Map<String, dynamic> params) {
late var deviceDetailsHandler = Handler(
handlerFunc: (BuildContext? context, Map<String, dynamic> params) {
return DeviceDetailsPage(tbContext, params["id"][0]);
});
@@ -39,5 +43,4 @@ class DeviceRoutes extends TbRoutes {
router.define("/deviceList", handler: deviceListHandler);
router.define("/device/:id", handler: deviceDetailsHandler);
}
}

View File

@@ -1,7 +1,6 @@
import 'dart:core';
import 'package:flutter/material.dart';
import 'package:flutter/widgets.dart';
import 'package:flutter_svg/flutter_svg.dart';
import 'package:intl/intl.dart';
import 'package:thingsboard_app/constants/assets_path.dart';
@@ -14,7 +13,6 @@ import 'package:thingsboard_app/utils/utils.dart';
import 'package:thingsboard_client/thingsboard_client.dart';
mixin DevicesBase on EntitiesBase<EntityData, EntityDataQuery> {
@override
String get title => 'Devices';
@@ -28,14 +26,19 @@ mixin DevicesBase on EntitiesBase<EntityData, EntityDataQuery> {
@override
void onEntityTap(EntityData device) async {
var profile = await DeviceProfileCache.getDeviceProfileInfo(tbClient, device.field('type')!, device.entityId.id!);
var profile = await DeviceProfileCache.getDeviceProfileInfo(
tbClient, device.field('type')!, device.entityId.id!);
if (profile.defaultDashboardId != null) {
var dashboardId = profile.defaultDashboardId!.id!;
var state = Utils.createDashboardEntityState(device.entityId, entityName: device.field('name')!, entityLabel: device.field('label')!);
navigateToDashboard(dashboardId, dashboardTitle: device.field('name'), state: state);
var state = Utils.createDashboardEntityState(device.entityId,
entityName: device.field('name')!,
entityLabel: device.field('label')!);
navigateToDashboard(dashboardId,
dashboardTitle: device.field('name'), state: state);
} else {
if (tbClient.isTenantAdmin()) {
showWarnNotification('Mobile dashboard should be configured in device profile!');
showWarnNotification(
'Mobile dashboard should be configured in device profile!');
}
}
}
@@ -57,42 +60,51 @@ mixin DevicesBase on EntitiesBase<EntityData, EntityDataQuery> {
bool displayCardImage(bool listWidgetCard) => listWidgetCard;
Widget _buildEntityListCard(BuildContext context, EntityData device, bool listWidgetCard) {
return DeviceCard(tbContext, device: device, listWidgetCard: listWidgetCard, displayImage: displayCardImage(listWidgetCard));
Widget _buildEntityListCard(
BuildContext context, EntityData device, bool listWidgetCard) {
return DeviceCard(tbContext,
device: device,
listWidgetCard: listWidgetCard,
displayImage: displayCardImage(listWidgetCard));
}
}
class DeviceQueryController extends PageKeyController<EntityDataQuery> {
DeviceQueryController({int pageSize = 20, String? searchText, String? deviceType, bool? active}):
super(EntityQueryApi.createDefaultDeviceQuery(pageSize: pageSize, searchText: searchText, deviceType: deviceType, active: active));
DeviceQueryController(
{int pageSize = 20, String? searchText, String? deviceType, bool? active})
: super(EntityQueryApi.createDefaultDeviceQuery(
pageSize: pageSize,
searchText: searchText,
deviceType: deviceType,
active: active));
@override
EntityDataQuery nextPageKey(EntityDataQuery deviceQuery) => deviceQuery.next();
EntityDataQuery nextPageKey(EntityDataQuery deviceQuery) =>
deviceQuery.next();
onSearchText(String searchText) {
value.pageKey.pageLink.page = 0;
value.pageKey.pageLink.textSearch = searchText;
notifyListeners();
}
}
class DeviceCard extends TbContextWidget {
final EntityData device;
final bool listWidgetCard;
final bool displayImage;
DeviceCard(TbContext tbContext, {required this.device, this.listWidgetCard = false, this.displayImage = false}) : super(tbContext);
DeviceCard(TbContext tbContext,
{required this.device,
this.listWidgetCard = false,
this.displayImage = false})
: super(tbContext);
@override
_DeviceCardState createState() => _DeviceCardState();
}
class _DeviceCardState extends TbContextState<DeviceCard> {
final entityDateFormat = DateFormat('yyyy-MM-dd');
late Future<DeviceProfileInfo> deviceProfileFuture;
@@ -129,250 +141,256 @@ class _DeviceCardState extends TbContextState<DeviceCard> {
}
Widget buildCard(BuildContext context) {
return Stack(
children: [
Positioned.fill(
return Stack(children: [
Positioned.fill(
child: Container(
alignment: Alignment.centerLeft,
child: Container(
alignment: Alignment.centerLeft,
child: Container(
width: 4,
decoration: BoxDecoration(
color: widget.device.attribute('active') == 'true' ? Color(0xFF008A00) : Color(0xFFAFAFAF),
borderRadius: BorderRadius.only(topLeft: Radius.circular(4), bottomLeft: Radius.circular(4))
),
)
)
),
FutureBuilder<DeviceProfileInfo>(
width: 4,
decoration: BoxDecoration(
color: widget.device.attribute('active') == 'true'
? Color(0xFF008A00)
: Color(0xFFAFAFAF),
borderRadius: BorderRadius.only(
topLeft: Radius.circular(4),
bottomLeft: Radius.circular(4))),
))),
FutureBuilder<DeviceProfileInfo>(
future: deviceProfileFuture,
builder: (context, snapshot) {
if (snapshot.hasData &&
snapshot.connectionState == ConnectionState.done) {
var profile = snapshot.data!;
bool hasDashboard = profile.defaultDashboardId != null;
Widget image;
BoxFit imageFit;
if (profile.image != null) {
image = Utils.imageFromBase64(profile.image!);
imageFit = BoxFit.contain;
} else {
image = SvgPicture.asset(
ThingsboardImage.deviceProfilePlaceholder,
color: Theme.of(context).primaryColor,
colorBlendMode: BlendMode.overlay,
semanticsLabel: 'Device');
imageFit = BoxFit.cover;
}
return Row(
mainAxisSize: MainAxisSize.max,
crossAxisAlignment: CrossAxisAlignment.center,
children: [
SizedBox(width: 20),
Flexible(
fit: FlexFit.tight,
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
SizedBox(height: 12),
Row(
mainAxisSize: MainAxisSize.max,
crossAxisAlignment: CrossAxisAlignment.center,
children: [
if (widget.displayImage)
Container(
width: 40,
height: 40,
decoration: BoxDecoration(
borderRadius: BorderRadius.all(
Radius.circular(4))),
child: ClipRRect(
borderRadius: BorderRadius.all(
Radius.circular(4)),
child: Stack(
children: [
Positioned.fill(
child: FittedBox(
fit: imageFit,
child: image,
))
],
))),
SizedBox(width: 12),
Flexible(
fit: FlexFit.tight,
child: Column(children: [
Row(
mainAxisSize: MainAxisSize.max,
mainAxisAlignment:
MainAxisAlignment.spaceBetween,
children: [
Flexible(
fit: FlexFit.tight,
child: FittedBox(
fit: BoxFit.scaleDown,
alignment:
Alignment.centerLeft,
child: Text(
'${widget.device.field('name')!}',
style: TextStyle(
color: Color(
0xFF282828),
fontSize: 14,
fontWeight:
FontWeight
.w500,
height:
20 / 14)))),
SizedBox(width: 12),
Text(
entityDateFormat.format(DateTime
.fromMillisecondsSinceEpoch(
widget.device
.createdTime!)),
style: TextStyle(
color: Color(0xFFAFAFAF),
fontSize: 12,
fontWeight:
FontWeight.normal,
height: 16 / 12))
]),
SizedBox(height: 4),
Row(
mainAxisSize: MainAxisSize.max,
mainAxisAlignment:
MainAxisAlignment.spaceBetween,
children: [
Text(
'${widget.device.field('type')!}',
style: TextStyle(
color: Color(0xFFAFAFAF),
fontSize: 12,
fontWeight:
FontWeight.normal,
height: 16 / 12)),
Text(
widget.device.attribute(
'active') ==
'true'
? 'Active'
: 'Inactive',
style: TextStyle(
color: widget.device
.attribute(
'active') ==
'true'
? Color(0xFF008A00)
: Color(0xFFAFAFAF),
fontSize: 12,
height: 16 / 12,
fontWeight: FontWeight.normal,
))
],
)
])),
SizedBox(width: 16),
if (hasDashboard)
Icon(Icons.chevron_right,
color: Color(0xFFACACAC)),
if (hasDashboard) SizedBox(width: 16),
]),
SizedBox(height: 12)
],
))
]);
} else {
return Container(
height: 64,
child: Center(
child: RefreshProgressIndicator(
valueColor: AlwaysStoppedAnimation(
Theme.of(tbContext.currentState!.context)
.colorScheme
.primary))));
}
})
]);
}
Widget buildListWidgetCard(BuildContext context) {
return Row(mainAxisSize: MainAxisSize.min, children: [
if (widget.displayImage)
Container(
width: 58,
height: 58,
decoration: BoxDecoration(
// color: Color(0xFFEEEEEE),
borderRadius: BorderRadius.horizontal(left: Radius.circular(4))),
child: FutureBuilder<DeviceProfileInfo>(
future: deviceProfileFuture,
builder: (context, snapshot) {
if (snapshot.hasData && snapshot.connectionState == ConnectionState.done) {
if (snapshot.hasData &&
snapshot.connectionState == ConnectionState.done) {
var profile = snapshot.data!;
bool hasDashboard = profile.defaultDashboardId != null;
Widget image;
BoxFit imageFit;
if (profile.image != null) {
image = Utils.imageFromBase64(profile.image!);
imageFit = BoxFit.contain;
} else {
image = SvgPicture.asset(ThingsboardImage.deviceProfilePlaceholder,
image = SvgPicture.asset(
ThingsboardImage.deviceProfilePlaceholder,
color: Theme.of(context).primaryColor,
colorBlendMode: BlendMode.overlay,
semanticsLabel: 'Device');
imageFit = BoxFit.cover;
}
return Row(
mainAxisSize: MainAxisSize.max,
crossAxisAlignment: CrossAxisAlignment.center,
children: [
SizedBox(width: 20),
Flexible(
fit: FlexFit.tight,
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
SizedBox(height: 12),
Row(
mainAxisSize: MainAxisSize.max,
crossAxisAlignment: CrossAxisAlignment.center,
children: [
if (widget.displayImage) Container(
width: 40,
height: 40,
decoration: BoxDecoration(
borderRadius: BorderRadius.all(Radius.circular(4))
),
child: ClipRRect(
borderRadius: BorderRadius.all(Radius.circular(4)),
child: Stack(
children: [
Positioned.fill(
child: FittedBox(
fit: imageFit,
child: image,
)
)
],
)
)
),
SizedBox(width: 12),
Flexible(
fit: FlexFit.tight,
child: Column(
children: [
Row(
mainAxisSize: MainAxisSize.max,
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: [
Flexible(
fit: FlexFit.tight,
child: FittedBox(
fit: BoxFit.scaleDown,
alignment: Alignment.centerLeft,
child: Text('${widget.device.field('name')!}',
style: TextStyle(
color: Color(0xFF282828),
fontSize: 14,
fontWeight: FontWeight.w500,
height: 20 / 14
))
)
),
SizedBox(width: 12),
Text(entityDateFormat.format(DateTime.fromMillisecondsSinceEpoch(widget.device.createdTime!)),
style: TextStyle(
color: Color(0xFFAFAFAF),
fontSize: 12,
fontWeight: FontWeight.normal,
height: 16 / 12
))
]
),
SizedBox(height: 4),
Row(
mainAxisSize: MainAxisSize.max,
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: [
Text('${widget.device.field('type')!}',
style: TextStyle(
color: Color(0xFFAFAFAF),
fontSize: 12,
fontWeight: FontWeight.normal,
height: 16 / 12
)),
Text(widget.device.attribute('active') == 'true' ? 'Active' : 'Inactive',
style: TextStyle(
color: widget.device.attribute('active') == 'true' ? Color(0xFF008A00) : Color(0xFFAFAFAF),
fontSize: 12,
height: 16 / 12,
fontWeight: FontWeight.normal,
))
],
)
]
)
),
SizedBox(width: 16),
if (hasDashboard) Icon(Icons.chevron_right, color: Color(0xFFACACAC)),
if (hasDashboard) SizedBox(width: 16),
]
),
SizedBox(height: 12)
],
)
)
]
);
return ClipRRect(
borderRadius:
BorderRadius.horizontal(left: Radius.circular(4)),
child: Stack(
children: [
Positioned.fill(
child: FittedBox(
fit: imageFit,
child: image,
))
],
));
} else {
return Container(
height: 64,
child: Center(
child: RefreshProgressIndicator(
valueColor: AlwaysStoppedAnimation(Theme.of(tbContext.currentState!.context).colorScheme.primary)
)
)
);
return Center(
child: RefreshProgressIndicator(
valueColor: AlwaysStoppedAnimation(
Theme.of(tbContext.currentState!.context)
.colorScheme
.primary)));
}
}
)
]
);
}
Widget buildListWidgetCard(BuildContext context) {
return Row(
mainAxisSize: MainAxisSize.min,
children: [
if (widget.displayImage) Container(
width: 58,
height: 58,
decoration: BoxDecoration(
// color: Color(0xFFEEEEEE),
borderRadius: BorderRadius.horizontal(left: Radius.circular(4))
),
child: FutureBuilder<DeviceProfileInfo>(
future: deviceProfileFuture,
builder: (context, snapshot) {
if (snapshot.hasData && snapshot.connectionState == ConnectionState.done) {
var profile = snapshot.data!;
Widget image;
BoxFit imageFit;
if (profile.image != null) {
image = Utils.imageFromBase64(profile.image!);
imageFit = BoxFit.contain;
} else {
image = SvgPicture.asset(ThingsboardImage.deviceProfilePlaceholder,
color: Theme.of(context).primaryColor,
colorBlendMode: BlendMode.overlay,
semanticsLabel: 'Device');
imageFit = BoxFit.cover;
}
return ClipRRect(
borderRadius: BorderRadius.horizontal(left: Radius.circular(4)),
child: Stack(
children: [
Positioned.fill(
child: FittedBox(
fit: imageFit,
child: image,
)
)
],
)
);
} else {
return Center(child: RefreshProgressIndicator(
valueColor: AlwaysStoppedAnimation(Theme.of(tbContext.currentState!.context).colorScheme.primary)
));
}
},
),
},
),
Flexible(
fit: FlexFit.loose,
child:
Container(
padding: EdgeInsets.symmetric(vertical: 9, horizontal: 16),
child: Column(
children: [
Row(
mainAxisSize: MainAxisSize.min,
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: [
FittedBox(
fit: BoxFit.fitWidth,
alignment: Alignment.centerLeft,
child: Text('${widget.device.field('name')!}',
style: TextStyle(
color: Color(0xFF282828),
fontSize: 14,
fontWeight: FontWeight.w500,
height: 20 / 14
))
)
]
),
SizedBox(height: 4),
Row(
mainAxisSize: MainAxisSize.min,
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: [
Text('${widget.device.field('type')!}',
style: TextStyle(
color: Color(0xFFAFAFAF),
fontSize: 12,
fontWeight: FontWeight.normal,
height: 16 / 12
)),
]
)
],
)
)
)
]
);
),
Flexible(
fit: FlexFit.loose,
child: Container(
padding: EdgeInsets.symmetric(vertical: 9, horizontal: 16),
child: Column(
children: [
Row(
mainAxisSize: MainAxisSize.min,
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: [
FittedBox(
fit: BoxFit.fitWidth,
alignment: Alignment.centerLeft,
child: Text('${widget.device.field('name')!}',
style: TextStyle(
color: Color(0xFF282828),
fontSize: 14,
fontWeight: FontWeight.w500,
height: 20 / 14)))
]),
SizedBox(height: 4),
Row(
mainAxisSize: MainAxisSize.min,
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: [
Text('${widget.device.field('type')!}',
style: TextStyle(
color: Color(0xFFAFAFAF),
fontSize: 12,
fontWeight: FontWeight.normal,
height: 16 / 12)),
])
],
)))
]);
}
}

View File

@@ -4,15 +4,15 @@ import 'package:thingsboard_app/core/entity/entities_list.dart';
import 'package:thingsboard_app/modules/device/devices_base.dart';
import 'package:thingsboard_client/thingsboard_client.dart';
class DevicesList extends BaseEntitiesWidget<EntityData, EntityDataQuery> with DevicesBase, EntitiesListStateBase {
class DevicesList extends BaseEntitiesWidget<EntityData, EntityDataQuery>
with DevicesBase, EntitiesListStateBase {
final bool displayDeviceImage;
DevicesList(TbContext tbContext, PageKeyController<EntityDataQuery> pageKeyController, {searchMode = false, this.displayDeviceImage = false}):
super(tbContext, pageKeyController, searchMode: searchMode);
DevicesList(
TbContext tbContext, PageKeyController<EntityDataQuery> pageKeyController,
{searchMode = false, this.displayDeviceImage = false})
: super(tbContext, pageKeyController, searchMode: searchMode);
@override
bool displayCardImage(bool listWidgetCard) => displayDeviceImage;
}

View File

@@ -6,87 +6,85 @@ import 'package:thingsboard_app/modules/device/devices_list.dart';
import 'package:thingsboard_app/widgets/tb_app_bar.dart';
class DevicesListPage extends TbPageWidget {
final String? deviceType;
final bool? active;
final bool searchMode;
DevicesListPage(TbContext tbContext, {this.deviceType, this.active, this.searchMode = false}) : super(tbContext);
DevicesListPage(TbContext tbContext,
{this.deviceType, this.active, this.searchMode = false})
: super(tbContext);
@override
_DevicesListPageState createState() => _DevicesListPageState();
}
class _DevicesListPageState extends TbPageState<DevicesListPage> {
late final DeviceQueryController _deviceQueryController;
@override
void initState() {
super.initState();
_deviceQueryController = DeviceQueryController(deviceType: widget.deviceType, active: widget.active);
_deviceQueryController = DeviceQueryController(
deviceType: widget.deviceType, active: widget.active);
}
@override
Widget build(BuildContext context) {
var devicesList = DevicesList(tbContext, _deviceQueryController, searchMode: widget.searchMode, displayDeviceImage: widget.deviceType == null);
var devicesList = DevicesList(tbContext, _deviceQueryController,
searchMode: widget.searchMode,
displayDeviceImage: widget.deviceType == null);
PreferredSizeWidget appBar;
if (widget.searchMode) {
appBar = TbAppSearchBar(
tbContext,
onSearch: (searchText) => _deviceQueryController.onSearchText(searchText),
onSearch: (searchText) =>
_deviceQueryController.onSearchText(searchText),
);
} else {
String titleText = widget.deviceType != null ? widget.deviceType! : 'All devices';
String titleText =
widget.deviceType != null ? widget.deviceType! : 'All devices';
String? subTitleText;
if (widget.active != null) {
subTitleText = widget.active == true ? 'Active' : 'Inactive';
}
Column title = Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Text(titleText, style: TextStyle(
fontWeight: FontWeight.w500,
fontSize: subTitleText != null ? 16 : 20,
height: subTitleText != null ? 20 / 16 : 24 / 20
)),
if (subTitleText != null)
Text(subTitleText, style: TextStyle(
color: Theme.of(context).primaryTextTheme.headline6!.color!.withAlpha((0.38 * 255).ceil()),
fontSize: 12,
fontWeight: FontWeight.normal,
height: 16 / 12
))
]
);
Column title =
Column(crossAxisAlignment: CrossAxisAlignment.start, children: [
Text(titleText,
style: TextStyle(
fontWeight: FontWeight.w500,
fontSize: subTitleText != null ? 16 : 20,
height: subTitleText != null ? 20 / 16 : 24 / 20)),
if (subTitleText != null)
Text(subTitleText,
style: TextStyle(
color: Theme.of(context)
.primaryTextTheme
.headline6!
.color!
.withAlpha((0.38 * 255).ceil()),
fontSize: 12,
fontWeight: FontWeight.normal,
height: 16 / 12))
]);
appBar = TbAppBar(
tbContext,
title: title,
actions: [
IconButton(
icon: Icon(
Icons.search
),
onPressed: () {
List<String> params = [];
params.add('search=true');
if (widget.deviceType != null) {
params.add('deviceType=${widget.deviceType}');
}
if (widget.active != null) {
params.add('active=${widget.active}');
}
navigateTo('/deviceList?${params.join('&')}');
},
)
]);
appBar = TbAppBar(tbContext, title: title, actions: [
IconButton(
icon: Icon(Icons.search),
onPressed: () {
List<String> params = [];
params.add('search=true');
if (widget.deviceType != null) {
params.add('deviceType=${widget.deviceType}');
}
if (widget.active != null) {
params.add('active=${widget.active}');
}
navigateTo('/deviceList?${params.join('&')}');
},
)
]);
}
return Scaffold(
appBar: appBar,
body: devicesList
);
return Scaffold(appBar: appBar, body: devicesList);
}
@override
@@ -94,5 +92,4 @@ class _DevicesListPageState extends TbPageState<DevicesListPage> {
_deviceQueryController.dispose();
super.dispose();
}
}

View File

@@ -4,9 +4,11 @@ import 'package:thingsboard_app/core/entity/entities_list_widget.dart';
import 'package:thingsboard_app/modules/device/devices_base.dart';
import 'package:thingsboard_client/thingsboard_client.dart';
class DevicesListWidget extends EntitiesListWidget<EntityData, EntityDataQuery> with DevicesBase {
DevicesListWidget(TbContext tbContext, {EntitiesListWidgetController? controller}): super(tbContext, controller: controller);
class DevicesListWidget extends EntitiesListWidget<EntityData, EntityDataQuery>
with DevicesBase {
DevicesListWidget(TbContext tbContext,
{EntitiesListWidgetController? controller})
: super(tbContext, controller: controller);
@override
void onViewAll() {
@@ -14,6 +16,6 @@ class DevicesListWidget extends EntitiesListWidget<EntityData, EntityDataQuery>
}
@override
PageKeyController<EntityDataQuery> createPageKeyController() => DeviceQueryController(pageSize: 5);
PageKeyController<EntityDataQuery> createPageKeyController() =>
DeviceQueryController(pageSize: 5);
}

View File

@@ -6,16 +6,14 @@ import 'package:thingsboard_app/modules/device/device_profiles_grid.dart';
import 'package:thingsboard_app/widgets/tb_app_bar.dart';
class DevicesMainPage extends TbContextWidget {
DevicesMainPage(TbContext tbContext) : super(tbContext);
@override
_DevicesMainPageState createState() => _DevicesMainPageState();
}
class _DevicesMainPageState extends TbContextState<DevicesMainPage> with AutomaticKeepAliveClientMixin<DevicesMainPage> {
class _DevicesMainPageState extends TbContextState<DevicesMainPage>
with AutomaticKeepAliveClientMixin<DevicesMainPage> {
final PageLinkController _pageLinkController = PageLinkController();
@override
@@ -28,12 +26,8 @@ class _DevicesMainPageState extends TbContextState<DevicesMainPage> with Automat
super.build(context);
var deviceProfilesList = DeviceProfilesGrid(tbContext, _pageLinkController);
return Scaffold(
appBar: TbAppBar(
tbContext,
title: Text(deviceProfilesList.title)
),
body: deviceProfilesList
);
appBar: TbAppBar(tbContext, title: Text(deviceProfilesList.title)),
body: deviceProfilesList);
}
@override
@@ -41,5 +35,4 @@ class _DevicesMainPageState extends TbContextState<DevicesMainPage> with Automat
_pageLinkController.dispose();
super.dispose();
}
}

View File

@@ -6,28 +6,21 @@ import 'package:thingsboard_app/modules/device/device_profiles_grid.dart';
import 'package:thingsboard_app/widgets/tb_app_bar.dart';
class DevicesPage extends TbPageWidget {
DevicesPage(TbContext tbContext) : super(tbContext);
@override
_DevicesPageState createState() => _DevicesPageState();
}
class _DevicesPageState extends TbPageState<DevicesPage> {
final PageLinkController _pageLinkController = PageLinkController();
@override
Widget build(BuildContext context) {
var deviceProfilesList = DeviceProfilesGrid(tbContext, _pageLinkController);
return Scaffold(
appBar: TbAppBar(
tbContext,
title: Text(deviceProfilesList.title)
),
body: deviceProfilesList
);
appBar: TbAppBar(tbContext, title: Text(deviceProfilesList.title)),
body: deviceProfilesList);
}
@override
@@ -35,5 +28,4 @@ class _DevicesPageState extends TbPageState<DevicesPage> {
_pageLinkController.dispose();
super.dispose();
}
}