0

I tried to understand why topRated and popular always get null value although results print it's value in console I'm using The Movie DB Api

import 'package:expandable/expandable.dart';
import 'package:flutter/material.dart';
import 'package:flutter_bloc/flutter_bloc.dart';
import 'package:g_movies/models/movies_model.dart';
import 'package:g_movies/shared/cubit/cubit.dart';
import 'package:g_movies/shared/cubit/states.dart';
import 'package:g_movies/shared/styles/colors.dart';
import 'package:google_fonts/google_fonts.dart';

class MoviesLayout extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return BlocProvider(
      create: (BuildContext context) {
        return MoviesCubit()
          ..getTopRatedData()
          ..getPopularData();
      },
      child: BlocConsumer<MoviesCubit, MoviesStates>(
        listener: (context, state) {},
        builder: (context, state) {
          return Scaffold(
            appBar: AppBar(
              title: Text(
                'GMovies',
                style: GoogleFonts.oswald(
                  fontWeight: FontWeight.bold,
                  fontSize: 24,
                  color: defaultColor,
                ),
              ),
              actions: [
                Padding(
                  padding: const EdgeInsets.symmetric(horizontal: 20),
                  child: CircleAvatar(
                    radius: 20,
                    child: IconButton(
                      icon: Icon(Icons.person),
                      onPressed: () {},
                    ),
                  ),
                ),
              ],
            ),
            body: SingleChildScrollView(
              child: Padding(
                padding: const EdgeInsets.all(20.0),
                child: Column(
                  children: [
                    ExpandablePanel(
                      theme: ExpandableThemeData(
                        headerAlignment: ExpandablePanelHeaderAlignment.center,
                      ),
                      header: Row(
                        children: [
                          Text(
                            'Top Rated',
                            style: TextStyle(
                              fontWeight: FontWeight.bold,
                              fontSize: 22,
                            ),
                          ),
                          SizedBox(
                            width: 5,
                          ),
                          Text(
                            'Movies',
                            style: TextStyle(
                              fontSize: 22,
                            ),
                          ),
                        ],
                      ),
                      collapsed: SizedBox(
                        height: 2,
                      ),
                      expanded: SizedBox(
                        width: double.infinity,
                        height: 200,
                        child: ListView.separated(
                          shrinkWrap: true,
                          scrollDirection: Axis.horizontal,
                          itemBuilder: (context, index) => movieItem(
                              MoviesCubit.get(context)
                                  .topRated!
                                  .results[index]),
                          separatorBuilder: (context, index) => SizedBox(
                            width: 10,
                          ),
                          itemCount:
                              MoviesCubit.get(context).topRated!.results.length,
                        ),
                      ),
                    ),
                    SizedBox(
                      height: 10,
                    ),
                    ExpandablePanel(
                      theme: ExpandableThemeData(
                        headerAlignment: ExpandablePanelHeaderAlignment.center,
                      ),
                      header: Row(
                        children: [
                          Text(
                            'Popular',
                            style: TextStyle(
                              fontWeight: FontWeight.bold,
                              fontSize: 22,
                            ),
                          ),
                          SizedBox(
                            width: 5,
                          ),
                          Text(
                            'Movies',
                            style: TextStyle(
                              fontSize: 22,
                            ),
                          ),
                        ],
                      ),
                      collapsed: SizedBox(
                        height: 2,
                      ),
                      expanded: SizedBox(
                        width: double.infinity,
                        height: 200,
                        child: ListView.separated(
                          shrinkWrap: true,
                          scrollDirection: Axis.horizontal,
                          itemBuilder: (context, index) => movieItem(
                              MoviesCubit.get(context).popular!.results[index]),
                          separatorBuilder: (context, index) => SizedBox(
                            width: 10,
                          ),
                          itemCount:
                              MoviesCubit.get(context).popular!.results.length,
                        ),
                      ),
                    ),
                    SizedBox(
                      height: 10,
                    ),
                    ExpandablePanel(
                      theme: ExpandableThemeData(
                        headerAlignment: ExpandablePanelHeaderAlignment.center,
                      ),
                      header: Row(
                        children: [
                          Text(
                            'Coming',
                            style: TextStyle(
                              fontWeight: FontWeight.bold,
                              fontSize: 22,
                            ),
                          ),
                          SizedBox(
                            width: 5,
                          ),
                          Text(
                            'Soon',
                            style: TextStyle(
                              fontSize: 22,
                            ),
                          ),
                        ],
                      ),
                      collapsed: SizedBox(
                        height: 2,
                      ),
                      expanded: SizedBox(
                        width: double.infinity,
                        height: 200,
                        child: ListView.separated(
                          shrinkWrap: true,
                          scrollDirection: Axis.horizontal,
                          itemBuilder: (context, index) => movieItem(
                              MoviesCubit.get(context)
                                  .topRated!
                                  .results[index]),
                          separatorBuilder: (context, index) => SizedBox(
                            width: 10,
                          ),
                          itemCount: 10,
                        ),
                      ),
                    ),
                    SizedBox(
                      height: 10,
                    ),
                    ExpandablePanel(
                      theme: ExpandableThemeData(
                        headerAlignment: ExpandablePanelHeaderAlignment.center,
                      ),
                      header: Row(
                        children: [
                          Text(
                            'Now',
                            style: TextStyle(
                              fontWeight: FontWeight.bold,
                              fontSize: 22,
                            ),
                          ),
                          SizedBox(
                            width: 5,
                          ),
                          Text(
                            'Playing',
                            style: TextStyle(
                              fontSize: 22,
                            ),
                          ),
                        ],
                      ),
                      collapsed: SizedBox(
                        height: 2,
                      ),
                      expanded: SizedBox(
                        width: double.infinity,
                        height: 200,
                        child: ListView.separated(
                          shrinkWrap: true,
                          scrollDirection: Axis.horizontal,
                          itemBuilder: (context, index) => movieItem(
                              MoviesCubit.get(context)
                                  .topRated!
                                  .results[index]),
                          separatorBuilder: (context, index) => SizedBox(
                            width: 10,
                          ),
                          itemCount: 10,
                        ),
                      ),
                    ),
                  ],
                ),
              ),
            ),
          );
        },
      ),
    );
  }

  Widget movieItem(Result model) {
    return ClipRRect(
      borderRadius: BorderRadius.circular(20),
      child: Image(
        fit: BoxFit.fill,
        width: 140,
        height: 210,
        image:
            NetworkImage('http://image.tmdb.org/t/p/w500${model.posterPath}'),
      ),
    );
  }
}

