Closed amoslai5128 closed 4 years ago
Cloud you offer a new example about Firestore based on example 007? It's more than happy with a user profile upload function or more complex practice!
I will do in the future.
Btw, where happened to example 009? The example 010 should be 009, I Will fix it in the next release.
Cloud you offer a new example about Firestore based on example 007? It's more than happy with a user profile upload function or more complex practice!
I will do in the future.
Btw, where happened to example 009? The example 010 should be 009, I Will fix it in the next release.
Dear GIfatahTH, I've finished a Firestore version of Example 009/010, instead of your Firebase Database, however, there's something I don't understand:
In data_source, xxx_respository.dart, you defined a Stream<List
Here is my code: I don't know is there a better alternative of using a broadcast stream?
final StreamController<List<Asset>> _assetsController =
StreamController<List<Asset>>.broadcast();
@override
Stream assetStream(String uid) {
// Register the handler for when the assets posts data changes
ref.document(uid).collection('Assets').snapshots().listen((snap) {
if (snap.documents.isNotEmpty) {
var posts = snap.documents
.map((doc) {
print('resp: ' + doc.data.entries.toString());
return Asset.fromMap(doc.data, doc.documentID);
})
.where((aseet) => aseet.name != null)
.toList();
// Add the posts onto the controller
_assetsController.add(posts);
}
});
// Return the stream underlying our _assetsController.
return _assetsController.stream;
}
I will try it.
I will try it.
Thank you so much.
OMG, YOU DID IT :) How generous you are!
So many breaking changes... what's the difference between these three scripts (any performance issues)?
observe: () => RM.future(IN.get<UserService>().currentUser())
observe: () => RM.get<UserService>.future((s, stateAsync) => s.currentUser())
observe: () => RM.get<UserService>()..setState((s) => s.currentUser(), watch:userServiceRM.state.user),
The first and the second both give you a new ReactiveModel from the returning future of currentUser. Expect that the second exposes the stateAsycn if you want to await possible currently executing future.
The third one execute the setState on the global UserService ReactiveModel.
So if you want to limit the rebuild to the current StateBuilder use the first or the second. If you want to rebuild all other observer widgets subscribed to the global UserService ReactiveModel use the third.
BTW, in the third case, can you show some cases where it doesn't work.
The first and the second both give you a new ReactiveModel from the returning future of currentUser. Expect that the second exposes the stateAsycn if you want to await possible currently executing future.
The third one execute the setState on the global UserService ReactiveModel.
So if you want to limit the rebuild to the current StateBuilder use the first or the second. If you want to rebuild all other observer widgets subscribed to the global UserService ReactiveModel use the third.
BTW, in the third case, can you show some cases where it doesn't work.
Thank you for your kind answer, I've got a better understanding. The third one was coursed by other things after I checked, so it's completely no problem :)
Hey GIfatahTH :) I know it's out of this topic, however, will you plan to release a new article about the least changes from the states_rebuilder?
I figured out your ReactiveModel is not a simple thing at all, to satisfy the curiosity, I've been reading papers, studies for Dependency Injection, Dependency Inversion and Inversion of Control (IoC), but it's over my knowledgable level X_X...
UNTIL I saw old comments from Youtube tutorial (Interestingly he did mention your library LOL) https://www.youtube.com/watch?v=vBT-FhgMaWM
I figured out your ReactiveModel is not a simple thing at al
Do you mean that it is difficult to understand and implement? And what do you expect from an article to make it simpler?
UNTIL I saw old comments from Youtube tutorial (Interestingly he did mention your library LOL) >https://www.youtube.com/watch?v=vBT-FhgMaWM
Do you mind quote the comment?
Do you mean that it is difficult to understand and implement? And what do you expect from an article to make it simpler?
Not at all, your library is very handy and good to use, especially with a clean design pattern, Perfect! I wish the doc can tell more about How DI (or Service Locator?) with Reactive Model, the flow of the state... Haha, is it too greedy?
To share a bit what's all about:
- Vanilla BLoC for state management and also serve as MVP/MVVM architecture, no package, just InheritedWidget + Streams and StreamBuilders + one little widget I created for simplicity (when streams are overkill) inspired on the package states_rebuilder (which is a very simplistic but elegant way of state management using what's already provided by Dart and Flutter). This little widget is basically a stateful widget that uses setState when notified from the BLoC, somehow serves a similar function that of "NotifyPropertyChanged" but without all the hassle of the binding pattern.
It's at the first pinned comment, by searching the keyword "Amen brother! I'm a huge"
Great!!
I wish the doc can tell more about How DI (or Service Locator?) with Reactive Model, the states >flow, and it might be an idea to have a simple comparison to the latest flutter_bloc. Haha, is it too >greedy?
This is exactly what I'm doing right now:
I am rewriting the documents so that the readme file is very short for a quick start, and I will use github wiki for detailed documentation.
I am implementing the TodoMVC using states_rebuilder following the same approach as with flutter_bloc (immutability, states, events) so that one can easily compare the two.
Any contribution is welcome.
๐WoW, cannot wait more!! I think your library will grow up as popular as flutter bloc๐.
Yes, I think a short quick start guideline is necessary. Also a part for people shouldn't do that, some kind of examples on tge table, giving few bad examples on left side, and answer on the right
I can contribute the language translation of traditional Chinese & ZH-SC, if you wish๐
That would be fantastic.
Now docs are on the v-2.1.0 branch, do you mind checking them?
That would be fantastic.
Now docs are on the v-2.1.0 branch, do you mind checking them?
Haha, I do like the first part with emoji ๐๐คฃ It looks fun and vibrant๐
Btw, would the new version 2.1 add multiple RMKeys: [ () => ]?
In the new version 2.1, to get the reaciveModel
created in observeMany
parameter, we use the rmKey paramter with the new get
method.
Here is an example inspired from #83
class StateBuilderTest extends StatelessWidget {
//create a key
final rmKey = RMKey();
@override
Widget build(BuildContext context) {
return Scaffold(
body: Center(
child: StateBuilder(
observeMany: [
//two ReactiveModel of type string
() => RM.create<String>('x'),
() => RM.create<String>('y'),
() => RM.create<bool>(false),
],
//Assign it to this StatesBuilder(),
rmKey: rmKey,
builder: (context, model) {
return Column(
mainAxisAlignment: MainAxisAlignment.center,
children: [
Text('${model.state}'),
RaisedButton(
child: Text('Concat string'),
onPressed: () {
//to get a model we use rmKey.get<T>()
if (rmKey.get<bool>().state) {
//If more than one model are of the same type,
//the default behavior is to get the first one
rmKey.get<String>().state += 'x';
} else {
//in this case rmKey.get<String>(1) means get the second ReactiveModel of type String
rmKey.get<String>(1).state += 'y';
}
},
),
RaisedButton(
child: Text('Switch bool'),
onPressed: () {
print(rmKey.get<bool>().state);
rmKey.get<bool>().state = !rmKey.get<bool>().state;
},
),
],
);
},
),
),
);
}
}
In the new version 2.1, to get the
reaciveModel
created inobserveMany
parameter, we use the rmKey paramter with the newget
method.Here is an example inspired from #83
class StateBuilderTest extends StatelessWidget { //create a key final rmKey = RMKey(); @override Widget build(BuildContext context) { return Scaffold( body: Center( child: StateBuilder( observeMany: [ //two ReactiveModel of type string () => RM.create<String>('x'), () => RM.create<String>('y'), () => RM.create<bool>(false), ], //Assign it to this StatesBuilder(), rmKey: rmKey, builder: (context, model) { return Column( mainAxisAlignment: MainAxisAlignment.center, children: [ Text('${model.state}'), RaisedButton( child: Text('Concat string'), onPressed: () { //to get a model we use rmKey.get<T>() if (rmKey.get<bool>().state) { //If more than one model are of the same type, //the default behavior is to get the first one rmKey.get<String>().state += 'x'; } else { //in this case rmKey.get<String>(1) means get the second ReactiveModel of type String rmKey.get<String>(1).state += 'y'; } }, ), RaisedButton( child: Text('Switch bool'), onPressed: () { print(rmKey.get<bool>().state); rmKey.get<bool>().state = !rmKey.get<bool>().state; }, ), ], ); }, ), ), ); } }
What if my RMKey is defined as Dynamic or other components?
observeMany: [
//two ReactiveModel of type Dynamic
() => RM.create<Dynamic>(Something type unknown ),
() => RM.create<Dynamic>('A'),
() => RM.create<ZoomController>(...),
() => RM.create<PointerDownEvent>(...),
],
Should I type as follow?
rmKey.get(0).state += 123;
rmKey.get(1).state += 'ABC';
rmKey.get<ZoomController>.setState((xx) => ....);
Should I type as follow?
rmKey.get(0).state += 123;
rmKey.get(1).state += 'ABC';
rmKey.get<ZoomController>.setState((xx) => ....);
YES ๐
Should I type as follow?
rmKey.get(0).state += 123;
rmKey.get(1).state += 'ABC';
rmKey.get<ZoomController>.setState((xx) => ....);
YES ๐
Seems like I'm getting more familiar with States_Rebuilder haha Watching on every update ๐ฅ
Hey GIfatahTH,
I want to ask more about the relationship between Service & Domain layer in Clean_architecture, should service handles the main logic when transforms the data into action for UI? Let say I got a data set from server:
Data Source Layer: Code only handles the server connection, without parsing that data? (shouldn't directly contact to Domain?)
Service Layer: It's just like a bridge that directly contacts with Domain & Data Source, so it gets the data first and escapes the JSON parsing functions from Domain, but this layer also does the classification works, finally transforming it into a state.
Domain layer: just like the core, however, it's more like a data model?
UI Layer: Gets the state from Service Layer, and displays it.
Is that correct? I'm avoiding the data source do too much logic without the logic of server connection itself. Otherwise, it'd become a nightmare when switching other backend servers.
I'm getting some confusion with the service layer....
Design patterns and clean architecture principles are here to help us to keep our code simple, readable, maintainable and testable. They are principles not rules. So whenever an idea make sense for you just use it provided it keeps your code simple, readable, maintainable and testable.
Design patterns and clean architecture principles are here to help us to keep our code simple, readable, maintainable and testable. They are principles not rules. So whenever an idea make sense for you just use it provided it keeps your code simple, readable, maintainable and testable.
Thank you! I will keep my code as clean as possible HAHA
Old code of using broadcast stream
@override Stream assetStream(String uid) { // Register the handler for when the assets posts data changes ref.document(uid).collection('Assets').snapshots().listen((snap) { if (snap.documents.isNotEmpty) { var posts = snap.documents .map((doc) { print('resp: ' + doc.data.entries.toString()); return Asset.fromMap(doc.data, doc.documentID); }) .where((aseet) => aseet.name != null) .toList(); // Add the posts onto the controller _assetsController.add(posts); } }); // Return the stream underlying our _assetsController. return _assetsController.stream; }
/* Great & Workable Code:
@override
Stream<List<Asset>> assetStream(String uid) async* {
yield* ref.document(uid).collection('Assets').snapshots().asyncMap(
(snapshot) async => snapshot.documents
.map((doc) => Asset.fromMap(doc.data, doc.documentID))
.toList()
//descending sort b, a
..sort((b, a) => a.createTime.compareTo(b.createTime)));
}
/* Still, I don't know why the below code didn't work as excepted
by defining a **final snapshots** to let the code better looking: */
@override
Stream<List<Asset>> assetStream(String uid) async* {
final snapshots =
ref.document(uid).collection('Assets').getDocuments().asStream();
yield* snapshots.asyncMap(
(snapshot) async => snapshot.documents
.map((doc) => Asset.fromMap(doc.data, doc.documentID))
.toList()
//descending sort b, a
..sort((b, a) => a.createTime.compareTo(b.createTime)));
}
I know it's out of this topic, however, will you plan to release a new article about the least changes >from the states_rebuilder
Here it is one:
I know it's out of this topic, however, will you plan to release a new article about the least changes >from the states_rebuilder
Here it is one:
Oh WoW, reading on it!!
Very good article! Now, I'm trying to do a complex application with states_rebuilder ;)
I know it's out of this topic, however, will you plan to release a new article about the least changes >from the states_rebuilder
Here it is one:
Oh, the GitHub link inside this article seems dead. https://github.com/GIfatahTH/states_rebuilder_shopper
Dear GIfatahTH,
Here I have some suggestions for example 9 - immutable state todo app. We all know that immutability does a good job but it'd be unfriendly for beginners dealing with those exhaustive codes like:
//From Data Class / Todo_State
@override
bool operator ==(Object o) {
if (identical(this, o)) return true;
return o is Todo &&
o.id == id &&
o.complete == complete &&
o.note == note &&
o.task == task;
}
@override
int get hashCode {
return id.hashCode ^ complete.hashCode ^ note.hashCode ^ task.hashCode;
}
@override
String toString() {
return 'Todo(id: $id,task:$task, complete: $complete)';
}
However, with a VSCode Data Class extension + Equatable package, it looks much nicer :) The extension can also generate the annoying ### toJson/ fromJson, copyWith() automatically. It's lovely to reduce the typo error.
VSCode Extension: https://marketplace.visualstudio.com/items?itemName=BendixMa.dart-data-class-generator Equatable package: https://pub.dev/packages/equatable
//After the extends Equatable, shorter but no performance difference
@override
List<Object> get props => [id, complete, note, task];
@override
bool get stringify => true;
I saw you did define a photoUrl in example 9 immutable todo. Will it be a good idea to do an example of firebase storage for upload photos, docs (with upload progress status & downloaded Url)?
// domain/entities/user.dart
class User {
.......
final String photoUrl;
In an immutable way, I've tried to use your example to do that, however, the code looks unclean since I've to get the download Url into Service_State.dart before it's been CopyWith in add_edit_screen.dart .
Here is my current flow with the upload part: upload multiple photos or docs.
I know step 5 would be unnecessary since it should be done in Repo / Service level, however, I used your data_source/todo_repository.dart, there is a List
To work with immutable object its is very helpful to use Equitable and kt_dart libraries.
To show upload or download progress status, you have to have two variables: 1- total size of the file, 2- current uploaded or downloaded size.
Usually libraries provides a stream to listen to get the current size.
If this is the case, I follow this pattern:
1- Interface
abstract class IUploaderRepository {
//.. other stuff
Stream<dounble> progress(int totalSize);
}
2- Service
class UploaserService{
//.. other stuff
Stream<dounble> progress(int totalSize){
return uploaderRepository.progress(totalSize);
}
}
3- UI
....
RaisedButton(
onPressed: (){
// use file picker to get the file ptath
// get the file total size
int totalSize = .....;
// shwoDialog
showDiaglog(
context : context,
builder : (context){
.....
StateBuilder<double>(
observe : ()=> RM.get<UpdouderService>().stream((s,_)=>s.progress(totalSize)).
//You can automatically cloze the dialog after finishing the upload
onSetState : (context, progressRM){
if(progressRM.state >=100){
RM.navigator.pop();
}
},
builder: (context , progressRM){
// Display some thing
}
)
}
}
)
It remains the data_source part. This is the least important part because it is an implementation detail that can be changed at any time without affecting our application.
Simply choose the provider of your choice (Firebase store for example) and implement the IUploader interface.
Hey GIfatahTH,
I need your help with the question of complex objects in the immutable class. I would be if you would give me some advice!
Let say I have two classes: Shop, Product. I want to know which option would be suitable when dealing with a bundle of data in long-term. I want to know the good and downside of each of them according to the loading on the network and local state management, and which one should be better.
Option 1: Two classes are separated, Product keeps a shopID to connect back with the Shop. Thus, it'd be two files of service.dart.
@immutable
class Shop {
final String shopID;
final String name;
final double rating;
CopyWith()....
}
@immutable
class Product {
final String pid;
final String shopID; // shopID for query to its owner;
final name;
final imgURL;
final .....
CopyWith()....
}
Option 2: Two classes are together, Shop has a complex class object. Thus, it'd be only one ReactiveModel for Service.dart .
@immutable
class Shop {
final String shopID;
final String name;
final double rating;
final UnmodifiableListView <Proudct> products; // Or List <Product>, which's better?
CopyWith()....
}
@immutable
class Product {
final String pid;
final name;
final imgURL;
final .....
CopyWith()....
}
The answer starts by known the relation between the two entities. is it one-to-many (one Shop can have many products or in the other sense one Product can be in many shops) or is the relation many-to-many?
What do you think?
The answer starts by known the relation between the two entities. is it one-to-many (one Shop can have many products or in the other sense one Product can be in many shops) or is the relation many-to-many?
What do you think?
Thank you for your reply,
I think one shop can have many products, one to many. Thus, option 2 would be suitable, right? And there's another question, should I put these two together in single service rather than separate out each of them for a different state? Such as ShopService, ProductService.dart?
OMG, what a breaking change in version 3.0, hehe, Functional injection looks very cool! However, any performance/security difference between old and new?
what a breaking change in version 3.0
Although, global functional injection is a new feature, old apps written with the Injector
approach still work with v.3.0.
There are two minor breaking changes in v3.0 :
StateBuilder
only rebuilds when notification has data.
So one that has a code like this :
StateBuilder(
observer : () => modelRM,
builder: (context, modelRM){
return Text (myModel.state);
}
)
will not notice any change and performance is better because we are minimizing the unnecessary rebuilds (onWaint and hasError statuses).
BUT one that writes something like this:
StateBuilder(
observer : () => modelRM,
builder: (context, modelRM){
if (modelRM.isWaiting){
return SomeWaitingWidget();
}
return Text (myModel.state);
}
)
Will notice that SomeWaitingWidget
is not displayed when the modelRM
is waiting.
To Solve:
either use the shouldRebuild
parameter:
StateBuilder(
observer : () => modelRM,
shouldRebuild : (_)=> true,
builder: (context, modelRM){
if (modelRM.isWaiting){
return SomeWidget();
}
return Text (myModel.state);
}
)
or use WhenBuilderOr
instead of StateBuilder
:
WhenBuilderOr(
observer : () => modelRM,
builder: (context, modelRM){
if (modelRM.isWaiting){
return SomeWidget();
}
return Text (myModel.state);
}
)
The other breaking change is that the API of StateWithMixinBuilder
is changed and I think the StateWithMixinBuilder
is not widely used.
However, any performance/security difference between old and new
the global functional injection is more secure and has more performance.
By the way, I think it is time to close this issue, and open new issues for any question or comment under related opened issues.
Thank you so much for the work. Anything that I can contribute? Like formatting the readme, or something else.
Hey, I've improved the readme formatting on your v3.0.0 branch, and I cannot pull the request.
That's all right, I pulled.
GIfatahTH,
Thanks for your great works and you're a lifesaver, I do love this states_rebuilder.
Could you offer a new example about Firestore based on example 007?
It's more than happy with a user profile upload function or more complex practice!
Btw, where happened to example 009?
Cheers,