Flutter Bloc doesn't update data in UI using BLOC pattern - flutter

I am working on an ordering application. where I have an object called "Comanda" that contains a list of objects called "CommandLine". I want to handle with the BLOC pattern the whole issue of adding lines to the command, deleting and/or editing.
I show the implemented code.
CLASSES:
class Comanda extends Equatable {
Comanda({this.lineasComanda = const <LineaComanda>[], this.mesa, this.zona});
List<LineaComanda> lineasComanda;
Mesa? mesa;
Zona? zona;
factory Comanda.fromJson(Map<String, dynamic> json) => Comanda(
lineasComanda: json["lineasComanda"] ?? [],
mesa: Mesa.fromJson(json["mesa"]),
zona: Zona.fromJson(json["mesa"]));
Map<String, dynamic> toJson() => {
"lineasComanda": lineasComanda,
"mesa": mesa != null ? mesa!.toJson() : null,
"zona": zona != null ? zona!.toJson() : null,
};
Comanda copyWith({
List<LineaComanda>? lineasComanda,
Mesa? mesa,
Zona? zona,
}) {
return Comanda(
lineasComanda: lineasComanda!,
mesa: mesa ?? this.mesa,
zona: zona ?? this.zona,
);
}
#override
List<Object?> get props => [lineasComanda, mesa, zona];
}
class LineaComanda extends Equatable {
LineaComanda({
required this.articulo,
required this.unidades,
this.unidadesPendientes = 0,
this.anotaciones,
});
Articulo? articulo;
int unidades;
int unidadesPendientes;
List<String>? anotaciones = [];
//UI VARIALBLES
bool isExpanded = false;
LineaComanda copyWith(
{Articulo? articulo,
int unidades = 0,
int unidadesPendientes = 0,
List<String>? anotaciones = const []}) {
return LineaComanda(
articulo: articulo ?? this.articulo,
unidades: unidades,
unidadesPendientes: unidadesPendientes,
anotaciones: anotaciones ?? this.anotaciones,
);
}
#override
List<Object?> get props => [articulo, unidades, unidadesPendientes, anotaciones];
}
BLOC:
part of 'Comanda_bloc.dart';
abstract class ComandaState extends Equatable {
const ComandaState();
}
class ComandaInitial extends ComandaState {
#override
List<Object> get props => [];
}
class ComandaLoading extends ComandaState {
#override
List<Object> get props => [];
}
class ComandaLoaded extends ComandaState {
final Comanda comanda;
const ComandaLoaded({required this.comanda});
#override
List<Object> get props => [comanda];
}
class ComandaError extends ComandaState {
final String error;
const ComandaError(this.error);
#override
List<Object?> get props => [error];
}
part of 'Comanda_bloc.dart';
abstract class ComandaEvent extends Equatable {
const ComandaEvent();
}
class StartComanda extends ComandaEvent {
#override
List<Object?> get props => [];
}
class AddLineaComanda extends ComandaEvent {
final LineaComanda lineaComanda;
const AddLineaComanda(this.lineaComanda);
#override
List<Object> get props => [lineaComanda];
}
class RemoveLineaComanda extends ComandaEvent {
final LineaComanda lineaComanda;
RemoveLineaComanda(this.lineaComanda);
#override
List<Object> get props => [lineaComanda];
}
class AddUnidades extends ComandaEvent {
final LineaComanda lineaComanda;
AddUnidades(this.lineaComanda);
#override
List<Object> get props => [lineaComanda];
}
class RemoveUnidades extends ComandaEvent {
final LineaComanda lineaComanda;
RemoveUnidades(this.lineaComanda);
#override
List<Object> get props => [lineaComanda];
}
class RemoveAllLineaComanda extends ComandaEvent {
final LineaComanda lineaComanda;
RemoveAllLineaComanda(this.lineaComanda);
#override
List<Object> get props => [lineaComanda];
}
class SelectMesa extends ComandaEvent {
final Mesa mesa;
SelectMesa(this.mesa);
#override
List<Object> get props => [mesa];
}
class LoadComanda extends ComandaEvent {
final List<LineaComanda> lineasComanda;
LoadComanda(this.lineasComanda);
#override
List<Object?> get props => [lineasComanda];
}
part 'Comanda_event.dart';
part 'Comanda_state.dart';
class ComandaBloc extends Bloc<ComandaEvent, ComandaState> {
final Comanda_Repository repository;
ComandaBloc(this.repository) : super(ComandaInitial()) {
//EVENTOS
on<LoadComanda>((event, emit) async {
emit(ComandaLoading());
try {
// final comanda = await repository.getone(1);
emit(ComandaLoaded(comanda: Comanda()));
} catch (error) {
emit(ComandaError(error.toString()));
}
});
on<StartComanda>((event, emit) async {
emit(ComandaLoading());
try {
// final comanda = await repository.getone(1);
emit(ComandaLoaded(comanda: Comanda()));
} catch (error) {
emit(ComandaError(error.toString()));
}
});
on<AddLineaComanda>((event, emit) async {
final state = this.state;
if (state is ComandaLoaded) {
try {
emit(
ComandaLoaded(
comanda: state.comanda.copyWith(
lineasComanda: List.from(state.comanda.lineasComanda)
..add(event.lineaComanda))),
);
} catch (_) {}
}
});
on<RemoveLineaComanda>((event, emit) async {
final state = this.state;
if (state is ComandaLoaded) {
try {
emit(
ComandaLoaded(
comanda: state.comanda.copyWith(
lineasComanda: List.from(state.comanda.lineasComanda)
..remove(event.lineaComanda))),
);
} catch (_) {}
}
});
on<AddUnidades>((event, emit) async {
emit(ComandaLoading());
final state = this.state;
if (state is ComandaLoaded) {
try {
if (state.comanda.lineasComanda.contains(event.lineaComanda)) {
List<LineaComanda> lineasComanda = state.comanda.lineasComanda;
lineasComanda.firstWhere((item) => item == event.lineaComanda).unidades += 1;
emit(
ComandaLoaded(comanda: state.comanda.copyWith(lineasComanda: lineasComanda)),
);
}
} catch (_) {}
}
});
on<RemoveUnidades>((event, emit) async {
final state = this.state;
if (state is ComandaLoaded) {
try {
if (state.comanda.lineasComanda.contains(event.lineaComanda)) {
List<LineaComanda> lineasComanda = state.comanda.lineasComanda;
lineasComanda.firstWhere((item) => item == event.lineaComanda).unidades -= 1;
emit(
ComandaLoaded(comanda: state.comanda.copyWith(lineasComanda: lineasComanda)),
);
}
// LineaComanda lineaComanda =
// state.comanda.lineasComanda.firstWhere((element) => element == event.lineaComanda);
} catch (_) {}
}
});
}
}
UI:
class Comanda extends StatefulWidget {
const Comanda({Key? key}) : super(key: key);
#override
State<Comanda> createState() => _ComandaState();
}
class _ComandaState extends State<Comanda> {
#override
void initState() {
super.initState();
}
#override
Widget build(BuildContext context) {
return Expanded(
child: Padding(
padding: const EdgeInsets.all(10.0),
child: BlocBuilder<ComandaBloc, ComandaState>(
builder: (context, state) {
if (state is ComandaLoading) {
return const Center(
child: CircularProgressIndicator(),
);
}
if (state is ComandaLoaded) {
return state.comanda.lineasComanda.isEmpty
? const EmptyList(
text: 'No existen artículos en la comanda',
)
: ListView(children: [
ExpansionPanelList(
elevation: 3,
// dividerColor: Colors.blue,
expandedHeaderPadding: const EdgeInsets.all(0),
expansionCallback: (index, isExpanded) {
setState(() {
state.comanda.lineasComanda[index].isExpanded = !isExpanded;
});
},
animationDuration: const Duration(milliseconds: 200),
children: state.comanda.lineasComanda
.map(
(item) => LineaComandaCard(item),
)
.toList(),
// Card_lineaComanda(flatButtonStyle),
),
]);
}
if (state is ComandaError) {
return Center(
child: Text(state.error.toString()),
);
}
return Container();
},
),
),
);
}
ExpansionPanel LineaComandaCard(LineaComanda lineaComanda) {
//Conusltamos el color de la famila
Color color = Colors.grey;
;
// do stuff here based on BlocA's state
return ExpansionPanel(
canTapOnHeader: true,
// backgroundColor: item['isExpanded'] == true ? Colors.cyan[100] : Colors.white,
headerBuilder: (context, isExpanded) {
return Container(
decoration: BoxDecoration(
// borderRadius: BorderRadius.circular(3),
border: Border(left: BorderSide(color: color, width: 3))),
padding: const EdgeInsets.symmetric(vertical: 5, horizontal: 10),
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
mainAxisAlignment: MainAxisAlignment.center,
children: [
Text(lineaComanda.articulo!.nombre, style: AppTheme.tituloCard),
Row(
// mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: [
AnimatedSwitcher(
duration: const Duration(milliseconds: 500),
child: Row(
children: [
Text(
'Unidades:',
style: AppTheme.textTagsCard,
),
BlocBuilder<ComandaBloc, ComandaState>(
builder: (context, state) {
if (state is ComandaLoaded)
return Text(
' ${state.comanda.lineasComanda.firstWhere((item) => item == lineaComanda).unidades}',
style: AppTheme.textTagsCard,
key: ValueKey(lineaComanda.unidades),
);
return Container();
},
),
],
),
transitionBuilder: (Widget child, Animation<double> animation) {
return ScaleTransition(scale: animation, child: child);
},
),
const Text(" | "),
Padding(
padding: const EdgeInsets.symmetric(horizontal: 5.0, vertical: 2),
child: Text(
'Pendientes: ${lineaComanda.unidades}',
style: AppTheme.textTagsCard,
),
),
(lineaComanda.unidadesPendientes >= lineaComanda.unidades)
? const Icon(
Icons.check_box_rounded,
color: AppTheme.greenOscuro,
size: 15,
)
: (lineaComanda.unidades == lineaComanda.unidadesPendientes)
? const Icon(Icons.disabled_by_default_rounded,
color: AppTheme.redOscuro, size: 15)
: const Icon(Icons.indeterminate_check_box_rounded,
color: AppTheme.yellow, size: 15),
const SizedBox(
width: 10,
),
// const SizedBox(
// width: 20,
// ),
],
),
],
));
},
body: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
const Padding(
padding: EdgeInsets.only(top: 8, bottom: 3, left: 10, right: 10),
child: Text(
"Anotaciones:",
style: TextStyle(fontSize: 12),
),
),
const Padding(
padding: EdgeInsets.only(top: 0, left: 30),
child: Text(
"· Sin pepinillo.",
style: TextStyle(fontSize: 12),
),
),
const Divider(),
Padding(
padding: const EdgeInsets.all(4.0),
child: Row(
mainAxisAlignment: MainAxisAlignment.spaceAround,
// alignment: MainAxisAlignment.spaceAround,
// buttonHeight: 12.0,
// buttonMinWidth: 10.0,
children: <Widget>[
TextButton(
// style: flatButtonStyle,
onPressed: () {
context.read<ComandaBloc>()..add(AddUnidades(lineaComanda));
// setState(() {
// lineaComanda.unidades += 1;
// });
},
child: Column(
children: const <Widget>[
Icon(
Icons.add,
color: AppTheme.grismedio,
),
Padding(
padding: EdgeInsets.symmetric(vertical: 2.0),
),
Text(
'Más',
style: TextStyle(fontSize: 9, color: AppTheme.secondaryTextColor),
),
],
),
),
TextButton(
// style: flatButtonStyle,
onPressed: () {
context.read<ComandaBloc>()..add(RemoveUnidades(lineaComanda));
// setState(() {
// // lineaComanda.unidades -= 1;
// });
},
child: Column(
children: const <Widget>[
Icon(
Icons.remove,
color: AppTheme.grismedio,
),
Padding(
padding: EdgeInsets.symmetric(vertical: 2.0),
),
Text(
'Menos',
style: TextStyle(fontSize: 9, color: AppTheme.secondaryTextColor),
),
],
),
),
TextButton(
// style: flatButtonStyle,
onPressed: () {},
child: Column(
children: const <Widget>[
Icon(
Icons.edit_note_outlined,
color: AppTheme.grismedio,
),
Padding(
padding: EdgeInsets.symmetric(vertical: 2.0),
),
Text(
'Anotaciones',
style: TextStyle(fontSize: 9, color: AppTheme.secondaryTextColor),
),
],
),
),
TextButton(
// style: flatButtonStyle,
onPressed: () {
context.read<ComandaBloc>()..add(RemoveLineaComanda(lineaComanda));
},
child: Column(
children: const <Widget>[
Icon(
Icons.delete_outline_outlined,
color: AppTheme.grismedio,
),
Padding(
padding: EdgeInsets.symmetric(vertical: 2.0),
),
Text(
'Eliminar',
style: TextStyle(fontSize: 9, color: AppTheme.secondaryTextColor),
),
],
),
),
TextButton(
// style: flatButtonStyle,
onPressed: () {},
child: Column(
children: const <Widget>[
Icon(
Icons.local_offer_outlined,
color: AppTheme.grismedio,
),
Padding(
padding: EdgeInsets.symmetric(vertical: 2.0),
),
Text(
'Invitar',
style: TextStyle(fontSize: 9, color: AppTheme.secondaryTextColor),
),
],
),
)
],
),
),
],
),
isExpanded: lineaComanda.isExpanded,
);
}
}
The add and remove command line events do work correctly and the UI is redrawed, but the add and remove unit events do not redraw the UI, although the event is executed.
I show console logs:
ADD/REMOVE Command Line:
I/flutter (13260): ComandaBloc AddLineaComanda(LineaComanda(Article(389, , CHIVITO BOCADILLO, 1, 0.0, 0.0, 0.0, 0.0, 10, YesNO.No, YesNO.No, YesNO.No, -1, 0, - 1, YesNO.No, YesNO.No), 2, 0, [])) I/flutter (13260): Transition { currentState: CommandLoaded(Command([], null, null)), event: AddCommandLine(CommandLine(Item (389, , CHIVITO SNACK, 1, 0.0, 0.0, 0.0, 0.0, 10, YesNO.No, YesNO.No, YesNO.No, -1, 0, -1, YesNO.No, YesNO.No), 2, 0, [])), nextState: CommandLoaded(Command([CommandLine(Article(389, , CHIVITO BOCADILLO, 1, 0.0, 0.0, 0.0, 0.0, 10, SioNO.No, SioNO.No, SioNO.No, -1 , 0, -1, YesNO.No, YesNO.No), 2, 0, [])], null, null)) }
ADD / REMOVE UNITS:
I/flutter (13260): CommandBloc AddUnits(CommandLine(Article(389, , CHIVITO BOCADILLO, 1, 0.0, 0.0, 0.0, 0.0, 10, YesNO.No, YesNO.No, YesNO.No, -1, 0, - 1, YesNO.No, YesNO.No), 4, 0, []))
Any solution to redraw the UI correctly???
Thank you very much.

When you handle RemoveLineaComanda and AddLineaComanda events, you create a new instance of a List with List.from().
In the case of the RemoveUnidades and AddUnidades you emit a state with the same instance of List from the previous state. If you create a new List in for these events as well it should work properly.
This implementation works for me:
on<AddUnidades>((event, emit) async {
final state = this.state;
if (state is ComandaLoaded) {
try {
if (state.comanda.lineasComanda.contains(event.lineaComanda)) {
List<LineaComanda> lineasComanda = state.comanda.lineasComanda;
// find the order of the event
int index = lineasComanda.indexOf(event.lineaComanda);
// make a new list from the state.
List<LineaComanda> nuevaLista = List.of(lineasComanda);
// edit the order by creating a new LineaComanda to replace the edited one.
nuevaLista[index] = lineasComanda[index]
.copyWith(unidades: lineasComanda[index].unidades + 1);
// emit a new state with the new list of orders.
emit(
ComandaLoaded(
comanda: state.comanda.copyWith(lineasComanda: nuevaLista)),
);
}
} catch (_) {}
}
});
Similarly RemoveUnidades should become:
on<RemoveUnidades>((event, emit) async {
final state = this.state;
if (state is ComandaLoaded) {
try {
if (state.comanda.lineasComanda.contains(event.lineaComanda)) {
List<LineaComanda> lineasComanda = state.comanda.lineasComanda;
int index = lineasComanda.indexOf(event.lineaComanda);
// make a new list from the state.
List<LineaComanda> nuevaLista = List.of(lineasComanda);
// edit the order by creating a new LineaComanda to replace the edited one.
nuevaLista[index] = lineasComanda[index]
.copyWith(unidades: lineasComanda[index].unidades - 1);
emit(
ComandaLoaded(
comanda: state.comanda.copyWith(lineasComanda: nuevaLista)),
);
}
} catch (_) {}
}
});

If the user interface isn't getting redrawn it's because the emmited state is the same as before.
With "Same as before" i mean that the == operator returns true.
I see that you are using equatable.
I personally prefer to override the == so that I have more control when deciding wether or not the two objects are the same.
Anyway, in your case, carefully check that you have correctly overridden the propery props of all the types you are using (IE, I can't see the implementation for the class Articulo).
You could also write some unit tests to make sure that all the types you create correctly implement Equatable.

Related

Pull to refresh in Flutter doesn't invoke onLoading function unless I switch page and come back

I am using a smart refresher for pagination in Flutter. It calls the below function to get data from the API on the basis of start and end date.
Whenever the smart refresher calls this function, it gives the right data the first time (with initial start and end date), but when I change the dates, it fetches new data accordingly but now when I pull it again to fetch more data, it doesn't even recognise that pull action as a valid action and nothing happens.
Unless I switch to another screen and come back, it works as expected.
If you need more info or code segments, I'll gladly provide them.
// THIS IS THE FUNCTION CALLED IN SMART REFRESHER in init() isFirst = true otherwise its false
Future<void> fetchAnalytics({required bool isFirst}) async {
if (isFirst == true) {
analyticsLoadingFirst(true);
analyticsPage.value = 1;
analyticsTotalPage.value = 0;
analyticsList.clear();
} else {
analyticsLoading(true);
}
analytics.value = await RemoteServices.getStatsAnalytics(
token: Constants.token!,
start: DateFormat('yyyy-MM-dd'). format(analyticsStartDate.value),
end: DateFormat('yyyy-MM-dd'). format(analytiscEndDate.value),
page: analyticsPage.value,
);
if (analytics.value.data!.data!.isNotEmpty) {
analyticsList.value = analyticsList.value + analytics.value.data!.data!;
analyticsPage.value += 1;
analyticsTotalPage.value = analytics.value.data!.lastPage!;
}
isFirst == true ? analyticsLoadingFirst(false) : analyticsLoading(false);
}
// THIS IS THE TAB
class AnalyticsStatsScreen extends StatefulWidget {
const AnalyticsStatsScreen({Key? key}) : super(key: key);
#override
State<AnalyticsStatsScreen> createState() => _AnalyticsStatsScreenState();
}
class _AnalyticsStatsScreenState extends State<AnalyticsStatsScreen> with TickerProviderStateMixin {
final statsController = Get.find<StatsController>();
late AnimationController animationController;
late AnimationController animationController2;
RefreshController paginateController = RefreshController();
ScrollController? _scroll;
#override
void initState() {
super.initState();
_scroll = ScrollController();
animationController = AnimationController(vsync: this);
animationController2 = AnimationController(vsync: this);
}
#override
void setState(VoidCallback fn) {
if (mounted) super.setState(fn);
}
#override
void dispose() {
animationController.dispose();
animationController2.dispose();
super.dispose();
}
#override
Widget build(BuildContext context) {
return Obx(
() => statsController.analyticsLoadingFirst.isTrue
? Center(
child: AlertWidgets.IULoading(animationController1: animationController, animationController2: animationController2),
)
: statsController.analytics.value.data == null
? LoadingError().notFoundWidget()
: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
SizedBox(height: 12.h),
/// ****** Today Calendar Button ******
GestureDetector(
onTap: () {
Get.defaultDialog(
title: "Select a range",
titlePadding: EdgeInsets.only(top: 16.h),
titleStyle: TextStyle(fontSize: 18.sp),
contentPadding: EdgeInsets.zero,
content: SizedBox(
// width: 0.5.sw,
// height: 0.65.sw,
width: 0.75.sw,
height: 0.75.sw,
child: SfDateRangePicker(
onSelectionChanged: _onChange,
onSubmit: (value) {
Get.back();
statsController.analyticsPage.value = 1;
statsController.analyticsTotalPage.value = 0;
_scrollUp();
statsController.fetchAnalytics(isFirst: true);
},
onCancel: () {
Get.back();
},
headerStyle: DateRangePickerHeaderStyle(
textStyle: ThemeStyles.NormalLight,
),
selectionMode: DateRangePickerSelectionMode.range,
maxDate: DateTime.now(),
showActionButtons: true,
headerHeight: 50.h,
rangeTextStyle: ThemeStyles.NormalLight,
selectionTextStyle: ThemeStyles.NormalLight.copyWith(color: Constants.TextWhite),
initialSelectedRange: PickerDateRange(statsController.startDate.value, statsController.endDate.value
// DateTime.now(),
// DateTime.now()
// .subtract(Duration(days: 7)),
),
),
),
);
},
child: Container(
width: Get.width,
height: 0.06.sh,
decoration: BoxDecoration(
borderRadius: BorderRadius.circular(4.r),
border: Border.all(color: Constants.TextBlack),
),
child: Row(
mainAxisAlignment: MainAxisAlignment.center,
children: [
SvgPicture.asset(
Constants.calender,
width: 22.w,
height: 22.w,
),
SizedBox(width: 12.w),
Text(
'Today',
style: ThemeStyles.NormalBold,
),
],
),
),
),
Expanded(
child: ExcludeSemantics(
child: Obx(
() => SmartRefresher(
// scrollController: _scroll,
controller: paginateController,
enablePullUp: true,
enablePullDown: false,
onLoading: () async {
print("total page loading => ${statsController.analyticsTotalPage.value}");
print("current page loading => ${statsController.analyticsPage.value}");
if (statsController.analyticsTotalPage.value >= statsController.analyticsPage.value) {
await statsController.fetchAnalytics(isFirst: false);
if (statsController.analyticsLoading.isFalse) {
paginateController.loadComplete();
} else {
paginateController.loadFailed();
}
} else {
paginateController.loadNoData();
}
},
child: ListView.builder(
controller: _scroll,
shrinkWrap: true,
padding: EdgeInsets.zero,
physics: const BouncingScrollPhysics(),
itemCount: statsController.analyticsList.length,
itemBuilder: (ctx, index) {
// int count = index + 1;
var item = statsController.analyticsList[index];
return productCard(analyticsDatum: item, index: index);
},
),
),
),
),
),
SizedBox(height: 12.h),
],
),
);
}
// ***** Paid Summary *****
Widget productCard({required AnalyticsDatum analyticsDatum, int? index}) {
// double netSales = saleDatum.amount! - (saleDatum.discount! + saleDatum.tax!) - saleDatum.refundAmount!;
return Padding(
padding: EdgeInsets.only(top: 12.h),
child: Container(
width: Get.width,
decoration: BoxDecoration(
borderRadius: BorderRadius.circular(8.r),
color: Constants.BackgroundSecondary,
),
padding: EdgeInsets.symmetric(vertical: 12.h, horizontal: 8.w),
child: Column(
mainAxisAlignment: MainAxisAlignment.start,
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Row(
children: [
SizedBox(
width: Get.width > 600 ? 10.w : 16.w,
height: Get.width > 600 ? 15.h : 16.h,
child: SvgPicture.asset(
Constants.analytic,
// color: Colors.black,
// size: 30.w,
),
),
SizedBox(width: 8.w),
Flexible(
child: Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: [
SizedBox(
width: Get.width * 0.55,
child: Text(
analyticsDatum.productTitle.toString(),
style: ThemeStyles.NormalLightW600,
maxLines: 2,
overflow: TextOverflow.ellipsis,
),
),
Text(
"${analyticsDatum.counts} Views",
style: ThemeStyles.NormalLightW600,
maxLines: 2,
overflow: TextOverflow.ellipsis,
),
],
),
),
],
),
],
),
),
);
}
void _onChange(DateRangePickerSelectionChangedArgs args) {
print("DateRangePickerSelectionChangedArgs = ${args.value.startDate}");
if (args.value is PickerDateRange) {
statsController.analyticsStartDate.value = args.value.startDate;
statsController.analytiscEndDate.value = args.value.endDate ?? args.value.startDate;
}
}
void _scrollUp() {
if (_scroll!.hasClients){
_scroll!.jumpTo(_scroll!.position.minScrollExtent);
}
}
}
// THIS IS THE CONTROLLER BEING FOUND AT THE START OF THE TAB
class StatsController extends GetxController {
var startDate = DateTime.now().subtract(Duration(days: 6)).obs;
var endDate = DateTime.now().obs;
var analyticsStartDate = DateTime.now().subtract(Duration(days: 6)).obs;
var analytiscEndDate = DateTime.now().obs;
var analyticsLoadingFirst = false.obs;
var analyticsLoading = false.obs;
var analytics = AnalyticsModel().obs;
var analyticsList = <AnalyticsDatum>[].obs;
// ******** Analytics *******
Future<void> fetchAnalytics({required bool isFirst}) async {
if (isFirst == true) {
analyticsLoadingFirst(true);
analyticsPage.value = 1;
analyticsTotalPage.value = 0;
analyticsList.clear();
} else {
analyticsLoading(true);
}
analytics.value = await RemoteServices.getStatsAnalytics(
token: Constants.token!,
start: DateFormat('yyyy-MM-dd').format(analyticsStartDate.value),
end: DateFormat('yyyy-MM-dd').format(analytiscEndDate.value),
page: analyticsPage.value,
);
if (analytics.value.data!.data!.isNotEmpty) {
analyticsList.value = analyticsList.value + analytics.value.data!.data!;
analyticsPage.value += 1;
analyticsTotalPage.value = analytics.value.data!.lastPage!;
}
isFirst == true ? analyticsLoadingFirst(false) : analyticsLoading(false);
}
#override
void onInit() {
fetchAnalytics(isFirst: true);
super.onInit();
}
#override
void onClose() {
startDate.value = DateTime.now().subtract(Duration(days: 7));
endDate.value = DateTime.now();
super.onClose();
}
}
I've tried to look it up in the docs of pull to refresh but found nothing
You may have loaded all the data and called the refreshController.loadNoData() function.
Once you call the refreshController.loadNoData() then onLoading function will not be invoked by the time you call the refreshController.resetNoData().
In short, to get rid of this issue just call the refreshController.resetNoData() in onRefresh function.

I tried to fetch data from firestore to chip widgets but then show "LateInitializationError". How I solve it?

I tried to fetch data from firestore to chip widgets but then show "LateInitializationError". And also chips should be can multi selection(select many chips). And also how to align 4 chips in a row like this example?I my code I think chips are show like ListView.
error..
I mean like this..
my code..
class uitry extends StatefulWidget {
const uitry({Key? key}) : super(key: key);
#override
State<uitry> createState() => _uitryState();
}
#override
Future<List<Words12>> fetchRecords() async {
var records = await FirebaseFirestore.instance.collection('12words').get();
return mapRecords(records);
}
List<Words12> mapRecords(QuerySnapshot<Map<String, dynamic>> records) {
var _list = records.docs
.map(
(words12) => Words12(
id: words12.id,
wordName: words12['wordName'],
categoryName: words12['categoryName'],
),
)
.toList();
return _list;
}
late int defaultChoiceIndex;
#override
void initState() {
initState();
defaultChoiceIndex = 0;
}
child: SizedBox(
width: width * 0.94,
height: height * 0.30,
child: FutureBuilder<List<Words12>>(
future: fetchRecords(),
builder: (context, snapshot) {
if (snapshot.hasError) {
return Text('Error: ${snapshot.error}');
} else {
List<Words12> data = snapshot.data ?? [];
return ListView.builder(
itemCount: data.length,
itemBuilder: (context, index) {
return (ChoiceChip(
label: Text(data[index].wordName),
selected: defaultChoiceIndex == index,
selectedColor: Colors.deepPurple,
onSelected: (value) {
setState(() {
defaultChoiceIndex =
value ? index : defaultChoiceIndex;
});
},
// backgroundColor: color,
elevation: 1,
padding: const EdgeInsets.symmetric(
horizontal: 5.0),
));
},
);
}
}),
),
#override
void initState() {
initState();
defaultChoiceIndex = 0;
}
Should be:
#override
void initState() {
super.initState();
defaultChoiceIndex = 0;
}
I believe it will initialize your defaultChoiceIndex then.
For the alignment of chips: Wrap your ChoiceChip in the ListView.builder in a Row(), with a mainAxisAlignment of your choosing:
return Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: [
ChoiceChip(etc.),
],
),
I tried it .Now it working..
code
SizedBox(
width: width * 0.94,
height: height * 0.30,
child: Column(
children: <Widget>[
const SizedBox(height: 16),
Wrap(
children: hobbyList.map(
(hobby) {
bool isSelected = false;
if (selectedHobby!.contains(hobby)) {
isSelected = true;
}
return GestureDetector(
onTap: () {
if (!selectedHobby!.contains(hobby)) {
if (selectedHobby!.length < 50) {
selectedHobby!.add(hobby);
setState(() {});
print(selectedHobby);
}
} else {
selectedHobby!.removeWhere(
(element) => element == hobby);
setState(() {});
print(selectedHobby);
}
},
child: Container(
margin: const EdgeInsets.symmetric(
horizontal: 5, vertical: 4),
child: Container(
padding: const EdgeInsets.symmetric(
vertical: 5, horizontal: 12),
decoration: BoxDecoration(
color: isSelected
? HexColor('#F5F185')
: HexColor('#D9D9D9'),
borderRadius:
BorderRadius.circular(18),
border: Border.all(
color: isSelected
? HexColor('#F5F185')
: HexColor('#D9D9D9'),
width: 2)),
child: Text(
hobby,
style: TextStyle(
color: isSelected
? Colors.black
: Colors.black,
fontSize: 14,
fontWeight: FontWeight.w600),
),
),
),
);
},
).toList(),
),
],
),
),
class _uitryState extends State<uitry> {
List<String> hobbyList = [
'Shopping',
'Brunch',
'Music',
'Road Trips',
'Tea',
'Trivia',
'Comedy',
'Clubbing',
'Drinking',
'Wine',
];
List<String>? selectedHobby = [];

Add new items in list without rerendering old items

I 'am writing a message application whenever I insert a new message at the 0th index in the list all the messages within the offset are rerendered even though their properties have not been changed. How can I stop this?
Below in the code, you can see The ChatBook class which renders MessageList which is a a ScrollablePositionedListView.
I opted this in order to reach tagged messages instantly. As soon as I press the send button which is in InputBar The onPressSend Method is called which adds new message in the _messages array also I have provided _messages array inside InheritedWidget so that i can inherit it within the child since it required at multiple places. Whenever i add a new message simple a textmessage it re renders all the previous messages even though there properties have not been changed. Below are the required code snippets.
ChatBook class
/// This is the entry point of the [ChatBook] package.
class ChatBook extends StatefulWidget {
const ChatBook({
Key? key,
required this.author,
required this.onSendMessage,
this.theme = const DefaultChatTheme(),
this.giphyApiKey,
}) : super(key: key);
/// List of messages from which messagefor [Ayesavi ChatBook] will be retrieved by default this field is provided [DefaultChatTheme]
/// Theme assigned to ChatBook for info see [ChatTheme]
final ChatTheme theme;
/// callback for onPress event on sendMessageButton in inputbar [InputBar]
final void Function(Message currentMessage) onSendMessage;
/// GiphyApiKey required to retrieve giphy from servers.
final String? giphyApiKey;
final User author;
#override
State<ChatBook> createState() => _ChatBookState();
}
class _ChatBookState extends State<ChatBook> {
final List<Message> _messages = [];
/// Initialising ItemScrollController
/// It is needed for jumpTo and scrollTo methods to reach any particular item
final ItemScrollController itemScrollController = ItemScrollController();
/// Initialising ItemPositionListner
/// Listens for the position of any item.
final ItemPositionsListener itemPositionsListener =
ItemPositionsListener.create();
final TagMessageHelper _tagMessageHelper = TagMessageHelper();
/// It is for holding the selectedGif after onPressedEvent on Gif
/// GiphyCient is later initilising the [GiphyClient] in the [initState] method.
late GiphyClient client;
/// System used to generate random id for the user at the runtime by default initialised with aan empty String
String randomId = "";
/// For holding giphy Api Key
//! Store API keys in the env mode separate it is advised so to keep your API keys private to you only
#override
void initState() {
super.initState();
if (widget.giphyApiKey != null) {
client = GiphyClient(apiKey: widget.giphyApiKey!, randomId: '');
WidgetsBinding.instance.addPostFrameCallback((_) {
client.getRandomId().then((value) {
setState(() {
randomId = value;
});
});
});
}
}
void onPressSend(
Message sendingMessage,
) {
setState(() {
_messages.insert(0, sendingMessage);
});
widget.onSendMessage.call(sendingMessage.copyWith(
self: false,
));
}
#override
Widget build(BuildContext context) {
return GiphyGetWrapper(
/// GiphyGetWrapper is used to get giphys where ever called in the application.
giphy_api_key: widget.giphyApiKey!,
builder: (stream, giphyGetWrapper) {
stream.listen((gif) {
// widget.onGiphyPressed?.call(gif);
onPressSend(GifMessage(
author: const User(id: ""),
id: '',
gif: gif,
createdAt: DateTime.now().millisecondsSinceEpoch,
self: true,
status: Status.seen));
});
return InheritedProperties(
tagHelper: _tagMessageHelper,
theme: widget.theme,
giphyGetWrapper: giphyGetWrapper,
author: widget.author,
//* Needed for inheriting acess of messages to all its child widgets.
child: InheritedMessagesWidget(
messages: _messages,
child: Column(
children: <Widget>[
Expanded(
child: Padding(
padding: const EdgeInsets.symmetric(
horizontal: 18.00,
),
child: MessageList(
controller: itemScrollController,
positionsListener: itemPositionsListener,
),
),
),
const SizedBox(
height: 10,
),
// RepaintBoundary(
// child:
ValueListenableBuilder(
valueListenable: _tagMessageHelper.tagNotifier,
builder: (_, value, __) {
if (value == null) return const SizedBox();
return TaggedMessageIndicator(
message: value as Message);
}),
// ),
ConstrainedBox(
constraints:
const BoxConstraints(maxHeight: 150, minHeight: 60),
child: InputBar(
giphyGetWrapper: giphyGetWrapper,
onSendMessage: onPressSend,
))
],
),
),
);
});
}
}
MessageList
class MessageList extends StatefulWidget {
const MessageList(
{Key? key, required this.controller, required this.positionsListener})
: super(key: key);
final ItemScrollController controller;
final ItemPositionsListener positionsListener;
#override
State<MessageList> createState() => _MessageListState();
}
class _MessageListState extends State<MessageList> {
#override
void dispose() {
super.dispose();
}
#override
Widget build(BuildContext context) {
return ScrollablePositionedList.builder(
reverse: true,
itemScrollController: widget.controller,
itemPositionsListener: widget.positionsListener,
initialScrollIndex: 0,
itemCount: InheritedMessagesWidget.of(context).messages.length,
itemBuilder: (_, index) {
return MessageBuilder(
message: InheritedMessagesWidget.of(context).messages[index],
prevMessage:
index != InheritedMessagesWidget.of(context).messages.length - 1
? InheritedMessagesWidget.of(context).messages[index + 1]
: null,
);
},
scrollDirection: Axis.vertical,
);
}
}
MessageBuilder
class MessageBuilder extends StatefulWidget {
const MessageBuilder({Key? key, required this.message, this.prevMessage})
: super(key: key);
final Message message;
final Message? prevMessage;
#override
State<MessageBuilder> createState() => _MessageBuilderState();
}
class _MessageBuilderState extends State<MessageBuilder> {
#override
Widget build(BuildContext context) {
return Column(
children: [
_dateProvider(widget.message, widget.prevMessage),
Padding(
padding: const EdgeInsets.symmetric(vertical: 2.0),
child: Container(
width: double.infinity,
alignment:
widget.message.self != null && widget.message.self == true
? Alignment.centerRight
: Alignment.centerLeft,
child: Row(
mainAxisSize: MainAxisSize.min,
children: [
LimitedBox(
maxWidth: 6.5 / 10 * (MediaQuery.of(context).size.width),
child: IntrinsicWidth(
child: RepaintBoundary(
child: Swipeable(
maxOffset: .7,
movementDuration: const Duration(milliseconds: 500),
background: const Align(
alignment: Alignment.centerLeft,
child: Icon(
Icons.share,
size: 20,
color: Colors.white,
)),
direction: SwipeDirection.startToEnd,
onSwipe: (dir) {
if (SwipeDirection.startToEnd == dir) {
logger.log("swiped");
InheritedProperties.of(context)
.tagHelper
.tagNotifier
.value = widget.message;
}
},
confirmSwipe: (direction) async {
logger.log((SwipeDirection.startToEnd == direction)
.toString());
return SwipeDirection.startToEnd == direction;
},
allowedPointerKinds: const {
PointerDeviceKind.mouse,
PointerDeviceKind.stylus,
PointerDeviceKind.touch,
PointerDeviceKind.trackpad,
PointerDeviceKind.unknown,
},
key: const ValueKey(1),
child: Bubble(
padding: const BubbleEdges.all(0),
showNip: _nipGiver(widget.message, widget.prevMessage),
nip: widget.message.self == true
? BubbleNip.rightTop
: BubbleNip.leftTop,
color: _bubbleColorGiver(widget.message),
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
_messageProviderWidget(widget.message)!,
Padding(
padding:
const EdgeInsets.symmetric(horizontal: 8.0),
child: Row(
mainAxisAlignment: MainAxisAlignment.end,
children: [
Text(
DateFormat.jm().format(
DateTime.fromMillisecondsSinceEpoch(
widget.message.createdAt!)),
style: widget.message.self == true
? InheritedProperties.of(context)
.theme
.sentTimeTextStyle
: InheritedProperties.of(context)
.theme
.receivedTimeTextStyle),
const SizedBox(
width: 5,
),
if (widget.message.self == true)
_statusProvider(widget.message),
]),
),
],
),
),
),
)),
),
],
),
),
),
],
);
}
Widget? _messageProviderWidget(Message message) {
MessageType type = message.type;
switch (type) {
case MessageType.text:
return TextMessageWidget(
message: message as TextMessage,
);
case MessageType.gif:
return GifMessageWidget(message: message as GifMessage);
case MessageType.audio:
message as AudioMessage;
return AudioMessageWidget(message: message);
default:
return null;
}
}
bool? _nipGiver(Message currentMessage, Message? prevMessage) {
if (prevMessage != null &&
sameDay(currentMessage.createdAt!, prevMessage.createdAt) == true &&
currentMessage.self == prevMessage.self) {
return false;
} else {
return true;
}
}
Widget _dateProvider(Message currentMessage, Message? prevMessage) {
if (prevMessage != null &&
sameDay(currentMessage.createdAt!, prevMessage.createdAt) == false) {
return Padding(
padding: const EdgeInsets.symmetric(vertical: 20.0),
child: Text(
DateFormat('dd MMM yyyy').format(
DateTime.fromMicrosecondsSinceEpoch(currentMessage.createdAt!)),
style: InheritedProperties.of(context).theme.dateHeaderTextStyle,
),
);
} else if (prevMessage == null) {
return Padding(
padding: const EdgeInsets.all(10.0),
child: Text(
DateFormat('dd MMM yyyy').format(
DateTime.fromMicrosecondsSinceEpoch(currentMessage.createdAt!)),
style: InheritedProperties.of(context).theme.dateHeaderTextStyle,
),
);
} else {
return const SizedBox();
}
}
Widget _statusProvider(Message message) {
switch (message.status) {
case Status.seen:
return SvgPicture.asset(
'assets/double_tick.svg',
color: Colors.blue,
height: 10,
width: 10,
);
case Status.delivered:
return SvgPicture.asset(
'assets/double_tick.svg',
color: Colors.grey,
height: 10,
width: 10,
);
case Status.error:
return const Icon(Icons.error_outline, color: Colors.red, size: 18);
case Status.sending:
return const SizedBox(
height: 10, width: 10, child: CupertinoActivityIndicator());
case Status.sent:
return SvgPicture.asset(
'asset/single_tick.svg',
color: Colors.grey,
height: 10,
width: 10,
);
default:
return const SizedBox();
}
}
Color _bubbleColorGiver(Message message) {
if (["text", 'audio', 'video'].contains(message.type.name)) {
return widget.message.self == true
? InheritedProperties.of(context).theme.sentMessageBubbleColor
: InheritedProperties.of(context).theme.receivedMessageBubbleColor;
} else if (message.type.name == 'gif') {
return Colors.transparent;
} else {
return Colors.transparent;
}
}
}
TextMessageWidget
class TextMessageWidget extends StatefulWidget {
const TextMessageWidget({
Key? key,
required this.message,
}) : super(key: key);
final TextMessage message;
#override
State<TextMessageWidget> createState() => _TextMessageWidgetState();
}
class _TextMessageWidgetState extends State<TextMessageWidget> {
#override
Widget build(BuildContext context) {
final bodyTextStyle = widget.message.self == true
? InheritedProperties.of(context).theme.sentMessageTextStyle
: InheritedProperties.of(context).theme.receivedMessageTextStyle;
final boldTextStyle = widget.message.self == true
? InheritedProperties.of(context).theme.sentMessageBoldTextStyle
: InheritedProperties.of(context).theme.receivedMessageBoldTextStyle;
final codeTextStyle = widget.message.self == true
? InheritedProperties.of(context).theme.sentMessageBodyCodeTextStyle
: InheritedProperties.of(context)
.theme
.receivedMessageBodyCodeTextStyle;
RegExp exp = RegExp(r'(?:(?:https?|ftp):)?[\w/\-?=%.]+\.[\w/\-?=%.]+');
String? _urlGiver(String text) {
String? urlText;
Iterable<RegExpMatch> matches = exp.allMatches(text);
for (var match in matches) {
urlText = (text.substring(match.start, match.end));
}
return urlText;
}
_urlGiver(widget.message.text);
return Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
if (_urlGiver(widget.message.text) != null)
AnyLinkPreview.builder(
placeholderWidget: const SizedBox(
height: 0,
width: 0,
),
errorWidget: const SizedBox(
height: 0,
width: 0,
),
link: _urlGiver(widget.message.text)!,
itemBuilder: (_, metadata, image) {
return GestureDetector(
onTap: () {
if (metadata.url != null) {
launchUrl(Uri.parse(metadata.url!));
}
},
child: Card(
clipBehavior: Clip.hardEdge,
margin: EdgeInsets.zero,
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
if (image != null) ...[
ClipRRect(
borderRadius: const BorderRadius.only(
bottomRight: Radius.circular(5),
bottomLeft: Radius.circular(5)),
child: Image(image: image),
),
],
Padding(
padding: const EdgeInsets.all(8.0),
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Text(
metadata.title!,
style: const TextStyle(
fontWeight: FontWeight.bold),
),
if (metadata.desc != null &&
metadata.desc != '' &&
metadata.desc !=
'A new Flutter project.') ...[
const SizedBox(height: 10),
Text(metadata.desc!)
]
],
),
),
],
),
),
);
}),
const SizedBox(
height: 5,
),
Padding(
padding: const EdgeInsets.symmetric(horizontal: 8.0),
child: ParsedText(
selectable: true,
text: widget.message.text,
style: bodyTextStyle,
parse: [
MatchText(
onTap: (mail) async {
final url = Uri(scheme: 'mailto', path: mail);
if (await canLaunchUrl(url)) {
await launchUrl(url);
}
},
pattern:
r'([a-zA-Z0-9+._-]+#[a-zA-Z0-9._-]+\.[a-zA-Z0-9_-]+)',
style: const TextStyle(
decoration: TextDecoration.underline,
),
),
MatchText(
onTap: (urlText) async {
final protocolIdentifierRegex = exp;
if (!urlText.startsWith(protocolIdentifierRegex)) {
urlText = 'https://$urlText';
}
{
final url = Uri.tryParse(urlText);
if (url != null && await canLaunchUrl(url)) {
await launchUrl(
url,
mode: LaunchMode.externalApplication,
);
}
}
},
pattern:
r'((http|ftp|https):\/\/)?([\w_-]+(?:(?:\.[\w_-]+)+))([\w.,#?^=%&:/~+#-]*[\w#?^=%&/~+#-])?',
style: const TextStyle(
color: Colors.blue,
decoration: TextDecoration.underline,
),
),
MatchText(
pattern: PatternStyle.bold.pattern,
style: boldTextStyle ??
bodyTextStyle.merge(PatternStyle.bold.textStyle),
renderText:
({required String str, required String pattern}) => {
'display': str.replaceAll(
PatternStyle.bold.from,
PatternStyle.bold.replace,
),
},
),
MatchText(
pattern: PatternStyle.italic.pattern,
style: bodyTextStyle.merge(PatternStyle.italic.textStyle),
renderText:
({required String str, required String pattern}) => {
'display': str.replaceAll(
PatternStyle.italic.from,
PatternStyle.italic.replace,
),
},
),
MatchText(
pattern: PatternStyle.lineThrough.pattern,
style:
bodyTextStyle.merge(PatternStyle.lineThrough.textStyle),
renderText:
({required String str, required String pattern}) => {
'display': str.replaceAll(
PatternStyle.lineThrough.from,
PatternStyle.lineThrough.replace,
),
},
),
MatchText(
pattern: PatternStyle.code.pattern,
style: codeTextStyle ??
bodyTextStyle.merge(PatternStyle.code.textStyle),
renderText:
({required String str, required String pattern}) => {
'display': str.replaceAll(
PatternStyle.code.from,
PatternStyle.code.replace,
),
},
),
MatchText(
pattern: PatternStyle.at.pattern,
style: codeTextStyle ??
bodyTextStyle.merge(PatternStyle.at.textStyle),
renderText:
({required String str, required String pattern}) => {
'display': str.replaceAll(
PatternStyle.code.from,
PatternStyle.code.replace,
),
},
),
],
)
// SelectableLinkify(text: widget.message.text,onOpen: (element){
// _launchInBrowser(Uri.parse(element.url));
// },),
),
],
);
}
}