and my Cubit :

import 'package:flutter_bloc/flutter_bloc.dart';
import 'package:g_movies/models/movies_model.dart';
import 'package:g_movies/shared/cubit/states.dart';
import 'package:g_movies/shared/network/end_points.dart';
import 'package:g_movies/shared/network/remote/dio_helper.dart';

class MoviesCubit extends Cubit<MoviesStates> {
  MoviesCubit() : super(InitialState());

  static MoviesCubit get(context) => BlocProvider.of(context);

  MoviesModel? topRated;

  void getTopRatedData() {
    emit(GetTopRatedLoadingState());
    DioHelper.getData(
      url: topRatedMovies,
    ).then((value) {
      topRated = MoviesModel.fromJson(value.data);
      //print(value.data.toString());
      print(topRated!.results);
      emit(GetTopRatedSuccessState());
    }).catchError((error) {
      print(error);
      emit(GetTopRatedErrorState(error.toString()));
    });
  }

  MoviesModel? popular;

  void getPopularData() async {
    emit(GetPopularLoadingState());
    DioHelper.getData(
      url: popularMovies,
    ).then((value) {
      popular = MoviesModel.fromJson(value.data);
      //print(value.data.toString());
      print(popular!.results);
      emit(GetPopularSuccessState());
    }).catchError((error) {
      print(error);
      emit(GetPopularErrorState(error.toString()));
    });
  }
}

Console :

Performing hot restart...
Syncing files to device AOSP on IA Emulator...
Restarted application in 3,156ms.
I/flutter (12795): onCreate -- MoviesCubit
I/flutter (12795): onChange -- MoviesCubit, Change { currentState: Instance of 'InitialState', nextState: Instance of 'GetTopRatedLoadingState' }
I/flutter (12795): onChange -- MoviesCubit, Change { currentState: Instance of 'GetTopRatedLoadingState', nextState: Instance of 'GetPopularLoadingState' }

