Flutter offline data changes in Hive, ListView UI not updating when data changes using Cache Manager

87 Views Asked by At

I am creating a most popular tv shows app where in the UI I display a list of posters. I am using Hive and Cache Manager packages to store the data in a local database and then try to retrieve them when offline. In the console it is showing how the data has been added to the Hive db with the posters. Though when offline it gives nothing. This is my code:

class MovieApiProvider {   Client client = Client();   final _apiKey = 'c6aeee577586ba38e487b74dfede5deb';   late Box<ItemModel> moviesBox;   Box<String>? postersBox;


  Future<void> initHive() async {
    final appDocumentDirectory = await path_provider.getApplicationDocumentsDirectory();
    await Hive.initFlutter(appDocumentDirectory.path);
    moviesBox = await Hive.openBox<ItemModel>('movieItems');
    postersBox = await Hive.openBox<String>('posters');
    print('Hive box length: ${postersBox?.length}');   }


  Future<void> closeHive() async {
    await moviesBox.close();
    await postersBox!.close();   }

  Future<void> downloadAndStorePoster(String url) async {
    await initHive();
    final file = await DefaultCacheManager().getSingleFile(url);
    if (file != null) {
      await postersBox?.put(url, file.path);
    }   }

  String? getStoredPoster(String url) {
    if (postersBox != null) {
      final storedValue = postersBox!.get(url);
      return storedValue?.toString();
    }
    return null;   }

  MovieApiProvider() {
    initHive().then((_){
      fetchMovieList();
    });   }


  Future<ItemModel> fetchMovieList() async {
    await initHive();
    print('entered');
    try {
      var url = Uri.parse(
          'http://api.themoviedb.org/3/tv/popular?api_key=c6aeee577586ba38e487b74dfede5deb');
      final response = await client.get(url);
      print(response.body.toString());
      if (response.statusCode == 200) {
        var itemModel = ItemModel.fromJson(json.decode(response.body));
        await postersBox!.clear();
        await moviesBox.clear();
        for (var result in itemModel.results) {
          // var item = ItemModel(result);
          await downloadAndStorePoster('https://image.tmdb.org/t/p/w300/${result.poster_path}');
          print('Added item to Hive box: $result');
        }
        await closeHive();
        return itemModel;
      } else {
        throw Exception('No Internet');
      }
    } catch (e) {
      print(e);
      throw Exception('Error fetching data');

    }

  }   List<ItemModel> getStoredData() {
    final box = Hive.box<ItemModel>('movieItems');
    return box.values.toList().cast<ItemModel>();
      }   }

Repository file

class Repository {
  final moviesApiProvider = MovieApiProvider();

  Future<ItemModel> fetchAllMovies () => moviesApiProvider.fetchMovieList();
}

Movie Bloc File:

class MoviesBloc {
  final _repository = Repository();
  final _moviesFetcher = PublishSubject<ItemModel>();
  bool _isClosed = false;

  Stream<ItemModel> get allMovies => _moviesFetcher.stream;

  fetchAllMovies() async {
    try {
      ItemModel itemModel = await _repository.fetchAllMovies();
      if (!_isClosed) {
        _moviesFetcher.sink.add(itemModel);
      }
    } catch (e) {
      List<ItemModel> storedData = _repository.moviesApiProvider
          .getStoredData(); // CHECK
      print('Stored data from Hive: $storedData');
      if (!_moviesFetcher.isClosed) {
        if (storedData.isNotEmpty) {
          ItemModel itemModel = ItemModel(storedData);
          _moviesFetcher.sink.add(itemModel);
        }
        else {
          _moviesFetcher.addError('No movies available');
        }
      }
    }
  }
      dispose() {
        _isClosed = true;
        _moviesFetcher.close();
      }
    }

    final bloc = MoviesBloc();

and the MovieList file to show the UI:

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

  @override
  State<StatefulWidget> createState() =>
     MovieListState();
}



class MovieListState extends State<MovieList> {


  @override
  void initState() {
    super.initState();
    bloc.fetchAllMovies();
  }

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


  @override
  Widget build(BuildContext context) {
    return Scaffold(
      body: StreamBuilder(
        stream: bloc.allMovies,
        builder: (context, AsyncSnapshot<ItemModel> snapshot) {
          if (snapshot.hasData) {
            return buildList(snapshot);
          } else if (snapshot.hasError) {
            return Text(snapshot.error.toString());
          }
          return const Center(child: CircularProgressIndicator());
        },
      ),
    );
  }

  Widget buildList(AsyncSnapshot<ItemModel> snapshot) {
    final orientation = MediaQuery.of(context).orientation;

    if (snapshot == null || !snapshot.hasData || snapshot.data!.results.isEmpty) {
      return Center(child: Text("No data available"));
    }

    return orientation == Orientation.portrait ? GridView.builder(
        itemCount: snapshot.data!.results.length,
        gridDelegate:
        const SliverGridDelegateWithFixedCrossAxisCount(crossAxisCount: 1,
        mainAxisSpacing: 5,
        crossAxisSpacing: 5),
        itemBuilder: (BuildContext context, int index) {
          final movie = snapshot.data!.results[index];
          final posterUrl = 'https://image.tmdb.org/t/p/w300/${movie.poster_path}';
          final cachedPoster = MovieApiProvider().getStoredPoster(posterUrl);
          return GridTile(
            child: InkResponse(
              enableFeedback: true,
              child: cachedPoster != null
                  ? Image.file(File(cachedPoster as String), fit: BoxFit.none)
                  : Image.network(
                'https://image.tmdb.org/t/p/w300/${movie.poster_path}',
                fit: BoxFit.none,
              ),
              onTap: () => openDetailPage(snapshot.data!, index),
            ),
          );
        }) : GridView.builder(
        itemCount: snapshot.data!.results.length,
        gridDelegate:
        const SliverGridDelegateWithFixedCrossAxisCount(crossAxisCount: 2,
            mainAxisSpacing: 5,
            crossAxisSpacing: 5),
        itemBuilder: (BuildContext context, int index) {
          final movie = snapshot.data!.results[index];
          final posterUrl = 'https://image.tmdb.org/t/p/w185/${movie.poster_path}';
          final cachedPoster = MovieApiProvider().getStoredPoster(posterUrl);
          return GridTile(
            child: InkResponse(
              enableFeedback: true,
              child: cachedPoster != null && File(cachedPoster as String).existsSync()
                  ? Image.file(File(cachedPoster as String), fit: BoxFit.cover)
              : Image.network(
          'https://image.tmdb.org/t/p/w185/${movie.poster_path}',
          fit: BoxFit.cover,
          ),
          ),
          );
        });
  }

  openDetailPage(ItemModel data, int index) {
    final page = MovieDetailBlocProvider(
      child: MovieDetail(
        title: data.results[index].title,
        posterUrl: data.results[index].backdrop_path,
        description: data.results[index].overview,
        releaseDate: data.results[index].release_date,
        voteAverage: data.results[index].vote_average.toString(),
        movieId: data.results[index].id,
      ),
    );
    Navigator.push(
      context,
      MaterialPageRoute(builder: (context) {
        return page;
      }),
    );
  }
}

With internet connection is working fine.

There was a similar post by other 2 members here in Stack Overflow but no solution was provided.

0

There are 0 best solutions below