Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat: Added UI for speed dial and made some UI changes #946

Merged
merged 2 commits into from
Jun 7, 2024
Merged
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
258 changes: 129 additions & 129 deletions lib/bademagic_module/bluetooth/bluetooth.dart
Original file line number Diff line number Diff line change
@@ -1,129 +1,129 @@
import 'dart:async';
import 'package:badgemagic/bademagic_module/models/data.dart';
import 'package:badgemagic/bademagic_module/utils/data_to_bytearray_converter.dart';
import 'package:flutter_blue_plus/flutter_blue_plus.dart';
import 'package:logger/logger.dart';

class BadgeMagicBluetooth {
static final Logger logger = Logger();

static Future<void> writeCharacteristic(
BluetoothDevice device,
Guid characteristicId,
Data data,
) async {
List<List<int>> dataChunks = convert(data);
logger.d("Data to write: $dataChunks");

try {
List<BluetoothService> services = await device.discoverServices();
for (BluetoothService service in services) {
for (BluetoothCharacteristic characteristic
in service.characteristics) {
if (characteristic.uuid == characteristicId &&
characteristic.properties.write) {
for (int attempt = 1; attempt <= 3; attempt++) {
for (List<int> chunk in dataChunks) {
bool success = false;
try {
await characteristic.write(chunk, withoutResponse: false);
await Future.delayed(const Duration(
milliseconds: 100)); // Add a delay between writes
success = true;
} catch (e) {
logger.e("Write failed, retrying ($attempt/3): $e");
}
if (!success) {
throw Exception(
"Failed to write chunk after 3 attempts: $chunk");
}
}
}
logger.d("Characteristic written successfully");
return; // Exit once the target characteristic is written
}
}
}
logger.d("Target characteristic not found");
} catch (e) {
logger.e("Failed to write characteristic: $e");
}
}

static Future<void> scanAndConnect(Data data) async {
ScanResult? foundDevice;

StreamSubscription<List<ScanResult>>? subscription;

try {
subscription = FlutterBluePlus.scanResults.listen(
(results) async {
if (results.isNotEmpty) {
foundDevice = results.firstWhere(
(result) =>
result.device.remoteId.toString() == "50:54:7B:63:10:F5",
);
if (foundDevice != null) {
await connectToDevice(foundDevice!, data);
} else {
logger.e("Target device not found.");
}
}
},
onError: (e) {
logger.e("Scan error: $e");
},
);

await FlutterBluePlus.startScan(
withServices: [Guid("0000fee0-0000-1000-8000-00805f9b34fb")],
timeout: const Duration(seconds: 10),
);

// Wait for the scan to complete before cancelling the subscription
await Future.delayed(const Duration(seconds: 11));
} finally {
await subscription?.cancel();
}
}

static Future<void> connectToDevice(ScanResult scanResult, Data data) async {
const int maxRetries = 3;
int attempt = 0;
bool connected = false;

while (attempt < maxRetries && !connected) {
try {
await scanResult.device.connect(autoConnect: false);
BluetoothConnectionState connectionState =
await scanResult.device.connectionState.first;

if (connectionState == BluetoothConnectionState.connected) {
logger.d("Device connected");
await writeCharacteristic(
scanResult.device,
Guid("0000fee1-0000-1000-8000-00805f9b34fb"),
data,
);
connected = true;
} else {
logger.e("Failed to connect to the device");
}
} catch (e) {
logger.e("Connection error: $e");
attempt++;
if (attempt < maxRetries) {
logger.d("Retrying connection ($attempt/$maxRetries)...");
await Future.delayed(
const Duration(seconds: 2)); // Wait before retrying
} else {
logger.e("Max retries reached. Connection failed.");
}
} finally {
if (!connected) {
await scanResult.device.disconnect();
}
}
}
}
}
import 'dart:async';
import 'package:badgemagic/bademagic_module/models/data.dart';
import 'package:badgemagic/bademagic_module/utils/data_to_bytearray_converter.dart';
import 'package:flutter_blue_plus/flutter_blue_plus.dart';
import 'package:logger/logger.dart';
class BadgeMagicBluetooth {
static final Logger logger = Logger();
static Future<void> writeCharacteristic(
BluetoothDevice device,
Guid characteristicId,
Data data,
) async {
List<List<int>> dataChunks = convert(data);
logger.d("Data to write: $dataChunks");
try {
List<BluetoothService> services = await device.discoverServices();
for (BluetoothService service in services) {
for (BluetoothCharacteristic characteristic
in service.characteristics) {
if (characteristic.uuid == characteristicId &&
characteristic.properties.write) {
for (int attempt = 1; attempt <= 3; attempt++) {
for (List<int> chunk in dataChunks) {
bool success = false;
try {
await characteristic.write(chunk, withoutResponse: false);
await Future.delayed(const Duration(
milliseconds: 100)); // Add a delay between writes
success = true;
} catch (e) {
logger.e("Write failed, retrying ($attempt/3): $e");
}
if (!success) {
throw Exception(
"Failed to write chunk after 3 attempts: $chunk");
}
}
}
logger.d("Characteristic written successfully");
return; // Exit once the target characteristic is written
}
}
}
logger.d("Target characteristic not found");
} catch (e) {
logger.e("Failed to write characteristic: $e");
}
}
static Future<void> scanAndConnect(Data data) async {
ScanResult? foundDevice;
StreamSubscription<List<ScanResult>>? subscription;
try {
subscription = FlutterBluePlus.scanResults.listen(
(results) async {
if (results.isNotEmpty) {
foundDevice = results.firstWhere(
(result) =>
result.device.remoteId.toString() == "50:54:7B:63:10:F5",
);
if (foundDevice != null) {
await connectToDevice(foundDevice!, data);
} else {
logger.e("Target device not found.");
}
}
},
onError: (e) {
logger.e("Scan error: $e");
},
);
await FlutterBluePlus.startScan(
withServices: [Guid("0000fee0-0000-1000-8000-00805f9b34fb")],
timeout: const Duration(seconds: 10),
);
// Wait for the scan to complete before cancelling the subscription
await Future.delayed(const Duration(seconds: 11));
} finally {
await subscription?.cancel();
}
}
static Future<void> connectToDevice(ScanResult scanResult, Data data) async {
const int maxRetries = 3;
int attempt = 0;
bool connected = false;
while (attempt < maxRetries && !connected) {
try {
await scanResult.device.connect(autoConnect: false);
BluetoothConnectionState connectionState =
await scanResult.device.connectionState.first;
if (connectionState == BluetoothConnectionState.connected) {
logger.d("Device connected");
await writeCharacteristic(
scanResult.device,
Guid("0000fee1-0000-1000-8000-00805f9b34fb"),
data,
);
connected = true;
} else {
logger.e("Failed to connect to the device");
}
} catch (e) {
logger.e("Connection error: $e");
attempt++;
if (attempt < maxRetries) {
logger.d("Retrying connection ($attempt/$maxRetries)...");
await Future.delayed(
const Duration(seconds: 2)); // Wait before retrying
} else {
logger.e("Max retries reached. Connection failed.");
}
} finally {
if (!connected) {
await scanResult.device.disconnect();
}
}
}
}
}
52 changes: 26 additions & 26 deletions lib/bademagic_module/utils/byte_array_utils.dart
Original file line number Diff line number Diff line change
@@ -1,26 +1,26 @@
import 'package:logger/logger.dart';

