Changed default behavior of bottom bar

Commit also includes:
- Initial implementation of shocker limits
- minor code cleanup
- version bump to 2.2

**Release 2.5 due very soon!**
This commit is contained in:
Mercurio 2023-11-13 09:31:28 +01:00
parent 9e1e60492e
commit e3fc6acf32
7 changed files with 178 additions and 114 deletions

View file

@ -1,9 +1,12 @@
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;
import 'bottom_bar.dart';
import 'app_state.dart'; // Import the AppState class
import 'settings_page.dart';
import 'package:provider/provider.dart';
class LogsPage extends StatefulWidget {
const LogsPage({Key? key}) : super(key: key);
@ -30,7 +33,8 @@ class _LogsPageState extends State<LogsPage> {
return;
}
final url = 'https://api.shocklink.net/1/shockers/$shockerId/logs?offset=0&limit=30';
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',
@ -56,6 +60,8 @@ class _LogsPageState extends State<LogsPage> {
@override
Widget build(BuildContext context) {
final appState = Provider.of<AppState>(context, listen: false);
return Scaffold(
appBar: AppBar(
title: const Text('Logs'),
@ -79,13 +85,15 @@ class _LogsPageState extends State<LogsPage> {
DataColumn(label: Text('Duration (s)')),
],
rows: logs.map((log) {
final controlledBy = log['controlledBy'] as Map<String, dynamic>?;
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
final duration = (log['duration'] as int?)! /
1000; // Convert to seconds
// Add null checks for name and intensity
if (name != null && intensity != null) {
@ -109,38 +117,24 @@ class _LogsPageState extends State<LogsPage> {
),
),
),
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
bottomNavigationBar: BottomBar(
currentIndex: appState.currentIndex,
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()),
);
}
appState.currentIndex = index;
setState(() {
if (index == 0) {
appState.currentIndex = 0; // Reset to home index
// 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),
),
);
}

12
lib/app_state.dart Normal file
View file

@ -0,0 +1,12 @@
import 'package:flutter/material.dart';
class AppState extends ChangeNotifier {
int _currentIndex = 0;
int get currentIndex => _currentIndex;
set currentIndex(int index) {
_currentIndex = index;
notifyListeners();
}
}

38
lib/bottom_bar.dart Normal file
View file

@ -0,0 +1,38 @@
import 'package:flutter/material.dart';
class BottomBar extends StatelessWidget {
final int currentIndex;
final Function(int) onTap;
const BottomBar({
required this.currentIndex,
required this.onTap,
Key? key,
}) : super(key: key);
@override
Widget build(BuildContext context) {
return BottomNavigationBar(
currentIndex: currentIndex,
onTap: onTap,
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),
unselectedItemColor: Theme.of(context).textTheme.bodySmall?.color,
showUnselectedLabels: true,
selectedLabelStyle: const TextStyle(fontWeight: FontWeight.bold),
);
}
}

View file

@ -5,9 +5,19 @@ 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:shared_preferences/shared_preferences.dart';
import 'bottom_bar.dart';
import 'app_state.dart';
import 'package:provider/provider.dart';
void main() {
runApp(const MyApp());
runApp(
MultiProvider(
providers: [
ChangeNotifierProvider(create: (_) => AppState()), // Add this line
],
child: const MyApp(),
),
);
}
class MyApp extends StatelessWidget {
@ -34,6 +44,12 @@ class MyApp extends StatelessWidget {
final SharedPreferences prefs = await SharedPreferences.getInstance();
return prefs.getBool('isDarkMode') ?? false;
}
Future<void> getLimits() async {
final SharedPreferences prefs = await SharedPreferences.getInstance();
final String intensityLimit = prefs.getString('intensityLimit') ?? '';
final String durationLimit = prefs.getString('durationLimit') ?? '';
}
}
class SliderPage extends StatefulWidget {
@ -44,12 +60,13 @@ class SliderPage extends StatefulWidget {
}
class _SliderPageState extends State<SliderPage> {
int intensityValue = 0;
int timeValue = 0;
int currentIndex = 0;
int intensityValue = 1;
int timeValue = 1;
@override
Widget build(BuildContext context) {
final appState = Provider.of<AppState>(context, listen: false);
return Scaffold(
appBar: AppBar(
title: const Text('Openshock Companion'),
@ -64,7 +81,7 @@ class _SliderPageState extends State<SliderPage> {
),
Slider(
value: intensityValue.toDouble(),
min: 0,
min: 1,
max: 100,
onChanged: (value) {
setState(() {
@ -78,7 +95,7 @@ class _SliderPageState extends State<SliderPage> {
),
Slider(
value: timeValue.toDouble(),
min: 0,
min: 1,
max: 30,
onChanged: (value) {
setState(() {
@ -129,12 +146,13 @@ class _SliderPageState extends State<SliderPage> {
],
),
),
bottomNavigationBar: BottomNavigationBar(
currentIndex: currentIndex,
bottomNavigationBar: BottomBar(
currentIndex: appState.currentIndex,
onTap: (index) {
appState.currentIndex = index;
setState(() {
currentIndex = index;
if (index == 0) {
appState.currentIndex = 0; // Reset to home index
// Home tab
} else if (index == 1) {
// Settings tab
@ -150,24 +168,6 @@ class _SliderPageState extends State<SliderPage> {
}
});
},
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),
),
);
}

View file

@ -1,6 +1,11 @@
import 'package:OpenshockCompanion/LogsPage.dart';
// ignore_for_file: use_key_in_widget_constructors, library_private_types_in_public_api, use_build_context_synchronously
import 'package:flutter/material.dart';
import 'package:shared_preferences/shared_preferences.dart';
import 'bottom_bar.dart';
import 'app_state.dart'; // Import the AppState class
import 'LogsPage.dart';
import 'package:provider/provider.dart';
class SettingsPage extends StatefulWidget {
const SettingsPage({Key? key});
@ -12,10 +17,12 @@ class SettingsPage extends StatefulWidget {
class _SettingsPageState extends State<SettingsPage> {
final TextEditingController apiKeyController = TextEditingController();
final TextEditingController shockerIdController = TextEditingController();
final TextEditingController intensityLimitController =
TextEditingController();
final TextEditingController durationLimitController = TextEditingController();
bool showApiKey = false;
bool showShockerId = false;
bool isDarkMode = false;
@override
void initState() {
@ -28,20 +35,26 @@ class _SettingsPageState extends State<SettingsPage> {
setState(() {
apiKeyController.text = prefs.getString('apiKey') ?? '';
shockerIdController.text = prefs.getString('shockerId') ?? '';
isDarkMode = prefs.getBool('isDarkMode') ?? false;
intensityLimitController.text = prefs.getString('intensityLimit') ?? '';
durationLimitController.text = prefs.getString('durationLimit') ?? '';
});
}
Future<void> saveSettingsAndTheme() async {
Future<void> saveSettings() async {
final SharedPreferences prefs = await SharedPreferences.getInstance();
prefs.setString('apiKey', apiKeyController.text);
prefs.setString('shockerId', shockerIdController.text);
prefs.setBool('isDarkMode', isDarkMode);
intensityLimitController.text =
prefs.getString('intensityLimit') ?? '100'; // Default to 100
durationLimitController.text =
prefs.getString('durationLimit') ?? '30'; // Default to 30
Navigator.pop(context);
}
@override
Widget build(BuildContext context) {
final appState = Provider.of<AppState>(context, listen: false);
return Scaffold(
appBar: AppBar(
title: const Text('Settings'),
@ -57,7 +70,8 @@ class _SettingsPageState extends State<SettingsPage> {
decoration: InputDecoration(
labelText: 'API Key',
suffixIcon: IconButton(
icon: Icon(showApiKey ? Icons.visibility_off : Icons.visibility),
icon: Icon(
showApiKey ? Icons.visibility_off : Icons.visibility),
onPressed: () {
setState(() {
showApiKey = !showApiKey;
@ -74,7 +88,8 @@ class _SettingsPageState extends State<SettingsPage> {
decoration: InputDecoration(
labelText: 'Shocker ID',
suffixIcon: IconButton(
icon: Icon(showShockerId ? Icons.visibility_off : Icons.visibility),
icon: Icon(
showShockerId ? Icons.visibility_off : Icons.visibility),
onPressed: () {
setState(() {
showShockerId = !showShockerId;
@ -85,58 +100,45 @@ class _SettingsPageState extends State<SettingsPage> {
obscureText: !showShockerId,
),
const SizedBox(height: 16),
Row(
children: [
const Text('Dark Mode'),
Switch(
value: isDarkMode,
onChanged: (value) {
setState(() {
isDarkMode = value;
});
},
),
],
const Text('Intensity Limit'),
TextField(
controller: intensityLimitController,
decoration: const InputDecoration(
labelText: 'Intensity Limit',
),
),
const SizedBox(height: 16),
const Text('Duration Limit'),
TextField(
controller: durationLimitController,
decoration: const InputDecoration(
labelText: 'Duration Limit',
),
),
const SizedBox(height: 16),
ElevatedButton(
onPressed: saveSettingsAndTheme,
onPressed: saveSettings,
child: const Text('Save'),
),
],
),
),
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
bottomNavigationBar: BottomBar(
currentIndex: appState.currentIndex,
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()),
);
}
appState.currentIndex = index;
setState(() {
if (index == 0) {
appState.currentIndex = 0;
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),
),
);
}

View file

@ -208,6 +208,14 @@ packages:
url: "https://pub.dev"
source: hosted
version: "1.9.1"
nested:
dependency: transitive
description:
name: nested
sha256: "03bac4c528c64c95c722ec99280375a6f2fc708eec17c7b3f07253b626cd2a20"
url: "https://pub.dev"
source: hosted
version: "1.0.0"
path:
dependency: transitive
description:
@ -272,6 +280,14 @@ packages:
url: "https://pub.dev"
source: hosted
version: "3.7.3"
provider:
dependency: "direct main"
description:
name: provider
sha256: "9a96a0a19b594dbc5bf0f1f27d2bc67d5f95957359b461cd9feb44ed6ae75096"
url: "https://pub.dev"
source: hosted
version: "6.1.1"
shared_preferences:
dependency: "direct main"
description:

View file

@ -2,9 +2,9 @@ name: OpenshockCompanion
description: Companion app for managing openshock-compatible devices
# The following line prevents the package from being accidentally published to
# 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'
version: 0.2.0
version: 0.2.2
environment:
sdk: '>=3.1.2 <4.0.0'
@ -22,6 +22,8 @@ dependencies:
shared_preferences: ^2.0.8
flutter_launcher_icons: ^0.9.2
fluttertoast: ^8.2.3
provider: ^6.1.1
# The following adds the Cupertino Icons font to your application.
# Use with the CupertinoIcons class for iOS style icons.