Reputation:
I am trying to add fruit item to the cart, but nothing happens
Once I pressed on the 'add fruit' button nothing happens. It supposes to add fruit items in the cart list. I get an error once trying to access the cart screen by pressing on the cart icon in the app bar after the 'add fruit' button was pressed.
In this way doesn't work properly:
class MyApp extends StatelessWidget {
@override
Widget build(BuildContext context) {
return new ScopedModel(
model: ListModel(),
child: ScopedModel(
model: CartModel(),
child: MaterialApp(
debugShowCheckedModeBanner: false,
title: 'State Management Scoped Model',
theme: myAppTheme,
initialRoute: '/',
routes: {
'/': (context) => MyList(),
'/cart': (context) => MyCart(),
},
),
),
);
}
}
Another way as well:
class MyApp extends StatelessWidget {
@override
Widget build(BuildContext context) {
return new ScopedModel<ListModel>(
model: ListModel(),
child: ScopedModelDescendant<ListModel>(
builder: (context, child, model) => ScopedModel<CartModel>(
model: CartModel(),
),
child: MaterialApp(
debugShowCheckedModeBanner: false,
title: 'State Management Scoped Model',
theme: myAppTheme,
initialRoute: '/',
routes: {
'/': (context) => MyList(),
'/cart': (context) => MyCart(),
},
),
),
);
}
}
With Provider package everything works fine :)
Upvotes: 1
Views: 826
Reputation:
Cannot comment yet.
Try adding the MaterialApp as the direct descendant child of ScopedModel and the use of the ScopedModelDescendant when the changes to the Model actually affect the UI.
class MyApp extends StatelessWidget {
@override
Widget build(BuildContext context) {
return new ScopedModel<ListModel>(
model: ListModel(),
child: MaterialApp(
debugShowCheckedModeBanner: false,
title: 'State Management Scoped Model',
theme: myAppTheme,
initialRoute: '/',
routes: {
'/': (context) => MyList(),
'/cart': (context) => MyCart(),
},
),
);
}
}
and when the property changes lets say in MyCart widget when you list card items
... widget tree ...
child: ScopedModelDescendant<ListModel>(
builder: (context, child, ScopedModel<CartModel> model){
List cartItems = model.cartItems;
List<Widget> cartItemWidgets=[];
cartItems.forEach((cartItemData){
cartItemWidgets.add(new CartItemWidget(cartItemData));
});
return Column(
children:cartItemWidgets,
);
}
),
...widget tree...
Hope this helps.
Note, ScopedModelDescendant changes every time notifyLsteners() is called. Doing so on the entire app would be quite expensive I'd think.
Edit:
forgot to add the rebuildOnChange: true property. And also made a mistake.
... widget tree ...
//__YOUR__MODEL__: model that changes
child: ScopedModelDescendant<__YOUR__MODEL__>(
rebuildOnChange: true, // now the widget rebuilds when notifyListeners(); is called inside the __YOUR__MODEL__
builder: (context, child,__YOUR__MODEL__ model){
List cartItems = model.cartItems;
List<Widget> cartItemWidgets=[];
cartItems.forEach((cartItemData){
cartItemWidgets.add(new CartItemWidget(cartItemData));
});
return Column(
children:cartItemWidgets,
);
}
),
...widget tree...
EDIT 2:
From going through the git repository I was able to build an example of what you wanted to do. Here is the link to the GitHub repository. Note: I've initiated an ownership transfer to you. And I'll update the link if you choose to accept. But to also describe the implementation.
You wanted to have two models a ListModel and a CartModel, the list model currently serves as a placeholder for an actual list.
In the CartModel you attached the ListModel.
Whenever you needed the list you got it through the CartModel.
How I changed things?
Split class Fruit into a separate file. // objects.dart
Turned ListModel into abstract ShoppingModel that extends Model
Created a singleton SuperModel that extends Model with ShoppingModel and CartModel. Gave it a private constructor and an instance attribute. There will ever only be one instance of this object. SuperModel is a mixing it is able to access all the public properties of ShoppingModel and CartModel. To avoid editor displayed errors a file was added//analysis_options.yaml that suppresses the error. Note this doesn't affect the program, you can find more information about it on the web.
CartModel extends ShoppingModel, now CartModel has access to all the methods of the ShoppingModel it does not have to store the ListModel. Use ShoppingModel to add attributes that you might have to use across multiple models.
Wrap the app in a ScopeModel, the model attribute is the SuperModel. Since SuperModel is a singleton I used SuperModel.instance, it never has to be instantiated.
Added ScopedModelDescendant everywhere where changes might occur, don't forget the rebuildOnChange: true, property.
I've also given you the owner of the repository.
In general, when using multiple models, use inheritance and mixins if they need to exchange attributes. I always used a top abstract Model descendant class that holds all the data that all of my other Models use. Other models extend this class so they are able to get access to these attributes.
But since we need access to their properties across the app and the abstract Model descendant doesn't know their children, we can create a mixin of all the Model in this case SuperModel. And because we will ever need a single instance make it a Singleton
class SuperModel extend Model with Model1, Model2{
SuperModel._privateConstructor();
static final SuperModel _instance = SuperModel._privateConstructor();
static SuperModel get instance {
return _instance;
}
}
now we can pass the SuperModel to the ScopedModel.
To enable error free mixins add:
analyzer:
strong-mode: true
language:
enableSuperMixins: true
to the end of the pubspec.yaml file
and a new root file analysis_options.yaml:
analyzer:
errors:
mixin_inherits_from_not_object: ignore
Now this applies to Visual studio code, I don't know if this is handled any differently for Android Studio
Upvotes: 0