Giter VIP home page Giter VIP logo

Comments (31)

gimox avatar gimox commented on June 12, 2024 8

love scope_model approach, and yes , multiple model work perfect but with many models the init code it's a bit confused...

from scoped_model.

brianegan avatar brianegan commented on June 12, 2024 8

All of these technologies can be used to separate View logic from Business Logic, they just differ in how they solve that problem.

tl;dr: In my view, scoped_model is the simplest of the three.

While BLoC & Redux are great, they are more complex than scoped_model. That often means you'll be writing a bit more code and need to understand a few more concepts to work with those patterns.

The extra concepts in BLoC are Streams. Streams are really powerful, but take some time to learn. That extra power allows you to do some really neat things that you can't do with Models or Listenables, but the additional complexity might not always be worth it!

I'd argue the same is true about Redux. It takes a bit longer to understand than scoped_model, but can give you some really nice benefits, such as being able Replay the actions a user took through your app. This is immediately useful for crash reporting and measuring user flows through your app.

Hope that helps!

from scoped_model.

brianegan avatar brianegan commented on June 12, 2024 6

Thanks! Definitely an interesting idea. I'm a bit swamped right now and can't guarantee I'll be able to get this done in the next couple weeks, but I'll take a look at it and see how we can do it in a sane way.

In order to support type safety, we'll need to carefully think about the API. For example, we could allow the user to provide a List<Model<dynamic>> as you've suggested, but then you'd lose all type information about which model you're working with.

Therefore, we might need something like:

new ScopedModel2<BookModel, CounterModel>(
  models: [new BookModel(), new CounterModel()],
  child: ...
);
new ScopedModel3<BookModel, CounterModel>(
  models: [new BookModel(), new CounterModel(), new SearchModel()],
  child: ...
);

As well as corresponding ScopedModelDescendant2, ScopedModelDescendant3, etc

from scoped_model.

brianegan avatar brianegan commented on June 12, 2024 6

Added an example here: Let me know if ya think this solves the issue!

https://github.com/brianegan/scoped_model/blob/multiple-models/example/lib/main.dart

from scoped_model.

roeierez avatar roeierez commented on June 12, 2024 4

Hi,
I have followed this discussion and one drawback about the pattern is that from the widget tree perspective it is still one big Model. This means that notification changes will render every widget that uses this Model even if the widget is only interested in one "Sub Model", for example "UserState".
Although I think it is very elegant separation.
Do you think I am wrong here?

from scoped_model.

Ali-Azmoud avatar Ali-Azmoud commented on June 12, 2024 4
Widget build(BuildContext context) {
    // At the top level of our app, we'll, create a ScopedModel Widget. This
    // will provide the CounterModel to all children in the app that request it
    // using a ScopedModelDescendant.
    return ScopedModel<UserModel>( // <========
      model: userModel,
      child: ScopedModel<CounterModel>( // <========
        model: counterModel,
        child: MaterialApp(
          title: 'Scoped Model Demo',
          home: CounterHome('Scoped Model Demo'),
        ),
      ),
    );
  }
}

in your multi model sample, you nested models . since your sample has only 2 models its ok, what if the app grows and at the end there be 10 models or more. should all those 10 models be nested inside each other ?

from scoped_model.

brianegan avatar brianegan commented on June 12, 2024 4

Hey hey -- Just a quick question: Do all of your models contain "Global" data that should be accessed anywhere? If so, then you'd need to currently nest your MaterialApp inside of all of them.

If you only need to access some of these models on specific pages, you could nest a few "Global" models at the top level, and only instantiate new models when you navigate to the Routes that require them.

Overall, I think it's smart to think about where you need data from a Scoped Model, and only provided that Data from that point down. However, if you really have 10 Models that need to be shared globally across your app, then we might need to think about reasonable alternatives to the current implementation.

Unfortunately, the implementation details get tricky here -- if you try to avoid nesting, you often sacrifice some helpful functionality or make other things more complex.

from scoped_model.

gimox avatar gimox commented on June 12, 2024 3

@pedromassango I recently build a navigation app, with complex data relation between widgets. I used scope model. I really love this pattern. Very simple and fast for refactoring. Before start my app, i try bloc pattern but, they need a redux to mantain the state, so the alternave to scope can be bloc pattern with redux. I m a angular developer and redux are a standard in complex app. They are very efficient and cover all your needs....but they are complex to mantain and, very hard for refactoring. Scope model are powerful like redux and simple to mantain. My advice is to take time to project your app life cycle, and be careful to use it in the right way, reloading only the widget you need to refresh.

