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,328 @@
import 'package:flutter/material.dart';
import 'package:flutter/widgets.dart';
import 'package:flutter_speed_dial/flutter_speed_dial.dart';
import 'package:infinite_scroll_pagination/infinite_scroll_pagination.dart';
import 'package:thingsboard_app/widgets/tb_app_bar.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 DeviceInfoCard extends StatelessWidget {
final DeviceInfo device;
final void Function(DeviceInfo device)? onDetails;
DeviceInfoCard(this.device, {this.onDetails});
@override
Widget build(BuildContext context) {
return Card(
child: Padding(
padding: const EdgeInsets.symmetric(vertical: 5.0),
child: ListTile(
title: Text('${device.name}'),
subtitle: Text('${device.type}'),
trailing: IconButton(
icon: Icon(Icons.navigate_next),
onPressed: () {
if (onDetails != null) {
onDetails!(device);
}
},
),
)
)
);
}
}
class FirstPageExceptionIndicator extends StatelessWidget {
const FirstPageExceptionIndicator({
required this.title,
this.message,
this.onTryAgain,
Key? key,
}) : super(key: key);
final String title;
final String? message;
final VoidCallback? onTryAgain;
@override
Widget build(BuildContext context) {
final message = this.message;
return Center(
child: Padding(
padding: const EdgeInsets.symmetric(vertical: 32, horizontal: 16),
child: Column(
children: [
Text(
title,
textAlign: TextAlign.center,
style: Theme.of(context).textTheme.headline6,
),
if (message != null)
const SizedBox(
height: 16,
),
if (message != null)
Text(
message,
textAlign: TextAlign.center,
),
if (onTryAgain != null)
const SizedBox(
height: 48,
),
if (onTryAgain != null)
SizedBox(
height: 50,
width: double.infinity,
child: ElevatedButton.icon(
onPressed: onTryAgain,
icon: const Icon(
Icons.refresh,
color: Colors.white,
),
label: const Text(
'Try Again',
style: TextStyle(
fontSize: 16,
color: Colors.white,
),
),
),
),
],
),
),
);
}
}
class DevicesPage extends TbPageWidget<DevicesPage, _DevicesPageState> {
DevicesPage(TbContext tbContext) : super(tbContext);
@override
_DevicesPageState createState() => _DevicesPageState();
}
class _DevicesPageState extends TbPageState<DevicesPage, _DevicesPageState> {
final _searchModeNotifier = ValueNotifier<bool>(false);
final PagingController<PageLink, DeviceInfo> _pagingController = PagingController(firstPageKey: PageLink(10));
@override
void initState() {
super.initState();
_pagingController.addPageRequestListener((pageKey) {
_fetchPage(pageKey);
});
}
@override
void dispose() {
_pagingController.dispose();
super.dispose();
}
bool dataLoading = false;
bool scheduleRefresh = false;
void refresh() {
if (dataLoading) {
scheduleRefresh = true;
} else {
_pagingController.refresh();
}
}
Future<void> _fetchPage(PageLink pageKey) async {
dataLoading = true;
try {
hideNotification();
final pageData = await tbContext.tbClient.getDeviceService().getTenantDeviceInfos(pageKey);
final isLastPage = !pageData.hasNext;
if (isLastPage) {
_pagingController.appendLastPage(pageData.data);
} else {
final nextPageKey = pageKey.nextPageLink();
_pagingController.appendPage(pageData.data, nextPageKey);
}
} catch (error) {
_pagingController.error = error;
} finally {
dataLoading = false;
if (scheduleRefresh) {
scheduleRefresh = false;
_pagingController.refresh();
}
}
}
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: TbAppBar(
tbContext,
title: const Text('Devices'),
searchModeNotifier: _searchModeNotifier,
searchHint: 'Search devices',
onSearch: (String searchText) {
_pagingController.firstPageKey.textSearch = searchText;
refresh();
},
),
body: Builder(
builder: (BuildContext context) {
return PagedListView(
pagingController: _pagingController,
builderDelegate: PagedChildBuilderDelegate<DeviceInfo>(
itemBuilder: (context, item, index) {
return DeviceInfoCard(
item,
onDetails: (device) {
print('open details: $device');
},
);
},
noMoreItemsIndicatorBuilder: (context) => FirstPageExceptionIndicator(
title: 'No more devices'
),
noItemsFoundIndicatorBuilder: (context) => FirstPageExceptionIndicator(
title: 'No devices found',
message: 'The list is currently empty.',
onTryAgain: () => refresh(),
)
),
);
}
),
bottomNavigationBar: BottomAppBar(
shape: CircularNotchedRectangle(),
notchMargin: 4.0,
child: new Row(
children: <Widget>[
IconButton(icon: Icon(Icons.refresh), onPressed: () {
refresh();
},),
Spacer(),
IconButton(icon: Icon(Icons.search), onPressed: () {
_searchModeNotifier.value = true;
}),
_simplePopup(),
],
),
),
floatingActionButtonLocation: FloatingActionButtonLocation.centerDocked,
floatingActionButton: FloatingActionButton(
child: const Icon(Icons.add), onPressed: () {},),
/* SpeedDial(
animatedIcon: AnimatedIcons.menu_close,
animatedIconTheme: IconThemeData(size: 22),
backgroundColor: Theme.of(context).colorScheme.secondary,
foregroundColor: Colors.white,
visible: true,
curve: Curves.bounceIn,
children: [
// FAB 1
SpeedDialChild(
child: Icon(Icons.refresh),
backgroundColor: Theme.of(context).colorScheme.secondary,
foregroundColor: Colors.white,
onTap: () {
refresh();
/* setState(() {
var rng = Random();
var pageSize = 1 + rng.nextInt(9);
futureDevices = tbContext.tbClient.getDeviceService().getTenantDeviceInfos(PageLink(pageSize));
}); */
},
label: 'Refresh',
labelStyle: TextStyle(
fontWeight: FontWeight.w500,
fontSize: 16.0),
),
// FAB 2
SpeedDialChild(
child: Icon(Icons.logout),
backgroundColor: Theme.of(context).colorScheme.secondary,
foregroundColor: Colors.white,
onTap: () {
tbContext.tbClient.logout(requestConfig: RequestConfig(ignoreErrors: true));
},
label: 'Logout',
labelStyle: TextStyle(
fontWeight: FontWeight.w500,
fontSize: 16.0),
)
],
)*/
);
}
Widget _simplePopup() => PopupMenuButton<int>(
itemBuilder: (context) => [
PopupMenuItem(
value: 1,
child: Text("First"),
),
PopupMenuItem(
value: 2,
child: ListTile(
leading: Icon(Icons.work),
title: Text('Second'),
)
),
],
icon: Icon(Icons.settings),
);
SpeedDial speedDial(context) => SpeedDial(
animatedIcon: AnimatedIcons.menu_close,
animatedIconTheme: IconThemeData(size: 22),
backgroundColor: Theme.of(context).colorScheme.secondary,
foregroundColor: Colors.white,
visible: true,
curve: Curves.bounceIn,
children: [
// FAB 1
SpeedDialChild(
child: Icon(Icons.refresh),
backgroundColor: Theme.of(context).colorScheme.secondary,
foregroundColor: Colors.white,
onTap: () {
refresh();
/* setState(() {
var rng = Random();
var pageSize = 1 + rng.nextInt(9);
futureDevices = tbContext.tbClient.getDeviceService().getTenantDeviceInfos(PageLink(pageSize));
}); */
},
label: 'Refresh',
labelStyle: TextStyle(
fontWeight: FontWeight.w500,
fontSize: 16.0),
),
// FAB 2
SpeedDialChild(
child: Icon(Icons.logout),
backgroundColor: Theme.of(context).colorScheme.secondary,
foregroundColor: Colors.white,
onTap: () {
tbContext.tbClient.logout(requestConfig: RequestConfig(ignoreErrors: true));
},
label: 'Logout',
labelStyle: TextStyle(
fontWeight: FontWeight.w500,
fontSize: 16.0),
)
],
);
}

