feat: working connection, conn setting, and gear ratio setting for universal shifters

This commit is contained in:
2026-02-22 23:05:12 +01:00
parent f92d6d04f5
commit dcb1e6596e
93 changed files with 10538 additions and 668 deletions

View File

@ -0,0 +1,207 @@
import 'dart:math';
import 'package:flutter/material.dart';
class HorizontalScanningAnimation extends StatefulWidget {
final bool isScanning; // Add this to control the animation
final Color waveColor;
final double height;
const HorizontalScanningAnimation({
super.key,
required this.isScanning, // Make it required
this.waveColor = Colors.lightBlueAccent,
this.height = 50.0,
});
@override
_HorizontalScanningAnimationState createState() =>
_HorizontalScanningAnimationState();
}
class _HorizontalScanningAnimationState
extends State<HorizontalScanningAnimation>
with SingleTickerProviderStateMixin {
late AnimationController _controller;
@override
void initState() {
super.initState();
_controller = AnimationController(
vsync: this,
duration: const Duration(seconds: 2),
);
// Start repeating only if initially scanning
if (widget.isScanning) {
_controller.repeat();
}
}
@override
void dispose() {
_controller.dispose();
super.dispose();
}
@override
void didUpdateWidget(covariant HorizontalScanningAnimation oldWidget) {
super.didUpdateWidget(oldWidget);
if (widget.isScanning != oldWidget.isScanning) {
if (widget.isScanning) {
// Start or resume repeating
if (!_controller.isAnimating) {
// If stopped previously, reset before repeating for a clean start
// Though, repeat() should handle restarting if stopped. Testing needed.
// _controller.reset(); // Optional: uncomment if repeat doesn't restart smoothly
_controller.repeat();
}
} else {
// Stop repeating, but let the current animation cycle finish visually
if (_controller.isAnimating) {
_controller.stop(
canceled:
false); // Use canceled: false to let it finish the current tick
}
}
}
}
@override
Widget build(BuildContext context) {
// Only build the painter if the controller is active or was recently stopped
// This prevents drawing when completely idle. Check if value is changing or non-zero.
// Or simply rely on the AnimatedBuilder which won't rebuild if controller is idle at 0.0
return SizedBox(
height: widget.height,
width: double.infinity,
child: AnimatedBuilder(
animation: _controller,
builder: (context, child) {
return CustomPaint(
painter: _HorizontalWavePainter(
progress: _controller.value,
waveColor: widget.waveColor,
),
);
},
),
);
}
}
class _HorizontalWavePainter extends CustomPainter {
final double progress; // Animation value from 0.0 to 1.0
final Color waveColor;
final int waveCount = 2; // Number of waves visible at once
final double waveAmplitude = 10.0; // Max height deviation of the wave
_HorizontalWavePainter({required this.progress, required this.waveColor});
@override
void paint(Canvas canvas, Size size) {
final paint = Paint()
..color = waveColor.withValues(alpha: 0.6) // Semi-transparent waves
..style = PaintingStyle.fill;
final centerY = size.height / 2;
final width = size.width;
// Draw multiple waves propagating outwards
for (int i = 0; i < waveCount; i++) {
// Calculate the phase offset for each wave based on progress and index
// This creates the effect of waves moving outwards
double waveProgress = (progress + i / waveCount) % 1.0;
// Use an easing curve for smoother expansion
double easedProgress = Curves.easeInOutSine.transform(waveProgress);
// Calculate the current horizontal position (expanding from center)
// The wave starts narrow and expands outwards
double currentWidth =
width * easedProgress * 0.8; // Max 80% width expansion
double startX = (width / 2) - (currentWidth / 2);
double endX = (width / 2) + (currentWidth / 2);
// Calculate opacity based on progress (fade in and out)
double opacity;
if (waveProgress < 0.1) {
opacity = waveProgress / 0.1; // Fade in
} else if (waveProgress > 0.8) {
opacity = (1.0 - waveProgress) / 0.2; // Fade out
} else {
opacity = 1.0;
}
opacity = max(0.0, opacity); // Clamp opacity
if (opacity <= 0.0 || currentWidth < 5)
continue; // Skip drawing if invisible or too small
// Create the wave path
final path = Path();
path.moveTo(startX, centerY);
// Calculate points for the sine wave shape within the current width
const int segments = 50; // Number of segments for the curve
for (int j = 0; j <= segments; j++) {
double segmentProgress = j / segments;
double x = startX + currentWidth * segmentProgress;
// Apply sine wave based on segment progress and overall animation progress
// Multiply by (1 - easedProgress) to reduce amplitude as it expands
double yOffset = waveAmplitude *
sin(segmentProgress * 2 * pi + progress * 4 * pi) *
(1 - easedProgress * 0.8) * // Reduce amplitude as it expands
opacity; // Apply opacity effect to amplitude too
path.lineTo(x, centerY + yOffset);
}
// Draw a filled shape (like a lens flare or horizontal bar)
// Adjust thickness based on easedProgress (thicker in the middle, thinner at ends)
double thickness =
waveAmplitude * (1 - easedProgress * 0.9) * opacity * 0.5;
paint.color = waveColor.withValues(
alpha: opacity * 0.5); // Update paint color with opacity
// Simplified: Draw a rectangle that pulses
// More complex shapes could be drawn here using path.arcTo or path.quadraticBezierTo
// For simplicity, let's use a slightly blurred rectangle effect
final rectPath = Path()
..addRRect(RRect.fromRectAndRadius(
Rect.fromCenter(
center: Offset(width / 2, centerY),
width: currentWidth,
height: thickness * 2),
Radius.circular(thickness)));
// Apply a blur effect
final blurPaint = Paint()
..color = waveColor.withValues(alpha: opacity * 0.4)
..maskFilter = MaskFilter.blur(
BlurStyle.normal, thickness * 1.5); // Blur based on thickness
// Draw the blurred shape
canvas.drawPath(rectPath, blurPaint);
// Draw a slightly smaller, less opaque shape on top for highlight
final highlightPaint = Paint()
..color = waveColor.withValues(alpha: opacity * 0.7)
..style = PaintingStyle.fill;
final highlightRectPath = Path()
..addRRect(RRect.fromRectAndRadius(
Rect.fromCenter(
center: Offset(width / 2, centerY),
width: currentWidth * 0.95,
height: thickness * 1.5),
Radius.circular(thickness * 0.8)));
canvas.drawPath(highlightRectPath, highlightPaint);
// Old Path drawing - keep if rectangle isn't desired
// paint.color = waveColor.withValues(alpha: opacity * 0.5); // Apply opacity
// canvas.drawPath(path, paint);
}
}
@override
bool shouldRepaint(covariant _HorizontalWavePainter oldDelegate) {
// Repaint whenever the animation progress or color changes
return oldDelegate.progress != progress ||
oldDelegate.waveColor != waveColor;
}
}