final Logger logger = Logger();

String toHex(List<int> bytes) {
StringBuffer buffer = StringBuffer();
for (int byte in bytes) {
buffer.write('${byte < 16 ? '0' : ''}${byte.toRadixString(16)}');
}
return buffer.toString().toUpperCase();
}

List<int> hexStringToByteArray(String hexString) {
if (hexString.length % 2 != 0) {
throw ArgumentError("Invalid hex string: $hexString");
}

List<int> data = [];
for (int i = 0; i < hexString.length; i += 2) {
int firstDigit = int.parse(hexString[i], radix: 16);
int secondDigit = int.parse(hexString[i + 1], radix: 16);
data.add((firstDigit << 4) + secondDigit);
}
logger.d(data.length);
return data;
}
import 'package:logger/logger.dart';
final Logger logger = Logger();
String toHex(List<int> bytes) {
StringBuffer buffer = StringBuffer();
for (int byte in bytes) {
buffer.write('${byte < 16 ? '0' : ''}${byte.toRadixString(16)}');
}
return buffer.toString().toUpperCase();
}
List<int> hexStringToByteArray(String hexString) {
if (hexString.length % 2 != 0) {
throw ArgumentError("Invalid hex string: $hexString");
}
List<int> data = [];
for (int i = 0; i < hexString.length; i += 2) {
int firstDigit = int.parse(hexString[i], radix: 16);
int secondDigit = int.parse(hexString[i + 1], radix: 16);
data.add((firstDigit << 4) + secondDigit);
}
logger.d(data.length);
return data;
}
462 changes: 231 additions & 231 deletions lib/bademagic_module/utils/data_to_bytearray_converter.dart

