# Flutter TV Admin UI: Best Practices & Implementation Guide

## Executive Summary

**Objective**: Build a world-class admin dashboard experience for Android TV using Flutter, maintaining the single-APK architecture while delivering excellent UX despite TV input constraints.

**Key Insight**: Flutter is fully capable of building excellent TV dashboards. The cross-platform nature doesn't limit quality - it means we can build platform-optimized experiences with proper focus management and TV-specific widgets.

**Target Platform**: Android TV Box (1920x1080 landscape, D-pad navigation, no touch input)

---

## 1. Understanding Android TV UX Constraints

### Critical Challenges

```
❌ No touch input (D-pad navigation only)
❌ Awkward text input (on-screen keyboard via remote)
❌ Screen distance (2-3 meters from viewer)
❌ Limited input methods (remote control only)
❌ No mouse hover states
❌ Small text is unreadable
```

### Our Advantages

```
✅ Flutter has excellent focus management built-in
✅ Rich animation and transition support
✅ Custom widgets are straightforward to build
✅ TV-specific packages available
✅ We control the entire experience
✅ Hardware acceleration for smooth animations
✅ Scalable UI via MediaQuery
```

---

## 2. Focus Management (The Foundation)

Focus management is **the most critical** aspect of TV UI. All interactions flow through proper focus handling.

### Core Principles

1. **Always show focus**: Users must see what's currently selected
2. **Predictable navigation**: D-pad moves in expected directions
3. **Large focus targets**: Everything must be easily selectable
4. **Clear visual feedback**: Animated focus indicators
5. **Sound feedback**: Audible confirmation of focus changes

### Implementation Pattern

```dart
// All TV widgets should follow this pattern:
class TVWidget extends StatefulWidget {
  @override
  State<TVWidget> createState() => _TVWidgetState();
}

class _TVWidgetState extends State<TVWidget> {
  late FocusNode _focusNode;

  @override
  void initState() {
    super.initState();
    _focusNode = FocusNode();

    // Listen to focus changes for visual feedback
    _focusNode.addListener(() {
      setState(() {
        // Update UI when focus changes
      });

      // Play sound feedback
      if (_focusNode.hasFocus) {
        TVSoundService().playFocusSound();
      }
    });
  }

  @override
  void dispose() {
    _focusNode.dispose();
    super.dispose();
  }

  @override
  Widget build(BuildContext context) {
    return Focus(
      focusNode: _focusNode,
      onKeyEvent: (node, event) {
        // Handle D-pad navigation
        return KeyEventResult.ignored;
      },
      child: Builder(
        builder: (context) {
          final isFocused = _focusNode.hasFocus;
          // Build focused/unfocused states
        },
      ),
    );
  }
}
```

---

## 3. TV Scaling System

All dimensions must scale relative to screen width to ensure consistency across different TV sizes.

```dart
class TVScaling {
  static double scale(BuildContext context) {
    return MediaQuery.of(context).size.width / 1920;
  }

  // Helper methods for common scaled values
  static double padding(BuildContext context) => 32 * scale(context);
  static double gap(BuildContext context) => 24 * scale(context);
  static double borderRadius(BuildContext context) => 12 * scale(context);

  // Typography scale
  static double fontSizeHeading(BuildContext context) => 48 * scale(context);
  static double fontSizeTitle(BuildContext context) => 36 * scale(context);
  static double fontSizeBody(BuildContext context) => 28 * scale(context);
  static double fontSizeCaption(BuildContext context) => 22 * scale(context);
}
```

---

## 4. TV-Optimized Form Widgets

### A. TV TextField

**Key Features:**
- Extra large hit targets (minimum 48x48dp)
- Clear focus indication (border + background + shadow)
- Keyboard hint for users
- Auto-scroll into view when focused
- Sound feedback on focus

**Usage:**
```dart
TVTextField(
  label: 'Nama Masjid',
  value: settings.masjidName,
  onChanged: (value) => updateSetting('masjidName', value),
  hint: 'Masjid Ar-Rahman',
  autofocus: false,
)
```

### B. TV Selection Field

**Key Features:**
- Dropdown/selector pattern (avoids typing)
- Search functionality for long lists
- Clear visual hierarchy
- Keyboard navigation (arrow keys)
- Selected state indication

**Usage:**
```dart
TVSelectionField<String>(
  label: 'Pilih Lokasi',
  value: settings.cityIdApi,
  options: [
    TVSelectionOption(
      label: 'Kota Yogyakarta',
      value: '577ef1154f3240ad5b9b413aa7346a1e',
      subtitle: 'Daerah Istimewa Yogyakarta',
      icon: Icons.location_city,
    ),
    TVSelectionOption(
      label: 'Jakarta Pusat',
      value: 'another_id_here',
      subtitle: 'DKI Jakarta',
      icon: Icons.location_city,
    ),
  ],
  onChanged: (value) => saveLocation(value),
)
```

### C. TV Button

**Key Features:**
- Large touch targets (minimum 48x48dp)
- Multiple style variants (primary, secondary, danger)
- Icon + label support
- Animated focus feedback
- Sound on press
- Keyboard activation (Enter/Select)

**Usage:**
```dart
TVButton(
  label: 'Simpan Pengaturan',
  icon: Icons.save,
  style: TVButtonStyle.primary,
  onPressed: () => saveSettings(),
)

// Icon-only variant
TVButton.icon(
  icon: Icons.arrow_back,
  label: 'Kembali',
  onPressed: () => Navigator.pop(context),
)
```

### D. TV Toggle Switch

**Key Features:**
- Large clickable area (entire row)
- Clear on/off state
- Animated transition
- Focus indication
- Label with description

**Usage:**
```dart
TVSwitch(
  label: 'Auto Kalibrasi Online',
  subtitle: 'Sinkronisasi waktu otomatis saat online',
  value: settings.autoCalibrationEnabled,
  onChanged: (value) => updateSetting('autoCalibration', value),
)
```

---

## 5. Layout Patterns

### A. Grid-Based Dashboard

**Purpose**: Main admin navigation screen

**Characteristics:**
- 3-column grid layout
- Large cards with icons
- Clear visual hierarchy
- Easy D-pad navigation
- Focus flows naturally left-to-right, top-to-bottom

**Structure:**
```
┌─────────────┬─────────────┬─────────────┐
│  Location   │  Appearance │  Settings   │
│   & Schedule│              │              │
├─────────────┼─────────────┼─────────────┤
│  Rotation   │    About    │             │
└─────────────┴─────────────┴─────────────┘
```

### B. Wizard-Style Multi-Step Forms

**Purpose**: Break complex forms into manageable steps

**Benefits:**
- Reduces cognitive load
- Easier navigation
- Progress indication
- Can save state between steps
- Less overwhelming than long forms

**When to Use:**
- Initial setup wizard
- Complex configuration with multiple sections
- Settings that require multiple steps
- Data entry with validation

**Structure:**
```
Step 1: Identity → Step 2: Location → Step 3: Appearance → Step 4: Confirm
    ↓                    ↓                   ↓                    ↓
[Masjid Name]    [Select City]      [Theme Color]      [Review All]
[Address]        [Sync Data]        [Background]      [Save & Exit]
```

### C. Tabbed Interface

**Purpose**: Organize related settings

**Benefits:**
- Clear categorization
- Easy to add new sections
- Familiar pattern from web
- Works well with D-pad

**Tab Structure:**
1. **Identitas** - Mosque name, address
2. **Jadwal & Sync** - Location, synchronization
3. **Tampilan** - Theme, background, slideshow
4. **Rotasi** - Screen timing
5. **Pembarisan** - Updates, version
6. **Tentang** - App information

---

## 6. Reducing Text Input (The Secret Sauce)

The best TV admin interfaces **minimize typing**. This is crucial because:

1. TV remotes are terrible for text input
2. On-screen keyboards are slow and error-prone
3. Screen distance makes typing difficult
4. User frustration increases with typing

### Strategies to Minimize Typing

#### A. Use Selection Instead of Text Input

**Bad (Text Input):**
```dart
TextField(
  decoration: InputDecoration(labelText: 'City ID API'),
)
```

**Good (Selection):**
```dart
TVSelectionField<String>(
  label: 'Pilih Kota',
  value: settings.cityIdApi,
  options: cityOptions,
  onChanged: (value) => saveCity(value),
)
```

#### B. Search + Select Pattern

For long lists (like 500+ Indonesian cities):

```dart
TVSearchSelectField<String>(
  label: 'Cari Lokasi',
  hint: 'Ketik nama kota...',
  searchDelegate: MyQuranCitySearchDelegate(),
  onSelect: (city) => saveCity(city),
)
```

#### C. Preset Options with Custom Value

```dart
TVSelectionField<String>(
  label: 'Warna Tema',
  value: settings.themeColor,
  options: [
    TVSelectionOption(label: 'Hijau', value: '0xFF006400'),
    TVSelectionOption(label: 'Biru', value: '0xFF0000FF'),
    TVSelectionOption(label: 'Merah', value: '0xFFFF0000'),
    TVSelectionOption(label: 'Emas', value: '0xFFFFD700'),
    TVSelectionOption(label: 'Kustom...', value: 'custom'),
  ],
  onChanged: (value) {
    if (value == 'custom') {
      // Show color picker dialog
    } else {
      saveThemeColor(value);
    }
  },
)
```

#### D. Slider for Numeric Values

**Bad (Number Input):**
```dart
TextField(
  keyboardType: TextInputType.number,
  decoration: InputDecoration(labelText: 'Durasi Iqomah (menit)'),
)
```

**Good (Slider):**
```dart
TVSlider(
  label: 'Durasi Iqomah',
  value: settings.iqomahDuration.inMinutes,
  min: 5,
  max: 20,
  divisions: 15,
  unit: 'menit',
  onChanged: (value) => saveIqomahDuration(Duration(minutes: value)),
)
```

#### E. Time Picker

```dart
TVTimePicker(
  label: 'Waktu Mulai Subuh',
  value: settings.subuhStartTime,
  onChanged: (value) => saveSubuhStartTime(value),
)
```

---

## 7. Visual Design Guidelines

### Focus Indicators

Focus indicators must be:
- **High contrast** (visible from 2-3 meters)
- **Animated** (smooth transitions, ~200ms)
- **Multi-layered** (border + background + shadow)
- **Consistent** (same pattern throughout app)

**Recommended Focus States:**

```dart
// Unfocused
border: 1px solid grey
background: white
shadow: none

// Focused
border: 3px solid primary_color
background: primary_color_10_percent
shadow: primary_color_30_percent_blur_20px
```

### Typography Scale

```dart
// Based on 1920px width baseline
Display (Clock):     120px  // For large time displays
Heading:             48px  // Screen titles
Title:               36px  // Section headers, button labels
Body:                28px  // Form labels, content
Caption:             22px  // Metadata, hints
```

### Color Contrast

- **Minimum contrast ratio**: 4.5:1 (WCAG AA)
- **Recommended contrast ratio**: 7:1 (WCAG AAA)
- **Focus indication**: Always use high contrast
- **Error states**: Red with white text
- **Success states**: Green with white text

### Spacing System

```dart
// Based on 8px grid system
padding_xs:   8px   // Tight spacing
padding_sm:   16px  // Compact
padding_md:   24px  // Default
padding_lg:   32px  // Comfortable
padding_xl:   48px  // Generous
```

---

## 8. Navigation Patterns

### D-Pad Navigation Flow

```
          ↑
          ↑
    ←  [Focus]  →
          ↓
          ↓
```

**Rules:**
1. **Natural flow**: Left-to-right, top-to-bottom
2. **Wrap around**: Last item → first item in same row
3. **Section boundaries**: Clear visual separators
4. **Skip separators**: Focus should jump over dividers

### Keyboard Shortcuts

```dart
// Standard Android TV shortcuts
KEY_ENTER / KEY_SELECT:  Activate focused item
KEY_BACK:               Go back / cancel
KEY_MENU:               Show context menu
KEY_MEDIA_PLAY_PAUSE:   Play/pause media
KEY_MEDIA_FAST_FORWARD: Skip forward
KEY_MEDIA_REWIND:       Skip back
```

---

## 9. Feedback Systems

### Visual Feedback

1. **Focus indicator**: Border + background + shadow
2. **Pressed state**: Scale down slightly (0.95x)
3. **Loading state**: Progress indicator + "Loading..." text
4. **Success state**: Green checkmark + success message
5. **Error state**: Red X + error message

### Sound Feedback

```dart
// Sound effects for different interactions
focus.mp3:      Subtle "click" when focus moves
select.mp3:    Confirming "ding" when item activated
error.mp3:     Low "buzz" for errors
success.mp3:   Pleasant "chime" for success
```

**Implementation:**
```dart
class TVSoundService {
  static final TVSoundService _instance = TVSoundService._internal();
  factory TVSoundService() => _instance;
  TVSoundService._internal();

  final AudioPlayer _audioPlayer = AudioPlayer();

  Future<void> playFocus() => _audioPlayer.play(AssetSource('sounds/focus.mp3'));
  Future<void> playSelect() => _audioPlayer.play(AssetSource('sounds/select.mp3'));
  Future<void> playError() => _audioPlayer.play(AssetSource('sounds/error.mp3'));
  Future<void> playSuccess() => _audioPlayer.play(AssetSource('sounds/success.mp3'));
}
```

### Haptic Feedback

