Skip to content

feat: add offline support using hive local database β€” OpSo currently requires internet connectivity to display program information, showing a blank screen with no cached data when offlineΒ #482

Description

@divyanshim27

πŸš€ Problem Statement

OpSo is a discovery app β€” its primary function is providing information about open-source programs. This information (program descriptions, dates, eligibility, etc.) is largely static content that changes infrequently. Yet:

  • If a student opens OpSo on an airplane or in a low-connectivity area, they see a loading spinner or blank screen
  • Program details they were just reading are not available offline
  • There is no local caching mechanism anywhere in the codebase
  • The app does not function as a reliable reference tool

For a tool meant to help developers prepare for program applications, being unavailable offline is a serious UX gap.

βœ… Proposed Solution

Use hive (lightweight, fast Flutter key-value database) to cache program data locally with a 24-hour TTL.

1. Add dependencies to pubspec.yaml

dependencies:
  hive: ^2.2.3
  hive_flutter: ^1.1.0
  connectivity_plus: ^6.0.3

dev_dependencies:
  hive_generator: ^2.0.1
  build_runner: ^2.4.9

2. Create cache service lib/services/cache_service.dart

// lib/services/cache_service.dart
import 'package:hive_flutter/hive_flutter.dart';

class CacheService {
  static const _programBox = 'programs_cache';
  static const _metaBox = 'cache_meta';
  static const cacheTTL = Duration(hours: 24);

  static Future<void> init() async {
    await Hive.initFlutter();
    await Hive.openBox(_programBox);
    await Hive.openBox(_metaBox);
  }

  static Future<void> cachePrograms(String key, List<Map<String, dynamic>> data) async {
    final box = Hive.box(_programBox);
    await box.put(key, data);
    await Hive.box(_metaBox).put('${key}_timestamp', DateTime.now().toIso8601String());
  }

  static List<Map<String, dynamic>>? getCachedPrograms(String key) {
    final metaBox = Hive.box(_metaBox);
    final timestampStr = metaBox.get('${key}_timestamp') as String?;
    if (timestampStr == null) return null;

    final timestamp = DateTime.parse(timestampStr);
    if (DateTime.now().difference(timestamp) > cacheTTL) return null; // expired

    final box = Hive.box(_programBox);
    final data = box.get(key);
    return data != null ? List<Map<String, dynamic>>.from(data) : null;
  }

  static bool isCacheValid(String key) {
    return getCachedPrograms(key) != null;
  }
}

3. Create connectivity-aware data provider

// lib/providers/program_provider.dart
import 'package:connectivity_plus/connectivity_plus.dart';

class ProgramProvider extends ChangeNotifier {
  List<ProgramModel> _programs = [];
  bool _isOffline = false;
  bool _isLoading = false;

  bool get isOffline => _isOffline;

  Future<void> loadPrograms() async {
    _isLoading = true;
    notifyListeners();

    final connectivity = await Connectivity().checkConnectivity();
    _isOffline = connectivity == ConnectivityResult.none;

    if (_isOffline) {
      // Load from cache
      final cached = CacheService.getCachedPrograms('all_programs');
      if (cached != null) {
        _programs = cached.map(ProgramModel.fromMap).toList();
      }
    } else {
      // Fetch fresh data and update cache
      final freshData = await ProgramRepository.fetchAll();
      _programs = freshData;
      await CacheService.cachePrograms('all_programs', freshData.map((p) => p.toMap()).toList());
    }

    _isLoading = false;
    notifyListeners();
  }
}

4. Show offline banner in the home screen

// lib/screens/home_screen.dart
if (provider.isOffline)
  Container(
    width: double.infinity,
    color: Colors.orange[100],
    padding: const EdgeInsets.symmetric(vertical: 6, horizontal: 16),
    child: Row(children: [
      const Icon(Icons.wifi_off, size: 16, color: Colors.orange),
      const SizedBox(width: 8),
      const Text('You\'re offline β€” showing cached data', style: TextStyle(color: Colors.orange)),
    ]),
  ),

πŸ“ Files to Create / Modify

File Change
pubspec.yaml Add hive, hive_flutter, connectivity_plus
lib/services/cache_service.dart Create Hive cache service
lib/providers/program_provider.dart Add offline/online data loading
lib/main.dart Initialize Hive on startup
lib/screens/home_screen.dart Show offline banner when cache used

Suggested labels: enhancement, flutter, offline, performance, level: intermediate

I would like to work on this. Could you please assign it to me?

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions