In the application, when you click on the "Add Group" button, a page with a text field opens, the user enters the name of the group and it is added to the database and drawn on the screen.
The problem is that only the name of the first group is displayed on the screen, and when you enter the name of the group a second time, it does not appear on the screen. But when you click on Hot Reload, all added groups are displayed. How to make the screen refresh every time a group is added and they are displayed as a list?
In my code, I access the bloc and pass the entered group name to the event:
void _saveGroup() {
final isValid = _formKey.currentState!.validate();
if (isValid) {
BlocProvider.of<NewGroupBloc>(context)
.add(AddGroupEvent(_groupController.text.trim()));
Navigator.pop(context);
}
}
Further, this name is converted into a database module and saved. I then add the model from the database to the list and output it:
lass GroupRepository extends BaseRepository<GroupDao, GroupDBData, Group> {
#override
GroupDao dao = injector<AppDb>().groupDao;
#override
BaseConverter<GroupDBData, Group> converter = GroupDbConverter();
final List<Group> groups = [];
Future<Group> convertGroup(Group group) async {
final convert = converter.outToIn(group);
final groupId = await dao.insert(convert);
Group groupWithID = Group(
id: groupId,
groupName: group.groupName,
);
return groupWithID;
}
Future<List<Group>> getList(Group group) async {
final groupWithID = await convertGroup(group);
groups.add(groupWithID);
return groups;
}
}
class GroupsPage extends StatefulWidget {
const GroupsPage({Key? key}) : super(key: key);
#override
State<GroupsPage> createState() => _GroupsPageState();
}
class _GroupsPageState extends State<GroupsPage> {
#override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: const Text('Groups'),
),
body: BlocBuilder<NewGroupBloc, NewGroupState>(
builder: (context, state) {
if (state is AddGroupState) {
return ListView.builder(
itemCount: state.groups.length,
itemBuilder: (context, index) {
final group = state.groups[index];
return ListTile(
title: Text(group.groupName),
);
},
);
}
return Container();
},
),
floatingActionButton: Padding(
child: FloatingActionButton.extended(
onPressed: () => _onAddGroup(context),
icon: const Icon(Icons.add),
label: const Text('Add Group'),
),
),
);
}
My bloc:
//bloc
class NewGroupBloc extends Bloc<NewGroupEvent, NewGroupState> {
NewGroupBloc() : super(const NewGroupInitial()) {
on<AddGroupEvent>(
(event, emit) async => emit(
await _addGroup(event),
),
);
}
final _provider = ProviderInjector.instance.addGroup;
Future<NewGroupState> _addGroup(AddGroupEvent event) async => _provider
.addGroup(
Group(groupName: event.groupName),
)
.then(AddGroupState.new);
}
//state
#immutable
abstract class NewGroupState extends Equatable {
const NewGroupState();
#override
List<Object?> get props => [];
}
class NewGroupInitial extends NewGroupState {
const NewGroupInitial();
}
class AddGroupState extends NewGroupState {
const AddGroupState(this.groups);
final List<Group> groups;
#override
List<Object?> get props => [groups];
}
//event
#immutable
abstract class NewGroupEvent extends Equatable {
#override
List<Object?> get props => [];
}
class AddGroupEvent extends NewGroupEvent {
AddGroupEvent(this.groupName);
final String groupName;
#override
List<Object?> get props => [groupName];
}
_onAddGroup:
void _onAddGroup(BuildContext context) {
Navigator.of(context, rootNavigator: true).push(
CupertinoPageRoute(
builder: (context) => const AddGroupPage(),
),
);
}
class AddGroupState extends NewGroupState {
const AddGroupState(this.groups);
final List<Group> groups;
#override
List<Object?> get props => [this.groups];
}
Try this
Related
Introduction: I am encountering an unexpected problem in my code when using BLoC, which I don't know how to fix, and I was looking for a solution.
When I use the code below, an object adds to the ListView.builder whenever I click on the ListTile as expected. However, This addition only happens once and when I continue to press on the ListTile to add more items to the list, the state is not updated (which is verifiable by pressing the Hot Reload button, which then updates the table with the items).
Problem: I have converted the MyHomePage widget to a StatefulWidget and wrapped the call to my bloc in a setState method (uncomment the commented section of the code to see that it works as expected). Now, as I understand it, I should be able to use BLoC for my state management needs and not need to use a StatefulWidget.
Questions:
Why is my code not working correctly?
How can I change my code to fix my problem?
Code:
Here is the complete minimum code, which reflects the problem that I am having:
import 'package:flutter/material.dart';
import 'package:flutter_bloc/flutter_bloc.dart';
main() {
runApp(const MyApp());
}
class MyApp extends StatelessWidget {
const MyApp({super.key});
#override
Widget build(BuildContext context) {
return BlocProvider(
create: (context) => CollectionBloc(),
child: const MaterialApp(
home: MyHomePage(),
),
);
}
}
class MyHomePage extends StatefulWidget {
const MyHomePage({super.key});
#override
State<MyHomePage> createState() => _MyHomePageState();
}
class _MyHomePageState extends State<MyHomePage> {
#override
Widget build(BuildContext context) {
return BlocBuilder<CollectionBloc, Collection>(
builder: (context, state) {
return Scaffold(
appBar: AppBar(
title: const Text('Flutter Demo Home Page'),
),
body: ListView.builder(
itemCount: state.hierarchy(state).length,
itemBuilder: (context, index) {
return ListTile(
onTap: () {
var showType = state.hierarchy(state)[index].showType;
// TODO: setState function is here.
setState(() {
if (showType == ShowType.collection) {
BlocProvider.of<CollectionBloc>(context).add(AddSeries(
series: Collection(
name:
"Series ${state.getChildrenOfNode(state.hierarchy(state)[index]).length + 1}",
showType: ShowType.series,
children: [],
)));
BlocProvider.of<CollectionBloc>(context).add(UpdateBloc(collection: state));
}
if (showType == ShowType.series) {
BlocProvider.of<CollectionBloc>(context).add(AddSeason(
series: state.hierarchy(state)[index],
season: Collection(
name:
'Season ${state.getChildrenOfNode(state.hierarchy(state)[index]).length + 1}',
showType: ShowType.season,
children: [],
)));
}
if (showType == ShowType.season) {
BlocProvider.of<CollectionBloc>(context).add(AddEpisode(
season: state.hierarchy(state)[index],
episode: Collection(
name:
"Episode ${state.getChildrenOfNode(state.hierarchy(state)[index]).length + 1}",
showType: ShowType.episode,
children: [],
)));
}
});
},
leading: Card(
child: TextWidget(name: state.hierarchy(state)[index].name),
),
);
},
),
floatingActionButton: FloatingActionButton(
child: const Text('to json'),
onPressed: () {
var toJson = state.toJson();
var fromJson = Collection.fromJson(toJson);
print(fromJson);
},
),
);
},
);
}
}
class TextWidget extends StatelessWidget {
const TextWidget({super.key, required this.name});
final String name;
#override
Widget build(BuildContext context) {
return Text(name);
}
}
/// BLoC
class InitialState extends Collection {
InitialState(collection)
: super(
name: collection.name,
showType: collection.showType,
children: collection.children,
);
}
abstract class BLoCEvents {}
class AddSeries extends BLoCEvents {
AddSeries({required this.series});
final Collection series;
}
class AddSeason extends BLoCEvents {
AddSeason({required this.series, required this.season});
final Collection series;
final Collection season;
}
class AddEpisode extends BLoCEvents {
AddEpisode({required this.season, required this.episode});
final Collection season;
final Collection episode;
}
class UpdateBloc extends BLoCEvents {
UpdateBloc({required this.collection});
final Collection collection;
}
class CollectionBloc extends Bloc<BLoCEvents, InitialState> {
CollectionBloc()
: super(InitialState(Collection(
name: 'Collection', showType: ShowType.collection, children: []))) {
on<AddSeries>(
((event, emit) => emit(state..addSeries(series: event.series))));
on<AddSeason>(((event, emit) =>
emit(state..addSeason(series: event.series, season: event.season))));
on<AddEpisode>(((event, emit) =>
emit(state..addEpisode(season: event.season, episode: event.episode))));
///todo: update bloc here.
on<UpdateBloc>(((event, emit) => print(state.toJson())));
}
}
/// Model
enum ShowType { collection, series, season, episode }
class Collection {
Collection(
{required this.name, required this.showType, required this.children});
final String name;
final ShowType showType;
List<Collection> children = [];
void addSeries({required Collection series}) => children.add(series);
void addSeason({required Collection series, required Collection season}) =>
series.children.add(season);
void addEpisode({required Collection season, required Collection episode}) =>
season.children.add(episode);
List<Collection> hierarchy(Collection node) {
List<Collection> list = [];
list.add(node);
for (Collection child in node.children) {
list.addAll(hierarchy(child));
}
return list;
}
List<Collection> getChildrenOfNode(Collection node) {
List<Collection> list = [];
for (Collection child in node.children) {
list.add(child);
}
return list;
}
toJson() {
return {
'name': name,
'showType': showType,
'children': children.map((child) => child.toJson()).toList(),
};
}
factory Collection.fromJson(Map<String, dynamic> json) {
return Collection(
name: json['name'],
showType: json['showType'],
children: json['children']
.map<Collection>((child) => Collection.fromJson(child))
.toList());
}
#override
String toString() {
return 'Collection{name: $name, showType: $showType, children: $children}';
}
}
I'm new to Bloc ( coming from provider ), everything works fine except whenever i try to add a second event to the same bloc on the same widget it never gets triggered, even the mapEventToState doesn't trigger,
i first add an event to get posts in the initState, this works fine, later on i add event on the refresh indicator onRefresh function, everything inside the function triggers Except the bloc add event.
I'm using Equatable, so i tried without it and no difference .
i tried to make separate class for each state, still Not working as well .
i tried Not to close the bloc on dispose, also not working.
ForumMain widget is a child of a BottomNavigationTabBar
here is my code :
main.dart
List<BlocProvider<Bloc>> _blocProviders() => [
BlocProvider<UserBloc>(
create: (context) => UserBloc(userRepository: RepositoryProvider.of(context),navigator: RepositoryProvider.of(context),prefs: RepositoryProvider.of(context),
),
),
BlocProvider<ForumBloc>(
create: (context) => ForumBloc( RepositoryProvider.of(context),_userBloc, RepositoryProvider.of(context),),
),
BlocProvider<WUpdateBloc>(
create: (context) => WUpdateBloc(
RepositoryProvider.of(context),
RepositoryProvider.of(context)),
),
BlocProvider<PlanBloc>(
create: (context) => PlanBloc(RepositoryProvider.of(context),RepositoryProvider.of(context),
)),
];
Widget build(BuildContext context) {
return LayoutBuilder(builder: (context, constraints) {
return OrientationBuilder(builder: (context, orientation) {
return RepositoryProvider(
create: (context) => _dioInstance(),
child: MultiRepositoryProvider(
providers: _repositoryProviders(),
child: MultiBlocProvider(
providers: _blocProviders(),
child: Builder(
builder: (context) => MaterialApp(
// theme: AppTheme.theme,
navigatorKey: navigatorKey,
navigatorObservers: [appNavigatorObserver],
localizationsDelegates: _getLocalizationsDelegates(),
supportedLocales: S.delegate.supportedLocales,
home: LoadingPage(),
debugShowCheckedModeBanner: false,
),
)),
),
);
});
});
}
forum_bloc.dart
class ForumBloc extends Bloc<ForumEvent, ForumState> {
ForumBloc(this._forumRepository, this._userBloc, this.navigator) : super(ForumState.defaultState());
final ForumRepository _forumRepository;
final UserBloc _userBloc;
final AppNavigator navigator;
#override
Stream<ForumState> mapEventToState(
ForumEvent event,
) async* {
if (event is GetPostsEvent) yield* _getPosts(event);
if (event is GetCommentsEvent) yield* _getComments(event);
if (event is RefreshPostsEvent) yield* _refreshPosts(event);
if (event is RefreshCommentsEvent) yield* _refreshComments(event);
if (event is NewPostRequest) yield* _newPost(event);
if (event is NewCommentRequest) yield* _newComment(event);
}
Stream<ForumState> _getPosts(GetPostsEvent event) async* {
print("get posts event called");
yield state.copyWith(status: BlocStatus.pending);
try {
// show progress hud
final postsResponse = await _forumRepository.getPosts(event.trainerID);
postsResponse.posts.sort((a, b) => b.datetimeCreated.compareTo(a.datetimeCreated));
print(postsResponse.posts[0].question);
yield state.copyWith(posts: postsResponse.posts, status: BlocStatus.success, total: postsResponse.posts.length);
} on DioError catch (error) {
// report error
yield state.copyWith(status: BlocStatus.error, appError: error.toAppError());
}
}
Stream<ForumState> _refreshPosts(RefreshPostsEvent event) async* {
print("refresh posts event called");
try {
// show progress hud
final postsResponse = await _forumRepository.getPosts(event.trainerID);
print(postsResponse.posts.length);
postsResponse.posts.sort((a, b) => b.datetimeCreated.compareTo(a.datetimeCreated));
yield state.copyWith(posts: postsResponse.posts, status: BlocStatus.success, total: postsResponse.posts.length);
} on DioError catch (error) {
// report error
// yield state.copyWith(status: BlocStatus.error, appError: error.toAppError());
}
}
forum_state.dart
class ForumState extends Equatable {
ForumState({
this.total,
this.posts,
this.status,
this.appError
});
final int total;
final List<BlogPost> posts;
final BlocStatus status;
final AppError appError;
factory ForumState.defaultState() => ForumState(
total: 0,
posts: [],
status: BlocStatus.idle,
appError: null
);
ForumState copyWith({
int total,
List<BlogPost> posts,
AppError appError,
BlocStatus status, }) { return ForumState(
total: total ?? this.total,
posts: posts ?? this.posts,
status: status,
);}
#override
List<Object> get props => [posts,total,status,appError];
#override
bool get stringify => true;
}
forum_state.dart failed trial to make separate class for each state
class ForumState extends Equatable {
#override
// TODO: implement props
List<Object> get props => [];
}
class ForumStateLoading extends ForumState {
}
class ForumStateSuccess extends ForumState {
ForumStateSuccess({
this.total,
this.posts,
});
final int total;
final List<BlogPost> posts;
#override
List<Object> get props => [posts,total];
}
class ForumStateRefresh extends ForumState {
ForumStateRefresh({
this.total,
this.posts,
});
final int total;
final List<BlogPost> posts;
#override
List<Object> get props => [posts,total];
}
class ForumStateError extends ForumState {
ForumStateError({
this.error,
});
final AppError error;
#override
List<Object> get props => [error];
}
forum_event.dart
abstract class ForumEvent extends Equatable {
const ForumEvent();
#override
List<Object> get props => [];
}
class GetPostsEvent extends ForumEvent {
final String trainerID;
GetPostsEvent(this.trainerID);
}
class RefreshPostsEvent extends ForumEvent {
final String trainerID;
RefreshPostsEvent(this.trainerID);
}
class GetCommentsEvent extends ForumEvent {
final String postID;
final String trainerID;
GetCommentsEvent(this.postID,this.trainerID);
}
class RefreshCommentsEvent extends ForumEvent {
final String postID;
final String trainerID;
RefreshCommentsEvent(this.postID,this.trainerID);
}
class SendPostEvent extends ForumEvent {
final NewPostRequest postRequest;
SendPostEvent(this.postRequest);
}
class SendCommentEvent extends ForumEvent {
final NewCommentRequest commentRequest;
SendCommentEvent(this.commentRequest);
}
forum_screen.dart
class ForumMain extends StatefulWidget {
#override
_ForumMainState createState() => _ForumMainState();
}
class _ForumMainState extends State<ForumMain> {
TextEditingController nameController = TextEditingController();
MyTheme myTheme = MyTheme();
ForumBloc _forumBloc;
PlanBloc _planBloc;
Completer<void> _refreshCompleter;
#override
void initState() {
// TODO: implement initState
super.initState();
myTheme.initLoadingHUD();
_forumBloc = BlocProvider.of<ForumBloc>(context);
_planBloc = BlocProvider.of<PlanBloc>(context);
_forumBloc.add(GetPostsEvent(_planBloc.state.chosenOrder.trainer.id));
_refreshCompleter = Completer<void>();
}
#override
void dispose() {
_forumBloc.close();
_planBloc.close();
super.dispose();
}
#override
Widget build(BuildContext context) {
ScreenUtil.init(context, width: 375, height: 812, allowFontScaling: true);
return BlocConsumer<ForumBloc,ForumState>(
listener: (context, state) {
print(state.status.toString());
setState(() {
_refreshCompleter?.complete();
_refreshCompleter = Completer();
});
myTheme.errorBlocListener(context, state.appError);
},
cubit: _forumBloc,
builder: (context, state) {
return Scaffold(
backgroundColor: Colors.white,
appBar: ForumAppBar(
height: 80.h,
forum: true,
),
body: state.status == BlocStatus.success ? Stack(
children: [
RefreshIndicator(
onRefresh: () {
_forumBloc.add(RefreshPostsEvent(
_planBloc.state.chosenOrder.trainer.id));
return _refreshCompleter.future;
},
child: ListView(
children: state.posts.map((e) {
Please change your forum_event file like this:
abstract class ForumEvent extends Equatable {
const ForumEvent([List props = const []]) : super();
}
class GetPostsEvent extends ForumEvent {
final String trainerID;
GetPostsEvent(this.trainerID);
#override
List<Object> get props => [trainerID];
}
class RefreshPostsEvent extends ForumEvent {
final String trainerID;
RefreshPostsEvent(this.trainerID);
#override
List<Object> get props => [trainerID];
}
class GetCommentsEvent extends ForumEvent {
final String postID;
final String trainerID;
GetCommentsEvent(this.postID,this.trainerID);
#override
List<Object> get props => [postID, trainerID];
}
class RefreshCommentsEvent extends ForumEvent {
final String postID;
final String trainerID;
RefreshCommentsEvent(this.postID,this.trainerID);
#override
List<Object> get props => [postID, trainerID];
}
class SendPostEvent extends ForumEvent {
final NewPostRequest postRequest;
SendPostEvent(this.postRequest);
#override
List<Object> get props => [postRequest];
}
class SendCommentEvent extends ForumEvent {
final NewCommentRequest commentRequest;
SendCommentEvent(this.commentRequest);
#override
List<Object> get props => [commentRequest];
}
You can try instead of _blocProviders() just place the List of providers inside MultiBlocProvider( providers: [ ... ],.
I see. Please try removing the following code. I don't think you should close it in this widget, since this is not where you initialize it.
_forumBloc.close();
_planBloc.close();
I develop an app using BLoC pattern.
In my app there are 2 routes, route A and B, and both of them access same data.
A problem caused when moving the routes as below.
Move to route B from route A that shows the data.
Update the data at route B.
Back to route A.
After moving back to route A, the StreamBuilder of showing the data never updates automatically.
How can I let the StreamBuilder update on resumed state?
Here are sample codes.
routeA.dart
class RouteA extends StatefulWidget {
#override
_RouteAState createState() => _RouteAState();
}
class _RouteAState extends State<RouteA> {
#override
Widget build(BuildContext context) {
final bloc = Bloc();
return Column(
children: [
StreamBuilder( // this StreamBuilder never updates on resumed state
stream: bloc.data, // mistake, fixed. before: bloc.count
builder: (_, snapshot) => Text(
snapshot.data ?? "",
)),
RaisedButton(
child: Text("Move to route B"),
onPressed: () {
Navigator.of(context).pushNamed("routeB");
},
),
],
);
}
}
routeB.dart
class RouteB extends StatefulWidget {
#override
_RouteBState createState() => _RouteBState();
}
class _RouteBState extends State<RouteB> {
#override
Widget build(BuildContext context) {
final bloc = Bloc();
return Center(
child: RaisedButton(
child: Text("Update data"),
onPressed: () {
bloc.update.add(null);
},
),
);
}
}
bloc.dart
class Bloc {
Stream<String> data;
Sink<void> update;
Model _model;
Bloc() {
_model = Model();
final update = PublishSubject<void>();
this.update = update;
final data = BehaviorSubject<String>(seedValue: "");
this.data = data;
update.map((event) => _model.update()).listen((event) => data.sink.add(_model.getData()));
}
}
model.dart
class Model {
static Model _model;
factory Model() { // model is singleton.
_model ??= Model._();
return _model;
}
Model._();
int _data = 0;
void update() {
_data++;
}
String getData() {
return _data.toString();
}
}
StreamBuilder updates the data whenever it gets changed not when just by calling stream
RouteA
class RouteA extends StatefulWidget {
#override
_RouteAState createState() => _RouteAState();
}
class _RouteAState extends State<RouteA> {
#override
Widget build(BuildContext context) {
return Column(
children: [
StreamBuilder( // this StreamBuilder never updates on resumed state
stream: bloc.data, // mistake, fixed. before: bloc.count
builder: (_, snapshot) => Text(
snapshot.data ?? "",
)),
RaisedButton(
child: Text("Move to route B"),
onPressed: () {
Navigator.of(context).push(MaterialPageRoute(builder: (ctx) {
return RouteB();
}));
},
),
],
);
}
}
Route B
class RouteB extends StatefulWidget {
#override
_RouteBState createState() => _RouteBState();
}
class _RouteBState extends State<RouteB> {
#override
Widget build(BuildContext context) {
return Center(
child: RaisedButton(
child: Text("Update data"),
onPressed: () {
bloc.updateData();
},
),
);
}
}
Bloc
class Bloc {
final _update = PublishSubject<String>();
Model _model = Model();
Stream<String> get data => _update.stream;
void updateData() async {
_model.update();
_update.sink.add(_model.getData());
_update.stream.listen((event) {
print(event);
});
}
dispose() {
_update.close();
}
}
final bloc = Bloc();
just follow above changes, it will do the trick for you.
i'm trying to pull new data from firebase cloud firestore and rebuild the widget on onPress of floating action button. i'm not sure how to rebuild the whole widget. Tried to call getList from the onPressed and setState() but still not rebulding widget evening nameList was updated.
class MyList extends StatefulWidget {
static const String id = 'test';
#override
_MyListState createState() => _MyListState();
}
class _MyListState extends State<MyList> {
#override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(title: Text('TEST'),),
body: MainList(),
floatingActionButton: FloatingActionButton(
child: Icon(Icons.refresh),
backgroundColor: Colors.teal,
onPressed: () {
}),
);
}
}
class MainList extends StatefulWidget {
#override
_MainListState createState() => _MainListState();
}
class _MainListState extends State<MainList> {
List<Test> nameList = [];
#override
void initState() {
super.initState();
getList();
}
getList() async {
final _name = await
Firestore.instance.collection('test').getDocuments();
nameList.clear();
for (var name in _name.documents) {
Test addName = new Test(
name.data['name'],
);
nameList.add(addName);
setState(() {});
}
}
#override
Widget build(BuildContext context) {
return ListView.builder(
itemCount: nameList.length,
itemBuilder: (BuildContext context, int index) {
return Container(
child: Text(nameList[index].name),
);
});
}
}
Once a widget is created initState isn't called again. So your listview is still reflecting the old data.
You could getList in the onPressed which would then update your nameList. You could then pass this nameList to MainList.
class MyList extends StatefulWidget {
static const String id = 'test';
#override
_MyListState createState() => _MyListState();
}
class _MyListState extends State<MyList> {
List<Test> nameList = [];
getList() async {
final _name = await
Firestore.instance.collection('test').getDocuments();
nameList.clear();
for (var name in _name.documents) {
Test addName = new Test(
name.data['name'],
);
nameList.add(addName);
setState(() {});
}
}
#override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(title: Text('TEST'),),
body: MainList(nameList: nameList),
floatingActionButton: FloatingActionButton(
child: Icon(Icons.refresh),
backgroundColor: Colors.teal,
onPressed: () {
getList();
}),
);
}
}
Your MainList widget would then look like:
class MainList extends StatefulWidget {
final List nameList;
MainList({this.nameList});
#override
_MainListState createState() => _MainListState();
}
class _MainListState extends State<MainList> {
#override
Widget build(BuildContext context) {
return ListView.builder(
itemCount: nameList.length,
itemBuilder: (BuildContext context, int index) {
return Container(
child: Text(nameList[index].name),
);
});
}
}
Just calling setState() is not enough. You'll have to tell Dart what you are going to set. Sample code :
setState ( ()=> nameList = _fetchedList ) ;
In the above code, the variable nameList is assigned within setState().
In your code, you've two options.
Option 1 :
setState(() {
nameList.clear();
for (var name in _name.documents) {
Test addName = new Test(
name.data['name'],
);
nameList.add(addName);
}
});
Or option 2, better way, use for loop to add in the data in another list and use setState with one line as below :
List<Test> _fetchedList ;
for (var name in _name.documents) {
Test addName = new Test(
name.data['name'],
);
_fetchedList.add(addName);
}
setState( ()=> nameList = _fetchedList ) ;
I recently started to dive into Bloc Pattern in flutter, and have learned using this project.
In this project, there is an adding post feature, so I wanted to implement alike one.
Even I changed Bloc's State via Event, It doesn't add Container Widget to ListView. I tried and search this problem but I can't figure:(
Here are my code below.
Here are Bloc Parts,
#immutable
abstract class ContentsState extends Equatable {
ContentsState([List props = const []]) : super(props);
}
class ContentsLoading extends ContentsState {
#override
String toString() => 'ContentsLoading';
}
class ContentsLoaded extends ContentsState {
final List<Content> contents;
ContentsLoaded(this.contents);
#override
String toString() => 'ContentsLoaded { contents: $contents }';
}
#immutable
abstract class ContentsEvent extends Equatable {
ContentsEvent([List props = const []]) : super(props);
}
class LoadContents extends ContentsEvent {
#override
String toString() => 'LoadContents';
}
class AddContent extends ContentsEvent {
final Content content;
AddContent(this.content) : super([content]);
#override
String toString() => 'AddContent { content: $content }';
}
class ContentsBloc extends Bloc<ContentsEvent, ContentsState> {
#override
ContentsState get initialState => ContentsLoading();
#override
Stream<ContentsState> mapEventToState(ContentsEvent event) async* {
if (event is LoadContents) {
yield* _mapLoadContentsToState();
} else if (event is AddContent) {
yield* _mapAddContentToState(event);
}
}
Stream<ContentsState> _mapLoadContentsToState() async* {
final List<Content> contents = [Content('message')];
yield ContentsLoaded(contents);
}
Stream<ContentsState> _mapAddContentToState(AddContent event) async* {
if (currentState is ContentsLoaded) {
final List<Content> updateContents =
List.from((currentState as ContentsLoaded).contents)
..add(event.content);
// Output update contents
print('update contents : $updateContents');
yield ContentsLoaded(updateContents);
}
}
}
and presentation part,
class AddPage extends StatelessWidget {
#override
Widget build(BuildContext context) {
return BlocProvider(
builder: (context) => ContentsBloc()..dispatch(LoadContents()),
child: Column(
children: <Widget>[
AddBody(),
InputArea(),
],
),
);
}
}
class AddBody extends StatelessWidget {
AddBody({Key key}) : super(key: key);
#override
Widget build(BuildContext context) {
final contentsBloc = BlocProvider.of<ContentsBloc>(context);
return BlocBuilder(
bloc: contentsBloc,
builder: (
BuildContext context,
ContentsState state,
) {
if (state is ContentsLoading) {
return Center(child: CircularProgressIndicator());
} else if (state is ContentsLoaded) {
final contents = state.contents;
return Expanded(
child: ListView.builder(
itemCount: contents.length,
itemBuilder: (context, index) {
final content = contents[index];
return ContentItem(content: content);
},
),
);
}
},
);
}
}
class ContentItem extends StatelessWidget {
final Content content;
ContentItem({
Key key,
this.content,
}) : super(key: key);
#override
Widget build(BuildContext context) {
return Container(
key: Key('__Content__${content.message}__'),
child: Column(
children: <Widget>[
Text('Message Here'),
Text(content.message.toString()),
],
),
);
}
}
~~ in InputArea. There is a TextEditingController and TextField.
onTap: () => contentsBloc.dispatch(AddContent(Content(_controller.text))),
~~
Finally, this is my Content class
#immutable
class Content extends Equatable {
final String message;
Content(this.message);
#override
String toString() => 'Content { message: $message }';
}
When I run this code, I can see CircleProgressIndicator() -When State is ContentsLoading()- after that, one Container with message text shows up. So far, so good.
But After I pressed add button and fired AddContent() Event, new content doesn't show up.
Here is a console Log.
AddContent { content: Content { message: new message } }
update contents : [Content { message: message }, Content { message: new message }]
I want to add Container, but it doesn't work.
Can anyone help me? Thanks in advance:)
I resolved this problem.
I just add this one line in Bloc's ContentsLoaded class which extends ContentsState.
class ContentsLoaded extends ContentsState {
final List<Content> contents;
// just add this line below.
ContentsLoaded([this.contents = const []]) : super([contents]);
#override
String toString() => 'ContentsLoaded { contents: $contents }';
}
Thanks.