Large diffs are not rendered by default.

92 changes: 52 additions & 40 deletions lib/providers/badge_message_provider.dart
Original file line number Diff line number Diff line change
@@ -1,40 +1,52 @@
import 'package:badgemagic/bademagic_module/bluetooth/bluetooth.dart';
import 'package:badgemagic/bademagic_module/models/data.dart';
import 'package:badgemagic/bademagic_module/models/messages.dart';
import 'package:badgemagic/bademagic_module/models/mode.dart';
import 'package:badgemagic/bademagic_module/models/speed.dart';
import 'package:flutter/material.dart';
import 'package:logger/logger.dart';

class BadgeMessageProvider extends ChangeNotifier {
static final Logger logger = Logger();

Map<int, Mode> modeValueMap = {
0: Mode.left,
1: Mode.right,
2: Mode.up,
3: Mode.down,
4: Mode.fixed,
5: Mode.snowflake,
6: Mode.picture,
7: Mode.animation,
8: Mode.laser
};

void generateMessage(
String text, bool flash, bool marq, Speed speed, Mode mode) {
Data data = Data(messages: [
Message(text: text, flash: flash, marquee: marq, speed: speed, mode: mode)
]);
dataFormed(data);
transferData(data);
}

void transferData(Data data) {
BadgeMagicBluetooth.scanAndConnect(data);
logger.d(".......Data is being transferred.......");
}

void dataFormed(Data data) => logger.d(
"${data.messages.length} message : ${data.messages[0].text} Flash : ${data.messages[0].flash} Marquee : ${data.messages[0].marquee} Mode : ${data.messages[0].mode}");
}
import 'package:badgemagic/bademagic_module/bluetooth/bluetooth.dart';
import 'package:badgemagic/bademagic_module/models/data.dart';
import 'package:badgemagic/bademagic_module/models/messages.dart';
import 'package:badgemagic/bademagic_module/models/mode.dart';
import 'package:badgemagic/bademagic_module/models/speed.dart';
import 'package:flutter/material.dart';
import 'package:logger/logger.dart';

class BadgeMessageProvider extends ChangeNotifier {
BadgeMagicBluetooth badgeMagicBluetooth = BadgeMagicBluetooth();
static final Logger logger = Logger();

Map<int, Mode> modeValueMap = {
0: Mode.left,
1: Mode.right,
2: Mode.up,
3: Mode.down,
4: Mode.fixed,
5: Mode.snowflake,
6: Mode.picture,
7: Mode.animation,
8: Mode.laser
};

Map<int, Speed> speedMap = {
1: Speed.one,
2: Speed.two,
3: Speed.three,
4: Speed.four,
5: Speed.five,
6: Speed.six,
7: Speed.seven,
8: Speed.eight,
};

void generateMessage(
String text, bool flash, bool marq, Speed speed, Mode mode) {
Data data = Data(messages: [
Message(text: text, flash: flash, marquee: marq, speed: speed, mode: mode)
]);
dataFormed(data);
transferData(data);
}

void transferData(Data data) {
BadgeMagicBluetooth.scanAndConnect(data);
logger.d(".......Data is being transferred.......");
}

void dataFormed(Data data) => logger.d(
"${data.messages.length} message : ${data.messages[0].text} Flash : ${data.messages[0].flash} Marquee : ${data.messages[0].marquee} Mode : ${data.messages[0].mode}");
}
55 changes: 32 additions & 23 deletions lib/providers/cardsprovider.dart
Original file line number Diff line number Diff line change
@@ -1,23 +1,32 @@
import 'package:flutter/material.dart';

