chore: format code
This commit is contained in:
parent
31180f9921
commit
1b3440e2fe
7 changed files with 516 additions and 2 deletions
461
.opencode/specs/ui-inconsistencies-analysis.md
Normal file
461
.opencode/specs/ui-inconsistencies-analysis.md
Normal file
|
|
@ -0,0 +1,461 @@
|
|||
# UI Inconsistencies Analysis - TracPulse
|
||||
|
||||
**Date:** Fri May 01 2026
|
||||
**Status:** Analysis Complete
|
||||
**Purpose:** Identify and document all UI inconsistencies across the TracPulse Flutter app
|
||||
|
||||
---
|
||||
|
||||
## Executive Summary
|
||||
|
||||
This analysis identifies **15 UI inconsistencies** across the TracPulse application, categorized into:
|
||||
- **Critical** (4): Impact user experience significantly
|
||||
- **Moderate** (6): Noticeable visual/interaction inconsistencies
|
||||
- **Minor** (5): Small polish items
|
||||
|
||||
---
|
||||
|
||||
## Critical Inconsistencies
|
||||
|
||||
### 1. **Inconsistent AppBar Implementations**
|
||||
**Severity:** Critical
|
||||
**Location:** Multiple screens
|
||||
**Issue:**
|
||||
- `main_screen.dart`: Uses custom header with `Container` (lines 171-226)
|
||||
- `settings_screen.dart`: Uses standard `AppBar` widget (lines 132-160)
|
||||
- `status_screen.dart`: Uses standard `AppBar` widget (lines 102-139)
|
||||
- `permission_screen.dart`: Uses custom header with `Container` (lines 247-271)
|
||||
|
||||
**Impact:** Breaks visual consistency and navigation patterns.
|
||||
|
||||
**Details:**
|
||||
```dart
|
||||
// Main & Permission screens - custom header
|
||||
Container(
|
||||
padding: const EdgeInsets.symmetric(horizontal: 20, vertical: 16),
|
||||
decoration: const BoxDecoration(
|
||||
color: Color(0xFF161616),
|
||||
border: Border(bottom: BorderSide(color: Color(0xFF2a2a2a), width: 1)),
|
||||
),
|
||||
// ...
|
||||
)
|
||||
|
||||
// Settings & Status screens - AppBar
|
||||
AppBar(
|
||||
backgroundColor: const Color(0xFF161616),
|
||||
elevation: 0,
|
||||
leading: IconButton(...),
|
||||
// ...
|
||||
)
|
||||
```
|
||||
|
||||
**Recommendation:** Standardize on either AppBar or custom header across all screens.
|
||||
|
||||
---
|
||||
|
||||
### 2. **Inconsistent Border Radius Values**
|
||||
**Severity:** Critical
|
||||
**Location:** All screens
|
||||
**Issue:** Mixed use of `4` and `3` pixel border radius values throughout the app.
|
||||
|
||||
**Examples:**
|
||||
- `permission_screen.dart` line 118: `borderRadius: BorderRadius.circular(4)` (dialog)
|
||||
- `permission_screen.dart` line 288: `BorderRadius.all(Radius.circular(4))` (icon container)
|
||||
- `permission_screen.dart` line 406: `borderRadius: BorderRadius.circular(3)` (granted badge)
|
||||
- `status_screen.dart` line 224: `borderRadius: BorderRadius.circular(3)` (filter chip)
|
||||
- `status_screen.dart` line 282: `borderRadius: BorderRadius.circular(3)` (icon container)
|
||||
|
||||
**Impact:** Subtle visual inconsistency that degrades polish.
|
||||
|
||||
**Recommendation:** Standardize on `4px` border radius globally (align with design system).
|
||||
|
||||
---
|
||||
|
||||
### 3. **Inconsistent Icon Sizes**
|
||||
**Severity:** Critical
|
||||
**Location:** All screens
|
||||
**Issue:** Icon sizes vary without clear hierarchy or purpose.
|
||||
|
||||
**Examples:**
|
||||
```dart
|
||||
// Main Screen
|
||||
Icons.shield, size: 20 // permission_screen header
|
||||
Icons.gps_fixed, size: 14 // status card
|
||||
Icons.radio_button_checked, size: 20 // tracking toggle
|
||||
Icons.arrow_upward, size: 16 // send button
|
||||
Icons.tune/terminal, size: 16 // nav buttons
|
||||
|
||||
// Settings Screen
|
||||
Icons.arrow_back, size: 20 // back button
|
||||
|
||||
// Status Screen
|
||||
Icons.terminal, size: 18 // title icon
|
||||
Icons.arrow_back, size: 20 // back button
|
||||
Icons.refresh, size: 20 // refresh button
|
||||
Icons.inbox_outlined, size: 48 // empty state
|
||||
Icons.[log icon], size: 10 // log entry icons
|
||||
```
|
||||
|
||||
**Impact:** Creates visual hierarchy confusion.
|
||||
|
||||
**Recommendation:** Define a clear icon size system:
|
||||
- Extra Large: 48px (empty states, hero elements)
|
||||
- Large: 24px (primary actions)
|
||||
- Medium: 20px (navigation, headers)
|
||||
- Small: 16px (inline actions)
|
||||
- Extra Small: 12px (badges, chips)
|
||||
|
||||
---
|
||||
|
||||
### 4. **Inconsistent Padding and Spacing**
|
||||
**Severity:** Critical
|
||||
**Location:** All screens
|
||||
**Issue:** Inconsistent padding values without clear system.
|
||||
|
||||
**Examples:**
|
||||
```dart
|
||||
// Various padding values used:
|
||||
padding: EdgeInsets.all(20) // main_screen status card
|
||||
padding: EdgeInsets.symmetric(horizontal: 20) // main_screen scroll
|
||||
padding: EdgeInsets.all(16) // permission intro card
|
||||
padding: EdgeInsets.all(14) // permission items
|
||||
padding: EdgeInsets.symmetric(horizontal: 14) // settings fields
|
||||
padding: EdgeInsets.symmetric(horizontal: 16) // status filter bar
|
||||
padding: EdgeInsets.symmetric(horizontal: 10) // status filter chips
|
||||
padding: EdgeInsets.only(left: 14, right: 14) // status log entries
|
||||
```
|
||||
|
||||
**Impact:** Reduces visual rhythm and professionalism.
|
||||
|
||||
**Recommendation:** Establish spacing system:
|
||||
- xxs: 4px
|
||||
- xs: 8px
|
||||
- sm: 12px
|
||||
- md: 16px
|
||||
- lg: 20px
|
||||
- xl: 24px
|
||||
- xxl: 32px
|
||||
|
||||
---
|
||||
|
||||
## Moderate Inconsistencies
|
||||
|
||||
### 5. **Inconsistent Text Styles**
|
||||
**Severity:** Moderate
|
||||
**Location:** All screens
|
||||
**Issue:** Text styling lacks systematic hierarchy.
|
||||
|
||||
**Examples:**
|
||||
```dart
|
||||
// Font sizes used: 9, 10, 11, 12, 13, 14, 16, 18, 48
|
||||
// Letter spacing: 0, 1, 2, 3
|
||||
// Font weights: w400, w600, w700
|
||||
|
||||
// Headers
|
||||
fontSize: 18, letterSpacing: 3 // main_screen
|
||||
fontSize: 16, letterSpacing: 3 // permission_screen
|
||||
fontSize: 14, letterSpacing: 2 // settings_screen, status_screen
|
||||
|
||||
// Body text
|
||||
fontSize: 13 // various places
|
||||
fontSize: 12 // various places
|
||||
fontSize: 11 // various places
|
||||
```
|
||||
|
||||
**Impact:** Reduces readability and visual hierarchy.
|
||||
|
||||
**Recommendation:** Create text style system with consistent naming.
|
||||
|
||||
---
|
||||
|
||||
### 6. **Inconsistent Button Styles**
|
||||
**Severity:** Moderate
|
||||
**Location:** Multiple screens
|
||||
**Issue:** Different button implementations for similar actions.
|
||||
|
||||
**Examples:**
|
||||
```dart
|
||||
// permission_screen: Custom Material + InkWell (line 512)
|
||||
Material(
|
||||
color: _allGranted ? ... : ...,
|
||||
child: InkWell(onTap: ..., child: Container(...))
|
||||
)
|
||||
|
||||
// main_screen: GestureDetector (line 308)
|
||||
GestureDetector(
|
||||
onTap: _toggleTracking,
|
||||
child: Container(...)
|
||||
)
|
||||
|
||||
// main_screen: OutlinedButton.icon (line 377)
|
||||
OutlinedButton.icon(
|
||||
onPressed: ...,
|
||||
style: OutlinedButton.styleFrom(...),
|
||||
)
|
||||
|
||||
// main_screen: Custom Material + InkWell (line 442)
|
||||
Material(
|
||||
color: const Color(0xFF161616),
|
||||
child: InkWell(...)
|
||||
)
|
||||
```
|
||||
|
||||
**Impact:** Inconsistent touch feedback and visual states.
|
||||
|
||||
**Recommendation:** Standardize on Material + InkWell pattern with consistent ripple effects.
|
||||
|
||||
---
|
||||
|
||||
### 7. **Inconsistent Empty State Handling**
|
||||
**Severity:** Moderate
|
||||
**Location:** `status_screen.dart`
|
||||
**Issue:** Only Status screen has empty state, but other screens might need it too.
|
||||
|
||||
**Current Implementation:** (lines 166-198)
|
||||
```dart
|
||||
child: _filteredLogs.isEmpty
|
||||
? Center(child: Column(...)) // Empty state
|
||||
: ListView.builder(...)
|
||||
```
|
||||
|
||||
**Impact:** Inconsistent UX when screens have no data.
|
||||
|
||||
**Recommendation:** Consider empty states for other screens where applicable.
|
||||
|
||||
---
|
||||
|
||||
### 8. **Inconsistent Color Opacity Usage**
|
||||
**Severity:** Moderate
|
||||
**Location:** All screens
|
||||
**Issue:** Mixed use of `.withValues(alpha: x)` and color literals.
|
||||
|
||||
**Examples:**
|
||||
```dart
|
||||
Color(0xFF00bcd4).withValues(alpha: 0.8) // main_screen line 251
|
||||
Color(0xFF00e676).withValues(alpha: 0.3) // permission_screen line 347
|
||||
Color(0xFF00e676).withValues(alpha: 0.15) // permission_screen line 359
|
||||
Color(0xFF00bcd4).withValues(alpha: 0.1) // permission_screen line 430
|
||||
```
|
||||
|
||||
**Impact:** Difficult to maintain consistent transparency levels.
|
||||
|
||||
**Recommendation:** Define standard alpha values (0.05, 0.1, 0.15, 0.3, 0.5, 0.6, 0.8).
|
||||
|
||||
---
|
||||
|
||||
### 9. **Inconsistent Loading/Error State Handling**
|
||||
**Severity:** Moderate
|
||||
**Location:** All screens
|
||||
**Issue:** No consistent loading indicators or error states.
|
||||
|
||||
**Observation:**
|
||||
- No loading spinners shown during async operations
|
||||
- Error handling relies only on SnackBar
|
||||
- No retry mechanisms for failed operations
|
||||
|
||||
**Recommendation:** Add loading states and error recovery UI.
|
||||
|
||||
---
|
||||
|
||||
### 10. **Inconsistent Switch Widget Styling**
|
||||
**Severity:** Moderate
|
||||
**Location:** `settings_screen.dart` line 386
|
||||
**Issue:** Switch uses deprecated color properties pattern (though updated to new API).
|
||||
|
||||
**Current:**
|
||||
```dart
|
||||
Switch(
|
||||
value: value,
|
||||
onChanged: onChanged,
|
||||
activeThumbColor: const Color(0xFF00e676),
|
||||
activeTrackColor: const Color(0xFF00e676).withValues(alpha: 0.3),
|
||||
)
|
||||
```
|
||||
|
||||
**Recommendation:** Consider using Material 3 Switch theming for consistency.
|
||||
|
||||
---
|
||||
|
||||
## Minor Inconsistencies
|
||||
|
||||
### 11. **Inconsistent SizedBox Spacing**
|
||||
**Severity:** Minor
|
||||
**Location:** All screens
|
||||
**Issue:** Arbitrary SizedBox heights without clear system.
|
||||
|
||||
**Examples:**
|
||||
```dart
|
||||
const SizedBox(height: 2) // settings_screen line 374
|
||||
const SizedBox(height: 4) // permission_screen line 311
|
||||
const SizedBox(height: 5) // status_screen line 306
|
||||
const SizedBox(height: 8) // multiple places
|
||||
const SizedBox(height: 10) // multiple places
|
||||
const SizedBox(height: 12) // multiple places
|
||||
// ... and more
|
||||
```
|
||||
|
||||
**Recommendation:** Use spacing system constants instead of literals.
|
||||
|
||||
---
|
||||
|
||||
### 12. **Inconsistent Container Decoration Pattern**
|
||||
**Severity:** Minor
|
||||
**Location:** All screens
|
||||
**Issue:** Some decorations defined inline, others could use constants.
|
||||
|
||||
**Example:**
|
||||
```dart
|
||||
// Repeated decoration pattern:
|
||||
decoration: BoxDecoration(
|
||||
color: const Color(0xFF161616),
|
||||
borderRadius: BorderRadius.circular(4),
|
||||
border: Border.all(color: const Color(0xFF2a2a2a), width: 1),
|
||||
)
|
||||
```
|
||||
|
||||
**Recommendation:** Create reusable decoration constants.
|
||||
|
||||
---
|
||||
|
||||
### 13. **Inconsistent Const Usage**
|
||||
**Severity:** Minor
|
||||
**Location:** Multiple files
|
||||
**Issue:** Some widgets marked const, others not (inconsistently).
|
||||
|
||||
**Example:**
|
||||
```dart
|
||||
// settings_screen line 111
|
||||
const [
|
||||
Icon(Icons.check_circle, color: Color(0xFF00e676), size: 18),
|
||||
// ...
|
||||
]
|
||||
```
|
||||
|
||||
**Recommendation:** Audit and add const where applicable for performance.
|
||||
|
||||
---
|
||||
|
||||
### 14. **Inconsistent Dialog Implementation**
|
||||
**Severity:** Minor
|
||||
**Location:** `permission_screen.dart` line 112
|
||||
**Issue:** Only one dialog in app, but pattern not reusable.
|
||||
|
||||
**Current:**
|
||||
```dart
|
||||
showDialog(
|
||||
context: context,
|
||||
barrierDismissible: false,
|
||||
builder: (ctx) => AlertDialog(...)
|
||||
)
|
||||
```
|
||||
|
||||
**Recommendation:** Create reusable dialog component with consistent styling.
|
||||
|
||||
---
|
||||
|
||||
### 15. **Inconsistent Animation Usage**
|
||||
**Severity:** Minor
|
||||
**Location:** `main_screen.dart` only
|
||||
**Issue:** Only main screen has animations (pulse effect).
|
||||
|
||||
**Current:** (lines 24-36)
|
||||
```dart
|
||||
late AnimationController _pulseController;
|
||||
late Animation<double> _pulseAnimation;
|
||||
```
|
||||
|
||||
**Impact:** Other screens feel static in comparison.
|
||||
|
||||
**Recommendation:** Consider subtle animations elsewhere for consistency (e.g., permission granted transitions).
|
||||
|
||||
---
|
||||
|
||||
## Color System Issues
|
||||
|
||||
### Inconsistent Color Definitions
|
||||
All colors are defined inline with hex literals. Should be centralized:
|
||||
|
||||
```dart
|
||||
// Current usage
|
||||
const Color(0xFF0d0d0d) // Background
|
||||
const Color(0xFF161616) // Card background
|
||||
const Color(0xFF2a2a2a) // Border
|
||||
const Color(0xFF00bcd4) // Primary (teal)
|
||||
const Color(0xFF00e676) // Success (green)
|
||||
const Color(0xFFe0e0e0) // Text primary
|
||||
const Color(0xFF9e9e9e) // Text secondary
|
||||
const Color(0xFF616161) // Text disabled
|
||||
```
|
||||
|
||||
**Recommendation:** Create `lib/theme/colors.dart` with named constants.
|
||||
|
||||
---
|
||||
|
||||
## Priority Matrix
|
||||
|
||||
| Priority | Count | Items |
|
||||
|----------|-------|-------|
|
||||
| Critical | 4 | AppBar inconsistency, Border radius, Icon sizes, Padding/spacing |
|
||||
| Moderate | 6 | Text styles, Button styles, Empty states, Color opacity, Loading states, Switch styling |
|
||||
| Minor | 5 | SizedBox spacing, Container decorations, Const usage, Dialog pattern, Animation usage |
|
||||
|
||||
---
|
||||
|
||||
## Recommendations Summary
|
||||
|
||||
1. **Create Design System** (`lib/theme/`)
|
||||
- `colors.dart` - Centralized color constants
|
||||
- `spacing.dart` - Spacing scale
|
||||
- `typography.dart` - Text style hierarchy
|
||||
- `decorations.dart` - Reusable decorations
|
||||
- `dimensions.dart` - Icon sizes, border radius
|
||||
|
||||
2. **Standardize Components** (`lib/widgets/`)
|
||||
- `app_button.dart` - Consistent button component
|
||||
- `app_header.dart` - Reusable header/appbar
|
||||
- `app_card.dart` - Standard card container
|
||||
- `app_dialog.dart` - Consistent dialog styling
|
||||
|
||||
3. **Update All Screens**
|
||||
- Replace inline styles with design system constants
|
||||
- Standardize component usage
|
||||
- Add consistent loading/error states
|
||||
- Ensure const usage for performance
|
||||
|
||||
4. **Testing**
|
||||
- Visual regression testing
|
||||
- Ensure animations perform smoothly
|
||||
- Verify touch targets meet accessibility standards
|
||||
|
||||
---
|
||||
|
||||
## Next Steps
|
||||
|
||||
1. **Phase 1: Design System Setup**
|
||||
- Create theme files
|
||||
- Define constants
|
||||
- Document usage
|
||||
|
||||
2. **Phase 2: Component Standardization**
|
||||
- Build reusable widgets
|
||||
- Update existing screens
|
||||
- Test interactions
|
||||
|
||||
3. **Phase 3: Polish**
|
||||
- Add missing animations
|
||||
- Improve empty/error states
|
||||
- Performance optimization
|
||||
|
||||
---
|
||||
|
||||
## Notes
|
||||
|
||||
- All screens use `fontFamily: 'monospace'` consistently ✓
|
||||
- Dark theme colors are consistent (good base) ✓
|
||||
- Material 3 is enabled (`useMaterial3: true`) ✓
|
||||
- Deprecated APIs have been updated (`.withValues(alpha:)`) ✓
|
||||
- ZoomPageTransitionsBuilder prevents white flash ✓
|
||||
|
||||
---
|
||||
|
||||
**End of Analysis**
|
||||
|
|
@ -93,6 +93,9 @@ class BridgeModule : FlutterPlugin, MethodChannel.MethodCallHandler {
|
|||
"isBatteryOptimizationDisabled" -> {
|
||||
result.success(isBatteryOptimizationDisabled())
|
||||
}
|
||||
"clearLogs" -> {
|
||||
clearLogs(result)
|
||||
}
|
||||
else -> result.notImplemented()
|
||||
}
|
||||
}
|
||||
|
|
@ -191,6 +194,7 @@ class BridgeModule : FlutterPlugin, MethodChannel.MethodCallHandler {
|
|||
}
|
||||
mainHandler.post { result.success(logs) }
|
||||
} catch (e: Exception) {
|
||||
Log.e(TAG, "fetchLogs failed", e)
|
||||
mainHandler.post { result.success(emptyList<Map<String, Any>>()) }
|
||||
}
|
||||
}.start()
|
||||
|
|
@ -228,4 +232,23 @@ class BridgeModule : FlutterPlugin, MethodChannel.MethodCallHandler {
|
|||
false
|
||||
}
|
||||
}
|
||||
|
||||
private fun clearLogs(result: MethodChannel.Result) {
|
||||
Thread {
|
||||
try {
|
||||
val deleted = runBlocking(Dispatchers.IO) {
|
||||
val db = context?.let { AppDatabase.getInstance(it) }
|
||||
if (db != null) {
|
||||
db.eventLogDao().deleteAll()
|
||||
} else {
|
||||
0
|
||||
}
|
||||
}
|
||||
mainHandler.post { result.success(deleted) }
|
||||
} catch (e: Exception) {
|
||||
Log.e(TAG, "clearLogs failed", e)
|
||||
mainHandler.post { result.success(-1) }
|
||||
}
|
||||
}.start()
|
||||
}
|
||||
}
|
||||
|
|
@ -42,7 +42,7 @@ import android.util.Log
|
|||
class LocationTrackingService : Service() {
|
||||
|
||||
private val binder = LocalBinder()
|
||||
private val serviceScope = CoroutineScope(SupervisorJob() + Dispatchers.Main)
|
||||
private val serviceScope = CoroutineScope(SupervisorJob() + Dispatchers.IO)
|
||||
private val TAG = "LocationTrackingService"
|
||||
|
||||
private lateinit var fusedLocationProvider: FusedLocationProvider
|
||||
|
|
|
|||
|
|
@ -18,4 +18,7 @@ interface EventLogDao {
|
|||
|
||||
@Query("DELETE FROM event_log WHERE timestamp < :beforeTimestamp")
|
||||
suspend fun deleteOlderThan(beforeTimestamp: Long)
|
||||
|
||||
@Query("DELETE FROM event_log")
|
||||
suspend fun deleteAll()
|
||||
}
|
||||
|
|
@ -116,4 +116,14 @@ class LocationBridge {
|
|||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
static Future<int> clearLogs() async {
|
||||
try {
|
||||
final result = await _methodChannel.invokeMethod<int>('clearLogs');
|
||||
return result ?? -1;
|
||||
} on PlatformException catch (e) {
|
||||
debugPrint('Failed to clear logs: ${e.message}');
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -269,4 +269,4 @@ class InfoScreen extends StatelessWidget {
|
|||
),
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -47,6 +47,15 @@ class _StatusScreenState extends State<StatusScreen> {
|
|||
}
|
||||
}
|
||||
|
||||
Future<void> _clearLogs() async {
|
||||
final result = await LocationBridge.clearLogs();
|
||||
if (mounted) {
|
||||
if (result >= 0) {
|
||||
await _loadLogs();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
String _formatTimestamp(int timestamp) {
|
||||
final dt = DateTime.fromMillisecondsSinceEpoch(timestamp);
|
||||
final ms = (dt.millisecond / 1000).toStringAsFixed(3).substring(2);
|
||||
|
|
@ -128,6 +137,14 @@ class _StatusScreenState extends State<StatusScreen> {
|
|||
onPressed: () => Navigator.pop(context),
|
||||
),
|
||||
actions: [
|
||||
IconButton(
|
||||
icon: const Icon(
|
||||
Icons.delete_outline,
|
||||
color: Color(0xFF616161),
|
||||
size: 20,
|
||||
),
|
||||
onPressed: _clearLogs,
|
||||
),
|
||||
IconButton(
|
||||
icon: const Icon(Icons.refresh, color: Color(0xFF616161), size: 20),
|
||||
onPressed: _loadLogs,
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue