Initial commit

This commit is contained in:
Igor Kulikov
2021-04-23 19:35:13 +03:00
parent 422cfb3b0b
commit 2212d9db7c
81 changed files with 2873 additions and 26 deletions

View File

@@ -0,0 +1,147 @@
import 'dart:ui';
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_client/thingsboard_client.dart';
import 'package:thingsboard_app/core/context/tb_context.dart';
import 'package:thingsboard_app/core/context/tb_context_widget.dart';
class LoginPage extends TbPageWidget<LoginPage, _LoginPageState> {
LoginPage(TbContext tbContext) : super(tbContext);
@override
_LoginPageState createState() => _LoginPageState();
}
class _LoginPageState extends TbPageState<LoginPage, _LoginPageState> {
final usernameController = TextEditingController();
final passwordController = TextEditingController();
@override
void initState() {
super.initState();
}
@override
void dispose() {
usernameController.dispose();
passwordController.dispose();
super.dispose();
}
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: const Text('Login to ThingsBoard'),
),
body: ValueListenableBuilder(
valueListenable: loadingNotifier,
builder: (BuildContext context, bool loading, child) {
List<Widget> children = [
SingleChildScrollView(
child: Column(
children: [
Padding(
padding: const EdgeInsets.only(top: 60.0),
child: Center(
child: Container(
width: 300,
height: 150,
child: SvgPicture.asset(ThingsboardImage.thingsBoardLogoBlue,
semanticsLabel: 'ThingsBoard Logo')
)
)
),
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'),
),
),
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'),
),
),
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, 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 : () {
tbContext.tbClient.login(
LoginRequest(usernameController.text, passwordController.text));
},
child: Text(
'Login',
style: TextStyle(color: Colors.white, fontSize: 25),
),
),
),
SizedBox(
height: 130,
),
Text('New User? Create Account')
]
)
)
];
if (loading) {
children.add(
SizedBox.expand(
child: ClipRect(
child: BackdropFilter(
filter: ImageFilter.blur(sigmaX: 5.0, sigmaY: 5.0),
child: Container(
decoration: new BoxDecoration(
color: Colors.grey.shade200.withOpacity(0.2)
),
child: Center(
child: CircularProgressIndicator(),
),
)
)
)
)
);
//children.add(Center(child: CircularProgressIndicator()));
}
return Stack(
children: children,
);
})
);
}
}

View File