class CardProvider extends ChangeNotifier {
TextEditingController message = TextEditingController();
int animationIndex = 0;
List<int> effectsIndex = [0, 0, 0];

int getAnimationIndex() => animationIndex;

TextEditingController getController() => message;

int getEffectIndex(int index) => effectsIndex[index];

void setAnimationIndex(int index) {
animationIndex = index;
notifyListeners();
}

void setEffectIndex(int index) {
effectsIndex[index] = effectsIndex[index] == 1 ? 0 : 1;
notifyListeners();
}
}
import 'package:flutter/material.dart';

class CardProvider extends ChangeNotifier {
int outerValue = 1;

int getOuterValue() => outerValue;

void setOuterValue(int value) {
outerValue = value;
notifyListeners();
}

TextEditingController message = TextEditingController();
int animationIndex = 0;
List<int> effectsIndex = [0, 0, 0];

int getAnimationIndex() => animationIndex;

TextEditingController getController() => message;

int getEffectIndex(int index) => effectsIndex[index];

void setAnimationIndex(int index) {
animationIndex = index;
notifyListeners();
}

void setEffectIndex(int index) {
effectsIndex[index] = effectsIndex[index] == 1 ? 0 : 1;
notifyListeners();
}
}
272 changes: 174 additions & 98 deletions lib/view/homescreen.dart
Original file line number Diff line number Diff line change
@@ -1,98 +1,174 @@
import 'package:badgemagic/bademagic_module/models/speed.dart';
import 'package:badgemagic/providers/badge_message_provider.dart';
import 'package:badgemagic/providers/cardsprovider.dart';
import 'package:badgemagic/view/widgets/homescreentabs.dart';
import 'package:flutter/material.dart';
import 'package:logger/logger.dart';
import 'package:provider/provider.dart';

class HomeScreen extends StatefulWidget {
const HomeScreen({super.key});

@override
State<HomeScreen> createState() => _HomeScreenState();
}

class _HomeScreenState extends State<HomeScreen> with TickerProviderStateMixin {
static final Logger logger = Logger();

late final TabController _tabController;
@override
void initState() {
super.initState();
_tabController = TabController(length: 3, vsync: this);
}

@override
Widget build(BuildContext context) {
BadgeMessageProvider badgeData = Provider.of<BadgeMessageProvider>(context);
CardProvider cardData = Provider.of<CardProvider>(context);
return DefaultTabController(
length: 3,
child: Scaffold(
appBar: AppBar(
backgroundColor: Colors.red,
title: const Text(
'Badge Magic',
style: TextStyle(color: Colors.white, fontWeight: FontWeight.bold),
),
centerTitle: true,
),
body: SafeArea(
child: Column(
children: [
const Badge(),
Container(
margin: const EdgeInsets.all(15),
child: Material(
borderRadius: BorderRadius.circular(10),
elevation: 10,
child: TextField(
controller: cardData.getController(),
decoration: InputDecoration(
border: OutlineInputBorder(
borderRadius: BorderRadius.circular(10)),
prefixIcon: const Icon(Icons.tag_faces_outlined),
focusedBorder: const OutlineInputBorder(
borderSide: BorderSide(color: Colors.red))),
),
),
),
TabBar(
indicatorSize: TabBarIndicatorSize.label,
controller: _tabController,
tabs: const [
Tab(text: 'Speed'),
Tab(text: 'Animation'),
Tab(text: 'Effects'),
],
),
Expanded(
child: TabBarView(
controller: _tabController,
children: [
Container(),
const AnimationTab(),
const EffectTab(),
],
),
),
TextButton(
onPressed: () async {
logger.d(
"${cardData.getAnimationIndex()} : ${cardData.getController().text} : ${cardData.getEffectIndex(2)}");
badgeData.generateMessage(
cardData.getController().text,
cardData.getEffectIndex(1) == 1,
cardData.getEffectIndex(2) == 1,
Speed.eight,
badgeData.modeValueMap[cardData.getAnimationIndex()]!);
},
child: const Text('Transffer'))
],
),
),
),
);
}
}
import 'package:badgemagic/providers/badge_message_provider.dart';
import 'package:badgemagic/providers/cardsprovider.dart';
import 'package:badgemagic/view/widgets/homescreentabs.dart';
import 'package:badgemagic/view/widgets/speedial.dart';
import 'package:badgemagic/virtualbadge/view/badgeui.dart';
import 'package:flutter/material.dart';
import 'package:fluttertoast/fluttertoast.dart';
import 'package:provider/provider.dart';

class HomeScreen extends StatefulWidget {
const HomeScreen({super.key});

@override
State<HomeScreen> createState() => _HomeScreenState();
}

class _HomeScreenState extends State<HomeScreen> with TickerProviderStateMixin {
late final TabController _tabController;
BadgeMessageProvider badgeData = BadgeMessageProvider();

@override
void initState() {
super.initState();
_tabController = TabController(length: 3, vsync: this);
}

Future<void> transferData(BuildContext context, CardProvider cardData) async {
Fluttertoast.showToast(
msg: "Transferring...",
toastLength: Toast.LENGTH_LONG,
gravity: ToastGravity.BOTTOM,
backgroundColor: Colors.black,
textColor: Colors.white,
fontSize: 16.0,
);

try {
badgeData.generateMessage(
cardData.getController().text,
cardData.getEffectIndex(1) == 1,
cardData.getEffectIndex(2) == 1,
badgeData.speedMap[cardData.getOuterValue()]!,
badgeData.modeValueMap[cardData.getAnimationIndex()]!,
);

Fluttertoast.showToast(
msg: "Transfer successful",
toastLength: Toast.LENGTH_SHORT,
gravity: ToastGravity.BOTTOM,
backgroundColor: Colors.green,
textColor: Colors.white,
fontSize: 16.0,
);
} catch (e) {
Fluttertoast.showToast(
msg: "Transfer failed: $e",
toastLength: Toast.LENGTH_LONG,
gravity: ToastGravity.CENTER,
backgroundColor: Colors.red,
textColor: Colors.white,
fontSize: 16.0,
);
}
}

@override
Widget build(BuildContext context) {
double height = MediaQuery.of(context).size.height;
double width = MediaQuery.of(context).size.width;
CardProvider cardData = Provider.of<CardProvider>(context);
return DefaultTabController(
length: 3,
child: Scaffold(
appBar: AppBar(
backgroundColor: Colors.red,
title: const Text(
'Badge Magic',
style: TextStyle(color: Colors.white, fontWeight: FontWeight.bold),
),
centerTitle: true,
),
body: SafeArea(
child: SizedBox(
height: height,
width: width,
child: Column(
children: [
const BMBadge(),
Container(
margin: const EdgeInsets.all(15),
child: Material(
borderRadius: BorderRadius.circular(10),
elevation: 10,
child: TextField(
controller: cardData.getController(),
decoration: InputDecoration(
border: OutlineInputBorder(
borderRadius: BorderRadius.circular(10)),
prefixIcon: const Icon(Icons.tag_faces_outlined),
focusedBorder: const OutlineInputBorder(
borderSide: BorderSide(color: Colors.red))),
),
),
),
TabBar(
indicatorSize: TabBarIndicatorSize.label,
controller: _tabController,
tabs: const [
Tab(text: 'Speed'),
Tab(text: 'Animation'),
Tab(text: 'Effects'),
],
),
SingleChildScrollView(
child: Column(
children: [
AspectRatio(
aspectRatio: 1.5,
child: TabBarView(
physics: const NeverScrollableScrollPhysics(),
controller: _tabController,
children: const [
RadialDial(),
AnimationTab(),
EffectTab(),
],
),
),
Container(
padding: EdgeInsets.only(
bottom: height * 0.2,
top: height * 0.02), // Adjust the value as needed
child: Row(
mainAxisAlignment: MainAxisAlignment.center,
children: [
GestureDetector(
onTap: () {
if (cardData.getController().text.isEmpty) {
Fluttertoast.showToast(
msg: "Please enter some text to transfer.",
toastLength: Toast.LENGTH_SHORT,
gravity: ToastGravity.BOTTOM,
backgroundColor: Colors.red,
textColor: Colors.white,
fontSize: 16.0,
);
return;
}
transferData(context, cardData);
},
child: Container(
padding: const EdgeInsets.symmetric(
horizontal: 20, vertical: 8),
decoration: BoxDecoration(
borderRadius: BorderRadius.circular(10),
color: Colors.grey.shade400,
),
child: const Text('Transfer'),
),
),
],
),
),
],
),
),
],
),
),
),
),
);
}
}
252 changes: 252 additions & 0 deletions lib/view/widgets/speedial.dart
Original file line number Diff line number Diff line change
@@ -0,0 +1,252 @@
import 'package:badgemagic/providers/cardsprovider.dart';
import 'package:flutter/material.dart';
import 'dart:math';

