用于空值的空检查运算符 - BloC with Null-Safety

时间:2021-07-21 01:17:16

标签: flutter dart runtime-error bloc dart-null-safety

我试图理解为什么 topRatedpopular 总是得到空值,尽管结果在控制台中打印了它的值 我正在使用 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}'),
      ),
    );
  }
}

还有我的肘子:

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()));
    });
  }
}

控制台:

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 个答案:

答案 0 :(得分:0)

当您访问 MoviesStates 的 topRated 或流行变量时,
您需要在 BlocBuilder 中检查状态状态。

我建议使用如下文章中的事件和状态。
https://blog.logrocket.com/state-management-flutter-bloc-pattern/

建议

  1. [MoviesLayout] 发送数据加载事件以获取 topRate 和热门数据
  2. [MoviesCubit]接收数据加载事件并调用api获取数据
  3. [MoviesCubit] 使用 topRate 和流行数据发出数据加载完成状态
  4. [MoviesLayout] BlocBuilder 中数据加载完成状态时,
    构建布局访问 State 的 topRate 和流行数据。
    (如果 State 不是数据加载完成状态,则构建加载布局。)

答案 1 :(得分:0)

BlocBuilder 内返回小部件之前,您应该检查块的不同状态以确保数据已加载且不会发生错误:


// ... 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

    );
  },
)

当显示具有 2 个不同数据源的 2 个列表时,您应该使用 2 个不同的块,以便当 1 个数据源失败时,不会影响另一个。在这种情况下,将 topRatedpopular 数据拆分为不同的 BlocBuilder 可以更好地避免潜在错误。