```dart
// For game controllers with vibration
if (_focusNode.hasFocus) {
  HapticFeedback.lightImpact(); // Subtle vibration on focus
}
```

---

## 10. Performance Considerations

### Animation Performance

```dart
// Use AnimatedBuilder for efficient rebuilds
AnimatedBuilder(
  animation: _animation,
  builder: (context, child) {
    return Opacity(
      opacity: _animation.value,
      child: child,
    );
  },
  child: ExpensiveWidget(), // Built only once
)
```

### Lazy Loading

```dart
// Use ListView.builder for long lists
ListView.builder(
  itemCount: items.length,
  itemBuilder: (context, index) {
    return TVListItem(item: items[index]);
  },
)
```

### Image Optimization

```dart
// Cache images, use placeholders
CachedNetworkImage(
  imageUrl: url,
  placeholder: (context, url) => TVLoadingIndicator(),
  errorWidget: (context, url, error) => TVErrorIcon(),
)
```

---

## 11. Accessibility

### Screen Reader Support

```dart
// Add semantic labels
Semantics(
  label: 'Nama masjid field',
  value: settings.masjidName,
  hint: 'Masukkan nama masjid',
  child: TVTextField(...),
)
```

### High Contrast Mode

```dart
// Respect system high contrast setting
final isHighContrast = MediaQuery.of(context).highContrast;

if (isHighContrast) {
  // Use black and white only
  // Remove subtle gradients
  // Increase border widths
}
```

### Font Scaling

```dart
// Respect user's font size preferences
final scaleFactor = MediaQuery.of(context).textScaleFactor;

Text(
  'Hello',
  style: TextStyle(fontSize: 28 * scaleFactor),
)
```

---

## 12. Implementation Roadmap

### Phase 1: Foundation (Week 1)

**Focus Management System**
- [ ] Create `TVFocusManager` widget
- [ ] Implement focus scope nodes
- [ ] Add focus listener utilities
- [ ] Create focus ring animation
- [ ] Test D-pad navigation

**Scaling System**
- [ ] Create `TVScaling` utility
- [ ] Implement responsive layout helpers
- [ ] Define typography scale
- [ ] Test on different screen sizes

### Phase 2: Core Widgets (Week 2)

**Form Widgets**
- [ ] `TVTextField` with focus management
- [ ] `TVSelectionField` with dropdown
- [ ] `TVButton` with multiple styles
- [ ] `TVSwitch` for toggles
- [ ] `TVSlider` for numeric values

**Layout Widgets**
- [ ] `TVDashboardTile` for grid items
- [ ] `TVListItem` for list items
- [ ] `TVCard` for containers
- [ ] `TVSection` for grouping

### Phase 3: Advanced Features (Week 3)

**Navigation & Layouts**
- [ ] Grid-based dashboard screen
- [ ] Wizard-style multi-step form
- [ ] Tabbed interface
- [ ] Breadcrumb navigation

**Input Optimization**
- [ ] `TVSearchSelectField` with search
- [ ] `TVTimePicker` for time selection
- [ ] `TVDatePicker` for date selection
- [ ] `TVColorPicker` for color selection

### Phase 4: Polish (Week 4)

**Feedback Systems**
- [ ] Sound effects (focus, select, error, success)
- [ ] Visual feedback animations
- [ ] Loading indicators
- [ ] Error state UIs
- [ ] Success state UIs

**Performance**
- [ ] Animation optimization
- [ ] Image caching
- [ ] Lazy loading
- [ ] Memory leak testing

**Accessibility**
- [ ] Screen reader support
- [ ] High contrast mode
- [ ] Font scaling
- [ ] Focus indicators for color blind users

---

## 13. Testing Guidelines

### Unit Tests

```dart
testWidgets('TVTextField should show focus indicator', (tester) async {
  await tester.pumpWidget(
    MaterialApp(home: TVTextField(label: 'Test')),
  );

  // Simulate focus
  await tester.sendKeyEvent(LogicalKeyboardKey.tab);
  await tester.pump();

  // Verify focus indicator is visible
  expect(find.byType(Border), findsOneWidget);
});
```

### Integration Tests

```dart
testWidgets('Complete admin flow should work', (tester) async {
  // Build admin screen
  await tester.pumpWidget(MyApp());

  // Navigate to location settings
  await tester.sendKeyEvent(LogicalKeyboardKey.arrowRight);
  await tester.sendKeyEvent(LogicalKeyboardKey.enter);
  await tester.pumpAndSettle();

  // Select city
  await tester.sendKeyEvent(LogicalKeyboardKey.arrowDown);
  await tester.sendKeyEvent(LogicalKeyboardKey.enter);
  await tester.pumpAndSettle();

  // Verify selection was saved
  expect(find.text('Kota Yogyakarta'), findsOneWidget);
});
```