import 'package:provider/provider.dart';

class InnerDialPainter extends CustomPainter {
@override
void paint(Canvas canvas, Size size) {
final center = Offset(size.width / 2, size.height / 2);
final radius = min(size.width / 2, size.height / 2) * 0.9;

final paint = Paint()
..color = Colors.grey.shade300
..style = PaintingStyle.stroke
..strokeWidth = 12;

canvas.drawCircle(center, radius, paint);
}

@override
bool shouldRepaint(covariant CustomPainter oldDelegate) {
return false;
}
}

class RadialDialPainter extends CustomPainter {
final double value;
final double max;
final Color color;

RadialDialPainter({
required this.value,
required this.max,
required this.color,
});

@override
void paint(Canvas canvas, Size size) {
final center = Offset(size.width / 2, size.height / 2);
final radius = min(size.width / 2, size.height / 2) * 0.8;

final paint = Paint()
..color = Colors.grey.shade300
..style = PaintingStyle.stroke
..strokeCap = StrokeCap.round
..strokeWidth = 15;

const startAngle = 3 * pi / 4;

canvas.drawArc(
Rect.fromCircle(center: center, radius: radius),
startAngle,
6 * pi / 4,
false,
paint,
);

final progressPaint = Paint()
..color = Colors.red
..style = PaintingStyle.stroke
..strokeCap = StrokeCap.round
..strokeWidth = 15;

canvas.drawArc(
Rect.fromCircle(center: center, radius: radius),
startAngle,
6 * pi / 4 * (value / max),
false,
progressPaint,
);
}

@override
bool shouldRepaint(covariant CustomPainter oldDelegate) {
return true;
}
}

class InnerPointerPainter extends CustomPainter {
final double value;
final double max;
final Color color;

InnerPointerPainter({
required this.value,
required this.max,
required this.color,
});

@override
void paint(Canvas canvas, Size size) {
final center = Offset(size.width / 2, size.height / 2);
final radius = min(size.width / 2, size.height / 2) * 0.8;

final pointerAngle = 3 * pi / 4 + 6 * pi / 4 * (value / max);
final pointerLength = radius + 25;

final pointerPaint = Paint()
..color = color
..strokeCap = StrokeCap.round
..strokeWidth = 4;

final pointerStart = Offset(
center.dx + radius * cos(pointerAngle),
center.dy + radius * sin(pointerAngle),
);
final pointerEnd = Offset(
center.dx + pointerLength * cos(pointerAngle),
center.dy + pointerLength * sin(pointerAngle),
);

canvas.drawLine(pointerStart, pointerEnd, pointerPaint);
}

@override
bool shouldRepaint(covariant CustomPainter oldDelegate) {
return true;
}
}

