felangel / bloc Goto Github PK
View Code? Open in Web Editor NEWA predictable state management library that helps implement the BLoC design pattern
Home Page: https://bloclibrary.dev
License: MIT License
A predictable state management library that helps implement the BLoC design pattern
Home Page: https://bloclibrary.dev
License: MIT License
As much as I like your library, correct me if I'm wrong but I think it is much closer to the Redux architecture than it is to a Bloc one.
Although there is no clear definition of a Bloc yet, from what I've gathered it's much more reactive, should use Sinks are input, and Streams as output.
While this library is used to convert Events (like strongly-typed Redux Actions) into a single State output, using the mapEventToState
reducer function, which is very cool in its way, just not what a Bloc architecture is.
https://www.didierboelens.com/2018/08/reactive-programming---streams---bloc/
Just wondering if anyone has run into an issue where calling dispatch multiple times in quick succession results in mapEventToState not being called?
I have a listener on a socket and when it gets fired, it does _messageBloc.dispatch(MessageReceived(name: name, body: body));
I can print the contents of the message in the listener, but my print statement in mapEventToState only usually gets called for the last message.
If I send a single message after waiting, it makes it to mapEventToState every time.
No exceptions are thrown, I'm on the latest versions of everything.
If you need more info, let me know.
Hi all, Thanks for this framwork. It is clear and simple :)
I have a question. how would you recommend to implement different Debounce on the stream input in the transform bloc method. immagine A have a input test field for search -> debounce 500ms but a submit button -> No debounce?
Again thanks for this great work.
Is your feature request related to a problem? Please describe.
The way this works is pretty cool.
For large apps you end up with alot of code to maintain and i was thinking about a generator to help.
Describe the solution you'd like
There is a bloc generator here and its pretty nice.
https://github.com/CallumIddon/bloc_generator
Describe alternatives you've considered
A clear and concise description of any alternative solutions or features you've considered.
Have a looksie and see what you think.
Very nice package!
I would like to know if this package will be compatible with Dart for Web, like AngularDart.
Thanks in advance.
Describe the bug
It seems that BlocBuilder is not showing actual state, but an initial one. State in the app is managed correctly, and Bloc is provided to the Widget.
To Reproduce
You can reproduce the error by trying to use BlocBuilder in widget, where you provide Bloc with BlocProvider.of(context); It seems that problem is happening only in this case. Does the same on login flow example, if you try using BlocBuilder in home_page.dart
Expected behavior
I expect to get actual state instead of initial one.
Code
main.dart BlocBuilder works with authBloc, profile_page.dart BlocBuilder does not work, but StreamBuilder works. This leads me to believe, that Bloc is provided correctly. onPressed events also work.
main.dart
import 'package:bloc/bloc.dart';
import 'package:flutter/material.dart';
import 'package:flutter_bloc/flutter_bloc.dart';
import 'package:testapp/authentication/authentication.dart';
import 'package:testapp/pages/drawer/test_page.dart';
import 'package:testapp/pages/login/forgot_page.dart';
import 'package:testapp/pages/home_page.dart';
import 'package:testapp/pages/login/login_page.dart';
import 'package:testapp/pages/drawer/profile_page.dart';
import 'package:testapp/pages/login/signup_page.dart';
import 'package:testapp/pages/login/splash_page.dart';
import 'package:testapp/widgets/loading_indicator.dart';
class SimpleBlocDelegate extends BlocDelegate {
@override
void onTransition(Transition transition) {
print(transition.toString());
}
}
void main() {
BlocSupervisor().delegate = SimpleBlocDelegate();
runApp(App());
}
class App extends StatefulWidget {
@override
_AppState createState() => _AppState();
}
class _AppState extends State<App> {
final AuthenticationBloc _authBloc = AuthenticationBloc();
_AppState() {
_authBloc.onAppStart();
}
@override
void dispose() {
_authBloc.dispose();
super.dispose();
}
@override
Widget build(BuildContext context) {
return BlocProvider<AuthenticationBloc>(
bloc: _authBloc,
child: MaterialApp(
debugShowCheckedModeBanner: false,
title: 'LastMile',
theme:
// ThemeData.dark(),
ThemeData(
primarySwatch: Colors.orange,
),
routes: <String, WidgetBuilder>{
'/home': (context) => HomePage(),
'/login': (context) => LoginPage(),
'/signup': (context) => SignupPage(),
'/forgot': (context) => ForgotPage(),
'/profile': (context) => ProfilePage(),
'/test': (context) => TestPage(),
},
home: _rootPage(),
),
);
}
Widget _rootPage() {
return BlocBuilder<AuthenticationEvent, AuthenticationState>(
bloc: _authBloc,
builder: (BuildContext context, AuthenticationState state) {
List<Widget> widgets = [];
if (state.isAuthenticated) {
widgets.add(HomePage());
} else {
widgets.add(LoginPage());
}
if (state.isInitializing) {
widgets.add(SplashPage());
}
if (state.isLoading) {
widgets.add(LoadingIndicator());
}
return Stack(
children: widgets,
);
},
);
}
}
profile_page.dart
import 'package:flutter/material.dart';
import 'package:flutter_bloc/flutter_bloc.dart';
import 'package:testapp/authentication/authentication.dart';
class ProfilePage extends StatelessWidget {
@override
Widget build(BuildContext context) {
final AuthenticationBloc _authBloc =
BlocProvider.of<AuthenticationBloc>(context);
return Scaffold(
appBar: AppBar(title: Text('Profile')),
body: Center(
child: Column(children: <Widget>[
BlocBuilder<AuthenticationEvent, AuthenticationState>(
bloc: _authBloc,
builder: (BuildContext context, AuthenticationState state) {
if (state.isAuthenticated) {
return Text('User ID: ${state.userId}');
} else {
return Text('Loading data');
}
},
),
StreamBuilder(
stream: _authBloc.state,
initialData: _authBloc.initialState,
builder: (context, AsyncSnapshot<AuthenticationState> state) {
if (state.data.isAuthenticated) {
return Text('User ID: ${state.data.userId}');
} else {
return Text('Loading data');
}
},
),
RaisedButton(
child: Text('Link Facebook'),
onPressed: _authBloc.onLinkFacebookButtonPressed,
),
RaisedButton(
child: Text('Link Google'),
onPressed: _authBloc.onLinkGoogleButtonPressed,
),
RaisedButton(
child: Text('Sign out'),
onPressed: () {
_authBloc.onLogout();
Navigator.pop(context);
},
),
RaisedButton(
child: Text('Delete user'),
onPressed: () {
Navigator.pop(context);
_authBloc.onDeleteUser();
},
),
]),
),
);
}
}
**Logs **
flutter analyze:
No issues found! (ran in 3.2s)
flutter doctor -v:
[✓] Flutter (Channel beta, v1.0.0, on Mac OS X 10.14.1 18B75, locale en-LT)
• Flutter version 1.0.0 at /Users/algirdas/development/flutter
• Framework revision 5391447fae (11 days ago), 2018-11-29 19:41:26 -0800
• Engine revision 7375a0f414
• Dart version 2.1.0 (build 2.1.0-dev.9.4 f9ebf21297)
[✓] Android toolchain - develop for Android devices (Android SDK 28.0.3)
• Android SDK at /Users/algirdas/Library/Android/sdk
• Android NDK location not configured (optional; useful for native profiling support)
• Platform android-28, build-tools 28.0.3
• ANDROID_HOME = /Users/algirdas/Library/Android/sdk
• Java binary at: /Applications/Android Studio.app/Contents/jre/jdk/Contents/Home/bin/java
• Java version OpenJDK Runtime Environment (build 1.8.0_152-release-1136-b06)
• All Android licenses accepted.
[✓] iOS toolchain - develop for iOS devices (Xcode 10.1)
• Xcode at /Applications/Xcode.app/Contents/Developer
• Xcode 10.1, Build version 10B61
• ios-deploy 1.9.4
• CocoaPods version 1.5.3
[✓] Android Studio (version 3.2)
• Android Studio at /Applications/Android Studio.app/Contents
• Flutter plugin version 31.1.1
• Dart plugin version 181.5656
• Java version OpenJDK Runtime Environment (build 1.8.0_152-release-1136-b06)
[✓] VS Code (version 1.29.1)
• VS Code at /Applications/Visual Studio Code.app/Contents
• Flutter extension version 2.21.1
[✓] Connected device (1 available)
• iPhone XS Max • 3A626DC0-8339-4895-B831-738F98A987DA • ios • iOS 12.1 (simulator)
• No issues found!
Just want to say this is a great package and has been really helpful in helping me implement the bloc pattern.
I think I noticed a really small mistake in the Readme.
Where it says yield LoginState.error(error.toString());
(unsure of the actual line number) I think it should be yield LoginState.failure(error.toString());
.
If that's the case, happy to raise a PR if you want me to?
Let's extended counter example, where all is working well.
Imagine I add a thir fab action that uses navigator.pushReplacement to navigate a different page.
The destination page needs to see counter value.
How to?
Is your feature request related to a problem? Please describe.
In the Login Tutorial, AuthenticationState
is represented by three finals: isInitializing
, isLoading
and isAuthenticated
. This seems a bad idea because they can be combined in many illegal combinations, like isLoading = true
&& isAuthenticated = true
.
Describe the solution you'd like
We should always represent discrete states using sum types. Since Dart doesn't have it yet, we can simulate using classes like this (taken from official examples of rxdart):
class SearchState {}
class SearchLoading extends SearchState {}
class SearchError extends SearchState {}
class SearchNoTerm extends SearchState {}
class SearchPopulated extends SearchState {
final SearchResult result;
SearchPopulated(this.result);
}
class SearchEmpty extends SearchState {}
Additional context
I'm sorry if this may sound like a rant. But the lack of sum types is just so disappointing when it comes to state management. While the same ideas can be coded both more secure and more expressive in modern languages like TypeScript and Swift, in Dart we have to make do.
Hi, I wonder if it's possible to access state of one bloc from another.
Redux has getState() and redux-thunk to get this to work. Thanks.
Looking at the BlocProvider
code I found that you are using the BuildContext.ancestorWidgetOfExactType
method instead of
BuildContext.inheritFromWidgetOfExactType
From what I could gather this will prevent Widgets that use the BlocProvider
from rebuilding when the Bloc is swapped for another one.
I have already created a version that derives BlocProvider
from StatelessWidget
because I don't need the addditional overhead that comes with using InheritedWidget
.
I haven't made a pull request so far as I have no time to make the required tests to support my claim. Let me know if you are interested.
Is your feature request related to a problem? Please describe.
I wish to listen to event like startLocationService
and than init a new event/s (like log)
Describe the solution you'd like
I was thinking of mingling events at the BlockSupervisor
. But given that, I just lately started with dart
and flutter
so I am not sure of anything.
_BlocProviderState doesn't dispose BLoC in dispose method, so I don't see the reason why BlocProvider does extend StatefulWidget.
i have a question
i want listen to state, and doing something with new state
for example i want listen to AuthenticationState
, when is AuthenticationAuthenticated
i want after 3 seconds push to new page, how can achieve to this ?
thanks
Hello,
I'm trying to implement bloc with SearchDelegate.
Do you have any example?
Thanks
For example, I have a button that would normally have the Navigator call in the onPressed. Using this Bloc pattern, I would dispatch that button click event and then what? What runs the Navigator call?
I have to provide multiple blocs to application level.
final withAuth = BlocProvider<AuthenticationBloc>(
bloc: _authenticationBloc,
child: app,
);
return BlocProvider<NotificationBloc>(
bloc: _notificationBloc,
child: withAuth,
);
Are there any cleaner solutions?
You're welcome to PR your awesome solution on awesome flutter
https://github.com/Solido/awesome-flutter
A Bloc section under Framework would be the best place.
Thank you !
There's a "message" variable inside the state, I used to Toast the "message" whenever it's not null. So, whenever I trigger a Navigator.push(), previous page rebuild, then the state contain non-null "message", it triggered the Toast again.
Hi,
I am pretty much sure this state is confusing. Shouldn't this be if LoggedOut
or something?
Thank you for your effort, just making sure I am not missing something here 👍
First, I want to thank you for your great job. Nevertheless I want to share my experience trying to learn from you example projects.
*If I edit pubspec.yaml inside ../common_github_search and change for meta 1.1.6, the get packages works... I think.
Solution: Maybe talk about it in the README, eg how to upgrade flutter to meta 1.1.7, if not possible maybe not 'metaying' to bleeding edge...)
Ok then what about I open one up, github_search? Dear god, vsc is choking like there is no tomorrow.
Solution: Maybe talk about it in the README, eg. what is to be done to handle this 'exotic' project arrangement: for a toy example project this is arguably 'unwelcoming'. I still haven't found how to run this one...
Thx for your time and concern,
ohenley
Describe the bug
Using the Login tutorial, after navigate to another page using pushReplacementNamed, the _AppState BlocBuilder builder function was not called when I dispatch a new state from the new Page.
To Reproduce
Steps to reproduce the behavior:
authenticationBloc.dispatch(LoggedOut());
using the global AuthenticationBloc;Expected behavior
From new page dispatch LoggedOut and change the MaterialApp home to LoadingIndicator and LoginPage after;
Additional context
I'm using the last version ^0.5.3
and testing in iOS simulator.
Can you show how it use with RefreshIndicator?
Hi all, Thanks for this component. Looks very promising.
I'm struggling with a question. How would you manage animation and controller in the Bloc builder?
Image that on the login example you would have a PageView instead of a stack with the Splash as first page and the Login/Home as second page. When the initialization completes you want to use a page controller to animate from the Splash page to the Home/Login page.
How would you do that?
Thanks,
Alex.
Hi, I saw that you have changed from StatefulWidget to InheritedWidget because you haven't implemented the dispose method. #57
Why did you change from stateful to inherited if you could implement dispose method as the Didier version?
https://www.didierboelens.com/2018/12/reactive-programming---streams---bloc---practical-use-cases/
The Didier approach is good because we don't need to create StatefulWidget to use the dispose method only.
Hi,
I'm getting this error when trying to call a state method that dispatches an event:
I/flutter (27135): ══╡ EXCEPTION CAUGHT BY GESTURE ╞═══════════════════════════════════════════════════════════════════
I/flutter (27135): The following StateError was thrown while handling a gesture:
I/flutter (27135): Bad state: Cannot add new events after calling close
I/flutter (27135):
I/flutter (27135): When the exception was thrown, this was the stack:
I/flutter (27135): #1 Subject._add (package:rxdart/src/subjects/subject.dart:124:16)
I/flutter (27135): #2 Subject.add (package:rxdart/src/subjects/subject.dart:118:5)
I/flutter (27135): #3 _StreamSinkWrapper.add (package:rxdart/src/subjects/subject.dart:150:13)
I/flutter (27135): #4 Bloc.dispatch (package:bloc/src/bloc.dart:27:24)
I/flutter (27135): #5 AuthenticationBloc.onLoginButtonPressed (package:eat_app/blocs/authentication_bloc.dart:141:5)
I/flutter (27135): #6 _AuthPageState._onLoginButtonPressed (package:eat_app/pages/auth_page.dart:177:14)
I/flutter (27135): #7 _AuthPageState._buildLoginPage.<anonymous closure>.<anonymous closure> (package:eat_app/pages/auth_page.dart:157:29)
I/flutter (27135): #8 _InkResponseState._handleTap (package:flutter/src/material/ink_well.dart:507:14)
I/flutter (27135): #9 _InkResponseState.build.<anonymous closure> (package:flutter/src/material/ink_well.dart:562:30)
I/flutter (27135): #10 GestureRecognizer.invokeCallback (package:flutter/src/gestures/recognizer.dart:102:24)
I/flutter (27135): #11 TapGestureRecognizer._checkUp (package:flutter/src/gestures/tap.dart:242:9)
I/flutter (27135): #12 TapGestureRecognizer.acceptGesture (package:flutter/src/gestures/tap.dart:204:7)
I/flutter (27135): #13 GestureArenaManager.sweep (package:flutter/src/gestures/arena.dart:156:27)
I/flutter (27135): #14 _WidgetsFlutterBinding&BindingBase&GestureBinding.handleEvent (package:flutter/src/gestures/binding.dart:147:20)
I/flutter (27135): #15 _WidgetsFlutterBinding&BindingBase&GestureBinding.dispatchEvent (package:flutter/src/gestures/binding.dart:121:22)
I/flutter (27135): #16 _WidgetsFlutterBinding&BindingBase&GestureBinding._handlePointerEvent (package:flutter/src/gestures/binding.dart:101:7)
I/flutter (27135): #17 _WidgetsFlutterBinding&BindingBase&GestureBinding._flushPointerEventQueue (package:flutter/src/gestures/binding.dart:64:7)
I/flutter (27135): #18 _WidgetsFlutterBinding&BindingBase&GestureBinding._handlePointerDataPacket (package:flutter/src/gestures/binding.dart:48:7)
I/flutter (27135): #19 _invoke1 (dart:ui/hooks.dart:153:13)
I/flutter (27135): #20 _dispatchPointerDataPacket (dart:ui/hooks.dart:107:5)
I/flutter (27135): (elided one frame from package dart:async)
I/flutter (27135):
I/flutter (27135): Handler: onTap
I/flutter (27135): Recognizer:
I/flutter (27135): TapGestureRecognizer#5dc3e(debugOwner: GestureDetector, state: ready, won arena, finalPosition:
I/flutter (27135): Offset(269.7, 428.6), sent tap down)
I/flutter (27135): ════════════════════════════════════════════════════════════════════════════════════════════════════
The function is being called here:
authBloc.onLoginButtonPressed(
email: _loginEmailTextEditingController.text,
password: _loginPasswordTextEditingController.text);
And the function that calls the dispatch is:
void onLoginButtonPressed({@required String email, @required String password}) {
dispatch(
LoginButtonPressed(email: email, password: password )
);
}
any this code?
static T of<T extends BlocBase>(BuildContext context){
final type = _typeOf<BlocProvider<T>>();
BlocProvider<T> provider = context.ancestorWidgetOfExactType(type);
return provider.bloc;
}
tks.
Describe the bug
In the _bindStateSubject function of the "bloc/packages/bloc/lib/src/bloc.dart" file,
The currentEvent variable is not updated in time.
modify the lines
"
65 (S nextState) {
66 final transition = Transition(
"
to
"
65 (S nextState) {
66 currentState = _stateSubject.value;
67 final transition = Transition(
"
Because of each foreach loop, the value of the currentState variable will change.
Is your feature request related to a problem? Please describe.
no
Describe the solution you'd like
A clear and concise description of what you want to happen.
Describe alternatives you've considered
A clear and concise description of any alternative solutions or features you've considered.
Additional context
Add any other context or screenshots about the feature request here.
In rxdart 0.19.0, the ValueObservable type is provided.
I recommend changing
"Stream get state => _stateSubject.stream;"
to
"ValueObservable get state => _stateSubject.stream;".
This way, in StreamBuilder , initialData can use your_bloc.state.value
Please refer to "https://twitter.com/filiphracek/status/1050798900968181761"
Hi! Can you show me how to bind the state object in HTML file? In the angular_counter_example
the state is a simple int
and you bind this state to view like this:
<h2>Current Count: {{ counterBloc | bloc }}</h2>
If my state seems like this:
class MyState {
num foo;
num bar;
}
How can I work with this state in HTML view? Is necesary to use the BlocPipe?
Thanks!
I can't trace the issue on my code, not sure it's my code problem or the library
StateError: Bad state: Cannot add new events after calling close
File "broadcast_stream_controller.dart", line 253, in _BroadcastStreamController.add
File "subject.dart", line 124, in Subject._add
File "subject.dart", line 118, in Subject.add
File "bloc.dart", line 73, in Bloc._bindStateSubject.<fn>
File "stream.dart", line 816, in Stream.forEach.<fn>.<fn>
File "stream_pipe.dart", line 11, in _runUserCode
File "stream.dart", line 816, in Stream.forEach.<fn>
File "zone.dart", line 1132, in _rootRunUnary
File "zone.dart", line 1029, in _CustomZone.runUnary
File "zone.dart", line 931, in _CustomZone.runUnaryGuarded
File "stream_impl.dart", line 336, in _BufferingStreamSubscription._sendData
File "stream_impl.dart", line 263, in _BufferingStreamSubscription._add
File "broadcast_stream_controller.dart", line 379, in _SyncBroadcastStreamController._sendData
File "broadcast_stream_controller.dart", line 291, in _BroadcastStreamController._add
File "zone.dart", line 1132, in _rootRunUnary
File "zone.dart", line 1029, in _CustomZone.runUnary
File "zone.dart", line 931, in _CustomZone.runUnaryGuarded
File "stream_impl.dart", line 336, in _BufferingStreamSubscription._sendData
File "stream_impl.dart", line 591, in _DelayedData.perform
File "stream_impl.dart", line 707, in _StreamImplEvents.handleNext
File "stream_impl.dart", line 667, in _PendingEvents.schedule.<fn>
File "zone.dart", line 1120, in _rootRun
File "zone.dart", line 1021, in _CustomZone.run
File "zone.dart", line 923, in _CustomZone.runGuarded
File "zone.dart", line 963, in _CustomZone.bindCallbackGuarded.<fn>
File "zone.dart", line 1124, in _rootRun
File "zone.dart", line 1021, in _CustomZone.run
File "zone.dart", line 923, in _CustomZone.runGuarded
File "zone.dart", line 963, in _CustomZone.bindCallbackGuarded.<fn>
File "schedule_microtask.dart", line 41, in _microtaskLoop
File "schedule_microtask.dart", line 50, in _startMicrotaskLoop
Hi
if i need the oldState in order to create the new state how can i do that without state.take(1).then()...
can you implement mapEventToState(State,Event) ?
maybe just save the state locally in each mapEventToState call ?
At page:
https://felangel.github.io/bloc/#/flutterinfinitelisttutorial?id=data-model
The syntax
const Post({this.id, this.title, this.body}) : super([id, title, body]);
looks like no more valid, it throws 2 errors
Constant constructor can't call non-constant super constructor of 'Equatable'.
and
Initializer expressions in constant constructors must be constants.
It seems fixable as
Post({this.id, this.title, this.body}) : super([id, title, body]);
But I don't know if removing leading const
changes the behaviours. If add
Hi Felix, I am currently learning bloc and have a question regarding the practice of using bloc. My question is that should one bloc handle different types of events?
For example, I have several CRUD operations to my database. So the events could be loading posts, adding posts, upvoting, adding comments, etc. These events could have corresponding states like LoadPostsSuccess/Failure, AddPostSuccess/Failure, etc. I am wondering whether I should use one single DBBloc to handle all these events/states or should I use LoadPostBloc, AddPostBloc, and UpvoteBloc to handle these?
Thank you in advance for your help!
Is your feature request related to a problem? Please describe.
The Bloc
class type signature as well as the onTransition
method is <E, S>
. But the mapEventToState
parameters are (S, E)
.
Describe the solution you'd like
It would be more consistent if the mapEventToState
method would follow the signature of the rest of the package and also have (E event, S state)
instead of (S state, E event)
.
Here: https://felangel.github.io/bloc/#/flutterinfinitelisttutorial?id=post-states
The import
import 'package:flutter_infinite_list/models/models.dart';
is wrong becausse at Data Model section we create post.dart
and not models/models.dart
.
If I am not wrong.
Sorry for annoying you with this issues.
It seems that following implementation could be used when updating bloc itself.
class _BlocProviderInherited<T extends Bloc<dynamic, dynamic>>
...
bool updateShouldNotify(_BlocProviderInherited oldWidget) => oldWidget.bloc != bloc;
}
The missing ")" on line 88 in the tutorial makes the example not compile
To Reproduce
Expected behavior
The example should compile
Hi, author.
Do i have to override == and hashcode everytime i write state and event files?
If it's possible, could you give us some example for this too?
Thank you.
Hi!
I am trying to trigger an event that in turn makes a network REST call, and finally, render a widget injected by the response data.
I am trying to dispatch my event in the build() method of an extended State class of a StatefulWidget.
I tried to mimic the flutter github search structure. Any idea on what I am missing?
Thanks!
Using flutter_bloc 0.5.2
At the top level of my app I have a custom BlocProvider
which is very similar to yours, but gives me access to two of my blocs (i) AppDataBloc
(ii) AnalyticsBloc
.
My view is wrapped in your BlockBuilder
and it passes in the main AppDataBloc
bloc as the bloc
parameter.
Inside my view it uses AppDataBloc
to load initial data and on scrolling (infinite scroll). Other actions on that page may cause an event for AnalyticsBloc
to be dispatched.
This seems to work most of the time, but I have noticed that some of the AnalyticsBloc
events get delayed until some other action is performed e.g. AnalyticsBloc::mapEventToState
isn't executed until I change to a different view or trigger the infinite scroll on the main view.
Looking in the debugger I can see that all events have been correctly dispatched and that the processing is delayed.
Is this because the BlockBuilder
only knows about the stream from AppDataBloc
and so it doesn't cause a flutter render when AppDataBloc
is changed? This was my initial thought, but it doesn't explain why it frequently works.
Should I be using two different blocs like this? Should I be able to dispatch an event to any other unrelated bloc from inside BlockBuilder
?
i wrote an example of github search api (similar to your example provided) just to get familiar with the idea of bloc and i found that when events are sent in delays which leads to inconstant behaviour.
here is my implementation of the bloc class
class SearchEvent {}
class SearchState {}
class SubmitSearchEvent implements SearchEvent {
String query;
SubmitSearchEvent({this.query});
}
class SearchEmptyState implements SearchState {}
class SearchLoadingState implements SearchState {}
class SearchSuccessState implements SearchState {
final SearchResult result;
SearchSuccessState(this.result);
}
class SearchErrorState implements SearchState {
final Error error;
SearchErrorState(this.error);
}
class SearchBloc extends Bloc<SearchEvent, SearchState> {
final SearchApi _api;
SearchBloc(this._api);
@override
SearchState get initialState => SearchEmptyState();
@override
void onTransition(Transition<SearchEvent, SearchState> transition) {
super.onTransition(transition);
print("${DateTime.now()} ");
print("event: ${transition.event}");
print("current state: ${transition.currentState}");
print("next state: ${transition.nextState}");
}
@override
Stream<SearchState> mapEventToState(
SearchState currentState, SearchEvent event) async* {
if (event is SubmitSearchEvent) {
var query = event.query;
if (query.isEmpty)
yield SearchEmptyState();
else {
yield SearchLoadingState();
try {
var response = await _api.queryRepositores(query);
yield SearchSuccessState(response);
} catch (error) {
yield SearchErrorState(error);
}
}
}
}
@override
void dispose() {
super.dispose();
}
}
here the ui implementation
void main() => runApp(MaterialApp(
home: Main(),
theme: ThemeData(),
));
class Main extends StatefulWidget {
@override
_MainState createState() => _MainState();
}
class _MainState extends State<Main> {
String value = "";
SearchBloc _searchBloc;
@override
void initState() {
_searchBloc = SearchBloc(SearchApi());
super.initState();
}
@override
void dispose() {
_searchBloc.dispose();
super.dispose();
}
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: Text('text'),
),
body: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: <Widget>[
Padding(
padding: const EdgeInsets.all(16.0),
child: Column(
children: <Widget>[
TextField(
onChanged: (newValue) {
_searchBloc.dispatch(SubmitSearchEvent(query: newValue));
},
decoration: InputDecoration(
border: UnderlineInputBorder(
borderRadius: BorderRadius.all(Radius.circular(10))),
prefixIcon: Icon(Icons.search),
filled: true,
hintText: 'Search for Repositories'),
),
],
),
),
BlocBuilder(
bloc: _searchBloc,
builder: (context, event) {
if (event is SearchLoadingState) {
return Center(
child: CircularProgressIndicator(),
);
} else if (event is SearchEmptyState) {
return Center(child: Text('Search for data'));
} else if (event is SearchSuccessState) {
return Center(
child: Text("Success ${event.result.totalCount}"),
);
} else if (event is SearchErrorState) {
return Center(child: Text(event.error.toString()));
} else {
return Center(child: Text('Search for data'));
}
},
)
],
),
);
}
}
and here is the log with timestamp between each event
I/flutter ( 1846): 2019-01-28 17:24:35.854326
I/flutter ( 1846): event: SubmitSearchEvent - current state: SearchEmptyState - next state: SearchLoadingState
I/flutter ( 1846): sending request to https://api.github.com/search/repositories?q=s
I/flutter ( 1846): 2019-01-28 17:24:38.570055
I/flutter ( 1846): event: SubmitSearchEvent - current state: SearchLoadingState - next state: SearchSuccessState
I/flutter ( 1846): 2019-01-28 17:24:38.570929
I/flutter ( 1846): event: SubmitSearchEvent - current state: SearchSuccessState - next state: SearchLoadingState
I/flutter ( 1846): sending request to https://api.github.com/search/repositories?q=se
I/flutter ( 1846): 2019-01-28 17:24:39.593691
I/flutter ( 1846): event: SubmitSearchEvent - current state: SearchLoadingState - next state: SearchSuccessState
I/flutter ( 1846): 2019-01-28 17:24:39.594166
I/flutter ( 1846): event: SubmitSearchEvent - current state: SearchSuccessState - next state: SearchLoadingState
I/flutter ( 1846): sending request to https://api.github.com/search/repositories?q=sea
I/flutter ( 1846): 2019-01-28 17:24:40.484570
I/flutter ( 1846): event: SubmitSearchEvent - current state: SearchLoadingState - next state: SearchSuccessState
I/flutter ( 1846): 2019-01-28 17:24:40.485276
I/flutter ( 1846): event: SubmitSearchEvent - current state: SearchSuccessState - next state: SearchLoadingState
I/flutter ( 1846): sending request to https://api.github.com/search/repositories?q=sear
I/flutter ( 1846): 2019-01-28 17:24:41.964403
I/flutter ( 1846): event: SubmitSearchEvent - current state: SearchLoadingState - next state: SearchSuccessState
I/flutter ( 1846): 2019-01-28 17:24:41.964978
I/flutter ( 1846): event: SubmitSearchEvent - current state: SearchSuccessState - next state: SearchLoadingState
I/flutter ( 1846): sending request to https://api.github.com/search/repositories?q=searc
I/flutter ( 1846): 2019-01-28 17:24:43.177989
I/flutter ( 1846): event: SubmitSearchEvent - current state: SearchLoadingState - next state: SearchSuccessState
I/flutter ( 1846): 2019-01-28 17:24:43.179184
I/flutter ( 1846): event: SubmitSearchEvent - current state: SearchSuccessState - next state: SearchLoadingState
I/flutter ( 1846): sending request to https://api.github.com/search/repositories?q=search
I/flutter ( 1846): 2019-01-28 17:24:44.129876
I/flutter ( 1846): event: SubmitSearchEvent - current state: SearchLoadingState - next state: SearchSuccessState
idk if im doing something wrong but everything seems to be ok.
thanks for the library and your time.
Is your feature request related to a problem? Please describe.
As a user of bloc
, I'd like to have a way to identify a WidgetHasBeenLoaded
UiEvent. This way, if I need to respond in my bloc to an action immediately after a widget has been drawn on the screen, I can do it without relying on the bloc constructor, which might be too late.
By also following this approach, we would respect the single input/single output, allowing us to still track the events and responses.
Describe the solution you'd like
We might have to alternatives:
@override
Stream<SalutationUiState> mapEventToState(
SalutationUiState state, event) async* {
if (event is WidgetHasBeenLoaded) {
// do your job here
}
}
where WidgetHasBeenLoaded
is a UiEvent provided by Bloc.
Or we could add an additional method, optional, to the bloc API that is just: Future<UiState> onInitialEvent()
(or similar).
Describe alternatives you've considered
At this moment, the only way to achieve this is using the bloc's constructor. However, that might be too soon: if you initialize your bloc in an upper node of your tree, but it is only consumed in a widget on lower leafs, we might get the result of the action before even the widget was rendered.
hi author, i want to place some components in bloc and dispose them when bloc destroyed.
i hope initState()
and dispose()
featured will be support
Let's suppose I have two blocs Router and Model and I have to create gui based on the state of both of them with router I choose Widget that will be rendered and I have to pass model to each view widget.
Is it possible to easily merge two Bloc s Like for instance ZipStream.
Hi,
thanks for the great work on this bloc library (or maybe framework).
I'm now trying to follow the instruction on your tutorial and I had a question.
So , is there way to update state multiple time with single event?
In other words is there way to behave as hot stream (or Observable) ?
For example on tutorial, I was wondering how to manage "Loading State".
Simple way which I came up was first update with PostLoading
and whenever the data was fetched update with PostLoaded
from single Fetch
event. Is this possible with current API ?
code example
@override
Stream<PostState> mapEventToState(
PostState currentState,
PostEvent event,
) async* {
if (event is Fetch && !_hasReachedMax(currentState)) {
try {
if (currentState is PostUninitialized) {
final posts = await _fetchPosts(0, 20);
// want to return PostLoading();
yield PostLoaded(posts: posts, hasReachedMax: false);
}
if (currentState is PostLoaded) {
// want to return PostLoading();
final posts = await _fetchPosts(currentState.posts.length, 20);
yield posts.isEmpty ? currentState.copyWith(hasReachedMax: true) : PostLoaded(posts: currentState.posts + posts, hasReachedMax: false);
}
} catch (_) {
yield PostError();
}
}
}
Anyway thanks for the awesome work.
In flutter_login exemple, UserRepository
creation is into AppState
class.
If you need to use network service for authentication (e.g. firebase_auth), when writing tests, there is no easy way to mock UserRepository
in the current implementation.
How to inject UserRepository
in the current implementation ? with another Bloc ?
Let's assume the replacement of UserRepository
by Firebase Authentication.
Then, how to share currentUser after login ? with another Bloc ?
Based on original example for not authorize user will see a login page, but i did little bit modified instead of not authenticate user will see a landing page. User now pick button option for login (consist many option for login email/google/etc) then it will open login page. The problem is after login success the page still at login page not redirected to authenticate page i set. Only work if i click back button of restart app
`class LandingPage extends StatelessWidget {
final UserRepository userRepository;
LandingPage({Key key, this.userRepository}) : super(key: key);
@OverRide
Widget build(BuildContext context) {
return Scaffold(
body: Container(
child: Center(
child: RaisedButton(
onPressed: (){
Navigator.push(context, MaterialPageRoute(
builder: (context) => LoginPage(userRepository: userRepository,)
));
},
),
),
),
) ;
}
}`
Can you please provide an example of overriding transform
? Specifically debouncing a certain event (e.g. Search), but not other events in the bloc? Thanks!
A declarative, efficient, and flexible JavaScript library for building user interfaces.
🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.
TypeScript is a superset of JavaScript that compiles to clean JavaScript output.
An Open Source Machine Learning Framework for Everyone
The Web framework for perfectionists with deadlines.
A PHP framework for web artisans
Bring data to life with SVG, Canvas and HTML. 📊📈🎉
JavaScript (JS) is a lightweight interpreted programming language with first-class functions.
Some thing interesting about web. New door for the world.
A server is a program made to process requests and deliver data to clients.
Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.
Some thing interesting about visualization, use data art
Some thing interesting about game, make everyone happy.
We are working to build community through open source technology. NB: members must have two-factor auth.
Open source projects and samples from Microsoft.
Google ❤️ Open Source for everyone.
Alibaba Open Source for everyone
Data-Driven Documents codes.
China tencent open source team.