FLUTTER : How to only rebuild the top page in stack flutter

I have an issue with rebuilding pages in the stack with Flutter
This is all my users and have added the search to the top appBar.
But it works with conditions to see if it is widgets there then get height of the widgets that is being fixed underneath the appBar...But that happens asynchronously.
So when firstLoad it works but when I call setState it then rebuilds all the pages in the stack and with that it looks like this
This is how it looks after a set state. The problem i saw is that the previous pages have an influence. I couldn't find a good viable solution to this. Will explain my architecture.
I have a page Layout that is a container wrapper for all my pages that has it's appBar styles and just sends through the children. But that is the page Layout wrapper that is being rebuild every time the a set States happen
HOW I GET MY WIDGET SIZE
HOW I IMPLEMENTED IT
It goes in the else with the other pages in the stack.. I tried putting it in the initState but it never goes inside because it is used in the other pages in the stack
I only need an implementation to rebuild the TOP page in the stack.
PAGE LAYOUT
// ignore_for_file: prefer_const_constructors
import 'package:flutter/material.dart';
import 'package:flutter/scheduler.dart';
import 'package:flutter/widgets.dart';
import 'package:flutterweb/constants.dart';
import 'package:flutterweb/controllers/channel_controller.dart';
import 'package:flutterweb/controllers/user_controller.dart';
import 'package:flutterweb/main.dart';
import 'package:flutterweb/models/channels_model.dart';
import 'package:flutterweb/utils/functions.dart';
import 'package:flutterweb/views/channels/func.dart';
import 'package:flutterweb/views/home/home.dart';
import 'package:flutterweb/views/menu/permissions/choose_assign_group.dart';
import 'package:flutterweb/widgets/builders/KNetworkFadeImage.dart';
import 'package:flutterweb/widgets/builders/kPopups.dart';
import 'package:flutterweb/widgets/drawerDara.dart';
import 'package:get/get.dart';
class CustomAppBar extends StatefulWidget {
final String title;
final List<Map<String, dynamic>>? topTabs;
final TabController? topTabController;
final List<Widget>? children;
final List<Widget>? childrenFixed;
final Function? leftActionFunction;
final Icon? leftActionIcon;
final Drawer? drawer;
final Function? logOutPressed;
final bool showOptionsMenu;
final Widget? optionMenu;
final Widget? bottomNavigationBar;
final String? backGroundImage;
final ScrollController? scrollController;
CustomAppBar({
required this.title,
this.topTabs,
this.topTabController,
this.leftActionFunction,
this.leftActionIcon,
this.children,
this.childrenFixed,
this.drawer,
this.logOutPressed,
this.showOptionsMenu = false,
this.optionMenu,
this.bottomNavigationBar,
this.backGroundImage,
this.scrollController,
});
#override
_CustomAppBarState createState() => _CustomAppBarState();
}
double app_content_height = 0;
double fixedWidgetSize = 0;
String prevTitle = "";
class _CustomAppBarState extends State<CustomAppBar>
with SingleTickerProviderStateMixin {
final GlobalKey<ScaffoldState> scaffoldkey = GlobalKey<ScaffoldState>();
#override
void initState() {
super.initState();
}
#override
void dispose() {
super.dispose();
}
_toggleAnimation() {
scaffoldkey.currentState!.openDrawer();
}
double _getAppBarSize() {
double fixedHeightInclude = fixedWidgetSize;
if (widget.topTabs != null) {
fixedHeightInclude += 100;
} else if (widget.title == "") {
fixedHeightInclude += 0;
} else {
fixedHeightInclude += 60;
}
return fixedHeightInclude;
}
#override
Widget build(BuildContext context) {
// if (widget.title != global_title) return SizedBox();
List<Widget> arr = [];
Widget arrView = SizedBox();
double statusBar = 0;
double _width = MediaQuery.of(context).size.width;
Widget? fixedChild = SizedBox();
if ((widget.childrenFixed?.length ?? 0) > 1) {
fixedChild = WidgetSize(
child: Column(children: widget.childrenFixed!),
onChange: (Size size) {
fixedWidgetSize = 0;
setState(() {
fixedWidgetSize = size.height;
});
kPrint("fixedWidgetSize ${size.height}");
},
);
} else {
fixedWidgetSize = 0;
}
// Widget? fixedChild = (widget.childrenFixed?.length ?? 0) > 1
// ? WidgetSize(
// child: Column(children: widget.childrenFixed!),
// onChange: (Size size) {
// fixedWidgetSize = 0;
// setState(() {
// fixedWidgetSize = size.height;
// });
// kPrint("fixedWidgetSize ${size.height}");
// },
// )
// : SizedBox();
app_content_height =
MediaQuery.of(context).size.height - _getAppBarSize() - statusBar;
if (widget.title != "") {
arr.add(
Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: [
Expanded(
flex: 2,
child: widget.showOptionsMenu == true
? IconButton(
icon: const Icon(Icons.menu, color: Colors.white),
onPressed: () => _toggleAnimation(),
)
: IconButton(
icon: widget.leftActionIcon ??
const Icon(Icons.arrow_back, color: Colors.white),
onPressed: () {
if (widget.leftActionFunction != null) {
widget.leftActionFunction!();
} else {
if (Navigator.canPop(context)) {
Get.back();
}
}
},
),
),
Expanded(flex: 2, child: SizedBox()),
Expanded(
flex: 10,
child: Center(
child: Text(
widget.title,
style: const TextStyle(color: Colors.white, fontSize: 24.0),
),
),
),
Expanded(
flex: 4,
child: widget.logOutPressed != null
? IconButton(
icon: const Icon(Icons.power_settings_new_outlined,
color: Colors.white),
onPressed: () {
widget.logOutPressed!();
},
)
: widget.optionMenu ?? Container(),
),
],
),
);
}
if (widget.topTabs != null) {
List<Widget> tempTopBar = [];
List<Widget> tempTopView = [];
for (var i = 0; i < widget.topTabs!.length; i++) {
String key = widget.topTabs![i].keys
.toString()
.replaceAll("(", "")
.replaceAll(")", "");
Widget value = widget.topTabs![i][key];
tempTopBar.add(Tab(text: key));
tempTopView.add(SingleChildScrollView(child: value));
}
arr.add(
Padding(
padding: const EdgeInsets.only(bottom: 8),
child: Center(
child: Container(
height: 30,
child: DefaultTabController(
length: widget.topTabs!.length,
child: TabBar(
labelPadding: widget.topTabs!.length == 2
? const EdgeInsets.symmetric(horizontal: 40.0)
: const EdgeInsets.symmetric(horizontal: 16.0),
controller: widget.topTabController,
indicatorSize: TabBarIndicatorSize.tab,
indicator: CircleTabIndicator(color: Colors.white, radius: 4),
isScrollable: true,
labelColor: Colors.white,
tabs: tempTopBar,
),
),
),
),
),
);
// arr.add(child);
arrView = Container(
width: _width,
height: app_content_height,
child: TabBarView(
controller: widget.topTabController,
children: tempTopView,
),
);
}
if (widget.children != null) {
arrView = Container(
width: _width,
height: app_content_height,
child: ListView(
// controller: widget.scrollController ?? ScrollController(),
children: widget.children!,
),
);
}
_getStatus() {
if (statusBar > 0) {
Color color = AppColors.kBlue;
return Container(
height: Get.height * 0.03,
width: Get.width,
color: color,
child: const Center(
child: Text(
"",
style: const TextStyle(color: Colors.black),
),
),
);
} else {
return const SizedBox();
}
}
return SafeArea(
child: Material(
child: Stack(
children: [
Scaffold(
resizeToAvoidBottomInset:
true, //That the keyboard shows correctly
extendBodyBehindAppBar: true,
key: scaffoldkey,
appBar: PreferredSize(
preferredSize: Size.fromHeight(
_getAppBarSize()), // here the desired height
child: Container(
decoration: kAppBarBoxDecorations,
child: Column(
children: [
Column(
children: arr,
),
fixedChild,
],
),
),
),
drawer: Drawer(
child: ListView(
children: <Widget>[
UserAccountsDrawerHeader(
accountName: InkWell(
onTap: () {
Channels element =
ChannelController.to.gSelectedChannel.value;
getChannelRoles(element);
},
child: Text(
"${ChannelController.to.gSelectedChannel.value.rolDesc} >",
),
),
accountEmail: Text(
UserController.to.gUserModel.value.email.toString()),
currentAccountPicture: GestureDetector(
child: const CircleAvatar(
backgroundImage: NetworkImage(
"https://images.pexels.com/photos/220453/pexels-photo-220453.jpeg?auto=compress&cs=tinysrgb&dpr=1&w=500"),
),
onTap: () => print("Current User")),
decoration: const BoxDecoration(
image: DecorationImage(
fit: BoxFit.fill,
image: NetworkImage(
"${URLS.keyBaseUrl}/assets/images/background/background7.jpg")),
),
),
ListTile(
title: const Text("Home"),
trailing: const Icon(Icons.home),
onTap: () =>
Get.toNamed(Home.router, preventDuplicates: false),
),
ListTile(
title: const Text("Menu Permissions"),
trailing: const Icon(Icons.home),
onTap: () => Get.toNamed(ChooseAssignGroup.router,
preventDuplicates: false),
),
const Divider(
thickness: 1.0,
),
drawerData(),
const Divider(
thickness: 1.0,
),
ListTile(
title: const Text("Close"),
trailing: const Icon(Icons.cancel),
onTap: () => Navigator.of(context).pop(),
),
ListTile(
title: const Text("Log Out"),
trailing: const Icon(Icons.logout),
onTap: () => UserController.to.logOutUser(),
),
],
),
),
body: Container(
decoration: widget.backGroundImage != null
? BoxDecoration(
color: Colors.black.withOpacity(0.9),
image: DecorationImage(
fit: BoxFit.cover,
colorFilter: ColorFilter.mode(
Colors.black.withOpacity(0.2), BlendMode.dstATop),
image: NetworkImage(widget.backGroundImage!),
),
)
: BoxDecoration(color: Colors.grey.shade400),
child: Center(
child: Container(
constraints: BoxConstraints(maxWidth: 800),
padding: EdgeInsets.only(
left: 15.0, right: 15.0, top: _getAppBarSize()),
child: MediaQuery.removePadding(
context: context,
removeTop: true,
child: arrView,
),
),
),
),
bottomNavigationBar: widget.bottomNavigationBar,
),
],
),
),
);
}
}
class CircleTabIndicator extends Decoration {
final BoxPainter _painter;
CircleTabIndicator({required Color color, required double radius})
: _painter = _CirclePainter(color, radius);
#override
BoxPainter createBoxPainter([onChanged()?]) => _painter;
}
class _CirclePainter extends BoxPainter {
final Paint _paint;
final double radius;
_CirclePainter(Color color, this.radius)
: _paint = Paint()
..color = color
..isAntiAlias = true;
#override
void paint(Canvas canvas, Offset offset, ImageConfiguration cfg) {
final Offset circleOffset =
offset + Offset(cfg.size!.width / 2, cfg.size!.height - radius);
canvas.drawCircle(circleOffset, radius, _paint);
}
}
class WidgetSize extends StatefulWidget {
final Widget child;
final Function onChange;
const WidgetSize({
required this.onChange,
required this.child,
});
#override
_WidgetSizeState createState() => _WidgetSizeState();
}
class _WidgetSizeState extends State<WidgetSize> {
#override
Widget build(BuildContext context) {
SchedulerBinding.instance.addPostFrameCallback(postFrameCallback);
return Container(
key: widgetKey,
child: widget.child,
);
}
var widgetKey = GlobalKey();
var oldSize;
void postFrameCallback(_) {
var context = widgetKey.currentContext;
if (context == null) return;
var newSize = context.size;
if (oldSize == newSize) return;
oldSize = newSize;
widget.onChange(newSize);
}
}
ALL VERIFIED USERS
import 'package:firebase_auth/firebase_auth.dart';
import 'package:flutter/cupertino.dart';
import 'package:flutter/material.dart';
import 'package:flutterweb/controllers/channel_controller.dart';
import 'package:flutterweb/controllers/user_controller.dart';
import 'package:flutterweb/main.dart';
import 'package:flutterweb/models/user_model.dart';
import 'package:flutterweb/thirdParty/googleSignin.dart';
import 'package:flutterweb/utils/functions.dart';
import 'package:flutterweb/views/menu/permissions/menu_assign.dart';
import 'package:flutterweb/widgets/builders/kPopups.dart';
import 'package:flutterweb/widgets/buttons/KIconOnlyButton.dart';
import 'package:flutterweb/widgets/builders/KNetworkFadeImage.dart';
import 'package:flutterweb/widgets/builders/customAppBar.dart';
import 'package:flutterweb/widgets/buttons/KButton.dart';
import 'package:flutterweb/constants.dart';
import 'package:flutterweb/widgets/cards/KStudentInfoCard.dart';
import 'package:flutterweb/widgets/cards/kStudentCard.dart';
import 'package:flutterweb/widgets/input/KInputBar.dart';
import 'package:flutterweb/widgets/input/KTextField.dart';
import 'package:flutterweb/widgets/static/kLabel.dart';
import 'package:flutterweb/widgets/text/kInfo.dart';
import 'package:get/get.dart';
import 'package:google_fonts/google_fonts.dart';
import 'package:infinite_scroll_pagination/infinite_scroll_pagination.dart';
class AllVerifiedUsers extends StatefulWidget {
static const String router = "/allVerifiedUsers";
AllVerifiedUsers({Key? key}) : super(key: key);
#override
_MyPageState createState() => _MyPageState();
}
// The controller for the ListView
late ScrollController _controller;
class _MyPageState extends State<AllVerifiedUsers> {
// The controller for the ListView
late ScrollController _controllerTest;
int _page = 1;
final int _limit = 20;
bool _hasNextPage = true;
bool _isFirstLoadRunning = false;
bool _isLoadMoreRunning = false;
List<UserModel> _posts = [];
String searchVal = "";
void _firstLoad() async {
setState(() {
_isFirstLoadRunning = true;
});
try {
List<UserModel> lUserMode = await ChannelController.to
.fetchUsersChannels(_limit, _page, searchVal);
setState(() {
_posts = lUserMode;
});
} catch (err) {
kPrint('Something went wrong');
}
setState(() {
_isFirstLoadRunning = false;
});
}
void _loadMore() async {
if (_isFirstLoadRunning == false &&
_isLoadMoreRunning == false &&
_controller.position.extentAfter < 300) {
setState(() {
_isLoadMoreRunning = true; // Display a progress indicator at the bottom
});
_page += 1; // Increase _page by 1
try {
List<UserModel> lUserMode = await ChannelController.to
.fetchUsersChannels(_limit, _page, searchVal);
if (lUserMode.isNotEmpty) {
setState(() {
_hasNextPage = true;
_posts.addAll(lUserMode);
});
} else {
// This means there is no more data
// and therefore, we will not send another GET request
setState(() {
_hasNextPage = false;
});
}
} catch (err) {
print('Something went wrong!');
}
setState(() {
_isLoadMoreRunning = false;
});
}
}
#override
void initState() {
super.initState();
_firstLoad();
_controller = ScrollController()..addListener(_loadMore);
_controllerTest = ScrollController()
..addListener(() => {kPrint("CustomView Scroll")});
}
#override
void dispose() {
super.dispose();
_controller.removeListener(_loadMore);
}
#override
Widget build(BuildContext context) {
global_title = "All Verified Users";
return CustomAppBar(
title: "All Verified Users",
scrollController: _controllerTest,
childrenFixed: [
kAddSpace(2),
CustomInputBar(
inverse: true,
title: "Search",
onChanged: (String value) {
if (value == "") {
_firstLoad();
return;
}
setState(() {
searchVal = value;
_posts = [];
});
_loadMore();
},
),
],
children: [
kAddSpace(2),
KLabel(
label: "Choose Verified User",
),
kAddSpace(2),
_isFirstLoadRunning
? const Center(
child: CircularProgressIndicator(),
)
: Column(
children: [
SizedBox(
height: app_content_height,
// width: Get.width,
child: ListView.builder(
shrinkWrap: true,
controller: _controller,
itemCount: _posts.length,
itemBuilder: (_, index) {
UserModel item = _posts[index];
return KStudentCard(
imgUrl: "",
onPressed: () {
ChannelController.to.gSelectedMenuUserModel.value =
item;
Get.toNamed(MenuAssign.router);
},
name: "${item.name} ${item.surname}",
);
},
),
),
// when the _loadMore function is running
if (_isLoadMoreRunning == true)
const Padding(
padding: EdgeInsets.only(top: 10, bottom: 40),
child: Center(
child: CircularProgressIndicator(),
),
),
// When nothing else to load
if (_hasNextPage == false)
Container(
padding: const EdgeInsets.only(top: 30, bottom: 40),
color: Colors.amber,
child: const Center(
child: Text('You have fetched all of the content'),
),
),
],
),
kAddSpace(2),
],
);
}
}
Thank you
As I understand it, the idea is a fixed TextArea and a scrollable List beneath. A solution that works without computing any height would be:
final items = List<String>.generate(1000, (i) => 'Item $i');
Widget build(BuildContext context) {
return Scaffold(
body: Column(children: [
//
// TEXTBOX
//
Container(
color: Colors.red,
child: Padding(
padding: const EdgeInsets.all(16),
child: TextFormField(
// controller: controller,
))),
const SizedBox(height: 16),
//
// LIST
//
Expanded(
child: ListView.builder(
shrinkWrap: true, // IMPORTANT
itemCount: items.length,
itemBuilder: (context, index) {
return Text(items[index]);
})),
]),
);
}