from scoped_model.

chimon2000 avatar chimon2000 commented on June 12, 2024 2

Definitely a fan of the type safe approach. Is it possible to have a class that accepts an infinite number of types in Dart? I was unsure about it. The other piece that I was unsure of is how those items would be destructured in the builder.

After digging around Dart's documentation, I did find a pattern using mixins that seems equally robust w/o changing the api:

class CounterModel extends Model {
  var _counter = 0;

  int get counter => _counter;
}

abstract class IncrementModel extends CounterModel {
  void increment() {
    // First, increment the counter
    _counter++;

    // Then notify all the listeners.
    notifyListeners();
  }
}

abstract class DecrementModel extends CounterModel {
  void decrement() {
    // First, decrement the counter
    _counter--;

    // Then notify all the listeners.
    notifyListeners();
  }
}

abstract class ResetModel extends CounterModel {
  void reset() {
    // First, reset the counter
    _counter = 0;

    // Then notify all the listeners.
    notifyListeners();
  }
}

class MainModel extends Model
    with CounterModel, IncrementModel, DecrementModel, ResetModel {}

This is a contrived example but it simulates how you can compose an infinite number of models together (similar to combineReducers in redux).

from scoped_model.

chimon2000 avatar chimon2000 commented on June 12, 2024 2

Yes, I think that is a much better example that demonstrates the value. #laziness. I will probably update my post to include some context.

from scoped_model.

tungvn avatar tungvn commented on June 12, 2024 2

The only advantage of the ScopedModelDescendant class is that it allows you to use the Model directly in the child of the ScopedModel and subscribe to updates,

Hi,
I am new and dont understand the 1st advantage mentioned in the above line, possible to explain a bit more, thanks!

As my understanding, I think when you use the ScopedModelDescendant, you can only subscribe to least element-tree you really want to updates when the model changed. In another side, when you use ScopedModel.of, you must call it in the top of Widget.build, and when the model changed, the widget will re-build. It is expensive for the performance.

from scoped_model.

tungvn avatar tungvn commented on June 12, 2024 2

@gimox Me too.
I am a react-redux developer, but on the mobile app, it complexes more than I think. So I used the simplest pattern, I used scope model.
If must start one new flutter app more, I will use scope model again 👍

from scoped_model.

brianegan avatar brianegan commented on June 12, 2024 1

Yah, that's an interesting approach. Just saw your blog post -- as an example without this context, this might a bit hard to follow (e.g. I first thought: Why doesn't he just put all of those methods together to form one logical model?). It might interesting to compose different types of State together, such as a UserState and a SearchState as an example to show the benefits of the approach.

from scoped_model.

gcshri avatar gcshri commented on June 12, 2024 1

One of the problems I've been struggling with is passing multiple but different sets of data/models down the widget tree.

In the simplest use case, I'd like to keep track of say AppConfig (a few app related items) and UserConfig (user data, permissions etc). I could make a larger AppState which includes both App, User and other classes, but for sake of keeping code isolated, would prefer separate models which I could pass down.

from scoped_model.

gcshri avatar gcshri commented on June 12, 2024 1

@brianegan - thank you. Explains a lot of concepts nicely! Would work very well in my situation where we need about a couple of functional models - appglobals, userglobals and then some more on individual views.

from scoped_model.

gcshri avatar gcshri commented on June 12, 2024 1

Hi Brian, I have adapted my code to work based on your multiple model sample. Works as advertised!

Need to look at using the .of(context) in my fluro based route handlers to implement client side authorisation. Have no reason to think it would not work.

from scoped_model.

ShashankSirmour avatar ShashankSirmour commented on June 12, 2024 1

Definitely a fan of the type safe approach. Is it possible to have a class that accepts an infinite number of types in Dart? I was unsure about it. The other piece that I was unsure of is how those items would be destructured in the builder.

After digging around Dart's documentation, I did find a pattern using mixins that seems equally robust w/o changing the api:

class CounterModel extends Model {
  var _counter = 0;

  int get counter => _counter;
}

abstract class IncrementModel extends CounterModel {
  void increment() {
    // First, increment the counter
    _counter++;

    // Then notify all the listeners.
    notifyListeners();
  }
}

