Redesign login page. Add OAuth2 support.
This commit is contained in:
@@ -44,6 +44,16 @@
|
||||
<category android:name="android.intent.category.LAUNCHER"/>
|
||||
</intent-filter>
|
||||
</activity>
|
||||
|
||||
<activity android:name="com.linusu.flutter_web_auth.CallbackActivity" >
|
||||
<intent-filter android:label="flutter_web_auth">
|
||||
<action android:name="android.intent.action.VIEW" />
|
||||
<category android:name="android.intent.category.DEFAULT" />
|
||||
<category android:name="android.intent.category.BROWSABLE" />
|
||||
<data android:scheme="org.thingsboard.app.auth" />
|
||||
</intent-filter>
|
||||
</activity>
|
||||
|
||||
<!-- Don't delete the meta-data below.
|
||||
This is used by the Flutter tool to generate GeneratedPluginRegistrant.java -->
|
||||
<meta-data
|
||||
|
||||
5
assets/images/facebook-logo.svg
Normal file
5
assets/images/facebook-logo.svg
Normal file
@@ -0,0 +1,5 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<svg viewBox="0 0 263 263" xmlns="http://www.w3.org/2000/svg">
|
||||
<path d="M263 131.5C263 58.9 204.1 0 131.5 0S0 58.9 0 131.5c0 65.6 48.1 120 110.9 129.9v-91.9H77.5v-38h33.4v-29c0-33 19.6-51.2 49.7-51.2 14.4 0 29.4 2.6 29.4 2.6v32.4h-16.5c-16.3 0-21.4 10.1-21.4 20.5v24.7h36.4l-5.8 38h-30.6v91.9c62.8-9.9 110.9-64.3 110.9-129.9z" fill="#1877f2"/>
|
||||
<path d="M182.7 169.5l5.8-38H152v-24.7c0-10.4 5.1-20.5 21.4-20.5H190V53.9s-15-2.6-29.4-2.6c-30 0-49.7 18.2-49.7 51.2v29H77.5v38h33.4v91.9c6.7 1.1 13.6 1.6 20.5 1.6s13.9-.5 20.5-1.6v-91.9h30.8z" fill="#fff"/>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 606 B |
4
assets/images/github-logo.svg
Normal file
4
assets/images/github-logo.svg
Normal file
@@ -0,0 +1,4 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<svg viewBox="0 0 32.7 32.7" xmlns="http://www.w3.org/2000/svg">
|
||||
<path d="M16.3 0C7.3 0 0 7.3 0 16.3c0 7.2 4.7 13.3 11.1 15.5.8.1 1.1-.4 1.1-.8v-2.8c-4.5 1-5.5-2.2-5.5-2.2-.7-1.9-1.8-2.4-1.8-2.4-1.5-1 .1-1 .1-1 1.6.1 2.5 1.7 2.5 1.7 1.5 2.5 3.8 1.8 4.7 1.4.1-1.1.6-1.8 1-2.2-3.6-.4-7.4-1.8-7.4-8.1 0-1.8.6-3.2 1.7-4.4-.2-.4-.7-2.1.2-4.3 0 0 1.4-.4 4.5 1.7 1.3-.4 2.7-.5 4.1-.5s2.8.2 4.1.5c3.1-2.1 4.5-1.7 4.5-1.7.9 2.2.3 3.9.2 4.3 1 1.1 1.7 2.6 1.7 4.4 0 6.3-3.8 7.6-7.4 8 .6.5 1.1 1.5 1.1 3v4.5c0 .4.3.9 1.1.8 6.5-2.2 11.1-8.3 11.1-15.5C32.6 7.3 25.3 0 16.3 0z" fill="#211c19"/>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 630 B |
8
assets/images/google-logo.svg
Normal file
8
assets/images/google-logo.svg
Normal file
@@ -0,0 +1,8 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<svg viewBox="0 0 48 48" xmlns="http://www.w3.org/2000/svg">
|
||||
<path fill="#EA4335" d="M24 9.5c3.54 0 6.71 1.22 9.21 3.6l6.85-6.85C35.9 2.38 30.47 0 24 0 14.62 0 6.51 5.38 2.56 13.22l7.98 6.19C12.43 13.72 17.74 9.5 24 9.5z"/>
|
||||
<path fill="#4285F4" d="M46.98 24.55c0-1.57-.15-3.09-.38-4.55H24v9.02h12.94c-.58 2.96-2.26 5.48-4.78 7.18l7.73 6c4.51-4.18 7.09-10.36 7.09-17.65z"/>
|
||||
<path fill="#FBBC05" d="M10.53 28.59c-.48-1.45-.76-2.99-.76-4.59s.27-3.14.76-4.59l-7.98-6.19C.92 16.46 0 20.12 0 24c0 3.88.92 7.54 2.56 10.78l7.97-6.19z"/>
|
||||
<path fill="#34A853" d="M24 48c6.48 0 11.93-2.13 15.89-5.81l-7.73-6c-2.15 1.45-4.92 2.3-8.16 2.3-6.26 0-11.57-4.22-13.47-9.91l-7.98 6.19C6.51 42.62 14.62 48 24 48z"/>
|
||||
<path fill="none" d="M0 0h48v48H0z"/>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 800 B |
@@ -1 +0,0 @@
|
||||
const thingsBoardApiEndpoint = 'http://localhost:8080';
|
||||
@@ -0,0 +1,4 @@
|
||||
abstract class ThingsboardAppConstants {
|
||||
static final thingsBoardApiEndpoint = 'http://localhost:8080';
|
||||
static final thingsboardOAuth2CallbackUrlScheme = 'org.thingsboard.app.auth';
|
||||
}
|
||||
|
||||
@@ -5,4 +5,11 @@ abstract class ThingsboardImage {
|
||||
static final thingsboardCenter = 'assets/images/thingsboard_center.svg';
|
||||
static final dashboardPlaceholder = 'assets/images/dashboard-placeholder.png';
|
||||
static final deviceProfilePlaceholder = 'assets/images/device-profile-placeholder.png';
|
||||
|
||||
static final oauth2Logos = <String,String>{
|
||||
'google-logo': 'assets/images/google-logo.svg',
|
||||
'github-logo': 'assets/images/github-logo.svg',
|
||||
'facebook-logo': 'assets/images/facebook-logo.svg'
|
||||
};
|
||||
|
||||
}
|
||||
|
||||
@@ -1,14 +1,17 @@
|
||||
import 'dart:ui';
|
||||
|
||||
import 'package:flutter/cupertino.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:flutter/widgets.dart';
|
||||
import 'package:flutter_svg/flutter_svg.dart';
|
||||
import 'package:flutter_web_auth/flutter_web_auth.dart';
|
||||
import 'package:material_design_icons_flutter/material_design_icons_flutter.dart';
|
||||
import 'package:thingsboard_app/constants/app_constants.dart';
|
||||
import 'package:thingsboard_app/constants/assets_path.dart';
|
||||
import 'package:thingsboard_app/widgets/tb_progress_indicator.dart';
|
||||
import 'package:thingsboard_client/thingsboard_client.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';
|
||||
import 'package:thingsboard_client/thingsboard_client.dart';
|
||||
|
||||
class LoginPage extends TbPageWidget<LoginPage, _LoginPageState> {
|
||||
|
||||
@@ -22,6 +25,7 @@ class LoginPage extends TbPageWidget<LoginPage, _LoginPageState> {
|
||||
class _LoginPageState extends TbPageState<LoginPage, _LoginPageState> {
|
||||
|
||||
final _isLoginNotifier = ValueNotifier<bool>(false);
|
||||
final _showPasswordNotifier = ValueNotifier<bool>(false);
|
||||
|
||||
final usernameController = TextEditingController();
|
||||
final passwordController = TextEditingController();
|
||||
@@ -40,91 +44,199 @@ class _LoginPageState extends TbPageState<LoginPage, _LoginPageState> {
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
final ButtonStyle oauth2ButtonStyle =
|
||||
ElevatedButton.styleFrom(primary: Theme.of(context).secondaryHeaderColor,
|
||||
onPrimary: Theme.of(context).colorScheme.onSurface);
|
||||
return Scaffold(
|
||||
backgroundColor: Colors.white,
|
||||
appBar: AppBar(
|
||||
title: const Text('Login to ThingsBoard'),
|
||||
),
|
||||
body: ValueListenableBuilder(
|
||||
resizeToAvoidBottomInset: false,
|
||||
body: ValueListenableBuilder<bool>(
|
||||
valueListenable: _isLoginNotifier,
|
||||
builder: (BuildContext context, bool loading, child) {
|
||||
List<Widget> children = [
|
||||
SingleChildScrollView(
|
||||
LoginPageBackground(),
|
||||
Padding(
|
||||
padding: EdgeInsets.fromLTRB(28, 71, 28, 28),
|
||||
child: Column(
|
||||
crossAxisAlignment: CrossAxisAlignment.stretch,
|
||||
children: [
|
||||
Padding(
|
||||
padding: const EdgeInsets.only(top: 60.0),
|
||||
child: Center(
|
||||
child: Container(
|
||||
width: 300,
|
||||
height: 150,
|
||||
child: SvgPicture.asset(ThingsboardImage.thingsBoardWithTitle,
|
||||
color: Theme.of(context).primaryColor,
|
||||
semanticsLabel: 'ThingsBoard Logo')
|
||||
)
|
||||
Row(
|
||||
children: [
|
||||
SvgPicture.asset(ThingsboardImage.thingsBoardWithTitle,
|
||||
height: 25,
|
||||
color: Theme.of(context).primaryColor,
|
||||
semanticsLabel: 'ThingsBoard Logo')
|
||||
]
|
||||
),
|
||||
Container(height: 32),
|
||||
Row(
|
||||
children: [
|
||||
Text(
|
||||
'Login to your account',
|
||||
style: TextStyle(
|
||||
fontWeight: FontWeight.bold,
|
||||
fontSize: 32,
|
||||
height: 40 / 32
|
||||
)
|
||||
)]
|
||||
),
|
||||
Padding(
|
||||
//padding: const EdgeInsets.only(left:15.0,right: 15.0,top:0,bottom: 0),
|
||||
padding: EdgeInsets.symmetric(horizontal: 15),
|
||||
child: TextField(
|
||||
enabled: !loading,
|
||||
controller: usernameController,
|
||||
decoration: InputDecoration(
|
||||
border: OutlineInputBorder(),
|
||||
labelText: 'Username (email)',
|
||||
hintText: 'Enter valid email id as abc@gmail.com'),
|
||||
Container(height: tbContext.hasOAuthClients ? 24 : 48),
|
||||
if (tbContext.hasOAuthClients)
|
||||
Column(
|
||||
crossAxisAlignment: CrossAxisAlignment.stretch,
|
||||
children: tbContext.oauth2Clients!.map((client) {
|
||||
Widget? icon;
|
||||
if (client.icon != null) {
|
||||
if (ThingsboardImage.oauth2Logos.containsKey(client.icon)) {
|
||||
icon = SvgPicture.asset(ThingsboardImage.oauth2Logos[client.icon]!,
|
||||
height: 24);
|
||||
} else {
|
||||
String strIcon = client.icon!;
|
||||
if (strIcon.startsWith('mdi:')) {
|
||||
strIcon = strIcon.substring(4);
|
||||
}
|
||||
var iconData = MdiIcons.fromString(strIcon);
|
||||
if (iconData != null) {
|
||||
icon = Icon(iconData, size: 24, color: Theme.of(context).primaryColor);
|
||||
}
|
||||
}
|
||||
}
|
||||
if (icon == null) {
|
||||
icon = Icon(Icons.login, size: 24, color: Theme.of(context).primaryColor);
|
||||
}
|
||||
return ElevatedButton.icon(
|
||||
style: oauth2ButtonStyle,
|
||||
onPressed: () async {
|
||||
_isLoginNotifier.value = true;
|
||||
var url = Uri.parse(ThingsboardAppConstants.thingsBoardApiEndpoint + client.url);
|
||||
var params = Map<String,String>.from(url.queryParameters);
|
||||
params['pkg'] = tbContext.packageName;
|
||||
url = url.replace(queryParameters: params);
|
||||
try {
|
||||
final result = await FlutterWebAuth.authenticate(
|
||||
url: url.toString(),
|
||||
callbackUrlScheme: ThingsboardAppConstants.thingsboardOAuth2CallbackUrlScheme);
|
||||
final resultUri = Uri.parse(result);
|
||||
final error = resultUri.queryParameters['error'];
|
||||
if (error != null) {
|
||||
_isLoginNotifier.value = false;
|
||||
showErrorNotification(error);
|
||||
} else {
|
||||
final accessToken = resultUri.queryParameters['accessToken'];
|
||||
final refreshToken = resultUri.queryParameters['refreshToken'];
|
||||
if (accessToken != null && refreshToken != null) {
|
||||
await tbClient.setUserFromJwtToken(accessToken, refreshToken, true);
|
||||
}
|
||||
}
|
||||
log.debug('result = $result');
|
||||
} catch (e) {
|
||||
log.error('Auth Error:', e);
|
||||
_isLoginNotifier.value = false;
|
||||
}
|
||||
},
|
||||
icon: icon,
|
||||
label: Text('Login with ${client.name}'));
|
||||
}).toList(),
|
||||
),
|
||||
if (tbContext.hasOAuthClients)
|
||||
Padding(padding: EdgeInsets.symmetric(vertical: 16),
|
||||
child: Row(
|
||||
children: [
|
||||
Flexible(child: Divider()),
|
||||
Padding(
|
||||
padding: EdgeInsets.symmetric(horizontal: 16),
|
||||
child: Text('OR'),
|
||||
),
|
||||
Flexible(child: Divider())
|
||||
],
|
||||
)
|
||||
),
|
||||
Padding(
|
||||
padding: const EdgeInsets.only(
|
||||
left: 15.0, right: 15.0, top: 15, bottom: 0),
|
||||
//padding: EdgeInsets.symmetric(horizontal: 15),
|
||||
child: TextField(
|
||||
enabled: !loading,
|
||||
controller: passwordController,
|
||||
obscureText: true,
|
||||
decoration: InputDecoration(
|
||||
border: OutlineInputBorder(),
|
||||
labelText: 'Password',
|
||||
hintText: 'Enter secure password'),
|
||||
),
|
||||
TextField(
|
||||
enabled: !loading,
|
||||
controller: usernameController,
|
||||
decoration: InputDecoration(
|
||||
border: OutlineInputBorder(),
|
||||
labelText: 'Email',
|
||||
hintText: 'Enter valid email id as abc@gmail.com'),
|
||||
),
|
||||
TextButton(
|
||||
onPressed: loading ? null : () {
|
||||
//TODO FORGOT PASSWORD SCREEN GOES HERE
|
||||
Container(height: 28),
|
||||
ValueListenableBuilder(
|
||||
valueListenable: _showPasswordNotifier,
|
||||
builder: (BuildContext context, bool showPassword, child) {
|
||||
return TextField(
|
||||
enabled: !loading,
|
||||
controller: passwordController,
|
||||
obscureText: !showPassword,
|
||||
decoration: InputDecoration(
|
||||
suffixIcon: IconButton(
|
||||
icon: Icon(showPassword ? Icons.visibility : Icons.visibility_off),
|
||||
onPressed: loading ? null : () {
|
||||
_showPasswordNotifier.value = !_showPasswordNotifier.value;
|
||||
},
|
||||
),
|
||||
border: OutlineInputBorder(),
|
||||
labelText: 'Password',
|
||||
hintText: 'Enter secure password'),
|
||||
);
|
||||
}
|
||||
),
|
||||
Row(
|
||||
mainAxisAlignment: MainAxisAlignment.end,
|
||||
children: [
|
||||
TextButton(
|
||||
onPressed: loading ? null : () {
|
||||
//TODO FORGOT PASSWORD SCREEN GOES HERE
|
||||
},
|
||||
child: Text(
|
||||
'Forgot Password?',
|
||||
style: TextStyle(color: loading ? Colors.black12 : 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: loading ? null : () async {
|
||||
_isLoginNotifier.value = true;
|
||||
try {
|
||||
await tbClient.login(
|
||||
LoginRequest(usernameController.text,
|
||||
passwordController.text));
|
||||
} catch (e) {
|
||||
_isLoginNotifier.value = false;
|
||||
}
|
||||
},
|
||||
child: Text(
|
||||
'Forgot Password?',
|
||||
style: TextStyle(color: loading ? Colors.black12 : Theme.of(context).colorScheme.primary, fontSize: 15),
|
||||
),
|
||||
),
|
||||
Container(
|
||||
height: 50,
|
||||
width: 250,
|
||||
decoration: BoxDecoration(
|
||||
color: loading ? Colors.black12 : Theme.of(context).colorScheme.primary, borderRadius: BorderRadius.circular(4)),
|
||||
child: TextButton(
|
||||
onPressed: loading ? null : () async {
|
||||
_isLoginNotifier.value = true;
|
||||
try {
|
||||
await tbClient.login(
|
||||
LoginRequest(usernameController.text,
|
||||
passwordController.text));
|
||||
} catch (e) {
|
||||
_isLoginNotifier.value = false;
|
||||
}
|
||||
},
|
||||
child: Text(
|
||||
'Login',
|
||||
style: TextStyle(color: Colors.white, fontSize: 25),
|
||||
),
|
||||
),
|
||||
height: 24,
|
||||
),
|
||||
SizedBox(
|
||||
height: 130,
|
||||
),
|
||||
Text('New User? Create Account')
|
||||
Row(
|
||||
mainAxisAlignment: MainAxisAlignment.center,
|
||||
children: [
|
||||
Text('New User?',
|
||||
style: TextStyle(
|
||||
fontSize: 14,
|
||||
height: 14 / 20
|
||||
)),
|
||||
TextButton(
|
||||
onPressed: loading ? null : () {
|
||||
//TODO CREATE ACCOUNT SCREEN GOES HERE
|
||||
},
|
||||
child: Text(
|
||||
'Create Account',
|
||||
style: TextStyle(color: loading ? Colors.black12 : Theme.of(context).colorScheme.primary,
|
||||
letterSpacing: 1,
|
||||
fontSize: 14,
|
||||
height: 14 / 20),
|
||||
),
|
||||
)
|
||||
],
|
||||
)
|
||||
]
|
||||
)
|
||||
)
|
||||
@@ -160,3 +272,47 @@ class _LoginPageState extends TbPageState<LoginPage, _LoginPageState> {
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
class LoginPageBackground extends StatelessWidget {
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return SizedBox.expand(
|
||||
child: CustomPaint(
|
||||
painter: _LoginPageBackgroundPainter(color: Theme.of(context).primaryColor),
|
||||
)
|
||||
);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
class _LoginPageBackgroundPainter extends CustomPainter {
|
||||
|
||||
final Color color;
|
||||
|
||||
const _LoginPageBackgroundPainter({required this.color});
|
||||
|
||||
@override
|
||||
void paint(Canvas canvas, Size size) {
|
||||
final paint = Paint()..color = color.withAlpha(14);
|
||||
paint.style = PaintingStyle.fill;
|
||||
var topPath = Path();
|
||||
topPath.moveTo(0, 0);
|
||||
topPath.lineTo(size.width / 2, 0);
|
||||
topPath.lineTo(0, size.height / 10);
|
||||
topPath.close();
|
||||
canvas.drawPath(topPath, paint);
|
||||
var bottomPath = Path();
|
||||
bottomPath.moveTo(0, size.height * 0.98);
|
||||
bottomPath.lineTo(size.width, size.height * 0.78);
|
||||
bottomPath.lineTo(size.width, size.height);
|
||||
bottomPath.lineTo(0, size.height);
|
||||
bottomPath.close();
|
||||
canvas.drawPath(bottomPath, paint);
|
||||
}
|
||||
|
||||
@override
|
||||
bool shouldRepaint(covariant _LoginPageBackgroundPainter oldDelegate) {
|
||||
return color != oldDelegate.color;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -6,11 +6,12 @@ import 'package:fluro/fluro.dart';
|
||||
import 'package:flutter/foundation.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:logger/logger.dart';
|
||||
import 'package:package_info/package_info.dart';
|
||||
import 'package:thingsboard_app/constants/app_constants.dart';
|
||||
import 'package:thingsboard_app/modules/main/main_page.dart';
|
||||
import 'package:thingsboard_app/utils/services/widget_action_handler.dart';
|
||||
import 'package:thingsboard_client/thingsboard_client.dart';
|
||||
import 'package:thingsboard_app/utils/services/tb_secure_storage.dart';
|
||||
import 'package:thingsboard_app/constants/api_path.dart';
|
||||
import 'package:thingsboard_app/core/context/tb_context_widget.dart';
|
||||
|
||||
enum NotificationType {
|
||||
@@ -106,6 +107,7 @@ class TbContext {
|
||||
bool _initialized = false;
|
||||
bool isUserLoaded = false;
|
||||
final ValueNotifier<bool> _isAuthenticated = ValueNotifier(false);
|
||||
List<OAuth2ClientInfo>? oauth2Clients;
|
||||
User? userDetails;
|
||||
HomeDashboardInfo? homeDashboard;
|
||||
final _isLoadingNotifier = ValueNotifier<bool>(false);
|
||||
@@ -113,6 +115,7 @@ class TbContext {
|
||||
late final _widgetActionHandler;
|
||||
late final AndroidDeviceInfo? _androidInfo;
|
||||
late final IosDeviceInfo? _iosInfo;
|
||||
late final String packageName;
|
||||
TbMainDashboardHolder? _mainDashboardHolder;
|
||||
|
||||
GlobalKey<ScaffoldMessengerState> messengerKey = GlobalKey<ScaffoldMessengerState>();
|
||||
@@ -138,7 +141,7 @@ class TbContext {
|
||||
return true;
|
||||
}());
|
||||
_initialized = true;
|
||||
tbClient = ThingsboardClient(thingsBoardApiEndpoint,
|
||||
tbClient = ThingsboardClient(ThingsboardAppConstants.thingsBoardApiEndpoint,
|
||||
storage: TbSecureStorage(),
|
||||
onUserLoaded: onUserLoaded,
|
||||
onError: onError,
|
||||
@@ -151,6 +154,8 @@ class TbContext {
|
||||
} else if (Platform.isIOS) {
|
||||
_iosInfo = await deviceInfoPlugin.iosInfo;
|
||||
}
|
||||
PackageInfo packageInfo = await PackageInfo.fromPlatform();
|
||||
packageName = packageInfo.packageName;
|
||||
await tbClient.init();
|
||||
} catch (e, s) {
|
||||
log.error('Failed to init tbContext: $e', e, s);
|
||||
@@ -253,6 +258,7 @@ class TbContext {
|
||||
} else {
|
||||
userDetails = null;
|
||||
homeDashboard = null;
|
||||
oauth2Clients = await tbClient.getOAuth2Service().getOAuth2Clients(pkgName: packageName);
|
||||
}
|
||||
await updateRouteState();
|
||||
|
||||
@@ -265,6 +271,8 @@ class TbContext {
|
||||
|
||||
bool get isAuthenticated => _isAuthenticated.value;
|
||||
|
||||
bool get hasOAuthClients => oauth2Clients != null && oauth2Clients!.isNotEmpty;
|
||||
|
||||
Future<void> updateRouteState() async {
|
||||
if (currentState != null) {
|
||||
if (tbClient.isAuthenticated()) {
|
||||
@@ -314,6 +322,17 @@ class TbContext {
|
||||
}
|
||||
}
|
||||
|
||||
String userAgent() {
|
||||
String userAgent = 'Mozilla/5.0';
|
||||
if (Platform.isAndroid) {
|
||||
userAgent += ' (Linux; Android ${_androidInfo!.version.release}; ${_androidInfo!.model})';
|
||||
} else if (Platform.isIOS) {
|
||||
userAgent += ' (${_iosInfo!.model})';
|
||||
}
|
||||
userAgent += ' AppleWebKit/537.36 (KHTML, like Gecko) Version/4.0 Chrome/83.0.4103.106 Mobile Safari/537.36';
|
||||
return userAgent;
|
||||
}
|
||||
|
||||
bool isHomePage() {
|
||||
if (currentState != null) {
|
||||
if (currentState is TbMainState) {
|
||||
@@ -440,7 +459,7 @@ mixin HasTbContext {
|
||||
|
||||
Future<dynamic> navigateTo(String path, {bool replace = false, bool clearStack = false}) => _tbContext.navigateTo(path, replace: replace, clearStack: clearStack);
|
||||
|
||||
void pop<T>([T? result]) => _tbContext.pop<T>(result);
|
||||
void pop<T>([T? result, BuildContext? context]) => _tbContext.pop<T>(result, context);
|
||||
|
||||
Future<bool> maybePop<T extends Object?>([ T? result ]) => _tbContext.maybePop<T>(result);
|
||||
|
||||
|
||||
@@ -5,7 +5,7 @@ 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/api_path.dart';
|
||||
import 'package:thingsboard_app/constants/app_constants.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';
|
||||
@@ -135,7 +135,7 @@ class _DashboardState extends TbContextState<Dashboard, _DashboardState> {
|
||||
void _onAuthenticated() async {
|
||||
if (tbContext.isAuthenticated) {
|
||||
if (!readyState.value) {
|
||||
_initialUrl = Uri.parse(thingsBoardApiEndpoint + '?accessToken=${tbClient.getJwtToken()!}&refreshToken=${tbClient.getRefreshToken()!}');
|
||||
_initialUrl = Uri.parse(ThingsboardAppConstants.thingsBoardApiEndpoint + '?accessToken=${tbClient.getJwtToken()!}&refreshToken=${tbClient.getRefreshToken()!}');
|
||||
readyState.value = true;
|
||||
} else {
|
||||
var windowMessage = <String, dynamic>{
|
||||
|
||||
27
pubspec.lock
27
pubspec.lock
@@ -172,6 +172,13 @@ packages:
|
||||
description: flutter
|
||||
source: sdk
|
||||
version: "0.0.0"
|
||||
flutter_web_auth:
|
||||
dependency: "direct main"
|
||||
description:
|
||||
name: flutter_web_auth
|
||||
url: "https://pub.dartlang.org"
|
||||
source: hosted
|
||||
version: "0.3.0"
|
||||
flutter_web_plugins:
|
||||
dependency: transitive
|
||||
description: flutter
|
||||
@@ -225,7 +232,7 @@ packages:
|
||||
name: image_picker
|
||||
url: "https://pub.dartlang.org"
|
||||
source: hosted
|
||||
version: "0.8.0+1"
|
||||
version: "0.8.0+3"
|
||||
image_picker_for_web:
|
||||
dependency: transitive
|
||||
description:
|
||||
@@ -282,6 +289,13 @@ packages:
|
||||
url: "https://pub.dartlang.org"
|
||||
source: hosted
|
||||
version: "0.12.10"
|
||||
material_design_icons_flutter:
|
||||
dependency: "direct main"
|
||||
description:
|
||||
name: material_design_icons_flutter
|
||||
url: "https://pub.dartlang.org"
|
||||
source: hosted
|
||||
version: "5.0.5955-rc.1"
|
||||
meta:
|
||||
dependency: transitive
|
||||
description:
|
||||
@@ -296,6 +310,13 @@ packages:
|
||||
url: "https://pub.dartlang.org"
|
||||
source: hosted
|
||||
version: "1.0.0"
|
||||
package_info:
|
||||
dependency: "direct main"
|
||||
description:
|
||||
name: package_info
|
||||
url: "https://pub.dartlang.org"
|
||||
source: hosted
|
||||
version: "2.0.2"
|
||||
path:
|
||||
dependency: transitive
|
||||
description:
|
||||
@@ -411,7 +432,7 @@ packages:
|
||||
description:
|
||||
path: "."
|
||||
ref: HEAD
|
||||
resolved-ref: "0fbaccafa7c0b3b3a6c9ac689c5949164101c5b5"
|
||||
resolved-ref: "00f08109b44b926ab6defaed12b8d4fdc44e07b0"
|
||||
url: "git@github.com:thingsboard/dart_thingsboard_client.git"
|
||||
source: git
|
||||
version: "1.0.0"
|
||||
@@ -477,7 +498,7 @@ packages:
|
||||
name: xml
|
||||
url: "https://pub.dartlang.org"
|
||||
source: hosted
|
||||
version: "5.1.1"
|
||||
version: "5.1.2"
|
||||
yaml:
|
||||
dependency: transitive
|
||||
description:
|
||||
|
||||
@@ -34,6 +34,9 @@ dependencies:
|
||||
qr_code_scanner: ^0.5.1
|
||||
device_info: ^2.0.0
|
||||
geolocator: ^7.0.3
|
||||
material_design_icons_flutter: ^5.0.5955-rc.1
|
||||
flutter_web_auth: ^0.3.0
|
||||
package_info: ^2.0.2
|
||||
|
||||
dev_dependencies:
|
||||
flutter_test:
|
||||
|
||||
Reference in New Issue
Block a user