Using Bloc pattern to show a list of data fetched from rest api.
Now I want to update the list on listview on button click.
When I click on button, it fetches the data but it does not update the listview
Here is the complete code.
main.dart
import 'package:bloc_demo/homepage.dart';
import 'package:flutter/material.dart';
void main() {
runApp(const MyApp());
}
class MyApp extends StatelessWidget {
const MyApp({super.key});
@override
Widget build(BuildContext context) {
return MaterialApp(
title: 'Flutter Demo',
theme: ThemeData(
primarySwatch: Colors.blue,
),
home: HomePage(),
);
}
}
HomePage.dart
class HomePage extends StatelessWidget {
String pageNo = "1";
@override
Widget build(BuildContext context) {
return MultiBlocProvider(
providers: [
BlocProvider<UserBloc>(
create: (BuildContext context) => UserBloc(UserRepository(),pageNo),
),
],
child: Scaffold(
appBar: AppBar(title: const Text('The BloC App')),
body: blocBody(),
floatingActionButton: FloatingActionButton(
onPressed: () {
pageNo = "2";
UserBloc(
UserRepository(),pageNo,
).add(LoadUserEvent());
},
child: const Icon(Icons.navigation),
),
),
);
}
Widget blocBody() {
return BlocProvider(
create: (context) => UserBloc(
UserRepository(),pageNo,
)..add(LoadUserEvent()),
child: BlocBuilder<UserBloc, UserState>(
builder: (context, state) {
if (state is UserLoadingState) {
return const Center(
child: CircularProgressIndicator(),
);
}
if (state is UserLoadedState) {
List<UserModel> userList = state.users;
return ListView.builder(
itemCount: userList.length,
itemBuilder: (_, index) {
return Padding(
padding:
const EdgeInsets.symmetric(vertical: 4, horizontal: 8),
child: Card(
color: Theme.of(context).primaryColor,
child: ListTile(
title: Text(
'${userList[index].firstName} ${userList[index].lastName}',
style: const TextStyle(color: Colors.white),
),
subtitle: Text(
'${userList[index].email}',
style: const TextStyle(color: Colors.white),
),
leading: CircleAvatar(
backgroundImage: NetworkImage(
userList[index].avatar.toString()),
))),
);
});
}
if (state is UserErrorState) {
return const Center(
child: Text("Error"),
);
}
return Container();
},
),
);
}
}
UserBloc
class UserBloc extends Bloc<UserEvent, UserState> {
final UserRepository _userRepository;
final String pageNo;
UserBloc(this._userRepository,this.pageNo) : super(UserLoadingState()) {
on<LoadUserEvent>((event, emit) async {
emit(UserLoadingState());
try {
final users = await _userRepository.getUsers(pageNo);
emit(UserLoadedState(users));
} catch (e) {
emit(UserErrorState(e.toString()));
}
});
}
}
UserEvent
@immutable
abstract class UserEvent extends Equatable {
const UserEvent();
}
class LoadUserEvent extends UserEvent {
@override
List<Object?> get props => [];
}
UserState
@immutable
abstract class UserState extends Equatable {}
//data loading state
class UserLoadingState extends UserState {
@override
List<Object?> get props => [];
}
class UserLoadedState extends UserState {
final List<UserModel> users;
UserLoadedState(this.users);
@override
List<Object?> get props => [users];
}
class UserErrorState extends UserState {
final String error;
UserErrorState(this.error);
@override
List<Object?> get props => [error];
}
UserModel
class UserModel {
int? id;
String? email;
String? firstName;
String? lastName;
String? avatar;
UserModel({this.id, this.email, this.firstName, this.lastName, this.avatar});
UserModel.fromJson(Map<String, dynamic> json) {
id = json['id'];
email = json['email'];
firstName = json['first_name'];
lastName = json['last_name'];
avatar = json['avatar'];
}
}
UserRepository
class UserRepository {
String userUrl = 'https://reqres.in/api/users?page=';
Future<List<UserModel>> getUsers(String pageNo) async {
Response response = await get(Uri.parse(userUrl+pageNo));
if (response.statusCode == 200) {
final List result = jsonDecode(response.body)['data'];
return result.map((e) => UserModel.fromJson(e)).toList();
} else {
throw Exception(response.reasonPhrase);
}
}
}
2
Answers
The first issue is here it is creating new bloc instance with new
UserRepository
repository. You can create singleton class if you like.To use access the current bloc instance you can use
BlocProvider.of<UserBloc>(context);
.Now while we fetch data, the old data is replaced by new data instead of extending the list. Therefor I am modifying the bloc like
Full snippet
HomePage
to aStatefulWidget
: This allows you to maintain the page number state properly. Also, it lets you interact with the bloc instance consistently.UserBloc
: Instead of creating a new instance ofUserBloc
on every button click, use the same instance that is provided to your widget tree viaBlocProvider
.how you can refactor your code:
Updated
HomePage