@@ -0,0 +1,202 @@
import 'dart:async';
import 'package:fluro/fluro.dart';
import 'package:flutter/foundation.dart';
import 'package:flutter/material.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 {
info,
warn,
success,
error
}
class TbContext {
bool _initialized = false;
bool isUserLoaded = false;
bool isAuthenticated = false;
final _isLoadingNotifier = ValueNotifier<bool>(false);
GlobalKey<ScaffoldMessengerState> messengerKey = GlobalKey<ScaffoldMessengerState>();
late ThingsboardClient tbClient;
final FluroRouter router;
final RouteObserver<PageRoute> routeObserver;
TbContextState? currentState;
TbContext(this.router, this.routeObserver);
void init() {
assert(() {
if (_initialized) {
throw StateError('TbContext already initialized!');
}
return true;
}());
_initialized = true;
tbClient = ThingsboardClient(thingsBoardApiEndpoint,
storage: TbSecureStorage(),
onUserLoaded: onUserLoaded,
onError: onError,
onLoadStarted: onLoadStarted,
onLoadFinished: onLoadFinished,
computeFunc: <Q, R>(callback, message) => compute(callback, message));
tbClient.init().onError((error, stackTrace) {
print('Error: $error');
print('Stack: $stackTrace');
});
}
void onError(ThingsboardError error) {
print('onError: error=$error');
showErrorNotification(error.message!);
}
void showErrorNotification(String message, {Duration? duration}) {
showNotification(message, NotificationType.error, duration: duration);
}
void showInfoNotification(String message, {Duration? duration}) {
showNotification(message, NotificationType.info, duration: duration);
}
void showWarnNotification(String message, {Duration? duration}) {
showNotification(message, NotificationType.warn, duration: duration);
}
void showSuccessNotification(String message, {Duration? duration}) {
showNotification(message, NotificationType.success, duration: duration);
}
void showNotification(String message, NotificationType type, {Duration? duration}) {
duration ??= const Duration(days: 1);
Color backgroundColor;
var textColor = Color(0xFFFFFFFF);
switch(type) {
case NotificationType.info:
backgroundColor = Color(0xFF323232);
break;
case NotificationType.warn:
backgroundColor = Color(0xFFdc6d1b);
break;
case NotificationType.success:
backgroundColor = Color(0xFF008000);
break;
case NotificationType.error:
backgroundColor = Color(0xFF800000);
break;
}
final snackBar = SnackBar(
duration: duration,
backgroundColor: backgroundColor,
content: Text(message,
style: TextStyle(
color: textColor
),
),
action: SnackBarAction(
label: 'Close',
textColor: textColor,
onPressed: () {
messengerKey.currentState!.hideCurrentSnackBar(reason: SnackBarClosedReason.dismiss);
},
),
);
messengerKey.currentState!.removeCurrentSnackBar();
messengerKey.currentState!.showSnackBar(snackBar);
}
void hideNotification() {
messengerKey.currentState!.removeCurrentSnackBar();
}
void onLoadStarted() {
print('ON LOAD STARTED!');
_isLoadingNotifier.value = true;
}
void onLoadFinished() {
print('ON LOAD FINISHED!');
_isLoadingNotifier.value = false;
}
Future<void> onUserLoaded() async {
try {
print('onUserLoaded: isAuthenticated=${tbClient.isAuthenticated()}');
isUserLoaded = true;
isAuthenticated = tbClient.isAuthenticated();
if (tbClient.isAuthenticated()) {
print('authUser: ${tbClient.getAuthUser()}');
}
updateRouteState();
} catch (e, s) {
print('Error: $e');
print('Stack: $s');
}
}
void updateRouteState() {
if (currentState != null) {
if (tbClient.isAuthenticated()) {
navigateTo('/home', replace: true);
} else {
navigateTo('/login', replace: true, clearStack: true, transition: TransitionType.inFromTop);
}
}
}
void navigateTo(String path, {bool replace = false, bool clearStack = false, TransitionType? transition}) {
if (currentState != null) {
if (transition == null) {
transition = TransitionType.inFromRight;
}
hideNotification();
router.navigateTo(currentState!.context, path, transition: transition, replace: replace, clearStack: clearStack);
}
}
void pop() {
if (currentState != null) {
router.pop(currentState!.context);
}
}
}
mixin HasTbContext {
late final TbContext _tbContext;
void setTbContext(TbContext tbContext) {
_tbContext = tbContext;
}
void setupTbContext(TbContextState currentState) {
_tbContext = currentState.widget.tbContext;
}
void setupCurrentState(TbContextState currentState) {
_tbContext.currentState = currentState;
}
ValueNotifier<bool> get loadingNotifier => _tbContext._isLoadingNotifier;
TbContext get tbContext => _tbContext;
void navigateTo(String path, {bool replace = false}) => _tbContext.navigateTo(path, replace: replace);
void pop() => _tbContext.pop();
void hideNotification() => _tbContext.hideNotification();
void showErrorNotification(String message, {Duration? duration}) => _tbContext.showErrorNotification(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 showSuccessNotification(String message, {Duration? duration}) => _tbContext.showSuccessNotification(message, duration: duration);
}

View File

@@ -0,0 +1,68 @@
import 'package:flutter/widgets.dart';
import 'package:thingsboard_app/core/context/tb_context.dart';
abstract class TbContextStatelessWidget extends StatelessWidget with HasTbContext {
TbContextStatelessWidget(TbContext tbContext, {Key? key}) : super(key: key) {
setTbContext(tbContext);
}
}
abstract class TbContextWidget<W extends TbContextWidget<W,S>, S extends TbContextState<W,S>> extends StatefulWidget with HasTbContext {
TbContextWidget(TbContext tbContext, {Key? key}) : super(key: key) {
setTbContext(tbContext);
}
}
abstract class TbContextState<W extends TbContextWidget<W,S>, S extends TbContextState<W,S>> extends State<W> with HasTbContext {
final bool handleLoading;
TbContextState({this.handleLoading = false});
@override
void initState() {
super.initState();
setupTbContext(this);
}
@override
void dispose() {
super.dispose();
}
void updateState() {
setState(() {});
}
}
abstract class TbPageWidget<W extends TbPageWidget<W,S>, S extends TbPageState<W,S>> extends TbContextWidget<W,S> {
TbPageWidget(TbContext tbContext, {Key? key}) : super(tbContext, key: key);
}
abstract class TbPageState<W extends TbPageWidget<W,S>, S extends TbPageState<W,S>> extends TbContextState<W,S> with RouteAware {
TbPageState({bool handleUserLoaded = false}): super(handleLoading: true);
@override
void didChangeDependencies() {
super.didChangeDependencies();
tbContext.routeObserver.subscribe(this, ModalRoute.of(context) as PageRoute);
}
@override
void dispose() {
tbContext.routeObserver.unsubscribe(this);
super.dispose();
}
@override
void didPush() {
setupCurrentState(this);
}
@override
void didPopNext() {
tbContext.hideNotification();
setupCurrentState(this);
}
}

View File

@@ -0,0 +1,34 @@
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 ThingsboardInitApp extends TbPageWidget<ThingsboardInitApp, _ThingsboardInitAppState> {
ThingsboardInitApp(TbContext tbContext, {Key? key}) : super(tbContext, key: key);
@override
_ThingsboardInitAppState createState() => _ThingsboardInitAppState();
}
class _ThingsboardInitAppState extends TbPageState<ThingsboardInitApp, _ThingsboardInitAppState> {
@override
void initState() {
super.initState();
tbContext.init();
}
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: const Text('ThingsBoard Init'),
),
body: Center(
child: CircularProgressIndicator()
)
);
}
}