Dashboard webview hybrid composition support

This commit is contained in:
Igor Kulikov
2021-06-04 15:35:59 +03:00
parent 125ecab009
commit 5be1d5491f
10 changed files with 108 additions and 21 deletions

View File

@@ -1,5 +1,5 @@
buildscript { buildscript {
ext.kotlin_version = '1.3.50' ext.kotlin_version = '1.5.10'
repositories { repositories {
google() google()
jcenter() jcenter()

View File

@@ -92,19 +92,33 @@ class ThingsboardAppState extends State<ThingsboardApp> with TickerProviderState
} }
Future<bool> _openMain({bool animate: true}) async { Future<bool> _openMain({bool animate: true}) async {
return _mainStackController.open(0, animate: animate); var res = await _mainStackController.open(0, animate: animate);
if (res) {
await _mainDashboardPageController.deactivateDashboard();
}
return res;
} }
Future<bool> _closeMain({bool animate: true}) async { Future<bool> _closeMain({bool animate: true}) async {
if (!isDashboardOpen()) {
await _mainDashboardPageController.activateDashboard();
}
return _mainStackController.close(0, animate: animate); return _mainStackController.close(0, animate: animate);
} }
Future<bool> _openDashboard({bool animate: true}) async { Future<bool> _openDashboard({bool animate: true}) async {
if (!isDashboardOpen()) {
_mainDashboardPageController.activateDashboard();
}
return _mainStackController.open(1, animate: animate); return _mainStackController.open(1, animate: animate);
} }
Future<bool> _closeDashboard({bool animate: true}) async { Future<bool> _closeDashboard({bool animate: true}) async {
return _mainStackController.close(1, animate: animate); var res = await _mainStackController.close(1, animate: animate);
if (res) {
_mainDashboardPageController.deactivateDashboard();
}
return res;
} }

View File

@@ -9,6 +9,7 @@ import 'package:thingsboard_app/constants/api_path.dart';
import 'package:thingsboard_app/core/context/tb_context.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/context/tb_context_widget.dart';
import 'package:thingsboard_app/widgets/tb_progress_indicator.dart'; import 'package:thingsboard_app/widgets/tb_progress_indicator.dart';
import 'package:thingsboard_app/widgets/two_value_listenable_builder.dart';
import 'package:url_launcher/url_launcher.dart'; import 'package:url_launcher/url_launcher.dart';
class DashboardController { class DashboardController {
@@ -29,6 +30,14 @@ class DashboardController {
canGoBack.value = await canGoBackFuture; canGoBack.value = await canGoBackFuture;
} }
Future<void> activateDashboard() async {
await dashboardState._activateDashboard();
}
Future<void> deactivateDashboard() async {
await dashboardState._deactivateDashboard();
}
dispose() { dispose() {
canGoBack.dispose(); canGoBack.dispose();
} }
@@ -42,11 +51,13 @@ typedef DashboardControllerCallback = void Function(DashboardController controll
class Dashboard extends TbContextWidget<Dashboard, _DashboardState> { class Dashboard extends TbContextWidget<Dashboard, _DashboardState> {
final bool? _home; final bool? _home;
final bool _activeByDefault;
final DashboardTitleCallback? _titleCallback; final DashboardTitleCallback? _titleCallback;
final DashboardControllerCallback? _controllerCallback; final DashboardControllerCallback? _controllerCallback;
Dashboard(TbContext tbContext, {Key? key, bool? home, DashboardTitleCallback? titleCallback, DashboardControllerCallback? controllerCallback}): Dashboard(TbContext tbContext, {Key? key, bool? home, bool activeByDefault = true, DashboardTitleCallback? titleCallback, DashboardControllerCallback? controllerCallback}):
this._home = home, this._home = home,
this._activeByDefault = activeByDefault,
this._titleCallback = titleCallback, this._titleCallback = titleCallback,
this._controllerCallback = controllerCallback, this._controllerCallback = controllerCallback,
super(tbContext); super(tbContext);
@@ -62,6 +73,7 @@ class _DashboardState extends TbContextState<Dashboard, _DashboardState> {
bool webViewLoading = true; bool webViewLoading = true;
final ValueNotifier<bool> dashboardLoading = ValueNotifier(true); final ValueNotifier<bool> dashboardLoading = ValueNotifier(true);
final ValueNotifier<bool> dashboardActive = ValueNotifier(true);
final ValueNotifier<bool> readyState = ValueNotifier(false); final ValueNotifier<bool> readyState = ValueNotifier(false);
final GlobalKey webViewKey = GlobalKey(); final GlobalKey webViewKey = GlobalKey();
@@ -80,7 +92,7 @@ class _DashboardState extends TbContextState<Dashboard, _DashboardState> {
// useOnDownloadStart: true // useOnDownloadStart: true
), ),
android: AndroidInAppWebViewOptions( android: AndroidInAppWebViewOptions(
useHybridComposition: false, useHybridComposition: true,
thirdPartyCookiesEnabled: true thirdPartyCookiesEnabled: true
), ),
ios: IOSInAppWebViewOptions( ios: IOSInAppWebViewOptions(
@@ -92,6 +104,7 @@ class _DashboardState extends TbContextState<Dashboard, _DashboardState> {
@override @override
void initState() { void initState() {
super.initState(); super.initState();
dashboardActive.value = widget._activeByDefault;
_dashboardController = DashboardController(this); _dashboardController = DashboardController(this);
if (widget._controllerCallback != null) { if (widget._controllerCallback != null) {
widget._controllerCallback!(_dashboardController); widget._controllerCallback!(_dashboardController);
@@ -139,6 +152,18 @@ class _DashboardState extends TbContextState<Dashboard, _DashboardState> {
super.dispose(); super.dispose();
} }
Future<void> _activateDashboard() async {
if (!dashboardActive.value) {
dashboardActive.value = true;
}
}
Future<void> _deactivateDashboard() async {
if (dashboardActive.value) {
dashboardActive.value = false;
}
}
Future<void> _openDashboard(String dashboardId, {String? state, bool? hideToolbar, bool fullscreen = false}) async { Future<void> _openDashboard(String dashboardId, {String? state, bool? hideToolbar, bool fullscreen = false}) async {
_fullscreen = fullscreen; _fullscreen = fullscreen;
dashboardLoading.value = true; dashboardLoading.value = true;
@@ -262,7 +287,7 @@ class _DashboardState extends TbContextState<Dashboard, _DashboardState> {
return Platform.isIOS ? NavigationActionPolicy.ALLOW : NavigationActionPolicy.CANCEL; return Platform.isIOS ? NavigationActionPolicy.ALLOW : NavigationActionPolicy.CANCEL;
}, },
onUpdateVisitedHistory: (controller, url, androidIsReload) async { onUpdateVisitedHistory: (controller, url, androidIsReload) async {
log.debug('onUpdateVisitedHistory: url'); log.debug('onUpdateVisitedHistory: $url');
_dashboardController.onHistoryUpdated(controller.canGoBack()); _dashboardController.onHistoryUpdated(controller.canGoBack());
}, },
onConsoleMessage: (controller, consoleMessage) { onConsoleMessage: (controller, consoleMessage) {
@@ -285,10 +310,11 @@ class _DashboardState extends TbContextState<Dashboard, _DashboardState> {
action: PermissionRequestResponseAction.GRANT); action: PermissionRequestResponseAction.GRANT);
}, },
), ),
ValueListenableBuilder( TwoValueListenableBuilder(
valueListenable: dashboardLoading, firstValueListenable: dashboardLoading,
builder: (BuildContext context, bool loading, child) { secondValueListenable: dashboardActive,
if (!loading) { builder: (BuildContext context, bool loading, bool active, child) {
if (!loading && active) {
return SizedBox.shrink(); return SizedBox.shrink();
} else { } else {
var data = MediaQueryData.fromWindow(WidgetsBinding.instance!.window); var data = MediaQueryData.fromWindow(WidgetsBinding.instance!.window);

View File

@@ -27,6 +27,7 @@ mixin DashboardsBase on EntitiesBase<DashboardInfo, PageLink> {
@override @override
void onEntityTap(DashboardInfo dashboard) { void onEntityTap(DashboardInfo dashboard) {
navigateToDashboard(dashboard.id!.id!, dashboardTitle: dashboard.title); navigateToDashboard(dashboard.id!.id!, dashboardTitle: dashboard.title);
// navigateTo('/fullscreenDashboard/${dashboard.id!.id}?title=${dashboard.title}');
// navigateTo('/dashboard/${dashboard.id!.id}?title=${dashboard.title}'); // navigateTo('/dashboard/${dashboard.id!.id}?title=${dashboard.title}');
} }

View File

@@ -55,7 +55,8 @@ class _FullscreenDashboardPageState extends TbPageState<FullscreenDashboardPage,
} }
) : null, ) : null,
showLoadingIndicator: false, showLoadingIndicator: false,
elevation: 0, elevation: 1,
shadowColor: Colors.transparent,
title: ValueListenableBuilder<String>( title: ValueListenableBuilder<String>(
valueListenable: dashboardTitleValue, valueListenable: dashboardTitleValue,
builder: (context, title, widget) { builder: (context, title, widget) {

View File

@@ -32,6 +32,15 @@ class MainDashboardPageController {
} }
await _dashboardController?.openDashboard(dashboardId, state: state, hideToolbar: hideToolbar); await _dashboardController?.openDashboard(dashboardId, state: state, hideToolbar: hideToolbar);
} }
Future<void> activateDashboard() async {
await _dashboardController?.activateDashboard();
}
Future<void> deactivateDashboard() async {
await _dashboardController?.deactivateDashboard();
}
} }
class MainDashboardPage extends TbContextWidget<MainDashboardPage, _MainDashboardPageState> { class MainDashboardPage extends TbContextWidget<MainDashboardPage, _MainDashboardPageState> {
@@ -84,7 +93,8 @@ class _MainDashboardPageState extends TbContextState<MainDashboardPage, _MainDas
} }
), ),
showLoadingIndicator: false, showLoadingIndicator: false,
elevation: 0, elevation: 1,
shadowColor: Colors.transparent,
title: ValueListenableBuilder<String>( title: ValueListenableBuilder<String>(
valueListenable: dashboardTitleValue, valueListenable: dashboardTitleValue,
builder: (context, title, widget) { builder: (context, title, widget) {
@@ -98,6 +108,7 @@ class _MainDashboardPageState extends TbContextState<MainDashboardPage, _MainDas
), ),
body: Dashboard( body: Dashboard(
tbContext, tbContext,
activeByDefault: false,
titleCallback: (title) { titleCallback: (title) {
dashboardTitleValue.value = title; dashboardTitleValue.value = title;
}, },

View File

@@ -12,13 +12,14 @@ class TbAppBar extends TbContextWidget<TbAppBar, _TbAppBarState> implements Pref
final Widget? title; final Widget? title;
final List<Widget>? actions; final List<Widget>? actions;
final double? elevation; final double? elevation;
final Color? shadowColor;
final bool showLoadingIndicator; final bool showLoadingIndicator;
@override @override
final Size preferredSize; final Size preferredSize;
TbAppBar(TbContext tbContext, {this.leading, this.title, this.actions, this.elevation = 8, TbAppBar(TbContext tbContext, {this.leading, this.title, this.actions, this.elevation = 8,
this.showLoadingIndicator = false}) : this.shadowColor, this.showLoadingIndicator = false}) :
preferredSize = Size.fromHeight(kToolbarHeight + (showLoadingIndicator ? 4 : 0)), preferredSize = Size.fromHeight(kToolbarHeight + (showLoadingIndicator ? 4 : 0)),
super(tbContext); super(tbContext);
@@ -68,7 +69,7 @@ class _TbAppBarState extends TbContextState<TbAppBar, _TbAppBarState> {
title: widget.title, title: widget.title,
actions: widget.actions, actions: widget.actions,
elevation: widget.elevation, elevation: widget.elevation,
shadowColor: Color(0xFFFFFFFF).withAlpha(150), shadowColor: widget.shadowColor ?? Color(0xFFFFFFFF).withAlpha(150),
); );
} }
} }

View File

@@ -0,0 +1,33 @@
import 'package:flutter/foundation.dart';
import 'package:flutter/widgets.dart';
class TwoValueListenableBuilder<A, B> extends StatelessWidget {
TwoValueListenableBuilder(
{
Key? key,
required this.firstValueListenable,
required this.secondValueListenable,
required this.builder,
this.child,
}) : super(key: key);
final ValueListenable<A> firstValueListenable;
final ValueListenable<B> secondValueListenable;
final Widget? child;
final Widget Function(BuildContext context, A a, B b, Widget? child) builder;
@override
Widget build(BuildContext context) {
return ValueListenableBuilder<A>(
valueListenable: firstValueListenable,
builder: (_, a, __) {
return ValueListenableBuilder<B>(
valueListenable: secondValueListenable,
builder: (context, b, __) {
return builder(context, a, b, child);
},
);
},
);
}
}

View File

@@ -21,7 +21,7 @@ packages:
name: async name: async
url: "https://pub.dartlang.org" url: "https://pub.dartlang.org"
source: hosted source: hosted
version: "2.5.0" version: "2.6.1"
auto_size_text: auto_size_text:
dependency: "direct main" dependency: "direct main"
description: description:
@@ -225,7 +225,7 @@ packages:
name: image_picker name: image_picker
url: "https://pub.dartlang.org" url: "https://pub.dartlang.org"
source: hosted source: hosted
version: "0.7.5+3" version: "0.8.0+1"
image_picker_for_web: image_picker_for_web:
dependency: transitive dependency: transitive
description: description:
@@ -344,7 +344,7 @@ packages:
name: qr_code_scanner name: qr_code_scanner
url: "https://pub.dartlang.org" url: "https://pub.dartlang.org"
source: hosted source: hosted
version: "0.4.0" version: "0.5.1"
sky_engine: sky_engine:
dependency: transitive dependency: transitive
description: flutter description: flutter
@@ -363,7 +363,7 @@ packages:
name: source_span name: source_span
url: "https://pub.dartlang.org" url: "https://pub.dartlang.org"
source: hosted source: hosted
version: "1.8.0" version: "1.8.1"
stack_trace: stack_trace:
dependency: transitive dependency: transitive
description: description:
@@ -405,7 +405,7 @@ packages:
name: test_api name: test_api
url: "https://pub.dartlang.org" url: "https://pub.dartlang.org"
source: hosted source: hosted
version: "0.2.19" version: "0.3.0"
thingsboard_client: thingsboard_client:
dependency: "direct main" dependency: "direct main"
description: description:

View File

@@ -28,10 +28,10 @@ dependencies:
# permission_handler: ^8.0.0+2 # permission_handler: ^8.0.0+2
# path_provider: ^2.0.2 # path_provider: ^2.0.2
url_launcher: ^6.0.3 url_launcher: ^6.0.3
image_picker: ^0.7.4 image_picker: ^0.8.0
mime: ^1.0.0 mime: ^1.0.0
logger: ^1.0.0 logger: ^1.0.0
qr_code_scanner: ^0.4.0 qr_code_scanner: ^0.5.1
device_info: ^2.0.0 device_info: ^2.0.0
geolocator: ^7.0.3 geolocator: ^7.0.3