View File

@@ -0,0 +1,48 @@
import 'package:flutter/material.dart';
import 'package:flutter/widgets.dart';
import 'package:thingsboard_app/widgets/tb_app_bar.dart';
import 'package:thingsboard_app/core/context/tb_context.dart';
import 'package:thingsboard_app/core/context/tb_context_widget.dart';
class HomePage extends TbPageWidget<HomePage, _HomePageState> {
HomePage(TbContext tbContext) : super(tbContext);
@override
_HomePageState createState() => _HomePageState();
}
class _HomePageState extends TbPageState<HomePage, _HomePageState> {
@override
void initState() {
super.initState();
}
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: TbAppBar(
tbContext,
title: const Text('ThingsBoard'),
),
body: Builder(
builder: (BuildContext context) {
return Center(child:
Column(
children: [
ElevatedButton(
child: Text('Devices'),
onPressed: () {
navigateTo('/devices');
},
)
],
)
);
}),
);
}
}

View File

@@ -0,0 +1,39 @@
import 'package:flutter/material.dart';
import 'package:flutter/widgets.dart';
import 'package:thingsboard_app/widgets/tb_app_bar.dart';
import 'package:thingsboard_app/core/context/tb_context.dart';
import 'package:thingsboard_app/core/context/tb_context_widget.dart';
class ProfilePage extends TbPageWidget<ProfilePage, _ProfilePageState> {
ProfilePage(TbContext tbContext) : super(tbContext);
@override
_ProfilePageState createState() => _ProfilePageState();
}
class _ProfilePageState extends TbPageState<ProfilePage, _ProfilePageState> {
@override
void initState() {
super.initState();
}
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: TbAppBar(
tbContext,
title: const Text('Profile'),
showProfile: false,
showLogout: true,
),
body: Builder(
builder: (BuildContext context) {
return Center(child: const Text('TODO: Implement!'));
}),
);
}
}