Skip to content

Commit a1e36d3

Browse files
fix: save and apply scan mode and badge names via provider
1 parent 1538489 commit a1e36d3

File tree

3 files changed

+147
-79
lines changed

3 files changed

+147
-79
lines changed

lib/bademagic_module/bluetooth/scan_state.dart

Lines changed: 34 additions & 31 deletions
Original file line numberDiff line numberDiff line change
@@ -22,51 +22,54 @@ class ScanState extends NormalBleState {
2222
StreamSubscription<List<ScanResult>>? subscription;
2323
toast.showToast("Searching for device...");
2424

25-
Completer<BleState?> nextStateCompleter = Completer();
25+
final Completer<BleState?> nextStateCompleter = Completer();
2626
bool isCompleted = false;
2727

2828
ScanResult? foundDevice;
2929

3030
try {
3131
subscription = FlutterBluePlus.scanResults.listen(
3232
(results) async {
33-
if (!isCompleted && results.isNotEmpty) {
34-
try {
35-
final foundDevice = results.firstWhere(
36-
(result) {
37-
final matchesUuid = result.advertisementData.serviceUuids
38-
.contains(Guid("0000fee0-0000-1000-8000-00805f9b34fb"));
39-
40-
final deviceName = result.device.name.trim().toLowerCase();
41-
final matchesName = mode == BadgeScanMode.any ||
42-
allowedNames
43-
.map((e) => e.trim().toLowerCase())
44-
.contains(deviceName);
45-
46-
return matchesUuid && matchesName;
47-
},
48-
orElse: () => throw Exception("Matching device not found."),
49-
);
50-
51-
toast.showToast('Device found. Connecting...');
52-
isCompleted = true;
53-
FlutterBluePlus.stopScan();
54-
55-
nextStateCompleter.complete(ConnectState(
56-
scanResult: foundDevice,
57-
manager: manager,
58-
));
59-
} catch (e) {
60-
logger.w("No matching device found in this scan batch: $e");
61-
}
33+
if (isCompleted || results.isEmpty) return;
34+
35+
try {
36+
final normalizedAllowedNames = allowedNames
37+
.map((e) => e.trim().toLowerCase())
38+
.where((e) => e.isNotEmpty)
39+
.toList();
40+
41+
final foundDevice = results.firstWhere(
42+
(result) {
43+
final matchesUuid = result.advertisementData.serviceUuids
44+
.contains(Guid("0000fee0-0000-1000-8000-00805f9b34fb"));
45+
46+
final deviceName = result.device.name.trim().toLowerCase();
47+
final matchesName = mode == BadgeScanMode.any ||
48+
normalizedAllowedNames.contains(deviceName);
49+
50+
return matchesUuid && matchesName;
51+
},
52+
orElse: () => throw Exception("Matching device not found."),
53+
);
54+
55+
isCompleted = true;
56+
FlutterBluePlus.stopScan();
57+
toast.showToast('Device found. Connecting...');
58+
59+
nextStateCompleter.complete(ConnectState(
60+
scanResult: foundDevice,
61+
manager: manager,
62+
));
63+
} catch (e) {
64+
logger.w("No matching device found in this batch: $e");
6265
}
6366
},
6467
onError: (e) async {
6568
if (!isCompleted) {
6669
isCompleted = true;
6770
logger.e("Scan error: $e");
6871
toast.showErrorToast('Scan error occurred.');
69-
nextStateCompleter.completeError(e);
72+
nextStateCompleter.completeError(Exception("Scan error: $e"));
7073
}
7174
},
7275
);

lib/view/badgeScanSettingsWidget.dart

Lines changed: 57 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
import 'package:badgemagic/providers/BadgeScanProvider.dart';
22
import 'package:flutter/material.dart';
3+
import 'package:provider/provider.dart';
34

45
class BadgeScanSettingsWidget extends StatefulWidget {
56
final Function(BadgeScanMode mode, List<String> names) onSave;
@@ -12,8 +13,56 @@ class BadgeScanSettingsWidget extends StatefulWidget {
1213
}
1314

1415
class _BadgeScanSettingsWidgetState extends State<BadgeScanSettingsWidget> {
15-
BadgeScanMode _mode = BadgeScanMode.any;
16-
List<String> _badgeNames = ["LED Badge Magic", "LSLED", "VBLAB"];
16+
late BadgeScanMode _mode;
17+
final List<TextEditingController> _controllers = [];
18+
19+
@override
20+
void initState() {
21+
super.initState();
22+
final provider = Provider.of<BadgeScanProvider>(context, listen: false);
23+
_mode = provider.mode;
24+
for (var name in provider.badgeNames) {
25+
_controllers.add(TextEditingController(text: name));
26+
}
27+
}
28+
29+
void _addBadgeName() {
30+
setState(() {
31+
_controllers.add(TextEditingController());
32+
});
33+
}
34+
35+
void _removeBadgeName(int index) {
36+
setState(() {
37+
_controllers.removeAt(index).dispose();
38+
});
39+
}
40+
41+
void _onSave() {
42+
final updatedNames = _controllers
43+
.map((c) => c.text.trim())
44+
.where((name) => name.isNotEmpty)
45+
.toList();
46+
47+
final provider = Provider.of<BadgeScanProvider>(context, listen: false);
48+
provider.setMode(_mode);
49+
provider.setBadgeNames(updatedNames);
50+
51+
ScaffoldMessenger.of(context).showSnackBar(
52+
const SnackBar(content: Text('Scan settings saved successfully')),
53+
);
54+
55+
widget.onSave(_mode, updatedNames);
56+
Navigator.pop(context);
57+
}
58+
59+
@override
60+
void dispose() {
61+
for (var c in _controllers) {
62+
c.dispose();
63+
}
64+
super.dispose();
65+
}
1766

1867
@override
1968
Widget build(BuildContext context) {
@@ -23,11 +72,8 @@ class _BadgeScanSettingsWidgetState extends State<BadgeScanSettingsWidget> {
2372
actions: [
2473
IconButton(
2574
icon: const Icon(Icons.save),
26-
onPressed: () {
27-
widget.onSave(_mode, _badgeNames);
28-
Navigator.pop(context);
29-
},
30-
)
75+
onPressed: _onSave,
76+
),
3177
],
3278
),
3379
body: Column(
@@ -47,28 +93,24 @@ class _BadgeScanSettingsWidgetState extends State<BadgeScanSettingsWidget> {
4793
if (_mode == BadgeScanMode.specific)
4894
Expanded(
4995
child: ListView.builder(
50-
itemCount: _badgeNames.length,
96+
itemCount: _controllers.length,
5197
itemBuilder: (context, index) {
5298
return Row(
5399
children: [
54100
Expanded(
55101
child: Padding(
56102
padding: const EdgeInsets.symmetric(horizontal: 12.0),
57103
child: TextField(
58-
controller:
59-
TextEditingController(text: _badgeNames[index]),
104+
controller: _controllers[index],
60105
decoration: const InputDecoration(
61106
labelText: 'Badge Name',
62107
),
63-
onChanged: (val) => _badgeNames[index] = val,
64108
),
65109
),
66110
),
67111
IconButton(
68112
icon: const Icon(Icons.remove_circle_outline),
69-
onPressed: () {
70-
setState(() => _badgeNames.removeAt(index));
71-
},
113+
onPressed: () => _removeBadgeName(index),
72114
),
73115
],
74116
);
@@ -79,7 +121,7 @@ class _BadgeScanSettingsWidgetState extends State<BadgeScanSettingsWidget> {
79121
Padding(
80122
padding: const EdgeInsets.all(12.0),
81123
child: ElevatedButton.icon(
82-
onPressed: () => setState(() => _badgeNames.add("")),
124+
onPressed: _addBadgeName,
83125
icon: const Icon(Icons.add),
84126
label: const Text("Add more"),
85127
),

lib/view/settings_screen.dart

Lines changed: 56 additions & 33 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@ import 'package:badgemagic/providers/BadgeScanProvider.dart';
33
import 'package:badgemagic/view/widgets/common_scaffold_widget.dart';
44
import 'package:flutter/material.dart';
55
import 'package:flutter/services.dart';
6+
import 'package:provider/provider.dart';
67

78
class SettingsScreen extends StatefulWidget {
89
const SettingsScreen({super.key});
@@ -15,13 +16,19 @@ class SettingsScreenState extends State<SettingsScreen> {
1516
String selectedLanguage = 'ENGLISH';
1617
final List<String> languages = ['ENGLISH', 'CHINESE'];
1718

18-
BadgeScanMode _scanMode = BadgeScanMode.any;
19-
List<String> _badgeNames = ['LSLED', 'VBLAB'];
19+
late BadgeScanMode _scanMode;
20+
late List<TextEditingController> _controllers;
2021

2122
@override
2223
void initState() {
23-
_setOrientation();
2424
super.initState();
25+
_setOrientation();
26+
27+
final scanProvider = Provider.of<BadgeScanProvider>(context, listen: false);
28+
_scanMode = scanProvider.mode;
29+
_controllers = scanProvider.badgeNames
30+
.map((name) => TextEditingController(text: name))
31+
.toList();
2532
}
2633

2734
void _setOrientation() {
@@ -31,6 +38,14 @@ class SettingsScreenState extends State<SettingsScreen> {
3138
]);
3239
}
3340

41+
@override
42+
void dispose() {
43+
for (final controller in _controllers) {
44+
controller.dispose();
45+
}
46+
super.dispose();
47+
}
48+
3449
@override
3550
Widget build(BuildContext context) {
3651
return CommonScaffold(
@@ -65,47 +80,55 @@ class SettingsScreenState extends State<SettingsScreen> {
6580
onChanged: (value) => setState(() => _scanMode = value!),
6681
),
6782
if (_scanMode == BadgeScanMode.specific)
68-
..._badgeNames.asMap().entries.map(
69-
(entry) {
70-
final index = entry.key;
71-
final name = entry.value;
72-
return Row(
73-
children: [
74-
Expanded(
75-
child: Padding(
76-
padding: const EdgeInsets.symmetric(vertical: 4),
77-
child: TextField(
78-
controller: TextEditingController(text: name),
79-
onChanged: (val) => _badgeNames[index] = val,
80-
decoration: const InputDecoration(
81-
hintText: 'Badge name',
82-
border: OutlineInputBorder(),
83-
),
83+
..._controllers.asMap().entries.map((entry) {
84+
final index = entry.key;
85+
final controller = entry.value;
86+
return Row(
87+
children: [
88+
Expanded(
89+
child: Padding(
90+
padding: const EdgeInsets.symmetric(vertical: 4),
91+
child: TextField(
92+
controller: controller,
93+
decoration: const InputDecoration(
94+
hintText: 'Badge name',
95+
border: OutlineInputBorder(),
8496
),
8597
),
8698
),
87-
IconButton(
88-
icon: const Icon(Icons.remove_circle_outline),
89-
onPressed: () {
90-
setState(() => _badgeNames.removeAt(index));
91-
},
92-
),
93-
],
94-
);
95-
},
96-
).toList(),
99+
),
100+
IconButton(
101+
icon: const Icon(Icons.remove_circle_outline),
102+
onPressed: () {
103+
setState(() {
104+
controller.dispose();
105+
_controllers.removeAt(index);
106+
});
107+
},
108+
),
109+
],
110+
);
111+
}).toList(),
97112
if (_scanMode == BadgeScanMode.specific)
98113
TextButton.icon(
99-
onPressed: () => setState(() => _badgeNames.add('')),
114+
onPressed: () => setState(() {
115+
_controllers.add(TextEditingController());
116+
}),
100117
icon: const Icon(Icons.add),
101118
label: const Text('Add More'),
102119
),
103120
const SizedBox(height: 24),
104121
ElevatedButton.icon(
105122
onPressed: () {
106-
// Save logic here or pass to Provider
107-
print('Scan mode: $_scanMode');
108-
print('Badge names: $_badgeNames');
123+
final provider =
124+
Provider.of<BadgeScanProvider>(context, listen: false);
125+
provider.setMode(_scanMode);
126+
provider.setBadgeNames(
127+
_controllers.map((c) => c.text.trim()).toList(),
128+
);
129+
ScaffoldMessenger.of(context).showSnackBar(
130+
const SnackBar(content: Text('Scan settings saved')),
131+
);
109132
},
110133
icon: const Icon(Icons.save),
111134
label: const Text("Save Settings"),

0 commit comments

Comments
 (0)