For a Dashboard design as shown in image, I've to display various counts fetched from API. Each count will be fetched from different API by looking at the label.
My developed Structure,
DashboardScreen
CustomBottomMenuOptionWidget
(label) -StatelessWidget
MenuCountPage
(label) -StatefulWidget
(to display count, I've taken separateMenuCountScreen
widget with its ownevent
,state
&bloc
as per label)
When application get open everything works fine. For each different menu option I'm able to get counts for each label. My main problem is user move forward in app and create a new event and when come back to dashboard, how can I refresh this counts or simply says how can I add event to BLoC
of MenuCountScreen
to get updated value?.
Current implementation:
dashboard.dart (GridView)
GridView.count(
primary: false,
padding: const EdgeInsets.all(20),
crossAxisSpacing: 20,
mainAxisSpacing: 20,
crossAxisCount: 2,
childAspectRatio: 1.3,
children: <Widget>[
HomeBottomGridMenuItem(
label: kMenuLabelCalendar,
onItemClick: () {
_onTapHomeMenuItem(context, kMenuLabelCalendar);
},
icon: ICON_CALENDAR,
itemCountExist: true,
itemCount: 10,
),
...other menu item
)
HomeBottomGridMenuItem.dart
import 'package:flutter/material.dart';
import 'package:flutter_app/resources/colors.dart';
import 'package:flutter_app/screens/dashboard/menu_count/menu_count.dart';
class HomeBottomGridMenuItem extends StatelessWidget {
final String label;
final String icon;
final Function onItemClick;
final bool itemCountExist;
final int itemCount;
HomeBottomGridMenuItem({
this.label,
this.icon,
this.onItemClick,
this.itemCountExist,
this.itemCount,
});
@override
Widget build(BuildContext context) {
return Stack(
fit: StackFit.expand,
children: <Widget>[
InkWell(
onTap: onItemClick,
splashColor: Colors.black26,
child: Container(
padding: EdgeInsets.symmetric(
horizontal: 16.0,
vertical: 8.0,
),
decoration: BoxDecoration(
border: Border.all(
color: Colors.grey,
),
borderRadius: BorderRadius.all(
Radius.circular(
8.0,
),
),
),
child: Column(
mainAxisSize: MainAxisSize.min,
mainAxisAlignment: MainAxisAlignment.spaceEvenly,
crossAxisAlignment: CrossAxisAlignment.start,
children: <Widget>[
Image.asset(
icon,
height: 40.0,
width: 40.0,
color: kColorDashboardMenuItemIcon,
),
Text(
label,
textAlign: TextAlign.start,
style: Theme.of(context).textTheme.headline4.copyWith(
fontWeight: FontWeight.w400,
color: kColorDashboardMenuItemLabel,
),
)
],
),
),
),
Positioned(
top: 0,
right: 0,
child: Visibility(
visible: itemCountExist,
child: Container(
padding: EdgeInsets.symmetric(
horizontal: 14.0,
vertical: 5.0,
),
decoration: BoxDecoration(
color: kColorAppPrimaryBlackShade,
borderRadius: BorderRadius.only(
topRight: Radius.circular(
5.0,
),
bottomLeft: Radius.circular(
5.0,
),
),
),
child: MenuCountPage(
label: label,
),
),
),
),
],
);
}
}
MenuCountPage.dart
import 'package:flutter/material.dart';
import 'package:flutter_bloc/flutter_bloc.dart';
import 'menu_count.dart';
class MenuCountPage extends StatelessWidget {
final String label;
MenuCountPage({
@required this.label,
});
@override
Widget build(BuildContext context) {
return BlocProvider<MenuCountBloc>(
create: (context) {
return MenuCountBloc(context: context);
},
child: MenuCountScreen(
menuLabel: label,
),
);
}
}
MenuCountScreen.dart
import 'package:flutter/material.dart';
import 'package:flutter_bloc/flutter_bloc.dart';
import 'package:flutter_app/widgets/loader_circular.dart';
import 'menu_count.dart';
class MenuCountScreen extends StatefulWidget {
final String menuLabel;
MenuCountScreen({
@required this.menuLabel,
});
@override
_MenuCountScreenState createState() => _MenuCountScreenState();
}
class _MenuCountScreenState extends State<MenuCountScreen> {
MenuCountBloc _menuCountBloc;
@override
void initState() {
_menuCountBloc = BlocProvider.of<MenuCountBloc>(context);
_menuCountBloc.add(
GetMenuCount(menuLabel: widget.menuLabel),
);
super.initState();
}
@override
Widget build(BuildContext context) {
return Center(
child: BlocBuilder<MenuCountBloc, MenuCountState>(
builder: (context, state) {
if (state is MenuCountSuccess) {
return Text(
'${state.count}',
style: Theme.of(context).textTheme.headline2.copyWith(
color: Colors.white,
),
);
}
if (state is MenuCountFail) {
return Text(
'0',
style: Theme.of(context).textTheme.headline2.copyWith(
color: Colors.white,
),
);
}
return CircularLoader(
size: 25,
strokeWidth: 3,
color: Colors.white60,
);
},
),
);
}
}
MenuCountEvent.dart
import 'package:equatable/equatable.dart';
import 'package:flutter/cupertino.dart';
abstract class MenuCountEvent extends Equatable {
const MenuCountEvent();
}
class GetMenuCount extends MenuCountEvent {
final String menuLabel;
GetMenuCount({@required this.menuLabel});
@override
List<Object> get props => [];
}
MenuCountState.dart
import 'package:equatable/equatable.dart';
import 'package:flutter/cupertino.dart';
abstract class MenuCountState extends Equatable {
const MenuCountState();
@override
List<Object> get props => [];
}
class MenuCountInitial extends MenuCountState {}
class MenuCountLoading extends MenuCountState {}
class MenuCountSuccess extends MenuCountState {
final int count;
MenuCountSuccess({@required this.count});
@override
List<Object> get props => [count];
}
class MenuCountFail extends MenuCountState {}
MenuCountBloc.dart
import 'package:chopper/chopper.dart';
import 'package:flutter/material.dart';
import 'package:flutter_bloc/flutter_bloc.dart';
import 'package:flutter_app/api/api_service.dart';
import 'package:flutter_app/models/dashboard_count_responses/events_count_response.dart';
import 'package:flutter_app/models/dashboard_count_responses/open_ticket_count_response.dart';
import 'package:flutter_app/models/dashboard_count_responses/properties_count_response.dart';
import 'package:flutter_app/resources/strings.dart';
import 'package:flutter_app/utility/sharedpref_helper.dart';
import 'package:provider/provider.dart';
import 'menu_count.dart';
class MenuCountBloc extends Bloc<MenuCountEvent, MenuCountState> {
final BuildContext context;
MenuCountBloc({@required this.context});
@override
MenuCountState get initialState => MenuCountInitial();
@override
Stream<MenuCountState> mapEventToState(MenuCountEvent event) async* {
if (event is GetMenuCount) {
yield MenuCountLoading();
if (event.menuLabel.length > 0) {
yield* _getCountValueByLabel(event.menuLabel);
}
}
}
Stream<MenuCountState> _getCountValueByLabel(String menuLabel) async* {
switch (menuLabel) {
case kMenuLabelOpenTickets:
try {
final String token = await SharedPreferenceHelper.getToken();
final Response<OpenTicketCountResponse> apiResponse =
await Provider.of<ApiService>(context, listen: false)
.getOpenTicketCount(token);
if (apiResponse.isSuccessful) {
yield MenuCountSuccess(count: apiResponse.body.openTickets);
} else {
print('API : Response Failed');
yield MenuCountSuccess(count: 0);
}
} catch (exception) {
print('Exception: ${exception.toString()}');
yield MenuCountSuccess(count: 0);
}
break;
case kMenuLabelProperties:
try {
final String token = await SharedPreferenceHelper.getToken();
final Response<PropertiesCountResponse> apiResponse =
await Provider.of<ApiService>(context, listen: false)
.getPropertiesCount(token);
if (apiResponse.isSuccessful) {
yield MenuCountSuccess(count: apiResponse.body.properties_active);
} else {
print('API : Response Failed');
yield MenuCountSuccess(count: 0);
}
} catch (exception) {
print('Exception: ${exception.toString()}');
yield MenuCountSuccess(count: 0);
}
break;
case kMenuLabelCalendar:
try {
final String token = await SharedPreferenceHelper.getToken();
final Response<EventsCountResponse> apiResponse =
await Provider.of<ApiService>(context, listen: false)
.getEventsCount(token);
if (apiResponse.isSuccessful) {
yield MenuCountSuccess(count: apiResponse.body.events);
} else {
print('API : Response Failed');
yield MenuCountSuccess(count: 0);
}
} catch (exception) {
print('Exception: ${exception.toString()}');
yield MenuCountSuccess(count: 0);
}
break;
case kMenuLabelAllTickets:
try {
final String token = await SharedPreferenceHelper.getToken();
final Response<OpenTicketCountResponse> apiResponse =
await Provider.of<ApiService>(context, listen: false)
.getOpenTicketCount(token);
if (apiResponse.isSuccessful) {
yield MenuCountSuccess(count: apiResponse.body.tickets);
} else {
print('API : Response Failed');
yield MenuCountSuccess(count: 0);
}
} catch (exception) {
print('Exception: ${exception.toString()}');
yield MenuCountSuccess(count: 0);
}
break;
default:
yield MenuCountSuccess(count: 0);
}
}
}
Things that I already tried:
Provided
BLoC
from Parent (from 'Dashboard') and when user come back to dashboard tried adding event to get updated count. Didn't work. (If it's because I've different API call based on label & each count is associated with it's own instance of MenuCountBloc - if I'm wrong please clear me out)Tried taking a
bool
value and pass it toMenuCountScreen
fromDashboard
and when user come back to dashboard update the value of thatbool
thinking that it'll refresh and call event again but didn't worked.Addition to tried option 1, take 4 different
int
parameters to store 4 different count inBLoC
as well as inMenuCountState
. Thought it'll store 4 values forBLoC
that I've provided fromDashboard
. But didn't worked out.I would like to know, my way of implementation is correct or not and things that I've tried. Also a possible solution to achieve my task where I'm stuck.
EDITED : Sample Project I pushed here : GitHub