diff --git a/android/app/build.gradle b/android/app/build.gradle index fe68591..b37341b 100644 --- a/android/app/build.gradle +++ b/android/app/build.gradle @@ -26,7 +26,7 @@ apply plugin: 'kotlin-android' apply from: "$flutterRoot/packages/flutter_tools/gradle/flutter.gradle" android { - compileSdkVersion 31 + compileSdkVersion 33 sourceSets { main.java.srcDirs += 'src/main/kotlin' @@ -36,7 +36,7 @@ android { // TODO: Specify your own unique Application ID (https://developer.android.com/studio/build/application-id.html). applicationId "org.thingsboard.app" minSdkVersion 21 - targetSdkVersion 31 + targetSdkVersion 33 versionCode flutterVersionCode.toInteger() versionName flutterVersionName } diff --git a/android/app/src/main/AndroidManifest.xml b/android/app/src/main/AndroidManifest.xml index be63724..9da246d 100644 --- a/android/app/src/main/AndroidManifest.xml +++ b/android/app/src/main/AndroidManifest.xml @@ -62,5 +62,15 @@ /> + + + + diff --git a/android/app/src/main/res/xml/provider_paths.xml b/android/app/src/main/res/xml/provider_paths.xml new file mode 100644 index 0000000..c0c21af --- /dev/null +++ b/android/app/src/main/res/xml/provider_paths.xml @@ -0,0 +1,6 @@ + + + + diff --git a/lib/config/routes/router.dart b/lib/config/routes/router.dart index 38d22f7..acf0e4b 100644 --- a/lib/config/routes/router.dart +++ b/lib/config/routes/router.dart @@ -1,6 +1,5 @@ import 'package:fluro/fluro.dart'; import 'package:flutter/material.dart'; -import 'package:flutter/widgets.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'; @@ -20,15 +19,12 @@ class ThingsboardAppRouter { late final _tbContext = TbContext(router); ThingsboardAppRouter() { - router.notFoundHandler = Handler(handlerFunc: (BuildContext? context, Map> params) { + router.notFoundHandler = Handler( + handlerFunc: (BuildContext? context, Map> params) { var settings = context!.settings; return Scaffold( - appBar: AppBar( - title: Text('Not Found') - ), - body: Center( - child: Text('Route not defined: ${settings!.name}') - ), + appBar: AppBar(title: Text('Not Found')), + body: Center(child: Text('Route not defined: ${settings!.name}')), ); }); InitRoutes(_tbContext).registerRoutes(); @@ -49,7 +45,6 @@ class ThingsboardAppRouter { } abstract class TbRoutes { - final TbContext _tbContext; TbRoutes(this._tbContext); @@ -61,5 +56,4 @@ abstract class TbRoutes { void doRegisterRoutes(FluroRouter router); TbContext get tbContext => _tbContext; - } diff --git a/lib/config/themes/tb_theme.dart b/lib/config/themes/tb_theme.dart index a666652..b89e4ba 100644 --- a/lib/config/themes/tb_theme.dart +++ b/lib/config/themes/tb_theme.dart @@ -24,7 +24,8 @@ const tbMatIndigo = MaterialColor( 700: Color(0xFF303F9F), 800: Color(0xFF283593), 900: Color(0xFF1A237E), - },); + }, +); const tbDarkMatIndigo = MaterialColor( _tbPrimaryColorValue, @@ -39,44 +40,40 @@ const tbDarkMatIndigo = MaterialColor( 700: Color(0xFF303F9F), 800: _tbPrimaryColor, 900: Color(0xFF1A237E), - },); + }, +); + +final ThemeData theme = ThemeData(); ThemeData tbTheme = ThemeData( primarySwatch: tbMatIndigo, - accentColor: Colors.deepOrange, + colorScheme: theme.colorScheme.copyWith(secondary: Colors.deepOrange), scaffoldBackgroundColor: Color(0xFFFAFAFA), textTheme: tbTypography.black, primaryTextTheme: tbTypography.black, typography: tbTypography, appBarTheme: AppBarTheme( - backgroundColor: Colors.white, - foregroundColor: _tbTextColor, - /* titleTextStyle: TextStyle( + backgroundColor: Colors.white, + foregroundColor: _tbTextColor, + /* titleTextStyle: TextStyle( color: _tbTextColor ), toolbarTextStyle: TextStyle( color: _tbTextColor ), */ - iconTheme: IconThemeData( - color: _tbTextColor - ) - - ), + iconTheme: IconThemeData(color: _tbTextColor)), bottomNavigationBarTheme: BottomNavigationBarThemeData( - backgroundColor: Colors.white, - selectedItemColor: _tbPrimaryColor, - unselectedItemColor: _tbPrimaryColor.withAlpha((255 * 0.38).ceil()), - showSelectedLabels: true, - showUnselectedLabels: true - ), + backgroundColor: Colors.white, + selectedItemColor: _tbPrimaryColor, + unselectedItemColor: _tbPrimaryColor.withAlpha((255 * 0.38).ceil()), + showSelectedLabels: true, + showUnselectedLabels: true), pageTransitionsTheme: PageTransitionsTheme(builders: { TargetPlatform.iOS: FadeOpenPageTransitionsBuilder(), TargetPlatform.android: FadeOpenPageTransitionsBuilder(), - }) -); + })); ThemeData tbDarkTheme = ThemeData( primarySwatch: tbDarkMatIndigo, - accentColor: Colors.deepOrange, - brightness: Brightness.dark -); + colorScheme: theme.colorScheme.copyWith(secondary: Colors.deepOrange), + brightness: Brightness.dark); diff --git a/lib/constants/assets_path.dart b/lib/constants/assets_path.dart index f8ac2cc..6821c3a 100644 --- a/lib/constants/assets_path.dart +++ b/lib/constants/assets_path.dart @@ -1,16 +1,17 @@ abstract class ThingsboardImage { - static final thingsBoardWithTitle = 'assets/images/thingsboard_with_title.svg'; + static final thingsBoardWithTitle = + 'assets/images/thingsboard_with_title.svg'; static final thingsboard = 'assets/images/thingsboard.svg'; static final thingsboardOuter = 'assets/images/thingsboard_outer.svg'; static final thingsboardCenter = 'assets/images/thingsboard_center.svg'; static final dashboardPlaceholder = 'assets/images/dashboard-placeholder.svg'; - static final deviceProfilePlaceholder = 'assets/images/device-profile-placeholder.svg'; + static final deviceProfilePlaceholder = + 'assets/images/device-profile-placeholder.svg'; - static final oauth2Logos = { + static final oauth2Logos = { 'google-logo': 'assets/images/google-logo.svg', 'github-logo': 'assets/images/github-logo.svg', 'facebook-logo': 'assets/images/facebook-logo.svg', 'apple-logo': 'assets/images/apple-logo.svg' }; - } diff --git a/lib/core/auth/auth_routes.dart b/lib/core/auth/auth_routes.dart index ad7ff4d..59e25d1 100644 --- a/lib/core/auth/auth_routes.dart +++ b/lib/core/auth/auth_routes.dart @@ -8,12 +8,13 @@ import 'package:thingsboard_app/core/context/tb_context.dart'; import 'login/login_page.dart'; class AuthRoutes extends TbRoutes { - - late var loginHandler = Handler(handlerFunc: (BuildContext? context, Map params) { + late var loginHandler = Handler( + handlerFunc: (BuildContext? context, Map params) { return LoginPage(tbContext); }); - late var resetPasswordRequestHandler = Handler(handlerFunc: (BuildContext? context, Map params) { + late var resetPasswordRequestHandler = Handler( + handlerFunc: (BuildContext? context, Map params) { return ResetPasswordRequestPage(tbContext); }); @@ -22,7 +23,7 @@ class AuthRoutes extends TbRoutes { @override void doRegisterRoutes(router) { router.define("/login", handler: loginHandler); - router.define("/login/resetPasswordRequest", handler: resetPasswordRequestHandler); + router.define("/login/resetPasswordRequest", + handler: resetPasswordRequestHandler); } - } diff --git a/lib/core/auth/login/login_page.dart b/lib/core/auth/login/login_page.dart index 7ea39da..4afec88 100644 --- a/lib/core/auth/login/login_page.dart +++ b/lib/core/auth/login/login_page.dart @@ -1,8 +1,6 @@ import 'dart:ui'; -import 'package:flutter/cupertino.dart'; import 'package:flutter/material.dart'; -import 'package:flutter/widgets.dart'; import 'package:flutter_form_builder/flutter_form_builder.dart'; import 'package:form_builder_validators/form_builder_validators.dart'; import 'package:flutter_svg/flutter_svg.dart'; @@ -16,23 +14,20 @@ import 'package:thingsboard_client/thingsboard_client.dart'; import 'login_page_background.dart'; class LoginPage extends TbPageWidget { - LoginPage(TbContext tbContext) : super(tbContext); @override _LoginPageState createState() => _LoginPageState(); - } class _LoginPageState extends TbPageState { + final ButtonStyle _oauth2ButtonWithTextStyle = OutlinedButton.styleFrom( + padding: EdgeInsets.all(16), + alignment: Alignment.centerLeft, + primary: Colors.black87); - final ButtonStyle _oauth2ButtonWithTextStyle = - OutlinedButton.styleFrom(padding: EdgeInsets.all(16), - alignment: Alignment.centerLeft, primary: Colors.black87); - - final ButtonStyle _oauth2IconButtonStyle = - OutlinedButton.styleFrom(padding: EdgeInsets.all(16), - alignment: Alignment.center); + final ButtonStyle _oauth2IconButtonStyle = OutlinedButton.styleFrom( + padding: EdgeInsets.all(16), alignment: Alignment.center); final _isLoginNotifier = ValueNotifier(false); final _showPasswordNotifier = ValueNotifier(false); @@ -54,174 +49,185 @@ class _LoginPageState extends TbPageState { return Scaffold( backgroundColor: Colors.white, resizeToAvoidBottomInset: false, - body: Stack( - children: [ - LoginPageBackground(), - Positioned.fill( - child: LayoutBuilder( - builder: (context, constraints) { - return SingleChildScrollView( - padding: EdgeInsets.fromLTRB(24, 71, 24, 24), - child: ConstrainedBox( - constraints: BoxConstraints(minHeight: constraints.maxHeight - (71 + 24)), - child: IntrinsicHeight( - child: Column( - crossAxisAlignment: CrossAxisAlignment.stretch, - children: [ - Row( - children: [ - SvgPicture.asset(ThingsboardImage.thingsBoardWithTitle, - height: 25, - color: Theme.of(context).primaryColor, - semanticsLabel: 'ThingsBoard Logo') - ] - ), - SizedBox(height: 32), - Row( - children: [ - Text( - 'Login to your account', - style: TextStyle( - fontWeight: FontWeight.bold, - fontSize: 28, - height: 36 / 28 - ) - )] - ), - SizedBox(height: 48), - if (tbContext.hasOAuthClients) - _buildOAuth2Buttons(tbContext.oauth2ClientInfos!), - if (tbContext.hasOAuthClients) - Padding(padding: EdgeInsets.only(top: 10, bottom: 16), - child: Row( - children: [ - Flexible(child: Divider()), - Padding( - padding: EdgeInsets.symmetric(horizontal: 16), - child: Text('OR'), - ), - Flexible(child: Divider()) - ], - ) + body: Stack(children: [ + LoginPageBackground(), + Positioned.fill(child: LayoutBuilder( + builder: (context, constraints) { + return SingleChildScrollView( + padding: EdgeInsets.fromLTRB(24, 71, 24, 24), + child: ConstrainedBox( + constraints: BoxConstraints( + minHeight: constraints.maxHeight - (71 + 24)), + child: IntrinsicHeight( + child: Column( + crossAxisAlignment: CrossAxisAlignment.stretch, + children: [ + Row(children: [ + SvgPicture.asset( + ThingsboardImage.thingsBoardWithTitle, + height: 25, + color: Theme.of(context).primaryColor, + semanticsLabel: 'ThingsBoard Logo') + ]), + SizedBox(height: 32), + Row(children: [ + Text('Login to your account', + style: TextStyle( + fontWeight: FontWeight.bold, + fontSize: 28, + height: 36 / 28)) + ]), + SizedBox(height: 48), + if (tbContext.hasOAuthClients) + _buildOAuth2Buttons( + tbContext.oauth2ClientInfos!), + if (tbContext.hasOAuthClients) + Padding( + padding: + EdgeInsets.only(top: 10, bottom: 16), + child: Row( + children: [ + Flexible(child: Divider()), + Padding( + padding: EdgeInsets.symmetric( + horizontal: 16), + child: Text('OR'), + ), + Flexible(child: Divider()) + ], + )), + FormBuilder( + key: _loginFormKey, + autovalidateMode: AutovalidateMode.disabled, + child: Column( + crossAxisAlignment: + CrossAxisAlignment.stretch, + children: [ + FormBuilderTextField( + name: 'username', + validator: + FormBuilderValidators.compose([ + FormBuilderValidators.required( + errorText: 'Email is required.'), + FormBuilderValidators.email( + errorText: + 'Invalid email format.') + ]), + decoration: InputDecoration( + border: OutlineInputBorder(), + labelText: 'Email'), ), - FormBuilder( - key: _loginFormKey, - autovalidateMode: AutovalidateMode.disabled, - child: Column( - crossAxisAlignment: CrossAxisAlignment.stretch, - children: [ - FormBuilderTextField( - name: 'username', - validator: FormBuilderValidators.compose([ - FormBuilderValidators.required(context, errorText: 'Email is required.'), - FormBuilderValidators.email(context, errorText: 'Invalid email format.') + SizedBox(height: 28), + ValueListenableBuilder( + valueListenable: + _showPasswordNotifier, + builder: (BuildContext context, + bool showPassword, child) { + return FormBuilderTextField( + name: 'password', + obscureText: !showPassword, + validator: FormBuilderValidators + .compose([ + FormBuilderValidators.required( + errorText: + 'Password is required.') ]), decoration: InputDecoration( + suffixIcon: IconButton( + icon: Icon(showPassword + ? Icons.visibility + : Icons.visibility_off), + onPressed: () { + _showPasswordNotifier + .value = + !_showPasswordNotifier + .value; + }, + ), border: OutlineInputBorder(), - labelText: 'Email' - ), - ), - SizedBox(height: 28), - ValueListenableBuilder( - valueListenable: _showPasswordNotifier, - builder: (BuildContext context, bool showPassword, child) { - return FormBuilderTextField( - name: 'password', - obscureText: !showPassword, - validator: FormBuilderValidators.compose([ - FormBuilderValidators.required(context, errorText: 'Password is required.') - ]), - decoration: InputDecoration( - suffixIcon: IconButton( - icon: Icon(showPassword ? Icons.visibility : Icons.visibility_off), - onPressed: () { - _showPasswordNotifier.value = !_showPasswordNotifier.value; - }, - ), - border: OutlineInputBorder(), - labelText: 'Password' - ), - ); - } - ) - ], - ) + labelText: 'Password'), + ); + }) + ], + )), + Row( + mainAxisAlignment: MainAxisAlignment.end, + children: [ + TextButton( + onPressed: () { + _forgotPassword(); + }, + child: Text( + 'Forgot Password?', + style: TextStyle( + color: Theme.of(context) + .colorScheme + .primary, + letterSpacing: 1, + fontSize: 12, + height: 16 / 12), ), - Row( - mainAxisAlignment: MainAxisAlignment.end, - children: [ - TextButton( - onPressed: () { - _forgotPassword(); - }, - child: Text( - 'Forgot Password?', - style: TextStyle(color: Theme.of(context).colorScheme.primary, - letterSpacing: 1, - fontSize: 12, - height: 16 / 12), - ), - ) - ], - ), - Spacer(), - ElevatedButton( - child: Text('Log In'), - style: ElevatedButton.styleFrom(padding: EdgeInsets.symmetric(vertical: 16)), - onPressed: () { - _login(); - }, - ), - SizedBox(height: 48) - ] + ) + ], ), - ) - ) - ); - }, - ) - ), - ValueListenableBuilder( + Spacer(), + ElevatedButton( + child: Text('Log In'), + style: ElevatedButton.styleFrom( + padding: + EdgeInsets.symmetric(vertical: 16)), + onPressed: () { + _login(); + }, + ), + SizedBox(height: 48) + ]), + ))); + }, + )), + ValueListenableBuilder( valueListenable: _isLoginNotifier, builder: (BuildContext context, bool loading, child) { if (loading) { - var data = MediaQueryData.fromWindow(WidgetsBinding.instance!.window); + var data = + MediaQueryData.fromWindow(WidgetsBinding.instance.window); var bottomPadding = data.padding.top; bottomPadding += kToolbarHeight; return SizedBox.expand( child: ClipRect( child: BackdropFilter( - filter: ImageFilter.blur(sigmaX: 5.0, sigmaY: 5.0), + filter: + ImageFilter.blur(sigmaX: 5.0, sigmaY: 5.0), child: Container( decoration: new BoxDecoration( - color: Colors.grey.shade200.withOpacity(0.2) - ), + color: + Colors.grey.shade200.withOpacity(0.2)), child: Container( - padding: EdgeInsets.only(bottom: bottomPadding), + padding: + EdgeInsets.only(bottom: bottomPadding), alignment: Alignment.center, child: TbProgressIndicator(size: 50.0), ), - ) - ) - ) - ); + )))); } else { return SizedBox.shrink(); } - } - ) - ] - ) - ); + }) + ])); } Widget _buildOAuth2Buttons(List clients) { if (clients.length == 1 || clients.length > 6) { return Column( - crossAxisAlignment: CrossAxisAlignment.stretch, - children: clients.asMap().map((index, client) => - MapEntry(index, _buildOAuth2Button(client, 'Login with ${client.name}', false, index == clients.length - 1))).values.toList() - ); + crossAxisAlignment: CrossAxisAlignment.stretch, + children: clients + .asMap() + .map((index, client) => MapEntry( + index, + _buildOAuth2Button(client, 'Login with ${client.name}', false, + index == clients.length - 1))) + .values + .toList()); } else { return Column( crossAxisAlignment: CrossAxisAlignment.stretch, @@ -231,18 +237,24 @@ class _LoginPageState extends TbPageState { child: Center(child: Text('LOGIN WITH')), ), Row( - children: clients.asMap().map((index, client) => - MapEntry(index, _buildOAuth2Button(client, clients.length == 2 ? client.name : null, true, index == clients.length - 1))).values.toList() - ) + children: clients + .asMap() + .map((index, client) => MapEntry( + index, + _buildOAuth2Button( + client, + clients.length == 2 ? client.name : null, + true, + index == clients.length - 1))) + .values + .toList()) ], ); } } - Widget _buildOAuth2Button(OAuth2ClientInfo client, - String? text, - bool expand, - bool isLast) { + Widget _buildOAuth2Button( + OAuth2ClientInfo client, String? text, bool expand, bool isLast) { Widget? icon; if (client.icon != null) { if (ThingsboardImage.oauth2Logos.containsKey(client.icon)) { @@ -255,7 +267,8 @@ class _LoginPageState extends TbPageState { } var iconData = MdiIcons.fromString(strIcon); if (iconData != null) { - icon = Icon(iconData, size: 24, color: Theme.of(context).primaryColor); + icon = + Icon(iconData, size: 24, color: Theme.of(context).primaryColor); } } } @@ -279,10 +292,9 @@ class _LoginPageState extends TbPageState { if (expand) { return Expanded( child: Padding( - padding: EdgeInsets.only(right: isLast ? 0 : 8), - child: button, - ) - ); + padding: EdgeInsets.only(right: isLast ? 0 : 8), + child: button, + )); } else { return button; } @@ -293,7 +305,8 @@ class _LoginPageState extends TbPageState { try { final result = await tbContext.oauth2Client.authenticate(client.url); if (result.success) { - await tbClient.setUserFromJwtToken(result.accessToken, result.refreshToken, true); + await tbClient.setUserFromJwtToken( + result.accessToken, result.refreshToken, true); } else { _isLoginNotifier.value = false; showErrorNotification(result.error!); diff --git a/lib/core/auth/login/login_page_background.dart b/lib/core/auth/login/login_page_background.dart index e86c7ea..0d79be5 100644 --- a/lib/core/auth/login/login_page_background.dart +++ b/lib/core/auth/login/login_page_background.dart @@ -1,21 +1,17 @@ import 'package:flutter/material.dart'; -import 'package:flutter/widgets.dart'; class LoginPageBackground extends StatelessWidget { - @override Widget build(BuildContext context) { return SizedBox.expand( child: CustomPaint( - painter: _LoginPageBackgroundPainter(color: Theme.of(context).primaryColor), - ) - ); + painter: + _LoginPageBackgroundPainter(color: Theme.of(context).primaryColor), + )); } - } class _LoginPageBackgroundPainter extends CustomPainter { - final Color color; const _LoginPageBackgroundPainter({required this.color}); diff --git a/lib/core/auth/login/reset_password_request_page.dart b/lib/core/auth/login/reset_password_request_page.dart index 218935e..dc0d2d2 100644 --- a/lib/core/auth/login/reset_password_request_page.dart +++ b/lib/core/auth/login/reset_password_request_page.dart @@ -1,5 +1,4 @@ import 'package:flutter/material.dart'; -import 'package:flutter/widgets.dart'; import 'package:flutter_form_builder/flutter_form_builder.dart'; import 'package:form_builder_validators/form_builder_validators.dart'; import 'package:thingsboard_app/core/auth/login/login_page_background.dart'; @@ -9,16 +8,15 @@ import 'package:thingsboard_app/widgets/tb_app_bar.dart'; import 'package:thingsboard_app/widgets/tb_progress_indicator.dart'; class ResetPasswordRequestPage extends TbPageWidget { - ResetPasswordRequestPage(TbContext tbContext) : super(tbContext); @override - _ResetPasswordRequestPageState createState() => _ResetPasswordRequestPageState(); - + _ResetPasswordRequestPageState createState() => + _ResetPasswordRequestPageState(); } -class _ResetPasswordRequestPageState extends TbPageState { - +class _ResetPasswordRequestPageState + extends TbPageState { final _isLoadingNotifier = ValueNotifier(false); final _resetPasswordFormKey = GlobalKey(); @@ -26,82 +24,74 @@ class _ResetPasswordRequestPageState extends TbPageState( - valueListenable: _isLoadingNotifier, - builder: (BuildContext context, bool loading, child) { - if (loading) { - return SizedBox.expand( - child: Container( - color: Color(0x99FFFFFF), - child: Center(child: TbProgressIndicator(size: 50.0)), - ) - ); - } else { - return SizedBox.shrink(); - } + key: _resetPasswordFormKey, + autovalidateMode: AutovalidateMode.disabled, + child: Column( + crossAxisAlignment: CrossAxisAlignment.stretch, + children: [ + SizedBox(height: 16), + Text( + 'Enter the email associated with your account and we\'ll send an email with password reset link', + textAlign: TextAlign.center, + style: TextStyle( + color: Color(0xFFAFAFAF), + fontSize: 14, + height: 24 / 14), + ), + SizedBox(height: 61), + FormBuilderTextField( + name: 'email', + autofocus: true, + validator: FormBuilderValidators.compose([ + FormBuilderValidators.required( + errorText: 'Email is required.'), + FormBuilderValidators.email( + errorText: 'Invalid email format.') + ]), + decoration: InputDecoration( + border: OutlineInputBorder(), + labelText: 'Email *'), + ), + Spacer(), + ElevatedButton( + child: Text('Request password reset'), + style: ElevatedButton.styleFrom( + padding: + EdgeInsets.symmetric(vertical: 16)), + onPressed: () { + _requestPasswordReset(); + }, + ) + ])))), + ValueListenableBuilder( + valueListenable: _isLoadingNotifier, + builder: (BuildContext context, bool loading, child) { + if (loading) { + return SizedBox.expand( + child: Container( + color: Color(0x99FFFFFF), + child: Center(child: TbProgressIndicator(size: 50.0)), + )); + } else { + return SizedBox.shrink(); } - ) - ] - ) - ) - ) - ]) - ); + }) + ]))) + ])); } void _requestPasswordReset() async { @@ -115,7 +105,7 @@ class _ResetPasswordRequestPageState extends TbPageState getAppSecret(); factory AppSecretProvider.local() => _LocalAppSecretProvider(); - } /// Not for production (only for debugging) class _LocalAppSecretProvider implements AppSecretProvider { - @override Future getAppSecret() async { return ThingsboardAppConstants.thingsboardOAuth2AppSecret; } - } diff --git a/lib/core/auth/oauth2/tb_oauth2_client.dart b/lib/core/auth/oauth2/tb_oauth2_client.dart index 9b5ac09..aada043 100644 --- a/lib/core/auth/oauth2/tb_oauth2_client.dart +++ b/lib/core/auth/oauth2/tb_oauth2_client.dart @@ -19,39 +19,42 @@ class TbOAuth2AuthenticateResult { TbOAuth2AuthenticateResult.failed(this.error); bool get success => error == null; - } class TbOAuth2Client { - final TbContext _tbContext; final AppSecretProvider _appSecretProvider; TbOAuth2Client( - { required TbContext tbContext, - required AppSecretProvider appSecretProvider} ): - _tbContext = tbContext, - _appSecretProvider = appSecretProvider; + {required TbContext tbContext, + required AppSecretProvider appSecretProvider}) + : _tbContext = tbContext, + _appSecretProvider = appSecretProvider; Future authenticate(String oauth2Url) async { final appSecret = await _appSecretProvider.getAppSecret(); final pkgName = _tbContext.packageName; final jwt = JWT( { - 'callbackUrlScheme': ThingsboardAppConstants.thingsboardOAuth2CallbackUrlScheme + 'callbackUrlScheme': + ThingsboardAppConstants.thingsboardOAuth2CallbackUrlScheme }, issuer: pkgName, ); final key = SecretKey(appSecret); - final appToken = jwt.sign(key, algorithm: _HMACBase64Algorithm.HS512, expiresIn: Duration(minutes: 2)); - var url = Uri.parse(ThingsboardAppConstants.thingsBoardApiEndpoint + oauth2Url); - final params = Map.from(url.queryParameters); + final appToken = jwt.sign(key, + algorithm: _HMACBase64Algorithm.HS512, expiresIn: Duration(minutes: 2)); + var url = + Uri.parse(ThingsboardAppConstants.thingsBoardApiEndpoint + oauth2Url); + final params = Map.from(url.queryParameters); params['pkg'] = pkgName; params['appToken'] = appToken; url = url.replace(queryParameters: params); final result = await TbWebAuth.authenticate( url: url.toString(), - callbackUrlScheme: ThingsboardAppConstants.thingsboardOAuth2CallbackUrlScheme, saveHistory: false); + callbackUrlScheme: + ThingsboardAppConstants.thingsboardOAuth2CallbackUrlScheme, + saveHistory: false); final resultUri = Uri.parse(result); final error = resultUri.queryParameters['error']; if (error != null) { @@ -62,14 +65,14 @@ class TbOAuth2Client { if (accessToken != null && refreshToken != null) { return TbOAuth2AuthenticateResult.success(accessToken, refreshToken); } else { - return TbOAuth2AuthenticateResult.failed('No authentication credentials in response.'); + return TbOAuth2AuthenticateResult.failed( + 'No authentication credentials in response.'); } } } } class _HMACBase64Algorithm extends JWTAlgorithm { - static const HS512 = _HMACBase64Algorithm('HS512'); final String _name; diff --git a/lib/core/auth/web/tb_web_auth.dart b/lib/core/auth/web/tb_web_auth.dart index 2398962..6013f3b 100644 --- a/lib/core/auth/web/tb_web_auth.dart +++ b/lib/core/auth/web/tb_web_auth.dart @@ -19,13 +19,18 @@ class _OnAppLifecycleResumeObserver extends WidgetsBindingObserver { class TbWebAuth { static const MethodChannel _channel = const MethodChannel('tb_web_auth'); - static final _OnAppLifecycleResumeObserver _resumedObserver = _OnAppLifecycleResumeObserver(() { + static final _OnAppLifecycleResumeObserver _resumedObserver = + _OnAppLifecycleResumeObserver(() { _cleanUpDanglingCalls(); }); - static Future authenticate({required String url, required String callbackUrlScheme, bool? saveHistory}) async { - WidgetsBinding.instance?.removeObserver(_resumedObserver); // safety measure so we never add this observer twice - WidgetsBinding.instance?.addObserver(_resumedObserver); + static Future authenticate( + {required String url, + required String callbackUrlScheme, + bool? saveHistory}) async { + WidgetsBinding.instance.removeObserver( + _resumedObserver); // safety measure so we never add this observer twice + WidgetsBinding.instance.addObserver(_resumedObserver); return await _channel.invokeMethod('authenticate', { 'url': url, 'callbackUrlScheme': callbackUrlScheme, @@ -35,6 +40,6 @@ class TbWebAuth { static Future _cleanUpDanglingCalls() async { await _channel.invokeMethod('cleanUpDanglingCalls'); - WidgetsBinding.instance?.removeObserver(_resumedObserver); + WidgetsBinding.instance.removeObserver(_resumedObserver); } } diff --git a/lib/core/context/tb_context.dart b/lib/core/context/tb_context.dart index 40efc47..a821cea 100644 --- a/lib/core/context/tb_context.dart +++ b/lib/core/context/tb_context.dart @@ -15,12 +15,7 @@ import 'package:thingsboard_client/thingsboard_client.dart'; import 'package:thingsboard_app/utils/services/tb_app_storage.dart'; import 'package:thingsboard_app/core/context/tb_context_widget.dart'; -enum NotificationType { - info, - warn, - success, - error -} +enum NotificationType { info, warn, success, error } class TbLogOutput extends LogOutput { @override @@ -45,18 +40,14 @@ class TbLogsFilter extends LogFilter { class TbLogger { final _logger = Logger( filter: TbLogsFilter(), - printer: PrefixPrinter( - PrettyPrinter( - methodCount: 0, - errorMethodCount: 8, - lineLength: 200, - colors: false, - printEmojis: true, - printTime: false - ) - ), - output: TbLogOutput() - ); + printer: PrefixPrinter(PrettyPrinter( + methodCount: 0, + errorMethodCount: 8, + lineLength: 200, + colors: false, + printEmojis: true, + printTime: false)), + output: TbLogOutput()); void verbose(dynamic message, [dynamic error, StackTrace? stackTrace]) { _logger.v(message, error, stackTrace); @@ -83,11 +74,15 @@ class TbLogger { } } -typedef OpenDashboardCallback = void Function(String dashboardId, {String? dashboardTitle, String? state, bool? hideToolbar}); +typedef OpenDashboardCallback = void Function(String dashboardId, + {String? dashboardTitle, String? state, bool? hideToolbar}); abstract class TbMainDashboardHolder { - - Future navigateToDashboard(String dashboardId, {String? dashboardTitle, String? state, bool? hideToolbar, bool animate = true}); + Future navigateToDashboard(String dashboardId, + {String? dashboardTitle, + String? state, + bool? hideToolbar, + bool animate = true}); Future openMain({bool animate}); @@ -100,7 +95,6 @@ abstract class TbMainDashboardHolder { bool isDashboardOpen(); Future dashboardGoBack(); - } class TbContext { @@ -121,7 +115,8 @@ class TbContext { TbMainDashboardHolder? _mainDashboardHolder; bool _closeMainFirst = false; - GlobalKey messengerKey = GlobalKey(); + GlobalKey messengerKey = + GlobalKey(); late final ThingsboardClient tbClient; late final TbOAuth2Client oauth2Client; @@ -147,14 +142,15 @@ class TbContext { _initialized = true; var storage = createAppStorage(); tbClient = ThingsboardClient(ThingsboardAppConstants.thingsBoardApiEndpoint, - storage: storage, - onUserLoaded: onUserLoaded, - onError: onError, - onLoadStarted: onLoadStarted, - onLoadFinished: onLoadFinished, - computeFunc: (callback, message) => compute(callback, message)); + storage: storage, + onUserLoaded: onUserLoaded, + onError: onError, + onLoadStarted: onLoadStarted, + onLoadFinished: onLoadFinished, + computeFunc: (callback, message) => compute(callback, message)); - oauth2Client = TbOAuth2Client(tbContext: this, appSecretProvider: AppSecretProvider.local()); + oauth2Client = TbOAuth2Client( + tbContext: this, appSecretProvider: AppSecretProvider.local()); try { if (UniversalPlatform.isAndroid) { @@ -203,11 +199,12 @@ class TbContext { showNotification(message, NotificationType.success, duration: duration); } - void showNotification(String message, NotificationType type, {Duration? duration}) { + void showNotification(String message, NotificationType type, + {Duration? duration}) { duration ??= const Duration(days: 1); Color backgroundColor; var textColor = Color(0xFFFFFFFF); - switch(type) { + switch (type) { case NotificationType.info: backgroundColor = Color(0xFF323232); break; @@ -224,16 +221,16 @@ class TbContext { final snackBar = SnackBar( duration: duration, backgroundColor: backgroundColor, - content: Text(message, - style: TextStyle( - color: textColor - ), + content: Text( + message, + style: TextStyle(color: textColor), ), action: SnackBarAction( label: 'Close', textColor: textColor, onPressed: () { - messengerKey.currentState!.hideCurrentSnackBar(reason: SnackBarClosedReason.dismiss); + messengerKey.currentState! + .hideCurrentSnackBar(reason: SnackBarClosedReason.dismiss); }, ), ); @@ -264,7 +261,8 @@ class TbContext { if (tbClient.getAuthUser()!.userId != null) { try { userDetails = await tbClient.getUserService().getUser(); - homeDashboard = await tbClient.getDashboardService().getHomeDashboardInfo(); + homeDashboard = + await tbClient.getDashboardService().getHomeDashboardInfo(); } catch (e) { if (!_isConnectionError(e)) { tbClient.logout(); @@ -276,33 +274,44 @@ class TbContext { } else { userDetails = null; homeDashboard = null; - oauth2ClientInfos = await tbClient.getOAuth2Service().getOAuth2Clients(pkgName: packageName, platform: _oauth2PlatformType); + oauth2ClientInfos = await tbClient.getOAuth2Service().getOAuth2Clients( + pkgName: packageName, platform: _oauth2PlatformType); } _isAuthenticated.value = tbClient.isAuthenticated(); await updateRouteState(); - } catch (e, s) { log.error('Error: $e', e, s); if (_isConnectionError(e)) { - var res = await confirm(title: 'Connection error', message: 'Failed to connect to server', cancel: 'Cancel', ok: 'Retry'); + var res = await confirm( + title: 'Connection error', + message: 'Failed to connect to server', + cancel: 'Cancel', + ok: 'Retry'); if (res == true) { onUserLoaded(); } else { - navigateTo('/login', replace: true, clearStack: true, transition: TransitionType.fadeIn, transitionDuration: Duration(milliseconds: 750)); + navigateTo('/login', + replace: true, + clearStack: true, + transition: TransitionType.fadeIn, + transitionDuration: Duration(milliseconds: 750)); } } } } bool _isConnectionError(e) { - return e is ThingsboardError && e.errorCode == ThingsBoardErrorCode.general && e.message == 'Unable to connect'; + return e is ThingsboardError && + e.errorCode == ThingsBoardErrorCode.general && + e.message == 'Unable to connect'; } Listenable get isAuthenticatedListenable => _isAuthenticated; bool get isAuthenticated => _isAuthenticated.value; - bool get hasOAuthClients => oauth2ClientInfos != null && oauth2ClientInfos!.isNotEmpty; + bool get hasOAuthClients => + oauth2ClientInfos != null && oauth2ClientInfos!.isNotEmpty; Future updateRouteState() async { if (currentState != null) { @@ -318,14 +327,20 @@ class TbContext { transition: TransitionType.none); } else { navigateTo('/fullscreenDashboard/$defaultDashboardId', - replace: true, - transition: TransitionType.fadeIn); + replace: true, transition: TransitionType.fadeIn); } } else { - navigateTo('/home', replace: true, transition: TransitionType.fadeIn, transitionDuration: Duration(milliseconds: 750)); + navigateTo('/home', + replace: true, + transition: TransitionType.fadeIn, + transitionDuration: Duration(milliseconds: 750)); } } else { - navigateTo('/login', replace: true, clearStack: true, transition: TransitionType.fadeIn, transitionDuration: Duration(milliseconds: 750)); + navigateTo('/login', + replace: true, + clearStack: true, + transition: TransitionType.fadeIn, + transitionDuration: Duration(milliseconds: 750)); } } } @@ -338,9 +353,10 @@ class TbContext { } bool _userForceFullscreen() { - return tbClient.getAuthUser()!.isPublic || - (userDetails != null && userDetails!.additionalInfo != null && - userDetails!.additionalInfo!['defaultDashboardFullscreen'] == true); + return tbClient.getAuthUser()!.isPublic! || + (userDetails != null && + userDetails!.additionalInfo != null && + userDetails!.additionalInfo!['defaultDashboardFullscreen'] == true); } bool isPhysicalDevice() { @@ -356,11 +372,13 @@ class TbContext { String userAgent() { String userAgent = 'Mozilla/5.0'; if (UniversalPlatform.isAndroid) { - userAgent += ' (Linux; Android ${_androidInfo!.version.release}; ${_androidInfo!.model})'; + userAgent += + ' (Linux; Android ${_androidInfo!.version.release}; ${_androidInfo!.model})'; } else if (UniversalPlatform.isIOS) { userAgent += ' (${_iosInfo!.model})'; } - userAgent += ' AppleWebKit/537.36 (KHTML, like Gecko) Version/4.0 Chrome/83.0.4103.106 Mobile Safari/537.36'; + userAgent += + ' AppleWebKit/537.36 (KHTML, like Gecko) Version/4.0 Chrome/83.0.4103.106 Mobile Safari/537.36'; return userAgent; } @@ -374,11 +392,17 @@ class TbContext { return false; } - Future navigateTo(String path, {bool replace = false, bool clearStack = false, closeDashboard = true, - TransitionType? transition, Duration? transitionDuration, bool restoreDashboard = true}) async { + Future navigateTo(String path, + {bool replace = false, + bool clearStack = false, + closeDashboard = true, + TransitionType? transition, + Duration? transitionDuration, + bool restoreDashboard = true}) async { if (currentState != null) { hideNotification(); - bool isOpenedDashboard = _mainDashboardHolder?.isDashboardOpen() == true && closeDashboard; + bool isOpenedDashboard = + _mainDashboardHolder?.isDashboardOpen() == true && closeDashboard; if (isOpenedDashboard) { _mainDashboardHolder?.openMain(); } @@ -403,12 +427,24 @@ class TbContext { } } _closeMainFirst = isOpenedDashboard; - return await router.navigateTo(currentState!.context, path, transition: transition, transitionDuration: transitionDuration, replace: replace, clearStack: clearStack); + return await router.navigateTo(currentState!.context, path, + transition: transition, + transitionDuration: transitionDuration, + replace: replace, + clearStack: clearStack); } } - Future navigateToDashboard(String dashboardId, {String? dashboardTitle, String? state, bool? hideToolbar, bool animate = true}) async { - await _mainDashboardHolder?.navigateToDashboard(dashboardId, dashboardTitle: dashboardTitle, state: state, hideToolbar: hideToolbar, animate: animate); + Future navigateToDashboard(String dashboardId, + {String? dashboardTitle, + String? state, + bool? hideToolbar, + bool animate = true}) async { + await _mainDashboardHolder?.navigateToDashboard(dashboardId, + dashboardTitle: dashboardTitle, + state: state, + hideToolbar: hideToolbar, + animate: animate); } Future showFullScreenDialog(Widget dialog) { @@ -416,8 +452,7 @@ class TbContext { builder: (BuildContext context) { return dialog; }, - fullscreenDialog: true - )); + fullscreenDialog: true)); } void pop([T? result, BuildContext? context]) async { @@ -428,7 +463,7 @@ class TbContext { } } - Future maybePop([ T? result ]) async { + Future maybePop([T? result]) async { if (currentState != null) { return Navigator.of(currentState!.context).maybePop(result); } else { @@ -441,7 +476,7 @@ class TbContext { return true; } if (_mainDashboardHolder != null) { - return await _mainDashboardHolder!.dashboardGoBack(); + return await _mainDashboardHolder!.dashboardGoBack(); } return true; } @@ -456,18 +491,22 @@ class TbContext { return false; } - Future confirm({required String title, required String message, String cancel = 'Cancel', String ok = 'Ok'}) { - return showDialog(context: currentState!.context, + Future confirm( + {required String title, + required String message, + String cancel = 'Cancel', + String ok = 'Ok'}) { + return showDialog( + context: currentState!.context, builder: (context) => AlertDialog( - title: Text(title), - content: Text(message), - actions: [ - TextButton(onPressed: () => pop(false, context), - child: Text(cancel)), - TextButton(onPressed: () => pop(true, context), - child: Text(ok)) - ], - )); + title: Text(title), + content: Text(message), + actions: [ + TextButton( + onPressed: () => pop(false, context), child: Text(cancel)), + TextButton(onPressed: () => pop(true, context), child: Text(ok)) + ], + )); } } @@ -480,11 +519,13 @@ mixin HasTbContext { void setupCurrentState(TbContextState currentState) { if (_tbContext.currentState != null) { - ModalRoute.of(_tbContext.currentState!.context)?.removeScopedWillPopCallback(_tbContext.willPop); + ModalRoute.of(_tbContext.currentState!.context) + ?.removeScopedWillPopCallback(_tbContext.willPop); } _tbContext.currentState = currentState; if (_tbContext.currentState != null) { - ModalRoute.of(_tbContext.currentState!.context)?.addScopedWillPopCallback(_tbContext.willPop); + ModalRoute.of(_tbContext.currentState!.context) + ?.addScopedWillPopCallback(_tbContext.willPop); } if (_tbContext._closeMainFirst) { _tbContext._closeMainFirst = false; @@ -514,33 +555,55 @@ mixin HasTbContext { await _tbContext.init(); } - Future navigateTo(String path, {bool replace = false, bool clearStack = false}) => _tbContext.navigateTo(path, replace: replace, clearStack: clearStack); + Future navigateTo(String path, + {bool replace = false, bool clearStack = false}) => + _tbContext.navigateTo(path, replace: replace, clearStack: clearStack); - void pop([T? result, BuildContext? context]) => _tbContext.pop(result, context); + void pop([T? result, BuildContext? context]) => + _tbContext.pop(result, context); - Future maybePop([ T? result ]) => _tbContext.maybePop(result); + Future maybePop([T? result]) => + _tbContext.maybePop(result); - Future navigateToDashboard(String dashboardId, {String? dashboardTitle, String? state, bool? hideToolbar, bool animate = true}) => - _tbContext.navigateToDashboard(dashboardId, dashboardTitle: dashboardTitle, state: state, hideToolbar: hideToolbar, animate: animate); + Future navigateToDashboard(String dashboardId, + {String? dashboardTitle, + String? state, + bool? hideToolbar, + bool animate = true}) => + _tbContext.navigateToDashboard(dashboardId, + dashboardTitle: dashboardTitle, + state: state, + hideToolbar: hideToolbar, + animate: animate); - Future confirm({required String title, required String message, String cancel = 'Cancel', String ok = 'Ok'}) => _tbContext.confirm(title: title, message: message, cancel: cancel, ok: ok); + Future 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); + void showErrorNotification(String message, {Duration? duration}) => + _tbContext.showErrorNotification(message, duration: duration); - void showInfoNotification(String message, {Duration? duration}) => _tbContext.showInfoNotification(message, duration: duration); + void showInfoNotification(String message, {Duration? duration}) => + _tbContext.showInfoNotification(message, duration: duration); - void showWarnNotification(String message, {Duration? duration}) => _tbContext.showWarnNotification(message, duration: duration); + void showWarnNotification(String message, {Duration? duration}) => + _tbContext.showWarnNotification(message, duration: duration); - void showSuccessNotification(String message, {Duration? duration}) => _tbContext.showSuccessNotification(message, duration: duration); + void showSuccessNotification(String message, {Duration? duration}) => + _tbContext.showSuccessNotification(message, duration: duration); void subscribeRouteObserver(TbPageState pageState) { - _tbContext.routeObserver.subscribe(pageState, ModalRoute.of(pageState.context) as PageRoute); + _tbContext.routeObserver + .subscribe(pageState, ModalRoute.of(pageState.context) as PageRoute); } void unsubscribeRouteObserver(TbPageState pageState) { _tbContext.routeObserver.unsubscribe(pageState); } - } diff --git a/lib/core/context/tb_context_widget.dart b/lib/core/context/tb_context_widget.dart index 6294cf6..4174dab 100644 --- a/lib/core/context/tb_context_widget.dart +++ b/lib/core/context/tb_context_widget.dart @@ -1,12 +1,12 @@ 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 { +abstract class TbContextStatelessWidget extends StatelessWidget + with HasTbContext { TbContextStatelessWidget(TbContext tbContext, {Key? key}) : super(key: key) { setTbContext(tbContext); } @@ -18,8 +18,8 @@ abstract class TbContextWidget extends StatefulWidget with HasTbContext { } } -abstract class TbContextState extends State with HasTbContext { - +abstract class TbContextState extends State + with HasTbContext { final bool handleLoading; bool closeMainFirst = false; @@ -35,25 +35,23 @@ abstract class TbContextState extends State with H void dispose() { super.dispose(); } - } mixin TbMainState { - bool canNavigate(String path); navigateToPath(String path); bool isHomePage(); - } abstract class TbPageWidget extends TbContextWidget { TbPageWidget(TbContext tbContext, {Key? key}) : super(tbContext, key: key); } -abstract class TbPageState extends TbContextState with RouteAware { - TbPageState({bool handleUserLoaded = false}): super(handleLoading: true); +abstract class TbPageState extends TbContextState + with RouteAware { + TbPageState({bool handleUserLoaded = false}) : super(handleLoading: true); @override void didChangeDependencies() { @@ -77,25 +75,20 @@ abstract class TbPageState extends TbContextState wit hideNotification(); setupCurrentState(this); } - } class TextContextWidget extends TbContextWidget { - final String text; TextContextWidget(TbContext tbContext, this.text) : super(tbContext); @override _TextContextWidgetState createState() => _TextContextWidgetState(); - } class _TextContextWidgetState extends TbContextState { - @override Widget build(BuildContext context) { return Scaffold(body: Center(child: Text(widget.text))); } - } diff --git a/lib/core/entity/entities_base.dart b/lib/core/entity/entities_base.dart index 8c3e3ac..07b82b0 100644 --- a/lib/core/entity/entities_base.dart +++ b/lib/core/entity/entities_base.dart @@ -1,7 +1,6 @@ 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'; @@ -31,7 +30,8 @@ const Map entityTypeTranslations = { }; typedef EntityTapFunction = Function(T entity); -typedef EntityCardWidgetBuilder = Widget Function(BuildContext context, T entity); +typedef EntityCardWidgetBuilder = Widget Function( + BuildContext context, T entity); class EntityCardSettings { bool dropShadow; @@ -39,7 +39,6 @@ class EntityCardSettings { } mixin EntitiesBase on HasTbContext { - final entityDateFormat = DateFormat('yyyy-MM-dd'); String get title; @@ -73,11 +72,9 @@ mixin EntitiesBase on HasTbContext { EntityCardSettings entityGridCardSettings(T entity) => EntityCardSettings(); void onEntityTap(T entity); - } -mixin ContactBasedBase on EntitiesBase { - +mixin ContactBasedBase on EntitiesBase { @override Widget buildEntityListCard(BuildContext context, T contact) { var address = Utils.contactToShortAddress(contact); @@ -89,8 +86,7 @@ mixin ContactBasedBase on EntitiesBase { children: [ Flexible( fit: FlexFit.tight, - child: - Column( + child: Column( crossAxisAlignment: CrossAxisAlignment.start, children: [ Row( @@ -105,39 +101,36 @@ mixin ContactBasedBase on EntitiesBase { color: Color(0xFF282828), fontSize: 14, fontWeight: FontWeight.w500, - height: 20 / 14 - )) - ), - Text(entityDateFormat.format(DateTime.fromMillisecondsSinceEpoch(contact.createdTime!)), + height: 20 / 14))), + Text( + entityDateFormat.format( + DateTime.fromMillisecondsSinceEpoch( + contact.createdTime!)), style: TextStyle( color: Color(0xFFAFAFAF), fontSize: 12, fontWeight: FontWeight.normal, - height: 16 / 12 - )) - ] - ), + height: 16 / 12)) + ]), SizedBox(height: 4), - if (contact.email != null) Text(contact.email!, - style: TextStyle( - color: Color(0xFFAFAFAF), - fontSize: 12, - fontWeight: FontWeight.normal, - height: 16 / 12 - )), - if (contact.email == null) - SizedBox(height: 16), + if (contact.email != null) + Text(contact.email!, + style: TextStyle( + color: Color(0xFFAFAFAF), + fontSize: 12, + fontWeight: FontWeight.normal, + height: 16 / 12)), + if (contact.email == null) SizedBox(height: 16), if (address != null) SizedBox(height: 4), - if (address != null) Text(address, - style: TextStyle( - color: Color(0xFFAFAFAF), - fontSize: 12, - fontWeight: FontWeight.normal, - height: 16 / 12 - )), + if (address != null) + Text(address, + style: TextStyle( + color: Color(0xFFAFAFAF), + fontSize: 12, + fontWeight: FontWeight.normal, + height: 16 / 12)), ], - ) - ), + )), SizedBox(width: 16), Icon(Icons.chevron_right, color: Color(0xFFACACAC)), SizedBox(width: 8) @@ -148,24 +141,21 @@ mixin ContactBasedBase on EntitiesBase { } abstract class PageKeyController

extends ValueNotifier> { - PageKeyController(P initialPageKey) : super(PageKeyValue(initialPageKey)); P nextPageKey(P pageKey); - } class PageKeyValue

{ - final P pageKey; PageKeyValue(this.pageKey); - } class PageLinkController extends PageKeyController { - - PageLinkController({int pageSize = 20, String? searchText}) : super(PageLink(pageSize, 0, searchText, SortOrder('createdTime', Direction.DESC))); + PageLinkController({int pageSize = 20, String? searchText}) + : super(PageLink( + pageSize, 0, searchText, SortOrder('createdTime', Direction.DESC))); @override PageLink nextPageKey(PageLink pageKey) => pageKey.nextPageLink(); @@ -175,12 +165,12 @@ class PageLinkController extends PageKeyController { value.pageKey.textSearch = searchText; notifyListeners(); } - } class TimePageLinkController extends PageKeyController { - - TimePageLinkController({int pageSize = 20, String? searchText}) : super(TimePageLink(pageSize, 0, searchText, SortOrder('createdTime', Direction.DESC))); + TimePageLinkController({int pageSize = 20, String? searchText}) + : super(TimePageLink( + pageSize, 0, searchText, SortOrder('createdTime', Direction.DESC))); @override TimePageLink nextPageKey(TimePageLink pageKey) => pageKey.nextPageLink(); @@ -190,29 +180,27 @@ class TimePageLinkController extends PageKeyController { value.pageKey.textSearch = searchText; notifyListeners(); } - } -abstract class BaseEntitiesWidget extends TbContextWidget with EntitiesBase { - +abstract class BaseEntitiesWidget extends TbContextWidget + with EntitiesBase { final bool searchMode; final PageKeyController

pageKeyController; - BaseEntitiesWidget(TbContext tbContext, this.pageKeyController, {this.searchMode = false}): - super(tbContext); + 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; - - + Widget? buildHeading(BuildContext context) => searchMode + ? Text('Search results', + style: TextStyle( + color: Color(0xFFAFAFAF), fontSize: 16, height: 24 / 16)) + : null; } -abstract class BaseEntitiesState extends TbContextState> { - +abstract class BaseEntitiesState + extends TbContextState> { late final PagingController pagingController; Completer? _refreshCompleter; bool _dataLoading = false; @@ -224,7 +212,8 @@ abstract class BaseEntitiesState extends TbContextState extends TbContextState Future.wait([ - widget.onRefresh(), - _refresh() - ]), - child: pagedViewBuilder(context) - ); + onRefresh: () => Future.wait([widget.onRefresh(), _refresh()]), + child: pagedViewBuilder(context)); } - + Widget pagedViewBuilder(BuildContext context); Widget firstPageProgressIndicatorBuilder(BuildContext context) { - return Stack( children: [ + return Stack(children: [ Positioned( top: 20, left: 0, @@ -338,7 +323,7 @@ abstract class BaseEntitiesState extends TbContextState extends TbContextState extends TbContextState pagingController.refresh(), ); } - } class FirstPageExceptionIndicator extends StatelessWidget { diff --git a/lib/core/entity/entities_grid.dart b/lib/core/entity/entities_grid.dart index bcfae9e..f60ed9c 100644 --- a/lib/core/entity/entities_grid.dart +++ b/lib/core/entity/entities_grid.dart @@ -6,14 +6,11 @@ import 'entities_base.dart'; import 'entity_grid_card.dart'; mixin EntitiesGridStateBase on StatefulWidget { - @override _EntitiesGridState createState() => _EntitiesGridState(); - } class _EntitiesGridState extends BaseEntitiesState { - _EntitiesGridState() : super(); @override @@ -24,9 +21,7 @@ class _EntitiesGridState extends BaseEntitiesState { if (heading != null) { slivers.add(SliverPadding( padding: EdgeInsets.fromLTRB(16, 16, 16, 0), - sliver: SliverToBoxAdapter( - child: heading - ))); + sliver: SliverToBoxAdapter(child: heading))); } slivers.add(SliverPadding( padding: EdgeInsets.all(16), @@ -44,19 +39,17 @@ class _EntitiesGridState extends BaseEntitiesState { ), builderDelegate: PagedChildBuilderDelegate( itemBuilder: (context, item, index) => EntityGridCard( - item, - key: widget.getKey(item), - entityCardWidgetBuilder: widget.buildEntityGridCard, - onEntityTap: widget.onEntityTap, - settings: widget.entityGridCardSettings(item), - ), - firstPageProgressIndicatorBuilder: firstPageProgressIndicatorBuilder, - newPageProgressIndicatorBuilder: newPageProgressIndicatorBuilder, - noItemsFoundIndicatorBuilder: noItemsFoundIndicatorBuilder - ) - ))); - return CustomScrollView( - slivers: slivers - ); + item, + key: widget.getKey(item), + entityCardWidgetBuilder: widget.buildEntityGridCard, + onEntityTap: widget.onEntityTap, + settings: widget.entityGridCardSettings(item), + ), + firstPageProgressIndicatorBuilder: + firstPageProgressIndicatorBuilder, + newPageProgressIndicatorBuilder: + newPageProgressIndicatorBuilder, + noItemsFoundIndicatorBuilder: noItemsFoundIndicatorBuilder)))); + return CustomScrollView(slivers: slivers); } } diff --git a/lib/core/entity/entities_list.dart b/lib/core/entity/entities_list.dart index 898f124..0590716 100644 --- a/lib/core/entity/entities_list.dart +++ b/lib/core/entity/entities_list.dart @@ -6,14 +6,11 @@ import 'package:thingsboard_app/core/entity/entities_base.dart'; import 'entity_list_card.dart'; mixin EntitiesListStateBase on StatefulWidget { - @override _EntitiesListState createState() => _EntitiesListState(); - } -class _EntitiesListState extends BaseEntitiesState { - +class _EntitiesListState extends BaseEntitiesState { _EntitiesListState() : super(); @override @@ -23,9 +20,7 @@ class _EntitiesListState extends BaseEntitiesState { if (heading != null) { slivers.add(SliverPadding( padding: EdgeInsets.fromLTRB(16, 16, 16, 0), - sliver: SliverToBoxAdapter( - child: heading - ))); + sliver: SliverToBoxAdapter(child: heading))); } slivers.add(SliverPadding( padding: EdgeInsets.all(16), @@ -34,19 +29,16 @@ class _EntitiesListState extends BaseEntitiesState { separatorBuilder: (context, index) => SizedBox(height: 8), builderDelegate: PagedChildBuilderDelegate( itemBuilder: (context, item, index) => EntityListCard( - item, - key: widget.getKey(item), - entityCardWidgetBuilder: widget.buildEntityListCard, - onEntityTap: widget.onEntityTap, - settings: widget.entityListCardSettings(item), - ), - firstPageProgressIndicatorBuilder: firstPageProgressIndicatorBuilder, - newPageProgressIndicatorBuilder: newPageProgressIndicatorBuilder, - noItemsFoundIndicatorBuilder: noItemsFoundIndicatorBuilder - ) - ))); - return CustomScrollView( - slivers: slivers - ); + item, + key: widget.getKey(item), + entityCardWidgetBuilder: widget.buildEntityListCard, + onEntityTap: widget.onEntityTap, + ), + firstPageProgressIndicatorBuilder: + firstPageProgressIndicatorBuilder, + newPageProgressIndicatorBuilder: + newPageProgressIndicatorBuilder, + noItemsFoundIndicatorBuilder: noItemsFoundIndicatorBuilder)))); + return CustomScrollView(slivers: slivers); } } diff --git a/lib/core/entity/entities_list_widget.dart b/lib/core/entity/entities_list_widget.dart index 2972269..2bf94bf 100644 --- a/lib/core/entity/entities_list_widget.dart +++ b/lib/core/entity/entities_list_widget.dart @@ -2,7 +2,6 @@ import 'dart:async'; import 'package:fading_edge_scrollview/fading_edge_scrollview.dart'; import 'package:flutter/material.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/core/entity/entities_base.dart'; @@ -11,14 +10,15 @@ import 'package:thingsboard_client/thingsboard_client.dart'; import 'entity_list_card.dart'; class EntitiesListWidgetController { - final List<_EntitiesListWidgetState> states = []; - void _registerEntitiesWidgetState(_EntitiesListWidgetState entitiesListWidgetState) { + void _registerEntitiesWidgetState( + _EntitiesListWidgetState entitiesListWidgetState) { states.add(entitiesListWidgetState); } - void _unregisterEntitiesWidgetState(_EntitiesListWidgetState entitiesListWidgetState) { + void _unregisterEntitiesWidgetState( + _EntitiesListWidgetState entitiesListWidgetState) { states.remove(entitiesListWidgetState); } @@ -29,45 +29,48 @@ class EntitiesListWidgetController { void dispose() { states.clear(); } - } -abstract class EntitiesListPageLinkWidget extends EntitiesListWidget { - - EntitiesListPageLinkWidget(TbContext tbContext, {EntitiesListWidgetController? controller}) : super(tbContext, controller: controller); +abstract class EntitiesListPageLinkWidget + extends EntitiesListWidget { + EntitiesListPageLinkWidget(TbContext tbContext, + {EntitiesListWidgetController? controller}) + : super(tbContext, controller: controller); @override - PageKeyController createPageKeyController() => PageLinkController(pageSize: 5); - + PageKeyController createPageKeyController() => + PageLinkController(pageSize: 5); } -abstract class EntitiesListWidget extends TbContextWidget with EntitiesBase { - +abstract class EntitiesListWidget extends TbContextWidget + with EntitiesBase { final EntitiesListWidgetController? _controller; - EntitiesListWidget(TbContext tbContext, {EntitiesListWidgetController? controller}): - _controller = controller, - super(tbContext); + EntitiesListWidget(TbContext tbContext, + {EntitiesListWidgetController? controller}) + : _controller = controller, + super(tbContext); @override - _EntitiesListWidgetState createState() => _EntitiesListWidgetState(_controller); + _EntitiesListWidgetState createState() => + _EntitiesListWidgetState(_controller); PageKeyController

createPageKeyController(); void onViewAll(); - } -class _EntitiesListWidgetState extends TbContextState> { - +class _EntitiesListWidgetState + extends TbContextState> { final EntitiesListWidgetController? _controller; late final PageKeyController

_pageKeyController; - final StreamController?> _entitiesStreamController = StreamController.broadcast(); + final StreamController?> _entitiesStreamController = + StreamController.broadcast(); - _EntitiesListWidgetState(EntitiesListWidgetController? controller): - _controller = controller; + _EntitiesListWidgetState(EntitiesListWidgetController? controller) + : _controller = controller; @override void initState() { @@ -76,7 +79,7 @@ class _EntitiesListWidgetState extends TbContextState extends TbContextState extends TbContextState?>( - stream: _entitiesStreamController.stream, - builder: (context, snapshot) { - if (snapshot.hasData) { - var data = snapshot.data!; - if (data.data.isEmpty) { - return _buildNoEntitiesFound(); //return Text('Loaded'); + stream: _entitiesStreamController.stream, + builder: (context, snapshot) { + if (snapshot.hasData) { + var data = snapshot.data!; + if (data.data.isEmpty) { + return _buildNoEntitiesFound(); //return Text('Loaded'); + } else { + return _buildEntitiesView(context, data.data); + } } else { - return _buildEntitiesView(context, data.data); + return Center( + child: RefreshProgressIndicator( + valueColor: AlwaysStoppedAnimation( + Theme.of(tbContext.currentState!.context) + .colorScheme + .primary), + )); } - } else { - return Center( - child: RefreshProgressIndicator( - valueColor: AlwaysStoppedAnimation(Theme.of(tbContext.currentState!.context).colorScheme.primary), - ) - ); - } - } - ), + }), ) ], - ) - ) - ), + ))), decoration: BoxDecoration( boxShadow: [ BoxShadow( color: Colors.black.withAlpha(25), blurRadius: 10.0, - offset: Offset(0, 4) - ), + offset: Offset(0, 4)), BoxShadow( color: Colors.black.withAlpha(18), blurRadius: 30.0, - offset: Offset(0, 10) - ), + offset: Offset(0, 10)), ], - ) - ); + )); } Widget _buildNoEntitiesFound() { return Container( - decoration: BoxDecoration( + decoration: BoxDecoration( border: Border.all( - color: Color(0xFFDEDEDE), - style: BorderStyle.solid, - width: 1 - ), - borderRadius: BorderRadius.circular(4) - ), - child: Center( - child: - Text(widget.noItemsFoundText, + color: Color(0xFFDEDEDE), style: BorderStyle.solid, width: 1), + borderRadius: BorderRadius.circular(4)), + child: Center( + child: Text(widget.noItemsFoundText, style: TextStyle( color: Color(0xFFAFAFAF), fontSize: 14, - ) - ), - ), + )), + ), ); } @@ -219,13 +209,11 @@ class _EntitiesListWidgetState extends TbContextState EntityListCard( - entity, - entityCardWidgetBuilder: widget.buildEntityListWidgetCard, - onEntityTap: widget.onEntityTap, - settings: widget.entityListCardSettings(entity), - listWidgetCard: true - )).toList() - )); + children: entities + .map((entity) => EntityListCard(entity, + entityCardWidgetBuilder: widget.buildEntityListWidgetCard, + onEntityTap: widget.onEntityTap, + listWidgetCard: true)) + .toList())); } } diff --git a/lib/core/entity/entity_details_page.dart b/lib/core/entity/entity_details_page.dart index fb47b91..b4c96ad 100644 --- a/lib/core/entity/entity_details_page.dart +++ b/lib/core/entity/entity_details_page.dart @@ -1,6 +1,4 @@ -import 'package:flutter/cupertino.dart'; import 'package:flutter/material.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/widgets/tb_app_bar.dart'; @@ -8,18 +6,11 @@ import 'package:thingsboard_app/widgets/tb_progress_indicator.dart'; import 'package:thingsboard_client/thingsboard_client.dart'; abstract class EntityDetailsPage extends TbPageWidget { + final labelTextStyle = + TextStyle(color: Color(0xFF757575), fontSize: 14, height: 20 / 14); - final labelTextStyle = TextStyle( - color: Color(0xFF757575), - fontSize: 14, - height: 20 / 14 - ); - - final valueTextStyle = TextStyle( - color: Color(0xFF282828), - fontSize: 14, - height: 20 / 14 - ); + final valueTextStyle = + TextStyle(color: Color(0xFF282828), fontSize: 14, height: 20 / 14); final String _defaultTitle; final String _entityId; @@ -29,19 +20,19 @@ abstract class EntityDetailsPage extends TbPageWidget { final double? _appBarElevation; EntityDetailsPage(TbContext tbContext, - {required String defaultTitle, - required String entityId, - String? subTitle, - bool showLoadingIndicator = true, - bool hideAppBar = false, - double? appBarElevation}): - this._defaultTitle = defaultTitle, - this._entityId = entityId, - this._subTitle = subTitle, - this._showLoadingIndicator = showLoadingIndicator, - this._hideAppBar = hideAppBar, - this._appBarElevation = appBarElevation, - super(tbContext); + {required String defaultTitle, + required String entityId, + String? subTitle, + bool showLoadingIndicator = true, + bool hideAppBar = false, + double? appBarElevation}) + : this._defaultTitle = defaultTitle, + this._entityId = entityId, + this._subTitle = subTitle, + this._showLoadingIndicator = showLoadingIndicator, + this._hideAppBar = hideAppBar, + this._appBarElevation = appBarElevation, + super(tbContext); @override _EntityDetailsPageState createState() => _EntityDetailsPageState(); @@ -53,11 +44,10 @@ abstract class EntityDetailsPage extends TbPageWidget { } Widget buildEntityDetails(BuildContext context, T entity); - } -class _EntityDetailsPageState extends TbPageState> { - +class _EntityDetailsPageState + extends TbPageState> { late Future entityFuture; late ValueNotifier titleValue; @@ -70,7 +60,7 @@ class _EntityDetailsPageState extends TbPageState extends TbPageState( - valueListenable: titleValue, - builder: (context, title, _widget) { - return Column( - crossAxisAlignment: CrossAxisAlignment.start, - children: [ - FittedBox( - fit: BoxFit.fitWidth, - alignment: Alignment.centerLeft, - child: Text(title, - style: widget._subTitle != null ? Theme.of(context).primaryTextTheme.headline6!.copyWith( - fontSize: 16 - ) : null - ) - ), - if (widget._subTitle != null) Text(widget._subTitle!, style: TextStyle( - color: Theme.of(context).primaryTextTheme.headline6!.color!.withAlpha((0.38 * 255).ceil()), - fontSize: 12, - fontWeight: FontWeight.normal, - height: 16 / 12 - )) - ] - ); - }, - ), - ), + appBar: widget._hideAppBar + ? null + : TbAppBar( + tbContext, + showLoadingIndicator: widget._showLoadingIndicator, + elevation: widget._appBarElevation, + title: ValueListenableBuilder( + valueListenable: titleValue, + builder: (context, title, _widget) { + return Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + FittedBox( + fit: BoxFit.fitWidth, + alignment: Alignment.centerLeft, + child: Text(title, + style: widget._subTitle != null + ? Theme.of(context) + .primaryTextTheme + .headline6! + .copyWith(fontSize: 16) + : null)), + if (widget._subTitle != null) + Text(widget._subTitle!, + style: TextStyle( + color: Theme.of(context) + .primaryTextTheme + .headline6! + .color! + .withAlpha((0.38 * 255).ceil()), + fontSize: 12, + fontWeight: FontWeight.normal, + height: 16 / 12)) + ]); + }, + ), + ), body: FutureBuilder( future: entityFuture, builder: (context, snapshot) { @@ -123,7 +120,8 @@ class _EntityDetailsPageState extends TbPageState extends TbPageState extends EntityDetailsPage { - +abstract class ContactBasedDetailsPage + extends EntityDetailsPage { ContactBasedDetailsPage(TbContext tbContext, - { required String defaultTitle, - required String entityId, - String? subTitle, - bool showLoadingIndicator = true, - bool hideAppBar = false, - double? appBarElevation}): - super(tbContext, defaultTitle: defaultTitle, entityId: entityId, - subTitle: subTitle, showLoadingIndicator: showLoadingIndicator, - hideAppBar: hideAppBar, appBarElevation: appBarElevation); + {required String defaultTitle, + required String entityId, + String? subTitle, + bool showLoadingIndicator = true, + bool hideAppBar = false, + double? appBarElevation}) + : super(tbContext, + defaultTitle: defaultTitle, + entityId: entityId, + subTitle: subTitle, + showLoadingIndicator: showLoadingIndicator, + hideAppBar: hideAppBar, + appBarElevation: appBarElevation); @override Widget buildEntityDetails(BuildContext context, T contact) { @@ -201,9 +202,6 @@ abstract class ContactBasedDetailsPage extends EntityDet SizedBox(height: 16), Text('Email', style: labelTextStyle), Text(contact.email ?? '', style: valueTextStyle), - ] - ) - ); + ])); } } - diff --git a/lib/core/entity/entity_grid_card.dart b/lib/core/entity/entity_grid_card.dart index 1435987..e3a6273 100644 --- a/lib/core/entity/entity_grid_card.dart +++ b/lib/core/entity/entity_grid_card.dart @@ -1,7 +1,4 @@ - import 'package:flutter/material.dart'; -import 'package:flutter/widgets.dart'; -import 'package:thingsboard_client/thingsboard_client.dart'; import 'entities_base.dart'; @@ -11,10 +8,12 @@ class EntityGridCard extends StatelessWidget { final EntityCardWidgetBuilder _entityCardWidgetBuilder; final EntityCardSettings _settings; - EntityGridCard(T entity, {Key? key, EntityTapFunction? onEntityTap, - required EntityCardWidgetBuilder entityCardWidgetBuilder, - required EntityCardSettings settings}): - this._entity = entity, + EntityGridCard(T entity, + {Key? key, + EntityTapFunction? onEntityTap, + required EntityCardWidgetBuilder entityCardWidgetBuilder, + required EntityCardSettings settings}) + : this._entity = entity, this._onEntityTap = onEntityTap, this._entityCardWidgetBuilder = entityCardWidgetBuilder, this._settings = settings, @@ -22,35 +21,31 @@ class EntityGridCard extends StatelessWidget { @override Widget build(BuildContext context) { - return - GestureDetector( - behavior: HitTestBehavior.opaque, - child: - Container( - child: Card( - margin: EdgeInsets.zero, - shape: RoundedRectangleBorder( - borderRadius: BorderRadius.circular(4), - ), - elevation: 0, - child: _entityCardWidgetBuilder(context, _entity) - ), - decoration: _settings.dropShadow ? BoxDecoration( - boxShadow: [ - BoxShadow( - color: Colors.black.withAlpha((255 * 0.05).ceil()), - blurRadius: 6.0, - offset: Offset(0, 4) + return GestureDetector( + behavior: HitTestBehavior.opaque, + child: Container( + child: Card( + margin: EdgeInsets.zero, + shape: RoundedRectangleBorder( + borderRadius: BorderRadius.circular(4), + ), + elevation: 0, + child: _entityCardWidgetBuilder(context, _entity)), + decoration: _settings.dropShadow + ? BoxDecoration( + boxShadow: [ + BoxShadow( + color: Colors.black.withAlpha((255 * 0.05).ceil()), + blurRadius: 6.0, + offset: Offset(0, 4)) + ], ) - ], - ) : null, - ), - onTap: () { - if (_onEntityTap != null) { - _onEntityTap!(_entity); - } + : null, + ), + onTap: () { + if (_onEntityTap != null) { + _onEntityTap!(_entity); } - ); + }); } } - diff --git a/lib/core/entity/entity_list_card.dart b/lib/core/entity/entity_list_card.dart index bfda649..59415bf 100644 --- a/lib/core/entity/entity_list_card.dart +++ b/lib/core/entity/entity_list_card.dart @@ -1,6 +1,4 @@ - import 'package:flutter/material.dart'; -import 'package:flutter/widgets.dart'; import 'entities_base.dart'; @@ -9,58 +7,51 @@ class EntityListCard extends StatelessWidget { final T _entity; final EntityTapFunction? _onEntityTap; final EntityCardWidgetBuilder _entityCardWidgetBuilder; - final EntityCardSettings _settings; - EntityListCard(T entity, {Key? key, EntityTapFunction? onEntityTap, - required EntityCardWidgetBuilder entityCardWidgetBuilder, - required EntityCardSettings settings, - bool listWidgetCard = false}): - this._entity = entity, + EntityListCard(T entity, + {Key? key, + EntityTapFunction? onEntityTap, + required EntityCardWidgetBuilder entityCardWidgetBuilder, + bool listWidgetCard = false}) + : this._entity = entity, this._onEntityTap = onEntityTap, this._entityCardWidgetBuilder = entityCardWidgetBuilder, - this._settings = settings, this._listWidgetCard = listWidgetCard, super(key: key); @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(4), + 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(4), + ), + elevation: 0, + 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((255 * 0.05).ceil()), + blurRadius: 6.0, + offset: Offset(0, 4)), + ], ), - elevation: 0, - 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((255 * 0.05).ceil()), - blurRadius: 6.0, - offset: Offset(0, 4) - ), - ], - ), - ), - onTap: () { - if (_onEntityTap != null) { - _onEntityTap!(_entity); - } + ), + onTap: () { + if (_onEntityTap != null) { + _onEntityTap!(_entity); } - ); + }); } } - diff --git a/lib/core/init/init_app.dart b/lib/core/init/init_app.dart index a545d29..890f19d 100644 --- a/lib/core/init/init_app.dart +++ b/lib/core/init/init_app.dart @@ -1,20 +1,17 @@ import 'package:flutter/material.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/widgets/tb_progress_indicator.dart'; class ThingsboardInitApp extends TbPageWidget { - - ThingsboardInitApp(TbContext tbContext, {Key? key}) : super(tbContext, key: key); + ThingsboardInitApp(TbContext tbContext, {Key? key}) + : super(tbContext, key: key); @override _ThingsboardInitAppState createState() => _ThingsboardInitAppState(); - } class _ThingsboardInitAppState extends TbPageState { - @override void initState() { super.initState(); @@ -26,10 +23,7 @@ class _ThingsboardInitAppState extends TbPageState { return Container( alignment: Alignment.center, color: Colors.white, - child: TbProgressIndicator( - size: 50.0 - ), + child: TbProgressIndicator(size: 50.0), ); } - } diff --git a/lib/core/init/init_routes.dart b/lib/core/init/init_routes.dart index 7c0a474..698e53c 100644 --- a/lib/core/init/init_routes.dart +++ b/lib/core/init/init_routes.dart @@ -7,8 +7,8 @@ import 'package:thingsboard_app/core/context/tb_context.dart'; import 'init_app.dart'; class InitRoutes extends TbRoutes { - - late var initHandler = Handler(handlerFunc: (BuildContext? context, Map params) { + late var initHandler = Handler( + handlerFunc: (BuildContext? context, Map params) { return ThingsboardInitApp(tbContext); }); @@ -18,5 +18,4 @@ class InitRoutes extends TbRoutes { void doRegisterRoutes(router) { router.define("/", handler: initHandler); } - } diff --git a/lib/main.dart b/lib/main.dart index 3cce13c..f5710e3 100644 --- a/lib/main.dart +++ b/lib/main.dart @@ -1,6 +1,5 @@ import 'package:universal_platform/universal_platform.dart'; -import 'package:flutter/foundation.dart'; import 'package:flutter/material.dart'; import 'package:flutter/services.dart'; import 'package:flutter_inappwebview/flutter_inappwebview.dart'; @@ -14,7 +13,6 @@ import 'config/themes/tb_theme.dart'; final appRouter = ThingsboardAppRouter(); void main() async { - WidgetsFlutterBinding.ensureInitialized(); // await FlutterDownloader.initialize(); // await Permission.storage.request(); @@ -27,18 +25,18 @@ void main() async { } class ThingsboardApp extends StatefulWidget { - ThingsboardApp({Key? key}) : super(key: key); @override ThingsboardAppState createState() => ThingsboardAppState(); - } -class ThingsboardAppState extends State with TickerProviderStateMixin implements TbMainDashboardHolder { - +class ThingsboardAppState extends State + with TickerProviderStateMixin + implements TbMainDashboardHolder { final TwoPageViewController _mainPageViewController = TwoPageViewController(); - final MainDashboardPageController _mainDashboardPageController = MainDashboardPageController(); + final MainDashboardPageController _mainDashboardPageController = + MainDashboardPageController(); final GlobalKey mainAppKey = GlobalKey(); final GlobalKey dashboardKey = GlobalKey(); @@ -50,8 +48,13 @@ class ThingsboardAppState extends State with TickerProviderState } @override - Future navigateToDashboard(String dashboardId, {String? dashboardTitle, String? state, bool? hideToolbar, bool animate = true}) async { - await _mainDashboardPageController.openDashboard(dashboardId, dashboardTitle: dashboardTitle, state: state, hideToolbar: hideToolbar); + Future navigateToDashboard(String dashboardId, + {String? dashboardTitle, + String? state, + bool? hideToolbar, + bool animate = true}) async { + await _mainDashboardPageController.openDashboard(dashboardId, + dashboardTitle: dashboardTitle, state: state, hideToolbar: hideToolbar); _openDashboard(animate: animate); } @@ -121,40 +124,36 @@ class ThingsboardAppState extends State with TickerProviderState return res; } - @override Widget build(BuildContext context) { SystemChrome.setSystemUIOverlayStyle(SystemUiOverlayStyle( systemNavigationBarColor: Colors.white, statusBarColor: Colors.white, - systemNavigationBarIconBrightness: Brightness.light - )); + systemNavigationBarIconBrightness: Brightness.light)); return MaterialApp( - title: 'ThingsBoard', + title: 'ThingsBoard', themeMode: ThemeMode.light, home: TwoPageView( - controller: _mainPageViewController, - first: MaterialApp( - key: mainAppKey, - scaffoldMessengerKey: appRouter.tbContext.messengerKey, - title: 'ThingsBoard', - theme: tbTheme, - themeMode: ThemeMode.light, - darkTheme: tbDarkTheme, - onGenerateRoute: appRouter.router.generator, - navigatorObservers: [appRouter.tbContext.routeObserver], - ), - second: MaterialApp( - key: dashboardKey, - // scaffoldMessengerKey: appRouter.tbContext.messengerKey, - title: 'ThingsBoard', - theme: tbTheme, - themeMode: ThemeMode.light, - darkTheme: tbDarkTheme, - home: MainDashboardPage(appRouter.tbContext, controller: _mainDashboardPageController), - ) - ) - ); + controller: _mainPageViewController, + first: MaterialApp( + key: mainAppKey, + scaffoldMessengerKey: appRouter.tbContext.messengerKey, + title: 'ThingsBoard', + theme: tbTheme, + themeMode: ThemeMode.light, + darkTheme: tbDarkTheme, + onGenerateRoute: appRouter.router.generator, + navigatorObservers: [appRouter.tbContext.routeObserver], + ), + second: MaterialApp( + key: dashboardKey, + // scaffoldMessengerKey: appRouter.tbContext.messengerKey, + title: 'ThingsBoard', + theme: tbTheme, + themeMode: ThemeMode.light, + darkTheme: tbDarkTheme, + home: MainDashboardPage(appRouter.tbContext, + controller: _mainDashboardPageController), + ))); } - } diff --git a/lib/modules/alarm/alarm_routes.dart b/lib/modules/alarm/alarm_routes.dart index 36b479d..f2851b2 100644 --- a/lib/modules/alarm/alarm_routes.dart +++ b/lib/modules/alarm/alarm_routes.dart @@ -6,8 +6,8 @@ import 'package:thingsboard_app/modules/alarm/alarms_page.dart'; import 'package:thingsboard_app/modules/main/main_page.dart'; class AlarmRoutes extends TbRoutes { - - late var alarmsHandler = Handler(handlerFunc: (BuildContext? context, Map params) { + late var alarmsHandler = Handler( + handlerFunc: (BuildContext? context, Map params) { var searchMode = params['search']?.first == 'true'; if (searchMode) { return AlarmsPage(tbContext, searchMode: true); @@ -22,5 +22,4 @@ class AlarmRoutes extends TbRoutes { void doRegisterRoutes(router) { router.define("/alarms", handler: alarmsHandler); } - } diff --git a/lib/modules/alarm/alarms_base.dart b/lib/modules/alarm/alarms_base.dart index 5471cad..3192696 100644 --- a/lib/modules/alarm/alarms_base.dart +++ b/lib/modules/alarm/alarms_base.dart @@ -1,6 +1,5 @@ import 'package:auto_size_text/auto_size_text.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'; @@ -8,7 +7,6 @@ import 'package:thingsboard_app/core/entity/entities_base.dart'; import 'package:thingsboard_app/utils/utils.dart'; import 'package:thingsboard_client/thingsboard_client.dart'; - const Map alarmSeverityColors = { AlarmSeverity.CRITICAL: Color(0xFFFF0000), AlarmSeverity.MAJOR: Color(0xFFFFA500), @@ -33,7 +31,6 @@ const Map alarmStatusTranslations = { }; mixin AlarmsBase on EntitiesBase { - @override String get title => 'Alarms'; @@ -49,11 +46,14 @@ mixin AlarmsBase on EntitiesBase { void onEntityTap(AlarmInfo alarm) { String? dashboardId = alarm.details?['dashboardId']; if (dashboardId != null) { - var state = Utils.createDashboardEntityState(alarm.originator, entityName: alarm.originatorName); - navigateToDashboard(dashboardId, dashboardTitle: alarm.originatorName, state: state); + var state = Utils.createDashboardEntityState(alarm.originator, + entityName: alarm.originatorName); + navigateToDashboard(dashboardId, + dashboardTitle: alarm.originatorName, state: state); } else { if (tbClient.isTenantAdmin()) { - showWarnNotification('Mobile dashboard should be configured in device profile alarm rules!'); + showWarnNotification( + 'Mobile dashboard should be configured in device profile alarm rules!'); } } } @@ -69,8 +69,11 @@ mixin AlarmsBase on EntitiesBase { } class AlarmQueryController extends PageKeyController { - - AlarmQueryController({int pageSize = 20, String? searchText}) : super(AlarmQuery(TimePageLink(pageSize, 0, searchText, SortOrder('createdTime', Direction.DESC)), fetchOriginator: true)); + AlarmQueryController({int pageSize = 20, String? searchText}) + : super(AlarmQuery( + TimePageLink(pageSize, 0, searchText, + SortOrder('createdTime', Direction.DESC)), + fetchOriginator: true)); @override AlarmQuery nextPageKey(AlarmQuery pageKey) { @@ -83,28 +86,24 @@ class AlarmQueryController extends PageKeyController { query.pageLink.textSearch = searchText; notifyListeners(); } - } class AlarmCard extends TbContextWidget { - final AlarmInfo alarm; AlarmCard(TbContext tbContext, {required this.alarm}) : super(tbContext); @override _AlarmCardState createState() => _AlarmCardState(alarm); - } class _AlarmCardState extends TbContextState { - bool loading = false; AlarmInfo alarm; final entityDateFormat = DateFormat('yyyy-MM-dd'); - _AlarmCardState(this.alarm): super(); + _AlarmCardState(this.alarm) : super(); @override void initState() { @@ -121,161 +120,176 @@ class _AlarmCardState extends TbContextState { @override Widget build(BuildContext context) { if (this.loading) { - return Container( height: 134, alignment: Alignment.center, child: RefreshProgressIndicator()); + return Container( + height: 134, + alignment: Alignment.center, + child: RefreshProgressIndicator()); } else { bool hasDashboard = alarm.details?['dashboardId'] != null; return Stack( children: [ Positioned.fill( - child: Container( - alignment: Alignment.centerLeft, - child: Container( - width: 4, - decoration: BoxDecoration( - color: alarmSeverityColors[alarm.severity]!, - borderRadius: BorderRadius.only(topLeft: Radius.circular(4), bottomLeft: Radius.circular(4)) - ), - ) - ) - ), - Row( - mainAxisSize: MainAxisSize.max, - children: [ - SizedBox(width: 4), - Flexible( - fit: FlexFit.tight, - child: Column( - crossAxisAlignment: CrossAxisAlignment.start, - children: [ - Row( - mainAxisSize: MainAxisSize.max, - crossAxisAlignment: CrossAxisAlignment.center, - children: [ - SizedBox(width: 16), - Flexible( - fit: FlexFit.tight, - child: Column( - crossAxisAlignment: CrossAxisAlignment.start, - children: [ - SizedBox(height: 12), - Row( - mainAxisSize: MainAxisSize.max, - 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(entityDateFormat.format(DateTime.fromMillisecondsSinceEpoch(alarm.createdTime!)), + child: Container( + alignment: Alignment.centerLeft, + child: Container( + width: 4, + decoration: BoxDecoration( + color: alarmSeverityColors[alarm.severity]!, + borderRadius: BorderRadius.only( + topLeft: Radius.circular(4), + bottomLeft: Radius.circular(4))), + ))), + Row(mainAxisSize: MainAxisSize.max, children: [ + SizedBox(width: 4), + Flexible( + fit: FlexFit.tight, + child: Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + Row( + mainAxisSize: MainAxisSize.max, + crossAxisAlignment: CrossAxisAlignment.center, + children: [ + SizedBox(width: 16), + Flexible( + fit: FlexFit.tight, + child: Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + SizedBox(height: 12), + Row( + mainAxisSize: MainAxisSize.max, + 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( + entityDateFormat.format(DateTime + .fromMillisecondsSinceEpoch( + alarm.createdTime!)), + style: TextStyle( + color: Color(0xFFAFAFAF), + fontWeight: FontWeight.normal, + 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, + fontWeight: + FontWeight.normal, 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(alarmSeverityTranslations[alarm.severity]!, - style: TextStyle( - color: alarmSeverityColors[alarm.severity]!, - fontWeight: FontWeight.w500, - fontSize: 12, - height: 16 / 12) - ) - ] - ), - SizedBox(height: 12)], - ) - ), - SizedBox(width: 16), - if (hasDashboard) Icon(Icons.chevron_right, color: Color(0xFFACACAC)), - if (hasDashboard) SizedBox(width: 16), - ] - ), - Divider(height: 1), - SizedBox(height: 8), + height: 16 / 12))), + Text( + alarmSeverityTranslations[ + alarm.severity]!, + style: TextStyle( + color: alarmSeverityColors[ + alarm.severity]!, + fontWeight: FontWeight.w500, + fontSize: 12, + height: 16 / 12)) + ]), + SizedBox(height: 12) + ], + )), + SizedBox(width: 16), + if (hasDashboard) + Icon(Icons.chevron_right, + color: Color(0xFFACACAC)), + if (hasDashboard) SizedBox(width: 16), + ]), + Divider(height: 1), + SizedBox(height: 8), + Row( + crossAxisAlignment: CrossAxisAlignment.center, + children: [ + SizedBox(width: 16), + Flexible( + fit: FlexFit.tight, + child: Text( + alarmStatusTranslations[alarm.status]!, + style: TextStyle( + color: Color(0xFF282828), + fontWeight: FontWeight.normal, + fontSize: 14, + height: 20 / 14))), + SizedBox(height: 32), Row( - crossAxisAlignment: CrossAxisAlignment.center, children: [ - SizedBox(width: 16), - Flexible( - fit: FlexFit.tight, - child: Text(alarmStatusTranslations[alarm.status]!, - style: TextStyle( - color: Color(0xFF282828), - fontWeight: FontWeight.normal, - fontSize: 14, - height: 20 / 14) - ) - ), - SizedBox(height: 32), - Row( - children: [ - if ([AlarmStatus.CLEARED_UNACK, AlarmStatus.ACTIVE_UNACK].contains(alarm.status)) - CircleAvatar( - radius: 16, - backgroundColor: Color(0xffF0F4F9), - child: IconButton(icon: Icon(Icons.done, size: 18), padding: EdgeInsets.all(7.0), onPressed: () => _ackAlarm(alarm)) - ), - if ([AlarmStatus.ACTIVE_UNACK, AlarmStatus.ACTIVE_ACK].contains(alarm.status)) - Row( - children: [ - SizedBox(width: 4), - CircleAvatar( - radius: 16, - backgroundColor: Color(0xffF0F4F9), - child: IconButton(icon: Icon(Icons.clear, size: 18), padding: EdgeInsets.all(7.0), onPressed: () => _clearAlarm(alarm)) - ) - ] - ) - ], - ), - SizedBox(width: 8) + if ([ + AlarmStatus.CLEARED_UNACK, + AlarmStatus.ACTIVE_UNACK + ].contains(alarm.status)) + CircleAvatar( + radius: 16, + backgroundColor: Color(0xffF0F4F9), + child: IconButton( + icon: Icon(Icons.done, size: 18), + padding: EdgeInsets.all(7.0), + onPressed: () => _ackAlarm(alarm))), + if ([ + AlarmStatus.ACTIVE_UNACK, + AlarmStatus.ACTIVE_ACK + ].contains(alarm.status)) + Row(children: [ + SizedBox(width: 4), + CircleAvatar( + radius: 16, + backgroundColor: Color(0xffF0F4F9), + child: IconButton( + icon: Icon(Icons.clear, size: 18), + padding: EdgeInsets.all(7.0), + onPressed: () => _clearAlarm(alarm))) + ]) ], ), - SizedBox(height: 8) - ] - ) - ) - ] - ) + SizedBox(width: 8) + ], + ), + SizedBox(height: 8) + ])) + ]) ], ); } } _clearAlarm(AlarmInfo alarm) async { - var res = await confirm(title: 'Clear Alarm', message: 'Are you sure you want to clear Alarm?', cancel: 'No', ok: 'Yes'); + var res = await confirm( + title: 'Clear Alarm', + message: 'Are you sure you want to clear Alarm?', + cancel: 'No', + ok: 'Yes'); if (res != null && res) { setState(() { loading = true; }); await tbClient.getAlarmService().clearAlarm(alarm.id!.id!); - var newAlarm = await tbClient.getAlarmService().getAlarmInfo( - alarm.id!.id!); + var newAlarm = + await tbClient.getAlarmService().getAlarmInfo(alarm.id!.id!); setState(() { loading = false; this.alarm = newAlarm!; @@ -284,19 +298,22 @@ class _AlarmCardState extends TbContextState { } _ackAlarm(AlarmInfo alarm) async { - var res = await confirm(title: 'Acknowledge Alarm', message: 'Are you sure you want to acknowledge Alarm?', cancel: 'No', ok: 'Yes'); + var res = await confirm( + title: 'Acknowledge Alarm', + message: 'Are you sure you want to acknowledge Alarm?', + cancel: 'No', + ok: 'Yes'); if (res != null && res) { setState(() { loading = true; }); await tbClient.getAlarmService().ackAlarm(alarm.id!.id!); - var newAlarm = await tbClient.getAlarmService().getAlarmInfo( - alarm.id!.id!); + var newAlarm = + await tbClient.getAlarmService().getAlarmInfo(alarm.id!.id!); setState(() { loading = false; this.alarm = newAlarm!; }); } } - } diff --git a/lib/modules/alarm/alarms_list.dart b/lib/modules/alarm/alarms_list.dart index 7f56af2..a28947d 100644 --- a/lib/modules/alarm/alarms_list.dart +++ b/lib/modules/alarm/alarms_list.dart @@ -1,4 +1,3 @@ -import 'package:flutter/widgets.dart'; 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'; @@ -6,9 +5,10 @@ import 'package:thingsboard_client/thingsboard_client.dart'; import 'alarms_base.dart'; -class AlarmsList extends BaseEntitiesWidget with AlarmsBase, EntitiesListStateBase { - - AlarmsList(TbContext tbContext, PageKeyController pageKeyController, {searchMode = false}) : super(tbContext, pageKeyController, searchMode: searchMode); - +class AlarmsList extends BaseEntitiesWidget + with AlarmsBase, EntitiesListStateBase { + AlarmsList( + TbContext tbContext, PageKeyController pageKeyController, + {searchMode = false}) + : super(tbContext, pageKeyController, searchMode: searchMode); } - diff --git a/lib/modules/alarm/alarms_page.dart b/lib/modules/alarm/alarms_page.dart index f17f440..ca0193c 100644 --- a/lib/modules/alarm/alarms_page.dart +++ b/lib/modules/alarm/alarms_page.dart @@ -7,18 +7,16 @@ import 'package:thingsboard_app/widgets/tb_app_bar.dart'; import 'alarms_list.dart'; class AlarmsPage extends TbContextWidget { - final bool searchMode; AlarmsPage(TbContext tbContext, {this.searchMode = false}) : super(tbContext); @override _AlarmsPageState createState() => _AlarmsPageState(); - } -class _AlarmsPageState extends TbContextState with AutomaticKeepAliveClientMixin { - +class _AlarmsPageState extends TbContextState + with AutomaticKeepAliveClientMixin { final AlarmQueryController _alarmQueryController = AlarmQueryController(); @override @@ -29,32 +27,26 @@ class _AlarmsPageState extends TbContextState with AutomaticKeepAliv @override Widget build(BuildContext context) { super.build(context); - var alarmsList = AlarmsList(tbContext, _alarmQueryController, searchMode: widget.searchMode); + var alarmsList = AlarmsList(tbContext, _alarmQueryController, + searchMode: widget.searchMode); PreferredSizeWidget appBar; if (widget.searchMode) { appBar = TbAppSearchBar( tbContext, - onSearch: (searchText) => _alarmQueryController.onSearchText(searchText), + onSearch: (searchText) => + _alarmQueryController.onSearchText(searchText), ); } else { - appBar = TbAppBar( - tbContext, - title: Text(alarmsList.title), - actions: [ - IconButton( - icon: Icon( - Icons.search - ), - onPressed: () { - navigateTo('/alarms?search=true'); - }, - ) - ]); + appBar = TbAppBar(tbContext, title: Text(alarmsList.title), actions: [ + IconButton( + icon: Icon(Icons.search), + onPressed: () { + navigateTo('/alarms?search=true'); + }, + ) + ]); } - return Scaffold( - appBar: appBar, - body: alarmsList - ); + return Scaffold(appBar: appBar, body: alarmsList); } @override @@ -62,5 +54,4 @@ class _AlarmsPageState extends TbContextState with AutomaticKeepAliv _alarmQueryController.dispose(); super.dispose(); } - } diff --git a/lib/modules/asset/asset_details_page.dart b/lib/modules/asset/asset_details_page.dart index 718875d..29055f1 100644 --- a/lib/modules/asset/asset_details_page.dart +++ b/lib/modules/asset/asset_details_page.dart @@ -5,11 +5,11 @@ import 'package:thingsboard_app/core/entity/entity_details_page.dart'; import 'package:thingsboard_client/thingsboard_client.dart'; class AssetDetailsPage extends EntityDetailsPage { - - AssetDetailsPage(TbContext tbContext, String assetId): - super(tbContext, - entityId: assetId, - defaultTitle: 'Asset', subTitle: 'Asset details'); + AssetDetailsPage(TbContext tbContext, String assetId) + : super(tbContext, + entityId: assetId, + defaultTitle: 'Asset', + subTitle: 'Asset details'); @override Future fetchEntity(String assetId) { @@ -35,9 +35,6 @@ class AssetDetailsPage extends EntityDetailsPage { SizedBox(height: 16), Text('Assigned to customer', style: labelTextStyle), Text(asset.customerTitle ?? '', style: valueTextStyle), - ] - ) - ); + ])); } - } diff --git a/lib/modules/asset/asset_routes.dart b/lib/modules/asset/asset_routes.dart index a2ca78f..6a52743 100644 --- a/lib/modules/asset/asset_routes.dart +++ b/lib/modules/asset/asset_routes.dart @@ -7,13 +7,14 @@ import 'package:thingsboard_app/modules/asset/assets_page.dart'; import 'asset_details_page.dart'; class AssetRoutes extends TbRoutes { - - late var assetsHandler = Handler(handlerFunc: (BuildContext? context, Map params) { + late var assetsHandler = Handler( + handlerFunc: (BuildContext? context, Map params) { var searchMode = params['search']?.first == 'true'; return AssetsPage(tbContext, searchMode: searchMode); }); - late var assetDetailsHandler = Handler(handlerFunc: (BuildContext? context, Map params) { + late var assetDetailsHandler = Handler( + handlerFunc: (BuildContext? context, Map params) { return AssetDetailsPage(tbContext, params["id"][0]); }); @@ -24,5 +25,4 @@ class AssetRoutes extends TbRoutes { router.define("/assets", handler: assetsHandler); router.define("/asset/:id", handler: assetDetailsHandler); } - } diff --git a/lib/modules/asset/assets_base.dart b/lib/modules/asset/assets_base.dart index 0bdcb69..83ab9d4 100644 --- a/lib/modules/asset/assets_base.dart +++ b/lib/modules/asset/assets_base.dart @@ -1,10 +1,8 @@ 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'; mixin AssetsBase on EntitiesBase { - @override String get title => 'Assets'; @@ -16,7 +14,9 @@ mixin AssetsBase on EntitiesBase { if (tbClient.isTenantAdmin()) { return tbClient.getAssetService().getTenantAssetInfos(pageLink); } else { - return tbClient.getAssetService().getCustomerAssetInfos(tbClient.getAuthUser()!.customerId, pageLink); + return tbClient + .getAssetService() + .getCustomerAssetInfos(tbClient.getAuthUser()!.customerId!, pageLink); } } @@ -41,115 +41,91 @@ mixin AssetsBase on EntitiesBase { } Widget _buildCard(context, AssetInfo asset) { - return Row( - mainAxisSize: MainAxisSize.max, - children: [ - Flexible( - fit: FlexFit.tight, - child: - Container( - padding: EdgeInsets.symmetric(vertical: 10, horizontal: 0), - child: Row( - mainAxisSize: MainAxisSize.max, - children: [ - SizedBox(width: 16), - Flexible( - fit: FlexFit.tight, - child: - Column( - crossAxisAlignment: CrossAxisAlignment.start, - children: [ - Row( - mainAxisSize: MainAxisSize.max, - mainAxisAlignment: MainAxisAlignment.spaceBetween, - children: [ - FittedBox( - fit: BoxFit.fitWidth, - alignment: Alignment.centerLeft, - child: Text('${asset.name}', - style: TextStyle( - color: Color(0xFF282828), - fontSize: 14, - fontWeight: FontWeight.w500, - height: 20 / 14 - )) - ), - Text(entityDateFormat.format(DateTime.fromMillisecondsSinceEpoch(asset.createdTime!)), + return Row(mainAxisSize: MainAxisSize.max, children: [ + Flexible( + fit: FlexFit.tight, + child: Container( + padding: EdgeInsets.symmetric(vertical: 10, horizontal: 0), + child: Row( + mainAxisSize: MainAxisSize.max, + children: [ + SizedBox(width: 16), + Flexible( + fit: FlexFit.tight, + child: Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + Row( + mainAxisSize: MainAxisSize.max, + mainAxisAlignment: MainAxisAlignment.spaceBetween, + children: [ + FittedBox( + fit: BoxFit.fitWidth, + alignment: Alignment.centerLeft, + child: Text('${asset.name}', style: TextStyle( - color: Color(0xFFAFAFAF), - fontSize: 12, - fontWeight: FontWeight.normal, - height: 16 / 12 - )) - ] - ), - SizedBox(height: 4), - Text('${asset.type}', - style: TextStyle( - color: Color(0xFFAFAFAF), - fontSize: 12, - fontWeight: FontWeight.normal, - height: 1.33 - )) - ], - ) - ), - SizedBox(width: 16), - Icon(Icons.chevron_right, color: Color(0xFFACACAC)), - SizedBox(width: 16) - ], - ), - ) - - ) - ] - ); + color: Color(0xFF282828), + fontSize: 14, + fontWeight: FontWeight.w500, + height: 20 / 14))), + Text( + entityDateFormat.format( + DateTime.fromMillisecondsSinceEpoch( + asset.createdTime!)), + style: TextStyle( + color: Color(0xFFAFAFAF), + fontSize: 12, + fontWeight: FontWeight.normal, + height: 16 / 12)) + ]), + SizedBox(height: 4), + Text('${asset.type}', + style: TextStyle( + color: Color(0xFFAFAFAF), + fontSize: 12, + fontWeight: FontWeight.normal, + height: 1.33)) + ], + )), + SizedBox(width: 16), + Icon(Icons.chevron_right, color: Color(0xFFACACAC)), + SizedBox(width: 16) + ], + ), + )) + ]); } Widget _buildListWidgetCard(BuildContext context, AssetInfo asset) { - return Row( - mainAxisSize: MainAxisSize.min, - children: [ - Flexible( - fit: FlexFit.loose, - child: - Container( - padding: EdgeInsets.symmetric(vertical: 9, horizontal: 16), - child: Row( - mainAxisSize: MainAxisSize.min, - children: [ - Flexible( - fit: FlexFit.loose, - child: - Column( - crossAxisAlignment: CrossAxisAlignment.start, - children: [ - FittedBox( - fit: BoxFit.fitWidth, - alignment: Alignment.centerLeft, - child: Text('${asset.name}', - style: TextStyle( - color: Color(0xFF282828), - fontSize: 14, - fontWeight: FontWeight.w500, - height: 1.7 - )) - ), - Text('${asset.type}', + return Row(mainAxisSize: MainAxisSize.min, children: [ + Flexible( + fit: FlexFit.loose, + child: Container( + padding: EdgeInsets.symmetric(vertical: 9, horizontal: 16), + child: Row(mainAxisSize: MainAxisSize.min, children: [ + Flexible( + fit: FlexFit.loose, + child: Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + FittedBox( + fit: BoxFit.fitWidth, + alignment: Alignment.centerLeft, + child: Text('${asset.name}', style: TextStyle( - color: Color(0xFFAFAFAF), - fontSize: 12, - fontWeight: FontWeight.normal, - height: 1.33 - )) - ], - ) - ) - ] - ) - ) - ) - ] - ); + color: Color(0xFF282828), + fontSize: 14, + fontWeight: FontWeight.w500, + height: 1.7))), + Text('${asset.type}', + style: TextStyle( + color: Color(0xFFAFAFAF), + fontSize: 12, + fontWeight: FontWeight.normal, + height: 1.33)) + ], + )) + ]))) + ]); } } diff --git a/lib/modules/asset/assets_list.dart b/lib/modules/asset/assets_list.dart index aa3da35..9f08002 100644 --- a/lib/modules/asset/assets_list.dart +++ b/lib/modules/asset/assets_list.dart @@ -5,9 +5,9 @@ import 'package:thingsboard_client/thingsboard_client.dart'; import 'assets_base.dart'; -class AssetsList extends BaseEntitiesWidget with AssetsBase, EntitiesListStateBase { - - AssetsList(TbContext tbContext, PageKeyController pageKeyController, {searchMode = false}) : super(tbContext, pageKeyController, searchMode: searchMode); - +class AssetsList extends BaseEntitiesWidget + with AssetsBase, EntitiesListStateBase { + AssetsList(TbContext tbContext, PageKeyController pageKeyController, + {searchMode = false}) + : super(tbContext, pageKeyController, searchMode: searchMode); } - diff --git a/lib/modules/asset/assets_list_widget.dart b/lib/modules/asset/assets_list_widget.dart index 85dcbf4..d02f95a 100644 --- a/lib/modules/asset/assets_list_widget.dart +++ b/lib/modules/asset/assets_list_widget.dart @@ -3,13 +3,14 @@ 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 with AssetsBase { - - AssetsListWidget(TbContext tbContext, {EntitiesListWidgetController? controller}): super(tbContext, controller: controller); +class AssetsListWidget extends EntitiesListPageLinkWidget + with AssetsBase { + AssetsListWidget(TbContext tbContext, + {EntitiesListWidgetController? controller}) + : super(tbContext, controller: controller); @override void onViewAll() { navigateTo('/assets'); } - } diff --git a/lib/modules/asset/assets_page.dart b/lib/modules/asset/assets_page.dart index 1f301a6..b691130 100644 --- a/lib/modules/asset/assets_page.dart +++ b/lib/modules/asset/assets_page.dart @@ -7,23 +7,21 @@ import 'package:thingsboard_app/widgets/tb_app_bar.dart'; import 'assets_list.dart'; class AssetsPage extends TbPageWidget { - final bool searchMode; AssetsPage(TbContext tbContext, {this.searchMode = false}) : super(tbContext); @override _AssetsPageState createState() => _AssetsPageState(); - } class _AssetsPageState extends TbPageState { - final PageLinkController _pageLinkController = PageLinkController(); @override Widget build(BuildContext context) { - var assetsList = AssetsList(tbContext, _pageLinkController, searchMode: widget.searchMode); + var assetsList = AssetsList(tbContext, _pageLinkController, + searchMode: widget.searchMode); PreferredSizeWidget appBar; if (widget.searchMode) { appBar = TbAppSearchBar( @@ -31,24 +29,16 @@ class _AssetsPageState extends TbPageState { onSearch: (searchText) => _pageLinkController.onSearchText(searchText), ); } else { - appBar = TbAppBar( - tbContext, - title: Text(assetsList.title), - actions: [ - IconButton( - icon: Icon( - Icons.search - ), - onPressed: () { - navigateTo('/assets?search=true'); - }, - ) - ]); + appBar = TbAppBar(tbContext, title: Text(assetsList.title), actions: [ + IconButton( + icon: Icon(Icons.search), + onPressed: () { + navigateTo('/assets?search=true'); + }, + ) + ]); } - return Scaffold( - appBar: appBar, - body: assetsList - ); + return Scaffold(appBar: appBar, body: assetsList); } @override @@ -56,5 +46,4 @@ class _AssetsPageState extends TbPageState { _pageLinkController.dispose(); super.dispose(); } - } diff --git a/lib/modules/audit_log/audit_log_details_page.dart b/lib/modules/audit_log/audit_log_details_page.dart index e821f20..a25b77e 100644 --- a/lib/modules/audit_log/audit_log_details_page.dart +++ b/lib/modules/audit_log/audit_log_details_page.dart @@ -9,29 +9,20 @@ import 'package:thingsboard_app/widgets/tb_app_bar.dart'; import 'package:thingsboard_client/thingsboard_client.dart'; class AuditLogDetailsPage extends TbContextWidget { - final AuditLog auditLog; AuditLogDetailsPage(TbContext tbContext, this.auditLog) : super(tbContext); @override _AuditLogDetailsPageState createState() => _AuditLogDetailsPageState(); - } class _AuditLogDetailsPageState extends TbContextState { + final labelTextStyle = + TextStyle(color: Color(0xFF757575), fontSize: 14, height: 20 / 14); - final labelTextStyle = TextStyle( - color: Color(0xFF757575), - fontSize: 14, - height: 20 / 14 - ); - - final valueTextStyle = TextStyle( - color: Color(0xFF282828), - fontSize: 14, - height: 20 / 14 - ); + final valueTextStyle = + TextStyle(color: Color(0xFF282828), fontSize: 14, height: 20 / 14); final JsonEncoder encoder = new JsonEncoder.withIndent(' '); @@ -39,51 +30,52 @@ class _AuditLogDetailsPageState extends TbContextState { Widget build(BuildContext context) { return Scaffold( backgroundColor: Colors.white, - appBar: TbAppBar( - tbContext, - title: Column( - crossAxisAlignment: CrossAxisAlignment.start, - children: [ - if (widget.auditLog.entityName != null) - Text(widget.auditLog.entityName!, style: TextStyle( - fontWeight: FontWeight.w500, - fontSize: 16, - height: 20 / 16 - )), - Text('Audit log details', style: TextStyle( - color: Theme.of(context).primaryTextTheme.headline6!.color!.withAlpha((0.38 * 255).ceil()), - fontSize: 12, - fontWeight: FontWeight.normal, - height: 16 / 12 - )) - ] - ) - ), + appBar: TbAppBar(tbContext, + title: + Column(crossAxisAlignment: CrossAxisAlignment.start, children: [ + if (widget.auditLog.entityName != null) + Text(widget.auditLog.entityName!, + style: TextStyle( + fontWeight: FontWeight.w500, + fontSize: 16, + height: 20 / 16)), + Text('Audit log details', + style: TextStyle( + color: Theme.of(context) + .primaryTextTheme + .headline6! + .color! + .withAlpha((0.38 * 255).ceil()), + fontSize: 12, + fontWeight: FontWeight.normal, + height: 16 / 12)) + ])), body: Padding( padding: EdgeInsets.all(16), child: Column( - crossAxisAlignment: CrossAxisAlignment.start, - mainAxisSize: MainAxisSize.max, - children: [ - Text('Entity Type', style: labelTextStyle), - Text(entityTypeTranslations[widget.auditLog.entityId.entityType]!, style: valueTextStyle), - SizedBox(height: 16), - Text('Type', style: labelTextStyle), - Text(actionTypeTranslations[widget.auditLog.actionType]!, style: valueTextStyle), - SizedBox(height: 16), - Flexible( - fit: FlexFit.loose, - child: buildBorderedText('Action data', encoder.convert(widget.auditLog.actionData)) - ), - if (widget.auditLog.actionStatus == ActionStatus.FAILURE) + crossAxisAlignment: CrossAxisAlignment.start, + mainAxisSize: MainAxisSize.max, + children: [ + Text('Entity Type', style: labelTextStyle), + Text(entityTypeTranslations[widget.auditLog.entityId.entityType]!, + style: valueTextStyle), + SizedBox(height: 16), + Text('Type', style: labelTextStyle), + Text(actionTypeTranslations[widget.auditLog.actionType]!, + style: valueTextStyle), SizedBox(height: 16), - if (widget.auditLog.actionStatus == ActionStatus.FAILURE) Flexible( - fit: FlexFit.loose, - child: buildBorderedText('Failure details', widget.auditLog.actionFailureDetails!) - ) - ] - ), + fit: FlexFit.loose, + child: buildBorderedText('Action data', + encoder.convert(widget.auditLog.actionData))), + if (widget.auditLog.actionStatus == ActionStatus.FAILURE) + SizedBox(height: 16), + if (widget.auditLog.actionStatus == ActionStatus.FAILURE) + Flexible( + fit: FlexFit.loose, + child: buildBorderedText('Failure details', + widget.auditLog.actionFailureDetails!)) + ]), ), ); } @@ -96,8 +88,7 @@ class _AuditLogDetailsPageState extends TbContextState { padding: EdgeInsets.fromLTRB(16, 18, 48, 18), margin: EdgeInsets.only(top: 6), decoration: BoxDecoration( - border: Border.all( - color: Color(0xFFDEDEDE), width: 1), + border: Border.all(color: Color(0xFFDEDEDE), width: 1), borderRadius: BorderRadius.circular(4), shape: BoxShape.rectangle, ), @@ -105,10 +96,7 @@ class _AuditLogDetailsPageState extends TbContextState { child: Text( content, style: TextStyle( - color: Color(0xFF282828), - fontSize: 14, - height: 20 / 14 - ), + color: Color(0xFF282828), fontSize: 14, height: 20 / 14), ), ), ), @@ -120,11 +108,11 @@ class _AuditLogDetailsPageState extends TbContextState { color: Colors.white, child: Text( title, - style: TextStyle(color: Color(0xFF757575), fontSize: 12, height: 14 / 12), + style: TextStyle( + color: Color(0xFF757575), fontSize: 12, height: 14 / 12), ), )), ], ); } - } diff --git a/lib/modules/audit_log/audit_logs_base.dart b/lib/modules/audit_log/audit_logs_base.dart index 847f78b..8909944 100644 --- a/lib/modules/audit_log/audit_logs_base.dart +++ b/lib/modules/audit_log/audit_logs_base.dart @@ -1,6 +1,5 @@ import 'package:auto_size_text/auto_size_text.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'; @@ -46,7 +45,6 @@ const Map actionStatusTranslations = { }; mixin AuditLogsBase on EntitiesBase { - @override String get title => 'Audit Logs'; @@ -59,8 +57,7 @@ mixin AuditLogsBase on EntitiesBase { } @override - void onEntityTap(AuditLog auditLog) { - } + void onEntityTap(AuditLog auditLog) {} @override Widget buildEntityListCard(BuildContext context, AuditLog auditLog) { @@ -73,21 +70,18 @@ mixin AuditLogsBase on EntitiesBase { } class AuditLogCard extends TbContextWidget { - final AuditLog auditLog; - AuditLogCard(TbContext tbContext, {required this.auditLog}) : super(tbContext); + AuditLogCard(TbContext tbContext, {required this.auditLog}) + : super(tbContext); @override _AuditLogCardState createState() => _AuditLogCardState(); - } class _AuditLogCardState extends TbContextState { - final entityDateFormat = DateFormat('yyyy-MM-dd'); - @override void initState() { super.initState(); @@ -100,131 +94,141 @@ class _AuditLogCardState extends TbContextState { @override Widget build(BuildContext context) { - return Stack( - children: [ - Positioned.fill( - child: Container( - alignment: Alignment.centerLeft, - child: Container( - width: 4, - decoration: BoxDecoration( - color: widget.auditLog.actionStatus == ActionStatus.SUCCESS ? Color(0xFF008A00) : Color(0xFFFF0000), - borderRadius: BorderRadius.only(topLeft: Radius.circular(4), bottomLeft: Radius.circular(4)) - ), - ) - ) - ), - Row( - mainAxisSize: MainAxisSize.max, - children: [ - SizedBox(width: 4), - Flexible( - fit: FlexFit.tight, - child: Column( - crossAxisAlignment: CrossAxisAlignment.start, + return Stack( + children: [ + Positioned.fill( + child: Container( + alignment: Alignment.centerLeft, + child: Container( + width: 4, + decoration: BoxDecoration( + color: + widget.auditLog.actionStatus == ActionStatus.SUCCESS + ? Color(0xFF008A00) + : Color(0xFFFF0000), + borderRadius: BorderRadius.only( + topLeft: Radius.circular(4), + bottomLeft: Radius.circular(4))), + ))), + Row(mainAxisSize: MainAxisSize.max, children: [ + SizedBox(width: 4), + Flexible( + fit: FlexFit.tight, + child: Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + Row( + mainAxisSize: MainAxisSize.max, + crossAxisAlignment: CrossAxisAlignment.center, children: [ - Row( - mainAxisSize: MainAxisSize.max, - crossAxisAlignment: CrossAxisAlignment.center, - children: [ - SizedBox(width: 16), - Flexible( - fit: FlexFit.tight, - child: Column( - crossAxisAlignment: CrossAxisAlignment.start, + SizedBox(width: 16), + Flexible( + fit: FlexFit.tight, + child: Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + SizedBox(height: 12), + Row( + mainAxisSize: MainAxisSize.max, + crossAxisAlignment: + CrossAxisAlignment.start, children: [ - SizedBox(height: 12), - Row( - mainAxisSize: MainAxisSize.max, - crossAxisAlignment: CrossAxisAlignment.start, - children: [ - Flexible( - fit: FlexFit.tight, - child: AutoSizeText(widget.auditLog.entityName ?? '', - maxLines: 2, - minFontSize: 8, - overflow: TextOverflow.ellipsis, - style: TextStyle( - color: Color(0xFF282828), - fontWeight: FontWeight.w500, - fontSize: 14, - height: 20 / 14) - ) - ), - Text(entityDateFormat.format(DateTime.fromMillisecondsSinceEpoch(widget.auditLog.createdTime!)), - style: TextStyle( - color: Color(0xFFAFAFAF), - fontWeight: FontWeight.normal, - fontSize: 12, - height: 16 / 12) - ) - ] - ), - SizedBox(height: 4), - Row( - crossAxisAlignment: CrossAxisAlignment.start, - children: [ - Flexible( - fit: FlexFit.tight, - child: Text(entityTypeTranslations[widget.auditLog.entityId.entityType]!, - style: TextStyle( - color: Color(0xFFAFAFAF), - fontWeight: FontWeight.normal, - fontSize: 12, - height: 16 / 12) - ) - ), - Text(actionStatusTranslations[widget.auditLog.actionStatus]!, - style: TextStyle( - color: widget.auditLog.actionStatus == ActionStatus.SUCCESS ? Color(0xFF008A00) : Color(0xFFFF0000), - fontWeight: FontWeight.w500, - fontSize: 12, - height: 16 / 12) - ) - ] - ), - SizedBox(height: 12)], - ) - ), - SizedBox(width: 16) - ] - ), - SizedBox(height: 8), - Row( - crossAxisAlignment: CrossAxisAlignment.center, - children: [ - SizedBox(width: 16), - Flexible( - fit: FlexFit.tight, - child: Text(actionTypeTranslations[widget.auditLog.actionType]!, - style: TextStyle( - color: Color(0xFF282828), - fontWeight: FontWeight.normal, - fontSize: 14, - height: 20 / 14) - ) - ), - SizedBox(height: 32), - CircleAvatar( - radius: 16, - backgroundColor: Color(0xffF0F4F9), - child: IconButton(icon: Icon(Icons.code, size: 18), padding: EdgeInsets.all(7.0), onPressed: () => _auditLogDetails(widget.auditLog)) - ), - SizedBox(width: 8) - ], - ), - SizedBox(height: 8) - ] - ) - ) - ] - ) - ], - ); + Flexible( + fit: FlexFit.tight, + child: AutoSizeText( + widget.auditLog.entityName ?? + '', + maxLines: 2, + minFontSize: 8, + overflow: TextOverflow.ellipsis, + style: TextStyle( + color: Color(0xFF282828), + fontWeight: FontWeight.w500, + fontSize: 14, + height: 20 / 14))), + Text( + entityDateFormat.format(DateTime + .fromMillisecondsSinceEpoch( + widget.auditLog + .createdTime!)), + style: TextStyle( + color: Color(0xFFAFAFAF), + fontWeight: FontWeight.normal, + fontSize: 12, + height: 16 / 12)) + ]), + SizedBox(height: 4), + Row( + crossAxisAlignment: + CrossAxisAlignment.start, + children: [ + Flexible( + fit: FlexFit.tight, + child: Text( + entityTypeTranslations[widget + .auditLog + .entityId + .entityType]!, + style: TextStyle( + color: Color(0xFFAFAFAF), + fontWeight: + FontWeight.normal, + fontSize: 12, + height: 16 / 12))), + Text( + actionStatusTranslations[ + widget.auditLog.actionStatus]!, + style: TextStyle( + color: widget.auditLog + .actionStatus == + ActionStatus.SUCCESS + ? Color(0xFF008A00) + : Color(0xFFFF0000), + fontWeight: FontWeight.w500, + fontSize: 12, + height: 16 / 12)) + ]), + SizedBox(height: 12) + ], + )), + SizedBox(width: 16) + ]), + SizedBox(height: 8), + Row( + crossAxisAlignment: CrossAxisAlignment.center, + children: [ + SizedBox(width: 16), + Flexible( + fit: FlexFit.tight, + child: Text( + actionTypeTranslations[ + widget.auditLog.actionType]!, + style: TextStyle( + color: Color(0xFF282828), + fontWeight: FontWeight.normal, + fontSize: 14, + height: 20 / 14))), + SizedBox(height: 32), + CircleAvatar( + radius: 16, + backgroundColor: Color(0xffF0F4F9), + child: IconButton( + icon: Icon(Icons.code, size: 18), + padding: EdgeInsets.all(7.0), + onPressed: () => + _auditLogDetails(widget.auditLog))), + SizedBox(width: 8) + ], + ), + SizedBox(height: 8) + ])) + ]) + ], + ); } _auditLogDetails(AuditLog auditLog) { - tbContext.showFullScreenDialog(new AuditLogDetailsPage(tbContext, auditLog)); + tbContext + .showFullScreenDialog(new AuditLogDetailsPage(tbContext, auditLog)); } - } diff --git a/lib/modules/audit_log/audit_logs_list.dart b/lib/modules/audit_log/audit_logs_list.dart index 346d4ca..c79abf6 100644 --- a/lib/modules/audit_log/audit_logs_list.dart +++ b/lib/modules/audit_log/audit_logs_list.dart @@ -4,8 +4,10 @@ import 'package:thingsboard_app/core/entity/entities_list.dart'; import 'package:thingsboard_app/modules/audit_log/audit_logs_base.dart'; import 'package:thingsboard_client/thingsboard_client.dart'; -class AuditLogsList extends BaseEntitiesWidget with AuditLogsBase, EntitiesListStateBase { - - AuditLogsList(TbContext tbContext, PageKeyController pageKeyController, {searchMode = false}) : super(tbContext, pageKeyController, searchMode: searchMode); - +class AuditLogsList extends BaseEntitiesWidget + with AuditLogsBase, EntitiesListStateBase { + AuditLogsList( + TbContext tbContext, PageKeyController pageKeyController, + {searchMode = false}) + : super(tbContext, pageKeyController, searchMode: searchMode); } diff --git a/lib/modules/audit_log/audit_logs_page.dart b/lib/modules/audit_log/audit_logs_page.dart index 4ebf7c2..e415557 100644 --- a/lib/modules/audit_log/audit_logs_page.dart +++ b/lib/modules/audit_log/audit_logs_page.dart @@ -6,48 +6,41 @@ import 'package:thingsboard_app/modules/audit_log/audit_logs_list.dart'; import 'package:thingsboard_app/widgets/tb_app_bar.dart'; class AuditLogsPage extends TbPageWidget { - final bool searchMode; - AuditLogsPage(TbContext tbContext, {this.searchMode = false}) : super(tbContext); + AuditLogsPage(TbContext tbContext, {this.searchMode = false}) + : super(tbContext); @override _AuditLogsPageState createState() => _AuditLogsPageState(); - } class _AuditLogsPageState extends TbPageState { - - final TimePageLinkController _timePageLinkController = TimePageLinkController(); + final TimePageLinkController _timePageLinkController = + TimePageLinkController(); @override Widget build(BuildContext context) { - var auditLogsList = AuditLogsList(tbContext, _timePageLinkController, searchMode: widget.searchMode); + var auditLogsList = AuditLogsList(tbContext, _timePageLinkController, + searchMode: widget.searchMode); PreferredSizeWidget appBar; if (widget.searchMode) { appBar = TbAppSearchBar( tbContext, - onSearch: (searchText) => _timePageLinkController.onSearchText(searchText), + onSearch: (searchText) => + _timePageLinkController.onSearchText(searchText), ); } else { - appBar = TbAppBar( - tbContext, - title: Text(auditLogsList.title), - actions: [ - IconButton( - icon: Icon( - Icons.search - ), - onPressed: () { - navigateTo('/auditLogs?search=true'); - }, - ) - ]); + appBar = TbAppBar(tbContext, title: Text(auditLogsList.title), actions: [ + IconButton( + icon: Icon(Icons.search), + onPressed: () { + navigateTo('/auditLogs?search=true'); + }, + ) + ]); } - return Scaffold( - appBar: appBar, - body: auditLogsList - ); + return Scaffold(appBar: appBar, body: auditLogsList); } @override @@ -55,5 +48,4 @@ class _AuditLogsPageState extends TbPageState { _timePageLinkController.dispose(); super.dispose(); } - } diff --git a/lib/modules/audit_log/audit_logs_routes.dart b/lib/modules/audit_log/audit_logs_routes.dart index 76f65ea..f753c44 100644 --- a/lib/modules/audit_log/audit_logs_routes.dart +++ b/lib/modules/audit_log/audit_logs_routes.dart @@ -5,8 +5,8 @@ import 'package:thingsboard_app/core/context/tb_context.dart'; import 'package:thingsboard_app/modules/audit_log/audit_logs_page.dart'; class AuditLogsRoutes extends TbRoutes { - - late var auditLogsHandler = Handler(handlerFunc: (BuildContext? context, Map params) { + late var auditLogsHandler = Handler( + handlerFunc: (BuildContext? context, Map params) { var searchMode = params['search']?.first == 'true'; return AuditLogsPage(tbContext, searchMode: searchMode); }); @@ -17,5 +17,4 @@ class AuditLogsRoutes extends TbRoutes { void doRegisterRoutes(router) { router.define("/auditLogs", handler: auditLogsHandler); } - } diff --git a/lib/modules/customer/customer_details_page.dart b/lib/modules/customer/customer_details_page.dart index 8174574..07d0572 100644 --- a/lib/modules/customer/customer_details_page.dart +++ b/lib/modules/customer/customer_details_page.dart @@ -3,13 +3,14 @@ import 'package:thingsboard_app/core/entity/entity_details_page.dart'; import 'package:thingsboard_client/thingsboard_client.dart'; class CustomerDetailsPage extends ContactBasedDetailsPage { - - CustomerDetailsPage(TbContext tbContext, String customerId): - super(tbContext, entityId: customerId, defaultTitle: 'Customer', subTitle: 'Customer details'); + CustomerDetailsPage(TbContext tbContext, String customerId) + : super(tbContext, + entityId: customerId, + defaultTitle: 'Customer', + subTitle: 'Customer details'); @override Future fetchEntity(String customerId) { return tbClient.getCustomerService().getCustomer(customerId); } - } diff --git a/lib/modules/customer/customer_routes.dart b/lib/modules/customer/customer_routes.dart index edd5c0a..25311ec 100644 --- a/lib/modules/customer/customer_routes.dart +++ b/lib/modules/customer/customer_routes.dart @@ -6,13 +6,14 @@ import 'customer_details_page.dart'; import 'customers_page.dart'; class CustomerRoutes extends TbRoutes { - - late var customersHandler = Handler(handlerFunc: (BuildContext? context, Map params) { + late var customersHandler = Handler( + handlerFunc: (BuildContext? context, Map params) { var searchMode = params['search']?.first == 'true'; return CustomersPage(tbContext, searchMode: searchMode); }); - late var customerDetailsHandler = Handler(handlerFunc: (BuildContext? context, Map params) { + late var customerDetailsHandler = Handler( + handlerFunc: (BuildContext? context, Map params) { return CustomerDetailsPage(tbContext, params["id"][0]); }); @@ -23,5 +24,4 @@ class CustomerRoutes extends TbRoutes { router.define("/customers", handler: customersHandler); router.define("/customer/:id", handler: customerDetailsHandler); } - } diff --git a/lib/modules/customer/customers_base.dart b/lib/modules/customer/customers_base.dart index e504264..3369920 100644 --- a/lib/modules/customer/customers_base.dart +++ b/lib/modules/customer/customers_base.dart @@ -1,8 +1,7 @@ import 'package:thingsboard_app/core/entity/entities_base.dart'; import 'package:thingsboard_client/thingsboard_client.dart'; -mixin CustomersBase on EntitiesBase { - +mixin CustomersBase on EntitiesBase { @override String get title => 'Customers'; @@ -18,5 +17,4 @@ mixin CustomersBase on EntitiesBase { void onEntityTap(Customer customer) { navigateTo('/customer/${customer.id!.id}'); } - } diff --git a/lib/modules/customer/customers_list.dart b/lib/modules/customer/customers_list.dart index 151d4b1..836a420 100644 --- a/lib/modules/customer/customers_list.dart +++ b/lib/modules/customer/customers_list.dart @@ -5,8 +5,10 @@ import 'package:thingsboard_client/thingsboard_client.dart'; import 'customers_base.dart'; -class CustomersList extends BaseEntitiesWidget with CustomersBase, ContactBasedBase, EntitiesListStateBase { - - CustomersList(TbContext tbContext, PageKeyController pageKeyController, {searchMode = false}) : super(tbContext, pageKeyController, searchMode: searchMode); - +class CustomersList extends BaseEntitiesWidget + with CustomersBase, ContactBasedBase, EntitiesListStateBase { + CustomersList( + TbContext tbContext, PageKeyController pageKeyController, + {searchMode = false}) + : super(tbContext, pageKeyController, searchMode: searchMode); } diff --git a/lib/modules/customer/customers_page.dart b/lib/modules/customer/customers_page.dart index 6deda21..15e0ebd 100644 --- a/lib/modules/customer/customers_page.dart +++ b/lib/modules/customer/customers_page.dart @@ -6,23 +6,22 @@ import 'package:thingsboard_app/modules/customer/customers_list.dart'; import 'package:thingsboard_app/widgets/tb_app_bar.dart'; class CustomersPage extends TbPageWidget { - final bool searchMode; - CustomersPage(TbContext tbContext, {this.searchMode = false}) : super(tbContext); + CustomersPage(TbContext tbContext, {this.searchMode = false}) + : super(tbContext); @override _CustomersPageState createState() => _CustomersPageState(); - } class _CustomersPageState extends TbPageState { - final PageLinkController _pageLinkController = PageLinkController(); @override Widget build(BuildContext context) { - var customersList = CustomersList(tbContext, _pageLinkController, searchMode: widget.searchMode); + var customersList = CustomersList(tbContext, _pageLinkController, + searchMode: widget.searchMode); PreferredSizeWidget appBar; if (widget.searchMode) { appBar = TbAppSearchBar( @@ -30,24 +29,16 @@ class _CustomersPageState extends TbPageState { onSearch: (searchText) => _pageLinkController.onSearchText(searchText), ); } else { - appBar = TbAppBar( - tbContext, - title: Text(customersList.title), - actions: [ - IconButton( - icon: Icon( - Icons.search - ), - onPressed: () { - navigateTo('/customers?search=true'); - }, - ) - ]); + appBar = TbAppBar(tbContext, title: Text(customersList.title), actions: [ + IconButton( + icon: Icon(Icons.search), + onPressed: () { + navigateTo('/customers?search=true'); + }, + ) + ]); } - return Scaffold( - appBar: appBar, - body: customersList - ); + return Scaffold(appBar: appBar, body: customersList); } @override @@ -55,5 +46,4 @@ class _CustomersPageState extends TbPageState { _pageLinkController.dispose(); super.dispose(); } - } diff --git a/lib/modules/dashboard/dashboard.dart b/lib/modules/dashboard/dashboard.dart index 3c0bcaf..e205381 100644 --- a/lib/modules/dashboard/dashboard.dart +++ b/lib/modules/dashboard/dashboard.dart @@ -3,7 +3,6 @@ import 'dart:convert'; import 'dart:io'; import 'package:flutter/material.dart'; -import 'package:flutter/widgets.dart'; import 'package:flutter_inappwebview/flutter_inappwebview.dart'; import 'package:thingsboard_app/constants/app_constants.dart'; import 'package:thingsboard_app/core/context/tb_context.dart'; @@ -11,10 +10,9 @@ import 'package:thingsboard_app/core/context/tb_context_widget.dart'; import 'package:thingsboard_app/widgets/tb_progress_indicator.dart'; import 'package:thingsboard_app/widgets/two_value_listenable_builder.dart'; import 'package:universal_platform/universal_platform.dart'; -import 'package:url_launcher/url_launcher.dart'; +import 'package:url_launcher/url_launcher_string.dart'; class DashboardController { - final ValueNotifier canGoBack = ValueNotifier(false); final ValueNotifier hasRightLayout = ValueNotifier(false); final ValueNotifier rightLayoutOpened = ValueNotifier(false); @@ -22,8 +20,10 @@ class DashboardController { final _DashboardState dashboardState; DashboardController(this.dashboardState); - Future openDashboard(String dashboardId, {String? state, bool? hideToolbar, bool fullscreen = false}) async { - return await dashboardState._openDashboard(dashboardId, state: state, hideToolbar: hideToolbar, fullscreen: fullscreen); + Future openDashboard(String dashboardId, + {String? state, bool? hideToolbar, bool fullscreen = false}) async { + return await dashboardState._openDashboard(dashboardId, + state: state, hideToolbar: hideToolbar, fullscreen: fullscreen); } Future goBack() async { @@ -59,22 +59,26 @@ class DashboardController { hasRightLayout.dispose(); rightLayoutOpened.dispose(); } - } typedef DashboardTitleCallback = void Function(String title); -typedef DashboardControllerCallback = void Function(DashboardController controller); +typedef DashboardControllerCallback = void Function( + DashboardController controller); class Dashboard extends TbContextWidget { - final bool? _home; final bool _activeByDefault; final DashboardTitleCallback? _titleCallback; final DashboardControllerCallback? _controllerCallback; - Dashboard(TbContext tbContext, {Key? key, bool? home, bool activeByDefault = true, DashboardTitleCallback? titleCallback, DashboardControllerCallback? controllerCallback}): - this._home = home, + Dashboard(TbContext tbContext, + {Key? key, + bool? home, + bool activeByDefault = true, + DashboardTitleCallback? titleCallback, + DashboardControllerCallback? controllerCallback}) + : this._home = home, this._activeByDefault = activeByDefault, this._titleCallback = titleCallback, this._controllerCallback = controllerCallback, @@ -82,12 +86,11 @@ class Dashboard extends TbContextWidget { @override _DashboardState createState() => _DashboardState(); - } class _DashboardState extends TbContextState { - - final Completer _controller = Completer(); + final Completer _controller = + Completer(); bool webViewLoading = true; final ValueNotifier dashboardLoading = ValueNotifier(true); @@ -98,8 +101,6 @@ class _DashboardState extends TbContextState { late final DashboardController _dashboardController; - bool _fullscreen = false; - InAppWebViewGroupOptions options = InAppWebViewGroupOptions( crossPlatform: InAppWebViewOptions( useShouldOverrideUrlLoading: true, @@ -110,13 +111,10 @@ class _DashboardState extends TbContextState { // useOnDownloadStart: true ), android: AndroidInAppWebViewOptions( - useHybridComposition: true, - thirdPartyCookiesEnabled: true - ), + useHybridComposition: true, thirdPartyCookiesEnabled: true), ios: IOSInAppWebViewOptions( - allowsInlineMediaPlayback: true, - allowsBackForwardNavigationGestures: false - )); + allowsInlineMediaPlayback: true, + allowsBackForwardNavigationGestures: false)); late Uri _initialUrl; @@ -137,7 +135,8 @@ class _DashboardState extends TbContextState { void _onAuthenticated() async { if (tbContext.isAuthenticated) { if (!readyState.value) { - _initialUrl = Uri.parse(ThingsboardAppConstants.thingsBoardApiEndpoint + '?accessToken=${tbClient.getJwtToken()!}&refreshToken=${tbClient.getRefreshToken()!}'); + _initialUrl = Uri.parse(ThingsboardAppConstants.thingsBoardApiEndpoint + + '?accessToken=${tbClient.getJwtToken()!}&refreshToken=${tbClient.getRefreshToken()!}'); readyState.value = true; } else { var windowMessage = { @@ -193,8 +192,8 @@ class _DashboardState extends TbContextState { } } - Future _openDashboard(String dashboardId, {String? state, bool? hideToolbar, bool fullscreen = false}) async { - _fullscreen = fullscreen; + Future _openDashboard(String dashboardId, + {String? state, bool? hideToolbar, bool fullscreen = false}) async { dashboardLoading.value = true; InAppWebViewController? controller; if (!UniversalPlatform.isWeb) { @@ -202,9 +201,7 @@ class _DashboardState extends TbContextState { } var windowMessage = { 'type': 'openDashboardMessage', - 'data': { - 'dashboardId': dashboardId - } + 'data': {'dashboardId': dashboardId} }; if (state != null) { windowMessage['data']['state'] = state; @@ -217,18 +214,17 @@ class _DashboardState extends TbContextState { } var webMessage = WebMessage(data: jsonEncode(windowMessage)); if (!UniversalPlatform.isWeb) { - await controller!.postWebMessage( - message: webMessage, targetOrigin: Uri.parse('*')); + await controller! + .postWebMessage(message: webMessage, targetOrigin: Uri.parse('*')); } } Future _toggleRightLayout() async { var controller = await _controller.future; - var windowMessage = { - 'type': 'toggleDashboardLayout' - }; + var windowMessage = {'type': 'toggleDashboardLayout'}; var webMessage = WebMessage(data: jsonEncode(windowMessage)); - await controller.postWebMessage(message: webMessage, targetOrigin: Uri.parse('*')); + await controller.postWebMessage( + message: webMessage, targetOrigin: Uri.parse('*')); } Future tryLocalNavigation(String? path) async { @@ -244,7 +240,8 @@ class _DashboardState extends TbContextState { 'customers', 'auditLogs' ].contains(parts[0])) { - if ((parts[0] == 'dashboard' || parts[0] == 'dashboards') && parts.length > 1) { + if ((parts[0] == 'dashboard' || parts[0] == 'dashboards') && + parts.length > 1) { var dashboardId = parts[1]; await navigateToDashboard(dashboardId); } else if (parts[0] != 'dashboard') { @@ -261,147 +258,176 @@ class _DashboardState extends TbContextState { @override Widget build(BuildContext context) { return WillPopScope( - onWillPop: () async { - if (widget._home == true && !tbContext.isHomePage()) { - return true; - } - if (readyState.value) { - return await _goBack(); + onWillPop: () async { + if (widget._home == true && !tbContext.isHomePage()) { + return true; + } + if (readyState.value) { + return await _goBack(); + } else { + return true; + } + }, + child: ValueListenableBuilder( + valueListenable: readyState, + builder: (BuildContext context, bool ready, child) { + if (!ready) { + return SizedBox.shrink(); } else { - return true; - } - }, - child: - ValueListenableBuilder( - valueListenable: readyState, - builder: (BuildContext context, bool ready, child) { - if (!ready) { - return SizedBox.shrink(); - } else { - return Stack( - children: [ - UniversalPlatform.isWeb ? Center(child: Text('Not implemented!')) : - InAppWebView( - key: webViewKey, - initialUrlRequest: URLRequest(url: _initialUrl), - initialOptions: options, - onWebViewCreated: (webViewController) { - log.debug("onWebViewCreated"); - webViewController.addJavaScriptHandler(handlerName: "tbMobileDashboardLoadedHandler", callback: (args) async { - bool hasRightLayout = args[0]; - bool rightLayoutOpened = args[1]; - log.debug("Invoked tbMobileDashboardLoadedHandler: hasRightLayout: $hasRightLayout, rightLayoutOpened: $rightLayoutOpened"); - _dashboardController.onHasRightLayout(hasRightLayout); - _dashboardController.onRightLayoutOpened(rightLayoutOpened); - dashboardLoading.value = false; - }); - webViewController.addJavaScriptHandler(handlerName: "tbMobileDashboardLayoutHandler", callback: (args) async { - bool rightLayoutOpened = args[0]; - log.debug("Invoked tbMobileDashboardLayoutHandler: rightLayoutOpened: $rightLayoutOpened"); - _dashboardController.onRightLayoutOpened(rightLayoutOpened); - }); - webViewController.addJavaScriptHandler(handlerName: "tbMobileDashboardStateNameHandler", callback: (args) async { - log.debug("Invoked tbMobileDashboardStateNameHandler: $args"); - if (args.isNotEmpty && args[0] is String) { - if (widget._titleCallback != null) { - widget._titleCallback!(args[0]); - } - } - }); - webViewController.addJavaScriptHandler(handlerName: "tbMobileNavigationHandler", callback: (args) async { - log.debug("Invoked tbMobileNavigationHandler: $args"); - if (args.length > 0) { - String? path = args[0]; - Map? params; - if (args.length > 1) { - params = args[1]; - } - log.debug("path: $path"); - log.debug("params: $params"); - tryLocalNavigation(path); - } - }); - webViewController.addJavaScriptHandler(handlerName: "tbMobileHandler", callback: (args) async { - log.debug("Invoked tbMobileHandler: $args"); - return await widgetActionHandler.handleWidgetMobileAction(args, webViewController); - }); - }, - shouldOverrideUrlLoading: (controller, navigationAction) async { - var uri = navigationAction.request.url!; - var uriString = uri.toString(); - log.debug('shouldOverrideUrlLoading $uriString'); - if (Platform.isAndroid || Platform.isIOS && navigationAction.iosWKNavigationType == IOSWKNavigationType.LINK_ACTIVATED) { - if (uriString.startsWith(ThingsboardAppConstants.thingsBoardApiEndpoint)) { - var target = uriString.substring(ThingsboardAppConstants.thingsBoardApiEndpoint.length); - if (!target.startsWith("?accessToken")) { - if (target.startsWith("/")) { - target = target.substring(1); + return Stack(children: [ + UniversalPlatform.isWeb + ? Center(child: Text('Not implemented!')) + : InAppWebView( + key: webViewKey, + initialUrlRequest: URLRequest(url: _initialUrl), + initialOptions: options, + onWebViewCreated: (webViewController) { + log.debug("onWebViewCreated"); + webViewController.addJavaScriptHandler( + handlerName: "tbMobileDashboardLoadedHandler", + callback: (args) async { + bool hasRightLayout = args[0]; + bool rightLayoutOpened = args[1]; + log.debug( + "Invoked tbMobileDashboardLoadedHandler: hasRightLayout: $hasRightLayout, rightLayoutOpened: $rightLayoutOpened"); + _dashboardController + .onHasRightLayout(hasRightLayout); + _dashboardController + .onRightLayoutOpened(rightLayoutOpened); + dashboardLoading.value = false; + }); + webViewController.addJavaScriptHandler( + handlerName: "tbMobileDashboardLayoutHandler", + callback: (args) async { + bool rightLayoutOpened = args[0]; + log.debug( + "Invoked tbMobileDashboardLayoutHandler: rightLayoutOpened: $rightLayoutOpened"); + _dashboardController + .onRightLayoutOpened(rightLayoutOpened); + }); + webViewController.addJavaScriptHandler( + handlerName: + "tbMobileDashboardStateNameHandler", + callback: (args) async { + log.debug( + "Invoked tbMobileDashboardStateNameHandler: $args"); + if (args.isNotEmpty && args[0] is String) { + if (widget._titleCallback != null) { + widget._titleCallback!(args[0]); } - await tryLocalNavigation(target); - return NavigationActionPolicy.CANCEL; } - } else if (await canLaunch(uriString)) { - await launch( - uriString, - ); + }); + webViewController.addJavaScriptHandler( + handlerName: "tbMobileNavigationHandler", + callback: (args) async { + log.debug( + "Invoked tbMobileNavigationHandler: $args"); + if (args.length > 0) { + String? path = args[0]; + Map? params; + if (args.length > 1) { + params = args[1]; + } + log.debug("path: $path"); + log.debug("params: $params"); + tryLocalNavigation(path); + } + }); + webViewController.addJavaScriptHandler( + handlerName: "tbMobileHandler", + callback: (args) async { + log.debug("Invoked tbMobileHandler: $args"); + return await widgetActionHandler + .handleWidgetMobileAction( + args, webViewController); + }); + }, + shouldOverrideUrlLoading: + (controller, navigationAction) async { + var uri = navigationAction.request.url!; + var uriString = uri.toString(); + log.debug('shouldOverrideUrlLoading $uriString'); + if (Platform.isAndroid || + Platform.isIOS && + navigationAction.iosWKNavigationType == + IOSWKNavigationType.LINK_ACTIVATED) { + if (uriString.startsWith(ThingsboardAppConstants + .thingsBoardApiEndpoint)) { + var target = uriString.substring( + ThingsboardAppConstants + .thingsBoardApiEndpoint.length); + if (!target.startsWith("?accessToken")) { + if (target.startsWith("/")) { + target = target.substring(1); + } + await tryLocalNavigation(target); return NavigationActionPolicy.CANCEL; } + } else if (await canLaunchUrlString(uriString)) { + await launchUrlString( + uriString, + ); + return NavigationActionPolicy.CANCEL; } - return Platform.isIOS ? NavigationActionPolicy.ALLOW : NavigationActionPolicy.CANCEL; - }, - onUpdateVisitedHistory: (controller, url, androidIsReload) async { - log.debug('onUpdateVisitedHistory: $url'); - _dashboardController.onHistoryUpdated(controller.canGoBack()); - }, - onConsoleMessage: (controller, consoleMessage) { - log.debug('[JavaScript console] ${consoleMessage.messageLevel}: ${consoleMessage.message}'); - }, - onLoadStart: (controller, url) async { - log.debug('onLoadStart: $url'); - }, - onLoadStop: (controller, url) async { - log.debug('onLoadStop: $url'); - if (webViewLoading) { - webViewLoading = false; - _controller.complete(controller); - } - }, - androidOnPermissionRequest: (controller, origin, resources) async { - log.debug('androidOnPermissionRequest origin: $origin, resources: $resources'); - return PermissionRequestResponse( - resources: resources, - action: PermissionRequestResponseAction.GRANT); - }, - ), - if (!UniversalPlatform.isWeb) - TwoValueListenableBuilder( - firstValueListenable: dashboardLoading, - secondValueListenable: dashboardActive, - builder: (BuildContext context, bool loading, bool active, child) { - if (!loading && active) { - return SizedBox.shrink(); - } else { - var data = MediaQueryData.fromWindow(WidgetsBinding.instance!.window); - var bottomPadding = data.padding.top; - if (widget._home != true) { - bottomPadding += kToolbarHeight; - } - return Container( - padding: EdgeInsets.only(bottom: bottomPadding), - alignment: Alignment.center, - color: Colors.white, - child: TbProgressIndicator( - size: 50.0 - ), - ); - } - } - ) - ] - ); - } - } - ) - ); + } + return Platform.isIOS + ? NavigationActionPolicy.ALLOW + : NavigationActionPolicy.CANCEL; + }, + onUpdateVisitedHistory: + (controller, url, androidIsReload) async { + log.debug('onUpdateVisitedHistory: $url'); + _dashboardController + .onHistoryUpdated(controller.canGoBack()); + }, + onConsoleMessage: (controller, consoleMessage) { + log.debug( + '[JavaScript console] ${consoleMessage.messageLevel}: ${consoleMessage.message}'); + }, + onLoadStart: (controller, url) async { + log.debug('onLoadStart: $url'); + }, + onLoadStop: (controller, url) async { + log.debug('onLoadStop: $url'); + if (webViewLoading) { + webViewLoading = false; + _controller.complete(controller); + } + }, + androidOnPermissionRequest: + (controller, origin, resources) async { + log.debug( + 'androidOnPermissionRequest origin: $origin, resources: $resources'); + return PermissionRequestResponse( + resources: resources, + action: PermissionRequestResponseAction.GRANT); + }, + ), + if (!UniversalPlatform.isWeb) + TwoValueListenableBuilder( + firstValueListenable: dashboardLoading, + secondValueListenable: dashboardActive, + builder: (BuildContext context, bool loading, + bool active, child) { + if (!loading && active) { + return SizedBox.shrink(); + } else { + var data = MediaQueryData.fromWindow( + WidgetsBinding.instance.window); + var bottomPadding = data.padding.top; + if (widget._home != true) { + bottomPadding += kToolbarHeight; + } + return Container( + padding: EdgeInsets.only(bottom: bottomPadding), + alignment: Alignment.center, + color: Colors.white, + child: TbProgressIndicator(size: 50.0), + ); + } + }) + ]); + } + })); } } diff --git a/lib/modules/dashboard/dashboard_page.dart b/lib/modules/dashboard/dashboard_page.dart index 13cc96d..b370736 100644 --- a/lib/modules/dashboard/dashboard_page.dart +++ b/lib/modules/dashboard/dashboard_page.dart @@ -1,31 +1,31 @@ import 'package:flutter/material.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/dashboard/dashboard.dart'; import 'package:thingsboard_app/widgets/tb_app_bar.dart'; class DashboardPage extends TbPageWidget { - final String? _dashboardTitle; - final String? _dashboardId; - final String? _state; - final bool? _fullscreen; + // final String? _dashboardId; + // final String? _state; + // final bool? _fullscreen; - DashboardPage(TbContext tbContext, {String? dashboardId, bool? fullscreen, String? dashboardTitle, String? state}): - _dashboardId = dashboardId, - _fullscreen = fullscreen, + DashboardPage(TbContext tbContext, + {String? dashboardId, + bool? fullscreen, + String? dashboardTitle, + String? state}) + : + // _dashboardId = dashboardId, + // _fullscreen = fullscreen, _dashboardTitle = dashboardTitle, - _state = state, + // _state = state, super(tbContext); @override _DashboardPageState createState() => _DashboardPageState(); - } class _DashboardPageState extends TbPageState { - late ValueNotifier dashboardTitleValue; @override @@ -37,27 +37,26 @@ class _DashboardPageState extends TbPageState { @override Widget build(BuildContext context) { return Scaffold( - appBar: TbAppBar( - tbContext, - showLoadingIndicator: false, - elevation: 0, - title: ValueListenableBuilder( - valueListenable: dashboardTitleValue, - builder: (context, title, widget) { - return FittedBox( - fit: BoxFit.fitWidth, - alignment: Alignment.centerLeft, - child: Text(title) - ); - }, + appBar: TbAppBar( + tbContext, + showLoadingIndicator: false, + elevation: 0, + title: ValueListenableBuilder( + valueListenable: dashboardTitleValue, + builder: (context, title, widget) { + return FittedBox( + fit: BoxFit.fitWidth, + alignment: Alignment.centerLeft, + child: Text(title)); + }, + ), ), - ), - body: Text('Deprecated') //Dashboard(tbContext, dashboardId: widget._dashboardId, state: widget._state, - //fullscreen: widget._fullscreen, titleCallback: (title) { + body: Text( + 'Deprecated') //Dashboard(tbContext, dashboardId: widget._dashboardId, state: widget._state, + //fullscreen: widget._fullscreen, titleCallback: (title) { //dashboardTitleValue.value = title; - //} - //), - ); + //} + //), + ); } - } diff --git a/lib/modules/dashboard/dashboard_routes.dart b/lib/modules/dashboard/dashboard_routes.dart index 2f6d655..71e5a9b 100644 --- a/lib/modules/dashboard/dashboard_routes.dart +++ b/lib/modules/dashboard/dashboard_routes.dart @@ -8,20 +8,25 @@ import 'package:thingsboard_app/modules/dashboard/fullscreen_dashboard_page.dart import 'dashboard_page.dart'; class DashboardRoutes extends TbRoutes { - - late var dashboardsHandler = Handler(handlerFunc: (BuildContext? context, Map params) { + late var dashboardsHandler = Handler( + handlerFunc: (BuildContext? context, Map params) { return DashboardsPage(tbContext); }); - late var dashboardDetailsHandler = Handler(handlerFunc: (BuildContext? context, Map> params) { + late var dashboardDetailsHandler = Handler( + handlerFunc: (BuildContext? context, Map> params) { var fullscreen = params['fullscreen']?.first == 'true'; var dashboardTitle = params['title']?.first; var state = params['state']?.first; - return DashboardPage(tbContext, dashboardId: params["id"]![0], fullscreen: fullscreen, - dashboardTitle: dashboardTitle, state: state); + return DashboardPage(tbContext, + dashboardId: params["id"]![0], + fullscreen: fullscreen, + dashboardTitle: dashboardTitle, + state: state); }); - late var fullscreenDashboardHandler = Handler(handlerFunc: (BuildContext? context, Map params) { + late var fullscreenDashboardHandler = Handler( + handlerFunc: (BuildContext? context, Map params) { return FullscreenDashboardPage(tbContext, params["id"]![0]); }); @@ -31,7 +36,7 @@ class DashboardRoutes extends TbRoutes { void doRegisterRoutes(router) { router.define("/dashboards", handler: dashboardsHandler); router.define("/dashboard/:id", handler: dashboardDetailsHandler); - router.define("/fullscreenDashboard/:id", handler: fullscreenDashboardHandler); + router.define("/fullscreenDashboard/:id", + handler: fullscreenDashboardHandler); } - } diff --git a/lib/modules/dashboard/dashboards_base.dart b/lib/modules/dashboard/dashboards_base.dart index 6ebb550..bdee29a 100644 --- a/lib/modules/dashboard/dashboards_base.dart +++ b/lib/modules/dashboard/dashboards_base.dart @@ -1,6 +1,5 @@ import 'package:auto_size_text/auto_size_text.dart'; import 'package:flutter/material.dart'; -import 'package:flutter/widgets.dart'; import 'package:flutter_svg/flutter_svg.dart'; import 'package:thingsboard_app/constants/assets_path.dart'; import 'package:thingsboard_app/core/context/tb_context.dart'; @@ -10,7 +9,6 @@ import 'package:thingsboard_app/utils/utils.dart'; import 'package:thingsboard_client/thingsboard_client.dart'; mixin DashboardsBase on EntitiesBase { - @override String get title => 'Dashboards'; @@ -20,9 +18,13 @@ mixin DashboardsBase on EntitiesBase { @override Future> fetchEntities(PageLink pageLink) { if (tbClient.isTenantAdmin()) { - return tbClient.getDashboardService().getTenantDashboards(pageLink, mobile: true); + return tbClient + .getDashboardService() + .getTenantDashboards(pageLink, mobile: true); } else { - return tbClient.getDashboardService().getCustomerDashboards(tbClient.getAuthUser()!.customerId, pageLink, mobile: true); + return tbClient.getDashboardService().getCustomerDashboards( + tbClient.getAuthUser()!.customerId!, pageLink, + mobile: true); } } @@ -39,34 +41,37 @@ mixin DashboardsBase on EntitiesBase { } @override - Widget buildEntityListWidgetCard(BuildContext context, DashboardInfo dashboard) { + Widget buildEntityListWidgetCard( + BuildContext context, DashboardInfo dashboard) { return _buildEntityListCard(context, dashboard, true); } @override - EntityCardSettings entityGridCardSettings(DashboardInfo dashboard) => EntityCardSettings(dropShadow: true); //dashboard.image != null); + EntityCardSettings entityGridCardSettings(DashboardInfo dashboard) => + EntityCardSettings(dropShadow: true); //dashboard.image != null); @override Widget buildEntityGridCard(BuildContext context, DashboardInfo dashboard) { return DashboardGridCard(tbContext, dashboard: dashboard); } - Widget _buildEntityListCard(BuildContext context, DashboardInfo dashboard, bool listWidgetCard) { + Widget _buildEntityListCard( + BuildContext context, DashboardInfo dashboard, bool listWidgetCard) { return Row( mainAxisSize: listWidgetCard ? MainAxisSize.min : MainAxisSize.max, children: [ Flexible( fit: listWidgetCard ? FlexFit.loose : FlexFit.tight, - child: - Container( - padding: EdgeInsets.symmetric(vertical: listWidgetCard ? 9 : 10, horizontal: 16), + child: Container( + padding: EdgeInsets.symmetric( + vertical: listWidgetCard ? 9 : 10, horizontal: 16), child: Row( - mainAxisSize: listWidgetCard ? MainAxisSize.min : MainAxisSize.max, + mainAxisSize: + listWidgetCard ? MainAxisSize.min : MainAxisSize.max, children: [ Flexible( fit: listWidgetCard ? FlexFit.loose : FlexFit.tight, - child: - Column( + child: Column( crossAxisAlignment: CrossAxisAlignment.start, children: [ FittedBox( @@ -77,37 +82,35 @@ mixin DashboardsBase on EntitiesBase { color: Color(0xFF282828), fontSize: 14, fontWeight: FontWeight.w500, - height: 1.7 - )) - ), + height: 1.7))), Text('${_dashboardDetailsText(dashboard)}', style: TextStyle( color: Color(0xFFAFAFAF), fontSize: 12, fontWeight: FontWeight.normal, - height: 1.33 - )) + height: 1.33)) ], - ) - ), - (!listWidgetCard ? Column( - mainAxisAlignment: MainAxisAlignment.end, - children: [ - Text(entityDateFormat.format(DateTime.fromMillisecondsSinceEpoch(dashboard.createdTime!)), - style: TextStyle( - color: Color(0xFFAFAFAF), - fontSize: 12, - fontWeight: FontWeight.normal, - height: 1.33 - )) - ], - ) : Container()) + )), + (!listWidgetCard + ? Column( + mainAxisAlignment: MainAxisAlignment.end, + children: [ + Text( + entityDateFormat.format( + DateTime.fromMillisecondsSinceEpoch( + dashboard.createdTime!)), + style: TextStyle( + color: Color(0xFFAFAFAF), + fontSize: 12, + fontWeight: FontWeight.normal, + height: 1.33)) + ], + ) + : Container()) ], ), - ) - ) - ] - ); + )) + ]); } String _dashboardDetailsText(DashboardInfo dashboard) { @@ -124,23 +127,20 @@ mixin DashboardsBase on EntitiesBase { bool _isPublicDashboard(DashboardInfo dashboard) { return dashboard.assignedCustomers.any((element) => element.isPublic); } - } class DashboardGridCard extends TbContextWidget { - final DashboardInfo dashboard; - DashboardGridCard(TbContext tbContext, {required this.dashboard}) : super(tbContext); + DashboardGridCard(TbContext tbContext, {required this.dashboard}) + : super(tbContext); @override _DashboardGridCardState createState() => _DashboardGridCardState(); - } class _DashboardGridCardState extends TbContextState { - - _DashboardGridCardState(): super(); + _DashboardGridCardState() : super(); @override void initState() { @@ -164,48 +164,37 @@ class _DashboardGridCardState extends TbContextState { colorBlendMode: BlendMode.overlay, semanticsLabel: 'Dashboard'); } - return - ClipRRect( - borderRadius: BorderRadius.circular(4), - child: Column( - children: [ - Expanded( - child: Stack ( - children: [ - SizedBox.expand( - child: FittedBox( - clipBehavior: Clip.hardEdge, - fit: BoxFit.cover, - child: image - ) - ) - ] - ) - ), - Divider(height: 1), - Container( - height: 44, - child: Padding( - padding: EdgeInsets.symmetric(horizontal: 6), - child: - Center( - child: AutoSizeText(widget.dashboard.title, - textAlign: TextAlign.center, - maxLines: 1, - minFontSize: 12, - overflow: TextOverflow.ellipsis, - style: TextStyle( - fontWeight: FontWeight.w500, - fontSize: 14, - height: 20 / 14 - ), - ) - ) - ), - ) - ], - ) - ); + return ClipRRect( + borderRadius: BorderRadius.circular(4), + child: Column( + children: [ + Expanded( + child: Stack(children: [ + SizedBox.expand( + child: FittedBox( + clipBehavior: Clip.hardEdge, + fit: BoxFit.cover, + child: image)) + ])), + Divider(height: 1), + Container( + height: 44, + child: Padding( + padding: EdgeInsets.symmetric(horizontal: 6), + child: Center( + child: AutoSizeText( + widget.dashboard.title, + textAlign: TextAlign.center, + maxLines: 1, + minFontSize: 12, + overflow: TextOverflow.ellipsis, + style: TextStyle( + fontWeight: FontWeight.w500, + fontSize: 14, + height: 20 / 14), + ))), + ) + ], + )); } - } diff --git a/lib/modules/dashboard/dashboards_grid.dart b/lib/modules/dashboard/dashboards_grid.dart index deb9235..fa255d6 100644 --- a/lib/modules/dashboard/dashboards_grid.dart +++ b/lib/modules/dashboard/dashboards_grid.dart @@ -8,16 +8,13 @@ import 'package:thingsboard_client/thingsboard_client.dart'; import 'dashboards_base.dart'; class DashboardsGridWidget extends TbContextWidget { - DashboardsGridWidget(TbContext tbContext) : super(tbContext); @override _DashboardsGridWidgetState createState() => _DashboardsGridWidgetState(); - } class _DashboardsGridWidgetState extends TbContextState { - final PageLinkController _pageLinkController = PageLinkController(); @override @@ -30,13 +27,11 @@ class _DashboardsGridWidgetState extends TbContextState { _pageLinkController.dispose(); super.dispose(); } - } - -class DashboardsGrid extends BaseEntitiesWidget with DashboardsBase, EntitiesGridStateBase { - - DashboardsGrid(TbContext tbContext, PageKeyController pageKeyController) : super(tbContext, pageKeyController); - +class DashboardsGrid extends BaseEntitiesWidget + with DashboardsBase, EntitiesGridStateBase { + DashboardsGrid( + TbContext tbContext, PageKeyController pageKeyController) + : super(tbContext, pageKeyController); } - diff --git a/lib/modules/dashboard/dashboards_list.dart b/lib/modules/dashboard/dashboards_list.dart index 050a603..159ff0c 100644 --- a/lib/modules/dashboard/dashboards_list.dart +++ b/lib/modules/dashboard/dashboards_list.dart @@ -5,9 +5,9 @@ import 'package:thingsboard_client/thingsboard_client.dart'; import 'dashboards_base.dart'; -class DashboardsList extends BaseEntitiesWidget with DashboardsBase, EntitiesListStateBase { - - DashboardsList(TbContext tbContext, PageKeyController pageKeyController) : super(tbContext, pageKeyController); - +class DashboardsList extends BaseEntitiesWidget + with DashboardsBase, EntitiesListStateBase { + DashboardsList( + TbContext tbContext, PageKeyController pageKeyController) + : super(tbContext, pageKeyController); } - diff --git a/lib/modules/dashboard/dashboards_list_widget.dart b/lib/modules/dashboard/dashboards_list_widget.dart index cb43962..a62f59f 100644 --- a/lib/modules/dashboard/dashboards_list_widget.dart +++ b/lib/modules/dashboard/dashboards_list_widget.dart @@ -3,14 +3,14 @@ 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 with DashboardsBase { - - DashboardsListWidget(TbContext tbContext, {EntitiesListWidgetController? controller}): super(tbContext, controller: controller); +class DashboardsListWidget extends EntitiesListPageLinkWidget + with DashboardsBase { + DashboardsListWidget(TbContext tbContext, + {EntitiesListWidgetController? controller}) + : super(tbContext, controller: controller); @override void onViewAll() { navigateTo('/dashboards'); } - } - diff --git a/lib/modules/dashboard/dashboards_page.dart b/lib/modules/dashboard/dashboards_page.dart index 0a7d6fc..9a1a5e6 100644 --- a/lib/modules/dashboard/dashboards_page.dart +++ b/lib/modules/dashboard/dashboards_page.dart @@ -6,30 +6,22 @@ import 'package:thingsboard_app/widgets/tb_app_bar.dart'; import 'dashboards_grid.dart'; class DashboardsPage extends TbPageWidget { - DashboardsPage(TbContext tbContext) : super(tbContext); @override _DashboardsPageState createState() => _DashboardsPageState(); - } class _DashboardsPageState extends TbPageState { - @override Widget build(BuildContext context) { return Scaffold( - appBar: TbAppBar( - tbContext, - title: Text('Dashboards') - ), - body: DashboardsGridWidget(tbContext) - ); + appBar: TbAppBar(tbContext, title: Text('Dashboards')), + body: DashboardsGridWidget(tbContext)); } @override void dispose() { super.dispose(); } - } diff --git a/lib/modules/dashboard/fullscreen_dashboard_page.dart b/lib/modules/dashboard/fullscreen_dashboard_page.dart index 94dc086..44385f5 100644 --- a/lib/modules/dashboard/fullscreen_dashboard_page.dart +++ b/lib/modules/dashboard/fullscreen_dashboard_page.dart @@ -1,26 +1,25 @@ import 'package:flutter/material.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/dashboard/dashboard.dart'; import 'package:thingsboard_app/widgets/tb_app_bar.dart'; class FullscreenDashboardPage extends TbPageWidget { - final String fullscreenDashboardId; final String? _dashboardTitle; - FullscreenDashboardPage(TbContext tbContext, this.fullscreenDashboardId, {String? dashboardTitle}): - _dashboardTitle = dashboardTitle, + FullscreenDashboardPage(TbContext tbContext, this.fullscreenDashboardId, + {String? dashboardTitle}) + : _dashboardTitle = dashboardTitle, super(tbContext); @override - _FullscreenDashboardPageState createState() => _FullscreenDashboardPageState(); - + _FullscreenDashboardPageState createState() => + _FullscreenDashboardPageState(); } -class _FullscreenDashboardPageState extends TbPageState { - +class _FullscreenDashboardPageState + extends TbPageState { late ValueNotifier dashboardTitleValue; final ValueNotifier showBackValue = ValueNotifier(false); @@ -47,13 +46,12 @@ class _FullscreenDashboardPageState extends TbPageState child: ValueListenableBuilder( valueListenable: showBackValue, builder: (context, canGoBack, widget) { - return TbAppBar( - tbContext, - leading: canGoBack ? BackButton( - onPressed: () { - maybePop(); - } - ) : null, + return TbAppBar(tbContext, + leading: canGoBack + ? BackButton(onPressed: () { + maybePop(); + }) + : null, showLoadingIndicator: false, elevation: 1, shadowColor: Colors.transparent, @@ -63,29 +61,25 @@ class _FullscreenDashboardPageState extends TbPageState return FittedBox( fit: BoxFit.fitWidth, alignment: Alignment.centerLeft, - child: Text(title) - ); + child: Text(title)); }, ), actions: [ - IconButton(icon: Icon(Icons.settings), onPressed: () => navigateTo('/profile?fullscreen=true')) - ] - ); - } - ), + IconButton( + icon: Icon(Icons.settings), + onPressed: () => + navigateTo('/profile?fullscreen=true')) + ]); + }), ), - body: Dashboard( - tbContext, - titleCallback: (title) { - dashboardTitleValue.value = title; - }, - controllerCallback: (controller) { - controller.canGoBack.addListener(() { - _onCanGoBack(controller.canGoBack.value); - }); - controller.openDashboard(widget.fullscreenDashboardId, fullscreen: true); - } - ) - ); + body: Dashboard(tbContext, titleCallback: (title) { + dashboardTitleValue.value = title; + }, controllerCallback: (controller) { + controller.canGoBack.addListener(() { + _onCanGoBack(controller.canGoBack.value); + }); + controller.openDashboard(widget.fullscreenDashboardId, + fullscreen: true); + })); } } diff --git a/lib/modules/dashboard/main_dashboard_page.dart b/lib/modules/dashboard/main_dashboard_page.dart index 17c3831..b30f06d 100644 --- a/lib/modules/dashboard/main_dashboard_page.dart +++ b/lib/modules/dashboard/main_dashboard_page.dart @@ -1,12 +1,10 @@ import 'package:flutter/material.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/dashboard/dashboard.dart'; import 'package:thingsboard_app/widgets/tb_app_bar.dart'; class MainDashboardPageController { - DashboardController? _dashboardController; _MainDashboardPageState? _mainDashboardPageState; @@ -26,11 +24,13 @@ class MainDashboardPageController { } } - Future openDashboard(String dashboardId, {String? dashboardTitle, String? state, bool? hideToolbar}) async { + Future openDashboard(String dashboardId, + {String? dashboardTitle, String? state, bool? hideToolbar}) async { if (dashboardTitle != null) { _mainDashboardPageState?._updateTitle(dashboardTitle); } - await _dashboardController?.openDashboard(dashboardId, state: state, hideToolbar: hideToolbar); + await _dashboardController?.openDashboard(dashboardId, + state: state, hideToolbar: hideToolbar); } Future activateDashboard() async { @@ -40,28 +40,24 @@ class MainDashboardPageController { Future deactivateDashboard() async { await _dashboardController?.deactivateDashboard(); } - } class MainDashboardPage extends TbContextWidget { - final String? _dashboardTitle; final MainDashboardPageController? _controller; MainDashboardPage(TbContext tbContext, - {MainDashboardPageController? controller, - String? dashboardTitle}): - _controller = controller, + {MainDashboardPageController? controller, String? dashboardTitle}) + : _controller = controller, _dashboardTitle = dashboardTitle, super(tbContext); @override _MainDashboardPageState createState() => _MainDashboardPageState(); - } -class _MainDashboardPageState extends TbContextState with TickerProviderStateMixin { - +class _MainDashboardPageState extends TbContextState + with TickerProviderStateMixin { late ValueNotifier dashboardTitleValue; final ValueNotifier hasRightLayout = ValueNotifier(false); DashboardController? _dashboardController; @@ -76,9 +72,7 @@ class _MainDashboardPageState extends TbContextState with Tic duration: Duration(milliseconds: 200), ); rightLayoutMenuAnimation = CurvedAnimation( - curve: Curves.linear, - parent: rightLayoutMenuController - ); + curve: Curves.linear, parent: rightLayoutMenuController); if (widget._controller != null) { widget._controller!._setMainDashboardPageState(this); } @@ -99,68 +93,57 @@ class _MainDashboardPageState extends TbContextState with Tic Widget build(BuildContext context) { return Scaffold( appBar: TbAppBar( - tbContext, - leading: BackButton( - onPressed: () { - maybePop(); - } - ), - showLoadingIndicator: false, - elevation: 1, - shadowColor: Colors.transparent, - title: ValueListenableBuilder( - valueListenable: dashboardTitleValue, - builder: (context, title, widget) { - return FittedBox( - fit: BoxFit.fitWidth, - alignment: Alignment.centerLeft, - child: Text(title) - ); - }, - ), - actions: [ - ValueListenableBuilder( + tbContext, + leading: BackButton(onPressed: () { + maybePop(); + }), + showLoadingIndicator: false, + elevation: 1, + shadowColor: Colors.transparent, + title: ValueListenableBuilder( + valueListenable: dashboardTitleValue, + builder: (context, title, widget) { + return FittedBox( + fit: BoxFit.fitWidth, + alignment: Alignment.centerLeft, + child: Text(title)); + }, + ), + actions: [ + ValueListenableBuilder( valueListenable: hasRightLayout, builder: (context, _hasRightLayout, widget) { if (_hasRightLayout) { return IconButton( - onPressed: () => _dashboardController?.toggleRightLayout(), + onPressed: () => + _dashboardController?.toggleRightLayout(), icon: AnimatedIcon( - progress: rightLayoutMenuAnimation, - icon: AnimatedIcons.menu_close - ) - ); + progress: rightLayoutMenuAnimation, + icon: AnimatedIcons.menu_close)); } else { return SizedBox.shrink(); } - } - ) - ], + }) + ], ), - body: Dashboard( - tbContext, - activeByDefault: false, + body: Dashboard(tbContext, activeByDefault: false, titleCallback: (title) { - dashboardTitleValue.value = title; - }, - controllerCallback: (controller) { - _dashboardController = controller; - if (widget._controller != null) { - widget._controller!._setDashboardController(controller); - controller.hasRightLayout.addListener(() { - hasRightLayout.value = controller.hasRightLayout.value; - }); - controller.rightLayoutOpened.addListener(() { - if(controller.rightLayoutOpened.value) { - rightLayoutMenuController.forward(); - } else { - rightLayoutMenuController.reverse(); - } - }); + dashboardTitleValue.value = title; + }, controllerCallback: (controller) { + _dashboardController = controller; + if (widget._controller != null) { + widget._controller!._setDashboardController(controller); + controller.hasRightLayout.addListener(() { + hasRightLayout.value = controller.hasRightLayout.value; + }); + controller.rightLayoutOpened.addListener(() { + if (controller.rightLayoutOpened.value) { + rightLayoutMenuController.forward(); + } else { + rightLayoutMenuController.reverse(); } - } - ) - ); + }); + } + })); } - } diff --git a/lib/modules/device/device_details_page.dart b/lib/modules/device/device_details_page.dart index ec30032..c2c9655 100644 --- a/lib/modules/device/device_details_page.dart +++ b/lib/modules/device/device_details_page.dart @@ -1,15 +1,11 @@ import 'package:flutter/material.dart'; -import 'package:flutter/widgets.dart'; import 'package:thingsboard_app/core/context/tb_context.dart'; import 'package:thingsboard_app/core/entity/entity_details_page.dart'; import 'package:thingsboard_client/thingsboard_client.dart'; class DeviceDetailsPage extends EntityDetailsPage { - - DeviceDetailsPage(TbContext tbContext, String deviceId): - super(tbContext, - entityId: deviceId, - defaultTitle: 'Device'); + DeviceDetailsPage(TbContext tbContext, String deviceId) + : super(tbContext, entityId: deviceId, defaultTitle: 'Device'); @override Future fetchEntity(String deviceId) { @@ -23,5 +19,4 @@ class DeviceDetailsPage extends EntityDetailsPage { subtitle: Text('${device.type}'), ); } - } diff --git a/lib/modules/device/device_profiles_base.dart b/lib/modules/device/device_profiles_base.dart index 2f846aa..5fcebae 100644 --- a/lib/modules/device/device_profiles_base.dart +++ b/lib/modules/device/device_profiles_base.dart @@ -2,7 +2,6 @@ import 'dart:async'; import 'package:auto_size_text/auto_size_text.dart'; import 'package:flutter/material.dart'; -import 'package:flutter/widgets.dart'; import 'package:flutter_svg/flutter_svg.dart'; import 'package:thingsboard_app/constants/assets_path.dart'; import 'package:thingsboard_app/core/context/tb_context.dart'; @@ -14,7 +13,6 @@ import 'package:thingsboard_app/utils/utils.dart'; import 'package:thingsboard_client/thingsboard_client.dart'; mixin DeviceProfilesBase on EntitiesBase { - final RefreshDeviceCounts refreshDeviceCounts = RefreshDeviceCounts(); @override @@ -48,7 +46,8 @@ mixin DeviceProfilesBase on EntitiesBase { } @override - Widget buildEntityGridCard(BuildContext context, DeviceProfileInfo deviceProfile) { + Widget buildEntityGridCard( + BuildContext context, DeviceProfileInfo deviceProfile) { return DeviceProfileCard(tbContext, deviceProfile); } @@ -56,7 +55,6 @@ mixin DeviceProfilesBase on EntitiesBase { double? gridChildAspectRatio() { return 156 / 200; } - } class RefreshDeviceCounts { @@ -64,20 +62,20 @@ class RefreshDeviceCounts { } class AllDevicesCard extends TbContextWidget { - final RefreshDeviceCounts refreshDeviceCounts; - AllDevicesCard(TbContext tbContext, this.refreshDeviceCounts) : super(tbContext); + AllDevicesCard(TbContext tbContext, this.refreshDeviceCounts) + : super(tbContext); @override _AllDevicesCardState createState() => _AllDevicesCardState(); - } class _AllDevicesCardState extends TbContextState { - - final StreamController _activeDevicesCount = StreamController.broadcast(); - final StreamController _inactiveDevicesCount = StreamController.broadcast(); + final StreamController _activeDevicesCount = + StreamController.broadcast(); + final StreamController _inactiveDevicesCount = + StreamController.broadcast(); @override void initState() { @@ -103,9 +101,12 @@ class _AllDevicesCardState extends TbContextState { Future _countDevices() { _activeDevicesCount.add(null); _inactiveDevicesCount.add(null); - Future activeDevicesCount = EntityQueryApi.countDevices(tbClient, active: true); - Future inactiveDevicesCount = EntityQueryApi.countDevices(tbClient, active: false); - Future> countsFuture = Future.wait([activeDevicesCount, inactiveDevicesCount]); + Future activeDevicesCount = + EntityQueryApi.countDevices(tbClient, active: true); + Future inactiveDevicesCount = + EntityQueryApi.countDevices(tbClient, active: false); + Future> countsFuture = + Future.wait([activeDevicesCount, inactiveDevicesCount]); countsFuture.then((counts) { if (this.mounted) { _activeDevicesCount.add(counts[0]); @@ -117,20 +118,19 @@ class _AllDevicesCardState extends TbContextState { @override Widget build(BuildContext context) { - return - GestureDetector( - behavior: HitTestBehavior.opaque, - child: - Container( - child: Card( - margin: EdgeInsets.zero, - shape: RoundedRectangleBorder( - borderRadius: BorderRadius.circular(4), - ), - elevation: 0, - child: Column( - children: [ - Padding(padding: EdgeInsets.fromLTRB(16, 12, 16, 15), + return GestureDetector( + behavior: HitTestBehavior.opaque, + child: Container( + child: Card( + margin: EdgeInsets.zero, + shape: RoundedRectangleBorder( + borderRadius: BorderRadius.circular(4), + ), + elevation: 0, + child: Column( + children: [ + Padding( + padding: EdgeInsets.fromLTRB(16, 12, 16, 15), child: Row( mainAxisSize: MainAxisSize.max, mainAxisAlignment: MainAxisAlignment.spaceBetween, @@ -139,120 +139,126 @@ class _AllDevicesCardState extends TbContextState { style: TextStyle( fontWeight: FontWeight.w500, fontSize: 14, - height: 20 / 14 - ) - ), + height: 20 / 14)), Icon(Icons.arrow_forward, size: 18) ], - ) - ), - Divider(height: 1), - Padding(padding: EdgeInsets.all(0), - child: Row( - mainAxisSize: MainAxisSize.max, - children: [ - Flexible(fit: FlexFit.tight, - child: GestureDetector( - behavior: HitTestBehavior.opaque, - child: Container( - height: 40, - decoration: BoxDecoration( - color: Colors.white, - borderRadius: BorderRadius.circular(4), - ), - child: StreamBuilder( - stream: _activeDevicesCount.stream, - builder: (context, snapshot) { - if (snapshot.hasData) { - var deviceCount = snapshot.data!; - return _buildDeviceCount(context, true, deviceCount); - } else { - return Center(child: - Container(height: 20, width: 20, - child: CircularProgressIndicator( - valueColor: AlwaysStoppedAnimation(Theme.of(tbContext.currentState!.context).colorScheme.primary), - strokeWidth: 2.5))); - } - }, - ) - ), - onTap: () { - navigateTo('/deviceList?active=true'); - } - ), - ), - // SizedBox(width: 4), - Container(width: 1, - height: 40, - child: VerticalDivider(width: 1) - ), - Flexible(fit: FlexFit.tight, - child: GestureDetector( - behavior: HitTestBehavior.opaque, - child: Container( - height: 40, - decoration: BoxDecoration( - color: Colors.white, - borderRadius: BorderRadius.circular(4), - ), - child: StreamBuilder( - stream: _inactiveDevicesCount.stream, - builder: (context, snapshot) { - if (snapshot.hasData) { - var deviceCount = snapshot.data!; - return _buildDeviceCount(context, false, deviceCount); - } else { - return Center(child: - Container(height: 20, width: 20, - child: CircularProgressIndicator( - valueColor: AlwaysStoppedAnimation(Theme.of(tbContext.currentState!.context).colorScheme.primary), - strokeWidth: 2.5))); - } - }, - ) - ), - onTap: () { - navigateTo('/deviceList?active=false'); - } - ), - ) - ], - ) - ) - ], - ) - ), - decoration: BoxDecoration( - boxShadow: [ - BoxShadow( - color: Colors.black.withAlpha((255 * 0.05).ceil()), - blurRadius: 6.0, - offset: Offset(0, 4) - ) - ], - ), + )), + Divider(height: 1), + Padding( + padding: EdgeInsets.all(0), + child: Row( + mainAxisSize: MainAxisSize.max, + children: [ + Flexible( + fit: FlexFit.tight, + child: GestureDetector( + behavior: HitTestBehavior.opaque, + child: Container( + height: 40, + decoration: BoxDecoration( + color: Colors.white, + borderRadius: BorderRadius.circular(4), + ), + child: StreamBuilder( + stream: _activeDevicesCount.stream, + builder: (context, snapshot) { + if (snapshot.hasData) { + var deviceCount = snapshot.data!; + return _buildDeviceCount( + context, true, deviceCount); + } else { + return Center( + child: Container( + height: 20, + width: 20, + child: CircularProgressIndicator( + valueColor: + AlwaysStoppedAnimation( + Theme.of(tbContext + .currentState! + .context) + .colorScheme + .primary), + strokeWidth: 2.5))); + } + }, + )), + onTap: () { + navigateTo('/deviceList?active=true'); + }), + ), + // SizedBox(width: 4), + Container( + width: 1, + height: 40, + child: VerticalDivider(width: 1)), + Flexible( + fit: FlexFit.tight, + child: GestureDetector( + behavior: HitTestBehavior.opaque, + child: Container( + height: 40, + decoration: BoxDecoration( + color: Colors.white, + borderRadius: BorderRadius.circular(4), + ), + child: StreamBuilder( + stream: _inactiveDevicesCount.stream, + builder: (context, snapshot) { + if (snapshot.hasData) { + var deviceCount = snapshot.data!; + return _buildDeviceCount( + context, false, deviceCount); + } else { + return Center( + child: Container( + height: 20, + width: 20, + child: CircularProgressIndicator( + valueColor: + AlwaysStoppedAnimation( + Theme.of(tbContext + .currentState! + .context) + .colorScheme + .primary), + strokeWidth: 2.5))); + } + }, + )), + onTap: () { + navigateTo('/deviceList?active=false'); + }), + ) + ], + )) + ], + )), + decoration: BoxDecoration( + boxShadow: [ + BoxShadow( + color: Colors.black.withAlpha((255 * 0.05).ceil()), + blurRadius: 6.0, + offset: Offset(0, 4)) + ], ), - onTap: () { - navigateTo('/deviceList'); - } - ); + ), + onTap: () { + navigateTo('/deviceList'); + }); } - } class DeviceProfileCard extends TbContextWidget { - final DeviceProfileInfo deviceProfile; DeviceProfileCard(TbContext tbContext, this.deviceProfile) : super(tbContext); @override _DeviceProfileCardState createState() => _DeviceProfileCardState(); - } class _DeviceProfileCardState extends TbContextState { - late Future activeDevicesCount; late Future inactiveDevicesCount; @@ -269,8 +275,10 @@ class _DeviceProfileCardState extends TbContextState { } _countDevices() { - activeDevicesCount = EntityQueryApi.countDevices(tbClient, deviceType: widget.deviceProfile.name, active: true); - inactiveDevicesCount = EntityQueryApi.countDevices(tbClient, deviceType: widget.deviceProfile.name, active: false); + activeDevicesCount = EntityQueryApi.countDevices(tbClient, + deviceType: widget.deviceProfile.name, active: true); + inactiveDevicesCount = EntityQueryApi.countDevices(tbClient, + deviceType: widget.deviceProfile.name, active: false); } @override @@ -292,99 +300,95 @@ class _DeviceProfileCardState extends TbContextState { imageFit = BoxFit.cover; padding = 0; } - return - ClipRRect( - borderRadius: BorderRadius.circular(4), - child: Column( - children: [ - Expanded( - child: Stack ( - children: [ - SizedBox.expand( - child: Padding( - padding: EdgeInsets.all(padding), - child: FittedBox( - clipBehavior: Clip.hardEdge, - fit: imageFit, - child: image - ) - ) - ) - ] - ) - ), - Container( - height: 44, - child: Padding( - padding: EdgeInsets.symmetric(horizontal: 6), - child: Center( - child: AutoSizeText(entity.name, - textAlign: TextAlign.center, - maxLines: 1, - minFontSize: 12, - overflow: TextOverflow.ellipsis, - style: TextStyle( - fontWeight: FontWeight.w500, - fontSize: 14, - height: 20 / 14 - ), - ) - ) - ) - ), - Divider(height: 1), - GestureDetector( - behavior: HitTestBehavior.opaque, - child: FutureBuilder( - future: activeDevicesCount, - builder: (context, snapshot) { - if (snapshot.hasData && snapshot.connectionState == ConnectionState.done) { - var deviceCount = snapshot.data!; - return _buildDeviceCount(context, true, deviceCount); - } else { - return Container(height: 40, - child: Center( - child: Container( - height: 20, width: 20, - child: - CircularProgressIndicator( - valueColor: AlwaysStoppedAnimation(Theme.of(tbContext.currentState!.context).colorScheme.primary), - strokeWidth: 2.5)))); - } - }, - ), - onTap: () { - navigateTo('/deviceList?active=true&deviceType=${entity.name}'); - } - ), - Divider(height: 1), - GestureDetector( - behavior: HitTestBehavior.opaque, - child: FutureBuilder( - future: inactiveDevicesCount, - builder: (context, snapshot) { - if (snapshot.hasData && snapshot.connectionState == ConnectionState.done) { - var deviceCount = snapshot.data!; - return _buildDeviceCount(context, false, deviceCount); - } else { - return Container(height: 40, - child: Center( - child: Container( - height: 20, width: 20, - child: - CircularProgressIndicator( - valueColor: AlwaysStoppedAnimation(Theme.of(tbContext.currentState!.context).colorScheme.primary), - strokeWidth: 2.5)))); - } - }, - ), - onTap: () { - navigateTo('/deviceList?active=false&deviceType=${entity.name}'); - } - ) - ] - ) - ); + return ClipRRect( + borderRadius: BorderRadius.circular(4), + child: Column(children: [ + Expanded( + child: Stack(children: [ + SizedBox.expand( + child: Padding( + padding: EdgeInsets.all(padding), + child: FittedBox( + clipBehavior: Clip.hardEdge, + fit: imageFit, + child: image))) + ])), + Container( + height: 44, + child: Padding( + padding: EdgeInsets.symmetric(horizontal: 6), + child: Center( + child: AutoSizeText( + entity.name, + textAlign: TextAlign.center, + maxLines: 1, + minFontSize: 12, + overflow: TextOverflow.ellipsis, + style: TextStyle( + fontWeight: FontWeight.w500, + fontSize: 14, + height: 20 / 14), + )))), + Divider(height: 1), + GestureDetector( + behavior: HitTestBehavior.opaque, + child: FutureBuilder( + future: activeDevicesCount, + builder: (context, snapshot) { + if (snapshot.hasData && + snapshot.connectionState == ConnectionState.done) { + var deviceCount = snapshot.data!; + return _buildDeviceCount(context, true, deviceCount); + } else { + return Container( + height: 40, + child: Center( + child: Container( + height: 20, + width: 20, + child: CircularProgressIndicator( + valueColor: AlwaysStoppedAnimation(Theme.of( + tbContext.currentState!.context) + .colorScheme + .primary), + strokeWidth: 2.5)))); + } + }, + ), + onTap: () { + navigateTo('/deviceList?active=true&deviceType=${entity.name}'); + }), + Divider(height: 1), + GestureDetector( + behavior: HitTestBehavior.opaque, + child: FutureBuilder( + future: inactiveDevicesCount, + builder: (context, snapshot) { + if (snapshot.hasData && + snapshot.connectionState == ConnectionState.done) { + var deviceCount = snapshot.data!; + return _buildDeviceCount(context, false, deviceCount); + } else { + return Container( + height: 40, + child: Center( + child: Container( + height: 20, + width: 20, + child: CircularProgressIndicator( + valueColor: AlwaysStoppedAnimation(Theme.of( + tbContext.currentState!.context) + .colorScheme + .primary), + strokeWidth: 2.5)))); + } + }, + ), + onTap: () { + navigateTo( + '/deviceList?active=false&deviceType=${entity.name}'); + }) + ])); } } @@ -402,26 +406,27 @@ Widget _buildDeviceCount(BuildContext context, bool active, int count) { Stack( children: [ Icon(Icons.devices_other, size: 16, color: color), - if (!active) CustomPaint( - size: Size.square(16), - painter: StrikeThroughPainter(color: color, offset: 2), - ) + if (!active) + CustomPaint( + size: Size.square(16), + painter: StrikeThroughPainter(color: color, offset: 2), + ) ], ), - SizedBox(width: 8.67), - Text(active ? 'Active' : 'Inactive', style: TextStyle( - fontSize: 12, - fontWeight: FontWeight.w500, - height: 16 / 12, - color: color - )), - SizedBox(width: 8.67), - Text(count.toString(), style: TextStyle( - fontSize: 12, - fontWeight: FontWeight.w500, - height: 16 / 12, - color: color - )) + SizedBox(width: 8.67), + Text(active ? 'Active' : 'Inactive', + style: TextStyle( + fontSize: 12, + fontWeight: FontWeight.w500, + height: 16 / 12, + color: color)), + SizedBox(width: 8.67), + Text(count.toString(), + style: TextStyle( + fontSize: 12, + fontWeight: FontWeight.w500, + height: 16 / 12, + color: color)) ], ), Icon(Icons.chevron_right, size: 16, color: Color(0xFFACACAC)) @@ -431,7 +436,6 @@ Widget _buildDeviceCount(BuildContext context, bool active, int count) { } class StrikeThroughPainter extends CustomPainter { - final Color color; final double offset; @@ -441,7 +445,8 @@ class StrikeThroughPainter extends CustomPainter { void paint(Canvas canvas, Size size) { final paint = Paint()..color = color; paint.strokeWidth = 1.5; - canvas.drawLine(Offset(offset, offset), Offset(size.width - offset, size.height - offset), paint); + canvas.drawLine(Offset(offset, offset), + Offset(size.width - offset, size.height - offset), paint); paint.color = Colors.white; canvas.drawLine(Offset(2, 0), Offset(size.width + 2, size.height), paint); } @@ -450,5 +455,4 @@ class StrikeThroughPainter extends CustomPainter { bool shouldRepaint(covariant StrikeThroughPainter oldDelegate) { return color != oldDelegate.color; } - } diff --git a/lib/modules/device/device_profiles_grid.dart b/lib/modules/device/device_profiles_grid.dart index 78282eb..2953839 100644 --- a/lib/modules/device/device_profiles_grid.dart +++ b/lib/modules/device/device_profiles_grid.dart @@ -5,8 +5,9 @@ import 'package:thingsboard_client/thingsboard_client.dart'; import 'device_profiles_base.dart'; -class DeviceProfilesGrid extends BaseEntitiesWidget with DeviceProfilesBase, EntitiesGridStateBase { - - DeviceProfilesGrid(TbContext tbContext, PageKeyController pageKeyController) : super(tbContext, pageKeyController); - +class DeviceProfilesGrid extends BaseEntitiesWidget + with DeviceProfilesBase, EntitiesGridStateBase { + DeviceProfilesGrid( + TbContext tbContext, PageKeyController pageKeyController) + : super(tbContext, pageKeyController); } diff --git a/lib/modules/device/device_routes.dart b/lib/modules/device/device_routes.dart index 59c8794..2495e60 100644 --- a/lib/modules/device/device_routes.dart +++ b/lib/modules/device/device_routes.dart @@ -9,24 +9,28 @@ import 'device_details_page.dart'; import 'devices_list_page.dart'; class DeviceRoutes extends TbRoutes { - - late var devicesHandler = Handler(handlerFunc: (BuildContext? context, Map params) { + late var devicesHandler = Handler( + handlerFunc: (BuildContext? context, Map params) { return MainPage(tbContext, path: '/devices'); }); - late var devicesPageHandler = Handler(handlerFunc: (BuildContext? context, Map params) { + late var devicesPageHandler = Handler( + handlerFunc: (BuildContext? context, Map params) { return DevicesPage(tbContext); }); - late var deviceListHandler = Handler(handlerFunc: (BuildContext? context, Map params) { + late var deviceListHandler = Handler( + handlerFunc: (BuildContext? context, Map params) { var searchMode = params['search']?.first == 'true'; var deviceType = params['deviceType']?.first; String? activeStr = params['active']?.first; bool? active = activeStr != null ? activeStr == 'true' : null; - return DevicesListPage(tbContext, searchMode: searchMode, deviceType: deviceType, active: active); + return DevicesListPage(tbContext, + searchMode: searchMode, deviceType: deviceType, active: active); }); - late var deviceDetailsHandler = Handler(handlerFunc: (BuildContext? context, Map params) { + late var deviceDetailsHandler = Handler( + handlerFunc: (BuildContext? context, Map params) { return DeviceDetailsPage(tbContext, params["id"][0]); }); @@ -39,5 +43,4 @@ class DeviceRoutes extends TbRoutes { router.define("/deviceList", handler: deviceListHandler); router.define("/device/:id", handler: deviceDetailsHandler); } - } diff --git a/lib/modules/device/devices_base.dart b/lib/modules/device/devices_base.dart index 07d192f..03f7090 100644 --- a/lib/modules/device/devices_base.dart +++ b/lib/modules/device/devices_base.dart @@ -1,7 +1,6 @@ import 'dart:core'; import 'package:flutter/material.dart'; -import 'package:flutter/widgets.dart'; import 'package:flutter_svg/flutter_svg.dart'; import 'package:intl/intl.dart'; import 'package:thingsboard_app/constants/assets_path.dart'; @@ -14,7 +13,6 @@ import 'package:thingsboard_app/utils/utils.dart'; import 'package:thingsboard_client/thingsboard_client.dart'; mixin DevicesBase on EntitiesBase { - @override String get title => 'Devices'; @@ -28,14 +26,19 @@ mixin DevicesBase on EntitiesBase { @override void onEntityTap(EntityData device) async { - var profile = await DeviceProfileCache.getDeviceProfileInfo(tbClient, device.field('type')!, device.entityId.id!); + var profile = await DeviceProfileCache.getDeviceProfileInfo( + tbClient, device.field('type')!, device.entityId.id!); if (profile.defaultDashboardId != null) { var dashboardId = profile.defaultDashboardId!.id!; - var state = Utils.createDashboardEntityState(device.entityId, entityName: device.field('name')!, entityLabel: device.field('label')!); - navigateToDashboard(dashboardId, dashboardTitle: device.field('name'), state: state); + var state = Utils.createDashboardEntityState(device.entityId, + entityName: device.field('name')!, + entityLabel: device.field('label')!); + navigateToDashboard(dashboardId, + dashboardTitle: device.field('name'), state: state); } else { if (tbClient.isTenantAdmin()) { - showWarnNotification('Mobile dashboard should be configured in device profile!'); + showWarnNotification( + 'Mobile dashboard should be configured in device profile!'); } } } @@ -57,42 +60,51 @@ mixin DevicesBase on EntitiesBase { bool displayCardImage(bool listWidgetCard) => listWidgetCard; - Widget _buildEntityListCard(BuildContext context, EntityData device, bool listWidgetCard) { - return DeviceCard(tbContext, device: device, listWidgetCard: listWidgetCard, displayImage: displayCardImage(listWidgetCard)); + Widget _buildEntityListCard( + BuildContext context, EntityData device, bool listWidgetCard) { + return DeviceCard(tbContext, + device: device, + listWidgetCard: listWidgetCard, + displayImage: displayCardImage(listWidgetCard)); } } class DeviceQueryController extends PageKeyController { - - DeviceQueryController({int pageSize = 20, String? searchText, String? deviceType, bool? active}): - super(EntityQueryApi.createDefaultDeviceQuery(pageSize: pageSize, searchText: searchText, deviceType: deviceType, active: active)); + DeviceQueryController( + {int pageSize = 20, String? searchText, String? deviceType, bool? active}) + : super(EntityQueryApi.createDefaultDeviceQuery( + pageSize: pageSize, + searchText: searchText, + deviceType: deviceType, + active: active)); @override - EntityDataQuery nextPageKey(EntityDataQuery deviceQuery) => deviceQuery.next(); + EntityDataQuery nextPageKey(EntityDataQuery deviceQuery) => + deviceQuery.next(); onSearchText(String searchText) { value.pageKey.pageLink.page = 0; value.pageKey.pageLink.textSearch = searchText; notifyListeners(); } - } class DeviceCard extends TbContextWidget { - final EntityData device; final bool listWidgetCard; final bool displayImage; - DeviceCard(TbContext tbContext, {required this.device, this.listWidgetCard = false, this.displayImage = false}) : super(tbContext); + DeviceCard(TbContext tbContext, + {required this.device, + this.listWidgetCard = false, + this.displayImage = false}) + : super(tbContext); @override _DeviceCardState createState() => _DeviceCardState(); - } class _DeviceCardState extends TbContextState { - final entityDateFormat = DateFormat('yyyy-MM-dd'); late Future deviceProfileFuture; @@ -129,250 +141,256 @@ class _DeviceCardState extends TbContextState { } Widget buildCard(BuildContext context) { - return Stack( - children: [ - Positioned.fill( + return Stack(children: [ + Positioned.fill( + child: Container( + alignment: Alignment.centerLeft, child: Container( - alignment: Alignment.centerLeft, - child: Container( - width: 4, - decoration: BoxDecoration( - color: widget.device.attribute('active') == 'true' ? Color(0xFF008A00) : Color(0xFFAFAFAF), - borderRadius: BorderRadius.only(topLeft: Radius.circular(4), bottomLeft: Radius.circular(4)) - ), - ) - ) - ), - FutureBuilder( + width: 4, + decoration: BoxDecoration( + color: widget.device.attribute('active') == 'true' + ? Color(0xFF008A00) + : Color(0xFFAFAFAF), + borderRadius: BorderRadius.only( + topLeft: Radius.circular(4), + bottomLeft: Radius.circular(4))), + ))), + FutureBuilder( + future: deviceProfileFuture, + builder: (context, snapshot) { + if (snapshot.hasData && + snapshot.connectionState == ConnectionState.done) { + var profile = snapshot.data!; + bool hasDashboard = profile.defaultDashboardId != null; + Widget image; + BoxFit imageFit; + if (profile.image != null) { + image = Utils.imageFromBase64(profile.image!); + imageFit = BoxFit.contain; + } else { + image = SvgPicture.asset( + ThingsboardImage.deviceProfilePlaceholder, + color: Theme.of(context).primaryColor, + colorBlendMode: BlendMode.overlay, + semanticsLabel: 'Device'); + imageFit = BoxFit.cover; + } + return Row( + mainAxisSize: MainAxisSize.max, + crossAxisAlignment: CrossAxisAlignment.center, + children: [ + SizedBox(width: 20), + Flexible( + fit: FlexFit.tight, + child: Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + SizedBox(height: 12), + Row( + mainAxisSize: MainAxisSize.max, + crossAxisAlignment: CrossAxisAlignment.center, + children: [ + if (widget.displayImage) + Container( + width: 40, + height: 40, + decoration: BoxDecoration( + borderRadius: BorderRadius.all( + Radius.circular(4))), + child: ClipRRect( + borderRadius: BorderRadius.all( + Radius.circular(4)), + child: Stack( + children: [ + Positioned.fill( + child: FittedBox( + fit: imageFit, + child: image, + )) + ], + ))), + SizedBox(width: 12), + Flexible( + fit: FlexFit.tight, + child: Column(children: [ + Row( + mainAxisSize: MainAxisSize.max, + mainAxisAlignment: + MainAxisAlignment.spaceBetween, + children: [ + Flexible( + fit: FlexFit.tight, + child: FittedBox( + fit: BoxFit.scaleDown, + alignment: + Alignment.centerLeft, + child: Text( + '${widget.device.field('name')!}', + style: TextStyle( + color: Color( + 0xFF282828), + fontSize: 14, + fontWeight: + FontWeight + .w500, + height: + 20 / 14)))), + SizedBox(width: 12), + Text( + entityDateFormat.format(DateTime + .fromMillisecondsSinceEpoch( + widget.device + .createdTime!)), + style: TextStyle( + color: Color(0xFFAFAFAF), + fontSize: 12, + fontWeight: + FontWeight.normal, + height: 16 / 12)) + ]), + SizedBox(height: 4), + Row( + mainAxisSize: MainAxisSize.max, + mainAxisAlignment: + MainAxisAlignment.spaceBetween, + children: [ + Text( + '${widget.device.field('type')!}', + style: TextStyle( + color: Color(0xFFAFAFAF), + fontSize: 12, + fontWeight: + FontWeight.normal, + height: 16 / 12)), + Text( + widget.device.attribute( + 'active') == + 'true' + ? 'Active' + : 'Inactive', + style: TextStyle( + color: widget.device + .attribute( + 'active') == + 'true' + ? Color(0xFF008A00) + : Color(0xFFAFAFAF), + fontSize: 12, + height: 16 / 12, + fontWeight: FontWeight.normal, + )) + ], + ) + ])), + SizedBox(width: 16), + if (hasDashboard) + Icon(Icons.chevron_right, + color: Color(0xFFACACAC)), + if (hasDashboard) SizedBox(width: 16), + ]), + SizedBox(height: 12) + ], + )) + ]); + } else { + return Container( + height: 64, + child: Center( + child: RefreshProgressIndicator( + valueColor: AlwaysStoppedAnimation( + Theme.of(tbContext.currentState!.context) + .colorScheme + .primary)))); + } + }) + ]); + } + + Widget buildListWidgetCard(BuildContext context) { + return Row(mainAxisSize: MainAxisSize.min, children: [ + if (widget.displayImage) + Container( + width: 58, + height: 58, + decoration: BoxDecoration( + // color: Color(0xFFEEEEEE), + borderRadius: BorderRadius.horizontal(left: Radius.circular(4))), + child: FutureBuilder( future: deviceProfileFuture, builder: (context, snapshot) { - if (snapshot.hasData && snapshot.connectionState == ConnectionState.done) { + if (snapshot.hasData && + snapshot.connectionState == ConnectionState.done) { var profile = snapshot.data!; - bool hasDashboard = profile.defaultDashboardId != null; Widget image; BoxFit imageFit; if (profile.image != null) { image = Utils.imageFromBase64(profile.image!); imageFit = BoxFit.contain; } else { - image = SvgPicture.asset(ThingsboardImage.deviceProfilePlaceholder, + image = SvgPicture.asset( + ThingsboardImage.deviceProfilePlaceholder, color: Theme.of(context).primaryColor, colorBlendMode: BlendMode.overlay, semanticsLabel: 'Device'); imageFit = BoxFit.cover; } - return Row( - mainAxisSize: MainAxisSize.max, - crossAxisAlignment: CrossAxisAlignment.center, - children: [ - SizedBox(width: 20), - Flexible( - fit: FlexFit.tight, - child: Column( - crossAxisAlignment: CrossAxisAlignment.start, - children: [ - SizedBox(height: 12), - Row( - mainAxisSize: MainAxisSize.max, - crossAxisAlignment: CrossAxisAlignment.center, - children: [ - if (widget.displayImage) Container( - width: 40, - height: 40, - decoration: BoxDecoration( - borderRadius: BorderRadius.all(Radius.circular(4)) - ), - child: ClipRRect( - borderRadius: BorderRadius.all(Radius.circular(4)), - child: Stack( - children: [ - Positioned.fill( - child: FittedBox( - fit: imageFit, - child: image, - ) - ) - ], - ) - ) - ), - SizedBox(width: 12), - Flexible( - fit: FlexFit.tight, - child: Column( - children: [ - Row( - mainAxisSize: MainAxisSize.max, - mainAxisAlignment: MainAxisAlignment.spaceBetween, - children: [ - Flexible( - fit: FlexFit.tight, - child: FittedBox( - fit: BoxFit.scaleDown, - alignment: Alignment.centerLeft, - child: Text('${widget.device.field('name')!}', - style: TextStyle( - color: Color(0xFF282828), - fontSize: 14, - fontWeight: FontWeight.w500, - height: 20 / 14 - )) - ) - ), - SizedBox(width: 12), - Text(entityDateFormat.format(DateTime.fromMillisecondsSinceEpoch(widget.device.createdTime!)), - style: TextStyle( - color: Color(0xFFAFAFAF), - fontSize: 12, - fontWeight: FontWeight.normal, - height: 16 / 12 - )) - ] - ), - SizedBox(height: 4), - Row( - mainAxisSize: MainAxisSize.max, - mainAxisAlignment: MainAxisAlignment.spaceBetween, - children: [ - Text('${widget.device.field('type')!}', - style: TextStyle( - color: Color(0xFFAFAFAF), - fontSize: 12, - fontWeight: FontWeight.normal, - height: 16 / 12 - )), - Text(widget.device.attribute('active') == 'true' ? 'Active' : 'Inactive', - style: TextStyle( - color: widget.device.attribute('active') == 'true' ? Color(0xFF008A00) : Color(0xFFAFAFAF), - fontSize: 12, - height: 16 / 12, - fontWeight: FontWeight.normal, - )) - ], - ) - ] - ) - ), - SizedBox(width: 16), - if (hasDashboard) Icon(Icons.chevron_right, color: Color(0xFFACACAC)), - if (hasDashboard) SizedBox(width: 16), - ] - ), - SizedBox(height: 12) - ], - ) - ) - ] - ); + return ClipRRect( + borderRadius: + BorderRadius.horizontal(left: Radius.circular(4)), + child: Stack( + children: [ + Positioned.fill( + child: FittedBox( + fit: imageFit, + child: image, + )) + ], + )); } else { - return Container( - height: 64, - child: Center( - child: RefreshProgressIndicator( - valueColor: AlwaysStoppedAnimation(Theme.of(tbContext.currentState!.context).colorScheme.primary) - ) - ) - ); + return Center( + child: RefreshProgressIndicator( + valueColor: AlwaysStoppedAnimation( + Theme.of(tbContext.currentState!.context) + .colorScheme + .primary))); } - } - ) - ] - ); - } - - Widget buildListWidgetCard(BuildContext context) { - return Row( - mainAxisSize: MainAxisSize.min, - children: [ - if (widget.displayImage) Container( - width: 58, - height: 58, - decoration: BoxDecoration( - // color: Color(0xFFEEEEEE), - borderRadius: BorderRadius.horizontal(left: Radius.circular(4)) - ), - child: FutureBuilder( - future: deviceProfileFuture, - builder: (context, snapshot) { - if (snapshot.hasData && snapshot.connectionState == ConnectionState.done) { - var profile = snapshot.data!; - Widget image; - BoxFit imageFit; - if (profile.image != null) { - image = Utils.imageFromBase64(profile.image!); - imageFit = BoxFit.contain; - } else { - image = SvgPicture.asset(ThingsboardImage.deviceProfilePlaceholder, - color: Theme.of(context).primaryColor, - colorBlendMode: BlendMode.overlay, - semanticsLabel: 'Device'); - imageFit = BoxFit.cover; - } - return ClipRRect( - borderRadius: BorderRadius.horizontal(left: Radius.circular(4)), - child: Stack( - children: [ - Positioned.fill( - child: FittedBox( - fit: imageFit, - child: image, - ) - ) - ], - ) - ); - } else { - return Center(child: RefreshProgressIndicator( - valueColor: AlwaysStoppedAnimation(Theme.of(tbContext.currentState!.context).colorScheme.primary) - )); - } - }, - ), + }, ), - Flexible( - fit: FlexFit.loose, - child: - Container( - padding: EdgeInsets.symmetric(vertical: 9, horizontal: 16), - child: Column( - children: [ - Row( - mainAxisSize: MainAxisSize.min, - mainAxisAlignment: MainAxisAlignment.spaceBetween, - children: [ - FittedBox( - fit: BoxFit.fitWidth, - alignment: Alignment.centerLeft, - child: Text('${widget.device.field('name')!}', - style: TextStyle( - color: Color(0xFF282828), - fontSize: 14, - fontWeight: FontWeight.w500, - height: 20 / 14 - )) - ) - ] - ), - SizedBox(height: 4), - Row( - mainAxisSize: MainAxisSize.min, - mainAxisAlignment: MainAxisAlignment.spaceBetween, - children: [ - Text('${widget.device.field('type')!}', - style: TextStyle( - color: Color(0xFFAFAFAF), - fontSize: 12, - fontWeight: FontWeight.normal, - height: 16 / 12 - )), - ] - ) - ], - ) - ) - ) - ] - ); + ), + Flexible( + fit: FlexFit.loose, + child: Container( + padding: EdgeInsets.symmetric(vertical: 9, horizontal: 16), + child: Column( + children: [ + Row( + mainAxisSize: MainAxisSize.min, + mainAxisAlignment: MainAxisAlignment.spaceBetween, + children: [ + FittedBox( + fit: BoxFit.fitWidth, + alignment: Alignment.centerLeft, + child: Text('${widget.device.field('name')!}', + style: TextStyle( + color: Color(0xFF282828), + fontSize: 14, + fontWeight: FontWeight.w500, + height: 20 / 14))) + ]), + SizedBox(height: 4), + Row( + mainAxisSize: MainAxisSize.min, + mainAxisAlignment: MainAxisAlignment.spaceBetween, + children: [ + Text('${widget.device.field('type')!}', + style: TextStyle( + color: Color(0xFFAFAFAF), + fontSize: 12, + fontWeight: FontWeight.normal, + height: 16 / 12)), + ]) + ], + ))) + ]); } - } diff --git a/lib/modules/device/devices_list.dart b/lib/modules/device/devices_list.dart index 0697109..089d7cf 100644 --- a/lib/modules/device/devices_list.dart +++ b/lib/modules/device/devices_list.dart @@ -4,15 +4,15 @@ import 'package:thingsboard_app/core/entity/entities_list.dart'; import 'package:thingsboard_app/modules/device/devices_base.dart'; import 'package:thingsboard_client/thingsboard_client.dart'; -class DevicesList extends BaseEntitiesWidget with DevicesBase, EntitiesListStateBase { - +class DevicesList extends BaseEntitiesWidget + with DevicesBase, EntitiesListStateBase { final bool displayDeviceImage; - DevicesList(TbContext tbContext, PageKeyController pageKeyController, {searchMode = false, this.displayDeviceImage = false}): - super(tbContext, pageKeyController, searchMode: searchMode); + DevicesList( + TbContext tbContext, PageKeyController pageKeyController, + {searchMode = false, this.displayDeviceImage = false}) + : super(tbContext, pageKeyController, searchMode: searchMode); @override bool displayCardImage(bool listWidgetCard) => displayDeviceImage; - } - diff --git a/lib/modules/device/devices_list_page.dart b/lib/modules/device/devices_list_page.dart index a7005ae..16d969a 100644 --- a/lib/modules/device/devices_list_page.dart +++ b/lib/modules/device/devices_list_page.dart @@ -6,87 +6,85 @@ import 'package:thingsboard_app/modules/device/devices_list.dart'; import 'package:thingsboard_app/widgets/tb_app_bar.dart'; class DevicesListPage extends TbPageWidget { - final String? deviceType; final bool? active; final bool searchMode; - DevicesListPage(TbContext tbContext, {this.deviceType, this.active, this.searchMode = false}) : super(tbContext); + DevicesListPage(TbContext tbContext, + {this.deviceType, this.active, this.searchMode = false}) + : super(tbContext); @override _DevicesListPageState createState() => _DevicesListPageState(); - } class _DevicesListPageState extends TbPageState { - late final DeviceQueryController _deviceQueryController; @override void initState() { super.initState(); - _deviceQueryController = DeviceQueryController(deviceType: widget.deviceType, active: widget.active); + _deviceQueryController = DeviceQueryController( + deviceType: widget.deviceType, active: widget.active); } @override Widget build(BuildContext context) { - var devicesList = DevicesList(tbContext, _deviceQueryController, searchMode: widget.searchMode, displayDeviceImage: widget.deviceType == null); + var devicesList = DevicesList(tbContext, _deviceQueryController, + searchMode: widget.searchMode, + displayDeviceImage: widget.deviceType == null); PreferredSizeWidget appBar; if (widget.searchMode) { appBar = TbAppSearchBar( tbContext, - onSearch: (searchText) => _deviceQueryController.onSearchText(searchText), + onSearch: (searchText) => + _deviceQueryController.onSearchText(searchText), ); } else { - String titleText = widget.deviceType != null ? widget.deviceType! : 'All devices'; + String titleText = + widget.deviceType != null ? widget.deviceType! : 'All devices'; String? subTitleText; if (widget.active != null) { subTitleText = widget.active == true ? 'Active' : 'Inactive'; } - Column title = Column( - crossAxisAlignment: CrossAxisAlignment.start, - children: [ - Text(titleText, style: TextStyle( - fontWeight: FontWeight.w500, - fontSize: subTitleText != null ? 16 : 20, - height: subTitleText != null ? 20 / 16 : 24 / 20 - )), - if (subTitleText != null) - Text(subTitleText, style: TextStyle( - color: Theme.of(context).primaryTextTheme.headline6!.color!.withAlpha((0.38 * 255).ceil()), - fontSize: 12, - fontWeight: FontWeight.normal, - height: 16 / 12 - )) - ] - ); + Column title = + Column(crossAxisAlignment: CrossAxisAlignment.start, children: [ + Text(titleText, + style: TextStyle( + fontWeight: FontWeight.w500, + fontSize: subTitleText != null ? 16 : 20, + height: subTitleText != null ? 20 / 16 : 24 / 20)), + if (subTitleText != null) + Text(subTitleText, + style: TextStyle( + color: Theme.of(context) + .primaryTextTheme + .headline6! + .color! + .withAlpha((0.38 * 255).ceil()), + fontSize: 12, + fontWeight: FontWeight.normal, + height: 16 / 12)) + ]); - appBar = TbAppBar( - tbContext, - title: title, - actions: [ - IconButton( - icon: Icon( - Icons.search - ), - onPressed: () { - List params = []; - params.add('search=true'); - if (widget.deviceType != null) { - params.add('deviceType=${widget.deviceType}'); - } - if (widget.active != null) { - params.add('active=${widget.active}'); - } - navigateTo('/deviceList?${params.join('&')}'); - }, - ) - ]); + appBar = TbAppBar(tbContext, title: title, actions: [ + IconButton( + icon: Icon(Icons.search), + onPressed: () { + List params = []; + params.add('search=true'); + if (widget.deviceType != null) { + params.add('deviceType=${widget.deviceType}'); + } + if (widget.active != null) { + params.add('active=${widget.active}'); + } + navigateTo('/deviceList?${params.join('&')}'); + }, + ) + ]); } - return Scaffold( - appBar: appBar, - body: devicesList - ); + return Scaffold(appBar: appBar, body: devicesList); } @override @@ -94,5 +92,4 @@ class _DevicesListPageState extends TbPageState { _deviceQueryController.dispose(); super.dispose(); } - } diff --git a/lib/modules/device/devices_list_widget.dart b/lib/modules/device/devices_list_widget.dart index 7d01f70..035fe30 100644 --- a/lib/modules/device/devices_list_widget.dart +++ b/lib/modules/device/devices_list_widget.dart @@ -4,9 +4,11 @@ import 'package:thingsboard_app/core/entity/entities_list_widget.dart'; import 'package:thingsboard_app/modules/device/devices_base.dart'; import 'package:thingsboard_client/thingsboard_client.dart'; -class DevicesListWidget extends EntitiesListWidget with DevicesBase { - - DevicesListWidget(TbContext tbContext, {EntitiesListWidgetController? controller}): super(tbContext, controller: controller); +class DevicesListWidget extends EntitiesListWidget + with DevicesBase { + DevicesListWidget(TbContext tbContext, + {EntitiesListWidgetController? controller}) + : super(tbContext, controller: controller); @override void onViewAll() { @@ -14,6 +16,6 @@ class DevicesListWidget extends EntitiesListWidget } @override - PageKeyController createPageKeyController() => DeviceQueryController(pageSize: 5); - + PageKeyController createPageKeyController() => + DeviceQueryController(pageSize: 5); } diff --git a/lib/modules/device/devices_main_page.dart b/lib/modules/device/devices_main_page.dart index 346fe2d..e6c4e80 100644 --- a/lib/modules/device/devices_main_page.dart +++ b/lib/modules/device/devices_main_page.dart @@ -6,16 +6,14 @@ import 'package:thingsboard_app/modules/device/device_profiles_grid.dart'; import 'package:thingsboard_app/widgets/tb_app_bar.dart'; class DevicesMainPage extends TbContextWidget { - DevicesMainPage(TbContext tbContext) : super(tbContext); @override _DevicesMainPageState createState() => _DevicesMainPageState(); - } -class _DevicesMainPageState extends TbContextState with AutomaticKeepAliveClientMixin { - +class _DevicesMainPageState extends TbContextState + with AutomaticKeepAliveClientMixin { final PageLinkController _pageLinkController = PageLinkController(); @override @@ -28,12 +26,8 @@ class _DevicesMainPageState extends TbContextState with Automat super.build(context); var deviceProfilesList = DeviceProfilesGrid(tbContext, _pageLinkController); return Scaffold( - appBar: TbAppBar( - tbContext, - title: Text(deviceProfilesList.title) - ), - body: deviceProfilesList - ); + appBar: TbAppBar(tbContext, title: Text(deviceProfilesList.title)), + body: deviceProfilesList); } @override @@ -41,5 +35,4 @@ class _DevicesMainPageState extends TbContextState with Automat _pageLinkController.dispose(); super.dispose(); } - } diff --git a/lib/modules/device/devices_page.dart b/lib/modules/device/devices_page.dart index ad364c9..bb7e295 100644 --- a/lib/modules/device/devices_page.dart +++ b/lib/modules/device/devices_page.dart @@ -6,28 +6,21 @@ import 'package:thingsboard_app/modules/device/device_profiles_grid.dart'; import 'package:thingsboard_app/widgets/tb_app_bar.dart'; class DevicesPage extends TbPageWidget { - DevicesPage(TbContext tbContext) : super(tbContext); @override _DevicesPageState createState() => _DevicesPageState(); - } class _DevicesPageState extends TbPageState { - final PageLinkController _pageLinkController = PageLinkController(); @override Widget build(BuildContext context) { var deviceProfilesList = DeviceProfilesGrid(tbContext, _pageLinkController); return Scaffold( - appBar: TbAppBar( - tbContext, - title: Text(deviceProfilesList.title) - ), - body: deviceProfilesList - ); + appBar: TbAppBar(tbContext, title: Text(deviceProfilesList.title)), + body: deviceProfilesList); } @override @@ -35,5 +28,4 @@ class _DevicesPageState extends TbPageState { _pageLinkController.dispose(); super.dispose(); } - } diff --git a/lib/modules/home/home_page.dart b/lib/modules/home/home_page.dart index cd3dffc..3185c15 100644 --- a/lib/modules/home/home_page.dart +++ b/lib/modules/home/home_page.dart @@ -1,26 +1,24 @@ import 'package:flutter/material.dart'; -import 'package:flutter/widgets.dart'; import 'package:flutter_svg/flutter_svg.dart'; import 'package:thingsboard_app/constants/assets_path.dart'; import 'package:thingsboard_app/core/context/tb_context.dart'; import 'package:thingsboard_app/core/context/tb_context_widget.dart'; -import 'package:thingsboard_app/modules/dashboard/dashboard.dart' as dashboardUi; +import 'package:thingsboard_app/modules/dashboard/dashboard.dart' + as dashboardUi; import 'package:thingsboard_app/modules/dashboard/dashboards_grid.dart'; import 'package:thingsboard_app/modules/tenant/tenants_widget.dart'; import 'package:thingsboard_app/widgets/tb_app_bar.dart'; import 'package:thingsboard_client/thingsboard_client.dart'; class HomePage extends TbContextWidget { - HomePage(TbContext tbContext) : super(tbContext); @override _HomePageState createState() => _HomePageState(); - } -class _HomePageState extends TbContextState with AutomaticKeepAliveClientMixin { - +class _HomePageState extends TbContextState + with AutomaticKeepAliveClientMixin { @override void initState() { super.initState(); @@ -50,33 +48,29 @@ class _HomePageState extends TbContextState with AutomaticKeepAliveCli height: 24, child: SvgPicture.asset(ThingsboardImage.thingsBoardWithTitle, color: Theme.of(context).primaryColor, - semanticsLabel: 'ThingsBoard Logo') - ) - ), + semanticsLabel: 'ThingsBoard Logo'))), actions: [ - if (tbClient.isSystemAdmin()) IconButton( - icon: Icon( - Icons.search - ), - onPressed: () { - navigateTo('/tenants?search=true'); - }, - ) + if (tbClient.isSystemAdmin()) + IconButton( + icon: Icon(Icons.search), + onPressed: () { + navigateTo('/tenants?search=true'); + }, + ) ], ), - body: Builder( - builder: (context) { - if (dashboardState) { - return _buildDashboardHome(context, homeDashboard!); - } else { - return _buildDefaultHome(context); - } - } - ), + body: Builder(builder: (context) { + if (dashboardState) { + return _buildDashboardHome(context, homeDashboard!); + } else { + return _buildDefaultHome(context); + } + }), ); } - Widget _buildDashboardHome(BuildContext context, HomeDashboardInfo dashboard) { + Widget _buildDashboardHome( + BuildContext context, HomeDashboardInfo dashboard) { return HomeDashboard(tbContext, dashboard); } @@ -91,31 +85,24 @@ class _HomePageState extends TbContextState with AutomaticKeepAliveCli Widget _buildSysAdminHome(BuildContext context) { return TenantsWidget(tbContext); } - } class HomeDashboard extends TbContextWidget { - final HomeDashboardInfo dashboard; HomeDashboard(TbContext tbContext, this.dashboard) : super(tbContext); @override _HomeDashboardState createState() => _HomeDashboardState(); - } class _HomeDashboardState extends TbContextState { - @override Widget build(BuildContext context) { - return dashboardUi.Dashboard(tbContext, - home: true, - controllerCallback: (controller) { - controller.openDashboard(widget.dashboard.dashboardId!.id!, - hideToolbar: widget.dashboard.hideDashboardToolbar); - } - ); + return dashboardUi.Dashboard(tbContext, home: true, + controllerCallback: (controller) { + controller.openDashboard(widget.dashboard.dashboardId!.id!, + hideToolbar: widget.dashboard.hideDashboardToolbar); + }); } - } diff --git a/lib/modules/home/home_routes.dart b/lib/modules/home/home_routes.dart index 4657ea4..6dc2cb3 100644 --- a/lib/modules/home/home_routes.dart +++ b/lib/modules/home/home_routes.dart @@ -5,8 +5,8 @@ import 'package:thingsboard_app/core/context/tb_context.dart'; import 'package:thingsboard_app/modules/main/main_page.dart'; class HomeRoutes extends TbRoutes { - - late var homeHandler = Handler(handlerFunc: (BuildContext? context, Map params) { + late var homeHandler = Handler( + handlerFunc: (BuildContext? context, Map params) { return MainPage(tbContext, path: '/home'); }); @@ -16,5 +16,4 @@ class HomeRoutes extends TbRoutes { void doRegisterRoutes(router) { router.define("/home", handler: homeHandler); } - } diff --git a/lib/modules/main/main_page.dart b/lib/modules/main/main_page.dart index 3991d44..1766444 100644 --- a/lib/modules/main/main_page.dart +++ b/lib/modules/main/main_page.dart @@ -1,6 +1,4 @@ -import 'package:flutter/gestures.dart'; import 'package:flutter/material.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'; @@ -15,17 +13,18 @@ class TbMainNavigationItem { final Icon icon; final String path; - TbMainNavigationItem({ - required this.page, - required this.title, - required this.icon, - required this.path - }); + TbMainNavigationItem( + {required this.page, + required this.title, + required this.icon, + required this.path}); static Map> mainPageStateMap = { Authority.SYS_ADMIN: Set.unmodifiable(['/home', '/more']), - Authority.TENANT_ADMIN: Set.unmodifiable(['/home', '/alarms', '/devices', '/more']), - Authority.CUSTOMER_USER: Set.unmodifiable(['/home', '/alarms', '/devices', '/more']), + Authority.TENANT_ADMIN: + Set.unmodifiable(['/home', '/alarms', '/devices', '/more']), + Authority.CUSTOMER_USER: + Set.unmodifiable(['/home', '/alarms', '/devices', '/more']), }; static bool isMainPageState(TbContext tbContext, String path) { @@ -44,40 +43,38 @@ class TbMainNavigationItem { page: HomePage(tbContext), title: 'Home', icon: Icon(Icons.home), - path: '/home' - ) + path: '/home') ]; - switch(tbContext.tbClient.getAuthUser()!.authority) { + switch (tbContext.tbClient.getAuthUser()!.authority) { case Authority.SYS_ADMIN: break; case Authority.TENANT_ADMIN: case Authority.CUSTOMER_USER: - items.addAll([ - TbMainNavigationItem( - page: AlarmsPage(tbContext), - title: 'Alarms', - icon: Icon(Icons.notifications), - path: '/alarms' - ), - TbMainNavigationItem( - page: DevicesMainPage(tbContext), - title: 'Devices', - icon: Icon(Icons.devices_other), - path: '/devices' - ) - ]); - break; + items.addAll([ + TbMainNavigationItem( + page: AlarmsPage(tbContext), + title: 'Alarms', + icon: Icon(Icons.notifications), + path: '/alarms'), + TbMainNavigationItem( + page: DevicesMainPage(tbContext), + title: 'Devices', + icon: Icon(Icons.devices_other), + path: '/devices') + ]); + break; case Authority.REFRESH_TOKEN: break; case Authority.ANONYMOUS: break; + case Authority.PRE_VERIFICATION_TOKEN: + break; } items.add(TbMainNavigationItem( page: MorePage(tbContext), title: 'More', icon: Icon(Icons.menu), - path: '/more' - )); + path: '/more')); return items; } else { return []; @@ -86,19 +83,18 @@ class TbMainNavigationItem { } class MainPage extends TbPageWidget { - final String _path; - MainPage(TbContext tbContext, {required String path}): - _path = path, super(tbContext); + MainPage(TbContext tbContext, {required String path}) + : _path = path, + super(tbContext); @override _MainPageState createState() => _MainPageState(); - } -class _MainPageState extends TbPageState with TbMainState, TickerProviderStateMixin { - +class _MainPageState extends TbPageState + with TbMainState, TickerProviderStateMixin { late ValueNotifier _currentIndexNotifier; late final List _tabItems; late TabController _tabController; @@ -108,7 +104,8 @@ class _MainPageState extends TbPageState with TbMainState, TickerProvi super.initState(); _tabItems = TbMainNavigationItem.getItems(tbContext); int currentIndex = _indexFromPath(widget._path); - _tabController = TabController(initialIndex: currentIndex, length: _tabItems.length, vsync: this); + _tabController = TabController( + initialIndex: currentIndex, length: _tabItems.length, vsync: this); _currentIndexNotifier = ValueNotifier(currentIndex); _tabController.animation!.addListener(_onTabAnimation); } @@ -119,7 +116,7 @@ class _MainPageState extends TbPageState with TbMainState, TickerProvi super.dispose(); } - _onTabAnimation () { + _onTabAnimation() { var value = _tabController.animation!.value; var targetIndex; if (value >= _tabController.previousIndex) { @@ -142,24 +139,24 @@ class _MainPageState extends TbPageState with TbMainState, TickerProvi }, child: Scaffold( body: TabBarView( - physics: tbContext.homeDashboard != null ? NeverScrollableScrollPhysics() : null, + physics: tbContext.homeDashboard != null + ? NeverScrollableScrollPhysics() + : null, controller: _tabController, children: _tabItems.map((item) => item.page).toList(), ), bottomNavigationBar: ValueListenableBuilder( - valueListenable: _currentIndexNotifier, - builder: (context, index, child) => BottomNavigationBar( - type: BottomNavigationBarType.fixed, - currentIndex: index, - onTap: (int index) => _setIndex(index) /*_currentIndex = index*/, - items: _tabItems.map((item) => BottomNavigationBarItem( - icon: item.icon, - label: item.title - )).toList() - ), - ) - ) - ); + valueListenable: _currentIndexNotifier, + builder: (context, index, child) => BottomNavigationBar( + type: BottomNavigationBarType.fixed, + currentIndex: index, + onTap: (int index) => + _setIndex(index) /*_currentIndex = index*/, + items: _tabItems + .map((item) => BottomNavigationBarItem( + icon: item.icon, label: item.title)) + .toList()), + ))); } int _indexFromPath(String path) { @@ -175,7 +172,7 @@ class _MainPageState extends TbPageState with TbMainState, TickerProvi navigateToPath(String path) { int targetIndex = _indexFromPath(path); _setIndex(targetIndex); - } + } @override bool isHomePage() { @@ -185,5 +182,4 @@ class _MainPageState extends TbPageState with TbMainState, TickerProvi _setIndex(int index) { _tabController.index = index; } - } diff --git a/lib/modules/more/more_page.dart b/lib/modules/more/more_page.dart index 324ea6d..6f9ae1d 100644 --- a/lib/modules/more/more_page.dart +++ b/lib/modules/more/more_page.dart @@ -4,129 +4,108 @@ import 'package:thingsboard_app/core/context/tb_context_widget.dart'; import 'package:thingsboard_client/thingsboard_client.dart'; class MorePage extends TbContextWidget { - MorePage(TbContext tbContext) : super(tbContext); @override _MorePageState createState() => _MorePageState(); - } class _MorePageState extends TbContextState { - @override Widget build(BuildContext context) { return Scaffold( backgroundColor: Colors.white, body: Container( padding: EdgeInsets.fromLTRB(16, 40, 16, 20), - child: - Column( - crossAxisAlignment: CrossAxisAlignment.start, - children: [ - Row( - crossAxisAlignment: CrossAxisAlignment.start, - children: [ - Icon(Icons.account_circle, size: 48, color: Color(0xFFAFAFAF)), - Spacer(), - IconButton(icon: Icon(Icons.settings, color: Color(0xFFAFAFAF)), onPressed: () async { - await navigateTo('/profile'); - setState(() {}); - }) - ], - ), - SizedBox(height: 22), - Text(_getUserDisplayName(), - style: TextStyle( - color: Color(0xFF282828), - fontWeight: FontWeight.w500, - fontSize: 20, - height: 23 / 20 - ) - ), - SizedBox(height: 2), - Text(_getAuthorityName(), - style: TextStyle( - color: Color(0xFFAFAFAF), - fontWeight: FontWeight.normal, - fontSize: 14, - height: 16 / 14 - ) - ), - SizedBox(height: 24), - Divider(color: Color(0xFFEDEDED)), - SizedBox(height: 8), - buildMoreMenuItems(context), - SizedBox(height: 8), - Divider(color: Color(0xFFEDEDED)), - SizedBox(height: 8), - GestureDetector( + child: Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + Row( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + Icon(Icons.account_circle, + size: 48, color: Color(0xFFAFAFAF)), + Spacer(), + IconButton( + icon: Icon(Icons.settings, color: Color(0xFFAFAFAF)), + onPressed: () async { + await navigateTo('/profile'); + setState(() {}); + }) + ], + ), + SizedBox(height: 22), + Text(_getUserDisplayName(), + style: TextStyle( + color: Color(0xFF282828), + fontWeight: FontWeight.w500, + fontSize: 20, + height: 23 / 20)), + SizedBox(height: 2), + Text(_getAuthorityName(), + style: TextStyle( + color: Color(0xFFAFAFAF), + fontWeight: FontWeight.normal, + fontSize: 14, + height: 16 / 14)), + SizedBox(height: 24), + Divider(color: Color(0xFFEDEDED)), + SizedBox(height: 8), + buildMoreMenuItems(context), + SizedBox(height: 8), + Divider(color: Color(0xFFEDEDED)), + SizedBox(height: 8), + GestureDetector( behavior: HitTestBehavior.opaque, child: Container( - height: 48, + height: 48, child: Padding( - padding: EdgeInsets.symmetric(vertical: 0, horizontal: 18), - child: Row( - mainAxisSize: MainAxisSize.max, - children: [ - Icon(Icons.logout, color: Color(0xFFE04B2F)), - SizedBox(width: 34), - Text('Log out', - style: TextStyle( - color: Color(0xFFE04B2F), - fontStyle: FontStyle.normal, - fontWeight: FontWeight.w500, - fontSize: 14, - height: 20 / 14 - )) - ] - ) - ) - ), + padding: + EdgeInsets.symmetric(vertical: 0, horizontal: 18), + child: Row(mainAxisSize: MainAxisSize.max, children: [ + Icon(Icons.logout, color: Color(0xFFE04B2F)), + SizedBox(width: 34), + Text('Log out', + style: TextStyle( + color: Color(0xFFE04B2F), + fontStyle: FontStyle.normal, + fontWeight: FontWeight.w500, + fontSize: 14, + height: 20 / 14)) + ]))), onTap: () { tbClient.logout( requestConfig: RequestConfig(ignoreErrors: true)); - } - ) - ], - ), - ) - ); + }) + ], + ), + )); } Widget buildMoreMenuItems(BuildContext context) { List items = MoreMenuItem.getItems(tbContext).map((menuItem) { return GestureDetector( - behavior: HitTestBehavior.opaque, - child: Container( - height: 48, - child: Padding( - padding: EdgeInsets.symmetric(vertical: 0, horizontal: 18), - child: Row( - mainAxisSize: MainAxisSize.max, - children: [ - Icon(menuItem.icon, color: Color(0xFF282828)), - SizedBox(width: 34), - Text(menuItem.title, - style: TextStyle( - color: Color(0xFF282828), - fontStyle: FontStyle.normal, - fontWeight: FontWeight.w500, - fontSize: 14, - height: 20 / 14 - )) - ] - ) - ) - ), - onTap: () { - navigateTo(menuItem.path); - } - ); + behavior: HitTestBehavior.opaque, + child: Container( + height: 48, + child: Padding( + padding: EdgeInsets.symmetric(vertical: 0, horizontal: 18), + child: Row(mainAxisSize: MainAxisSize.max, children: [ + Icon(menuItem.icon, color: Color(0xFF282828)), + SizedBox(width: 34), + Text(menuItem.title, + style: TextStyle( + color: Color(0xFF282828), + fontStyle: FontStyle.normal, + fontWeight: FontWeight.w500, + fontSize: 14, + height: 20 / 14)) + ]))), + onTap: () { + navigateTo(menuItem.path); + }); }).toList(); - return Column( - children: items - ); + return Column(children: items); } String _getUserDisplayName() { @@ -156,7 +135,7 @@ class _MorePageState extends TbContextState { var name = ''; if (user != null) { var authority = user.authority; - switch(authority) { + switch (authority) { case Authority.SYS_ADMIN: name = 'System Administrator'; break; @@ -166,11 +145,12 @@ class _MorePageState extends TbContextState { case Authority.CUSTOMER_USER: name = 'Customer'; break; + default: + break; } } return name; } - } class MoreMenuItem { @@ -178,11 +158,7 @@ class MoreMenuItem { final IconData icon; final String path; - MoreMenuItem({ - required this.title, - required this.icon, - required this.path - }); + MoreMenuItem({required this.title, required this.icon, required this.path}); static List getItems(TbContext tbContext) { if (tbContext.isAuthenticated) { @@ -195,33 +171,25 @@ class MoreMenuItem { MoreMenuItem( title: 'Customers', icon: Icons.supervisor_account, - path: '/customers' - ), - MoreMenuItem( - title: 'Assets', - icon: Icons.domain, - path: '/assets' - ), + path: '/customers'), + MoreMenuItem(title: 'Assets', icon: Icons.domain, path: '/assets'), MoreMenuItem( title: 'Audit Logs', icon: Icons.track_changes, - path: '/auditLogs' - ) + path: '/auditLogs') ]); break; case Authority.CUSTOMER_USER: items.addAll([ - MoreMenuItem( - title: 'Assets', - icon: Icons.domain, - path: '/assets' - ) + MoreMenuItem(title: 'Assets', icon: Icons.domain, path: '/assets') ]); break; case Authority.REFRESH_TOKEN: break; case Authority.ANONYMOUS: break; + case Authority.PRE_VERIFICATION_TOKEN: + break; } return items; } else { diff --git a/lib/modules/profile/change_password_page.dart b/lib/modules/profile/change_password_page.dart index 410ad19..4da2b42 100644 --- a/lib/modules/profile/change_password_page.dart +++ b/lib/modules/profile/change_password_page.dart @@ -7,16 +7,13 @@ import 'package:thingsboard_app/widgets/tb_app_bar.dart'; import 'package:thingsboard_app/widgets/tb_progress_indicator.dart'; class ChangePasswordPage extends TbContextWidget { - ChangePasswordPage(TbContext tbContext) : super(tbContext); @override _ChangePasswordPageState createState() => _ChangePasswordPageState(); - } class _ChangePasswordPageState extends TbContextState { - final _isLoadingNotifier = ValueNotifier(false); final _showCurrentPasswordNotifier = ValueNotifier(false); @@ -39,96 +36,105 @@ class _ChangePasswordPageState extends TbContextState { child: Padding( padding: EdgeInsets.all(16), child: SingleChildScrollView( - child: FormBuilder( - key: _changePasswordFormKey, - autovalidateMode: AutovalidateMode.disabled, - child: Column( - crossAxisAlignment: CrossAxisAlignment.stretch, - children: [ - SizedBox(height: 16), - ValueListenableBuilder( + child: FormBuilder( + key: _changePasswordFormKey, + autovalidateMode: AutovalidateMode.disabled, + child: Column( + crossAxisAlignment: CrossAxisAlignment.stretch, + children: [ + SizedBox(height: 16), + ValueListenableBuilder( valueListenable: _showCurrentPasswordNotifier, - builder: (BuildContext context, bool showPassword, child) { + builder: (BuildContext context, bool showPassword, + child) { return FormBuilderTextField( name: 'currentPassword', obscureText: !showPassword, autofocus: true, validator: FormBuilderValidators.compose([ - FormBuilderValidators.required(context, errorText: 'Current password is required.') + FormBuilderValidators.required( + errorText: + 'Current password is required.') ]), decoration: InputDecoration( suffixIcon: IconButton( - icon: Icon(showPassword ? Icons.visibility : Icons.visibility_off), + icon: Icon(showPassword + ? Icons.visibility + : Icons.visibility_off), onPressed: () { - _showCurrentPasswordNotifier.value = !_showCurrentPasswordNotifier.value; + _showCurrentPasswordNotifier.value = + !_showCurrentPasswordNotifier + .value; }, ), border: OutlineInputBorder(), - labelText: 'Current password *' - ), + labelText: 'Current password *'), ); - } - ), - SizedBox(height: 24), - ValueListenableBuilder( - valueListenable: _showNewPasswordNotifier, - builder: (BuildContext context, bool showPassword, child) { - return FormBuilderTextField( - name: 'newPassword', - obscureText: !showPassword, - validator: FormBuilderValidators.compose([ - FormBuilderValidators.required(context, errorText: 'New password is required.') - ]), - decoration: InputDecoration( - suffixIcon: IconButton( - icon: Icon(showPassword ? Icons.visibility : Icons.visibility_off), - onPressed: () { - _showNewPasswordNotifier.value = !_showNewPasswordNotifier.value; - }, - ), - border: OutlineInputBorder(), - labelText: 'New password *' - ), - ); - } - ), - SizedBox(height: 24), - ValueListenableBuilder( - valueListenable: _showNewPassword2Notifier, - builder: (BuildContext context, bool showPassword, child) { - return FormBuilderTextField( - name: 'newPassword2', - obscureText: !showPassword, - validator: FormBuilderValidators.compose([ - FormBuilderValidators.required(context, errorText: 'New password again is required.') - ]), - decoration: InputDecoration( - suffixIcon: IconButton( - icon: Icon(showPassword ? Icons.visibility : Icons.visibility_off), - onPressed: () { - _showNewPassword2Notifier.value = !_showNewPassword2Notifier.value; - }, - ), - border: OutlineInputBorder(), - labelText: 'New password again *' - ), - ); - } - ), - SizedBox(height: 24), - ElevatedButton( - style: ElevatedButton.styleFrom(padding: EdgeInsets.all(16), - alignment: Alignment.centerLeft), - onPressed: () { - _changePassword(); - }, - child: Center(child: Text('Change Password')) - ) - ] - ), - ) - ) - ), + }), + SizedBox(height: 24), + ValueListenableBuilder( + valueListenable: _showNewPasswordNotifier, + builder: (BuildContext context, bool showPassword, + child) { + return FormBuilderTextField( + name: 'newPassword', + obscureText: !showPassword, + validator: FormBuilderValidators.compose([ + FormBuilderValidators.required( + errorText: 'New password is required.') + ]), + decoration: InputDecoration( + suffixIcon: IconButton( + icon: Icon(showPassword + ? Icons.visibility + : Icons.visibility_off), + onPressed: () { + _showNewPasswordNotifier.value = + !_showNewPasswordNotifier.value; + }, + ), + border: OutlineInputBorder(), + labelText: 'New password *'), + ); + }), + SizedBox(height: 24), + ValueListenableBuilder( + valueListenable: _showNewPassword2Notifier, + builder: (BuildContext context, bool showPassword, + child) { + return FormBuilderTextField( + name: 'newPassword2', + obscureText: !showPassword, + validator: FormBuilderValidators.compose([ + FormBuilderValidators.required( + errorText: + 'New password again is required.') + ]), + decoration: InputDecoration( + suffixIcon: IconButton( + icon: Icon(showPassword + ? Icons.visibility + : Icons.visibility_off), + onPressed: () { + _showNewPassword2Notifier.value = + !_showNewPassword2Notifier.value; + }, + ), + border: OutlineInputBorder(), + labelText: 'New password again *'), + ); + }), + SizedBox(height: 24), + ElevatedButton( + style: ElevatedButton.styleFrom( + padding: EdgeInsets.all(16), + alignment: Alignment.centerLeft), + onPressed: () { + _changePassword(); + }, + child: Center(child: Text('Change Password'))) + ]), + ))), ), ValueListenableBuilder( valueListenable: _isLoadingNotifier, @@ -136,18 +142,15 @@ class _ChangePasswordPageState extends TbContextState { if (loading) { return SizedBox.expand( child: Container( - color: Color(0x99FFFFFF), - child: Center(child: TbProgressIndicator(size: 50.0)), - ) - ); + color: Color(0x99FFFFFF), + child: Center(child: TbProgressIndicator(size: 50.0)), + )); } else { return SizedBox.shrink(); } - } - ) + }) ], - ) - ); + )); } Future _changePassword() async { @@ -165,11 +168,10 @@ class _ChangePasswordPageState extends TbContextState { await Future.delayed(Duration(milliseconds: 300)); await tbClient.changePassword(currentPassword, newPassword); pop(true); - } catch(e) { + } catch (e) { _isLoadingNotifier.value = false; } } } } - } diff --git a/lib/modules/profile/profile_page.dart b/lib/modules/profile/profile_page.dart index bd570b8..6ae0a6b 100644 --- a/lib/modules/profile/profile_page.dart +++ b/lib/modules/profile/profile_page.dart @@ -1,5 +1,4 @@ import 'package:flutter/material.dart'; -import 'package:flutter/widgets.dart'; import 'package:flutter_form_builder/flutter_form_builder.dart'; import 'package:form_builder_validators/form_builder_validators.dart'; import 'package:thingsboard_app/modules/profile/change_password_page.dart'; @@ -11,18 +10,17 @@ import 'package:thingsboard_app/widgets/tb_progress_indicator.dart'; import 'package:thingsboard_client/thingsboard_client.dart'; class ProfilePage extends TbPageWidget { - final bool _fullscreen; - ProfilePage(TbContext tbContext, {bool fullscreen = false}) : _fullscreen = fullscreen, super(tbContext); + ProfilePage(TbContext tbContext, {bool fullscreen = false}) + : _fullscreen = fullscreen, + super(tbContext); @override _ProfilePageState createState() => _ProfilePageState(); - } class _ProfilePageState extends TbPageState { - final _isLoadingNotifier = ValueNotifier(true); final _profileFormKey = GlobalKey(); @@ -49,21 +47,16 @@ class _ProfilePageState extends TbPageState { title: const Text('Profile'), actions: [ IconButton( - icon: Icon( - Icons.check - ), + icon: Icon(Icons.check), onPressed: () { _saveProfile(); - } - ), - if (widget._fullscreen) IconButton( - icon: Icon( - Icons.logout - ), - onPressed: () { - tbClient.logout(); - } - ) + }), + if (widget._fullscreen) + IconButton( + icon: Icon(Icons.logout), + onPressed: () { + tbClient.logout(); + }) ], ), body: Stack( @@ -72,73 +65,66 @@ class _ProfilePageState extends TbPageState { child: Padding( padding: EdgeInsets.all(16), child: SingleChildScrollView( - child: FormBuilder( - key: _profileFormKey, - autovalidateMode: AutovalidateMode.onUserInteraction, - child: Column( - crossAxisAlignment: CrossAxisAlignment.stretch, - children: [ - SizedBox(height: 16), - FormBuilderTextField( - name: 'email', - validator: FormBuilderValidators.compose([ - FormBuilderValidators.required(context, errorText: 'Email is required.'), - FormBuilderValidators.email(context, errorText: 'Invalid email format.') - ]), - decoration: InputDecoration( - border: OutlineInputBorder(), - labelText: 'Email *' - ), - ), - SizedBox(height: 24), - FormBuilderTextField( - name: 'firstName', - decoration: InputDecoration( - border: OutlineInputBorder(), - labelText: 'First Name' - ), - ), - SizedBox(height: 24), - FormBuilderTextField( - name: 'lastName', - decoration: InputDecoration( - border: OutlineInputBorder(), - labelText: 'Last Name' - ), - ), - SizedBox(height: 24), - OutlinedButton( - style: OutlinedButton.styleFrom(padding: EdgeInsets.all(16), - alignment: Alignment.centerLeft), - onPressed: () { - _changePassword(); - }, - child: Center(child: Text('Change Password')) - ) - ] - ), - ) - ) - ), + child: FormBuilder( + key: _profileFormKey, + autovalidateMode: AutovalidateMode.onUserInteraction, + child: Column( + crossAxisAlignment: CrossAxisAlignment.stretch, + children: [ + SizedBox(height: 16), + FormBuilderTextField( + name: 'email', + validator: FormBuilderValidators.compose([ + FormBuilderValidators.required( + errorText: 'Email is required.'), + FormBuilderValidators.email( + errorText: 'Invalid email format.') + ]), + decoration: InputDecoration( + border: OutlineInputBorder(), + labelText: 'Email *'), + ), + SizedBox(height: 24), + FormBuilderTextField( + name: 'firstName', + decoration: InputDecoration( + border: OutlineInputBorder(), + labelText: 'First Name'), + ), + SizedBox(height: 24), + FormBuilderTextField( + name: 'lastName', + decoration: InputDecoration( + border: OutlineInputBorder(), + labelText: 'Last Name'), + ), + SizedBox(height: 24), + OutlinedButton( + style: OutlinedButton.styleFrom( + padding: EdgeInsets.all(16), + alignment: Alignment.centerLeft), + onPressed: () { + _changePassword(); + }, + child: Center(child: Text('Change Password'))) + ]), + ))), ), ValueListenableBuilder( valueListenable: _isLoadingNotifier, builder: (BuildContext context, bool loading, child) { - if (loading) { - return SizedBox.expand( + if (loading) { + return SizedBox.expand( child: Container( - color: Color(0x99FFFFFF), - child: Center(child: TbProgressIndicator(size: 50.0)), - ) - ); - } else { - return SizedBox.shrink(); - } - } - ) + color: Color(0x99FFFFFF), + child: Center(child: TbProgressIndicator(size: 50.0)), + )); + } else { + return SizedBox.shrink(); + } + }) ], - ) - ); + )); } Future _loadUser() async { @@ -170,15 +156,18 @@ class _ProfilePageState extends TbPageState { _setUser(); await Future.delayed(Duration(milliseconds: 300)); _isLoadingNotifier.value = false; - showSuccessNotification('Profile successfully updated', duration: Duration(milliseconds: 1500)); + showSuccessNotification('Profile successfully updated', + duration: Duration(milliseconds: 1500)); } } } _changePassword() async { - var res = await tbContext.showFullScreenDialog(new ChangePasswordPage(tbContext)); + var res = await tbContext + .showFullScreenDialog(new ChangePasswordPage(tbContext)); if (res == true) { - showSuccessNotification('Password successfully changed', duration: Duration(milliseconds: 1500)); + showSuccessNotification('Password successfully changed', + duration: Duration(milliseconds: 1500)); } } } diff --git a/lib/modules/profile/profile_routes.dart b/lib/modules/profile/profile_routes.dart index 11c2587..1d48089 100644 --- a/lib/modules/profile/profile_routes.dart +++ b/lib/modules/profile/profile_routes.dart @@ -6,8 +6,8 @@ import 'package:thingsboard_app/core/context/tb_context.dart'; import 'profile_page.dart'; class ProfileRoutes extends TbRoutes { - - late var profileHandler = Handler(handlerFunc: (BuildContext? context, Map params) { + late var profileHandler = Handler( + handlerFunc: (BuildContext? context, Map params) { var fullscreen = params['fullscreen']?.first == 'true'; return ProfilePage(tbContext, fullscreen: fullscreen); }); @@ -18,5 +18,4 @@ class ProfileRoutes extends TbRoutes { void doRegisterRoutes(router) { router.define("/profile", handler: profileHandler); } - } diff --git a/lib/modules/tenant/tenant_details_page.dart b/lib/modules/tenant/tenant_details_page.dart index 275683e..31e0d66 100644 --- a/lib/modules/tenant/tenant_details_page.dart +++ b/lib/modules/tenant/tenant_details_page.dart @@ -3,13 +3,14 @@ import 'package:thingsboard_app/core/entity/entity_details_page.dart'; import 'package:thingsboard_client/thingsboard_client.dart'; class TenantDetailsPage extends ContactBasedDetailsPage { - - TenantDetailsPage(TbContext tbContext, String tenantId): - super(tbContext, entityId: tenantId, defaultTitle: 'Tenant', subTitle: 'Tenant details'); + TenantDetailsPage(TbContext tbContext, String tenantId) + : super(tbContext, + entityId: tenantId, + defaultTitle: 'Tenant', + subTitle: 'Tenant details'); @override Future fetchEntity(String tenantId) { return tbClient.getTenantService().getTenant(tenantId); } - } diff --git a/lib/modules/tenant/tenant_routes.dart b/lib/modules/tenant/tenant_routes.dart index 02c3919..365667d 100644 --- a/lib/modules/tenant/tenant_routes.dart +++ b/lib/modules/tenant/tenant_routes.dart @@ -6,13 +6,14 @@ import 'tenant_details_page.dart'; import 'tenants_page.dart'; class TenantRoutes extends TbRoutes { - - late var tenantsHandler = Handler(handlerFunc: (BuildContext? context, Map params) { + late var tenantsHandler = Handler( + handlerFunc: (BuildContext? context, Map params) { var searchMode = params['search']?.first == 'true'; return TenantsPage(tbContext, searchMode: searchMode); }); - late var tenantDetailsHandler = Handler(handlerFunc: (BuildContext? context, Map params) { + late var tenantDetailsHandler = Handler( + handlerFunc: (BuildContext? context, Map params) { return TenantDetailsPage(tbContext, params["id"][0]); }); @@ -23,5 +24,4 @@ class TenantRoutes extends TbRoutes { router.define("/tenants", handler: tenantsHandler); router.define("/tenant/:id", handler: tenantDetailsHandler); } - } diff --git a/lib/modules/tenant/tenants_base.dart b/lib/modules/tenant/tenants_base.dart index 4ecb472..a61dabe 100644 --- a/lib/modules/tenant/tenants_base.dart +++ b/lib/modules/tenant/tenants_base.dart @@ -1,8 +1,7 @@ import 'package:thingsboard_app/core/entity/entities_base.dart'; import 'package:thingsboard_client/thingsboard_client.dart'; -mixin TenantsBase on EntitiesBase { - +mixin TenantsBase on EntitiesBase { @override String get title => 'Tenants'; @@ -18,5 +17,4 @@ mixin TenantsBase on EntitiesBase { void onEntityTap(Tenant tenant) { navigateTo('/tenant/${tenant.id!.id}'); } - } diff --git a/lib/modules/tenant/tenants_list.dart b/lib/modules/tenant/tenants_list.dart index 42265e6..543d33b 100644 --- a/lib/modules/tenant/tenants_list.dart +++ b/lib/modules/tenant/tenants_list.dart @@ -5,8 +5,10 @@ import 'package:thingsboard_client/thingsboard_client.dart'; import 'tenants_base.dart'; -class TenantsList extends BaseEntitiesWidget with TenantsBase, ContactBasedBase, EntitiesListStateBase { - - TenantsList(TbContext tbContext, PageKeyController pageKeyController, {searchMode = false}) : super(tbContext, pageKeyController, searchMode: searchMode); - +class TenantsList extends BaseEntitiesWidget + with TenantsBase, ContactBasedBase, EntitiesListStateBase { + TenantsList( + TbContext tbContext, PageKeyController pageKeyController, + {searchMode = false}) + : super(tbContext, pageKeyController, searchMode: searchMode); } diff --git a/lib/modules/tenant/tenants_page.dart b/lib/modules/tenant/tenants_page.dart index 7fd3514..77e7b93 100644 --- a/lib/modules/tenant/tenants_page.dart +++ b/lib/modules/tenant/tenants_page.dart @@ -7,23 +7,22 @@ import 'package:thingsboard_app/widgets/tb_app_bar.dart'; import 'tenants_list.dart'; class TenantsPage extends TbPageWidget { - final bool searchMode; - TenantsPage(TbContext tbContext, {this.searchMode = false}) : super(tbContext); + TenantsPage(TbContext tbContext, {this.searchMode = false}) + : super(tbContext); @override _TenantsPageState createState() => _TenantsPageState(); - } class _TenantsPageState extends TbPageState { - final PageLinkController _pageLinkController = PageLinkController(); @override Widget build(BuildContext context) { - var tenantsList = TenantsList(tbContext, _pageLinkController, searchMode: widget.searchMode); + var tenantsList = TenantsList(tbContext, _pageLinkController, + searchMode: widget.searchMode); PreferredSizeWidget appBar; if (widget.searchMode) { appBar = TbAppSearchBar( @@ -31,24 +30,16 @@ class _TenantsPageState extends TbPageState { onSearch: (searchText) => _pageLinkController.onSearchText(searchText), ); } else { - appBar = TbAppBar( - tbContext, - title: Text(tenantsList.title), - actions: [ - IconButton( - icon: Icon( - Icons.search - ), - onPressed: () { - navigateTo('/tenants?search=true'); - }, - ) - ]); + appBar = TbAppBar(tbContext, title: Text(tenantsList.title), actions: [ + IconButton( + icon: Icon(Icons.search), + onPressed: () { + navigateTo('/tenants?search=true'); + }, + ) + ]); } - return Scaffold( - appBar: appBar, - body: tenantsList - ); + return Scaffold(appBar: appBar, body: tenantsList); } @override @@ -56,5 +47,4 @@ class _TenantsPageState extends TbPageState { _pageLinkController.dispose(); super.dispose(); } - } diff --git a/lib/modules/tenant/tenants_widget.dart b/lib/modules/tenant/tenants_widget.dart index b50e68e..7f5f66e 100644 --- a/lib/modules/tenant/tenants_widget.dart +++ b/lib/modules/tenant/tenants_widget.dart @@ -6,21 +6,18 @@ import 'package:thingsboard_app/core/entity/entities_base.dart'; import 'tenants_list.dart'; class TenantsWidget extends TbContextWidget { - TenantsWidget(TbContext tbContext) : super(tbContext); @override _TenantsWidgetState createState() => _TenantsWidgetState(); - } class _TenantsWidgetState extends TbContextState { - final PageLinkController _pageLinkController = PageLinkController(); @override Widget build(BuildContext context) { - return TenantsList(tbContext, _pageLinkController); + return TenantsList(tbContext, _pageLinkController); } @override @@ -28,5 +25,4 @@ class _TenantsWidgetState extends TbContextState { _pageLinkController.dispose(); super.dispose(); } - } diff --git a/lib/utils/services/tb_secure_storage.dart b/lib/utils/services/_tb_secure_storage.dart similarity index 99% rename from lib/utils/services/tb_secure_storage.dart rename to lib/utils/services/_tb_secure_storage.dart index 9fe091d..caa315c 100644 --- a/lib/utils/services/tb_secure_storage.dart +++ b/lib/utils/services/_tb_secure_storage.dart @@ -4,7 +4,6 @@ import 'package:thingsboard_client/thingsboard_client.dart'; TbStorage createAppStorage() => TbSecureStorage(); class TbSecureStorage implements TbStorage { - final flutterStorage = FlutterSecureStorage(); @override @@ -21,5 +20,4 @@ class TbSecureStorage implements TbStorage { Future setItem(String key, String value) async { return await flutterStorage.write(key: key, value: value); } - } diff --git a/lib/utils/services/tb_web_local_storage.dart b/lib/utils/services/_tb_web_local_storage.dart similarity index 80% rename from lib/utils/services/tb_web_local_storage.dart rename to lib/utils/services/_tb_web_local_storage.dart index 5358776..5d1647e 100644 --- a/lib/utils/services/tb_web_local_storage.dart +++ b/lib/utils/services/_tb_web_local_storage.dart @@ -1,11 +1,10 @@ import 'package:thingsboard_client/thingsboard_client.dart'; -import 'dart:html'; +import 'package:universal_html/html.dart' as html; TbStorage createAppStorage() => TbWebLocalStorage(); class TbWebLocalStorage implements TbStorage { - - final Storage _localStorage = window.localStorage; + final html.Storage _localStorage = html.window.localStorage; @override Future deleteItem(String key) async { @@ -21,5 +20,4 @@ class TbWebLocalStorage implements TbStorage { Future setItem(String key, String value) async { _localStorage[key] = value; } - } diff --git a/lib/utils/services/device_profile_cache.dart b/lib/utils/services/device_profile_cache.dart index cf1bd79..ce188a3 100644 --- a/lib/utils/services/device_profile_cache.dart +++ b/lib/utils/services/device_profile_cache.dart @@ -1,25 +1,29 @@ import 'package:thingsboard_client/thingsboard_client.dart'; abstract class DeviceProfileCache { - static final _cache = Map(); - static Future getDeviceProfileInfo(ThingsboardClient tbClient, String name, String deviceId) async { + static Future getDeviceProfileInfo( + ThingsboardClient tbClient, String name, String deviceId) async { var deviceProfile = _cache[name]; if (deviceProfile == null) { var device = await tbClient.getDeviceService().getDevice(deviceId); - deviceProfile = await tbClient.getDeviceProfileService().getDeviceProfileInfo(device!.deviceProfileId!.id!); + deviceProfile = await tbClient + .getDeviceProfileService() + .getDeviceProfileInfo(device!.deviceProfileId!.id!); _cache[name] = deviceProfile!; } return deviceProfile; } - static Future> getDeviceProfileInfos(ThingsboardClient tbClient, PageLink pageLink) async { - var deviceProfileInfos = await tbClient.getDeviceProfileService().getDeviceProfileInfos(pageLink); + static Future> getDeviceProfileInfos( + ThingsboardClient tbClient, PageLink pageLink) async { + var deviceProfileInfos = await tbClient + .getDeviceProfileService() + .getDeviceProfileInfos(pageLink); deviceProfileInfos.data.forEach((deviceProfile) { _cache[deviceProfile.name] = deviceProfile; }); return deviceProfileInfos; } - } diff --git a/lib/utils/services/entity_query_api.dart b/lib/utils/services/entity_query_api.dart index 9ce10a0..eb66b75 100644 --- a/lib/utils/services/entity_query_api.dart +++ b/lib/utils/services/entity_query_api.dart @@ -1,7 +1,6 @@ import 'package:thingsboard_client/thingsboard_client.dart'; abstract class EntityQueryApi { - static final activeDeviceKeyFilter = KeyFilter( key: EntityKey(type: EntityKeyType.ATTRIBUTE, key: 'active'), valueType: EntityKeyValueType.BOOLEAN, @@ -27,36 +26,54 @@ abstract class EntityQueryApi { EntityKey(type: EntityKeyType.ATTRIBUTE, key: 'active') ]; - static Future countDevices(ThingsboardClient tbClient, {String? deviceType, bool? active}) { + static Future countDevices(ThingsboardClient tbClient, + {String? deviceType, bool? active}) { EntityFilter deviceFilter; if (deviceType != null) { - deviceFilter = DeviceTypeFilter(deviceType: deviceType, deviceNameFilter: ''); + deviceFilter = + DeviceTypeFilter(deviceType: deviceType, deviceNameFilter: ''); } else { deviceFilter = EntityTypeFilter(entityType: EntityType.DEVICE); } - EntityCountQuery deviceCountQuery = EntityCountQuery(entityFilter: deviceFilter); + EntityCountQuery deviceCountQuery = + EntityCountQuery(entityFilter: deviceFilter); if (active != null) { - deviceCountQuery.keyFilters = [active ? activeDeviceKeyFilter : inactiveDeviceKeyFilter]; + deviceCountQuery.keyFilters = [ + active ? activeDeviceKeyFilter : inactiveDeviceKeyFilter + ]; } - return tbClient.getEntityQueryService().countEntitiesByQuery(deviceCountQuery); + return tbClient + .getEntityQueryService() + .countEntitiesByQuery(deviceCountQuery); } - static EntityDataQuery createDefaultDeviceQuery({int pageSize = 20, String? searchText, String? deviceType, bool? active}) { + static EntityDataQuery createDefaultDeviceQuery( + {int pageSize = 20, + String? searchText, + String? deviceType, + bool? active}) { EntityFilter deviceFilter; List? keyFilters; if (deviceType != null) { - deviceFilter = DeviceTypeFilter(deviceType: deviceType, deviceNameFilter: ''); + deviceFilter = + DeviceTypeFilter(deviceType: deviceType, deviceNameFilter: ''); } else { deviceFilter = EntityTypeFilter(entityType: EntityType.DEVICE); } if (active != null) { keyFilters = [active ? activeDeviceKeyFilter : inactiveDeviceKeyFilter]; } - return EntityDataQuery(entityFilter: deviceFilter, keyFilters: keyFilters, - entityFields: defaultDeviceFields, latestValues: defaultDeviceAttributes, pageLink: EntityDataPageLink(pageSize: pageSize, + return EntityDataQuery( + entityFilter: deviceFilter, + keyFilters: keyFilters, + entityFields: defaultDeviceFields, + latestValues: defaultDeviceAttributes, + pageLink: EntityDataPageLink( + pageSize: pageSize, textSearch: searchText, - sortOrder: EntityDataSortOrder(key: EntityKey(type: EntityKeyType.ENTITY_FIELD, key: 'createdTime'), + sortOrder: EntityDataSortOrder( + key: EntityKey( + type: EntityKeyType.ENTITY_FIELD, key: 'createdTime'), direction: EntityDataSortOrderDirection.DESC))); } - } diff --git a/lib/utils/services/tb_app_storage.dart b/lib/utils/services/tb_app_storage.dart index d079962..f871f7d 100644 --- a/lib/utils/services/tb_app_storage.dart +++ b/lib/utils/services/tb_app_storage.dart @@ -1,3 +1,3 @@ export '_tb_app_storage.dart' - if (dart.library.io) 'tb_secure_storage.dart' - if (dart.library.html) 'tb_web_local_storage.dart'; + if (dart.library.io) '_tb_secure_storage.dart' + if (dart.library.html) '_tb_web_local_storage.dart'; diff --git a/lib/utils/services/widget_action_handler.dart b/lib/utils/services/widget_action_handler.dart index 2d73b39..f510198 100644 --- a/lib/utils/services/widget_action_handler.dart +++ b/lib/utils/services/widget_action_handler.dart @@ -8,7 +8,7 @@ import 'package:image_picker/image_picker.dart'; import 'package:mime/mime.dart'; import 'package:qr_code_scanner/qr_code_scanner.dart'; import 'package:thingsboard_app/core/context/tb_context.dart'; -import 'package:url_launcher/url_launcher.dart'; +import 'package:url_launcher/url_launcher_string.dart'; class WidgetMobileActionResult { T? result; @@ -16,11 +16,17 @@ class WidgetMobileActionResult { String? error; bool hasError = false; - WidgetMobileActionResult.errorResult(this.error): hasError = true, hasResult = false; + WidgetMobileActionResult.errorResult(this.error) + : hasError = true, + hasResult = false; - WidgetMobileActionResult.successResult(this.result): hasError = false, hasResult = true; + WidgetMobileActionResult.successResult(this.result) + : hasError = false, + hasResult = true; - WidgetMobileActionResult.emptyResult(): hasError = false, hasResult = false; + WidgetMobileActionResult.emptyResult() + : hasError = false, + hasResult = false; Map toJson() { var json = {}; @@ -33,7 +39,6 @@ class WidgetMobileActionResult { } class MobileActionResult { - MobileActionResult(); factory MobileActionResult.launched(bool launched) { @@ -123,24 +128,27 @@ enum WidgetMobileActionType { } WidgetMobileActionType widgetMobileActionTypeFromString(String value) { - return WidgetMobileActionType.values.firstWhere((e)=>e.toString().split('.')[1].toUpperCase()==value.toUpperCase(), orElse: () => WidgetMobileActionType.unknown); + return WidgetMobileActionType.values.firstWhere( + (e) => e.toString().split('.')[1].toUpperCase() == value.toUpperCase(), + orElse: () => WidgetMobileActionType.unknown); } class WidgetActionHandler with HasTbContext { - WidgetActionHandler(TbContext tbContext) { setTbContext(tbContext); } - Future> handleWidgetMobileAction(List args, InAppWebViewController controller) async { + Future> handleWidgetMobileAction( + List args, InAppWebViewController controller) async { var result = await _handleWidgetMobileAction(args, controller); return result.toJson(); } - Future _handleWidgetMobileAction(List args, InAppWebViewController controller) async { + Future _handleWidgetMobileAction( + List args, InAppWebViewController controller) async { if (args.isNotEmpty && args[0] is String) { var actionType = widgetMobileActionTypeFromString(args[0]); - switch(actionType) { + switch (actionType) { case WidgetMobileActionType.takePictureFromGallery: return await _takePicture(ImageSource.gallery); case WidgetMobileActionType.takePhoto: @@ -158,24 +166,26 @@ class WidgetActionHandler with HasTbContext { case WidgetMobileActionType.takeScreenshot: return await _takeScreenshot(controller); case WidgetMobileActionType.unknown: - return WidgetMobileActionResult.errorResult('Unknown actionType: ${args[0]}'); + return WidgetMobileActionResult.errorResult( + 'Unknown actionType: ${args[0]}'); } } else { - return WidgetMobileActionResult.errorResult('actionType is not provided.'); + return WidgetMobileActionResult.errorResult( + 'actionType is not provided.'); } } Future _takePicture(ImageSource source) async { try { final picker = ImagePicker(); - final pickedFile = await picker.getImage(source: source); + final pickedFile = await picker.pickImage(source: source); if (pickedFile != null) { var mimeType = lookupMimeType(pickedFile.path); if (mimeType != null) { var image = File(pickedFile.path); List imageBytes = await image.readAsBytes(); - String imageUrl = UriData.fromBytes(imageBytes, mimeType: mimeType) - .toString(); + String imageUrl = + UriData.fromBytes(imageBytes, mimeType: mimeType).toString(); return WidgetMobileActionResult.successResult( MobileActionResult.image(imageUrl)); } else { @@ -190,7 +200,8 @@ class WidgetActionHandler with HasTbContext { } } - Future _launchMap(List args, bool directionElseLocation) async { + Future _launchMap( + List args, bool directionElseLocation) async { try { num? lat; num? lon; @@ -213,9 +224,11 @@ class WidgetActionHandler with HasTbContext { Future _scanQrCode() async { try { - Barcode? barcode = await tbContext.navigateTo('/qrCodeScan', transition: TransitionType.nativeModal); + Barcode? barcode = await tbContext.navigateTo('/qrCodeScan', + transition: TransitionType.nativeModal); if (barcode != null && barcode.code != null) { - return WidgetMobileActionResult.successResult(MobileActionResult.qrCode(barcode.code!, describeEnum(barcode.format))); + return WidgetMobileActionResult.successResult(MobileActionResult.qrCode( + barcode.code!, describeEnum(barcode.format))); } else { return WidgetMobileActionResult.emptyResult(); } @@ -261,19 +274,24 @@ class WidgetActionHandler with HasTbContext { return WidgetMobileActionResult.errorResult( 'Location permissions are permanently denied, we cannot request permissions.'); } - var position = await Geolocator.getCurrentPosition(desiredAccuracy: LocationAccuracy.high); - return WidgetMobileActionResult.successResult(MobileActionResult.location(position.latitude, position.longitude)); + var position = await Geolocator.getCurrentPosition( + desiredAccuracy: LocationAccuracy.high); + return WidgetMobileActionResult.successResult( + MobileActionResult.location(position.latitude, position.longitude)); } catch (e) { return _handleError(e); } } - Future _takeScreenshot(InAppWebViewController controller) async { + Future _takeScreenshot( + InAppWebViewController controller) async { try { List? imageBytes = await controller.takeScreenshot(); if (imageBytes != null) { - String imageUrl = UriData.fromBytes(imageBytes, mimeType: 'image/png').toString(); - return WidgetMobileActionResult.successResult(MobileActionResult.image(imageUrl)); + String imageUrl = + UriData.fromBytes(imageBytes, mimeType: 'image/png').toString(); + return WidgetMobileActionResult.successResult( + MobileActionResult.image(imageUrl)); } else { return WidgetMobileActionResult.emptyResult(); } @@ -283,8 +301,8 @@ class WidgetActionHandler with HasTbContext { } Future _tryLaunch(String url) async { - if (await canLaunch(url)) { - await launch(url); + if (await canLaunchUrlString(url)) { + await launchUrlString(url); return MobileActionResult.launched(true); } else { log.error('Could not launch $url'); @@ -301,5 +319,4 @@ class WidgetActionHandler with HasTbContext { } return WidgetMobileActionResult.errorResult(error); } - } diff --git a/lib/utils/transition/page_transitions.dart b/lib/utils/transition/page_transitions.dart index 6f501cd..5192e31 100644 --- a/lib/utils/transition/page_transitions.dart +++ b/lib/utils/transition/page_transitions.dart @@ -1,5 +1,4 @@ import 'package:flutter/material.dart'; -import 'package:flutter/widgets.dart'; class FadeOpenPageTransitionsBuilder extends PageTransitionsBuilder { /// Constructs a page transition animation that slides the page up. @@ -7,12 +6,12 @@ class FadeOpenPageTransitionsBuilder extends PageTransitionsBuilder { @override Widget buildTransitions( - PageRoute? route, - BuildContext? context, - Animation animation, - Animation? secondaryAnimation, - Widget child, - ) { + PageRoute? route, + BuildContext? context, + Animation animation, + Animation? secondaryAnimation, + Widget child, + ) { return FadeOpenPageTransition(routeAnimation: animation, child: child); } } @@ -20,9 +19,11 @@ class FadeOpenPageTransitionsBuilder extends PageTransitionsBuilder { class FadeOpenPageTransition extends StatelessWidget { FadeOpenPageTransition({ Key? key, - required Animation routeAnimation, // The route's linear 0.0 - 1.0 animation. + required Animation + routeAnimation, // The route's linear 0.0 - 1.0 animation. required this.child, - }) : _positionAnimation = routeAnimation.drive(_leftRightTween.chain(_fastOutSlowInTween)), + }) : _positionAnimation = + routeAnimation.drive(_leftRightTween.chain(_fastOutSlowInTween)), _opacityAnimation = routeAnimation.drive(_easeInTween), super(key: key); @@ -31,8 +32,10 @@ class FadeOpenPageTransition extends StatelessWidget { begin: const Offset(0.5, 0.0), end: Offset.zero, ); - static final Animatable _fastOutSlowInTween = CurveTween(curve: Curves.fastOutSlowIn); - static final Animatable _easeInTween = CurveTween(curve: Curves.easeIn); + static final Animatable _fastOutSlowInTween = + CurveTween(curve: Curves.fastOutSlowIn); + static final Animatable _easeInTween = + CurveTween(curve: Curves.easeIn); final Animation _positionAnimation; final Animation _opacityAnimation; diff --git a/lib/utils/ui/qr_code_scanner.dart b/lib/utils/ui/qr_code_scanner.dart index 15270fe..665ab32 100644 --- a/lib/utils/ui/qr_code_scanner.dart +++ b/lib/utils/ui/qr_code_scanner.dart @@ -2,22 +2,18 @@ import 'dart:io'; import 'dart:async'; import 'package:flutter/material.dart'; -import 'package:flutter/widgets.dart'; import 'package:qr_code_scanner/qr_code_scanner.dart'; import 'package:thingsboard_app/core/context/tb_context.dart'; import 'package:thingsboard_app/core/context/tb_context_widget.dart'; class QrCodeScannerPage extends TbPageWidget { - QrCodeScannerPage(TbContext tbContext) : super(tbContext); @override _QrCodeScannerPageState createState() => _QrCodeScannerPageState(); - } class _QrCodeScannerPageState extends TbPageState { - Timer? simulatedQrTimer; QRViewController? controller; final GlobalKey qrKey = GlobalKey(debugLabel: 'QR'); @@ -48,64 +44,63 @@ class _QrCodeScannerPageState extends TbPageState { @override Widget build(BuildContext context) { return Scaffold( - body: Stack( - children: [ - _buildQrView(context), - Positioned( + body: Stack( + children: [ + _buildQrView(context), + Positioned( bottom: 0, left: 0, right: 0, height: kToolbarHeight, - child: Center(child: Text('Scan a code', style: TextStyle(color: Colors.white, fontSize: 20))) + child: Center( + child: Text('Scan a code', + style: TextStyle(color: Colors.white, fontSize: 20)))), + Positioned( + child: AppBar( + backgroundColor: Colors.transparent, + foregroundColor: Colors.white, + iconTheme: IconThemeData(color: Colors.white), + elevation: 0, + actions: [ + IconButton( + icon: FutureBuilder( + future: controller?.getFlashStatus(), + builder: (context, snapshot) { + return Icon(snapshot.data == false + ? Icons.flash_on + : Icons.flash_off); + }), + onPressed: () async { + await controller?.toggleFlash(); + setState(() {}); + }, + tooltip: 'Toggle flash', + ), + IconButton( + icon: FutureBuilder( + future: controller?.getCameraInfo(), + builder: (context, snapshot) { + return Icon(snapshot.data == CameraFacing.front + ? Icons.camera_rear + : Icons.camera_front); + }), + onPressed: () async { + await controller?.flipCamera(); + setState(() {}); + }, + tooltip: 'Toggle camera', + ), + ], ), - Positioned( - child: - AppBar( - backgroundColor: Colors.transparent, - foregroundColor: Colors.white, - iconTheme: IconThemeData( - color: Colors.white - ), - elevation: 0, - actions: [ - IconButton( - icon: FutureBuilder( - future: controller?.getFlashStatus(), - builder: (context, snapshot) { - return Icon(snapshot.data == false ? Icons.flash_on : Icons.flash_off); - } - ), - onPressed: () async { - await controller?.toggleFlash(); - setState(() {}); - }, - tooltip: 'Toggle flash', - ), - IconButton( - icon: FutureBuilder( - future: controller?.getCameraInfo(), - builder: (context, snapshot) { - return Icon(snapshot.data == CameraFacing.front ? Icons.camera_rear : Icons.camera_front); - } - ), - onPressed: () async { - await controller?.flipCamera(); - setState(() {}); - }, - tooltip: 'Toggle camera', - ), - ], - ), - ) - ], - ) - ); + ) + ], + )); } Widget _buildQrView(BuildContext context) { // For this example we check how width or tall the device is and change the scanArea and overlay accordingly. var scanArea = (MediaQuery.of(context).size.width < 400 || - MediaQuery.of(context).size.height < 400) + MediaQuery.of(context).size.height < 400) ? 150.0 : 300.0; // To ensure the Scanner view is properly sizes after rotation diff --git a/lib/utils/ui_utils_routes.dart b/lib/utils/ui_utils_routes.dart index 13059c0..308e1f0 100644 --- a/lib/utils/ui_utils_routes.dart +++ b/lib/utils/ui_utils_routes.dart @@ -5,8 +5,8 @@ import 'package:thingsboard_app/core/context/tb_context.dart'; import 'package:thingsboard_app/utils/ui/qr_code_scanner.dart'; class UiUtilsRoutes extends TbRoutes { - - late var qrCodeScannerHandler = Handler(handlerFunc: (BuildContext? context, Map params) { + late var qrCodeScannerHandler = Handler( + handlerFunc: (BuildContext? context, Map params) { return QrCodeScannerPage(tbContext); }); @@ -16,5 +16,4 @@ class UiUtilsRoutes extends TbRoutes { void doRegisterRoutes(router) { router.define("/qrCodeScan", handler: qrCodeScannerHandler); } - } diff --git a/lib/utils/utils.dart b/lib/utils/utils.dart index 6d15f03..8a7711a 100644 --- a/lib/utils/utils.dart +++ b/lib/utils/utils.dart @@ -5,13 +5,13 @@ import 'package:flutter_svg/flutter_svg.dart'; import 'package:thingsboard_client/thingsboard_client.dart'; abstract class Utils { - - static String createDashboardEntityState(EntityId entityId, {String? entityName, String? entityLabel}) { - var stateObj = [{ - 'params': { - 'entityId': entityId.toJson() + static String createDashboardEntityState(EntityId entityId, + {String? entityName, String? entityLabel}) { + var stateObj = [ + { + 'params': {'entityId': entityId.toJson()} } - }]; + ]; if (entityName != null) { stateObj[0]['params']['entityName'] = entityName; } @@ -19,14 +19,13 @@ abstract class Utils { stateObj[0]['params']['entityLabel'] = entityLabel; } var stateJson = json.encode(stateObj); - var encodedUri = Uri.encodeComponent(stateJson); - encodedUri = encodedUri.replaceAllMapped(RegExp(r'%([0-9A-F]{2})'), (match) { + var encodedUri = Uri.encodeComponent(stateJson); + encodedUri = + encodedUri.replaceAllMapped(RegExp(r'%([0-9A-F]{2})'), (match) { var p1 = match.group(1)!; return String.fromCharCode(int.parse(p1, radix: 16)); }); - return Uri.encodeComponent( - base64.encode(utf8.encode(encodedUri)) - ); + return Uri.encodeComponent(base64.encode(utf8.encode(encodedUri))); } static String? contactToShortAddress(ContactBased contact) { @@ -47,13 +46,21 @@ abstract class Utils { } } - static Widget imageFromBase64(String base64, {Color? color, double? width, double? height, String? semanticLabel}) { + static Widget imageFromBase64(String base64, + {Color? color, double? width, double? height, String? semanticLabel}) { var uriData = UriData.parse(base64); if (uriData.mimeType == 'image/svg+xml') { - return SvgPicture.memory(uriData.contentAsBytes(), color: color, width: width, height: height, semanticsLabel: semanticLabel); + return SvgPicture.memory(uriData.contentAsBytes(), + color: color, + width: width, + height: height, + semanticsLabel: semanticLabel); } else { - return Image.memory(uriData.contentAsBytes(), color: color, width: width, height: height, semanticLabel: semanticLabel); + return Image.memory(uriData.contentAsBytes(), + color: color, + width: width, + height: height, + semanticLabel: semanticLabel); } } - } diff --git a/lib/widgets/tb_app_bar.dart b/lib/widgets/tb_app_bar.dart index bbd7ea6..b6460fb 100644 --- a/lib/widgets/tb_app_bar.dart +++ b/lib/widgets/tb_app_bar.dart @@ -2,12 +2,10 @@ import 'dart:async'; import 'package:stream_transform/stream_transform.dart'; import 'package:flutter/material.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'; class TbAppBar extends TbContextWidget implements PreferredSizeWidget { - final Widget? leading; final Widget? title; final List? actions; @@ -18,18 +16,22 @@ class TbAppBar extends TbContextWidget implements PreferredSizeWidget { @override final Size preferredSize; - TbAppBar(TbContext tbContext, {this.leading, this.title, this.actions, this.elevation = 8, - this.shadowColor, this.showLoadingIndicator = false}) : - preferredSize = Size.fromHeight(kToolbarHeight + (showLoadingIndicator ? 4 : 0)), - super(tbContext); + TbAppBar(TbContext tbContext, + {this.leading, + this.title, + this.actions, + this.elevation = 8, + this.shadowColor, + this.showLoadingIndicator = false}) + : preferredSize = + Size.fromHeight(kToolbarHeight + (showLoadingIndicator ? 4 : 0)), + super(tbContext); @override _TbAppBarState createState() => _TbAppBarState(); - } class _TbAppBarState extends TbContextState { - @override void initState() { super.initState(); @@ -45,18 +47,15 @@ class _TbAppBarState extends TbContextState { List children = []; children.add(buildDefaultBar()); if (widget.showLoadingIndicator) { - children.add( - ValueListenableBuilder( - valueListenable: loadingNotifier, - builder: (context, bool loading, child) { - if (loading) { - return LinearProgressIndicator(); - } else { - return Container(height: 4); - } - } - ) - ); + children.add(ValueListenableBuilder( + valueListenable: loadingNotifier, + builder: (context, bool loading, child) { + if (loading) { + return LinearProgressIndicator(); + } else { + return Container(height: 4); + } + })); } return Column( children: children, @@ -75,7 +74,6 @@ class _TbAppBarState extends TbContextState { } class TbAppSearchBar extends TbContextWidget implements PreferredSizeWidget { - final double? elevation; final Color? shadowColor; final bool showLoadingIndicator; @@ -85,9 +83,14 @@ class TbAppSearchBar extends TbContextWidget implements PreferredSizeWidget { @override final Size preferredSize; - TbAppSearchBar(TbContext tbContext, {this.elevation = 8, - this.shadowColor, this.showLoadingIndicator = false, this.searchHint, this.onSearch}) : - preferredSize = Size.fromHeight(kToolbarHeight + (showLoadingIndicator ? 4 : 0)), + TbAppSearchBar(TbContext tbContext, + {this.elevation = 8, + this.shadowColor, + this.showLoadingIndicator = false, + this.searchHint, + this.onSearch}) + : preferredSize = + Size.fromHeight(kToolbarHeight + (showLoadingIndicator ? 4 : 0)), super(tbContext); @override @@ -95,7 +98,6 @@ class TbAppSearchBar extends TbContextWidget implements PreferredSizeWidget { } class _TbAppSearchBarState extends TbContextState { - final TextEditingController _filter = new TextEditingController(); final _textUpdates = StreamController(); @@ -106,7 +108,11 @@ class _TbAppSearchBarState extends TbContextState { _filter.addListener(() { _textUpdates.add(_filter.text); }); - _textUpdates.stream.skip(1).debounce(const Duration(milliseconds: 150)).distinct().forEach((element) => widget.onSearch!(element)); + _textUpdates.stream + .skip(1) + .debounce(const Duration(milliseconds: 150)) + .distinct() + .forEach((element) => widget.onSearch!(element)); } @override @@ -120,18 +126,15 @@ class _TbAppSearchBarState extends TbContextState { List children = []; children.add(buildSearchBar()); if (widget.showLoadingIndicator) { - children.add( - ValueListenableBuilder( - valueListenable: loadingNotifier, - builder: (context, bool loading, child) { - if (loading) { - return LinearProgressIndicator(); - } else { - return Container(height: 4); - } - } - ) - ); + children.add(ValueListenableBuilder( + valueListenable: loadingNotifier, + builder: (context, bool loading, child) { + if (loading) { + return LinearProgressIndicator(); + } else { + return Container(height: 4); + } + })); } return Column( children: children, @@ -140,40 +143,37 @@ class _TbAppSearchBarState extends TbContextState { AppBar buildSearchBar() { return AppBar( - centerTitle: true, - elevation: widget.elevation ?? 8, - shadowColor: widget.shadowColor ?? Color(0xFFFFFFFF).withAlpha(150), - title: TextField( - controller: _filter, - autofocus: true, - // cursorColor: Colors.white, - decoration: new InputDecoration( - border: InputBorder.none, - hintStyle: TextStyle( - color: Color(0xFF282828).withAlpha((255 * 0.38).ceil()), - ), - contentPadding: EdgeInsets.only(left: 15, bottom: 11, top: 15, right: 15), - hintText: widget.searchHint ?? 'Search', - ) - ), - actions: [ - ValueListenableBuilder(valueListenable: _filter, - builder: (context, value, child) { - if (_filter.text.isNotEmpty) { - return IconButton( - icon: Icon( - Icons.clear - ), - onPressed: () { - _filter.text = ''; - }, - ); - } else { - return Container(); - } - } - ) - ] - ); + centerTitle: true, + elevation: widget.elevation ?? 8, + shadowColor: widget.shadowColor ?? Color(0xFFFFFFFF).withAlpha(150), + title: TextField( + controller: _filter, + autofocus: true, + // cursorColor: Colors.white, + decoration: new InputDecoration( + border: InputBorder.none, + hintStyle: TextStyle( + color: Color(0xFF282828).withAlpha((255 * 0.38).ceil()), + ), + contentPadding: + EdgeInsets.only(left: 15, bottom: 11, top: 15, right: 15), + hintText: widget.searchHint ?? 'Search', + )), + actions: [ + ValueListenableBuilder( + valueListenable: _filter, + builder: (context, value, child) { + if (_filter.text.isNotEmpty) { + return IconButton( + icon: Icon(Icons.clear), + onPressed: () { + _filter.text = ''; + }, + ); + } else { + return Container(); + } + }) + ]); } } diff --git a/lib/widgets/tb_progress_indicator.dart b/lib/widgets/tb_progress_indicator.dart index dd11eb9..b4929b6 100644 --- a/lib/widgets/tb_progress_indicator.dart +++ b/lib/widgets/tb_progress_indicator.dart @@ -1,12 +1,10 @@ import 'dart:math'; import 'package:flutter/material.dart'; -import 'package:flutter/widgets.dart'; import 'package:flutter_svg/flutter_svg.dart'; import 'package:thingsboard_app/constants/assets_path.dart'; class TbProgressIndicator extends ProgressIndicator { - final double size; const TbProgressIndicator({ @@ -16,22 +14,22 @@ class TbProgressIndicator extends ProgressIndicator { String? semanticsLabel, String? semanticsValue, }) : super( - key: key, - value: null, - valueColor: valueColor, - semanticsLabel: semanticsLabel, - semanticsValue: semanticsValue, - ); + key: key, + value: null, + valueColor: valueColor, + semanticsLabel: semanticsLabel, + semanticsValue: semanticsValue, + ); @override _TbProgressIndicatorState createState() => _TbProgressIndicatorState(); - Color _getValueColor(BuildContext context) => valueColor?.value ?? Theme.of(context).primaryColor; - + Color _getValueColor(BuildContext context) => + valueColor?.value ?? Theme.of(context).primaryColor; } -class _TbProgressIndicatorState extends State with SingleTickerProviderStateMixin { - +class _TbProgressIndicatorState extends State + with SingleTickerProviderStateMixin { late AnimationController _controller; late CurvedAnimation _rotation; @@ -39,8 +37,10 @@ class _TbProgressIndicatorState extends State with SingleTi void initState() { super.initState(); _controller = AnimationController( - duration: const Duration(milliseconds: 1500), - vsync: this, upperBound: 1, animationBehavior: AnimationBehavior.preserve); + duration: const Duration(milliseconds: 1500), + vsync: this, + upperBound: 1, + animationBehavior: AnimationBehavior.preserve); _rotation = CurvedAnimation(parent: _controller, curve: Curves.easeInOut); _controller.repeat(); } @@ -48,8 +48,7 @@ class _TbProgressIndicatorState extends State with SingleTi @override void didUpdateWidget(TbProgressIndicator oldWidget) { super.didUpdateWidget(oldWidget); - if (!_controller.isAnimating) - _controller.repeat(); + if (!_controller.isAnimating) _controller.repeat(); } @override @@ -74,13 +73,10 @@ class _TbProgressIndicatorState extends State with SingleTi color: widget._getValueColor(context)), builder: (BuildContext context, Widget? child) { return Transform.rotate( - angle: _rotation.value * pi * 2, - child: child - ); + angle: _rotation.value * pi * 2, child: child); }, ) ], ); } - } diff --git a/lib/widgets/two_page_view.dart b/lib/widgets/two_page_view.dart index 9845749..ad152ea 100644 --- a/lib/widgets/two_page_view.dart +++ b/lib/widgets/two_page_view.dart @@ -2,7 +2,6 @@ import 'package:flutter/widgets.dart'; import 'package:preload_page_view/preload_page_view.dart'; class TwoPageViewController { - _TwoPageViewState? _state; setTransitionIndexedStackState(_TwoPageViewState state) { @@ -24,7 +23,6 @@ class TwoPageViewController { } int? get index => _state?._selectedIndex; - } class TwoPageView extends StatefulWidget { @@ -33,21 +31,19 @@ class TwoPageView extends StatefulWidget { final Duration duration; final TwoPageViewController? controller; - const TwoPageView({ - Key? key, - required this.first, - required this.second, - this.controller, - this.duration = const Duration(milliseconds: 250) - }) : super(key: key); + const TwoPageView( + {Key? key, + required this.first, + required this.second, + this.controller, + this.duration = const Duration(milliseconds: 250)}) + : super(key: key); @override _TwoPageViewState createState() => _TwoPageViewState(); - } class _TwoPageViewState extends State { - late List _pages; bool _reverse = false; int _selectedIndex = 0; @@ -68,7 +64,8 @@ class _TwoPageViewState extends State { _reverse = true; }); } - await _pageController.animateToPage(_selectedIndex, duration: widget.duration, curve: Curves.fastOutSlowIn); + await _pageController.animateToPage(_selectedIndex, + duration: widget.duration, curve: Curves.fastOutSlowIn); return true; } return false; @@ -77,7 +74,8 @@ class _TwoPageViewState extends State { Future _close(int index, {bool animate = true}) async { if (_selectedIndex == index) { _selectedIndex = index == 1 ? 0 : 1; - await _pageController.animateToPage(_selectedIndex, duration: widget.duration, curve: Curves.fastOutSlowIn); + await _pageController.animateToPage(_selectedIndex, + duration: widget.duration, curve: Curves.fastOutSlowIn); if (index == 0) { setState(() { _reverse = false; @@ -106,5 +104,4 @@ class _TwoPageViewState extends State { controller: _pageController, ); } - } diff --git a/lib/widgets/two_value_listenable_builder.dart b/lib/widgets/two_value_listenable_builder.dart index 3c9811b..2d81ffb 100644 --- a/lib/widgets/two_value_listenable_builder.dart +++ b/lib/widgets/two_value_listenable_builder.dart @@ -2,14 +2,13 @@ import 'package:flutter/foundation.dart'; import 'package:flutter/widgets.dart'; class TwoValueListenableBuilder extends StatelessWidget { - TwoValueListenableBuilder( - { - Key? key, - required this.firstValueListenable, - required this.secondValueListenable, - required this.builder, - this.child, - }) : super(key: key); + TwoValueListenableBuilder({ + Key? key, + required this.firstValueListenable, + required this.secondValueListenable, + required this.builder, + this.child, + }) : super(key: key); final ValueListenable firstValueListenable; final ValueListenable secondValueListenable; diff --git a/pubspec.lock b/pubspec.lock index a8ea8f8..7bb17d4 100644 --- a/pubspec.lock +++ b/pubspec.lock @@ -7,21 +7,21 @@ packages: name: archive url: "https://pub.dartlang.org" source: hosted - version: "3.1.6" + version: "3.3.1" args: dependency: transitive description: name: args url: "https://pub.dartlang.org" source: hosted - version: "2.3.0" + version: "2.3.1" async: dependency: transitive description: name: async url: "https://pub.dartlang.org" source: hosted - version: "2.8.1" + version: "2.8.2" auto_size_text: dependency: "direct main" description: @@ -42,7 +42,7 @@ packages: name: characters url: "https://pub.dartlang.org" source: hosted - version: "1.1.0" + version: "1.2.0" charcode: dependency: transitive description: @@ -50,6 +50,20 @@ packages: url: "https://pub.dartlang.org" source: hosted version: "1.3.1" + checked_yaml: + dependency: transitive + description: + name: checked_yaml + url: "https://pub.dartlang.org" + source: hosted + version: "2.0.1" + cli_util: + dependency: transitive + description: + name: cli_util + url: "https://pub.dartlang.org" + source: hosted + version: "0.3.5" clock: dependency: transitive description: @@ -63,42 +77,49 @@ packages: name: collection url: "https://pub.dartlang.org" source: hosted - version: "1.15.0" + version: "1.16.0" convert: dependency: transitive description: name: convert url: "https://pub.dartlang.org" source: hosted - version: "3.0.1" + version: "3.0.2" cross_file: dependency: transitive description: name: cross_file url: "https://pub.dartlang.org" source: hosted - version: "0.3.2" + version: "0.3.3+1" crypto: dependency: "direct main" description: name: crypto url: "https://pub.dartlang.org" source: hosted - version: "3.0.1" + version: "3.0.2" + csslib: + dependency: transitive + description: + name: csslib + url: "https://pub.dartlang.org" + source: hosted + version: "0.17.2" cupertino_icons: dependency: "direct main" description: name: cupertino_icons url: "https://pub.dartlang.org" source: hosted - version: "1.0.4" + version: "1.0.5" dart_jsonwebtoken: dependency: "direct main" description: name: dart_jsonwebtoken url: "https://pub.dartlang.org" source: hosted - version: "2.4.0" + version: "2.4.2" device_info: dependency: "direct main" description: @@ -119,21 +140,21 @@ packages: name: dio url: "https://pub.dartlang.org" source: hosted - version: "4.0.3" + version: "4.0.6" fading_edge_scrollview: dependency: "direct main" description: name: fading_edge_scrollview url: "https://pub.dartlang.org" source: hosted - version: "2.0.1" + version: "3.0.0" fake_async: dependency: transitive description: name: fake_async url: "https://pub.dartlang.org" source: hosted - version: "1.2.0" + version: "1.3.0" fluro: dependency: "direct main" description: @@ -152,21 +173,23 @@ packages: name: flutter_form_builder url: "https://pub.dartlang.org" source: hosted - version: "7.0.0" + version: "7.5.0" flutter_inappwebview: dependency: "direct main" description: - name: flutter_inappwebview - url: "https://pub.dartlang.org" - source: hosted - version: "5.3.2" + path: "." + ref: master + resolved-ref: a76ed7b1aafb4867517f2ed47dda9e9d15cf32d6 + url: "https://github.com/rshrc/flutter_inappwebview" + source: git + version: "5.4.3+7" flutter_launcher_icons: dependency: "direct dev" description: name: flutter_launcher_icons url: "https://pub.dartlang.org" source: hosted - version: "0.9.2" + version: "0.10.0" flutter_localizations: dependency: transitive description: flutter @@ -178,28 +201,28 @@ packages: name: flutter_plugin_android_lifecycle url: "https://pub.dartlang.org" source: hosted - version: "2.0.4" + version: "2.0.7" flutter_secure_storage: dependency: "direct main" description: name: flutter_secure_storage url: "https://pub.dartlang.org" source: hosted - version: "5.0.2" + version: "5.1.0" flutter_secure_storage_linux: dependency: transitive description: name: flutter_secure_storage_linux url: "https://pub.dartlang.org" source: hosted - version: "1.1.0" + version: "1.1.1" flutter_secure_storage_macos: dependency: transitive description: name: flutter_secure_storage_macos url: "https://pub.dartlang.org" source: hosted - version: "1.1.0" + version: "1.1.1" flutter_secure_storage_platform_interface: dependency: transitive description: @@ -227,14 +250,14 @@ packages: name: flutter_speed_dial url: "https://pub.dartlang.org" source: hosted - version: "4.6.6" + version: "6.0.0" flutter_svg: dependency: "direct main" description: name: flutter_svg url: "https://pub.dartlang.org" source: hosted - version: "0.23.0+1" + version: "1.1.3" flutter_test: dependency: "direct dev" description: flutter @@ -251,91 +274,119 @@ packages: name: form_builder_validators url: "https://pub.dartlang.org" source: hosted - version: "7.2.0" + version: "8.3.0" geolocator: dependency: "direct main" description: name: geolocator url: "https://pub.dartlang.org" source: hosted - version: "7.7.1" + version: "9.0.1" geolocator_android: dependency: transitive description: name: geolocator_android url: "https://pub.dartlang.org" source: hosted - version: "2.1.0" + version: "4.0.2" geolocator_apple: dependency: transitive description: name: geolocator_apple url: "https://pub.dartlang.org" source: hosted - version: "1.2.2" + version: "2.2.1" geolocator_platform_interface: dependency: transitive description: name: geolocator_platform_interface url: "https://pub.dartlang.org" source: hosted - version: "2.3.6" + version: "4.0.6" geolocator_web: dependency: transitive description: name: geolocator_web url: "https://pub.dartlang.org" source: hosted - version: "2.0.6" + version: "2.1.6" + geolocator_windows: + dependency: transitive + description: + name: geolocator_windows + url: "https://pub.dartlang.org" + source: hosted + version: "0.1.1" + html: + dependency: transitive + description: + name: html + url: "https://pub.dartlang.org" + source: hosted + version: "0.15.0" http: dependency: transitive description: name: http url: "https://pub.dartlang.org" source: hosted - version: "0.13.4" + version: "0.13.5" http_parser: dependency: transitive description: name: http_parser url: "https://pub.dartlang.org" source: hosted - version: "4.0.0" + version: "4.0.1" image: dependency: transitive description: name: image url: "https://pub.dartlang.org" source: hosted - version: "3.0.8" + version: "3.2.0" image_picker: dependency: "direct main" description: name: image_picker url: "https://pub.dartlang.org" source: hosted - version: "0.8.4+4" + version: "0.8.5+3" + image_picker_android: + dependency: transitive + description: + name: image_picker_android + url: "https://pub.dartlang.org" + source: hosted + version: "0.8.5+2" image_picker_for_web: dependency: transitive description: name: image_picker_for_web url: "https://pub.dartlang.org" source: hosted - version: "2.1.4" + version: "2.1.8" + image_picker_ios: + dependency: transitive + description: + name: image_picker_ios + url: "https://pub.dartlang.org" + source: hosted + version: "0.8.5+6" image_picker_platform_interface: dependency: transitive description: name: image_picker_platform_interface url: "https://pub.dartlang.org" source: hosted - version: "2.4.1" + version: "2.6.1" infinite_scroll_pagination: dependency: "direct main" description: name: infinite_scroll_pagination url: "https://pub.dartlang.org" source: hosted - version: "3.1.0" + version: "3.2.0" intl: dependency: "direct main" description: @@ -349,7 +400,14 @@ packages: name: js url: "https://pub.dartlang.org" source: hosted - version: "0.6.3" + version: "0.6.4" + json_annotation: + dependency: transitive + description: + name: json_annotation + url: "https://pub.dartlang.org" + source: hosted + version: "4.6.0" jwt_decoder: dependency: transitive description: @@ -370,14 +428,21 @@ packages: name: matcher url: "https://pub.dartlang.org" source: hosted - version: "0.12.10" + version: "0.12.11" + material_color_utilities: + dependency: transitive + description: + name: material_color_utilities + url: "https://pub.dartlang.org" + source: hosted + version: "0.1.4" material_design_icons_flutter: dependency: "direct main" description: name: material_design_icons_flutter url: "https://pub.dartlang.org" source: hosted - version: "5.0.6295" + version: "5.0.6996" meta: dependency: transitive description: @@ -391,7 +456,7 @@ packages: name: mime url: "https://pub.dartlang.org" source: hosted - version: "1.0.1" + version: "1.0.2" package_info: dependency: "direct main" description: @@ -405,42 +470,42 @@ packages: name: path url: "https://pub.dartlang.org" source: hosted - version: "1.8.0" + version: "1.8.1" path_drawing: dependency: transitive description: name: path_drawing url: "https://pub.dartlang.org" source: hosted - version: "0.5.1+1" + version: "1.0.1" path_parsing: dependency: transitive description: name: path_parsing url: "https://pub.dartlang.org" source: hosted - version: "0.2.1" + version: "1.0.1" petitparser: dependency: transitive description: name: petitparser url: "https://pub.dartlang.org" source: hosted - version: "4.4.0" + version: "5.0.0" plugin_platform_interface: dependency: transitive description: name: plugin_platform_interface url: "https://pub.dartlang.org" source: hosted - version: "2.0.2" + version: "2.1.2" pointycastle: dependency: transitive description: name: pointycastle url: "https://pub.dartlang.org" source: hosted - version: "3.4.0" + version: "3.6.1" preload_page_view: dependency: "direct main" description: @@ -454,7 +519,7 @@ packages: name: qr_code_scanner url: "https://pub.dartlang.org" source: hosted - version: "0.6.1" + version: "1.0.0" sky_engine: dependency: transitive description: flutter @@ -466,14 +531,14 @@ packages: name: sliver_tools url: "https://pub.dartlang.org" source: hosted - version: "0.2.5" + version: "0.2.7" source_span: dependency: transitive description: name: source_span url: "https://pub.dartlang.org" source: hosted - version: "1.8.1" + version: "1.8.2" stack_trace: dependency: transitive description: @@ -515,21 +580,37 @@ packages: name: test_api url: "https://pub.dartlang.org" source: hosted - version: "0.4.2" + version: "0.4.9" thingsboard_client: dependency: "direct main" description: - name: thingsboard_client - url: "https://pub.dartlang.org" - source: hosted - version: "1.0.2" + path: "." + ref: master + resolved-ref: d8dcad26dade6fca574d600ab88a6cc0ccaf1a7d + url: "git@github.com:thingsboard/dart_thingsboard_client.git" + source: git + version: "1.0.3" typed_data: dependency: transitive description: name: typed_data url: "https://pub.dartlang.org" source: hosted - version: "1.3.0" + version: "1.3.1" + universal_html: + dependency: "direct main" + description: + name: universal_html + url: "https://pub.dartlang.org" + source: hosted + version: "2.0.8" + universal_io: + dependency: transitive + description: + name: universal_io + url: "https://pub.dartlang.org" + source: hosted + version: "2.0.4" universal_platform: dependency: "direct main" description: @@ -543,70 +624,84 @@ packages: name: url_launcher url: "https://pub.dartlang.org" source: hosted - version: "6.0.13" + version: "6.1.5" + url_launcher_android: + dependency: transitive + description: + name: url_launcher_android + url: "https://pub.dartlang.org" + source: hosted + version: "6.0.17" + url_launcher_ios: + dependency: transitive + description: + name: url_launcher_ios + url: "https://pub.dartlang.org" + source: hosted + version: "6.0.17" url_launcher_linux: dependency: transitive description: name: url_launcher_linux url: "https://pub.dartlang.org" source: hosted - version: "2.0.2" + version: "3.0.1" url_launcher_macos: dependency: transitive description: name: url_launcher_macos url: "https://pub.dartlang.org" source: hosted - version: "2.0.2" + version: "3.0.1" url_launcher_platform_interface: dependency: transitive description: name: url_launcher_platform_interface url: "https://pub.dartlang.org" source: hosted - version: "2.0.4" + version: "2.1.0" url_launcher_web: dependency: transitive description: name: url_launcher_web url: "https://pub.dartlang.org" source: hosted - version: "2.0.4" + version: "2.0.13" url_launcher_windows: dependency: transitive description: name: url_launcher_windows url: "https://pub.dartlang.org" source: hosted - version: "2.0.2" + version: "3.0.1" vector_math: dependency: transitive description: name: vector_math url: "https://pub.dartlang.org" source: hosted - version: "2.1.0" + version: "2.1.2" web_socket_channel: dependency: transitive description: name: web_socket_channel url: "https://pub.dartlang.org" source: hosted - version: "2.1.0" + version: "2.2.0" xml: dependency: transitive description: name: xml url: "https://pub.dartlang.org" source: hosted - version: "5.3.1" + version: "6.1.0" yaml: dependency: transitive description: name: yaml url: "https://pub.dartlang.org" source: hosted - version: "3.1.0" + version: "3.1.1" sdks: - dart: ">=2.14.0 <3.0.0" - flutter: ">=2.5.0" + dart: ">=2.17.0 <3.0.0" + flutter: ">=3.0.0" diff --git a/pubspec.yaml b/pubspec.yaml index 465117a..8ffc40b 100644 --- a/pubspec.yaml +++ b/pubspec.yaml @@ -11,18 +11,24 @@ environment: dependencies: flutter: sdk: flutter - thingsboard_client: 1.0.2 + thingsboard_client: + git: + url: git@github.com:thingsboard/dart_thingsboard_client.git + ref: master intl: ^0.17.0 flutter_secure_storage: ^5.0.2 - flutter_speed_dial: ^4.6.6 + flutter_speed_dial: ^6.0.0 cupertino_icons: ^1.0.2 fluro: ^2.0.3 - flutter_svg: ^0.23.0+1 + flutter_svg: ^1.1.3 auto_size_text: ^3.0.0-nullsafety.0 infinite_scroll_pagination: ^3.0.1 - fading_edge_scrollview: ^2.0.0 + fading_edge_scrollview: ^3.0.0 stream_transform: ^2.0.0 - flutter_inappwebview: ^5.3.2 + flutter_inappwebview: + git: + url: https://github.com/rshrc/flutter_inappwebview + ref: master # flutter_downloader: ^1.6.0 # permission_handler: ^8.0.0+2 # path_provider: ^2.0.2 @@ -30,22 +36,23 @@ dependencies: image_picker: ^0.8.0 mime: ^1.0.0 logger: ^1.0.0 - qr_code_scanner: ^0.6.1 + qr_code_scanner: ^1.0.0 device_info: ^2.0.0 - geolocator: ^7.0.3 + geolocator: ^9.0.1 material_design_icons_flutter: ^5.0.5955-rc.1 package_info: ^2.0.2 dart_jsonwebtoken: ^2.2.0 crypto: ^3.0.1 flutter_form_builder: ^7.0.0 - form_builder_validators: ^7.2.0 + form_builder_validators: ^8.3.0 + universal_html: ^2.0.8 universal_platform: ^1.0.0+1 preload_page_view: ^0.1.6 dev_dependencies: flutter_test: sdk: flutter - flutter_launcher_icons: ^0.9.0 + flutter_launcher_icons: ^0.10.0 flutter: uses-material-design: true