class RadialDial extends StatefulWidget {
const RadialDial({super.key});

@override
// ignore: library_private_types_in_public_api
_RadialDialState createState() => _RadialDialState();
}

class _RadialDialState extends State<RadialDial> {
double outerValue = 0.0;
final double maxValue = 8.0;

final double initialAngle = 155 * pi / 180;
double previousAngle = 0.0;
bool isDragging = true;

@override
void initState() {
super.initState();

previousAngle = initialAngle;
}

@override
Widget build(BuildContext context) {
CardProvider outerValueProvider = Provider.of<CardProvider>(context);

void updateOuterValue(double angle) {
const startAngle = 155 * pi / 270;
const endAngle = 360 * pi / 180;

const totalAngle = endAngle - startAngle;

final numSections = maxValue;

final anglePerSection = totalAngle / numSections;

final section = ((angle - startAngle) / anglePerSection).round();

final clampedSection = section.clamp(1, numSections);

setState(() {
outerValueProvider.setOuterValue(clampedSection.toInt());
});
}

void updateAngle(Offset position, Size size) {
if (!isDragging) return;

final center = Offset(size.width / 2, size.height / 2);
final dx = position.dx - center.dx;
final dy = position.dy - center.dy;
final distanceFromCenter = sqrt(dx * dx + dy * dy);

if (distanceFromCenter > size.width / 2) return;

var angle = atan2(dy, dx);

if (angle < 0) {
angle += 2 * pi;
}

const startAngle = 155 * pi / 270;
const endAngle = 360 * pi / 180;

if (angle >= startAngle && angle <= endAngle) {
if ((angle >= previousAngle && angle <= endAngle) ||
(angle < startAngle && previousAngle < startAngle) ||
(angle - previousAngle).abs() < pi) {
setState(() {
updateOuterValue(angle);
});
}
previousAngle = angle;
}
}

return Stack(
alignment: Alignment.center,
children: [
CustomPaint(
painter: RadialDialPainter(
value: outerValueProvider.getOuterValue().toDouble(),
max: maxValue,
color: Colors.red),
child: const SizedBox(
width: 250,
height: 250,
),
),
CustomPaint(
painter: InnerDialPainter(),
child: Container(
color: Colors.transparent,
width: 170,
height: 170,
),
),
GestureDetector(
onPanUpdate: (details) {
if (isDragging) {
RenderBox renderBox = context.findRenderObject() as RenderBox;
Offset localPosition =
renderBox.globalToLocal(details.globalPosition);
updateAngle(localPosition, renderBox.size);
}
},
child: CustomPaint(
painter: InnerPointerPainter(
value: outerValueProvider.getOuterValue().toDouble(),
max: maxValue,
color: Colors.red),
child: const SizedBox(
width: 80,
height: 80,
),
),
),
Positioned(
child: Text(
(outerValueProvider.getOuterValue()).toString(),
style: const TextStyle(
fontSize: 60,
fontWeight: FontWeight.w600,
color: Color.fromRGBO(113, 113, 113, 1)),
),
),
],
);
}
}
47 changes: 34 additions & 13 deletions pubspec.lock
Original file line number Diff line number Diff line change
@@ -139,6 +139,19 @@ packages:
description: flutter
source: sdk
version: "0.0.0"
flutter_web_plugins:
dependency: transitive
description: flutter
source: sdk
version: "0.0.0"
fluttertoast:
dependency: "direct main"
description:
name: fluttertoast
sha256: "7eae679e596a44fdf761853a706f74979f8dd3cd92cf4e23cae161fda091b847"
url: "https://pub.dev"
source: hosted
version: "8.2.6"
glob:
dependency: transitive
description:
@@ -151,26 +164,26 @@ packages:
dependency: transitive
description:
name: leak_tracker
sha256: "7f0df31977cb2c0b88585095d168e689669a2cc9b97c309665e3386f3e9d341a"
sha256: "78eb209deea09858f5269f5a5b02be4049535f568c07b275096836f01ea323fa"
url: "https://pub.dev"
source: hosted
version: "10.0.4"
version: "10.0.0"
leak_tracker_flutter_testing:
dependency: transitive
description:
name: leak_tracker_flutter_testing
sha256: "06e98f569d004c1315b991ded39924b21af84cf14cc94791b8aea337d25b57f8"
sha256: b46c5e37c19120a8a01918cfaf293547f47269f7cb4b0058f21531c2465d6ef0
url: "https://pub.dev"
source: hosted
version: "3.0.3"
version: "2.0.1"
leak_tracker_testing:
dependency: transitive
description:
name: leak_tracker_testing
sha256: "6ba465d5d76e67ddf503e1161d1f4a6bc42306f9d66ca1e8f079a47290fb06d3"
sha256: a597f72a664dbd293f3bfc51f9ba69816f84dcd403cdac7066cb3f6003f3ab47
url: "https://pub.dev"
source: hosted
version: "3.0.1"
version: "2.0.1"
lints:
dependency: transitive
description:
@@ -207,10 +220,10 @@ packages:
dependency: transitive
description:
name: meta
sha256: "7687075e408b093f36e6bbf6c91878cc0d4cd10f409506f7bc996f68220b9136"
sha256: d584fa6707a52763a52446f02cc621b077888fb63b93bbcb1143a7be5a0c0c04
url: "https://pub.dev"
source: hosted
version: "1.12.0"
version: "1.11.0"
nested:
dependency: transitive
description:
@@ -300,10 +313,10 @@ packages:
dependency: transitive
description:
name: test_api
sha256: "9955ae474176f7ac8ee4e989dadfb411a58c30415bcfb648fa04b2b8a03afa7f"
sha256: "5c2f730018264d276c20e4f1503fd1308dfbbae39ec8ee63c5236311ac06954b"
url: "https://pub.dev"
source: hosted
version: "0.7.0"
version: "0.6.1"
typed_data:
dependency: transitive
description:
@@ -324,10 +337,10 @@ packages:
dependency: transitive
description:
name: vm_service
sha256: "3923c89304b715fb1eb6423f017651664a03bf5f4b29983627c4da791f74a4ec"
sha256: b3d56ff4341b8f182b96aceb2fa20e3dcb336b9f867bc0eafc0de10f1048e957
url: "https://pub.dev"
source: hosted
version: "14.2.1"
version: "13.0.0"
watcher:
dependency: transitive
description:
@@ -336,6 +349,14 @@ packages:
url: "https://pub.dev"
source: hosted
version: "1.1.0"
web:
dependency: transitive
description:
name: web
sha256: "97da13628db363c635202ad97068d47c5b8aa555808e7a9411963c533b449b27"
url: "https://pub.dev"
source: hosted
version: "0.5.1"
yaml:
dependency: transitive
description:
@@ -346,4 +367,4 @@ packages:
version: "3.1.2"
sdks:
dart: ">=3.3.4 <4.0.0"
flutter: ">=3.18.0-18.0.pre.54"
flutter: ">=2.5.0"
2 changes: 2 additions & 0 deletions pubspec.yaml
Original file line number Diff line number Diff line change
@@ -37,6 +37,7 @@ dependencies:
cupertino_icons: ^1.0.6
provider: ^6.1.2
flutter_blue_plus: ^1.32.7
fluttertoast: ^8.2.6
logger: ^2.3.0

dev_dependencies:
@@ -94,3 +95,4 @@ flutter:
#
# For details regarding fonts from package dependencies,
# see https://flutter.dev/custom-fonts/#from-packages