MOCKSTACKS
EN
Questions And Answers

More Tutorials









Flutter State & Scoped Model Approach

Approach

This approach removes most of the requirements to use Stateful Widgets, enabling the user to use Stateless Widgets instead in many cases.

ScopedModel has been mentioned in many articles as an alternative to just using InheritedWidget. At first sight, it looks like the ScopedModel package is basically InheritedWidget, only made easier to use.

Package

ScopedModel is a Dart package and it is available here: https://pub.dartlang.org/packages/scoped_model
As it is a package you will have to install it:
https://pub.dartlang.org/packages/scoped_model - -installing-tab-

Package Readme

The package README.md file includes the following text:
A set of utilities that allow you to easily pass a data Model from a parent Widget down to its descendants. In addition, it also rebuilds all of the children that use the model when the model is updated. This library was originally extracted from the Fuchsia codebase.
This package provides three main classes:

1. Model

You will extend this class to create your own Models, such as SearchModel or UserModel.
You can listen to Models for changes!

2. ScopedModel Widget.

If you need to pass a Model deep down your Widget hierarchy, you can wrap your Model in a ScopedModel Widget.
This will make the Model available to all descendant Widgets

3. ScopedModelDescendant Widget.


Use this Widget to find the appropriate ScopedModel in the Widget tree.
It will automatically rebuild whenever the Model notifies that change has taken place.

Multiple Models

At first glance, it looks as if this package allows the user to use multiple State Models. This certainly makes it a better candidate for working with larger applications. You could have User data in one model, Transaction data in another etc.

Exercise – ‘state_and_scoped_model’

The code below is not perfect by any means (you can add the same car twice and when you tap on it, it selects both) but it demonstrates how to get an app up and working with ScopedModel and how you can maintain separate states in separate models.
In this exercise, I use the ScopedModel to handle two separate state
models:
1. a list of cars (to which we can add cars)
2. the currently selected car (which you can change by tapping on a car).


There is more code for you to copy and paste in this example.
However, this app does more than some of the previous examples: it allows you to add cars and allows you to select cars.

Step 1 – Create Default Flutter App


Follow the instructions in Generate Your First App Leave project open.

Step 2 – Replace Application Code


Replace contents of file ‘main.dart’ in folder ‘lib’ with the following:

