Home and Alarms page
This commit is contained in:
BIN
assets/images/dashboard-placeholder.png
Normal file
BIN
assets/images/dashboard-placeholder.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 6.4 KiB |
BIN
assets/images/device-profile-placeholder.png
Normal file
BIN
assets/images/device-profile-placeholder.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 6.5 KiB |
@@ -2,6 +2,7 @@ import 'package:fluro/fluro.dart';
|
||||
import 'package:thingsboard_app/core/auth/auth_routes.dart';
|
||||
import 'package:thingsboard_app/core/context/tb_context.dart';
|
||||
import 'package:thingsboard_app/core/init/init_routes.dart';
|
||||
import 'package:thingsboard_app/modules/alarm/alarm_routes.dart';
|
||||
import 'package:thingsboard_app/modules/asset/asset_routes.dart';
|
||||
import 'package:thingsboard_app/modules/dashboard/dashboard_routes.dart';
|
||||
import 'package:thingsboard_app/modules/device/device_routes.dart';
|
||||
@@ -21,6 +22,7 @@ class ThingsboardAppRouter {
|
||||
ProfileRoutes(_tbContext).registerRoutes();
|
||||
AssetRoutes(_tbContext).registerRoutes();
|
||||
DeviceRoutes(_tbContext).registerRoutes();
|
||||
AlarmRoutes(_tbContext).registerRoutes();
|
||||
DashboardRoutes(_tbContext).registerRoutes();
|
||||
}
|
||||
|
||||
|
||||
@@ -36,7 +36,8 @@ const tbDarkMatIndigo = MaterialColor(
|
||||
|
||||
ThemeData tbTheme = ThemeData(
|
||||
primarySwatch: tbMatIndigo,
|
||||
accentColor: Colors.deepOrange
|
||||
accentColor: Colors.deepOrange,
|
||||
scaffoldBackgroundColor: Color(0xFFF0F4F9)
|
||||
);
|
||||
|
||||
ThemeData tbDarkTheme = ThemeData(
|
||||
|
||||
@@ -1,4 +1,6 @@
|
||||
abstract class ThingsboardImage {
|
||||
static final thingsBoardLogoBlue = 'assets/images/thingsboard_logo_blue.svg';
|
||||
static final thingsboard = 'assets/images/thingsboard.png';
|
||||
static final dashboardPlaceholder = 'assets/images/dashboard-placeholder.png';
|
||||
static final deviceProfilePlaceholder = 'assets/images/device-profile-placeholder.png';
|
||||
}
|
||||
|
||||
@@ -1,13 +1,22 @@
|
||||
import 'dart:async';
|
||||
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:flutter/widgets.dart';
|
||||
import 'package:infinite_scroll_pagination/infinite_scroll_pagination.dart';
|
||||
import 'package:intl/intl.dart';
|
||||
import 'package:thingsboard_app/core/context/tb_context.dart';
|
||||
import 'package:thingsboard_app/core/context/tb_context_widget.dart';
|
||||
import 'package:thingsboard_client/thingsboard_client.dart';
|
||||
|
||||
typedef EntityDetailsFunction<T extends BaseData> = Function(T entity);
|
||||
typedef EntityCardWidgetBuilder<T extends BaseData> = Widget Function(BuildContext context, T entity, bool briefView);
|
||||
typedef EntityTapFunction<T> = Function(T entity);
|
||||
typedef EntityCardWidgetBuilder<T> = Widget Function(BuildContext context, T entity);
|
||||
|
||||
mixin EntitiesBase<T extends BaseData> on HasTbContext {
|
||||
class EntityCardSettings {
|
||||
bool dropShadow;
|
||||
EntityCardSettings({this.dropShadow = true});
|
||||
}
|
||||
|
||||
mixin EntitiesBase<T, P> on HasTbContext {
|
||||
|
||||
final entityDateFormat = DateFormat('yyyy-MM-dd');
|
||||
|
||||
@@ -15,75 +24,255 @@ mixin EntitiesBase<T extends BaseData> on HasTbContext {
|
||||
|
||||
String get noItemsFoundText;
|
||||
|
||||
Future<PageData<T>> fetchEntities(PageLink pageLink);
|
||||
Future<PageData<T>> fetchEntities(P pageKey);
|
||||
|
||||
Widget buildEntityCard(BuildContext context, T entity, bool briefView);
|
||||
Future<void> onRefresh() => Future.value();
|
||||
|
||||
void onEntityDetails(T entity);
|
||||
Widget? buildHeading(BuildContext context) => null;
|
||||
|
||||
Widget buildEntityListCard(BuildContext context, T entity) {
|
||||
return Text('Not implemented!');
|
||||
}
|
||||
|
||||
Widget buildEntityListWidgetCard(BuildContext context, T entity) {
|
||||
return Text('Not implemented!');
|
||||
}
|
||||
|
||||
Widget buildEntityGridCard(BuildContext context, T entity) {
|
||||
return Text('Not implemented!');
|
||||
}
|
||||
|
||||
P createFirstKey({int pageSize = 10}) => throw UnimplementedError('Not implemented');
|
||||
|
||||
P nextPageKey(P pageKey) => throw UnimplementedError('Not implemented');
|
||||
|
||||
EntityCardSettings entityListCardSettings(T entity) => EntityCardSettings();
|
||||
|
||||
EntityCardSettings entityGridCardSettings(T entity) => EntityCardSettings();
|
||||
|
||||
void onEntityTap(T entity);
|
||||
|
||||
}
|
||||
|
||||
class EntityCard<T extends BaseData> extends StatelessWidget {
|
||||
final bool _briefView;
|
||||
final T _entity;
|
||||
final EntityDetailsFunction<T>? _onDetails;
|
||||
final EntityCardWidgetBuilder<T> _entityCardWidgetBuilder;
|
||||
mixin EntitiesBaseWithPageLink<T> on EntitiesBase<T, PageLink> {
|
||||
|
||||
EntityCard(T entity, {EntityDetailsFunction<T>? onDetails,
|
||||
required EntityCardWidgetBuilder<T> entityCardWidgetBuilder,
|
||||
required bool briefView}):
|
||||
this._entity = entity,
|
||||
this._onDetails = onDetails,
|
||||
this._entityCardWidgetBuilder = entityCardWidgetBuilder,
|
||||
this._briefView = briefView;
|
||||
@override
|
||||
PageLink createFirstKey({int pageSize = 10}) => PageLink(pageSize, 0, null, SortOrder('createdTime', Direction.DESC));
|
||||
|
||||
@override
|
||||
PageLink nextPageKey(PageLink pageKey) => pageKey.nextPageLink();
|
||||
|
||||
}
|
||||
|
||||
mixin EntitiesBaseWithTimePageLink<T> on EntitiesBase<T, TimePageLink> {
|
||||
|
||||
@override
|
||||
TimePageLink createFirstKey({int pageSize = 10}) => TimePageLink(pageSize, 0, null, SortOrder('createdTime', Direction.DESC));
|
||||
|
||||
@override
|
||||
TimePageLink nextPageKey(TimePageLink pageKey) => pageKey.nextPageLink();
|
||||
|
||||
}
|
||||
|
||||
abstract class BaseEntitiesPageLinkWidget<T> extends BaseEntitiesWidget<T, PageLink> with EntitiesBaseWithPageLink<T> {
|
||||
BaseEntitiesPageLinkWidget(TbContext tbContext): super(tbContext);
|
||||
}
|
||||
|
||||
abstract class BaseEntitiesTimePageLinkWidget<T> extends BaseEntitiesWidget<T, TimePageLink> with EntitiesBaseWithTimePageLink<T> {
|
||||
BaseEntitiesTimePageLinkWidget(TbContext tbContext): super(tbContext);
|
||||
}
|
||||
|
||||
abstract class BaseEntitiesWidget<T, P> extends TbContextWidget<BaseEntitiesWidget<T, P>, BaseEntitiesState<T, P>> with EntitiesBase<T, P> {
|
||||
|
||||
BaseEntitiesWidget(TbContext tbContext): super(tbContext);
|
||||
|
||||
}
|
||||
|
||||
abstract class BaseEntitiesState<T, P> extends TbContextState<BaseEntitiesWidget<T, P>, BaseEntitiesState<T, P>> {
|
||||
|
||||
late final PagingController<P, T> pagingController;
|
||||
Completer<void>? _refreshCompleter;
|
||||
|
||||
@override
|
||||
void initState() {
|
||||
super.initState();
|
||||
pagingController = PagingController(firstPageKey: widget.createFirstKey());
|
||||
pagingController.addPageRequestListener((pageKey) {
|
||||
_fetchPage(pageKey);
|
||||
});
|
||||
}
|
||||
|
||||
@override
|
||||
void dispose() {
|
||||
pagingController.dispose();
|
||||
super.dispose();
|
||||
}
|
||||
|
||||
bool _dataLoading = false;
|
||||
bool _scheduleRefresh = false;
|
||||
|
||||
Future<void> _refresh() {
|
||||
if (_refreshCompleter == null) {
|
||||
_refreshCompleter = Completer();
|
||||
}
|
||||
if (_dataLoading) {
|
||||
_scheduleRefresh = true;
|
||||
} else {
|
||||
_refreshPagingController();
|
||||
}
|
||||
return _refreshCompleter!.future;
|
||||
}
|
||||
|
||||
void _refreshPagingController() {
|
||||
_fetchPage(widget.createFirstKey(), refresh: true);
|
||||
}
|
||||
|
||||
Future<void> _fetchPage(P pageKey, {bool refresh = false}) async {
|
||||
if (mounted) {
|
||||
_dataLoading = true;
|
||||
try {
|
||||
hideNotification();
|
||||
final pageData = await widget.fetchEntities(pageKey);
|
||||
final isLastPage = !pageData.hasNext;
|
||||
if (refresh) {
|
||||
var state = pagingController.value;
|
||||
if (state.itemList != null) {
|
||||
state.itemList!.clear();
|
||||
}
|
||||
}
|
||||
if (isLastPage) {
|
||||
pagingController.appendLastPage(pageData.data);
|
||||
} else {
|
||||
final nextPageKey = widget.nextPageKey(pageKey);
|
||||
pagingController.appendPage(pageData.data, nextPageKey);
|
||||
}
|
||||
} catch (error) {
|
||||
if (mounted) {
|
||||
pagingController.error = error;
|
||||
}
|
||||
} finally {
|
||||
_dataLoading = false;
|
||||
if (refresh) {
|
||||
_refreshCompleter!.complete();
|
||||
_refreshCompleter = null;
|
||||
}
|
||||
if (_scheduleRefresh) {
|
||||
_scheduleRefresh = false;
|
||||
if (mounted) {
|
||||
_refreshPagingController();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return
|
||||
GestureDetector(
|
||||
behavior: HitTestBehavior.opaque,
|
||||
child:
|
||||
Container(
|
||||
height: 64,
|
||||
margin: _briefView ? EdgeInsets.only(right: 8) : EdgeInsets.symmetric(vertical: 4, horizontal: 8),
|
||||
child: Card(
|
||||
margin: EdgeInsets.zero,
|
||||
shape: RoundedRectangleBorder(
|
||||
borderRadius: BorderRadius.circular(_briefView ? 4 : 6),
|
||||
),
|
||||
elevation: 0,
|
||||
child: Padding(
|
||||
padding: const EdgeInsets.all(2),
|
||||
child: _entityCardWidgetBuilder(context, _entity, _briefView)
|
||||
)
|
||||
return RefreshIndicator(
|
||||
onRefresh: () => Future.wait([
|
||||
widget.onRefresh(),
|
||||
_refresh()
|
||||
]),
|
||||
child: pagedViewBuilder(context)
|
||||
);
|
||||
}
|
||||
|
||||
Widget pagedViewBuilder(BuildContext context);
|
||||
|
||||
Widget firstPageProgressIndicatorBuilder(BuildContext context) {
|
||||
return Stack( children: [
|
||||
Positioned(
|
||||
top: 20,
|
||||
left: 0,
|
||||
right: 0,
|
||||
child: Row(
|
||||
mainAxisAlignment: MainAxisAlignment.center,
|
||||
children: [RefreshProgressIndicator()],
|
||||
),
|
||||
)
|
||||
]);
|
||||
}
|
||||
|
||||
Widget newPageProgressIndicatorBuilder(BuildContext context) {
|
||||
return Padding(
|
||||
padding: const EdgeInsets.only(
|
||||
top: 16,
|
||||
bottom: 16,
|
||||
),
|
||||
child: Center(child: RefreshProgressIndicator()),
|
||||
);
|
||||
}
|
||||
|
||||
Widget noItemsFoundIndicatorBuilder(BuildContext context) {
|
||||
return FirstPageExceptionIndicator(
|
||||
title: widget.noItemsFoundText,
|
||||
message: 'The list is currently empty.',
|
||||
onTryAgain: () => pagingController.refresh(),
|
||||
);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
class FirstPageExceptionIndicator extends StatelessWidget {
|
||||
const FirstPageExceptionIndicator({
|
||||
required this.title,
|
||||
this.message,
|
||||
this.onTryAgain,
|
||||
Key? key,
|
||||
}) : super(key: key);
|
||||
|
||||
final String title;
|
||||
final String? message;
|
||||
final VoidCallback? onTryAgain;
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
final message = this.message;
|
||||
return Center(
|
||||
child: Padding(
|
||||
padding: const EdgeInsets.symmetric(vertical: 32, horizontal: 16),
|
||||
child: Column(
|
||||
children: [
|
||||
Text(
|
||||
title,
|
||||
textAlign: TextAlign.center,
|
||||
style: Theme.of(context).textTheme.headline6,
|
||||
),
|
||||
decoration: _briefView ? BoxDecoration(
|
||||
border: Border.all(
|
||||
color: Color(0xFFDEDEDE),
|
||||
style: BorderStyle.solid,
|
||||
width: 1
|
||||
if (message != null)
|
||||
const SizedBox(
|
||||
height: 16,
|
||||
),
|
||||
if (message != null)
|
||||
Text(
|
||||
message,
|
||||
textAlign: TextAlign.center,
|
||||
),
|
||||
if (onTryAgain != null)
|
||||
const SizedBox(
|
||||
height: 48,
|
||||
),
|
||||
if (onTryAgain != null)
|
||||
SizedBox(
|
||||
height: 50,
|
||||
width: double.infinity,
|
||||
child: ElevatedButton.icon(
|
||||
onPressed: onTryAgain,
|
||||
icon: const Icon(
|
||||
Icons.refresh,
|
||||
color: Colors.white,
|
||||
),
|
||||
label: const Text(
|
||||
'Try Again',
|
||||
style: TextStyle(
|
||||
fontSize: 16,
|
||||
color: Colors.white,
|
||||
),
|
||||
),
|
||||
),
|
||||
borderRadius: BorderRadius.circular(4)
|
||||
) : BoxDecoration(
|
||||
boxShadow: [
|
||||
BoxShadow(
|
||||
color: Colors.black.withAlpha(25),
|
||||
blurRadius: 10.0,
|
||||
offset: Offset(0, 4)
|
||||
),
|
||||
BoxShadow(
|
||||
color: Colors.black.withAlpha(18),
|
||||
blurRadius: 30.0,
|
||||
offset: Offset(0, 10)
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
onTap: () {
|
||||
if (_onDetails != null) {
|
||||
_onDetails!(_entity);
|
||||
}
|
||||
}
|
||||
);
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
58
lib/core/entity/entities_grid.dart
Normal file
58
lib/core/entity/entities_grid.dart
Normal file
@@ -0,0 +1,58 @@
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:flutter/widgets.dart';
|
||||
import 'package:infinite_scroll_pagination/infinite_scroll_pagination.dart';
|
||||
|
||||
import 'entities_base.dart';
|
||||
import 'entity_grid_card.dart';
|
||||
|
||||
mixin EntitiesGridStateBase on StatefulWidget {
|
||||
|
||||
@override
|
||||
_EntitiesGridState createState() => _EntitiesGridState();
|
||||
|
||||
}
|
||||
|
||||
class _EntitiesGridState<T, P> extends BaseEntitiesState<T, P> {
|
||||
|
||||
@override
|
||||
Widget pagedViewBuilder(BuildContext context) {
|
||||
var heading = widget.buildHeading(context);
|
||||
List<Widget> slivers = [];
|
||||
if (heading != null) {
|
||||
slivers.add(SliverPadding(
|
||||
padding: EdgeInsets.fromLTRB(16, 16, 16, 0),
|
||||
sliver: SliverToBoxAdapter(
|
||||
child: heading
|
||||
)));
|
||||
}
|
||||
slivers.add(SliverPadding(
|
||||
padding: EdgeInsets.all(16),
|
||||
sliver: PagedSliverGrid(
|
||||
showNewPageProgressIndicatorAsGridChild: false,
|
||||
showNewPageErrorIndicatorAsGridChild: false,
|
||||
showNoMoreItemsIndicatorAsGridChild: false,
|
||||
pagingController: pagingController,
|
||||
// padding: EdgeInsets.all(16),
|
||||
gridDelegate: const SliverGridDelegateWithFixedCrossAxisCount(
|
||||
childAspectRatio: 156 / 150,
|
||||
crossAxisSpacing: 16,
|
||||
mainAxisSpacing: 16,
|
||||
crossAxisCount: 2,
|
||||
),
|
||||
builderDelegate: PagedChildBuilderDelegate<T>(
|
||||
itemBuilder: (context, item, index) => EntityGridCard<T>(
|
||||
item,
|
||||
entityCardWidgetBuilder: widget.buildEntityGridCard,
|
||||
onEntityTap: widget.onEntityTap,
|
||||
settings: widget.entityGridCardSettings(item),
|
||||
),
|
||||
firstPageProgressIndicatorBuilder: firstPageProgressIndicatorBuilder,
|
||||
newPageProgressIndicatorBuilder: newPageProgressIndicatorBuilder,
|
||||
noItemsFoundIndicatorBuilder: noItemsFoundIndicatorBuilder
|
||||
)
|
||||
)));
|
||||
return CustomScrollView(
|
||||
slivers: slivers
|
||||
);
|
||||
}
|
||||
}
|
||||
49
lib/core/entity/entities_list.dart
Normal file
49
lib/core/entity/entities_list.dart
Normal file
@@ -0,0 +1,49 @@
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:flutter/widgets.dart';
|
||||
import 'package:infinite_scroll_pagination/infinite_scroll_pagination.dart';
|
||||
import 'package:thingsboard_app/core/entity/entities_base.dart';
|
||||
|
||||
import 'entity_list_card.dart';
|
||||
|
||||
mixin EntitiesListStateBase on StatefulWidget {
|
||||
|
||||
@override
|
||||
_EntitiesListState createState() => _EntitiesListState();
|
||||
|
||||
}
|
||||
|
||||
class _EntitiesListState<T,P> extends BaseEntitiesState<T, P> {
|
||||
|
||||
@override
|
||||
Widget pagedViewBuilder(BuildContext context) {
|
||||
var heading = widget.buildHeading(context);
|
||||
List<Widget> slivers = [];
|
||||
if (heading != null) {
|
||||
slivers.add(SliverPadding(
|
||||
padding: EdgeInsets.fromLTRB(16, 16, 16, 0),
|
||||
sliver: SliverToBoxAdapter(
|
||||
child: heading
|
||||
)));
|
||||
}
|
||||
slivers.add(SliverPadding(
|
||||
padding: EdgeInsets.all(16),
|
||||
sliver: PagedSliverList.separated(
|
||||
pagingController: pagingController,
|
||||
separatorBuilder: (context, index) => SizedBox(height: 8),
|
||||
builderDelegate: PagedChildBuilderDelegate<T>(
|
||||
itemBuilder: (context, item, index) => EntityListCard<T>(
|
||||
item,
|
||||
entityCardWidgetBuilder: widget.buildEntityListCard,
|
||||
onEntityTap: widget.onEntityTap,
|
||||
settings: widget.entityListCardSettings(item),
|
||||
),
|
||||
firstPageProgressIndicatorBuilder: firstPageProgressIndicatorBuilder,
|
||||
newPageProgressIndicatorBuilder: newPageProgressIndicatorBuilder,
|
||||
noItemsFoundIndicatorBuilder: noItemsFoundIndicatorBuilder
|
||||
)
|
||||
)));
|
||||
return CustomScrollView(
|
||||
slivers: slivers
|
||||
);
|
||||
}
|
||||
}
|
||||
@@ -3,22 +3,23 @@ import 'dart:async';
|
||||
import 'package:fading_edge_scrollview/fading_edge_scrollview.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:flutter/widgets.dart';
|
||||
import 'package:intl/intl.dart';
|
||||
import 'package:thingsboard_app/core/context/tb_context.dart';
|
||||
import 'package:thingsboard_app/core/context/tb_context_widget.dart';
|
||||
import 'package:thingsboard_app/core/entity/entities_base.dart';
|
||||
import 'package:thingsboard_client/thingsboard_client.dart';
|
||||
|
||||
class EntitiesWidgetController {
|
||||
import 'entity_list_card.dart';
|
||||
|
||||
final List<_EntitiesWidgetState> states = [];
|
||||
class EntitiesListWidgetController {
|
||||
|
||||
void _registerEntitiesWidgetState(_EntitiesWidgetState entitiesWidgetState) {
|
||||
states.add(entitiesWidgetState);
|
||||
final List<_EntitiesListWidgetState> states = [];
|
||||
|
||||
void _registerEntitiesWidgetState(_EntitiesListWidgetState entitiesListWidgetState) {
|
||||
states.add(entitiesListWidgetState);
|
||||
}
|
||||
|
||||
void _unregisterEntitiesWidgetState(_EntitiesWidgetState entitiesWidgetState) {
|
||||
states.remove(entitiesWidgetState);
|
||||
void _unregisterEntitiesWidgetState(_EntitiesListWidgetState entitiesListWidgetState) {
|
||||
states.remove(entitiesListWidgetState);
|
||||
}
|
||||
|
||||
Future<void> refresh() {
|
||||
@@ -31,29 +32,32 @@ class EntitiesWidgetController {
|
||||
|
||||
}
|
||||
|
||||
abstract class EntitiesWidget<T extends BaseData> extends TbContextWidget<EntitiesWidget<T>, _EntitiesWidgetState<T>> with EntitiesBase<T> {
|
||||
abstract class EntitiesListPageLinkWidget<T> extends EntitiesListWidget<T, PageLink> with EntitiesBaseWithPageLink<T> {
|
||||
EntitiesListPageLinkWidget(TbContext tbContext, {EntitiesListWidgetController? controller}): super(tbContext, controller: controller);
|
||||
}
|
||||
|
||||
final entityDateFormat = DateFormat('yyyy-MM-dd');
|
||||
final EntitiesWidgetController? _controller;
|
||||
abstract class EntitiesListWidget<T, P> extends TbContextWidget<EntitiesListWidget<T,P>, _EntitiesListWidgetState<T,P>> with EntitiesBase<T,P> {
|
||||
|
||||
EntitiesWidget(TbContext tbContext, {EntitiesWidgetController? controller}):
|
||||
final EntitiesListWidgetController? _controller;
|
||||
|
||||
EntitiesListWidget(TbContext tbContext, {EntitiesListWidgetController? controller}):
|
||||
_controller = controller,
|
||||
super(tbContext);
|
||||
|
||||
@override
|
||||
_EntitiesWidgetState createState() => _EntitiesWidgetState(_controller);
|
||||
_EntitiesListWidgetState createState() => _EntitiesListWidgetState(_controller);
|
||||
|
||||
void onViewAll();
|
||||
|
||||
}
|
||||
|
||||
class _EntitiesWidgetState<T extends BaseData> extends TbContextState<EntitiesWidget<T>, _EntitiesWidgetState<T>> {
|
||||
class _EntitiesListWidgetState<T,P> extends TbContextState<EntitiesListWidget<T,P>, _EntitiesListWidgetState<T,P>> {
|
||||
|
||||
final EntitiesWidgetController? _controller;
|
||||
final EntitiesListWidgetController? _controller;
|
||||
|
||||
final StreamController<PageData<T>?> _entitiesStreamController = StreamController.broadcast();
|
||||
|
||||
_EntitiesWidgetState(EntitiesWidgetController? controller):
|
||||
_EntitiesListWidgetState(EntitiesListWidgetController? controller):
|
||||
_controller = controller;
|
||||
|
||||
@override
|
||||
@@ -76,7 +80,7 @@ class _EntitiesWidgetState<T extends BaseData> extends TbContextState<EntitiesWi
|
||||
|
||||
Future<void> _refresh() {
|
||||
_entitiesStreamController.add(null);
|
||||
var entitiesFuture = widget.fetchEntities(PageLink(5, 0, null, SortOrder('createdTime', Direction.DESC)));
|
||||
var entitiesFuture = widget.fetchEntities(widget.createFirstKey(pageSize: 5));
|
||||
entitiesFuture.then((value) => _entitiesStreamController.add(value));
|
||||
return entitiesFuture;
|
||||
}
|
||||
@@ -204,11 +208,12 @@ class _EntitiesWidgetState<T extends BaseData> extends TbContextState<EntitiesWi
|
||||
child: ListView(
|
||||
scrollDirection: Axis.horizontal,
|
||||
controller: ScrollController(),
|
||||
children: entities.map((entity) => EntityCard<T>(
|
||||
children: entities.map((entity) => EntityListCard<T>(
|
||||
entity,
|
||||
entityCardWidgetBuilder: widget.buildEntityCard,
|
||||
onDetails: widget.onEntityDetails,
|
||||
briefView: true
|
||||
entityCardWidgetBuilder: widget.buildEntityListWidgetCard,
|
||||
onEntityTap: widget.onEntityTap,
|
||||
settings: widget.entityListCardSettings(entity),
|
||||
listWidgetCard: true
|
||||
)).toList()
|
||||
));
|
||||
}
|
||||
@@ -1,220 +0,0 @@
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:flutter/widgets.dart';
|
||||
import 'package:infinite_scroll_pagination/infinite_scroll_pagination.dart';
|
||||
import 'package:thingsboard_app/core/context/tb_context.dart';
|
||||
import 'package:thingsboard_app/core/context/tb_context_widget.dart';
|
||||
import 'package:thingsboard_app/core/entity/entities_base.dart';
|
||||
import 'package:thingsboard_app/widgets/tb_app_bar.dart';
|
||||
import 'package:thingsboard_client/thingsboard_client.dart';
|
||||
|
||||
abstract class EntitiesPage<T extends BaseData> extends TbContextWidget<EntitiesPage<T>, _EntitiesPageState<T>> with EntitiesBase<T> {
|
||||
|
||||
EntitiesPage(TbContext tbContext): super(tbContext);
|
||||
|
||||
String get searchHint;
|
||||
|
||||
String get noMoreItemsText;
|
||||
|
||||
@override
|
||||
_EntitiesPageState createState() => _EntitiesPageState();
|
||||
|
||||
}
|
||||
|
||||
class _EntitiesPageState<T extends BaseData> extends TbContextState<EntitiesPage<T>, _EntitiesPageState<T>> {
|
||||
|
||||
final _searchModeNotifier = ValueNotifier<bool>(false);
|
||||
|
||||
final PagingController<PageLink, T> _pagingController = PagingController(firstPageKey: PageLink(10, 0, null, SortOrder('createdTime', Direction.DESC)));
|
||||
|
||||
@override
|
||||
void initState() {
|
||||
super.initState();
|
||||
_pagingController.addPageRequestListener((pageKey) {
|
||||
_fetchPage(pageKey);
|
||||
});
|
||||
}
|
||||
|
||||
@override
|
||||
void dispose() {
|
||||
_pagingController.dispose();
|
||||
super.dispose();
|
||||
}
|
||||
|
||||
bool _dataLoading = false;
|
||||
bool _scheduleRefresh = false;
|
||||
|
||||
void _refresh() {
|
||||
if (_dataLoading) {
|
||||
_scheduleRefresh = true;
|
||||
} else {
|
||||
_pagingController.refresh();
|
||||
}
|
||||
}
|
||||
|
||||
Future<void> _fetchPage(PageLink pageKey) async {
|
||||
if (mounted) {
|
||||
_dataLoading = true;
|
||||
try {
|
||||
hideNotification();
|
||||
final pageData = await widget.fetchEntities(pageKey);
|
||||
final isLastPage = !pageData.hasNext;
|
||||
if (isLastPage) {
|
||||
_pagingController.appendLastPage(pageData.data);
|
||||
} else {
|
||||
final nextPageKey = pageKey.nextPageLink();
|
||||
_pagingController.appendPage(pageData.data, nextPageKey);
|
||||
}
|
||||
} catch (error) {
|
||||
if (mounted) {
|
||||
_pagingController.error = error;
|
||||
}
|
||||
} finally {
|
||||
_dataLoading = false;
|
||||
if (_scheduleRefresh) {
|
||||
_scheduleRefresh = false;
|
||||
if (mounted) {
|
||||
_pagingController.refresh();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return Scaffold(
|
||||
appBar: TbAppBar(
|
||||
tbContext,
|
||||
title: Text(widget.title),
|
||||
searchModeNotifier: _searchModeNotifier,
|
||||
searchHint: widget.searchHint,
|
||||
onSearch: (String searchText) {
|
||||
_pagingController.firstPageKey.textSearch = searchText;
|
||||
_pagingController.firstPageKey.page = 0;
|
||||
_refresh();
|
||||
},
|
||||
),
|
||||
body: RefreshIndicator(
|
||||
onRefresh: () => Future.sync(
|
||||
() => _refresh(),
|
||||
),
|
||||
child: PagedListView(
|
||||
pagingController: _pagingController,
|
||||
padding: EdgeInsets.all(0),
|
||||
builderDelegate: PagedChildBuilderDelegate<T>(
|
||||
itemBuilder: (context, item, index) => EntityCard<T>(
|
||||
item,
|
||||
entityCardWidgetBuilder: widget.buildEntityCard,
|
||||
onDetails: widget.onEntityDetails,
|
||||
briefView: false
|
||||
),
|
||||
firstPageProgressIndicatorBuilder: (context) {
|
||||
return Stack( children: [
|
||||
Positioned(
|
||||
top: 20,
|
||||
left: 0,
|
||||
right: 0,
|
||||
child: Row(
|
||||
mainAxisAlignment: MainAxisAlignment.center,
|
||||
children: [RefreshProgressIndicator()],
|
||||
),
|
||||
)
|
||||
]);
|
||||
},
|
||||
newPageProgressIndicatorBuilder: (context) {
|
||||
return Padding(
|
||||
padding: const EdgeInsets.only(
|
||||
top: 16,
|
||||
bottom: 16,
|
||||
),
|
||||
child: Center(child: RefreshProgressIndicator()),
|
||||
);
|
||||
},
|
||||
noItemsFoundIndicatorBuilder: (context) => FirstPageExceptionIndicator(
|
||||
title: widget.noItemsFoundText,
|
||||
message: 'The list is currently empty.',
|
||||
onTryAgain: () => _refresh(),
|
||||
)
|
||||
)
|
||||
)
|
||||
)
|
||||
/* bottomNavigationBar: BottomAppBar(
|
||||
child: Row(
|
||||
children: [
|
||||
IconButton(icon: Icon(Icons.refresh), onPressed: () {
|
||||
_refresh();
|
||||
}),
|
||||
Spacer(),
|
||||
IconButton(icon: Icon(Icons.search), onPressed: () {
|
||||
_searchModeNotifier.value = true;
|
||||
})
|
||||
]
|
||||
)
|
||||
) */
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
class FirstPageExceptionIndicator extends StatelessWidget {
|
||||
const FirstPageExceptionIndicator({
|
||||
required this.title,
|
||||
this.message,
|
||||
this.onTryAgain,
|
||||
Key? key,
|
||||
}) : super(key: key);
|
||||
|
||||
final String title;
|
||||
final String? message;
|
||||
final VoidCallback? onTryAgain;
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
final message = this.message;
|
||||
return Center(
|
||||
child: Padding(
|
||||
padding: const EdgeInsets.symmetric(vertical: 32, horizontal: 16),
|
||||
child: Column(
|
||||
children: [
|
||||
Text(
|
||||
title,
|
||||
textAlign: TextAlign.center,
|
||||
style: Theme.of(context).textTheme.headline6,
|
||||
),
|
||||
if (message != null)
|
||||
const SizedBox(
|
||||
height: 16,
|
||||
),
|
||||
if (message != null)
|
||||
Text(
|
||||
message,
|
||||
textAlign: TextAlign.center,
|
||||
),
|
||||
if (onTryAgain != null)
|
||||
const SizedBox(
|
||||
height: 48,
|
||||
),
|
||||
if (onTryAgain != null)
|
||||
SizedBox(
|
||||
height: 50,
|
||||
width: double.infinity,
|
||||
child: ElevatedButton.icon(
|
||||
onPressed: onTryAgain,
|
||||
icon: const Icon(
|
||||
Icons.refresh,
|
||||
color: Colors.white,
|
||||
),
|
||||
label: const Text(
|
||||
'Try Again',
|
||||
style: TextStyle(
|
||||
fontSize: 16,
|
||||
color: Colors.white,
|
||||
),
|
||||
),
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
||||
63
lib/core/entity/entity_grid_card.dart
Normal file
63
lib/core/entity/entity_grid_card.dart
Normal file
@@ -0,0 +1,63 @@
|
||||
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:flutter/widgets.dart';
|
||||
import 'package:thingsboard_client/thingsboard_client.dart';
|
||||
|
||||
import 'entities_base.dart';
|
||||
|
||||
class EntityGridCard<T> extends StatelessWidget {
|
||||
final T _entity;
|
||||
final EntityTapFunction<T>? _onEntityTap;
|
||||
final EntityCardWidgetBuilder<T> _entityCardWidgetBuilder;
|
||||
final EntityCardSettings _settings;
|
||||
|
||||
EntityGridCard(T entity, {EntityTapFunction<T>? onEntityTap,
|
||||
required EntityCardWidgetBuilder<T> entityCardWidgetBuilder,
|
||||
required EntityCardSettings settings}):
|
||||
this._entity = entity,
|
||||
this._onEntityTap = onEntityTap,
|
||||
this._entityCardWidgetBuilder = entityCardWidgetBuilder,
|
||||
this._settings = settings;
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return
|
||||
GestureDetector(
|
||||
behavior: HitTestBehavior.opaque,
|
||||
child:
|
||||
Container(
|
||||
child: Card(
|
||||
margin: EdgeInsets.zero,
|
||||
shape: RoundedRectangleBorder(
|
||||
borderRadius: BorderRadius.circular(10),
|
||||
),
|
||||
elevation: 0,
|
||||
child: Padding(
|
||||
padding: const EdgeInsets.all(4),
|
||||
child: _entityCardWidgetBuilder(context, _entity)
|
||||
)
|
||||
),
|
||||
decoration: _settings.dropShadow ? BoxDecoration(
|
||||
boxShadow: [
|
||||
BoxShadow(
|
||||
color: Colors.black.withAlpha(25),
|
||||
blurRadius: 10.0,
|
||||
offset: Offset(0, 4)
|
||||
),
|
||||
BoxShadow(
|
||||
color: Colors.black.withAlpha(18),
|
||||
blurRadius: 30.0,
|
||||
offset: Offset(0, 10)
|
||||
),
|
||||
],
|
||||
) : null,
|
||||
),
|
||||
onTap: () {
|
||||
if (_onEntityTap != null) {
|
||||
_onEntityTap!(_entity);
|
||||
}
|
||||
}
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
73
lib/core/entity/entity_list_card.dart
Normal file
73
lib/core/entity/entity_list_card.dart
Normal file
@@ -0,0 +1,73 @@
|
||||
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:flutter/widgets.dart';
|
||||
|
||||
import 'entities_base.dart';
|
||||
|
||||
class EntityListCard<T> extends StatelessWidget {
|
||||
final bool _listWidgetCard;
|
||||
final T _entity;
|
||||
final EntityTapFunction<T>? _onEntityTap;
|
||||
final EntityCardWidgetBuilder<T> _entityCardWidgetBuilder;
|
||||
final EntityCardSettings _settings;
|
||||
|
||||
EntityListCard(T entity, {EntityTapFunction<T>? onEntityTap,
|
||||
required EntityCardWidgetBuilder<T> entityCardWidgetBuilder,
|
||||
required EntityCardSettings settings,
|
||||
bool listWidgetCard = false}):
|
||||
this._entity = entity,
|
||||
this._onEntityTap = onEntityTap,
|
||||
this._entityCardWidgetBuilder = entityCardWidgetBuilder,
|
||||
this._settings = settings,
|
||||
this._listWidgetCard = listWidgetCard;
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return
|
||||
GestureDetector(
|
||||
behavior: HitTestBehavior.opaque,
|
||||
child:
|
||||
Container(
|
||||
margin: _listWidgetCard ? EdgeInsets.only(right: 8) : EdgeInsets.zero,
|
||||
child: Card(
|
||||
margin: EdgeInsets.zero,
|
||||
shape: RoundedRectangleBorder(
|
||||
borderRadius: BorderRadius.circular(_listWidgetCard ? 4 : 6),
|
||||
),
|
||||
elevation: 0,
|
||||
child: Padding(
|
||||
padding: const EdgeInsets.all(2),
|
||||
child: _entityCardWidgetBuilder(context, _entity)
|
||||
)
|
||||
),
|
||||
decoration: _listWidgetCard ? BoxDecoration(
|
||||
border: Border.all(
|
||||
color: Color(0xFFDEDEDE),
|
||||
style: BorderStyle.solid,
|
||||
width: 1
|
||||
),
|
||||
borderRadius: BorderRadius.circular(4)
|
||||
) : BoxDecoration(
|
||||
boxShadow: [
|
||||
BoxShadow(
|
||||
color: Colors.black.withAlpha(25),
|
||||
blurRadius: 10.0,
|
||||
offset: Offset(0, 4)
|
||||
),
|
||||
BoxShadow(
|
||||
color: Colors.black.withAlpha(18),
|
||||
blurRadius: 30.0,
|
||||
offset: Offset(0, 10)
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
onTap: () {
|
||||
if (_onEntityTap != null) {
|
||||
_onEntityTap!(_entity);
|
||||
}
|
||||
}
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -2,7 +2,6 @@ import 'dart:io';
|
||||
|
||||
import 'package:flutter/foundation.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:flutter/services.dart';
|
||||
import 'package:flutter_inappwebview/flutter_inappwebview.dart';
|
||||
import 'package:thingsboard_app/config/routes/router.dart';
|
||||
import 'config/themes/tb_theme.dart';
|
||||
|
||||
20
lib/modules/alarm/alarm_routes.dart
Normal file
20
lib/modules/alarm/alarm_routes.dart
Normal file
@@ -0,0 +1,20 @@
|
||||
import 'package:fluro/fluro.dart';
|
||||
import 'package:flutter/widgets.dart';
|
||||
import 'package:thingsboard_app/config/routes/router.dart';
|
||||
import 'package:thingsboard_app/core/context/tb_context.dart';
|
||||
import 'package:thingsboard_app/modules/main/main_page.dart';
|
||||
|
||||
class AlarmRoutes extends TbRoutes {
|
||||
|
||||
late var alarmsHandler = Handler(handlerFunc: (BuildContext? context, Map<String, dynamic> params) {
|
||||
return MainPage(tbContext, path: '/alarms');
|
||||
});
|
||||
|
||||
AlarmRoutes(TbContext tbContext) : super(tbContext);
|
||||
|
||||
@override
|
||||
void doRegisterRoutes(router) {
|
||||
router.define("/alarms", handler: alarmsHandler);
|
||||
}
|
||||
|
||||
}
|
||||
170
lib/modules/alarm/alarms_base.dart
Normal file
170
lib/modules/alarm/alarms_base.dart
Normal file
@@ -0,0 +1,170 @@
|
||||
import 'package:auto_size_text/auto_size_text.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:flutter/widgets.dart';
|
||||
import 'package:thingsboard_app/core/entity/entities_base.dart';
|
||||
import 'package:thingsboard_client/thingsboard_client.dart';
|
||||
|
||||
|
||||
const Map<AlarmSeverity, Color> alarmSeverityColors = {
|
||||
AlarmSeverity.CRITICAL: Color(0xFFFF0000),
|
||||
AlarmSeverity.MAJOR: Color(0xFFFFA500),
|
||||
AlarmSeverity.MINOR: Color(0xFFFFCA3D),
|
||||
AlarmSeverity.WARNING: Color(0xFFABAB00),
|
||||
AlarmSeverity.INDETERMINATE: Color(0xFF00FF00),
|
||||
};
|
||||
|
||||
const Map<AlarmSeverity, String> alarmSeverityTranslations = {
|
||||
AlarmSeverity.CRITICAL: 'Critical',
|
||||
AlarmSeverity.MAJOR: 'Major',
|
||||
AlarmSeverity.MINOR: 'Minor',
|
||||
AlarmSeverity.WARNING: 'Warning',
|
||||
AlarmSeverity.INDETERMINATE: 'Indeterminate',
|
||||
};
|
||||
|
||||
const Map<AlarmStatus, String> alarmStatusTranslations = {
|
||||
AlarmStatus.ACTIVE_ACK: 'Active Acknowledged',
|
||||
AlarmStatus.ACTIVE_UNACK: 'Active Unacknowledged',
|
||||
AlarmStatus.CLEARED_ACK: 'Cleared Acknowledged',
|
||||
AlarmStatus.CLEARED_UNACK: 'Cleared Unacknowledged',
|
||||
};
|
||||
|
||||
mixin AlarmsBase on EntitiesBase<AlarmInfo, AlarmQuery> {
|
||||
|
||||
@override
|
||||
String get title => 'Alarms';
|
||||
|
||||
@override
|
||||
String get noItemsFoundText => 'No alarms found';
|
||||
|
||||
@override
|
||||
AlarmQuery createFirstKey({int pageSize = 10}) => AlarmQuery(TimePageLink(pageSize, 0, null, SortOrder('createdTime', Direction.DESC)), fetchOriginator: true);
|
||||
|
||||
@override
|
||||
AlarmQuery nextPageKey(AlarmQuery query) {
|
||||
query.pageLink = query.pageLink.nextPageLink();
|
||||
return query;
|
||||
}
|
||||
|
||||
@override
|
||||
Future<PageData<AlarmInfo>> fetchEntities(AlarmQuery query) {
|
||||
return tbClient.getAlarmService().getAllAlarms(query);
|
||||
}
|
||||
|
||||
@override
|
||||
void onEntityTap(AlarmInfo alarm) {
|
||||
showErrorNotification('Balalai: alarm tap not implemented!');
|
||||
}
|
||||
|
||||
@override
|
||||
Widget buildEntityListCard(BuildContext context, AlarmInfo alarm) {
|
||||
return _buildEntityListCard(context, alarm);
|
||||
}
|
||||
|
||||
Widget _buildEntityListCard(BuildContext context, AlarmInfo alarm) {
|
||||
return Row(
|
||||
mainAxisSize: MainAxisSize.max,
|
||||
children: [
|
||||
Flexible(
|
||||
fit: FlexFit.tight,
|
||||
child:
|
||||
Padding(
|
||||
padding: EdgeInsets.symmetric(vertical: 12, horizontal: 16),
|
||||
child: Column(
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
children: [
|
||||
Row(
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
children: [
|
||||
Flexible(
|
||||
fit: FlexFit.tight,
|
||||
child: AutoSizeText(alarm.type,
|
||||
maxLines: 2,
|
||||
minFontSize: 8,
|
||||
overflow: TextOverflow.ellipsis,
|
||||
style: TextStyle(
|
||||
color: Color(0xFF282828),
|
||||
fontWeight: FontWeight.w500,
|
||||
fontSize: 14,
|
||||
height: 20 / 14)
|
||||
)
|
||||
),
|
||||
Text(alarmSeverityTranslations[alarm.severity]!,
|
||||
style: TextStyle(
|
||||
color: alarmSeverityColors[alarm.severity]!,
|
||||
fontWeight: FontWeight.w500,
|
||||
fontSize: 12,
|
||||
height: 16 / 12)
|
||||
)
|
||||
]
|
||||
),
|
||||
SizedBox(height: 4),
|
||||
Row(
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
children: [
|
||||
Flexible(
|
||||
fit: FlexFit.tight,
|
||||
child: Text(alarm.originatorName != null ? alarm.originatorName! : '',
|
||||
style: TextStyle(
|
||||
color: Color(0xFFAFAFAF),
|
||||
fontWeight: FontWeight.normal,
|
||||
fontSize: 12,
|
||||
height: 16 / 12)
|
||||
)
|
||||
),
|
||||
Text(entityDateFormat.format(DateTime.fromMillisecondsSinceEpoch(alarm.createdTime!)),
|
||||
style: TextStyle(
|
||||
color: Color(0xFFAFAFAF),
|
||||
fontWeight: FontWeight.normal,
|
||||
fontSize: 12,
|
||||
height: 16 / 12)
|
||||
)
|
||||
]
|
||||
),
|
||||
SizedBox(height: 22),
|
||||
Row(
|
||||
crossAxisAlignment: CrossAxisAlignment.center,
|
||||
children: [
|
||||
Flexible(
|
||||
fit: FlexFit.tight,
|
||||
child: Text(alarmStatusTranslations[alarm.status]!,
|
||||
style: TextStyle(
|
||||
color: Color(0xFF282828),
|
||||
fontWeight: FontWeight.normal,
|
||||
fontSize: 14,
|
||||
height: 20 / 14)
|
||||
)
|
||||
),
|
||||
Row(
|
||||
children: [
|
||||
if ([AlarmStatus.CLEARED_UNACK, AlarmStatus.ACTIVE_UNACK].contains(alarm.status))
|
||||
CircleAvatar(
|
||||
radius: 24,
|
||||
backgroundColor: Color(0xffF0F4F9),
|
||||
child: IconButton(icon: Icon(Icons.done), padding: EdgeInsets.all(6.0), onPressed: () => {})
|
||||
),
|
||||
if ([AlarmStatus.ACTIVE_UNACK, AlarmStatus.ACTIVE_ACK].contains(alarm.status))
|
||||
Row(
|
||||
children: [
|
||||
SizedBox(width: 4),
|
||||
CircleAvatar(
|
||||
radius: 24,
|
||||
backgroundColor: Color(0xffF0F4F9),
|
||||
child: IconButton(icon: Icon(Icons.clear), padding: EdgeInsets.all(6.0), onPressed: () => {})
|
||||
)
|
||||
]
|
||||
)
|
||||
],
|
||||
)
|
||||
],
|
||||
)
|
||||
|
||||
]
|
||||
)
|
||||
)
|
||||
|
||||
)
|
||||
]
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
13
lib/modules/alarm/alarms_list.dart
Normal file
13
lib/modules/alarm/alarms_list.dart
Normal file
@@ -0,0 +1,13 @@
|
||||
import 'package:thingsboard_app/core/context/tb_context.dart';
|
||||
import 'package:thingsboard_app/core/entity/entities_base.dart';
|
||||
import 'package:thingsboard_app/core/entity/entities_list.dart';
|
||||
import 'package:thingsboard_client/thingsboard_client.dart';
|
||||
|
||||
import 'alarms_base.dart';
|
||||
|
||||
class AlarmsList extends BaseEntitiesWidget<AlarmInfo, AlarmQuery> with AlarmsBase, EntitiesListStateBase {
|
||||
|
||||
AlarmsList(TbContext tbContext) : super(tbContext);
|
||||
|
||||
}
|
||||
|
||||
31
lib/modules/alarm/alarms_page.dart
Normal file
31
lib/modules/alarm/alarms_page.dart
Normal file
@@ -0,0 +1,31 @@
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:thingsboard_app/core/context/tb_context.dart';
|
||||
import 'package:thingsboard_app/core/context/tb_context_widget.dart';
|
||||
import 'package:thingsboard_app/widgets/tb_app_bar.dart';
|
||||
|
||||
import 'alarms_list.dart';
|
||||
|
||||
class AlarmsPage extends TbContextWidget<AlarmsPage, _AlarmsPageState> {
|
||||
|
||||
AlarmsPage(TbContext tbContext) : super(tbContext);
|
||||
|
||||
@override
|
||||
_AlarmsPageState createState() => _AlarmsPageState();
|
||||
|
||||
}
|
||||
|
||||
class _AlarmsPageState extends TbContextState<AlarmsPage, _AlarmsPageState> {
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
var alarmsList = AlarmsList(tbContext);
|
||||
return Scaffold(
|
||||
appBar: TbAppBar(
|
||||
tbContext,
|
||||
title: Text(alarmsList.title)
|
||||
),
|
||||
body: alarmsList
|
||||
);
|
||||
}
|
||||
|
||||
}
|
||||
@@ -3,7 +3,7 @@ import 'package:flutter/widgets.dart';
|
||||
import 'package:thingsboard_app/core/entity/entities_base.dart';
|
||||
import 'package:thingsboard_client/thingsboard_client.dart';
|
||||
|
||||
mixin AssetsBase on EntitiesBase<AssetInfo> {
|
||||
mixin AssetsBase on EntitiesBaseWithPageLink<AssetInfo> {
|
||||
|
||||
@override
|
||||
String get title => 'Assets';
|
||||
@@ -21,20 +21,40 @@ mixin AssetsBase on EntitiesBase<AssetInfo> {
|
||||
}
|
||||
|
||||
@override
|
||||
Widget buildEntityCard(BuildContext context, AssetInfo asset, bool briefView) {
|
||||
void onEntityTap(AssetInfo asset) {
|
||||
navigateTo('/asset/${asset.id!.id}');
|
||||
}
|
||||
|
||||
@override
|
||||
Widget buildEntityListCard(BuildContext context, AssetInfo asset) {
|
||||
return _buildEntityListCard(context, asset, false);
|
||||
}
|
||||
|
||||
@override
|
||||
Widget buildEntityListWidgetCard(BuildContext context, AssetInfo asset) {
|
||||
return _buildEntityListCard(context, asset, true);
|
||||
}
|
||||
|
||||
@override
|
||||
Widget buildEntityGridCard(BuildContext context, AssetInfo asset) {
|
||||
return Text(asset.name);
|
||||
}
|
||||
|
||||
|
||||
Widget _buildEntityListCard(BuildContext context, AssetInfo asset, bool listWidgetCard) {
|
||||
return Row(
|
||||
mainAxisSize: briefView ? MainAxisSize.min : MainAxisSize.max,
|
||||
mainAxisSize: listWidgetCard ? MainAxisSize.min : MainAxisSize.max,
|
||||
children: [
|
||||
Flexible(
|
||||
fit: briefView ? FlexFit.loose : FlexFit.tight,
|
||||
fit: listWidgetCard ? FlexFit.loose : FlexFit.tight,
|
||||
child:
|
||||
Container(
|
||||
padding: EdgeInsets.symmetric(vertical: briefView ? 9 : 10, horizontal: 16),
|
||||
padding: EdgeInsets.symmetric(vertical: listWidgetCard ? 9 : 10, horizontal: 16),
|
||||
child: Row(
|
||||
mainAxisSize: briefView ? MainAxisSize.min : MainAxisSize.max,
|
||||
mainAxisSize: listWidgetCard ? MainAxisSize.min : MainAxisSize.max,
|
||||
children: [
|
||||
Flexible(
|
||||
fit: briefView ? FlexFit.loose : FlexFit.tight,
|
||||
fit: listWidgetCard ? FlexFit.loose : FlexFit.tight,
|
||||
child:
|
||||
Column(
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
@@ -60,7 +80,7 @@ mixin AssetsBase on EntitiesBase<AssetInfo> {
|
||||
],
|
||||
)
|
||||
),
|
||||
(!briefView ? Column(
|
||||
(!listWidgetCard ? Column(
|
||||
mainAxisAlignment: MainAxisAlignment.end,
|
||||
children: [
|
||||
Text(entityDateFormat.format(DateTime.fromMillisecondsSinceEpoch(asset.createdTime!)),
|
||||
@@ -80,10 +100,4 @@ mixin AssetsBase on EntitiesBase<AssetInfo> {
|
||||
]
|
||||
);
|
||||
}
|
||||
|
||||
@override
|
||||
void onEntityDetails(AssetInfo asset) {
|
||||
navigateTo('/asset/${asset.id!.id}');
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
13
lib/modules/asset/assets_list.dart
Normal file
13
lib/modules/asset/assets_list.dart
Normal file
@@ -0,0 +1,13 @@
|
||||
import 'package:thingsboard_app/core/context/tb_context.dart';
|
||||
import 'package:thingsboard_app/core/entity/entities_base.dart';
|
||||
import 'package:thingsboard_app/core/entity/entities_list.dart';
|
||||
import 'package:thingsboard_client/thingsboard_client.dart';
|
||||
|
||||
import 'assets_base.dart';
|
||||
|
||||
class AssetsList extends BaseEntitiesPageLinkWidget<AssetInfo> with AssetsBase, EntitiesListStateBase {
|
||||
|
||||
AssetsList(TbContext tbContext) : super(tbContext);
|
||||
|
||||
}
|
||||
|
||||
15
lib/modules/asset/assets_list_widget.dart
Normal file
15
lib/modules/asset/assets_list_widget.dart
Normal file
@@ -0,0 +1,15 @@
|
||||
import 'package:thingsboard_app/core/context/tb_context.dart';
|
||||
import 'package:thingsboard_app/core/entity/entities_list_widget.dart';
|
||||
import 'package:thingsboard_app/modules/asset/assets_base.dart';
|
||||
import 'package:thingsboard_client/thingsboard_client.dart';
|
||||
|
||||
class AssetsListWidget extends EntitiesListPageLinkWidget<AssetInfo> with AssetsBase {
|
||||
|
||||
AssetsListWidget(TbContext tbContext, {EntitiesListWidgetController? controller}): super(tbContext, controller: controller);
|
||||
|
||||
@override
|
||||
void onViewAll() {
|
||||
navigateTo('/assets');
|
||||
}
|
||||
|
||||
}
|
||||
@@ -1,16 +1,31 @@
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:thingsboard_app/core/context/tb_context.dart';
|
||||
import 'package:thingsboard_app/core/entity/entities_page.dart';
|
||||
import 'package:thingsboard_app/modules/asset/assets_base.dart';
|
||||
import 'package:thingsboard_client/thingsboard_client.dart';
|
||||
import 'package:thingsboard_app/core/context/tb_context_widget.dart';
|
||||
import 'package:thingsboard_app/widgets/tb_app_bar.dart';
|
||||
|
||||
class AssetsPage extends EntitiesPage<AssetInfo> with AssetsBase {
|
||||
import 'assets_list.dart';
|
||||
|
||||
class AssetsPage extends TbPageWidget<AssetsPage, _AssetsPageState> {
|
||||
|
||||
AssetsPage(TbContext tbContext) : super(tbContext);
|
||||
|
||||
@override
|
||||
String get noMoreItemsText => 'No more assets';
|
||||
|
||||
@override
|
||||
String get searchHint => 'Search assets';
|
||||
_AssetsPageState createState() => _AssetsPageState();
|
||||
|
||||
}
|
||||
|
||||
class _AssetsPageState extends TbPageState<AssetsPage, _AssetsPageState> {
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
var assetsList = AssetsList(tbContext);
|
||||
return Scaffold(
|
||||
appBar: TbAppBar(
|
||||
tbContext,
|
||||
title: Text(assetsList.title)
|
||||
),
|
||||
body: assetsList
|
||||
);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -1,16 +0,0 @@
|
||||
import 'package:flutter/widgets.dart';
|
||||
import 'package:thingsboard_app/core/context/tb_context.dart';
|
||||
import 'package:thingsboard_app/core/entity/entities_widget.dart';
|
||||
import 'package:thingsboard_app/modules/asset/assets_base.dart';
|
||||
import 'package:thingsboard_client/thingsboard_client.dart';
|
||||
|
||||
class AssetsWidget extends EntitiesWidget<AssetInfo> with AssetsBase {
|
||||
|
||||
AssetsWidget(TbContext tbContext, {EntitiesWidgetController? controller}): super(tbContext, controller: controller);
|
||||
|
||||
@override
|
||||
void onViewAll() {
|
||||
navigateTo('/assets');
|
||||
}
|
||||
|
||||
}
|
||||
@@ -209,7 +209,7 @@ class _DashboardState extends TbContextState<Dashboard, _DashboardState> {
|
||||
return Container(
|
||||
decoration: BoxDecoration(color: Colors.white),
|
||||
child: Center(
|
||||
child: CircularProgressIndicator()
|
||||
child: RefreshProgressIndicator()
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
@@ -2,14 +2,14 @@ import 'package:fluro/fluro.dart';
|
||||
import 'package:flutter/widgets.dart';
|
||||
import 'package:thingsboard_app/config/routes/router.dart';
|
||||
import 'package:thingsboard_app/core/context/tb_context.dart';
|
||||
import 'package:thingsboard_app/modules/main/main_page.dart';
|
||||
import 'package:thingsboard_app/modules/dashboard/dashboards_page.dart';
|
||||
|
||||
import 'dashboard_page.dart';
|
||||
|
||||
class DashboardRoutes extends TbRoutes {
|
||||
|
||||
late var dashboardsHandler = Handler(handlerFunc: (BuildContext? context, Map<String, dynamic> params) {
|
||||
return MainPage(tbContext, path: '/dashboards');
|
||||
return DashboardsPage(tbContext);
|
||||
});
|
||||
|
||||
late var dashboardDetailsHandler = Handler(handlerFunc: (BuildContext? context, Map<String, List<String>> params) {
|
||||
|
||||
@@ -1,9 +1,11 @@
|
||||
import 'package:auto_size_text/auto_size_text.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:flutter/widgets.dart';
|
||||
import 'package:thingsboard_app/constants/assets_path.dart';
|
||||
import 'package:thingsboard_app/core/entity/entities_base.dart';
|
||||
import 'package:thingsboard_client/thingsboard_client.dart';
|
||||
|
||||
mixin DashboardsBase on EntitiesBase<DashboardInfo> {
|
||||
mixin DashboardsBase on EntitiesBaseWithPageLink<DashboardInfo> {
|
||||
|
||||
@override
|
||||
String get title => 'Dashboards';
|
||||
@@ -21,20 +23,95 @@ mixin DashboardsBase on EntitiesBase<DashboardInfo> {
|
||||
}
|
||||
|
||||
@override
|
||||
Widget buildEntityCard(BuildContext context, DashboardInfo dashboard, bool briefView) {
|
||||
void onEntityTap(DashboardInfo dashboard) {
|
||||
navigateTo('/dashboard/${dashboard.id!.id}?title=${dashboard.title}');
|
||||
}
|
||||
|
||||
@override
|
||||
Widget buildEntityListCard(BuildContext context, DashboardInfo dashboard) {
|
||||
return _buildEntityListCard(context, dashboard, false);
|
||||
}
|
||||
|
||||
@override
|
||||
Widget buildEntityListWidgetCard(BuildContext context, DashboardInfo dashboard) {
|
||||
return _buildEntityListCard(context, dashboard, true);
|
||||
}
|
||||
|
||||
@override
|
||||
EntityCardSettings entityGridCardSettings(DashboardInfo dashboard) => EntityCardSettings(dropShadow: true); //dashboard.image != null);
|
||||
|
||||
@override
|
||||
Widget buildEntityGridCard(BuildContext context, DashboardInfo entity) {
|
||||
var hasImage = entity.image != null;
|
||||
Widget image;
|
||||
if (hasImage) {
|
||||
var uriData = UriData.parse(entity.image!);
|
||||
image = Image.memory(uriData.contentAsBytes());
|
||||
} else {
|
||||
image = Image.asset(ThingsboardImage.dashboardPlaceholder);
|
||||
}
|
||||
return
|
||||
ClipRRect(
|
||||
borderRadius: BorderRadius.circular(6),
|
||||
child: Stack(
|
||||
children: [
|
||||
Positioned.fill(
|
||||
child: FittedBox(
|
||||
fit: BoxFit.cover,
|
||||
child: image,
|
||||
)
|
||||
),
|
||||
hasImage ? Positioned.fill(
|
||||
child: Container(
|
||||
decoration: BoxDecoration(
|
||||
gradient: LinearGradient(
|
||||
begin: Alignment.topCenter,
|
||||
end: Alignment.bottomCenter,
|
||||
colors: [
|
||||
Color(0x00000000),
|
||||
Color(0xb7000000)
|
||||
],
|
||||
stops: [0.4219, 1]
|
||||
)
|
||||
)
|
||||
),
|
||||
) : Container(),
|
||||
Positioned(
|
||||
bottom: 16,
|
||||
left: 16,
|
||||
right: 16,
|
||||
child: AutoSizeText(entity.title,
|
||||
textAlign: TextAlign.center,
|
||||
maxLines: 2,
|
||||
minFontSize: 8,
|
||||
overflow: TextOverflow.ellipsis,
|
||||
style: TextStyle(
|
||||
color: hasImage ? Colors.white : Color(0xFF282828),
|
||||
fontWeight: FontWeight.w500,
|
||||
fontSize: 14,
|
||||
height: 20 / 14
|
||||
),
|
||||
)
|
||||
)
|
||||
],
|
||||
)
|
||||
);
|
||||
}
|
||||
|
||||
Widget _buildEntityListCard(BuildContext context, DashboardInfo dashboard, bool listWidgetCard) {
|
||||
return Row(
|
||||
mainAxisSize: briefView ? MainAxisSize.min : MainAxisSize.max,
|
||||
mainAxisSize: listWidgetCard ? MainAxisSize.min : MainAxisSize.max,
|
||||
children: [
|
||||
Flexible(
|
||||
fit: briefView ? FlexFit.loose : FlexFit.tight,
|
||||
fit: listWidgetCard ? FlexFit.loose : FlexFit.tight,
|
||||
child:
|
||||
Container(
|
||||
padding: EdgeInsets.symmetric(vertical: briefView ? 9 : 10, horizontal: 16),
|
||||
padding: EdgeInsets.symmetric(vertical: listWidgetCard ? 9 : 10, horizontal: 16),
|
||||
child: Row(
|
||||
mainAxisSize: briefView ? MainAxisSize.min : MainAxisSize.max,
|
||||
mainAxisSize: listWidgetCard ? MainAxisSize.min : MainAxisSize.max,
|
||||
children: [
|
||||
Flexible(
|
||||
fit: briefView ? FlexFit.loose : FlexFit.tight,
|
||||
fit: listWidgetCard ? FlexFit.loose : FlexFit.tight,
|
||||
child:
|
||||
Column(
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
@@ -60,7 +137,7 @@ mixin DashboardsBase on EntitiesBase<DashboardInfo> {
|
||||
],
|
||||
)
|
||||
),
|
||||
(!briefView ? Column(
|
||||
(!listWidgetCard ? Column(
|
||||
mainAxisAlignment: MainAxisAlignment.end,
|
||||
children: [
|
||||
Text(entityDateFormat.format(DateTime.fromMillisecondsSinceEpoch(dashboard.createdTime!)),
|
||||
@@ -95,10 +172,4 @@ mixin DashboardsBase on EntitiesBase<DashboardInfo> {
|
||||
return dashboard.assignedCustomers.any((element) => element.isPublic);
|
||||
}
|
||||
|
||||
@override
|
||||
void onEntityDetails(DashboardInfo dashboard) {
|
||||
navigateTo('/dashboard/${dashboard.id!.id}?title=${dashboard.title}');
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
||||
13
lib/modules/dashboard/dashboards_grid.dart
Normal file
13
lib/modules/dashboard/dashboards_grid.dart
Normal file
@@ -0,0 +1,13 @@
|
||||
import 'package:thingsboard_app/core/context/tb_context.dart';
|
||||
import 'package:thingsboard_app/core/entity/entities_base.dart';
|
||||
import 'package:thingsboard_app/core/entity/entities_grid.dart';
|
||||
import 'package:thingsboard_client/thingsboard_client.dart';
|
||||
|
||||
import 'dashboards_base.dart';
|
||||
|
||||
class DashboardsGrid extends BaseEntitiesPageLinkWidget<DashboardInfo> with DashboardsBase, EntitiesGridStateBase {
|
||||
|
||||
DashboardsGrid(TbContext tbContext) : super(tbContext);
|
||||
|
||||
}
|
||||
|
||||
13
lib/modules/dashboard/dashboards_list.dart
Normal file
13
lib/modules/dashboard/dashboards_list.dart
Normal file
@@ -0,0 +1,13 @@
|
||||
import 'package:thingsboard_app/core/context/tb_context.dart';
|
||||
import 'package:thingsboard_app/core/entity/entities_list.dart';
|
||||
import 'package:thingsboard_app/core/entity/entities_base.dart';
|
||||
import 'package:thingsboard_client/thingsboard_client.dart';
|
||||
|
||||
import 'dashboards_base.dart';
|
||||
|
||||
class DashboardsList extends BaseEntitiesPageLinkWidget<DashboardInfo> with DashboardsBase, EntitiesListStateBase {
|
||||
|
||||
DashboardsList(TbContext tbContext) : super(tbContext);
|
||||
|
||||
}
|
||||
|
||||
15
lib/modules/dashboard/dashboards_list_widget.dart
Normal file
15
lib/modules/dashboard/dashboards_list_widget.dart
Normal file
@@ -0,0 +1,15 @@
|
||||
import 'package:thingsboard_app/core/context/tb_context.dart';
|
||||
import 'package:thingsboard_app/core/entity/entities_list_widget.dart';
|
||||
import 'package:thingsboard_app/modules/dashboard/dashboards_base.dart';
|
||||
import 'package:thingsboard_client/thingsboard_client.dart';
|
||||
|
||||
class DashboardsListWidget extends EntitiesListPageLinkWidget<DashboardInfo> with DashboardsBase {
|
||||
|
||||
DashboardsListWidget(TbContext tbContext, {EntitiesListWidgetController? controller}): super(tbContext, controller: controller);
|
||||
|
||||
@override
|
||||
void onViewAll() {
|
||||
navigateTo('/dashboards');
|
||||
}
|
||||
|
||||
}
|
||||
@@ -1,18 +1,31 @@
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:thingsboard_app/core/context/tb_context.dart';
|
||||
import 'package:thingsboard_app/core/entity/entities_page.dart';
|
||||
import 'package:thingsboard_app/modules/dashboard/dashboards_base.dart';
|
||||
import 'package:thingsboard_client/thingsboard_client.dart';
|
||||
import 'package:thingsboard_app/core/context/tb_context_widget.dart';
|
||||
import 'package:thingsboard_app/widgets/tb_app_bar.dart';
|
||||
|
||||
class DashboardsPage extends EntitiesPage<DashboardInfo> with DashboardsBase {
|
||||
import 'dashboards_list.dart';
|
||||
|
||||
DashboardsPage(TbContext tbContext) :
|
||||
super(tbContext);
|
||||
class DashboardsPage extends TbPageWidget<DashboardsPage, _DashboardsPageState> {
|
||||
|
||||
DashboardsPage(TbContext tbContext) : super(tbContext);
|
||||
|
||||
@override
|
||||
String get noMoreItemsText => 'No more dashboards';
|
||||
|
||||
@override
|
||||
String get searchHint => 'Search dashboards';
|
||||
_DashboardsPageState createState() => _DashboardsPageState();
|
||||
|
||||
}
|
||||
|
||||
class _DashboardsPageState extends TbPageState<DashboardsPage, _DashboardsPageState> {
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
var dashboardsList = DashboardsList(tbContext);
|
||||
return Scaffold(
|
||||
appBar: TbAppBar(
|
||||
tbContext,
|
||||
title: Text(dashboardsList.title)
|
||||
),
|
||||
body: dashboardsList
|
||||
);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -1,15 +0,0 @@
|
||||
import 'package:thingsboard_app/core/context/tb_context.dart';
|
||||
import 'package:thingsboard_app/core/entity/entities_widget.dart';
|
||||
import 'package:thingsboard_app/modules/dashboard/dashboards_base.dart';
|
||||
import 'package:thingsboard_client/thingsboard_client.dart';
|
||||
|
||||
class DashboardsWidget extends EntitiesWidget<DashboardInfo> with DashboardsBase {
|
||||
|
||||
DashboardsWidget(TbContext tbContext, {EntitiesWidgetController? controller}): super(tbContext, controller: controller);
|
||||
|
||||
@override
|
||||
void onViewAll() {
|
||||
navigateTo('/dashboards');
|
||||
}
|
||||
|
||||
}
|
||||
@@ -5,7 +5,6 @@ import 'package:thingsboard_app/core/context/tb_context.dart';
|
||||
import 'package:thingsboard_app/modules/main/main_page.dart';
|
||||
|
||||
import 'device_details_page.dart';
|
||||
import 'devices_page.dart';
|
||||
|
||||
class DeviceRoutes extends TbRoutes {
|
||||
|
||||
|
||||
@@ -3,7 +3,7 @@ import 'package:flutter/widgets.dart';
|
||||
import 'package:thingsboard_app/core/entity/entities_base.dart';
|
||||
import 'package:thingsboard_client/thingsboard_client.dart';
|
||||
|
||||
mixin DevicesBase on EntitiesBase<DeviceInfo> {
|
||||
mixin DevicesBase on EntitiesBaseWithPageLink<DeviceInfo> {
|
||||
|
||||
@override
|
||||
String get title => 'Devices';
|
||||
@@ -21,35 +21,56 @@ mixin DevicesBase on EntitiesBase<DeviceInfo> {
|
||||
}
|
||||
|
||||
@override
|
||||
void onEntityDetails(DeviceInfo device) {
|
||||
void onEntityTap(DeviceInfo device) {
|
||||
navigateTo('/device/${device.id!.id}');
|
||||
}
|
||||
|
||||
@override
|
||||
Widget buildEntityCard(BuildContext context, DeviceInfo device, bool briefView) {
|
||||
Widget? buildHeading(BuildContext context) {
|
||||
return Text('Hobo Devices!');
|
||||
}
|
||||
|
||||
|
||||
@override
|
||||
Widget buildEntityListCard(BuildContext context, DeviceInfo device) {
|
||||
return _buildEntityListCard(context, device, false);
|
||||
}
|
||||
|
||||
@override
|
||||
Widget buildEntityListWidgetCard(BuildContext context, DeviceInfo device) {
|
||||
return _buildEntityListCard(context, device, true);
|
||||
}
|
||||
|
||||
@override
|
||||
Widget buildEntityGridCard(BuildContext context, DeviceInfo device) {
|
||||
return Text(device.name);
|
||||
}
|
||||
|
||||
Widget _buildEntityListCard(BuildContext context, DeviceInfo device, bool listWidgetCard) {
|
||||
return Row(
|
||||
mainAxisSize: briefView ? MainAxisSize.min : MainAxisSize.max,
|
||||
mainAxisSize: listWidgetCard ? MainAxisSize.min : MainAxisSize.max,
|
||||
children: [
|
||||
Container(
|
||||
width: briefView ? 58 : 60,
|
||||
width: listWidgetCard ? 58 : 60,
|
||||
height: listWidgetCard ? 58 : 60,
|
||||
decoration: BoxDecoration(
|
||||
color: Color(0xFFEEEEEE),
|
||||
borderRadius: BorderRadius.horizontal(left: Radius.circular(briefView ? 4 : 6))
|
||||
borderRadius: BorderRadius.horizontal(left: Radius.circular(listWidgetCard ? 4 : 6))
|
||||
),
|
||||
child: Center(
|
||||
child: Icon(Icons.devices_other, color: Color(0xFFC2C2C2))
|
||||
),
|
||||
),
|
||||
Flexible(
|
||||
fit: briefView ? FlexFit.loose : FlexFit.tight,
|
||||
fit: listWidgetCard ? FlexFit.loose : FlexFit.tight,
|
||||
child:
|
||||
Container(
|
||||
padding: EdgeInsets.symmetric(vertical: briefView ? 9 : 10, horizontal: 16),
|
||||
padding: EdgeInsets.symmetric(vertical: listWidgetCard ? 9 : 10, horizontal: 16),
|
||||
child: Row(
|
||||
mainAxisSize: briefView ? MainAxisSize.min : MainAxisSize.max,
|
||||
mainAxisSize: listWidgetCard ? MainAxisSize.min : MainAxisSize.max,
|
||||
children: [
|
||||
Flexible(
|
||||
fit: briefView ? FlexFit.loose : FlexFit.tight,
|
||||
fit: listWidgetCard ? FlexFit.loose : FlexFit.tight,
|
||||
child:
|
||||
Column(
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
@@ -75,7 +96,7 @@ mixin DevicesBase on EntitiesBase<DeviceInfo> {
|
||||
],
|
||||
)
|
||||
),
|
||||
(!briefView ? Column(
|
||||
(!listWidgetCard ? Column(
|
||||
mainAxisAlignment: MainAxisAlignment.end,
|
||||
children: [
|
||||
Text(entityDateFormat.format(DateTime.fromMillisecondsSinceEpoch(device.createdTime!)),
|
||||
|
||||
12
lib/modules/device/devices_list.dart
Normal file
12
lib/modules/device/devices_list.dart
Normal file
@@ -0,0 +1,12 @@
|
||||
import 'package:thingsboard_app/core/context/tb_context.dart';
|
||||
import 'package:thingsboard_app/core/entity/entities_base.dart';
|
||||
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 BaseEntitiesPageLinkWidget<DeviceInfo> with DevicesBase, EntitiesListStateBase {
|
||||
|
||||
DevicesList(TbContext tbContext) : super(tbContext);
|
||||
|
||||
}
|
||||
|
||||
15
lib/modules/device/devices_list_widget.dart
Normal file
15
lib/modules/device/devices_list_widget.dart
Normal file
@@ -0,0 +1,15 @@
|
||||
import 'package:thingsboard_app/core/context/tb_context.dart';
|
||||
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 EntitiesListPageLinkWidget<DeviceInfo> with DevicesBase {
|
||||
|
||||
DevicesListWidget(TbContext tbContext, {EntitiesListWidgetController? controller}): super(tbContext, controller: controller);
|
||||
|
||||
@override
|
||||
void onViewAll() {
|
||||
navigateTo('/devices');
|
||||
}
|
||||
|
||||
}
|
||||
@@ -1,99 +1,30 @@
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:thingsboard_app/core/context/tb_context.dart';
|
||||
import 'package:thingsboard_app/core/entity/entities_page.dart';
|
||||
import 'package:thingsboard_app/modules/device/devices_base.dart';
|
||||
import 'package:thingsboard_client/thingsboard_client.dart';
|
||||
import 'package:thingsboard_app/core/context/tb_context_widget.dart';
|
||||
import 'package:thingsboard_app/modules/device/devices_list.dart';
|
||||
import 'package:thingsboard_app/widgets/tb_app_bar.dart';
|
||||
|
||||
class DevicesPage extends EntitiesPage<DeviceInfo> with DevicesBase {
|
||||
class DevicesPage extends TbContextWidget<DevicesPage, _DevicesPageState> {
|
||||
|
||||
DevicesPage(TbContext tbContext) : super(tbContext);
|
||||
|
||||
@override
|
||||
String get noMoreItemsText => 'No more devices';
|
||||
|
||||
@override
|
||||
String get searchHint => 'Search devices';
|
||||
_DevicesPageState createState() => _DevicesPageState();
|
||||
|
||||
}
|
||||
|
||||
/* bottomNavigationBar: BottomAppBar(
|
||||
shape: CircularNotchedRectangle(),
|
||||
notchMargin: 4.0,
|
||||
child: new Row(
|
||||
children: <Widget>[
|
||||
IconButton(icon: Icon(Icons.refresh), onPressed: () {
|
||||
refresh();
|
||||
},),
|
||||
Spacer(),
|
||||
IconButton(icon: Icon(Icons.search), onPressed: () {
|
||||
_searchModeNotifier.value = true;
|
||||
}),
|
||||
_simplePopup(),
|
||||
],
|
||||
class _DevicesPageState extends TbContextState<DevicesPage, _DevicesPageState> {
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
var devicesList = DevicesList(tbContext);
|
||||
return Scaffold(
|
||||
appBar: TbAppBar(
|
||||
tbContext,
|
||||
title: Text(devicesList.title)
|
||||
),
|
||||
),
|
||||
floatingActionButtonLocation: FloatingActionButtonLocation.centerDocked,
|
||||
floatingActionButton: FloatingActionButton(
|
||||
child: const Icon(Icons.add), onPressed: () {},),
|
||||
body: devicesList
|
||||
);
|
||||
}
|
||||
|
||||
Widget _simplePopup() => PopupMenuButton<int>(
|
||||
itemBuilder: (context) => [
|
||||
PopupMenuItem(
|
||||
value: 1,
|
||||
child: Text("First"),
|
||||
),
|
||||
PopupMenuItem(
|
||||
value: 2,
|
||||
child: ListTile(
|
||||
leading: Icon(Icons.work),
|
||||
title: Text('Second'),
|
||||
)
|
||||
),
|
||||
],
|
||||
icon: Icon(Icons.settings),
|
||||
);
|
||||
|
||||
SpeedDial speedDial(context) => SpeedDial(
|
||||
animatedIcon: AnimatedIcons.menu_close,
|
||||
animatedIconTheme: IconThemeData(size: 22),
|
||||
backgroundColor: Theme.of(context).colorScheme.secondary,
|
||||
foregroundColor: Colors.white,
|
||||
visible: true,
|
||||
curve: Curves.bounceIn,
|
||||
children: [
|
||||
// FAB 1
|
||||
SpeedDialChild(
|
||||
child: Icon(Icons.refresh),
|
||||
backgroundColor: Theme.of(context).colorScheme.secondary,
|
||||
foregroundColor: Colors.white,
|
||||
onTap: () {
|
||||
refresh();
|
||||
/* setState(() {
|
||||
var rng = Random();
|
||||
var pageSize = 1 + rng.nextInt(9);
|
||||
futureDevices = tbContext.tbClient.getDeviceService().getTenantDeviceInfos(PageLink(pageSize));
|
||||
}); */
|
||||
},
|
||||
label: 'Refresh',
|
||||
labelStyle: TextStyle(
|
||||
fontWeight: FontWeight.w500,
|
||||
fontSize: 16.0),
|
||||
),
|
||||
// FAB 2
|
||||
SpeedDialChild(
|
||||
child: Icon(Icons.logout),
|
||||
backgroundColor: Theme.of(context).colorScheme.secondary,
|
||||
foregroundColor: Colors.white,
|
||||
onTap: () {
|
||||
tbClient.logout(requestConfig: RequestConfig(ignoreErrors: true));
|
||||
},
|
||||
label: 'Logout',
|
||||
labelStyle: TextStyle(
|
||||
fontWeight: FontWeight.w500,
|
||||
fontSize: 16.0),
|
||||
)
|
||||
],
|
||||
);
|
||||
}
|
||||
*/
|
||||
|
||||
@@ -1,15 +0,0 @@
|
||||
import 'package:thingsboard_app/core/context/tb_context.dart';
|
||||
import 'package:thingsboard_app/core/entity/entities_widget.dart';
|
||||
import 'package:thingsboard_app/modules/device/devices_base.dart';
|
||||
import 'package:thingsboard_client/thingsboard_client.dart';
|
||||
|
||||
class DevicesWidget extends EntitiesWidget<DeviceInfo> with DevicesBase {
|
||||
|
||||
DevicesWidget(TbContext tbContext, {EntitiesWidgetController? controller}): super(tbContext, controller: controller);
|
||||
|
||||
@override
|
||||
void onViewAll() {
|
||||
navigateTo('/devices');
|
||||
}
|
||||
|
||||
}
|
||||
@@ -1,15 +1,12 @@
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:flutter/widgets.dart';
|
||||
import 'package:thingsboard_app/core/entity/entities_widget.dart';
|
||||
import 'package:thingsboard_app/modules/asset/assets_widget.dart';
|
||||
import 'package:thingsboard_app/modules/dashboard/dashboards_widget.dart';
|
||||
import 'package:thingsboard_app/modules/device/devices_widget.dart';
|
||||
import 'package:thingsboard_app/widgets/tb_app_bar.dart';
|
||||
|
||||
import 'package:thingsboard_app/core/context/tb_context.dart';
|
||||
import 'package:thingsboard_app/core/context/tb_context_widget.dart';
|
||||
import 'package:thingsboard_client/thingsboard_client.dart';
|
||||
import 'package:thingsboard_app/core/entity/entities_list_widget.dart';
|
||||
import 'package:thingsboard_app/modules/dashboard/dashboard.dart' as dashboardUi;
|
||||
import 'package:thingsboard_app/modules/dashboard/dashboards_grid.dart';
|
||||
import 'package:thingsboard_app/widgets/tb_app_bar.dart';
|
||||
import 'package:thingsboard_client/thingsboard_client.dart';
|
||||
|
||||
class HomePage extends TbContextWidget<HomePage, _HomePageState> {
|
||||
|
||||
@@ -22,7 +19,7 @@ class HomePage extends TbContextWidget<HomePage, _HomePageState> {
|
||||
|
||||
class _HomePageState extends TbContextState<HomePage, _HomePageState> {
|
||||
|
||||
final EntitiesWidgetController _entitiesWidgetController = EntitiesWidgetController();
|
||||
final EntitiesListWidgetController _entitiesWidgetController = EntitiesListWidgetController();
|
||||
|
||||
@override
|
||||
void initState() {
|
||||
@@ -42,7 +39,6 @@ class _HomePageState extends TbContextState<HomePage, _HomePageState> {
|
||||
return Scaffold(
|
||||
appBar: TbAppBar(
|
||||
tbContext,
|
||||
showLoadingIndicator: !dashboardState,
|
||||
elevation: dashboardState ? 0 : null,
|
||||
title: const Text('Home'),
|
||||
),
|
||||
@@ -64,15 +60,14 @@ class _HomePageState extends TbContextState<HomePage, _HomePageState> {
|
||||
}
|
||||
|
||||
Widget _buildDefaultHome(BuildContext context) {
|
||||
return RefreshIndicator(
|
||||
onRefresh: () => _entitiesWidgetController.refresh(),
|
||||
child: ListView(
|
||||
children: _buildUserHome(context)
|
||||
)
|
||||
);
|
||||
if (tbClient.isSystemAdmin()) {
|
||||
return _buildSysAdminHome(context);
|
||||
} else {
|
||||
return DashboardsGrid(tbContext);
|
||||
}
|
||||
}
|
||||
|
||||
List<Widget> _buildUserHome(BuildContext context) {
|
||||
/* List<Widget> _buildUserHome(BuildContext context) {
|
||||
if (tbClient.isSystemAdmin()) {
|
||||
return _buildSysAdminHome(context);
|
||||
} else if (tbClient.isTenantAdmin()) {
|
||||
@@ -80,25 +75,30 @@ class _HomePageState extends TbContextState<HomePage, _HomePageState> {
|
||||
} else {
|
||||
return _buildCustomerUserHome(context);
|
||||
}
|
||||
} */
|
||||
|
||||
Widget _buildSysAdminHome(BuildContext context) {
|
||||
return RefreshIndicator(
|
||||
onRefresh: () => _entitiesWidgetController.refresh(),
|
||||
child: ListView(
|
||||
children: [Container(child: Text('TODO: Implement'))]
|
||||
)
|
||||
);
|
||||
}
|
||||
|
||||
List<Widget> _buildSysAdminHome(BuildContext context) {
|
||||
return [Container(child: Text('TODO: Implement'))];
|
||||
}
|
||||
|
||||
List<Widget> _buildTenantAdminHome(BuildContext context) {
|
||||
/* List<Widget> _buildTenantAdminHome(BuildContext context) {
|
||||
return [
|
||||
AssetsWidget(tbContext, controller: _entitiesWidgetController),
|
||||
DevicesWidget(tbContext, controller: _entitiesWidgetController),
|
||||
DashboardsWidget(tbContext, controller: _entitiesWidgetController)
|
||||
AssetsListWidget(tbContext, controller: _entitiesWidgetController),
|
||||
DevicesListWidget(tbContext, controller: _entitiesWidgetController),
|
||||
DashboardsListWidget(tbContext, controller: _entitiesWidgetController)
|
||||
];
|
||||
}
|
||||
|
||||
List<Widget> _buildCustomerUserHome(BuildContext context) {
|
||||
return [
|
||||
AssetsWidget(tbContext, controller: _entitiesWidgetController),
|
||||
DevicesWidget(tbContext, controller: _entitiesWidgetController),
|
||||
DashboardsWidget(tbContext, controller: _entitiesWidgetController)
|
||||
AssetsListWidget(tbContext, controller: _entitiesWidgetController),
|
||||
DevicesListWidget(tbContext, controller: _entitiesWidgetController),
|
||||
DashboardsListWidget(tbContext, controller: _entitiesWidgetController)
|
||||
];
|
||||
}
|
||||
} */
|
||||
}
|
||||
|
||||
@@ -3,6 +3,7 @@ import 'package:flutter/services.dart';
|
||||
import 'package:flutter/widgets.dart';
|
||||
import 'package:thingsboard_app/core/context/tb_context.dart';
|
||||
import 'package:thingsboard_app/core/context/tb_context_widget.dart';
|
||||
import 'package:thingsboard_app/modules/alarm/alarms_page.dart';
|
||||
import 'package:thingsboard_app/modules/dashboard/dashboards_page.dart';
|
||||
import 'package:thingsboard_app/modules/device/devices_page.dart';
|
||||
import 'package:thingsboard_app/modules/home/home_page.dart';
|
||||
@@ -58,18 +59,18 @@ class TbMainNavigationItem {
|
||||
case Authority.TENANT_ADMIN:
|
||||
case Authority.CUSTOMER_USER:
|
||||
items.addAll([
|
||||
TbMainNavigationItem(
|
||||
page: AlarmsPage(tbContext),
|
||||
title: 'Alarms',
|
||||
icon: Icon(Icons.notifications),
|
||||
path: '/alarms'
|
||||
),
|
||||
TbMainNavigationItem(
|
||||
page: DevicesPage(tbContext),
|
||||
title: 'Devices',
|
||||
icon: Icon(Icons.devices_other),
|
||||
path: '/devices'
|
||||
),
|
||||
TbMainNavigationItem(
|
||||
page: DashboardsPage(tbContext),
|
||||
title: 'Dashboards',
|
||||
icon: Icon(Icons.dashboard),
|
||||
path: '/dashboards'
|
||||
)
|
||||
)
|
||||
]);
|
||||
break;
|
||||
case Authority.REFRESH_TOKEN:
|
||||
|
||||
@@ -24,7 +24,7 @@ class TbAppBar extends TbContextWidget<TbAppBar, _TbAppBarState> implements Pref
|
||||
final Size preferredSize;
|
||||
|
||||
TbAppBar(TbContext tbContext, {this.title, this.elevation, this.showProfile = true, this.showLogout = false,
|
||||
this.showLoadingIndicator = true, this.searchModeNotifier, this.searchHint, this.onSearch, this.onSearchClosed}) :
|
||||
this.showLoadingIndicator = false, this.searchModeNotifier, this.searchHint, this.onSearch, this.onSearchClosed}) :
|
||||
preferredSize = Size.fromHeight(kToolbarHeight + (showLoadingIndicator ? 4 : 0)),
|
||||
super(tbContext);
|
||||
|
||||
|
||||
11
pubspec.lock
11
pubspec.lock
@@ -22,6 +22,13 @@ packages:
|
||||
url: "https://pub.dartlang.org"
|
||||
source: hosted
|
||||
version: "2.5.0"
|
||||
auto_size_text:
|
||||
dependency: "direct main"
|
||||
description:
|
||||
name: auto_size_text
|
||||
url: "https://pub.dartlang.org"
|
||||
source: hosted
|
||||
version: "3.0.0-nullsafety.0"
|
||||
boolean_selector:
|
||||
dependency: transitive
|
||||
description:
|
||||
@@ -218,7 +225,7 @@ packages:
|
||||
name: image_picker
|
||||
url: "https://pub.dartlang.org"
|
||||
source: hosted
|
||||
version: "0.7.4"
|
||||
version: "0.7.5"
|
||||
image_picker_for_web:
|
||||
dependency: transitive
|
||||
description:
|
||||
@@ -404,7 +411,7 @@ packages:
|
||||
description:
|
||||
path: "."
|
||||
ref: HEAD
|
||||
resolved-ref: "4c2463beceb4f397c9de79ac1b0dee628aaf8add"
|
||||
resolved-ref: "0627f3aea8e9252d7c178cec864908e564ddee0a"
|
||||
url: "git@github.com:thingsboard/dart_thingsboard_client.git"
|
||||
source: git
|
||||
version: "1.0.0"
|
||||
|
||||
@@ -19,6 +19,7 @@ dependencies:
|
||||
cupertino_icons: ^1.0.2
|
||||
fluro: ^2.0.3
|
||||
flutter_svg: ^0.22.0
|
||||
auto_size_text: ^3.0.0-nullsafety.0
|
||||
infinite_scroll_pagination: ^3.0.1
|
||||
fading_edge_scrollview: ^2.0.0
|
||||
stream_transform: ^2.0.0
|
||||
|
||||
Reference in New Issue
Block a user