我试图理解为什么 topRated
和 popular
总是得到空值,尽管结果在控制台中打印了它的值
我正在使用 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' }
答案 0 :(得分:0)
当您访问 MoviesStates 的 topRated 或流行变量时,
您需要在 BlocBuilder 中检查状态状态。
我建议使用如下文章中的事件和状态。
https://blog.logrocket.com/state-management-flutter-bloc-pattern/
建议
答案 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 个数据源失败时,不会影响另一个。在这种情况下,将 topRated
和 popular
数据拆分为不同的 BlocBuilder
可以更好地避免潜在错误。