import 'package:flutter/material.dart';
import 'package:scoped_model/scoped_model.dart';
void main() => runApp(new CarAppWidget());
class Car {
String _make;
String _model;
String _imageSrc;
Car(this._make, this._model, this._imageSrc);
operator ==(other) =>
(other is Car) && (_make == other._make) && (_model == other._model);
int get hashCode => _make.hashCode ^ _model.hashCode ^
_imageSrc.hashCode;
}
class CarListModel extends Model {
List<Car> _carList = [
Car(
"Bmw",
"M3",
"Https://media.ed.edmundsmedia.com/bmw/m3/2018/oem/2018_bmw_m3_sedan_base_fq_oem_4_150.jpg",
),
Car(
"Nissan",
"GTR",
"Https://media.ed.edmunds-media.com/nissan/gt-r/2018/oem/2018_nissan_gtr_coupe_nismo_fq_oem_1_150.jpg",
),
Car(
"Nissan",
"Sentra",
"Https://media.ed.edmundsmedia.com/nissan/sentra/2017/oem/2017_nissan_sentra_sedan_srturbo_fq_oem_4_150.jpg",
)
];
List<Car> get carList => _carList;
void add(String make, String model, String imageSrc) {
_carList.add(Car(make, model, imageSrc));
notifyListeners();
}
}
class CarSelectionModel extends Model {
Car _selectedCar;
Car get selectedCar => _selectedCar;
void set selectedCar(Car selectedCar) {
_selectedCar = selectedCar;
notifyListeners();
}
bool isSelected(Car car) {
if (_selectedCar == null) {
return false;
} else {
return car == _selectedCar;
}
}
}
class CarAppWidget extends StatelessWidget {
@override
Widget build(BuildContext context) {
return new MaterialApp(
title: 'Car App',
theme: new ThemeData(
primarySwatch: Colors.blue,
),
home: ScopedModel<CarListModel>(
model: CarListModel(),
child: ScopedModel<CarSelectionModel>(
model: CarSelectionModel(),
child: CarAppLayoutWidget(title: 'Cars'))));
}
}
class CarAppLayoutWidget extends StatelessWidget {
CarAppLayoutWidget({Key key, this.title}) : super(key: key);
final String title;
_addCar(BuildContext context) {
ScopedModel.of<CarListModel>(context, rebuildOnChange: true).add(
"Subaru",
"WRX",
"Https://media.ed.edmunds-media"
".com/subaru/wrx/2018/oem/2018_subaru_wrx_sedan_stilimited_s_oem_1_150"
".jpg");
}
String _calculateSelectedCarName(BuildContext context) {
Car selectedCar =
ScopedModel.of<CarSelectionModel>(context, rebuildOnChange: true)
.selectedCar;
if (selectedCar == null) {
return "No car selected.";
} else {
return "Selected: ${selectedCar._make} ${selectedCar._model}";
}
}
@override
Widget build(BuildContext context) {
return new Scaffold(
appBar: new AppBar(
title: new Text(title),
),
body: Center(child: CarListWidget()),
persistentFooterButtons: <Widget>[
Text(_calculateSelectedCarName(context)),
IconButton(
icon: Icon(Icons.add),
onPressed: () {
_addCar(context);
}),
]);
}
}
class CarListWidget extends StatelessWidget {
@override
Widget build(BuildContext context) {
final carList =
ScopedModel.of<CarListModel>(context, rebuildOnChange: true).carList;
List<CarWidget> carWidgets = carList.map((Car car) {
return CarWidget(car);
}).toList();
return new ListView(children: carWidgets);
}
}
class CarWidget extends StatelessWidget {
CarWidget(this._car) : super();
final Car _car;
_buildCarWidget(context, child, CarSelectionModel selectionModel) {
return GestureDetector(
onTap: () => selectionModel.selectedCar = _car,
child: Padding(
padding: EdgeInsets.all(20.0),
child: Container(
decoration: BoxDecoration(
border: Border.all(),
color: selectionModel.isSelected(_car)
? Colors.blue
: Colors.white),
padding: EdgeInsets.all(20.0),
child: Center(
child: Column(children: <Widget>[
Text('${_car._make} ${_car._model}',
style: TextStyle(fontSize: 24.0)),
Padding(
padding: EdgeInsets.only(top: 20.0),
child: Image.network(_car._imageSrc))
])))));
}
@override
Widget build(BuildContext context) {
return ScopedModelDescendant<CarSelectionModel>(
builder: (context, child, selectionModel) =>
_buildCarWidget(context, child, selectionModel));
}
}

Step 3 – Open Emulator & Run


Follow the instructions in Open Android Emulator & Run Your First App
If you tap on the ‘+’ button at the bottom it adds another car.
If you tap on a car it selects the car (adding a blue background) and sets the text of the selected car at the bottom.

Conclusion

In this page (written and validated by ) you learned about Flutter State & Scoped Model Approach . What's Next? If you are interested in completing Flutter tutorial, your next topic will be learning about: Flutter State and BLoCs w Streams Approach.



Incorrect info or code snippet? We take very seriously the accuracy of the information provided on our website. We also make sure to test all snippets and examples provided for each section. If you find any incorrect information, please send us an email about the issue: mockstacks@gmail.com.


Share On:


Mockstacks was launched to help beginners learn programming languages; the site is optimized with no Ads as, Ads might slow down the performance. We also don't track any personal information; we also don't collect any kind of data unless the user provided us a corrected information. Almost all examples have been tested. Tutorials, references, and examples are constantly reviewed to avoid errors, but we cannot warrant full correctness of all content. By using Mockstacks.com, you agree to have read and accepted our terms of use, cookies and privacy policy.