I'm currently bringing myself up to speed on bloc architecture working with open api
1.https://jsonplaceholder.typicode.com/albums and 2.https://jsonplaceholder.typicode.com/photos/albumId
i.e photos/albumId depends on albums response
albums response
[
{
"userId": 1,
"id": 1,
"title": "quidem molestiae enim"
},
{
"userId": 1,
"id": 2,
"title": "sunt qui excepturi placeat culpa"
},
{
"userId": 1,
"id": 3,
"title": "omnis laborum odio"
},
]
photos response with id gotten from albums i.e photos/albumId=1
[
{
"albumId": 1,
"id": 1,
"title": "accusamus beatae ad facilis cum similique qui sunt",
"url": "https://via.placeholder.com/600/92c952",
"thumbnailUrl": "https://via.placeholder.com/150/92c952"
},
{
"albumId": 1,
"id": 2,
"title": "reprehenderit est deserunt velit ipsam",
"url": "https://via.placeholder.com/600/771796",
"thumbnailUrl": "https://via.placeholder.com/150/771796"
},
{
"albumId": 1,
"id": 3,
"title": "officia porro iure quia iusto qui ipsa ut modi",
"url": "https://via.placeholder.com/600/24f355",
"thumbnailUrl": "https://via.placeholder.com/150/24f355"
},
{
"albumId": 1,
"id": 4,
"title": "culpa odio esse rerum omnis laboriosam voluptate repudiandae",
"url": "https://via.placeholder.com/600/d32776",
"thumbnailUrl": "https://via.placeholder.com/150/d32776"
},
{
"albumId": 1,
"id": 5,
"title": "natus nisi omnis corporis facere molestiae rerum in",
"url": "https://via.placeholder.com/600/f66b97",
"thumbnailUrl": "https://via.placeholder.com/150/f66b97"
},
{
"albumId": 1,
"id": 6,
"title": "accusamus ea aliquid et amet sequi nemo",
"url": "https://via.placeholder.com/600/56a8c2",
"thumbnailUrl": "https://via.placeholder.com/150/56a8c2"
},
]
I have been able to implement this but what seems to be the problem now is I'm trying to generate widget based on what i'm trying to do
what i'm trying to achieve image
** the problem ** (image)(https://drive.google.com/file/d/1LU_bwCooDT6Tgx-DpDNAFGDrwStkn2Gq/view?usp=sharing)
BLOC ALBUMS
class AlbumsBloc extends Bloc<AlbumsEvent, AlbumsState> {
final AlbumsRepository albumsRepository;
AlbumsBloc(this.albumsRepository) : super(AlbumsLoadingState()) {
on<AlbumsEvent>((event, emit) async {
if (event is LoadAlbumsEvent || event is RefreshAlbumsEvent) {
emit(AlbumsLoadingState());
try {
final albums = await albumsRepository.fetchAlbumsData();
emit(AlbumsLoadedState(albums: albums));
} catch (e) {
emit(AlbumsErrorState(error: e.toString()));
}
}
});
}
}
abstract class AlbumsEvent extends Equatable {
const AlbumsEvent();
}
class LoadAlbumsEvent extends AlbumsEvent {
@override
List<Object> get props => [];
}
class RefreshAlbumsEvent extends AlbumsEvent {
@override
List<Object> get props => [];
}
abstract class AlbumsState extends Equatable {
const AlbumsState();
}
class AlbumsEmptyState extends AlbumsState {
@override
List<Object?> get props => [];
}
class AlbumsLoadingState extends AlbumsState {
@override
List<Object> get props => [];
}
class AlbumsLoadedState extends AlbumsState {
final List<Albums> albums;
const AlbumsLoadedState({required this.albums});
@override
List<Object?> get props => [albums];
}
class AlbumsErrorState extends AlbumsState {
final String error;
const AlbumsErrorState({required this.error});
@override
List<Object?> get props => [];
}
BLOC PHOTOS
class PhotosBloc extends Bloc<PhotosEvent, PhotosState> {
final PhotosRepository photosRepository;
PhotosBloc(this.photosRepository) : super(PhotosLoadingState()) {
on<PhotosEvent>((event, emit) async {
if (event is LoadPhotosEvent) {
emit(PhotosLoadingState());
try {
final photos = await photosRepository.fetchPhotosData(event.albumId);
emit(PhotosLoadedState(photos: photos));
} catch (e) {
emit(PhotosErrorState(error: e.toString()));
}
}
});
}
}
abstract class PhotosEvent extends Equatable {
const PhotosEvent();
}
class LoadPhotosEvent extends PhotosEvent {
final int albumId;
const LoadPhotosEvent({required this.albumId});
@override
List<Object> get props => [albumId];
}
abstract class PhotosState extends Equatable {
const PhotosState();
}
class PhotosEmpty extends PhotosState {
@override
List<Object?> get props => [];
}
class PhotosLoadingState extends PhotosState {
@override
List<Object> get props => [];
}
class PhotosLoadedState extends PhotosState {
final List<Photos> photos;
const PhotosLoadedState({required this.photos});
@override
List<Object?> get props => [photos];
}
class PhotosErrorState extends PhotosState {
final String error;
const PhotosErrorState({required this.error});
@override
List<Object?> get props => [error];
}
THE CODE
class AlbumScreen extends StatefulWidget {
const AlbumScreen({Key? key}) : super(key: key);
@override
State<StatefulWidget> createState() {
return AlbumScreenState();
}
}
class AlbumScreenState extends State<AlbumScreen> {
final PhotosRepository photosRepository = PhotosRepository();
@override
void didChangeDependencies() {
BlocProvider.of<AlbumsBloc>(context).add(LoadAlbumsEvent());
super.didChangeDependencies();
}
@override
Widget build(BuildContext context) {
//final photoBloc = BlocProvider.of<PhotosBloc>(context);
return Scaffold(
backgroundColor: Colors.white,
appBar: PreferredSize(
preferredSize: const Size.fromHeight(60.0),
child: AppBar(
backgroundColor: Colors.white,
elevation: 0,
title: const Text("", style: TextStyle(color: Colors.black, fontSize: 20.0),),
),
),
body: SafeArea(
child: SingleChildScrollView(
child: BlocBuilder<AlbumsBloc,AlbumsState>(
builder: (context,state){
if( state is AlbumsLoadingState){
return const Center(
child: CircularProgressIndicator(),
);
}
if(state is AlbumsLoadedState){
return Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
for(int i = 0; i < state.albums.length; i++)
BlocBuilder <PhotosBloc,PhotosState>(
bloc: BlocProvider.of<PhotosBloc>(context)..add(LoadPhotosEvent(albumId: i)),
builder: (context,state){
if( state is PhotosLoadingState){
return const Center(
child: CircularProgressIndicator(),
);
}
if(state is PhotosLoadedState){
final photos = state.photos;
return AlbumsWithAlbumCard(
albumIndex: i,
photos: photos,
);
}
return Container();
}
)
],
);
}
return Container();
},
),
),
),
);
}
}
Check this small restructure:
class AlbumScreen extends StatelessWidget {
const AlbumScreen({Key? key}) : super(key: key);
@override
Widget build(BuildContext context) {
return BlocProvider(
create: (context) => AlbumsBloc()..add(LoadAlbumsEvent()),
child: AlbumView(),
);
}
}
class AlbumView extends StatelessWidget {
AlbumView({Key? key}) : super(key: key);
final PhotosRepository photosRepository = PhotosRepository();
@override
Widget build(BuildContext context) {
return Scaffold(
backgroundColor: Colors.white,
appBar: PreferredSize(
preferredSize: const Size.fromHeight(60.0),
child: AppBar(
backgroundColor: Colors.white,
elevation: 0,
title: const Text(
"",
style: TextStyle(color: Colors.black, fontSize: 20.0),
),
),
),
body: SafeArea(
child: SingleChildScrollView(
child: BlocBuilder<AlbumsBloc, AlbumsState>(
builder: (context, state) {
if (state is AlbumsLoadingState) {
return const Center(
child: CircularProgressIndicator(),
);
}
if (state is AlbumsLoadedState) {
return Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: state.albums.map((album) {
BlocListener<AlbumBloc, AlbumState>(
listenWhen: (previous, current) => previous != current,
listener: (context, state) {
if(state is AlbumsLoadedState) {
context.read<PhotosBloc>().add(LoadPhotosEvent(albumId: album.id));
}
},
child: BlocBuilder<PhotosBloc, PhotosState>(
builder: (context, state) {
if( state is PhotosLoadingState){
return const Center(
child: CircularProgressIndicator(),
);
}
if(state is PhotosLoadedState){
final photos = state.photos;
return AlbumsWithAlbumCard(
albumIndex: album.id,
photos: photos,
);
}
return Container();
},
),
)
}).toList(),
);
}
return Container();
},
),
),
),
);
}
}