Alarms, Devices and More pages

This commit is contained in:
Igor Kulikov
2021-05-25 20:19:00 +03:00
parent bbe8c0c0d8
commit 5e536ab217
36 changed files with 1690 additions and 338 deletions

View File

@@ -306,6 +306,20 @@ class TbContext {
router.pop<T>(currentState!.context, result);
}
}
Future<bool?> confirm({required String title, required String message, String cancel = 'Cancel', String ok = 'Ok'}) {
return showDialog<bool>(context: currentState!.context,
builder: (context) => AlertDialog(
title: Text(title),
content: Text(message),
actions: [
TextButton(onPressed: () => pop(false),
child: Text(cancel)),
TextButton(onPressed: () => pop(true),
child: Text(ok))
],
));
}
}
mixin HasTbContext {
@@ -343,6 +357,8 @@ mixin HasTbContext {
void pop<T>([T? result]) => _tbContext.pop<T>(result);
Future<bool?> confirm({required String title, required String message, String cancel = 'Cancel', String ok = 'Ok'}) => _tbContext.confirm(title: title, message: message, cancel: cancel, ok: ok);
void hideNotification() => _tbContext.hideNotification();
void showErrorNotification(String message, {Duration? duration}) => _tbContext.showErrorNotification(message, duration: duration);

View File

@@ -1,6 +1,11 @@
import 'package:flutter/material.dart';
import 'package:flutter/widgets.dart';
import 'package:thingsboard_app/core/context/tb_context.dart';
abstract class RefreshableWidget extends Widget {
refresh();
}
abstract class TbContextStatelessWidget extends StatelessWidget with HasTbContext {
TbContextStatelessWidget(TbContext tbContext, {Key? key}) : super(key: key) {
setTbContext(tbContext);
@@ -71,3 +76,23 @@ abstract class TbPageState<W extends TbPageWidget<W,S>, S extends TbPageState<W,
}
}
class TextContextWidget extends TbContextWidget<TextContextWidget, _TextContextWidgetState> {
final String text;
TextContextWidget(TbContext tbContext, this.text) : super(tbContext);
@override
_TextContextWidgetState createState() => _TextContextWidgetState();
}
class _TextContextWidgetState extends TbContextState<TextContextWidget, _TextContextWidgetState> {
@override
Widget build(BuildContext context) {
return Scaffold(body: Center(child: Text(widget.text)));
}
}

View File

@@ -42,10 +42,6 @@ mixin EntitiesBase<T, P> on HasTbContext {
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();
@@ -54,37 +50,67 @@ mixin EntitiesBase<T, P> on HasTbContext {
}
mixin EntitiesBaseWithPageLink<T> on EntitiesBase<T, PageLink> {
abstract class PageKeyController<P> extends ValueNotifier<PageKeyValue<P>> {
@override
PageLink createFirstKey({int pageSize = 10}) => PageLink(pageSize, 0, null, SortOrder('createdTime', Direction.DESC));
PageKeyController(P initialPageKey) : super(PageKeyValue(initialPageKey));
P nextPageKey(P pageKey);
}
class PageKeyValue<P> {
final P pageKey;
PageKeyValue(this.pageKey);
}
class PageLinkController extends PageKeyController<PageLink> {
PageLinkController({int pageSize = 10, String? searchText}) : super(PageLink(pageSize, 0, searchText, SortOrder('createdTime', Direction.DESC)));
@override
PageLink nextPageKey(PageLink pageKey) => pageKey.nextPageLink();
onSearchText(String searchText) {
value.pageKey.page = 0;
value.pageKey.textSearch = searchText;
notifyListeners();
}
}
mixin EntitiesBaseWithTimePageLink<T> on EntitiesBase<T, TimePageLink> {
class TimePageLinkController extends PageKeyController<TimePageLink> {
@override
TimePageLink createFirstKey({int pageSize = 10}) => TimePageLink(pageSize, 0, null, SortOrder('createdTime', Direction.DESC));
TimePageLinkController({int pageSize = 10, String? searchText}) : super(TimePageLink(pageSize, 0, searchText, SortOrder('createdTime', Direction.DESC)));
@override
TimePageLink nextPageKey(TimePageLink pageKey) => pageKey.nextPageLink();
}
onSearchText(String searchText) {
value.pageKey.page = 0;
value.pageKey.textSearch = searchText;
notifyListeners();
}
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);
final bool searchMode;
final PageKeyController<P> pageKeyController;
BaseEntitiesWidget(TbContext tbContext, this.pageKeyController, {this.searchMode = false}):
super(tbContext);
@override
Widget? buildHeading(BuildContext context) => searchMode ? Text('Search results', style: TextStyle(
color: Color(0xFFAFAFAF),
fontSize: 16,
height: 24 / 16
)) : null;
}
@@ -92,24 +118,42 @@ abstract class BaseEntitiesState<T, P> extends TbContextState<BaseEntitiesWidget
late final PagingController<P, T> pagingController;
Completer<void>? _refreshCompleter;
bool _dataLoading = false;
bool _scheduleRefresh = false;
bool _reloadData = false;
BaseEntitiesState();
@override
void initState() {
super.initState();
pagingController = PagingController(firstPageKey: widget.createFirstKey());
pagingController = PagingController(firstPageKey: widget.pageKeyController.value.pageKey);
widget.pageKeyController.addListener(_didChangePageKeyValue);
pagingController.addPageRequestListener((pageKey) {
_fetchPage(pageKey);
});
}
@override
void didUpdateWidget(BaseEntitiesWidget<T, P> oldWidget) {
super.didUpdateWidget(oldWidget);
if (widget.pageKeyController != oldWidget.pageKeyController) {
oldWidget.pageKeyController.removeListener(_didChangePageKeyValue);
widget.pageKeyController.addListener(_didChangePageKeyValue);
}
}
@override
void dispose() {
widget.pageKeyController.removeListener(_didChangePageKeyValue);
pagingController.dispose();
super.dispose();
}
bool _dataLoading = false;
bool _scheduleRefresh = false;
void _didChangePageKeyValue() {
_reloadData = true;
_refresh();
}
Future<void> _refresh() {
if (_refreshCompleter == null) {
@@ -124,7 +168,12 @@ abstract class BaseEntitiesState<T, P> extends TbContextState<BaseEntitiesWidget
}
void _refreshPagingController() {
_fetchPage(widget.createFirstKey(), refresh: true);
if (_reloadData) {
pagingController.refresh();
_reloadData = false;
} else {
_fetchPage(widget.pageKeyController.value.pageKey, refresh: true);
}
}
Future<void> _fetchPage(P pageKey, {bool refresh = false}) async {
@@ -143,7 +192,7 @@ abstract class BaseEntitiesState<T, P> extends TbContextState<BaseEntitiesWidget
if (isLastPage) {
pagingController.appendLastPage(pageData.data);
} else {
final nextPageKey = widget.nextPageKey(pageKey);
final nextPageKey = widget.pageKeyController.nextPageKey(pageKey);
pagingController.appendPage(pageData.data, nextPageKey);
}
} catch (error) {
@@ -207,7 +256,7 @@ abstract class BaseEntitiesState<T, P> extends TbContextState<BaseEntitiesWidget
return FirstPageExceptionIndicator(
title: widget.noItemsFoundText,
message: 'The list is currently empty.',
onTryAgain: () => pagingController.refresh(),
onTryAgain: widget.searchMode ? null : () => pagingController.refresh(),
);
}

View File

@@ -14,6 +14,8 @@ mixin EntitiesGridStateBase on StatefulWidget {
class _EntitiesGridState<T, P> extends BaseEntitiesState<T, P> {
_EntitiesGridState() : super();
@override
Widget pagedViewBuilder(BuildContext context) {
var heading = widget.buildHeading(context);

View File

@@ -14,6 +14,8 @@ mixin EntitiesListStateBase on StatefulWidget {
class _EntitiesListState<T,P> extends BaseEntitiesState<T, P> {
_EntitiesListState() : super();
@override
Widget pagedViewBuilder(BuildContext context) {
var heading = widget.buildHeading(context);

View File

@@ -32,8 +32,13 @@ class EntitiesListWidgetController {
}
abstract class EntitiesListPageLinkWidget<T> extends EntitiesListWidget<T, PageLink> with EntitiesBaseWithPageLink<T> {
EntitiesListPageLinkWidget(TbContext tbContext, {EntitiesListWidgetController? controller}): super(tbContext, controller: controller);
abstract class EntitiesListPageLinkWidget<T> extends EntitiesListWidget<T, PageLink> {
EntitiesListPageLinkWidget(TbContext tbContext, {EntitiesListWidgetController? controller}) : super(tbContext, controller: controller);
@override
PageKeyController<PageLink> createPageKeyController() => PageLinkController(pageSize: 5);
}
abstract class EntitiesListWidget<T, P> extends TbContextWidget<EntitiesListWidget<T,P>, _EntitiesListWidgetState<T,P>> with EntitiesBase<T,P> {
@@ -47,6 +52,8 @@ abstract class EntitiesListWidget<T, P> extends TbContextWidget<EntitiesListWidg
@override
_EntitiesListWidgetState createState() => _EntitiesListWidgetState(_controller);
PageKeyController<P> createPageKeyController();
void onViewAll();
}
@@ -55,6 +62,8 @@ class _EntitiesListWidgetState<T,P> extends TbContextState<EntitiesListWidget<T,
final EntitiesListWidgetController? _controller;
late final PageKeyController<P> _pageKeyController;
final StreamController<PageData<T>?> _entitiesStreamController = StreamController.broadcast();
_EntitiesListWidgetState(EntitiesListWidgetController? controller):
@@ -63,6 +72,7 @@ class _EntitiesListWidgetState<T,P> extends TbContextState<EntitiesListWidget<T,
@override
void initState() {
super.initState();
_pageKeyController = widget.createPageKeyController();
if (_controller != null) {
_controller!._registerEntitiesWidgetState(this);
}
@@ -74,13 +84,14 @@ class _EntitiesListWidgetState<T,P> extends TbContextState<EntitiesListWidget<T,
if (_controller != null) {
_controller!._unregisterEntitiesWidgetState(this);
}
_pageKeyController.dispose();
_entitiesStreamController.close();
super.dispose();
}
Future<void> _refresh() {
_entitiesStreamController.add(null);
var entitiesFuture = widget.fetchEntities(widget.createFirstKey(pageSize: 5));
var entitiesFuture = widget.fetchEntities(_pageKeyController.value.pageKey);
entitiesFuture.then((value) => _entitiesStreamController.add(value));
return entitiesFuture;
}