I'm trying to execute Bloc more times than one, however I don't know how to handle that.
When I click search, it shows circular progress indicator and then the "Sukces" text, but when i click it for the second time, the indicator doesn't show.
How to fix it?
Link to the video of my app: https://streamable.com/77jyf1
My code:
import 'package:flutter/material.dart';
import 'package:flutter_bloc/flutter_bloc.dart';
import 'package:jaki_to_bank/screens/home/bloc/search_bloc.dart';
import 'package:provider/provider.dart';
import 'package:jaki_to_bank/generated/l10n.dart';
import 'package:jaki_to_bank/screens/home/widgets/search_button.dart';
import 'package:jaki_to_bank/screens/home/widgets/search_text_field.dart';
class HomeScreen extends StatefulWidget {
const HomeScreen({Key? key}) : super(key: key);
#override
_HomeScreenState createState() => _HomeScreenState();
}
class _HomeScreenState extends State<HomeScreen> {
final _formKey = GlobalKey<FormState>();
final SearchBloc _bloc = SearchBloc();
final TextEditingController _bankAccountController = TextEditingController();
#override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: Text(S.current.title),
),
body: Container(
padding: EdgeInsets.all(16.0),
child: Form(
key: _formKey,
child: Column(
children: [
SearchTextField(
controller: _bankAccountController,
validator: (text) {
if (text == null || text.isEmpty) {
return S.current.emptyFieldError;
}
return null;
},
),
SizedBox(height: 16.0),
SearchButton(
onPressed: () {
if (_formKey.currentState!.validate()) {
final validationBloc = context.read<SearchBloc>();
validationBloc.add(GetText(_bankAccountController.text));
}
},
),
BlocBuilder<SearchBloc, SearchState>(
builder: (context, state) {
if (state is SearchInitial) {
return Container();
}
if (state is SearchLoading) {
return CircularProgressIndicator();
}
if (state is SearchFinal) {
return Text('Sukces!');
}
return Container();
},
)
],
),
),
),
);
}
}
part of 'search_bloc.dart';
#immutable
abstract class SearchEvent {}
class GetText extends SearchEvent {
final String bankAccountNumber;
GetText(this.bankAccountNumber);
}
import 'dart:async';
import 'package:bloc/bloc.dart';
import 'package:flutter/material.dart';
import 'package:jaki_to_bank/data/repositories/bank_repository.dart';
import 'package:meta/meta.dart';
import 'package:xml/xml.dart';
part 'search_event.dart';
part 'search_state.dart';
class SearchBloc extends Bloc<SearchEvent, SearchState> {
SearchBloc() : super(SearchInitial());
#override
Stream<SearchState> mapEventToState(
SearchEvent event,
) async* {
if (event is GetText) {
yield SearchLoading();
final String bankAccountNumber = event.bankAccountNumber;
final String bankIdNumber = bankAccountNumber.substring(2, 5);
final BankRepository bankRepo = BankRepository();
final List<String> bankIdsList = <String>[];
final XmlDocument banks = await bankRepo.getParsedXmlText();
final Iterable<XmlElement> bankIds =
banks.findAllElements('NrInstytucji');
bankIds.map((node) => node.text).forEach((element) {
print(element);
});
yield SearchFinal();
}
}
}
part of 'search_bloc.dart';
#immutable
abstract class SearchState {}
class SearchInitial extends SearchState {
SearchInitial();
}
class SearchLoading extends SearchState {
SearchLoading();
}
class SearchFinal extends SearchState {
SearchFinal();
}
Related
Let's say I create a new screen team_screen which is the first parent of the tree.
Now for my team screen there are many widgets, some of theme have their own request, I want to show loader until every widget/request finished and ready.
I thought on 2 approaches.
All the requests are executed in team_screen with future builder and I pass the props to my widgets by demand.
Every widget with request get function that get executed in the async function in the initState function, then in my parent I make to every widget state parameter that is equal to true by the function I passed and when all is don't I stop the loader.
To sum up my problem is how to maintain a widget with many children and requests and showing one loader for entire page, making all the request on same widget? Pass isInitialize function to every widget?.
Which approach is better and if there are more approaches, I would like to hear.
Thank you for your help
Example for the second approach:
import 'package:flutter/material.dart';
import 'package:info_striker/locator.dart';
import 'package:info_striker/models/fixture/fixture.dart';
import 'package:info_striker/models/odds/bookmaker.dart';
import 'package:info_striker/models/synced-team/synced_team.dart';
import 'package:info_striker/services/fixture_service.dart';
import 'package:info_striker/utils/date_utilities.dart';
class TeamNextMatch extends StatefulWidget {
Function isInitialized;
SyncedTeam team;
TeamNextMatch({
Key? key,
required this.isInitialized,
required this.team,
}) : super(key: key);
#override
State<TeamNextMatch> createState() => _TeamNextMatchState();
}
class _TeamNextMatchState extends State<TeamNextMatch> {
Fixture? _fixture;
Bookmaker? _matchResult;
bool _isInitialized = false;
#override
void initState() {
super.initState();
init();
}
init() async {
final response = await locator<FixturesService>().getData(widget.team.id);
if (response != null) {
setState(() {
_fixture = Fixture.fromMap(response["fixture"]);
_matchResult = Bookmaker.fromMap(response["matchResultOdds"]);
});
}
widget.isInitialized(true);
}
#override
Widget build(BuildContext context) {
String? _date;
bool show = _fixture != null && _matchResult != null;
_fixture != null ? "${DateUtilities.getShortDateString(_fixture!.date)}, ${DateUtilities.getTimeString(_fixture!.date)}" : null;
return show
? Column(
children: [
Text(_fixture?.league?["name"]),
if (_date != null) Text(_date),
],
)
: const SizedBox();
}
}
You can show loader as described below -
import 'dart:convert';
import 'package:flutter/material.dart';
import 'package:flutter_application_1/data_model.dart';
import 'package:http/http.dart' as http;
class APiTest extends StatefulWidget {
const APiTest({Key? key}) : super(key: key);
#override
_APiTestState createState() => _APiTestState();
}
class _APiTestState extends State<APiTest> {
final String _url = "https://jsonplaceholder.typicode.com/todos/";
bool _isLoading = true;
final List<DataModel> _allData = [];
#override
void initState() {
super.initState();
_initData().then((value) {
setState(() {
_isLoading = false;
});
});
}
Future<void> _initData() async {
final response = await http.get(Uri.parse(_url));
final List res = jsonDecode(response.body);
res.forEach((element) {
_allData.add(DataModel.fromJson(element));
});
}
#override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: const Text("Loading Demo"),
),
body: Stack(
children: [
ListView.separated(
itemCount: _allData.length,
controller: ScrollController(),
separatorBuilder: (_, __) => const SizedBox(height: 10),
itemBuilder: ((context, index) {
return ListTile(
tileColor: Colors.grey[200],
title: Text(_allData[index].title!),
subtitle: Text(_allData[index].id.toString()),
);
}),
),
if (_isLoading)
const Center(
child: CircularProgressIndicator(),
)
],
),
);
}
}
I tried to follow the answer to this question, but I was not able to make it work.
I reproduced my issue on the counter app, changing it as follow.
main.dart
class MyApp extends StatelessWidget {
const MyApp({Key? key}) : super(key: key);
#override
Widget build(BuildContext context) {
return MultiBlocProvider(
providers: [
BlocProvider(
create: (BuildContext ctx) => DummyCubit(),
),
],
child: MaterialApp(
...
}
class _MyHomePageState extends State<MyHomePage> {
...
#override
Widget build(BuildContext context) {
return Scaffold(
...
body: Center(
child: CounterViewer(counter: _counter),
),
...
);
}
}
class CounterViewer extends StatelessWidget {
const CounterViewer({required this.counter, Key? key}) : super(key: key);
final int counter;
#override
Widget build(BuildContext context) {
return BlocBuilder<DummyCubit, AState>(
builder: (ctx, state) => (state is! StateLoaded)
? const CircularProgressIndicator()
: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: <Widget>[
const Text(
'You have pushed the button this many times:',
),
Text(
'$counter',
style: Theme.of(context).textTheme.headline4,
),
],
),
);
}
}
dummy_cubit.dart
import 'package:bloc/bloc.dart';
import 'package:meta/meta.dart';
class DummyCubit extends Cubit<AState> {
DummyCubit() : super(const InitState());
Future<void> executeLogic() async {
emit(const StateLoading());
// do some logic
emit(StateLoaded('some data'));
}
}
#immutable
abstract class AState {
const AState();
}
class InitState extends AState {
const InitState();
}
class StateLoading extends AState {
const StateLoading();
}
class StateLoaded extends AState {
const StateLoaded(this.data);
final String data;
#override
String toString() => data.toString();
#override
bool operator ==(Object other) =>
identical(this, other) ||
(other is StateLoaded &&
runtimeType == other.runtimeType &&
data == other.data);
#override
int get hashCode => data.hashCode;
}
widget_test.dart
import 'package:flutter/material.dart';
import 'package:flutter_bloc/flutter_bloc.dart';
import 'package:flutter_test/flutter_test.dart';
import 'package:bloc_test/bloc_test.dart';
import 'package:mocktail/mocktail.dart' as mocktail;
import 'package:counter/dummy_cubit.dart';
import 'package:counter/main.dart';
class MockDummyCubit extends MockCubit<AState> implements DummyCubit {}
class AStateFake extends Fake implements AState {}
final dummyCubit = MockDummyCubit();
Widget get counter => MultiBlocProvider(
providers: [
BlocProvider<DummyCubit>(
create: (BuildContext ctx) => dummyCubit,
),
],
child: const MaterialApp(
home: CounterViewer(counter: 1),
),
);
void main() {
setUpAll(() {
mocktail.registerFallbackValue(AStateFake());
});
group('Counter viewer', () {
mocktail.when(() => dummyCubit.state).thenReturn(InitState());
testWidgets('should build', (WidgetTester tester) async {
await tester.pumpWidget(counter);
});
});
}
When running the test, I get this error:
The following StateError was thrown running a test:
Bad state: No method stub was called from within `when()`. Was a real method called, or perhaps an
extension method?
And removing the mocktail.when line, I get this error:
The following _TypeError was thrown building CounterViewer:
type 'Null' is not a subtype of type 'AState'
How do I solve this issue?
How do I control which state is emitted by my DummyCubit?
After reading this, I found the solution
class MockDummyCubit extends MockCubit<AState> implements DummyCubit {}
class AStateFake extends Fake implements AState {}
void main() {
late MockDummyCubit dummyCubit;
setUpAll(() {
mocktail.registerFallbackValue(AStateFake());
});
setUp(() {
dummyCubit = MockDummyCubit();
mocktail.when(() => dummyCubit.state).thenReturn(const InitState());
});
group('Counter viewer', () {
testWidgets('should build', (WidgetTester tester) async {
await tester.pumpWidget(getCounter(dummyCubit));
});
});
}
Widget getCounter(MockDummyCubit dummyCubit) => MultiBlocProvider(
providers: [
BlocProvider<DummyCubit>(
create: (BuildContext ctx) => dummyCubit,
),
],
child: const MaterialApp(
home: CounterViewer(counter: 1),
),
);
I am new to Flutter, and bloc too. I got the idea, how bloc works. But When I create a simple app as the first step of my note app. The bloc doesn't give the data. This simple app has two screens. list screen and Notedetailscreen. Button in NoteDetailScreen tapped, data does not print to the text widget.
main.dart
import 'package:flutter/material.dart';
import 'package:flutter_bloc/flutter_bloc.dart';
import 'package:note_demo_bloc/bloc/note_bloc.dart';
import 'package:note_demo_bloc/list_screen.dart';
void main() {
runApp(MyApp());
}
class MyApp extends StatelessWidget {
#override
Widget build(BuildContext context) {
return BlocProvider<NoteBloc>(
create: (context) => NoteBloc(),
child: MaterialApp(
title: 'Flutter Demo',
theme: ThemeData(
primarySwatch: Colors.blue,
),
home: ListScreen(),
),
);
}
}
note_bloc.dart
import 'dart:async';
import 'package:flutter_bloc/flutter_bloc.dart';
import 'package:meta/meta.dart';
part 'note_event.dart';
part 'note_state.dart';
class NoteBloc extends Bloc<NoteblocEvent, NoteblocState> {
NoteBloc() : super(NoteblocInitial());
#override
Stream<NoteblocState> mapEventToState(
NoteblocEvent event,
) async* {
if (event == NoteSaveEvent) {
yield NoteSaveState(state);
}
}
}
part of 'note_bloc.dart';
#immutable
abstract class NoteblocEvent {}
class NoteSaveEvent extends NoteblocEvent {
NoteSaveEvent(this.text);
final text;
}
note_state.dart
part of 'note_bloc.dart';
#immutable
abstract class NoteblocState {}
class NoteblocInitial extends NoteblocState {}
class NoteSaveState extends NoteblocState {
NoteSaveState(this.text);
final text;
}
list_screen.dart
import 'package:flutter/material.dart';
import 'package:note_demo_bloc/note_detail_screen.dart';
class ListScreen extends StatefulWidget {
const ListScreen({Key? key}) : super(key: key);
#override
_ListScreenState createState() => _ListScreenState();
}
class _ListScreenState extends State<ListScreen> {
#override
Widget build(BuildContext context) {
return Scaffold(
body: Text('hi'),
floatingActionButton: FloatingActionButton(
onPressed: () {
Navigator.of(context).push(
MaterialPageRoute(
builder: (context) => NoteDetailScreen(),
),
);
},
),
);
}
}
Note_detailscreen.dart
import 'package:flutter/material.dart';
import 'package:flutter_bloc/flutter_bloc.dart';
import 'package:note_demo_bloc/bloc/note_bloc.dart';
class NoteDetailScreen extends StatefulWidget {
const NoteDetailScreen({Key? key}) : super(key: key);
#override
_NoteDetailScreenState createState() => _NoteDetailScreenState();
}
class _NoteDetailScreenState extends State<NoteDetailScreen> {
#override
Widget build(BuildContext context) {
return Scaffold(
body: Column(
children: [
ElevatedButton(
onPressed: () {
BlocProvider.of<NoteBloc>(context).add(NoteSaveEvent('hi'));
},
child: Text('click'),
),
BlocBuilder<NoteBloc, NoteblocState>(
builder: (context, state) {
return Text(state.toString());
},
)
],
),
);
}
}
Your bloc, state, and event looks fine. When you push screen you might need to use BlocProvider again. So try this:
main.dart
import 'package:flutter/material.dart';
import 'package:flutter_bloc/flutter_bloc.dart';
import 'package:note_demo_bloc/bloc/note_bloc.dart';
import 'package:note_demo_bloc/list_screen.dart';
void main() {
runApp(MyApp());
}
class MyApp extends StatelessWidget {
NoteBloc _noteBloc = NoteBloc();
#override
Widget build(BuildContext context) {
return BlocProvider<NoteBloc>(
create: (context) => _noteBloc(),
child: MaterialApp(
title: 'Flutter Demo',
theme: ThemeData(
primarySwatch: Colors.blue,
),
home: ListScreen(),
),
);
}
}
list_screen.dart
import 'package:flutter/material.dart';
import 'package:note_demo_bloc/note_detail_screen.dart';
class ListScreen extends StatefulWidget {
const ListScreen({Key? key}) : super(key: key);
#override
_ListScreenState createState() => _ListScreenState();
}
class _ListScreenState extends State<ListScreen> {
#override
Widget build(BuildContext context) {
return Scaffold(
body: Text('hi'),
floatingActionButton: FloatingActionButton(
onPressed: () {
Navigator.of(context).push(
MaterialPageRoute(
builder: (context) => BlocProvider.value(value: BlocProvider.of<NoteBloc>(context), child: NoteDetailScreen()),
),
);
},
),
);
}
}
Note_detailscreen.dart
import 'package:flutter/material.dart';
import 'package:flutter_bloc/flutter_bloc.dart';
import 'package:note_demo_bloc/bloc/note_bloc.dart';
class NoteDetailScreen extends StatefulWidget {
const NoteDetailScreen({Key? key}) : super(key: key);
#override
_NoteDetailScreenState createState() => _NoteDetailScreenState();
}
class _NoteDetailScreenState extends State<NoteDetailScreen> {
#override
Widget build(BuildContext context) {
return Scaffold(
body: Column(
children: [
ElevatedButton(
onPressed: () {
BlocProvider.of<NoteBloc>(context).add(NoteSaveEvent('hi'));
},
child: Text('click'),
),
BlocBuilder<NoteBloc, NoteblocState>(
bloc: BlocProvider.of<NoteBloc>(context),
builder: (context, state) {
return Text(state.toString());
},
)
],
),
);
}
}
So, this is not an answer of your question but consider that as alternative (for future users of SO).
As state management is a free choice, and everyone could manage that as it’s “modus operandi“ this helper class “home made” could be a good choice.
import 'dart:async';
import 'dart:core';
class Method {
Method(this.name, this.params);
final String name;
final Map<String, Object> params;
}
class _Controller {
_Controller._();
static final Map<String, _Controller> _this = new Map<String, _Controller>();
final Map<String, Function(Method)> _funcs = new Map<String, Function(Method)>();
factory _Controller(String identifier) => _this.putIfAbsent(identifier, () => _Controller._());
Future<void> activateListener(String listenerId, Function(Method) function) async {
if (function != null)
_funcs.containsKey(listenerId) ? _funcs[listenerId] = function : _funcs.putIfAbsent(listenerId, () => function);
}
Future<void> deactivateListener(String listenerId) async =>
_funcs.removeWhere((String key, Function(Method) func) => key == listenerId);
Future<void> removeListener(String identifier) async =>
_this.removeWhere((String key, _Controller mClass) => key == identifier);
Future<void> callMethod(String methodName, {Map<String, Object> params}) async =>
Future.forEach(_funcs.values.where((v) => v != null), (func) async => func.call(Method(methodName, params)));
}
mixin MethodListener on Object {
_Controller _getController(String identifier) => _Controller(identifier ?? this.runtimeType.toString());
Future<void> activateListener({String identifier, List<String> identifiers}) async {
if (identifiers != null && identifiers.length > 0)
identifiers.forEach(
(String currentId) => _getController(currentId).activateListener(this.hashCode.toString(), onMethodListener));
else
_getController(identifier).activateListener(this.hashCode.toString(), onMethodListener);
}
Future<void> deactivateListener({String identifier, List<String> identifiers}) async {
if (identifiers != null && identifiers.length > 0)
identifiers.forEach((String currentId) => _getController(currentId).deactivateListener(this.hashCode.toString()));
else
_getController(identifier).deactivateListener(this.hashCode.toString());
}
Future<void> removeListener({String identifier}) async => _getController(identifier).removeListener(identifier);
void onMethodListener(Method method) async => null;
Future<void> callMethodOn(String identifier, String methodName, {Map<String, Object> params}) async =>
_getController(identifier).callMethod(methodName, params: params);
}
class MethodManager with MethodListener {
MethodManager._();
static MethodManager _this;
factory MethodManager() {
if (_this == null) _this = MethodManager._();
return _this;
}
Future<void> callMethodOnWidgets(List<String> identifiers, String methodName, {Map<String, Object> params}) async =>
identifiers.forEach((String currentId) => callMethodOn(currentId, methodName, params: params));
#override
Future<void> callMethodOn(String identifier, String methodName, {Map<String, Object> params}) async =>
super.callMethodOn(identifier, methodName, params: params);
}
then you can implements classes with “with MethodListener” as follows:
import 'package:flutter/material.dart';
import 'package:yourpackagehere/utils/XMethods.dart';
class Test extends StatefulWidget {
static const String NAME = "Test";
#override
createState() => _TestState();
}
class _TestState extends State<Test> with MethodListener {
String _ciao;
#override
void initState() {
super.initState();
this.activateListener(identifier: Test.NAME);
}
#override
void dispose() {
this.deactivateListener(identifier: Test.NAME);
super.dispose();
}
#override
Widget build(BuildContext context) {
return Container(child: Text(_ciao));
}
#override
void onMethodListener(Method method) {
switch (method.name) {
case "say_hello":
if (mounted) {
setState(() {
_ciao = method.params["my_string"];
});
}
break;
}
}
}
Usage:
From everywhere (from widgets or classes):
MethodManager().callMethodOn(Test.NAME, "say_hello", params: {"my_string": "SIAMO CAMPIONI DI EUROPA!!!"});
When I run main.dart on my real phone code runs without any mistakes, I can see the string value exactly on my Location page but when I adjust my android emulator phone's location Turkey/İstanbul it stuckes on spinner (Loading page), Spinner keeps turning forever so Location page never open, by the way "I/eatherforecast(19501): Waiting for a blocking GC ProfileSaver" error written on console. I really wonder the reason of this tedious issue. Have a nice day..
import 'package:flutter/material.dart';
import 'package:weatherforecast2/loadingpage.dart';
void main() {
runApp(MyApp());
}
class MyApp extends StatelessWidget {
#override
Widget build(BuildContext context) {
return MaterialApp(
debugShowCheckedModeBanner: false,
theme: ThemeData.dark().copyWith(),
home: LoadingPage(),
);
}
}
import 'package:flutter/material.dart';
import 'package:flutter_spinkit/flutter_spinkit.dart';
import 'package:weatherforecast2/locationpage.dart';
import 'locationfinder.dart';
import 'package:geolocator/geolocator.dart';
import 'network.dart';
class LoadingPage extends StatefulWidget {
static String id = "loadingpage";
#override
_LoadingPageState createState() => _LoadingPageState();
}
class _LoadingPageState extends State<LoadingPage> {
Map<String,dynamic> currentLocationWeatherData;
void initState(){
super.initState();
getDecodedCurrentLocationWeatherData();
}
void getDecodedCurrentLocationWeatherData()async{
Position position=await LocationFinder().getCurrentLocation();
currentLocationWeatherData = await NetworkHelper().getCurrentLocationWeather(position.latitude,
position.longitude);
Navigator.push(context, MaterialPageRoute(builder: (context) {
return LocationPage(
currentLocationWeatherData: currentLocationWeatherData,
);
}));
}
#override
Widget build(BuildContext context) {
return Container(
child: Center(
child:SpinKitFadingCube(
color: Colors.white,
size: 50,
),
),
);
}
}
import 'dart:convert';
import 'package:http/http.dart';
const String appid="57aad03f4e48ca815bb1184e74624f46";
const String openWeatherMapURL="https://api.openweathermap.org/data/2.5/weather";
class NetworkHelper{
Future<dynamic>getCurrentLocationWeather(lat,lon)async{
Response response=await get("https://api.openweathermap.org/data/2.5/weather?
lat=$lat&lon=$lon&appid=$appid");
if (response.statusCode == 200){
return jsonDecode(response.body);
}
}
}
import 'package:flutter/material.dart';
class LocationPage extends StatefulWidget {
LocationPage({#required this.currentLocationWeatherData});
final Map<String,dynamic> currentLocationWeatherData;
static String id = "locationpage";
#override
_LocationPageState createState() => _LocationPageState();
}
class _LocationPageState extends State<LocationPage> {
#override
Widget build(BuildContext context) {
return Container(
color: Colors.white,
child: Center(
child: Text(
widget.currentLocationWeatherData["weather"][0]["description"],
),
),
);
}
}
import 'package:geolocator/geolocator.dart';
class LocationFinder{
Future<Position> getCurrentLocation()async{
Position position = await Geolocator.getCurrentPosition(desiredAccuracy: LocationAccuracy.low);
return position;
}
}
What am I doing wrong here that my state in a Bloc Pattern changes only one time then mapEventToState doesn't react to BlocProvider.of<CounterBloc>(context).add(ActiveEvent()); request?
I am trying to get into the way of things with the Bloc Pattern but when I switch state in the switcher on a counter page the state changes and after that, it doesn't update at all. It's like don't go any further from onChanged switch function.
I guess the issue is in my stream subscription which is implemented in the CounterBloc contractor. Or I return the state incorrectly.
I would appreciate your help and if you explain to me the mistake.
my bloc
import 'dart:async';
import 'package:flutter_bloc/flutter_bloc.dart';
import 'package:practicing_bloc/blocs/counter/counterEvents.dart';
import 'package:practicing_bloc/blocs/counter/counterState.dart';
class CounterBloc extends Bloc<CounterEvent, CounterState> {
#override
CounterState get initialState => Active(active: true, count: 0);
CounterBloc() {
_counterStream = _counter.stream;
}
StreamController<CounterState> _counter = StreamController<CounterState>();
Stream<CounterState> _counterStream;
#override
Stream<CounterState> mapEventToState(CounterEvent event) async* {
CounterState currentState = state;
print('currect: $currentState');
if (event is ActiveEvent) {
_counter.add(Active(active: true, count: currentState.count));
yield* _counterStream;
} else if (event is InactiveEvent) {
_counter.add(Inactive(active: false, count: currentState.count));
yield* _counterStream;
}
}
}
bloc state
import 'package:equatable/equatable.dart';
import 'package:meta/meta.dart';
abstract class CounterState extends Equatable {
final bool active;
final int count;
const CounterState({#required this.active, #required this.count});
#override
List<Object> get props => [active, count];
#override
String toString() => 'State { active : $active, count : $count }';
}
class Active extends CounterState {
const Active({#required bool active, #required int count})
: super(active: active, count: count);
}
class Inactive extends CounterState {
const Inactive({#required bool active, #required int count})
: super(active: active, count: count);
}
bloc Event
import 'package:equatable/equatable.dart';
abstract class CounterEvent extends Equatable {
const CounterEvent();
#override
List<Object> get props => [];
}
class Increase extends CounterEvent {}
class Decrease extends CounterEvent {}
class ActiveEvent extends CounterEvent {}
class InactiveEvent extends CounterEvent {}
counterPage
import 'package:flutter/material.dart';
import 'package:flutter_bloc/flutter_bloc.dart';
import 'package:practicing_bloc/blocs/counter/counterBloc.dart';
class CounterPage extends StatefulWidget {
#override
_CounterPageState createState() => _CounterPageState();
}
class _CounterPageState extends State<CounterPage> {
bool stateActive = false;
#override
Widget build(BuildContext context) {
//ignore: close_sinks
dynamic counterBloc = BlocProvider.of<CounterBloc>(context);
return Scaffold(
appBar: AppBar(title: Text('Flutter Counter | Page title')),
body: SafeArea(
child: BlocBuilder<CounterBloc, CounterState>(
builder: (context, state) {
String stateString = state.active ? 'Active' : 'Inactive';
return Center(
child: Column(
crossAxisAlignment: CrossAxisAlignment.center,
mainAxisAlignment: MainAxisAlignment.center,
children: <Widget>[
Text('Counter is : $stateString'),
Text('Current counter is : ${state.count}'),
Switch(
value: stateActive,
onChanged: (bool value) {
print(counterBloc.state);
setState(() {
stateActive = value;
});
CounterEvent newEvent =
value ? ActiveEvent() : InactiveEvent();
counterBloc.add(newEvent);
// print('BloC state: ${counterBloc.state.active} | switch state: ${state.active}');
},
)
],
),
);
},
),
),
);
}
}
Basically instead of yielding * _counterStream you need to yield the states in this i.e. Active or Inactive
Change this
if (event is ActiveEvent) {
_counter.add(Active(active: true, count: currentState.count));
yield* _counterStream;
} else if (event is InactiveEvent) {
_counter.add(Inactive(active: false, count: currentState.count));
yield* _counterStream;
}
to this
if (event is ActiveEvent) {
yield Inactive(active: false, count: currentState.count);
} else if (event is InactiveEvent) {
yield Active(active: true, count: currentState.count);
}