brianegan / scoped_model Goto Github PK
View Code? Open in Web Editor NEWA Widget that passes a Reactive Model to all of it's children
License: BSD 3-Clause "New" or "Revised" License
A Widget that passes a Reactive Model to all of it's children
License: BSD 3-Clause "New" or "Revised" License
Is there a way to retain the scoped model when routing on logically a child route?
Hey :)
So is there a way to get the previous state, when an update arrives.
My use case is starting an animation, so i have for example a loading variable in my LoginModel, but i only want to trigger the animation when loading changed from false to true.
Is there a way to do this?
Best regards,
Mathew
Hello,
As far as i see, if chil List of some model is updated, parent does not notice that. To work around, i am calling notifyListeners of model from the widget. How bad is it and why?
React just published the Context API with the same features as scoped_model. I was thinking about changing the names of scoped_model to conform the React names (Which are pretty good).
ScopedModel<AModel>
-> ContextProvider<AModel>
ScopedModelDescendant<AModel>
-> ContextConsumer<AModel>
//cc @apwilson
I have a super simple scenario where a ScopedModelDescendant's builder always has a null model.
Scenario is that I want to change app theme from a settings page, I navigate to the settings page via Navigator.of(ctx).push(SettingsPage).
The settings page has a ScopedModelDescendant to read/update current theme, but model is always null.
The ScopedModel is wrapping the main app.
Reproducer:
https://github.com/LarsKrogJensen/scoped_model
I see that the original repo makes some changes so the link in the README is not pointing to where the code was extracted from anymore.
And I notice that the model
has its own library now here: https://github.com/fuchsia-mirror/topaz/tree/master/public/lib/widgets/dart
And I was able to run the example in this repo with this fetching this dep:
dependencies:
lib.widgets.dart:
git:
url: https://github.com/fuchsia-mirror/topaz.git
path: public/lib/widgets/dart # for path from git: documentation is not merged yet: https://github.com/dart-lang/site-www/issues/314
as well as this import import 'package:lib.widgets.dart/model.dart';
Hi, I have a problem with scoped model and multiple pages placed in different file. Let's say that I have a main.dart file with the HomePage and a second_page.dart file with SecondPage but I get the red screen error (Receiver:nulll - NoSuchMethodError: The getter 'foo' was called on null). If I take my SecondPage and I put it all in the main.dart, than all works. Is there a way to split pages in multiple file sharing the same model? Thanks
I have an application which I don't want my model to be created in the beginning. I want to create my model etc when I am navigated to a logged in, state (Home.dart). Here I want to initialize the model, but when I try to find it with ScopedModelDescendant in a navigated class it returns null. How do I know if a widget is a child of a parent? And do I have to have the model at MaterialApp?
Previously I was grateful for your hard work to develop this pattern.
I have a problem when I want to connect several models in a separate file. I previously worked with 'with' but an error appeared during the update.
But I found an answer for this:
#44 (using mixin and on)
in my case, the application works using this when the request is on the server, but dont understand why the error appears on the screen?
my code:
mixin ServiceModel on Model {}
mixin WishlistModel on ServiceModel{}
class MainModel extends Model with ServiceModel, WishlistModel {}
in main.dart:
final MainModel model = new MainModel();
return ScopedModel(
model: model,
...
I'm trying to use the scoped model for a subPage, but when the screen is recreated on an orientation change the state is lost.
I've put together a simple gist : here
If I move the scoped model above the material app then the data persists.
I'd wanted to have page scoped state and app scoped state, but when I've got data for a specific page I'm not sure how to get this working. It's most likely an issue in my code as I'm new to flutter, but I think an example like this would be handy for others too.
Dear Brian, Could you please tell me what's the difference between the two? I want to build an app that has multiple (approx 8) models, and listen to specific model(s). I also wish to have some way to use imperative API, so that, for example, a button press could raise a notification or the like.
I will be really glad to know which of the two you think I should use.
Also: do ScopedModel and InheritedModel work well with built_value?
Hi, I am new to flutter. Do you have any login example which works together with the framework? Otherwise, could you please shed me some light how to do it or point me any reference for that?
Thanks in advance.
I am trying to load json data using the scoped model like this:
import 'package:scoped_model/scoped_model.dart';
import 'package:flutter/services.dart' show rootBundle;
class MyModel extends Model {
Map _jsonData;
Future MyModel() async {
// this won't work
String json = await rootBundle.loadString('assets/words5.json');
this._jsonData = json.decode(jsonString);
}
Map get jsonData => _jsonData;
}
After doing some research on stackoverflow, it was suggested I do something like this:
class MyModel extends Model {
Map _jsonData;
Future _doneFuture;
MyModel() {
_doneFuture = _init();
}
Future _init() async {
print('init');
String jsonString = await _loadJsonFile();
this._jsonData = json.decode(jsonString);
}
Map get jsonData => _jsonData;
Future get initializationDone => _doneFuture;
Future<String> _loadJsonFile() async {
return await rootBundle.loadString('assets/words.json');
}
}
Future main() async {
var c = new MyModel();
await c.initializationDone;
print('done');
Widget app = new ScopedModel<CounterModel>(
//..
)
}
Note that rootBundle
returns a Future and that is, as far as I know, the only way to load files in Flutter.
I am running the newest Flutter and Dart 2 (Channel master, v0.3.1-pre.15, on Mac OS X 10.12.6 16G29, locale en-US
) . Any idea how to load json data into my scoped model in Flutter?
The "Listening to multiple Models in a build function" doesn't cover how to instantiate the app for the first time, ie there's no multi-model equivalent of:
class CounterApp extends StatelessWidget {
@override
Widget build(BuildContext context) {
// First, create a `ScopedModel` widget. This will provide
// the `model` to the children that request it.
return new ScopedModel<CounterModel>(
model: new CounterModel(),
in the docs :-) Only solutions I found online boil down to creating a God object of all the model objects and then passing it to the main app widget in a ScopedModel<God>
call (ie https://proandroiddev.com/you-might-not-need-redux-the-flutter-edition-9c11eba006d7, but also https://www.udemy.com/learn-flutter-dart-to-build-ios-android-apps/learn/v4/t/lecture/12309188?start=0 & others),
this is the only solution I found so far, I refuse to believe this is the truth (and it doesn't work with newer versions of Dart, you can't instantiate a mixin), so I'm hoping for some sage guidance from the Creators ^_^
Using the second method ScopedModel.of
. I'm able to change the state of the model but the changes do not update children accessing the model using context. It works if I use the scoped descendant widget. Is this the expected behavior.
I have a test for this,
import 'package:flutter/material.dart';
import 'package:flutter_test/flutter_test.dart';
import 'package:scoped_model/scoped_model.dart';
void main() {
testWidgets("model change should update children widgets depending on context", (WidgetTester tester) async {
var testModel = new TestModel();
var tree = new MaterialApp(
home: new ScopedModel<TestModel>(
model: testModel,
child: new Container(
child: TestWidget(),
),
),
);
// initial drawing shows the counter form the model
await tester.pumpWidget(tree);
expect(find.text('0'), findsOneWidget);
// Increment the model, which should rebuild only the listening descendant subtree
testModel.increment();
await tester.pump();
await tester.pump();
// the text changes correctly
expect(find.text("1"), findsOneWidget);
});
}
class TestModel extends Model {
int _counter;
TestModel([int initialValue = 0]) {
_counter = initialValue;
}
static TestModel of(BuildContext context) => ScopedModel.of<TestModel>(context);
int get counter => _counter;
void increment([int value]) {
_counter++;
notifyListeners();
}
}
class TestWidget extends StatelessWidget {
@override
Widget build(BuildContext context) {
var model = TestModel.of(context);
return new Container(
child: Text(model.counter.toString(), textDirection: TextDirection.ltr),
);
}
}
Hi, sorry, for not sharing code, i am used to use flutter_flux, wanted to give scoped model a shot , and see if it fits better, here is my use case scenario, a list view initial data fetched by json, each item on the list view might be updated on the fly, i mean i am using some sort of websockt with a like button on an item of the list, when data comes back holding the new value, i can notice some sort of glitch where the item would pop down and up really fast updating the value, #11 i have read here, and did sth similar with condition on db not empty. Secondly, am using some sort of waking up connectivity if an internet connection went down, doing that i can see a request sent requiring new like value and the new values are back on console too, but UI never updates, i did call notify on my model where it gets the new values and update db. well. if am doing it right, dose that mean scoped model is not suitable for my use case?
Using a Model on a page with a text box, the model will reset because the keyboard rebuilds the app.
Hi - i have the below sample app using ScopedModel. I put an artificial delay of an async http call of 5 seconds, and i am noticing that after i call isLoading = true; notifyListeners(); , the transition "hangs". If i use a CircularProgressIndicator, it freezes until the future completes, if i use BottomNavigationBar, the "swipe in" completes half way, freezes, then finishes when future completes.
Is there a more idiomatic way to do this so that the page2 fully renders while the http future is running ?
//-----------------------
//main.dart
//-----------------------
void main() => runApp(new MyApp());
class MyApp extends StatelessWidget {
// This widget is the root of your application.
@override
Widget build(BuildContext context) {
return new MaterialApp(
routes: {
'/' : (BuildContext context) => Page1(),
'/page2' : (BuildContext context) => Page2()
}
);
}
}
//-----------------------
//page1.dart
//-----------------------
class Page1 extends StatelessWidget {
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(title: Text('Page 1')),
body: Container(
child: Column(children: <Widget>[
Text('Page 1 header'),
RaisedButton(
child: Text('Click me'),
onPressed: () {
Navigator.of(context).pushReplacementNamed('/page2');
})
],),)
);
}
}
//-----------------------
//page2.dart
//-----------------------
class Page2 extends StatefulWidget {
@override
State<StatefulWidget> createState() {
return _Page2State();
}
}
class _Page2State extends State<Page2> {
MainModel model = MainModel();
void initState() {
model.fetchData();
super.initState();
}
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(title: Text('Page 2')),
body: ScopedModel<MainModel>(
model: model,
child: ScopedModelDescendant(
builder: (context, child, MainModel model) {
if (model.isLoading) {
return Center(child: CircularProgressIndicator());
} else {
return Container(
child: Column(
children: <Widget>[
Text('Page 2 body'),
],
));
}
})));
}
}
//-----------------------
//main_model.dart
//-----------------------
class MainModel extends Model {
bool _isLoading = false;
bool get isLoading => _isLoading;
void fetchData() async {
_isLoading = true;
notifyListeners();
final String url = 'https://jsonplaceholder.typicode.com/todos/1';
await http.get(url)
.then<Null>((http.Response response) {
print('${DateTime.now()} In http response and about to sleep');
sleep(const Duration(seconds:5));
print('${DateTime.now()} done sleeping');
_isLoading = false;
notifyListeners();
return;
}
).catchError((error) {
print('Error: $error');
_isLoading = false;
notifyListeners();
return;
});
}
}
Is there any reason the notifyListeners call cannot contain a parameter that listeners could use to determine what has been updated?
For example if I have two models that I am composing into a root or 'MainModel' via mixins it would be cool if I could have the MainModel listen for updates to the model and in turn persist the correct model data to disk as opposed to the entire model. For example:
class MainModel extends Model with UserModel, ProductsModel {
Service service;
MainModel(this.service) {
addListener((String action) {
if (action == 'USER_MODEL_UPDATED') {
service.persistUserData(userData);
} else if (action == 'PRODUCTS_MODEL_UPDATED') {
service.persistProductData(productData);
}
});
}
}
mixin UserModel on Model {
userData;
...
}
mixin ProductsModel on Model {
productsData;
...
}
Any thoughts on this?
There are two ways to access a scoped model:
ScopedModelDescendant
builderSomeMode.of(context)
functionThe second one is not documented in the Readme
I am a big fan of unstated which takes advantages of React's new Context API and shares many similarities to this library.
It would be nice if you could combine multiple models in the subscriber
new ScopedModel(
model: [new BookModel(), new CounterModel()],
child: new Column(children: [
new ScopedModelDescendant(
builder: (context, child, { bookModel, counterModel }) => new Text(
counterModel.counter.toString()),
),
new Text("Another widget that doesn't depend on the CounterModel")
])
similar to how you can do it in unstated
<Subscribe to={[BookContainer, CounterContainer]}>
{
(bookStore, counterStore) => {
// do stuff here
}
}
</Subscribe>
Hello Brian,
I wanted to run this by you and see what you think.
I have an app where the MaterialApp which is wrapped by a ScopedModel and it's working perfectly 👍
I'm following this article to Create build environments in Flutter.
I've tested the code in a guinea pig flutter app and the code works fine. I'm running print statements of the inherited widget of AppConfig when initialized and the values are valid. But when the Material App is wrapped by a scoped model, the BuildContext objects within the scoped model have no idea of the Inherited Widget of AppConfig. The build contexts in the scoped model are completely different from the build contexts in the AppConfig Inherited Widget.
I'm not sure if this is a bug where ScopedModels aren't taking into account of the inherited widgets, or if there is some sort of call that has to be made in the ScopedModel files to abide by the inherited widget updating the application's BuildContext.
If you can shed any light on this, it'll be much appreciated. Thanks, Brian.
I think the answers to these questions(mostly related to which widget rebuilds) would be a good extension to the documentation. I suggest we add some clarity with the usage including some advantages/disadvantages in our nice docs
@override
Widget build(BuildContext context) {
// ScopedModel.of<HomeModel>(context, rebuildOnChange: true); under the hood
final model = HomeModel.of(context);
return Scaffold(
appBar: AppBar(
title: Text(
'A = model.countA}
),
),
);
}
Question:
Will the whole Scaffold rebuild if model.countA's value changes? If not, which child widget will be rebuilt on value change?
a.
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: Text(
'A = HomeModel.of(context).countA}
),
),
);
}
b.
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: ScopedModelDescendant<HomeModel>(
builder: (context, child, model) => Text(
'A = ${model.countA}'
),
),
),
);
}
Question:
Will a and b rebuild the same widget? Which is more performant?
Is there anyway to store translations within the model itself, and have the strings refreshed when the locale changes?
List<String> activeFilters = [
Translations.of(context).registrationState,
Translations.of(context).contactInfo,
Translations.of(context).customerStatus,
Translations.of(context).effectiveYear,
Translations.of(context).motor,
Translations.of(context).property,
Translations.of(context).travel,
Translations.of(context).health
];
Locale _appLocale = Locale('en');
Locale get appLocale => _appLocale ?? Locale("en");
void changeLocale(String langCode) {
_appLocale = Locale(langCode);
notifyListeners();
}
I wanted to interact with the model when the animations are over , is there any way to have such interactions with scoped model
var obj = new CounterModel();
@override
void initState() {
super.initState();
controller = new AnimationController(
vsync: this, duration: new Duration(milliseconds: 3000));
animation = new CurvedAnimation(parent: controller, curve: Curves.easeIn);
animation.addListener(() {
this.setState(() {
});
});
animation.addStatusListener((AnimationStatus status) {
if(status==AnimationStatus.completed){
print("BELOW ISTHE CODE");
model.increment(widget.widthRatio);
}
});
controller.forward();
}
I'm followinf a Udemy course using your package, but I thinks it's using a strange approach
Example: we have User model class
import 'package:meta/meta.dart';
class UserModel {
final String id;
final String email;
final String password;
UserModel({@required this.id, @required this.email, @required this.password});
}
Then we created a Scoped one
import 'package:scoped_model/scoped_model.dart';
import '../models/user_model.dart';
class ScopedUserModel extends Model {
UserModel _authenticatedUsers;
void login({String email, String password}) {
_authenticatedUsers = UserModel(
id: 'arararara', email: '[email protected]', password: 'ararara');
notifyListeners();
}
}
But we also have Product Model and Product Scoped Model.
Than I give it up, when I must use both of them
Why do we need a scoped class extending model and using our model class? To add listenable behaviour? This seems reasonable.
But what the right way to use both models without the chain of descendants? It's an hell.
Cource author is trying to use mixin, but Dart syntax for mixin already changed 3-4 times and now instructions are no more updated.
Asking for example in doc and a little more bit of theory
Hey all,
One downside to scoped_model: It cannot be used for AngularDart / Dart Web apps because Model
implements the Listenable
interface from Flutter.
Split scoped_model into 3 packages:
scoped_model
would contain only the Model
class, which implements no interfaces.scoped_model_flutter
would Import the Model
class and use it for the ScopedModel
/ ScopedModelDescendant
Widgetsscoped_model_angular
would provide a Pipe
that can be used to listen for changes to a Model
class, rebuilding the appropriate component on change. e.g. {{(myModel | listen).message}}
Makes the Model class Completely independent of Flutter, and can be re-used cross-platform.
MyModel.of(BuildContext context)
method directly on the Model if you want to use the library cross-platform. I don't know how common this is...scoped_model
as a Listenable (such as with an AnimatedBuilder
widget), or merging it with other Listenables via the Listenable.merge
constructor.Would love to hear your thoughts @passsy, @apwilson, @chimon2000 and others! Is anyone using the Model
class as a Listenable
?
I'm experimenting with the scoped_model system for designing an app, and I have a very simple question about how to initialize and change the value fields of widgets like DropDownButton and Switch.
I have a model structure that contains a list of options for the DropdownButton, I wrap the button in the ScopedModelDescendant so I can access the list within the model to create the DropdownMenuItem's necessary. However I need to give the DropdownButton an initial value that is mutable (so that selecting options updates the widget). I'm struggling to figure out where to store that variable and how to initialize it.
One option is to store in the local widget's state, but an initState() method does give me access to the existing list of categories in the model. If I put it in the build() method for the widget, it can access the model, but then I can't change it because the build() method will overwrite any changes made by the widget's setState(). I guess the third option would be to put a mutable variable in the model itself that reflects the state of the widget, but I'm not sure if that's following proper design principles.
I've created a small sample app below, and inserted my two questions with double stars (**)
import 'package:scoped_model/scoped_model.dart';
class MyAppModel extends Model {
List<String> categories = ['A', 'B', 'C'];
}
void main() => runApp(new MyApp());
class MyApp extends StatelessWidget {
@override
Widget build(BuildContext context) {
return ScopedModel<MyAppModel>(
model: MyAppModel(),
child: MaterialApp(
home: Scaffold(
body: Center(
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: <Widget>[
Text('Dropdown button from model:'),
MyAppDropdown(),
],
),
),
)));
}
}
class MyAppDropdown extends StatefulWidget {
@override
_MyAppDropdownState createState() => _MyAppDropdownState();
}
class _MyAppDropdownState extends State<MyAppDropdown> {
@override
Widget build(BuildContext context) {
return ScopedModelDescendant<MyAppModel>(
builder: (context, child, model) {
return Container(
child: DropdownButton(
value: **"How do I insert a local mutable value from the model"**
items: model.categories.map((String value) {
return DropdownMenuItem<String>(value: value, child: Text(value));
}).toList(),
onChanged: **"How do I change that local mutable value"**,
),
);
},
);
}
}
void main() {
runApp(new MyApp());
}
class MyApp extends StatelessWidget {
@override
Widget build(BuildContext context) {
return new MaterialApp(
home: new HomePage(),
onGenerateRoute: Routes.onGenerateRoute,
);
}
}
class HomePage extends StatelessWidget {
@override
Widget build(BuildContext context) {
return ScopedModel<SampleScopedModel>(
model: SampleScopedModel(),
child: Scaffold(
body: Container(),
floatingActionButton: FloatingActionButton(onPressed: () {
Navigator.pushNamed(context, "NextPage");
}),
),
);
}
}
class NextPage extends StatelessWidget {
@override
Widget build(BuildContext context) {
return ScopedModel<SampleScopedModel>(
model: SampleScopedModel(),
child: Scaffold(
body: ScopedModelDescendant<SampleScopedModel>(
builder: (context, child, model) {
return Text(model.payload.toString());
})
),
);
}
}
class SampleScopedModel extends Model {
int _payload;
int get payload => _payload;
static SampleScopedModel of(BuildContext context) =>
ScopedModel.of<SampleScopedModel>(context);
void setPayload(int payload) {
this._payload = payload;
notifyListeners();
}
}
Hi,
I like the idea of scoped model but all the examples only use primitive states. How can I use a list of objects in the model and update a single property of a single element of that list?
class Dog {
String name;
}
class MyModel extends Model {
List<Dog> _dogs;
List<Dog> get dogs => _dogs;
//How can I change the name of a specific dog in the list?
}
I could add a changeName(index, newName)
method but how can I do that if the objects are more complex and have 20+ properties? Do I have to create a changeProperty()
method for every property?
void main() => runApp(new MyApp());
class MyApp extends StatelessWidget { @override Widget build(BuildContext context) { return ScopedModel<AppModel>( model: new AppModel(), child: new MaterialApp( theme: ThemeData.light(), home: new DefaultTabController( length: 2, child: new Scaffold( appBar: new AppBar( title: new Text("title"), backgroundColor: Colors.pink, bottom: TabBar( tabs: <Widget>[ new Tab( text: "songs", icon: Icon(Icons.music_note), ), new Tab( text: "favorite", icon: Icon(Icons.favorite), ), ], ), ), drawer: new Drawer(), body: TabBarView( children: <Widget>[ new GridView.count( primary: false, crossAxisCount: 2, children: <Widget>[ new Carton(Assets.alqanas), new Carton(Assets.shoot), new Carton(Assets.alqanas), new Carton(Assets.shoot), new Carton(Assets.alqanas), new Carton(Assets.shoot), new Carton(Assets.alqanas), new Carton(Assets.shoot), ], ), new Center(child: new Text("favorite is here")), ], )), ), ), ); } }
class Carton extends StatelessWidget { final String imageUrl; Carton(this.imageUrl); @override Widget build(BuildContext context) { return new Column(children: [ ScopedModelDescendant<AppModel>( builder: (context, child, AppModel model) => new IconButton( icon: new Image.asset(imageUrl), iconSize: 10.0, onPressed: () { Future<void> bottomSheetAwaitClose = showModalBottomSheet<void>( context: context, builder: (BuildContext context) { return MusicR(); }, ); bottomSheetAwaitClose.then((void value) { model.playerStateOff(); }); }, ), ), SizedBox(height: 100.0), new Text("carton"), ]); } }
class AppModel extends Model { bool _playerState = false ; bool get getPlayerState => _playerState ; void playerStateOff (){ _playerState = false ; notifyListeners(); } void playerStateOn(){ _playerState = true ; notifyListeners() ; } }
I have imported all required packages but still getting this error : Could not find the correct ScopedModel
First off, thank you so much for creating this awesome package, it works very nicely! It fits the paradigm I'm used to with MobX and JavaScript. But one feature of MobX for which I couldn't find a counterpart is https://mobx.js.org/refguide/reaction.html and https://mobx.js.org/refguide/autorun.html, which allows you to define a function that is automatically run whenever a value changes.
In my case, I'd like to execute a Navigator.of(context).pushReplacementNamed('...')
once the value of my model.isSignedIn
boolean changes to true. In other words, once the user signs in, I'd like to redirect them to a members-only page.
I couldn't find a built-in way to do this in scoped_model, so I tried using a ValueNotifier but somehow I wasn't getting any notifications in my StatefulWidget. Another hacky way I was thinking of exploring was using a Timer to keep checking to see if model.isSignedIn
changes, but is there a better way?
Thank you!
While I'm tinkering with code example, I noticed the the counter is reset to 0 after a hot reload. Is this normal?
When I use the second way to find model, page does not refresh.
code:
Widget build(BuildContext context) {
final appModel = ScopedModel.of<AppModel>(context);
return Scaffold(
body: Row(
children: <Widget>[
Switch(
value: appModel.isNight,
onChanged: (bool value) {
print('value:$value');
appModel.changeTheme(value);
}),
],
),
);
}
Switch has not changed,
but the first way is ok.
If you can, I would like to see a complete example of the second method. Thank you!
Hi and Good Afternoon,
first thanks for your work. For me its the best balanced approach for handling state in flutter :)
This is actually not an issue of the lib but after updating Flutter, using 'with" to have separate model files is not supported anymore, what would be the best way - from the architects side (you)) to accomplish that.
In more complex apps having all logic in one file seems not appropriate and me personally dont like the Repository approach so much.
Any help appreciated.
Greets from Manila
TLDR;
due to the wrong usage of imports, models can get "null"
i changed
import 'model.dart';
to
import 'package:scoped_model_test/model.dart';
and it works now.
Original Post:
i think its related to this issue but i can't really figure out how to do it anyway.
The simple one file example works fine, but as soon as i start to refactor things out across multiple files things start to crash.
For simplicity i just took the initial example and split it to separate files
all under the lib folder.
main.dart:
import 'model.dart';
import 'package:flutter/material.dart';
import 'package:scoped_model/scoped_model.dart';
import 'package:scoped_model_test/MainScreen.dart';
void main() {
runApp(new MyApp());
}
class MyApp extends StatefulWidget {
@override
_MyAppState createState() => new _MyAppState();
}
class _MyAppState extends State<MyApp> {
@override
Widget build(BuildContext context) {
return new ScopedModel<CounterModel>(
model: CounterModel(),
child: new MaterialApp(
home: MainScreen(),
),
);
}
}
MainScreen.dart
import 'model.dart';
import 'package:flutter/material.dart';
import 'package:scoped_model/scoped_model.dart';
class MainScreen extends StatelessWidget {
@override
Widget build(BuildContext context) {
return new Material(
child: new Padding(
padding: const EdgeInsets.all(28.0),
child: Column(
children: <Widget>[
// ScopedModelDescendant<CounterModel>(builder:(context, child, model) { return Text(model.counter.toString());}),
ScopedModelDescendant<CounterModel>(
builder:(BuildContext context, Widget child, CounterModel model) {
print("model: $model");
return MaterialButton(
child: Text("+"),
onPressed: () {print("click"); /*model.increment();*/},
);
}
)
],
),
),
);
}
}
model.dart
import 'package:scoped_model/scoped_model.dart';
class CounterModel extends Model {
int _counter = 0;
int get counter => _counter;
void increment() {
_counter++;
notifyListeners();
}
}
i expected to just missed an import due to the description in the other issue, but i don't think that's the case here, but i think it is somehow related.
EDIT:
The code above is coming from a reduced example i did for myself. If i 100% apply this to the original code from the example code https://github.com/brianegan/scoped_model/blob/master/example/lib/main.dart it works fine ... but i can't really spot the essential difference :/
working code
main.dart
import 'package:flutter/material.dart';
import 'package:scoped_model/scoped_model.dart';
import 'package:scoped_model_test/model.dart';
import 'package:scoped_model_test/CounterHome.dart';
void main() {
runApp(new MyApp());
}
class MyApp extends StatelessWidget {
@override
Widget build(BuildContext context) {
return new ScopedModel<CounterModel>(
model: new CounterModel(),
child: new MaterialApp(
title: 'Flutter Demo',
theme: new ThemeData(
primarySwatch: Colors.green,
),
home: new CounterHome('Scoped Model Demo'),
),
);
}
}
model.dart
import 'package:scoped_model/scoped_model.dart';
class CounterModel extends Model {
int _counter = 0;
int get counter => _counter;
void increment() {
_counter++;
notifyListeners();
}
}
CounterHome.dart
import 'package:flutter/material.dart';
import 'package:scoped_model_test/model.dart';
import 'package:scoped_model/scoped_model.dart';
class CounterHome extends StatelessWidget {
final String title;
CounterHome(this.title);
@override
Widget build(BuildContext context) {
return new Scaffold(
appBar: new AppBar(
title: new Text(title),
),
body: new Center(
child: new Column(
mainAxisAlignment: MainAxisAlignment.center,
children: <Widget>[
new Text(
'You have pushed the button this many times:',
),
new ScopedModelDescendant<CounterModel>(
builder: (context, child, model) => new Text(
model.counter.toString(),
style: Theme.of(context).textTheme.display1),
),
],
),
),
floatingActionButton: new ScopedModelDescendant<CounterModel>(
builder: (context, child, model) => new FloatingActionButton(
onPressed: model.increment,
tooltip: 'Increment',
child: new Icon(Icons.add),
),
),
);
}
}
EDIT2:
Ok i just figured out, that this had something to do with how the imports are made. I just followed the autocompletion in Android Studio and changed
import 'model.dart';
to
import 'package:scoped_model_test/model.dart';
i think i have to look up what this actually means in regards to dart (i am quite new to that language). I think i can close this for now and added a TLDR; on top.
compiler message: lib/scoped-models/main.dart:6:17: Error: The type 'ProductsModel' can't be mixed in.
compiler message: class MainModel extends Model with UserModel, ProductsModel {}
compiler message: ^
compiler message: lib/scoped-models/main.dart:6:47: Error: Type 'ProductsModel' not found.
compiler message: class MainModel extends Model with UserModel, ProductsModel {}
compiler message: ^
compiler message: lib/scoped-models/main.dart:1: Error: The superclass, '_MainModel&Model&UserModel&ProductsModel', has no unnamed constructor that takes no arguments.
✓] Flutter (Channel beta, v0.8.2, on Mac OS X 10.13.6 17G2307, locale en-TH)
[✓] Android toolchain - develop for Android devices (Android SDK 28.0.1)
[✓] iOS toolchain - develop for iOS devices (Xcode 10.0)
[✓] Android Studio (version 3.1)
[✓] VS Code (version 1.27.2)
[✓] Connected devices (1 available)
When I test a ScopedModel widget that contains an async method ( model.update() ), there is an error:
A Timer is still pending even after the widget tree was disposed.
'package:flutter_test/src/binding.dart': Failed assertion: line 787 pos 7:
'_fakeAsync.nonPeriodicTimerCount == 0'
The widget I want to test:
class PostWidget extends StatelessWidget {
const PostWidget();
@override
Widget build(BuildContext context) {
return ScopedModel<PostModel>(
model: PostModel(),
child: Card(
child: Container(
child: ScopedModelDescendant<PostModel>(
builder: (context, child, model) {
if (model.api.isLoaded == true) {
return const PostContentWidget();
}
if (model.api.isLoading == true) {
return CircularProgressIndicatorCardWidget();
}
model.update();
return Container();
}),
),
),
);
}
The PostModel:
class PostModel extends Model {
Post _post = new Post();
Api _api = new Api();
Post get post => _post;
Api get api => _api;
Future<void> update() async {
if (_api.isLoading == true) return null;
_api.isLoaded = false;
_api.isLoading = true;
notifyListeners();
_post = await DI().rest.fetchPost();
_api.isLoaded = true;
_api.isLoading = false;
notifyListeners();
}
}
The test that fail:
void main() {
testWidgets('PostWidget test', (WidgetTester tester) async {
DI().configure(RestType.DUMMY);
await tester.pumpWidget(new PostWidget());
print('PostWidget test DONE');
});
}
The error returned:
PostWidget test DONE
══╡ EXCEPTION CAUGHT BY FLUTTER TEST FRAMEWORK ╞════════════════════════════════════════════════════
The following assertion was thrown running a test:
A Timer is still pending even after the widget tree was disposed.
'package:flutter_test/src/binding.dart': Failed assertion: line 787 pos 7:
'_fakeAsync.nonPeriodicTimerCount == 0'
Either the assertion indicates an error in the framework itself, or we should provide substantially
more information in this error message to help you determine and fix the underlying cause.
In either case, please report this assertion by filing a bug on GitHub:
https://github.com/flutter/flutter/issues/new
When the exception was thrown, this was the stack:
#2 AutomatedTestWidgetsFlutterBinding._verifyInvariants (package:flutter_test/src/binding.dart)
#3 TestWidgetsFlutterBinding._runTestBody (package:flutter_test/src/binding.dart:520:7)
<asynchronous suspension>
#6 TestWidgetsFlutterBinding._runTest (package:flutter_test/src/binding.dart:494:14)
#7 AutomatedTestWidgetsFlutterBinding.runTest.<anonymous closure> (package:flutter_test/src/binding.dart:751:24)
#13 AutomatedTestWidgetsFlutterBinding.runTest (package:flutter_test/src/binding.dart:749:16)
#14 testWidgets.<anonymous closure>.<anonymous closure> (package:flutter_test/src/widget_tester.dart:62:24)
#15 Declarer.test.<anonymous closure>.<anonymous closure>.<anonymous closure> (package:test/src/backend/declarer.dart:161:27)
<asynchronous suspension>
#16 Invoker.waitForOutstandingCallbacks.<anonymous closure> (package:test/src/backend/invoker.dart:249:15)
<asynchronous suspension>
#20 Invoker.waitForOutstandingCallbacks (package:test/src/backend/invoker.dart:246:5)
#21 Declarer.test.<anonymous closure>.<anonymous closure> (package:test/src/backend/declarer.dart:159:33)
#25 Declarer.test.<anonymous closure> (package:test/src/backend/declarer.dart:158:13)
<asynchronous suspension>
#26 Invoker._onRun.<anonymous closure>.<anonymous closure>.<anonymous closure>.<anonymous closure> (package:test/src/backend/invoker.dart:403:25)
<asynchronous suspension>
#40 _Timer._runTimers (dart:isolate/runtime/libtimer_impl.dart:382:19)
#41 _Timer._handleMessage (dart:isolate/runtime/libtimer_impl.dart:416:5)
#42 _RawReceivePortImpl._handleMessage (dart:isolate/runtime/libisolate_patch.dart:165:12)
(elided 28 frames from class _AssertionError, class _FakeAsync, package dart:async, and package
stack_trace)
The test description was:
PostWidget test
════════════════════════════════════════════════════════════════════════════════════════════════════
Test failed. See exception logs above.
The test description was: PostWidget test
How can I fix that error ?
Thank you.
Seems like the model is recreated when Hot Reload occurs which is quite detrimental when your doing quick changes and cannot see them getting applied correctly, ex: User login information gets lost thus you cannot visualize how the user profile would look when the missing information is present.
Is this by design or an issue?
It would seem scoped_model targets Dart SDK < 2.0.0, which means it will begin to cause compilation issues for people as they upgrade their projects to dart 2
If you use a combination of TextFormFields and Int counters when the text field gets focused the model gets reset.
Model:
import 'package:flutter/material.dart';
import 'package:scoped_model/scoped_model.dart';
import '../../util/format_money.dart';
class TipCalcModel extends Model {
int _tip = 18;
int _people = 1;
double _bill = 0.0;
int get tip => _tip;
int get people => _people;
double get bill => _bill;
set tip(int value) {
_tip = value;
notifyListeners();
}
set people(int value) {
_people = value;
notifyListeners();
}
set bill(double value) {
_bill = value;
notifyListeners();
}
double _calcTip() {
return (((tip ?? 18.0) / 100) * _bill) / _people;
}
String calcTipTotal() {
return formatMoney(_calcTip());
}
String calcBillTotal() {
double _amountTotal = (_calcTip() + _bill) / _people;
return formatMoney(_amountTotal);
}
}
Screen:
import 'dart:async';
import 'package:flutter/material.dart';
import 'package:flutter/rendering.dart';
import 'package:font_awesome_flutter/font_awesome_flutter.dart';
import 'package:native_widgets/native_widgets.dart';
import 'package:scoped_model/scoped_model.dart';
import '../../models/calcs/tip.dart';
import '../../util/dismiss_keyboard.dart';
import '../../util/new_line.dart';
import '../../util/popUp.dart';
import '../../widgets/circle_icon.dart';
import '../../widgets/spacer.dart';
class TipCalculator extends StatelessWidget {
static String routeName = "/tip_calc";
static final GlobalKey<FormState> formKey = GlobalKey<FormState>();
static final GlobalKey<FormFieldState<String>> billKey =
GlobalKey<FormFieldState<String>>();
@override
Widget build(BuildContext context) {
return new ScopedModel<TipCalcModel>(
model: TipCalcModel(),
child: Scaffold(
appBar: AppBar(
title: Text(
'Tip Calculator',
style: Theme.of(context)
.textTheme
.headline
.copyWith(color: Colors.white),
),
elevation: 0.0,
),
body: ScopedModelDescendant<TipCalcModel>(
builder: (context, child, model) => SafeArea(
child: Padding(
padding: const EdgeInsets.all(16.0),
child: ListView(children: <Widget>[
Form(
key: formKey,
autovalidate: false,
onWillPop: () {
return Future(() => true);
},
child: ListTile(
leading: Icon(FontAwesomeIcons.moneyBill),
title: TextFormField(
key: billKey,
autofocus: true,
style: Theme.of(context).textTheme.headline,
keyboardType: TextInputType.numberWithOptions(),
decoration: InputDecoration(labelText: 'Bill Total'),
validator: (val) => val.trim().isEmpty
? 'Total of the Bill Required'
: null,
),
),
),
spacer,
ListTile(
leading: Icon(FontAwesomeIcons.percentage),
title: Text(
model.tip.toString() + "%",
maxLines: 1,
style: Theme.of(context)
.textTheme
.display1
.copyWith(fontSize: 27.0),
),
onTap: () {
showAlertPopup(context, "Info",
"Tip Amount. $newLine\Averages 18-20 %");
},
trailing: ButtonBar(
mainAxisSize: MainAxisSize.min,
children: <Widget>[
circleIconButton(
icon: FontAwesomeIcons.minus,
onPressed:
model.tip == 0 ? null : () => model.tip--),
circleIconButton(
icon: FontAwesomeIcons.plus,
onPressed:
model.tip == 100 ? null : () => model.tip++),
],
),
),
spacer,
ListTile(
leading:
Icon(model.people == 1 ? Icons.person : Icons.people),
title: Text(
model.people.toString(),
style: Theme.of(context)
.textTheme
.display1
.copyWith(fontSize: 27.0),
),
onTap: () {
var _message =
"Number of People to Split the Check With.$newLine";
if (model.people == 1) {
_message += " Currently just you.";
} else if (model.people == 2) {
_message +=
" Currently you plus ${model.people - 1} other person.";
} else {
_message +=
" Currently you plus ${model.people - 1} people.";
}
showAlertPopup(context, "Info", _message);
},
trailing: ButtonBar(
mainAxisSize: MainAxisSize.min,
children: <Widget>[
circleIconButton(
icon: FontAwesomeIcons.minus,
onPressed: model.people == 1
? null
: () => model.people--),
circleIconButton(
icon: FontAwesomeIcons.plus,
onPressed: () => model.people++),
]),
),
spacer,
NativeButton(
onPressed: () {
final _bill = billKey.currentState.value;
model.bill = double.tryParse(_bill);
final form = formKey.currentState;
if (form.validate()) {
dismissKeyboard(context);
showModalBottomSheet<void>(
context: context,
builder: (BuildContext context) {
return SafeArea(
child: Column(
mainAxisSize: MainAxisSize.min,
children: <Widget>[
ListTile(
title: Text(
model.people == 1
? "Amount Due"
: "Per Person",
style: Theme.of(context)
.textTheme
.headline,
),
),
ListTile(
leading:
Icon(FontAwesomeIcons.percentage),
title: Text(
"Tip: " + model.calcTipTotal(),
style: Theme.of(context)
.textTheme
.headline,
),
),
ListTile(
leading: Icon(FontAwesomeIcons.moneyBill),
title: Text(
"Total: " + model.calcBillTotal(),
style: Theme.of(context)
.textTheme
.headline,
),
),
ListTile(
leading: Icon(Icons.info),
title: Text(
"Increase the Tip Percentage for Good Service",
style:
Theme.of(context).textTheme.body1,
),
subtitle: Text(
'Poor: 16%, Good: 18%, Great: 20%',
),
),
],
));
});
}
},
buttonColor: Colors.blue,
child: Text('Calculate'),
),
]),
),
),
),
),
);
}
}
provider is an alternative to inherited widgets management.
Both provider
and scoped_model
aims at doing the same thing, which provider
having a slightly larger vision:
provider
is not limited to exposing a Model
and works with potentially anything, including BLoC, streams, and others.
On the other hand, scoped_model
is a lot more popular than provider
.
The state of things is pretty confusing for the community.
scoped_model
is not enough for advanced inherited widgets, but provider
has a too small reach for peoples to find the package.
Would this make sense to merge both packages?
provider
already implements the same features as scoped_model
but with different names.
provider
could expose aliases of its implementations to match scoped_model
names to make the merging non-breaking.
done
Is there a way to navigate to a new route from the model (not from the page widget)?
I need to navigate out of the screen on some logical condition (as oppose to user action)
Is there a way to do it directly from the model logic?
Currently I setup a flag in the model and use it to show a "finish" button to the user where the navigation happens (onPressed), but I would like to navigate without the user's interaction.
Thank you
I am a newbie to flutter.
I found the TodosModel is a big model include all todos in the list, so there is no todoUpdated
or todoListUpdated
.
is that possible I can distinguish this 2 updates? so I could just re-build the updated todo item, not the all todo list? It should make the performance better.
Could you share us an example for that?
// this is my SignUp.dart file
import 'package:flutter/material.dart';
import 'dart:async';
import '../model/SignedUpList.dart';
import '../scoped_model/signupmodel.dart';
import 'package:scoped_model/scoped_model.dart';
class SignUp extends StatefulWidget {
@OverRide
State createState() {
// TODO: implement createState
return SignUpPage();
}
}
class SignUpPage extends State {
final myController = TextEditingController();
FocusNode EmailFocusNode = new FocusNode();
FocusNode PasswordFocusNode = new FocusNode();
FocusNode ConfirmFocusNode = new FocusNode();
List list=[];
@OverRide
void dispose() {
// Clean up the controller when the Widget is disposed
myController.dispose();
super.dispose();
}
final GlobalKey _formKey = GlobalKey();
final Map<String, dynamic> _formData = {
'username': null,
'email': null,
'password': null,
};
Widget _buildusername() {
return TextFormField(
decoration: InputDecoration(
focusedBorder: OutlineInputBorder(
borderRadius: BorderRadius.all(Radius.circular(0.0)),
borderSide: BorderSide(color: Colors.orange)),
hintText: 'Username',
border: OutlineInputBorder(
borderRadius: BorderRadius.all(Radius.circular(0.0)),
)),
keyboardType: TextInputType.text,
onEditingComplete: () {
FocusScope.of(context).requestFocus(EmailFocusNode);
},
validator: (String value) {
if (value.trim().length <= 0) {
return 'Please enter a Username';
}
},
onSaved: (String value) {
_formData['username'] = value;
},
);
}
Widget _buildgradientbutton(double targetWidth) {
return ScopedModelDescendant(
builder: (BuildContext context,Widget child,signupmodel model){
return
/model.isLoading?
Center(child: CircularProgressIndicator()):
Container(
width: targetWidth,
height: 50.0,
decoration: BoxDecoration(
gradient: LinearGradient(
colors: [Colors.red, Colors.orange],
),
boxShadow: [
BoxShadow(
color: Colors.grey[500],
offset: Offset(0.0, 1.5),
blurRadius: 1.5,
),
]),
child: Material(
color: Colors.transparent,
child: InkWell(
onTap: _submitForm(model.save_signup_form),
//onTap: this.onPressed,
child: Center(
child: Text(
'Sign Up',
style: TextStyle(color: Colors.white),
),
)),
),
)/;
}
);
}
Widget buildemail() {
return TextFormField(
decoration: InputDecoration(
focusedBorder: OutlineInputBorder(
borderRadius: BorderRadius.all(Radius.circular(0.0)),
borderSide: BorderSide(color: Colors.orange)),
hintText: 'E-Mail',
border: OutlineInputBorder(
borderRadius: BorderRadius.all(Radius.circular(0.0)),
),
filled: true,
fillColor: Colors.white),
keyboardType: TextInputType.emailAddress,
focusNode: EmailFocusNode,
onEditingComplete: () {
FocusScope.of(context).requestFocus(PasswordFocusNode);
},
validator: (String value) {
if (value.isEmpty ||
!RegExp(r"[a-z0-9!#$%&'*+/=?^{|}~-]+(?:\.[a-z0-9!#$%&'*+/=?^_
{|}~-]+)*@(?:a-z0-9?.)+a-z0-9?")
.hasMatch(value)) {
return 'Please enter a valid email';
}
},
onSaved: (String value) {
_formData['email'] = value;
},
);
}
Widget _buildpassword() {
return TextFormField(
controller: myController,
decoration: InputDecoration(
focusedBorder: OutlineInputBorder(
borderRadius: BorderRadius.all(Radius.circular(0.0)),
borderSide: BorderSide(color: Colors.orange)),
hintText: 'Password',
border: OutlineInputBorder(
borderRadius: BorderRadius.all(Radius.circular(0.0)),
),
),
obscureText: true,
focusNode: PasswordFocusNode,
onEditingComplete: (){
FocusScope.of(context).requestFocus(ConfirmFocusNode);
},
validator: (String value) {
if (value.isEmpty || value.length < 6) {
return 'Password invalid';
}
},
onSaved: (String value) {
_formData['password'] = value;
},
);
}
Widget _buildconfirmpassword() {
return TextFormField(
decoration: InputDecoration(
focusedBorder: OutlineInputBorder(
borderRadius: BorderRadius.all(Radius.circular(0.0)),
borderSide: BorderSide(color: Colors.orange)),
hintText: 'Confirm Password',
filled: true,
fillColor: Colors.white,
border: OutlineInputBorder(
borderRadius: BorderRadius.all(Radius.circular(0.0)),
),
),
obscureText: true,
focusNode: ConfirmFocusNode,
onEditingComplete: (){
_submitForm(signupmodel().save_signup_form);
/* setState(() {
_submitForm(signupmodel().save_signup_form);
signupmodel().isLoading=true;
});*/
},
validator: (String value) {
if (value != myController.text) {
print(value);
return 'Passwords not matching';
}
//dispose();
},
);
}
_submitForm(Function save_signup_form) {
if (!_formKey.currentState.validate()) {
return;
}
_formKey.currentState.save();
print(_formData);
save_signup_form(_formData['username'],_formData['email'],_formData['password']).then((bool success) {
if (success) {
Navigator.pushReplacementNamed(context, '/');
} else {
showDialog(
context: context,
builder: (BuildContext context) {
return AlertDialog(
title: Text('Something went wrong'),
content: Text('Please try again!'),
actions: <Widget>[
FlatButton(
onPressed: () => Navigator.of(context).pop(),
child: Text('Okay'),
)
],
);
});
}
});
//Navigator.pushReplacementNamed(context, '/Log_In');
}
void _loginpage() {
Navigator.pushReplacementNamed(context, '/');
}
Future _backButtonpressed() async{
Navigator.of(context).pop();
}
@OverRide
Widget build(BuildContext context) {
// TODO: implement build
final double deviceWidth = MediaQuery.of(context).size.width;
final double targetWidth = deviceWidth > 550.0 ? 500.0 : deviceWidth * 0.95;
return WillPopScope(
onWillPop: _backButtonpressed,
child:Scaffold(
appBar: AppBar(
backgroundColor: Colors.white,
elevation: 0.0,
title: Center(
child: Text('Sign Up'),
)),
body: Container(
decoration: BoxDecoration(color: Colors.white),
padding: EdgeInsets.all(10.0),
child: Center(
child: SingleChildScrollView(
child: Container(
width: targetWidth,
child: Form(
key: _formKey,
child: Column(
children: <Widget>[
_buildusername(),
_buildemail(),
_buildpassword(),
_buildconfirmpassword(),
SizedBox(
height: 10.0,
),
_buildgradientbutton(targetWidth),
],
),
),
),
),
),
),
bottomNavigationBar: FlatButton(
child: Text('Sign In'), onPressed: _loginpage),
)
);
}
}
//this is myscopedmodel signupmodel
import 'package:scoped_model/scoped_model.dart';
import '../model/SignedUpList.dart';
import 'package:http/http.dart' as http;
import 'dart:convert';
import 'dart:async';
class signupmodel extends Model{
bool isLoading;
List list=[];
Future save_signup_form(String _username,String _email,String _password,) async{
isLoading=true;
notifyListeners();
final Map<String, dynamic> _signupData = {
'username': _username,
'email': _email,
'password':_password,
};
try {
final http.Response response = await http.post(
'https://sample-f765a.firebaseio.com/signuplist.json',
body: json.encode(_signupData));
if (response.statusCode != 200 && response.statusCode != 201) {
isLoading = false;
notifyListeners();
return false;
}
final Map<String, dynamic> responseData = json.decode(response.body);
print(responseData);
isLoading = false;
notifyListeners();
return true;
} catch (error) {
isLoading = false;
notifyListeners();
return false;
}
}
}
Hi
i have problem , when call a Future function from outside of Scoped Model ,that is run and run ...
how can i call that just for run once ?
//// function in Scoped model /////
Future fetchSelectedProducts(String categoryid) async {
print("Run Fetch for $categoryid");
selectedProductData.clear();
isLoading = true;
notifyListeners();
final response =
await http.get('https://....');
List<dynamic> data = json.decode(response.body);
ProductModel products = ProductModel();
data.forEach((dynamic protdata) {
products = ProductModel(
product_id: protdata['product_id'],
product_name: protdata['product_name'],
product_category: protdata['product_category'],
product_des: protdata['product_des'],
product_color: protdata['product_color'],
product_size: protdata['product_size'],
product_barcode: protdata['product_barcode'],
product_image: protdata['product_image'],
product_count: protdata['product_count'],
product_price_buy: protdata['product_price_buy'],
product_price_sell: protdata['product_price_sell']);
if (products.product_category == categoryid) {
selectedProductData.add(products);
print("Found:${products.product_category}");
} else
print("NotFound");
notifyListeners();
});
isLoading = false;
notifyListeners();
return productData;
}
//// call fetchSelectedProducts function in outside widget ////
@override
Widget build(BuildContext context) {
if (widget.currentcategoryID == "0") {
return Container(
child: Text("Show All Products"),
);
} else {
runFetchSelectetProduct(widget.currentcategoryID);
print(
"Run GridView Product lentgh : ${widget.model.selectedProductData.length} ");
return Container(
child: Text("Show ${widget.currentcategoryID} Product"),
);
}
}
}
runFetchSelectetProduct(categoryid) {
widget.model.fetchSelectedProducts(categoryid);
print("Run Fetch Selected Product Id:$categoryid");
}
scoped_model/lib/scoped_model.dart
Line 29 in 9a47bae
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.