======== Exception caught by widgets library =======================================================
The following _CastError was thrown building BlocBuilder<MoviesCubit, MoviesStates>(dirty, state: _BlocBuilderBaseState<MoviesCubit, MoviesStates>#faf5b):
Null check operator used on a null value

The relevant error-causing widget was: 
  BlocBuilder<MoviesCubit, MoviesStates> file:///C:/src/flutter/.pub-cache/hosted/pub.dartlang.org/flutter_bloc-7.0.1/lib/src/bloc_consumer.dart:131:12
When the exception was thrown, this was the stack: 
#0      MoviesLayout.build.<anonymous closure> (package:g_movies/layout/movies_layout.dart:91:64)
#1      BlocBuilder.build (package:flutter_bloc/src/bloc_builder.dart:91:57)
#2      _BlocBuilderBaseState.build (package:flutter_bloc/src/bloc_builder.dart:163:21)
#3      StatefulElement.build (package:flutter/src/widgets/framework.dart:4691:27)
#4      ComponentElement.performRebuild (package:flutter/src/widgets/framework.dart:4574:15)
...
====================================================================================================

======== Exception caught by widgets library =======================================================
The following _CastError was thrown building BlocBuilder<MoviesCubit, MoviesStates>(dirty, state: _BlocBuilderBaseState<MoviesCubit, MoviesStates>#faf5b):
Null check operator used on a null value

The relevant error-causing widget was: 
  BlocConsumer<MoviesCubit, MoviesStates> file:///C:/Users/agala/.AndroidStudio4.0/g_movies/lib/layout/movies_layout.dart:19:14
When the exception was thrown, this was the stack: 
#0      MoviesLayout.build.<anonymous closure> (package:g_movies/layout/movies_layout.dart:91:64)
#1      BlocBuilder.build (package:flutter_bloc/src/bloc_builder.dart:91:57)
#2      _BlocBuilderBaseState.build (package:flutter_bloc/src/bloc_builder.dart:163:21)
#3      StatefulElement.build (package:flutter/src/widgets/framework.dart:4691:27)
#4      ComponentElement.performRebuild (package:flutter/src/widgets/framework.dart:4574:15)
...
====================================================================================================
I/flutter (12795): Invalid argument(s) (input): Must not be null
I/flutter (12795): onChange -- MoviesCubit, Change { currentState: Instance of 'GetPopularLoadingState', nextState: Instance of 'GetPopularErrorState' }
I/flutter (12795): [Instance of 'Result', Instance of 'Result', Instance of 'Result', Instance of 'Result', Instance of 'Result', Instance of 'Result', Instance of 'Result', Instance of 'Result', Instance of 'Result', Instance of 'Result', Instance of 'Result', Instance of 'Result', Instance of 'Result', Instance of 'Result', Instance of 'Result', Instance of 'Result', Instance of 'Result', Instance of 'Result', Instance of 'Result', Instance of 'Result']
I/flutter (12795): onChange -- MoviesCubit, Change { currentState: Instance of 'GetPopularErrorState', nextState: Instance of 'GetTopRatedSuccessState' }

2 Answers2

0

When you access MoviesStates's topRated or popular variable,
you need to check State status at BlocBuilder.

I recommend using event and state like below article.
https://blog.logrocket.com/state-management-flutter-bloc-pattern/

Suggestion

  1. [MoviesLayout] send data load Event to get topRate and popular data<br.
  2. [MoviesCubit] receive data load Event and call api to get data
  3. [MoviesCubit] emit data load completed State with topRate and popular data
  4. [MoviesLayout] In case of data load completed State in BlocBuilder,
    build layout accessing State's topRate and popular data.
    (If State is not data load completed State, build loading layout.)
KuKu
  • 6,654
  • 1
  • 13
  • 25
0

Before returning a widget inside a BlocBuilder, you should check different state of the bloc to ensure the data is loaded and no error happens:


// ... other lines

body: BlocConsumer<MoviesCubit, MoviesStates>(
  listener: (context, state) {},
  builder: (context, state) {

    // If the bloc is still in loading state

    if (state is GetTopRatedLoadingState ||
        state is GetPopularLoadingState) {
      return Container(child: Center(child: CircularProgressIndicator()));
    } else if (state is GetTopRatedErrorState || state is GetPopularErrorState) {

      // Return your error widget here with the error message

    }

    // If everything is success

    return Scaffold(
      appBar: AppBar(
        title: Text(
          'GMovies',
          style: GoogleFonts.oswald(
            fontWeight: FontWeight.bold,
            fontSize: 24,
            color: defaultColor,
          ),
        ),
      ),

      // ... other lines

    );
  },
)

When displaying 2 lists with 2 different data sources, you should use 2 different bloc so that when 1 data source failed, it won't affect the other one. In this case, splitting the topRated and popular data to different BlocBuilder is better to avoid potential bugs.

Bach
  • 2,928
  • 1
  • 6
  • 16