abstract class DecrementModel extends CounterModel {
  void decrement() {
    // First, decrement the counter
    _counter--;

    // Then notify all the listeners.
    notifyListeners();
  }
}

abstract class ResetModel extends CounterModel {
  void reset() {
    // First, reset the counter
    _counter = 0;

    // Then notify all the listeners.
    notifyListeners();
  }
}

class MainModel extends Model
    with CounterModel, IncrementModel, DecrementModel, ResetModel {}

This is a contrived example but it simulates how you can compose an infinite number of models together (similar to combineReducers in redux).

why it's giving error now and giving an error like "The class 'ConnectedModel' can't be used as a mixin because it extends a class other than Object"

from scoped_model.

linrz avatar linrz commented on June 12, 2024 1

I think this can help you. @ShashankSirmour

[Flutter/Dart] The class can’t be used as a mixin because it extends a class other than Object. Error [mixin_inherits_from_not_object]

from scoped_model.

brianegan avatar brianegan commented on June 12, 2024

@roeierez Yep, that's definitely true. I think this functionality might be useful in certain cases where you might want to combine different models together that are related for a specific part of the Widget tree without having to create 2-3 ScopedModels and corresponding ScopedModelDescendants to help reduce the amount of code ya need to write.

from scoped_model.

brianegan avatar brianegan commented on June 12, 2024

Hey all, was just updating scoped model a bit and then it hit me: Listening to multiple models is actually pretty simple to do already. Honestly a bit embarrassed I didn't think of this earlier...

In order to get one Widget to build with two models, you can just use the ScopedModel.of method directly (note: now that Dart 2 supports generics on methods, I'm moving this lib over to a static ScopedModel.of<MyModel> method). The cool thing is: This will even trigger rebuilds of the Widget when either model changes due to the magic of InheritedWidgets!

This still requires multiple ScopedModel Widgets, but providing a list of Widgets might get ugly in some ways. I'm also thinking of splitting the of function up into two different functions for readability: of and watch / listen.

class CombinedWidget extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    final username =
      ScopedModel.of<UserModel>(context, rebuildOnChange: true).username;
    final counter =
      ScopedModel.of<CounterModel>(context, rebuildOnChange: true).counter;

    return Text('$username tapped the button $counter times');
  }
}

from scoped_model.

jonahfang avatar jonahfang commented on June 12, 2024

I changed the CounterHome widget in https://github.com/brianegan/scoped_model/blob/multiple-models/example/lib/main.dart as follows:

class CounterHome extends StatelessWidget {
  final String title;

  CounterHome(this.title);

  @override
  Widget build(BuildContext context) {
    final userModel = ScopedModel.of<UserModel>(context, rebuildOnChange: true);
    final counterModel =
        ScopedModel.of<CounterModel>(context, rebuildOnChange: true);

    return Scaffold(
      appBar: AppBar(
        title: Text(title),
      ),
      body: Center(
        child: Column(
          mainAxisAlignment: MainAxisAlignment.center,
          children: <Widget>[
            Text('${userModel.username} pushed the button this many times:'),
            Text('${counterModel.counter}',
                style: Theme.of(context).textTheme.display1),
            Padding(
              padding: const EdgeInsets.all(8.0),
              child: RaisedButton(
                child: Text('Change Username'),
                onPressed: () {
                  userModel.username = 'Suzanne';
                },
              ),
            )
          ],
        ),
      ),
      floatingActionButton: FloatingActionButton(
        onPressed: () => counterModel.increment(),
        tooltip: 'Increment',
        child: Icon(Icons.add),
      ),
    );
  }
}

Why can we have effect of ScopedModelDescendant but use ScopedModel.of only? Is it necessary to use ScopedModelDescendant again?

from scoped_model.

brianegan avatar brianegan commented on June 12, 2024

Hey hey :)

Why can we have effect of ScopedModelDescendant but use ScopedModel.of only? Is it necessary to use ScopedModelDescendant again?

This is an interesting implementation detail! The ScopedModel class listens to the Model and builds an InheritedWidget under the hood. Every time the Model calls notifyListeners(), the InheritedWidget will be rebuilt.

InheritedWidgets are pretty cool -- they allow you to pass data down your Widget tree easily, and they also have a hidden superpower:

Inherited widgets, when referenced in this way, will cause the consumer to rebuild when the inherited widget itself changes state.

