Reputation: 95
I need help creating the architecture for my application. I am using Flutter and scoped_model to maintain state.
It's an application that has a login, that displays news in one part of the application, and shows a photo gallery among others. I would like to split this entire thing into separate Models. LoginModel that holds Login state (like username, token, name etc.). NewsModel that contains news retrieved from the API. GalleryModel to hold names of photos etc. I am not sure if this is the best practice to maintain state using scoped_model.
For eg, what If a text box depends on both LoginModel and NewsModel? I am not sure, but I guess it's not possible to retrieve state from two separate models. Also, the main reason I am maintaining separate Models to hold state is that I don't want the Login part of the app to get refreshed when I bring news. I guess that's how it goes when I put the entire state in a single model.
Upvotes: 8
Views: 7189
Reputation: 1461
I wanted to give you a simple example on ScopedModel.
pubspec.yaml file must include :-
dependencies:
scoped_model: ^1.0.1
then,
import 'package:flutter/material.dart';
import 'package:scoped_model/scoped_model.dart';
void main() => runApp(MyApp()); //main method
class MyApp extends StatelessWidget {
@override
Widget build(BuildContext context) {
return MaterialApp(
home: MyHomePage(), //new class MyHomePage
);
}
}
//-----------------------------------CounterModel [used by ScopedModel]
class CounterModel extends Model {
int _counter = 0;
int get counter => _counter;
void increment() {
_counter++;
notifyListeners();
}
}
//-----------------------------------ends
//-----------------------------------MyHomePage class
class MyHomePage extends StatefulWidget {
@override
_MyHomePageState createState() => _MyHomePageState();
}
class _MyHomePageState extends State<MyHomePage> {
@override
Widget build(BuildContext context) {
return ScopedModel( // ScopedModel used on top of the widget tree [it is wrapping up scaffold]
model: CounterModel(), // providing the CounterModel class as model
child: Scaffold(
appBar: AppBar(),
body: Container(
child: ScopedModelDescendant<CounterModel>( // ScopedModelDescendant accessing the data through ScopedModel
builder: (context, _, model) => Text("${model._counter}"), // fetching data from model without thinking of managing any state.
),
),
floatingActionButton: ScopedModelDescendant<CounterModel>(
builder: (context, _, model) => FloatingActionButton(
onPressed: model.increment, // calling function of model to increment counter
),
),
),
);
}
}
//-----------------------------------ends
Upvotes: 5
Reputation: 5351
The scoped_model
library is designed to work with multiple models in play at the same time. That's part of the reason that ScopedModel
and ScopedModelDescendant
are generics and have a type parameter. You can define multiple models near the top of your Widget tree using ScopedModel<LoginModel>
and ScopedModel<NewsModel>
and then consume those models lower in the tree using ScopedModelDescendant<LoginModel>
and ScopedModelDescendant<NewsModel>
. The descendants will go looking for the appropriate model based on their type parameter.
I knocked together a quick example. Here are the models:
class ModelA extends Model {
int count = 1;
void inc() {
count++;
notifyListeners();
}
}
class ModelB extends Model {
int count = 1;
void inc() {
count++;
notifyListeners();
}
}
And here's what I'm displaying in the app:
ScopedModel<ModelA>(
model: ModelA(),
child: ScopedModel<ModelB>(
model: ModelB(),
child: ScopedModelDescendant<ModelA>(
builder: (_, __, a) => ScopedModelDescendant<ModelB>(
builder: (_, __, b) {
return Center(
child: Column(
children: [
GestureDetector(
onTap: () => a.inc(),
child: Text(a.count.toString()),
),
SizedBox(height:100.0),
GestureDetector(
onTap: () => b.inc(),
child: Text(b.count.toString()),
),
],
),
);
},
),
),
),
)
It seems to be working just fine. A non-nested approach works as well:
ScopedModel<ModelA>(
model: ModelA(),
child: ScopedModel<ModelB>(
model: ModelB(),
child: Column(
children: [
ScopedModelDescendant<ModelA>(
builder: (_, __, model) => GestureDetector(
onTap: () => model.inc(),
child: Text(model.count.toString()),
),
),
SizedBox(height: 100.0),
ScopedModelDescendant<ModelB>(
builder: (_, __, model) => GestureDetector(
onTap: () => model.inc(),
child: Text(model.count.toString()),
),
),
],
),
),
)
Upvotes: 13