I am developing a simple counter app using Flutter, with Cubit for state management. My app features 6 buttons, each designed to add 1, 2, or 3 points to two players (Player A and Player B). However, I've encountered a strange issue: when I attempt to add 1 point to Player A's score using its dedicated button, the app sometimes adds an incorrect amount (2, 4, or even 10 points) instead of the expected 1 point. Interestingly, I'm using the same component for Player B, and it works perfectly without any issues.A counter App view code: https://github.com/As-tra/Counter
I try to modify the logic but it seems correct
I have made few changes and added relevant comments as well to help you understand.
Remember, Bloc/cubit means events will be added to Bloc or method of cubit will be called and new state will correct state values will be emitted.
import 'package:flutter/material.dart';
import 'package:flutter_bloc/flutter_bloc.dart';
import 'package:meta/meta.dart';
void main() {
WidgetsFlutterBinding.ensureInitialized();
runApp(const MyApp());
}
class MyApp extends StatelessWidget {
const MyApp({super.key});
@override
Widget build(BuildContext context) {
return BlocProvider(
create: (context) => CounterCubit(),
child: const MaterialApp(
debugShowCheckedModeBanner: false,
home: HomePage(),
),
);
}
}
class HomePage extends StatelessWidget {
const HomePage({super.key});
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
backgroundColor: Colors.orange,
title: const Text(
'Points Counter',
style: TextStyle(color: Colors.white),
),
),
body: BlocBuilder<CounterCubit, CounterIncrementState>(
builder: (context, state) {
return Column(
mainAxisAlignment: MainAxisAlignment.spaceEvenly,
children: [
Row(
mainAxisAlignment: MainAxisAlignment.spaceEvenly,
children: [
SizedBox(
height: 500,
child: Column(
mainAxisAlignment: MainAxisAlignment.spaceEvenly,
children: [
const Text(
'Team A',
style: TextStyle(fontSize: 32),
),
Text(
// use state variable to get current score value
'${state.teamA}',
style: const TextStyle(fontSize: 150),
),
const CustomButton(points: 1, team: 'A'),
const CustomButton(points: 2, team: 'A'),
const CustomButton(points: 3, team: 'A'),
],
),
),
const SizedBox(
height: 500,
child: VerticalDivider(
indent: 50,
endIndent: 50,
),
),
SizedBox(
height: 500,
child: Column(
mainAxisAlignment: MainAxisAlignment.spaceEvenly,
children: [
const Text(
'Team B',
style: TextStyle(fontSize: 32),
),
Text(
'${state.teamB}',
style: const TextStyle(fontSize: 150),
),
const CustomButton(points: 1, team: 'B'),
const CustomButton(points: 2, team: 'B'),
const CustomButton(points: 3, team: 'B'),
],
),
),
],
),
ElevatedButton(
onPressed: () {
context.read<CounterCubit>().reset();
},
style: ElevatedButton.styleFrom(
backgroundColor: Colors.orange,
foregroundColor: Colors.white,
shape: RoundedRectangleBorder(
borderRadius: BorderRadius.circular(8),
),
minimumSize: const Size(150, 50),
),
child: const Text(
'Reset',
style: TextStyle(
color: Colors.black,
fontSize: 18,
),
),
),
],
);
},
),
);
}
}
class CustomButton extends StatelessWidget {
final int points;
final String team;
const CustomButton({
super.key,
required this.points,
required this.team,
});
@override
Widget build(BuildContext context) {
return ElevatedButton(
onPressed: () {
context.read<CounterCubit>().teamIncrement(team: team, points: points);
},
style: ElevatedButton.styleFrom(
backgroundColor: Colors.orange,
foregroundColor: Colors.white,
shape: RoundedRectangleBorder(
borderRadius: BorderRadius.circular(8),
),
minimumSize: const Size(150, 50),
),
child: Text(
'Add $points point',
style: const TextStyle(
color: Colors.black,
fontSize: 18,
),
),
);
}
}
class CounterCubit extends Cubit<CounterIncrementState> {
CounterCubit() : super(CounterIncrementState.uninitialized());
// teamA and teamB moved to state class.
void teamIncrement({required String team, required int points}) {
if (team == 'A') {
emit(state.copyWith(teamA: state.teamA + points));
} else {
emit(state.copyWith(teamB: state.teamB + points));
}
}
void reset() {
emit(const CounterIncrementState(teamA: 0, teamB: 0));
}
}
// Have the team score as part of state
// on each operation, you can emit new state object with current score.
@immutable
class CounterIncrementState {
final int teamA;
final int teamB;
const CounterIncrementState({required this.teamA, required this.teamB});
// copy constructor to get new state object based on current object and
// passed params.
CounterIncrementState copyWith({
int? teamA,
int? teamB,
}) {
return CounterIncrementState(
teamA: teamA ?? this.teamA,
teamB: teamB ?? this.teamB,
);
}
factory CounterIncrementState.uninitialized() {
return const CounterIncrementState(teamA: 0, teamB: 0);
}
}