### Manual Testing Checklist

- [ ] All widgets are focusable with D-pad
- [ ] Focus indicators are clearly visible
- [ ] Navigation flow is predictable
- [ ] Text input works (though awkward)
- [ ] Sound feedback plays correctly
- [ ] Animations are smooth (60fps)
- [ ] Forms can be completed without mouse/touch
- [ ] Error states are clear
- [ ] Success states provide feedback
- [ ] Back button exits correctly

---

## 14. Common Pitfalls to Avoid

### ❌ Don't

1. **Don't use small hit targets**: Everything must be at least 48x48dp
2. **Don't rely on hover states**: TV remotes don't have hover
3. **Don't use subtle gradients**: Hard to see from distance
4. **Don't require extensive typing**: It's frustrating on TV
5. **Don't use small fonts**: Under 24px is unreadable from 2m
6. **Don't hide navigation**: Always show clear focus indicators
7. **Don't use web-only patterns**: TV ≠ web browser
8. **Don't forget sound feedback**: Crucial for accessibility
9. **Don't ignore keyboard shortcuts**: Power users expect them
10. **Don't make users backtrack**: Forward-flow navigation only

### ✅ Do

1. **Do make focus obvious**: Large borders, backgrounds, shadows
2. **Do provide clear feedback**: Visual + audio + haptic
3. **Do minimize text input**: Use selectors, wizards, presets
4. **Do use large fonts**: Minimum 24px, recommended 28px+
5. **Do test on real TV**: Emulator ≠ real experience
6. **Do optimize for D-pad**: Natural navigation flow
7. **Do add sound effects: Helpful feedback
8. **Do use animations: Smooth transitions (200ms)
9. **Do provide shortcuts: Keyboard navigation
10. **Do think in steps: Break complex forms into wizards

---

## 15. Recommended Packages

```yaml
dependencies:
  # TV-specific components
  flutter_tencent_eui: ^latest  # TV-optimized UI components

  # Enhanced focus management
  flutter_focus: ^latest  # Better focus control

  # Sound feedback
  audioplayers: ^latest  # Sound effects

  # Animations
  animations: ^latest  # Smooth transition helpers

  # Form validation
  form_field_validator: ^latest  # Reduce user errors

  # Image handling
  cached_network_image: ^latest  # Image caching
```

---

## 16. Success Metrics

### UX Metrics

- **Time to complete common tasks**:
  - Change mosque name: < 30 seconds
  - Change location: < 2 minutes
  - Sync monthly data: < 1 minute
  - Update theme: < 45 seconds

- **Error rate**:
  - Form validation errors: < 5%
  - Navigation errors: < 1%
  - Focus loss: < 2%

- **User satisfaction**:
  - Task completion rate: > 95%
  - User-reported frustration: < 10%
  - Willingness to use again: > 90%

### Technical Metrics

- **Performance**:
  - Focus change: < 16ms (60fps)
  - Screen transition: < 200ms
  - Form submission: < 500ms
  - No frame drops during animations

- **Reliability**:
  - No crashes in 24h operation
  - No memory leaks
  - No focus loss bugs
  - Graceful error handling

---

## 17. Conclusion

Flutter is **fully capable** of building excellent TV admin dashboards. The key is:

1. **Embrace TV constraints** rather than fighting them
2. **Think in focus**, not clicks
3. **Minimize text input** through smart UI choices
4. **Provide clear feedback** at every interaction
5. **Test on real hardware** with actual users

The single-APK architecture is **totally viable** with these TV-optimized widgets. You don't need a separate phone app to deliver a great admin experience.

### Next Steps

1. **Start with focus management** - this is the foundation
2. **Build TV-optimized widgets** - large, clear, responsive
3. **Reduce text input** - use selectors and wizards
4. **Add polish** - sounds, animations, feedback
5. **Test thoroughly** - on real TV with real users

Your admin dashboard will be excellent!

---

## Appendix: Code Examples

### Complete TV TextField Example

See `/lib/ui/widgets/tv/tv_text_field.dart` for full implementation.

### Complete TV Selection Field Example

See `/lib/ui/widgets/tv/tv_selection_field.dart` for full implementation.

### Complete TV Dashboard Example

See `/lib/ui/screens/tv_admin_screen.dart` for full implementation.

---

**Document Version**: 1.0
**Last Updated**: 2025-01-XX
**Author**: Claude Code
**Status**: Ready for Implementation
