UI changes, Logs viewer
version bumped to 0.2.0
This commit is contained in:
parent
c0a8012357
commit
9e1e60492e
147
lib/LogsPage.dart
Normal file
147
lib/LogsPage.dart
Normal file
|
@ -0,0 +1,147 @@
|
||||||
|
import 'dart:convert';
|
||||||
|
|
||||||
|
import 'package:flutter/material.dart';
|
||||||
|
import 'package:http/http.dart' as http;
|
||||||
|
import 'package:shared_preferences/shared_preferences.dart';
|
||||||
|
import 'package:OpenshockCompanion/settings_page.dart' show SettingsPage;
|
||||||
|
class LogsPage extends StatefulWidget {
|
||||||
|
const LogsPage({Key? key}) : super(key: key);
|
||||||
|
|
||||||
|
@override
|
||||||
|
_LogsPageState createState() => _LogsPageState();
|
||||||
|
}
|
||||||
|
|
||||||
|
class _LogsPageState extends State<LogsPage> {
|
||||||
|
List<Map<String, dynamic>> logs = [];
|
||||||
|
|
||||||
|
@override
|
||||||
|
void initState() {
|
||||||
|
super.initState();
|
||||||
|
fetchLogs();
|
||||||
|
}
|
||||||
|
|
||||||
|
Future<void> fetchLogs() async {
|
||||||
|
final SharedPreferences prefs = await SharedPreferences.getInstance();
|
||||||
|
final apiKey = prefs.getString('apiKey');
|
||||||
|
final shockerId = prefs.getString('shockerId');
|
||||||
|
|
||||||
|
if (apiKey == null || shockerId == null) {
|
||||||
|
// Handle missing API key or shockerId
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
final url = 'https://api.shocklink.net/1/shockers/$shockerId/logs?offset=0&limit=30';
|
||||||
|
|
||||||
|
final response = await http.get(Uri.parse(url), headers: {
|
||||||
|
'accept': 'application/json',
|
||||||
|
'OpenShockToken': apiKey,
|
||||||
|
});
|
||||||
|
|
||||||
|
if (response.statusCode == 200) {
|
||||||
|
final jsonData = json.decode(response.body);
|
||||||
|
|
||||||
|
if (jsonData['data'] != null) {
|
||||||
|
setState(() {
|
||||||
|
logs = List<Map<String, dynamic>>.from(jsonData['data']);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
// Handle API request error
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Future<void> _handleRefresh() async {
|
||||||
|
await fetchLogs();
|
||||||
|
}
|
||||||
|
|
||||||
|
@override
|
||||||
|
Widget build(BuildContext context) {
|
||||||
|
return Scaffold(
|
||||||
|
appBar: AppBar(
|
||||||
|
title: const Text('Logs'),
|
||||||
|
),
|
||||||
|
body: RefreshIndicator(
|
||||||
|
onRefresh: _handleRefresh,
|
||||||
|
child: Scrollbar(
|
||||||
|
thumbVisibility: true,
|
||||||
|
child: Padding(
|
||||||
|
padding: const EdgeInsets.all(16.0),
|
||||||
|
child: Column(
|
||||||
|
crossAxisAlignment: CrossAxisAlignment.start,
|
||||||
|
children: [
|
||||||
|
const Text('Logs'),
|
||||||
|
Expanded(
|
||||||
|
child: SingleChildScrollView(
|
||||||
|
child: DataTable(
|
||||||
|
columns: const [
|
||||||
|
DataColumn(label: Text('Name')),
|
||||||
|
DataColumn(label: Text('Intensity')),
|
||||||
|
DataColumn(label: Text('Duration (s)')),
|
||||||
|
],
|
||||||
|
rows: logs.map((log) {
|
||||||
|
final controlledBy = log['controlledBy'] as Map<String, dynamic>?;
|
||||||
|
|
||||||
|
// Add null check for controlledBy
|
||||||
|
if (controlledBy != null) {
|
||||||
|
final name = controlledBy['name'] as String?;
|
||||||
|
final intensity = log['intensity'] as int?;
|
||||||
|
final duration = (log['duration'] as int?)! / 1000; // Convert to seconds
|
||||||
|
|
||||||
|
// Add null checks for name and intensity
|
||||||
|
if (name != null && intensity != null) {
|
||||||
|
return DataRow(
|
||||||
|
cells: [
|
||||||
|
DataCell(Text(name)),
|
||||||
|
DataCell(Text(intensity.toString())),
|
||||||
|
DataCell(Text(duration.toString())),
|
||||||
|
],
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return const DataRow(cells: []);
|
||||||
|
}).toList(),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
bottomNavigationBar: BottomNavigationBar(
|
||||||
|
items: const [
|
||||||
|
BottomNavigationBarItem(
|
||||||
|
icon: Icon(Icons.home),
|
||||||
|
label: 'Home',
|
||||||
|
),
|
||||||
|
BottomNavigationBarItem(
|
||||||
|
icon: Icon(Icons.settings),
|
||||||
|
label: 'Settings',
|
||||||
|
),
|
||||||
|
BottomNavigationBarItem(
|
||||||
|
icon: Icon(Icons.list),
|
||||||
|
label: 'Logs',
|
||||||
|
),
|
||||||
|
],
|
||||||
|
currentIndex: 2, // Set the current index to 2 for the Logs page
|
||||||
|
onTap: (index) {
|
||||||
|
if (index == 0) {
|
||||||
|
// Navigate back to the main page
|
||||||
|
Navigator.popUntil(context, (route) => route.isFirst);
|
||||||
|
} else if (index == 1) {
|
||||||
|
// Navigate to the Settings page
|
||||||
|
Navigator.push(
|
||||||
|
context,
|
||||||
|
MaterialPageRoute(builder: (context) => const SettingsPage()),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
},
|
||||||
|
selectedItemColor: const Color.fromARGB(255, 211, 187, 255), // or any color you prefer
|
||||||
|
unselectedItemColor: Theme.of(context).textTheme.bodySmall?.color,
|
||||||
|
showUnselectedLabels: true,
|
||||||
|
selectedLabelStyle: const TextStyle(fontWeight: FontWeight.bold),
|
||||||
|
),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
|
@ -2,6 +2,7 @@ import 'package:flutter/material.dart';
|
||||||
import 'package:flutter/services.dart';
|
import 'package:flutter/services.dart';
|
||||||
import 'package:fluttertoast/fluttertoast.dart';
|
import 'package:fluttertoast/fluttertoast.dart';
|
||||||
import 'package:OpenshockCompanion/settings_page.dart' show SettingsPage;
|
import 'package:OpenshockCompanion/settings_page.dart' show SettingsPage;
|
||||||
|
import 'package:OpenshockCompanion/LogsPage.dart' show LogsPage;
|
||||||
import 'package:OpenshockCompanion/api_handler.dart' show sendApiRequest;
|
import 'package:OpenshockCompanion/api_handler.dart' show sendApiRequest;
|
||||||
import 'package:shared_preferences/shared_preferences.dart';
|
import 'package:shared_preferences/shared_preferences.dart';
|
||||||
|
|
||||||
|
@ -10,7 +11,7 @@ void main() {
|
||||||
}
|
}
|
||||||
|
|
||||||
class MyApp extends StatelessWidget {
|
class MyApp extends StatelessWidget {
|
||||||
const MyApp({super.key});
|
const MyApp({Key? key}) : super(key: key);
|
||||||
|
|
||||||
@override
|
@override
|
||||||
Widget build(BuildContext context) {
|
Widget build(BuildContext context) {
|
||||||
|
@ -36,7 +37,7 @@ class MyApp extends StatelessWidget {
|
||||||
}
|
}
|
||||||
|
|
||||||
class SliderPage extends StatefulWidget {
|
class SliderPage extends StatefulWidget {
|
||||||
const SliderPage({super.key});
|
const SliderPage({Key? key}) : super(key: key);
|
||||||
|
|
||||||
@override
|
@override
|
||||||
_SliderPageState createState() => _SliderPageState();
|
_SliderPageState createState() => _SliderPageState();
|
||||||
|
@ -45,6 +46,7 @@ class SliderPage extends StatefulWidget {
|
||||||
class _SliderPageState extends State<SliderPage> {
|
class _SliderPageState extends State<SliderPage> {
|
||||||
int intensityValue = 0;
|
int intensityValue = 0;
|
||||||
int timeValue = 0;
|
int timeValue = 0;
|
||||||
|
int currentIndex = 0;
|
||||||
|
|
||||||
@override
|
@override
|
||||||
Widget build(BuildContext context) {
|
Widget build(BuildContext context) {
|
||||||
|
@ -124,18 +126,49 @@ class _SliderPageState extends State<SliderPage> {
|
||||||
),
|
),
|
||||||
],
|
],
|
||||||
),
|
),
|
||||||
ElevatedButton(
|
|
||||||
child: const Text('Settings'),
|
|
||||||
onPressed: () {
|
|
||||||
Navigator.push(
|
|
||||||
context,
|
|
||||||
MaterialPageRoute(builder: (context) => const SettingsPage()),
|
|
||||||
);
|
|
||||||
},
|
|
||||||
),
|
|
||||||
],
|
],
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
|
bottomNavigationBar: BottomNavigationBar(
|
||||||
|
currentIndex: currentIndex,
|
||||||
|
onTap: (index) {
|
||||||
|
setState(() {
|
||||||
|
currentIndex = index;
|
||||||
|
if (index == 0) {
|
||||||
|
// Home tab
|
||||||
|
} else if (index == 1) {
|
||||||
|
// Settings tab
|
||||||
|
Navigator.push(
|
||||||
|
context,
|
||||||
|
MaterialPageRoute(builder: (context) => const SettingsPage()),
|
||||||
|
);
|
||||||
|
} else if (index == 2) {
|
||||||
|
Navigator.push(
|
||||||
|
context,
|
||||||
|
MaterialPageRoute(builder: (context) => const LogsPage()),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
},
|
||||||
|
items: const [
|
||||||
|
BottomNavigationBarItem(
|
||||||
|
icon: Icon(Icons.home),
|
||||||
|
label: 'Home',
|
||||||
|
),
|
||||||
|
BottomNavigationBarItem(
|
||||||
|
icon: Icon(Icons.settings),
|
||||||
|
label: 'Settings',
|
||||||
|
),
|
||||||
|
BottomNavigationBarItem(
|
||||||
|
icon: Icon(Icons.list),
|
||||||
|
label: 'Logs',
|
||||||
|
),
|
||||||
|
],
|
||||||
|
selectedItemColor: const Color.fromARGB(255, 211, 187, 255), // or any color you prefer
|
||||||
|
unselectedItemColor: Theme.of(context).textTheme.bodySmall?.color,
|
||||||
|
showUnselectedLabels: true,
|
||||||
|
selectedLabelStyle: const TextStyle(fontWeight: FontWeight.bold),
|
||||||
|
),
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -180,4 +213,4 @@ final ThemeData darkTheme = ThemeData(
|
||||||
seedColor: Colors.deepPurple,
|
seedColor: Colors.deepPurple,
|
||||||
brightness: Brightness.dark,
|
brightness: Brightness.dark,
|
||||||
),
|
),
|
||||||
);
|
);
|
|
@ -1,9 +1,9 @@
|
||||||
// settings_page.dart
|
import 'package:OpenshockCompanion/LogsPage.dart';
|
||||||
import 'package:flutter/material.dart';
|
import 'package:flutter/material.dart';
|
||||||
import 'package:shared_preferences/shared_preferences.dart';
|
import 'package:shared_preferences/shared_preferences.dart';
|
||||||
|
|
||||||
class SettingsPage extends StatefulWidget {
|
class SettingsPage extends StatefulWidget {
|
||||||
const SettingsPage({super.key});
|
const SettingsPage({Key? key});
|
||||||
|
|
||||||
@override
|
@override
|
||||||
_SettingsPageState createState() => _SettingsPageState();
|
_SettingsPageState createState() => _SettingsPageState();
|
||||||
|
@ -106,6 +106,38 @@ class _SettingsPageState extends State<SettingsPage> {
|
||||||
],
|
],
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
|
bottomNavigationBar: BottomNavigationBar(
|
||||||
|
items: const [
|
||||||
|
BottomNavigationBarItem(
|
||||||
|
icon: Icon(Icons.home),
|
||||||
|
label: 'Home',
|
||||||
|
),
|
||||||
|
BottomNavigationBarItem(
|
||||||
|
icon: Icon(Icons.settings),
|
||||||
|
label: 'Settings',
|
||||||
|
),
|
||||||
|
BottomNavigationBarItem(
|
||||||
|
icon: Icon(Icons.list),
|
||||||
|
label: 'Logs',
|
||||||
|
),
|
||||||
|
],
|
||||||
|
currentIndex: 1, // Set the current index to 1 for the Settings page
|
||||||
|
onTap: (index) {
|
||||||
|
if (index == 0) {
|
||||||
|
// Navigate back to the main page
|
||||||
|
Navigator.popUntil(context, (route) => route.isFirst);
|
||||||
|
} else if (index == 2) {
|
||||||
|
Navigator.push(
|
||||||
|
context,
|
||||||
|
MaterialPageRoute(builder: (context) => const LogsPage()),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
},
|
||||||
|
selectedItemColor: const Color.fromARGB(255, 211, 187, 255), // or any color you prefer
|
||||||
|
unselectedItemColor: Theme.of(context).textTheme.caption?.color,
|
||||||
|
showUnselectedLabels: true,
|
||||||
|
selectedLabelStyle: TextStyle(fontWeight: FontWeight.bold),
|
||||||
|
),
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -4,7 +4,7 @@ description: Companion app for managing openshock-compatible devices
|
||||||
# pub.dev using `flutter pub publish`. This is preferred for private packages.
|
# pub.dev using `flutter pub publish`. This is preferred for private packages.
|
||||||
publish_to: 'none' # Remove this line if you wish to publish to pub.dev
|
publish_to: 'none' # Remove this line if you wish to publish to pub.dev
|
||||||
|
|
||||||
version: 0.1.4
|
version: 0.2.0
|
||||||
|
|
||||||
environment:
|
environment:
|
||||||
sdk: '>=3.1.2 <4.0.0'
|
sdk: '>=3.1.2 <4.0.0'
|
||||||
|
|
Loading…
Reference in a new issue