Therefore, every time the model changes, it will build a new InheritedWidget, which will then tell every Widget that is connected to it via the of method to rebuild as well if you use the context.inheritFromWidgetOfExactType method!

The only advantage of the ScopedModelDescendant class is that it allows you to use the Model directly in the child of the ScopedModel and subscribe to updates, or if you wan to limit the updates to a specific part of the build method. After that, the two are almost equivalent.

More info:

from scoped_model.

jonahfang avatar jonahfang commented on June 12, 2024

@brianegan Thanks for your detailed explain. I suggest add on method:

final userModel = ScopedModel.of<UserModel>(context, rebuildOnChange: true);
==>
final userModel = ScopedModel.on<UserModel>(context);

from scoped_model.

brianegan avatar brianegan commented on June 12, 2024

Thanks @jonahfang! Yah, was thinking the same thing, but wasn't sure if on and of were too similar (easy to misread, basically). That said, it's nice and short :)

from scoped_model.

brianegan avatar brianegan commented on June 12, 2024

Are folks using this successfully? Does it solve the problem and feel like the issue can be closed?

from scoped_model.

brianegan avatar brianegan commented on June 12, 2024

Sounds good! I'll go ahead and close this out for now. Please let me know if you run into trouble, or spot any room for improvement in the API :)

from scoped_model.

kk21 avatar kk21 commented on June 12, 2024

The only advantage of the ScopedModelDescendant class is that it allows you to use the Model directly in the child of the ScopedModel and subscribe to updates,

Hi,
I am new and dont understand the 1st advantage mentioned in the above line, possible to explain a bit more, thanks!

from scoped_model.

pedromassango avatar pedromassango commented on June 12, 2024
Widget build(BuildContext context) {
    // At the top level of our app, we'll, create a ScopedModel Widget. This
    // will provide the CounterModel to all children in the app that request it
    // using a ScopedModelDescendant.
    return ScopedModel<UserModel>( // <========
      model: userModel,
      child: ScopedModel<CounterModel>( // <========
        model: counterModel,
        child: MaterialApp(
          title: 'Scoped Model Demo',
          home: CounterHome('Scoped Model Demo'),
        ),
      ),
    );
  }
}

in your multi model sample, you nested models . since your sample has only 2 models its ok, what if the app grows and at the end there be 10 models or more. should all those 10 models be nested inside each other ?

I this way too. My app have five models, do I need to nest inside each other? There is a best way to do that?

from scoped_model.

pedromassango avatar pedromassango commented on June 12, 2024

What the advantage of Scoped_Model when comparing with Redux and BLoC?

from scoped_model.

pedromassango avatar pedromassango commented on June 12, 2024

@brianegan My boss give to me a project that is an app like Gmail. Suppose that you are building an Gmail app wich is the best approach: Redux, BLoC or ScopedModel?

I just stopped the development after reaching this question, I do not want to build without a pattern and some time later have to refactor the whole project.

Please give me some tips, it's always good to hear from experts. Thank you in advanced

from scoped_model.

mbartn avatar mbartn commented on June 12, 2024

love scope_model approach, and yes , multiple model work perfect but with many models the init code it's a bit confused...

I was trying to solve multiple initialization problem and I've come up with this code

class MultipleScopedModel extends StatelessWidget {
  final List<Model> models;
  final Widget child;

  MultipleScopedModel({Key key, @required this.models, @required this.child})
      : super(key: key);

  @override
  Widget build(BuildContext context) {
    Widget mergedModels = child;
    for (var model in models) {
      mergedModels = ScopedModel(model: model, child: mergedModels);
    }
    return mergedModels;
  }
}

but for this initialization

class Model1 extends Model {}

class Model2 extends Model {}

void main() {
  runApp(MaterialApp(
    home: MultipleScopedModel(models: [Model1(), Model2()], child: Home()),
  ));
}

class Home extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return Scaffold(
      body: Center(
        child: Text('hello'),
      ),
    );
  }
}

the tree looks like this:
image

the problem is that these initialized models are seen by dart as of generic type Model, not my custom classes e.g. Model1 or Model2. It is not possible to get it by ScopedModel because it can't find type Model1 or Model2 in the tree.

The best solution would be something like

final List<dynamic extends Model> models;

but it is not allowed in Dart.

Do you guys have any idea how to solve this issue?

from scoped_model.

Related Issues (20)

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. 📊📈🎉

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❤️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.