update sibling state (data) flutter bloc

I am trying to update data in a sibling widget which already loaded data at first. The second widget adds to the same data and fetches it at the same time showing an updated data. The new data is not reflected in the first widget. Take a shopping cart and quantity of items as an example. When I add new items or change quantities in the cart it does not reflect in the cart.
All data is remotely called in a django rest api.
If I reload the app the new data shows in the cart.
this is the widget where i want to change the data in it
class MainScreen extends StatefulWidget {
final TokenResponse token;
const MainScreen({Key key, this.token});
#override
_MainScreenState createState() => _MainScreenState();
}
class _MainScreenState extends State<MainScreen> {
BottomNavBarBloc _bottomNavBarBloc;
CartBloc cartBloc;
#override
void initState() {
_bottomNavBarBloc = BottomNavBarBloc();
super.initState();
}
#override
void dispose() {
super.dispose();
}
#override
Widget build(BuildContext context) {
cartBloc = BlocProvider.of<CartBloc>(context);
cartBloc.add(FetchOrderSummaryEvent());
return Scaffold(
backgroundColor: Colors.white,
body: AnnotatedRegion<SystemUiOverlayStyle>(
value: const SystemUiOverlayStyle(
statusBarColor: Colors.transparent,
),
sized: false,
child: StreamBuilder<NavBarItem>(
stream: _bottomNavBarBloc.itemStream,
initialData: _bottomNavBarBloc.defaultItem,
builder:
(BuildContext context, AsyncSnapshot<NavBarItem> snapshot) {
switch (snapshot.data) {
case NavBarItem.HOME:
return HomeScreen();
case NavBarItem.OFFERS:
return OffersScreen();
case NavBarItem.REWARDS:
return _testScreen();
case NavBarItem.FAVORITE:
return _testScreen();
case NavBarItem.BAG:
return CartScreen();
case NavBarItem.ACCOUNT:
return AccountMainScreen();
}
return Container();
}),
),
bottomNavigationBar: StreamBuilder(
stream: _bottomNavBarBloc.itemStream,
initialData: _bottomNavBarBloc.defaultItem,
builder: (BuildContext context, AsyncSnapshot<NavBarItem> snapshot) {
return BottomNavigationBar(
unselectedItemColor: Style.Colors.secondaryColor,
selectedItemColor: Style.Colors.primaryColor,
backgroundColor: Colors.white,
selectedFontSize: 10.0,
unselectedFontSize: 10.0,
type: BottomNavigationBarType.fixed,
currentIndex: snapshot.data.index,
onTap: _bottomNavBarBloc.pickItem,
items: [
BottomNavigationBarItem(
title: Padding(
padding: EdgeInsets.only(top: 5.0),
child: Text("Home"),
),
icon: Icon(
FontAwesomeIcons.bars,
color: Style.Colors.secondaryColor,
),
activeIcon: Icon(
FontAwesomeIcons.bars,
color: Style.Colors.primaryColor,
)),
BottomNavigationBarItem(
title: Padding(
padding: EdgeInsets.only(top: 5.0),
child: Text("Offers"),
),
icon: Icon(
FontAwesomeIcons.tag,
color: Style.Colors.secondaryColor,
),
activeIcon: Icon(
FontAwesomeIcons.tag,
color: Style.Colors.primaryColor,
)),
BottomNavigationBarItem(
title: Padding(
padding: EdgeInsets.only(top: 5.0),
child: Text("Rewards"),
),
icon: Icon(
FontAwesomeIcons.gift,
color: Style.Colors.secondaryColor,
),
activeIcon: Icon(
FontAwesomeIcons.gift,
color: Style.Colors.primaryColor,
)),
BottomNavigationBarItem(
title: Padding(
padding: EdgeInsets.only(top: 5.0),
child: Text("Favorite"),
),
icon: Icon(
FontAwesomeIcons.heart,
color: Style.Colors.secondaryColor,
),
activeIcon: Icon(
FontAwesomeIcons.heart,
color: Style.Colors.primaryColor,
)),
BottomNavigationBarItem(
title: Padding(
padding: EdgeInsets.only(top: 5.0),
child: Text("Bag"),
),
icon: Container(
width: 30,
height: 25,
child: Stack(children: <Widget>[
Icon(
FontAwesomeIcons.shoppingBag,
color: Style.Colors.secondaryColor,
),
BlocBuilder<AuthenticationBloc, AuthenticationState>(
builder: (context, state) {
if (state is AuthenticationAuthenticated) {
return BlocBuilder<CartBloc, CartState>(
builder: (context, state) {
if (state is CartLoaded) {
print("loaded ${state.order.totalItemQuantity}"); // <=== change this data
return Positioned(
right: 0,
top: 0,
child: Container(
height: 20.0,
width: 20.0,
decoration: BoxDecoration(
shape: BoxShape.circle,
color: Colors.red,
border: Border.all(
width: 1.0,
color: Colors.white,
),
),
child: Column(
mainAxisAlignment:
MainAxisAlignment.center,
crossAxisAlignment:
CrossAxisAlignment.center,
children: <Widget>[
Padding(
padding: const EdgeInsets.only(
left: 1.0,
bottom: 1.0,
),
child: Text(
state.order.totalItemQuantity !=null? state.order.totalItemQuantity.toString():"",
style: TextStyle(
color: Colors.white,
fontSize: 10.0,
fontWeight: FontWeight.bold,
),
),
),
],
),
),
);
} else {
return Container(
);
}
},
);
} else {
return Text(" ");
}
},
)
]),
),
activeIcon: Container(
width: 30,
height: 25,
child: Stack(children: <Widget>[
Icon(
FontAwesomeIcons.shoppingBag,
color: Style.Colors.primaryColor,
),
BlocBuilder<AuthenticationBloc, AuthenticationState>(
builder: (context, state) {
if (state is AuthenticationAuthenticated) {
return BlocBuilder<CartBloc, CartState>(
builder: (context, state) {
if (state is CartLoaded) {
print("loaded ${state.order.totalItemQuantity}");
return Positioned(
right: 0,
top: 0,
child: Container(
height: 20.0,
width: 20.0,
decoration: BoxDecoration(
shape: BoxShape.circle,
color: Colors.red,
border: Border.all(
width: 1.0,
color: Colors.white,
),
),
child: Column(
mainAxisAlignment:
MainAxisAlignment.center,
crossAxisAlignment:
CrossAxisAlignment.center,
children: <Widget>[
Padding(
padding: const EdgeInsets.only(
left: 1.0,
bottom: 1.0,
),
child: Text(
state.order.totalItemQuantity !=null? state.order.totalItemQuantity.toString():"",
style: TextStyle(
color: Colors.white,
fontSize: 10.0,
fontWeight: FontWeight.bold,
),
),
),
],
),
),
);
} else {
return Container(
);
}
},
);
} else {
return Text(" ");
}
},
)
]),
),),
BottomNavigationBarItem(
title: Padding(
padding: EdgeInsets.only(top: 5.0),
child: BlocBuilder<AuthenticationBloc, AuthenticationState>(
builder: (context, state) {
if (state is AuthenticationAuthenticated) {
return Text("Account");
} else {
return Text("Login");
}
},
),
),
icon: Icon(
FontAwesomeIcons.user,
color: Style.Colors.secondaryColor,
),
activeIcon: Icon(
FontAwesomeIcons.user,
color: Style.Colors.primaryColor,
)),
],
);
},
),
);
}
Widget _testScreen() {
return Center(
child: Text(
'Test Screen',
style: TextStyle(
fontWeight: FontWeight.bold,
color: Colors.red,
fontSize: 25.0,
),
),
);
}
}
i call the API in a sibling widget here i am posting the handling method that was called after pressing a button from a sibling widget:
handleAddItemFromCart(
String slug, List<ItemVariations> itemVariations, BuildContext context) {
final cartBloc = BlocProvider.of<CartBloc>(context);
final currentState = cartBloc.state;
if( currentState is CartLoaded){
print("order quantity: ${currentState.order.totalItemQuantity.toString()}");
}
print("current state " + currentState.toString());
cartBloc.add(AddItemToCartEvent(
slug: slug, variations: formatVariations(itemVariations)));
cartBloc.add(FetchOrderSummaryEvent());
}
cart_event.dart
#immutable
abstract class CartEvent extends Equatable {
CartEvent([List props = const []]):super();
}
class FetchOrderSummaryEvent extends CartEvent {
FetchOrderSummaryEvent([List props = const[]]) : super(props);
Order order;
#override
List<Object> get props => [this.order];
}
class RemoveItemFromCartEvent extends CartEvent {
String slug;
List<int> variations;
RemoveItemFromCartEvent({#required this.slug, this.variations});
#override
List<Object> get props => [];
}
class AddItemToCartEvent extends CartEvent {
String slug;
List<int> variations;
AddItemToCartEvent({#required this.slug, #required this.variations});
#override
List<Object> get props => [];
}
class RemoveItemEvent extends CartEvent {
#override
List<Object> get props => [];
}
class CheckoutEvent extends CartEvent {
String stripeToken;
int selectedAddress;
CheckoutEvent({#required this.stripeToken, #required this.selectedAddress});
#override
List<Object> get props => [];
}
cart_state.dart
#immutable
abstract class CartState extends Equatable {
CartState([List props = const[]]):super();
#override
List<Object> get props => [];
}
class CartInitial extends CartState {
#override
// TODO: implement props
List<Object> get props => [];
}
class CartLoading extends CartState {
#override
// TODO: implement props
List<Object> get props => [];
}
class CartLoaded extends CartState {
Order order;
CartLoaded({#required this.order});
CartLoaded copyWith({Order order}) {
return CartLoaded(order: order ?? this.order);
}
#override
// TODO: implement props
List<Object> get props => [order];
}
class CartFailure extends CartState {
String message;
CartFailure({#required this.message});
#override
// TODO: implement props
List<Object> get props => [message];
}
cart_bloc.dart:
class CartBloc extends Bloc<CartEvent, CartState> {
MenuItemsRepository menuItemsRepository;
CartBloc({#required this.menuItemsRepository});
#override
// TODO: implement initialState
CartState get initialState => CartInitial();
#override
Stream<CartState> mapEventToState(CartEvent event) async* {
final currentState = state;
if (event is FetchOrderSummaryEvent) {
yield CartLoading();
try {
Order order = await menuItemsRepository.getOrder();
yield CartLoaded(order: order);
} catch (err) {
yield CartFailure(message: err.message);
}
}
if (event is AddItemToCartEvent) {
//yield CartLoading();
try {
await menuItemsRepository.addItemToCart(event.slug, event.variations);
} catch (err) {
yield CartFailure(message: err.toString());
}
}
if (event is RemoveItemFromCartEvent) {
yield CartLoading();
try {
await menuItemsRepository.removeItemFromCart(
event.slug, event.variations);
} catch (err) {
yield CartFailure(message: err.toString());
}
}
if (event is CheckoutEvent) {
yield CartLoading();
try {
print("token: ${event.stripeToken}");
await menuItemsRepository.makePayment(
event.stripeToken, event.selectedAddress);
} catch (err) {
yield CartFailure(message: err.toString());
}
}
}
}
repository:
Future<bool> addItemToCart(String slug, List<int> variations) async {
final token = "Token " + await storage.read(key: utils.TOKEN);
var params = {
"slug": slug,
"variations": variations,
};
try {
Response response = await _dio.post(getAddToCartURL,
data: jsonEncode(params),
options: Options(headers: {
HttpHeaders.authorizationHeader: token,
HttpHeaders.contentTypeHeader: "application/json",
}));
return response.statusCode == 200;
} catch (error, stackTrace) {
print("Exception occurred: $error stackTrace: $stackTrace");
return false;
}
}
Future<Order> getOrder() async {
final token = "Token " + await storage.read(key: utils.TOKEN);
var params = {
"api_key": apiKey,
};
try {
Response response = await _dio.get(getOrderSummaryURL,
queryParameters: params,
options: Options(headers: {
HttpHeaders.authorizationHeader: token,
HttpHeaders.contentTypeHeader: "application/json",
}));
print(response.data);
return Order.fromJson(response.data);
} catch (error, stackTrace) {
print("Exception occurred: $error stackTrace: $stackTrace");
return Order.withError(error.toString());
}
}
UPDATE:
i figured it out!
the bloc CartBloc was instantiating 2 times! one at the beginning of the app creation or start in the main.dart file and then another one in a child widget which was the order_summary file. This was was causing to not be able to force the parent widget fetch the data from api. so all the updates was done in the child level not the parent.
I hope this would help anyone who might face the same issue. Make sure you instantiate the bloc once only so you can update or receive the updates from the same bloc.