Dashboard webview hybrid composition support
This commit is contained in:
@@ -1,5 +1,5 @@
|
|||||||
buildscript {
|
buildscript {
|
||||||
ext.kotlin_version = '1.3.50'
|
ext.kotlin_version = '1.5.10'
|
||||||
repositories {
|
repositories {
|
||||||
google()
|
google()
|
||||||
jcenter()
|
jcenter()
|
||||||
|
|||||||
@@ -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;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
@@ -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);
|
||||||
|
|||||||
@@ -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}');
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -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) {
|
||||||
|
|||||||
@@ -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;
|
||||||
},
|
},
|
||||||
|
|||||||
@@ -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),
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
33
lib/widgets/two_value_listenable_builder.dart
Normal file
33
lib/widgets/two_value_listenable_builder.dart
Normal 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);
|
||||||
|
},
|
||||||
|
);
|
||||||
|
},
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
10
pubspec.lock
10
pubspec.lock
@@ -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:
|
||||||
|
|||||||
@@ -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
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user