Scoped Model은 Flutter용 가벼운 상태 관리 솔루션으로, 위젯 트리 전체에서 반응형 모델 공유를 가능하게 합니다. 모델 객체를 위젯 계층 구조 아래로 전달하는 간단한 방법을 제공하여 자식 위젯이 모델에 반응적으로 액세스하고 업데이트할 수 있도록 합니다. 이 패키지는 스코프 기반 접근 방식을 사용하여 모델이 변경될 때 관련된 위젯만 다시 빌드되도록 보장함으로써 성능을 향상시킵니다. Provider나 Bloc과 같은 완전한 상태 관리 솔루션이 과도할 수 있는 소규모부터 중간 규모의 앱에 적합합니다. 모든 주요 플랫폼을 지원하며 기존 Flutter 프로젝트에 원활하게 통합됩니다.
사용 사례
작은 앱에서의 간단한 상태 관리
여러 위젯 간에 데이터 공유
모델 변경에 따른 반응형 UI 업데이트
폼 상태 관리
테마 또는 사용자 선호 사항 구현
주요 기능
반응형 모델 전파
최소한의 보일러플레이트
크로스 플랫폼 지원
기존 코드와의 간편한 통합
모델 변경 시 위젯 자동 재구성
적합한 대상
초보자 플러터 개발자
경량 상태 요구 사항
Prototyping
중등도 상태 복잡성의 앱
무거운 상태 관리 프레임워크를 피하는 프로젝트
고려 사항
대규모 애플리케이션에는 적합하지 않음
제한된 내장 디버깅 도구
Riverpod과 같은 고급 솔루션보다 유연성이 낮음
복잡한 상태 전이의 수동 처리
모델 업데이트에 내장된 오류 처리 없음
원본 README
연결된 소스 저장소의 README 원문 스냅샷입니다. 최신 내용은 저장소에서 확인하세요.
scoped_model
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 Library provides three main classes:
The Model class. You will extend this class to create your own Models, such as SearchModel or UserModel. You can listen to Models for changes!
The 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.
The 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.
This library is built upon several features of Flutter:
The Model class implements the Listenable interface
AnimationController and TextEditingController are also Listenables
The Model is passed down the Widget tree using an InheritedWidget. When an InheritedWidget is rebuilt, it will surgically rebuild all of the Widgets that depend on its data. No need to manage subscriptions!
It uses the AnimatedBuilder Widget under the hood to listen to the Model and rebuild the InheritedWidget when the model changes.
Examples
Counter App - Introduction to the tools provided by Scoped Model.
Todo App - Shows how to write a Todo app with persistence and tests.
Usage
Let's demo the basic usage with the all-time favorite: A counter example!
// Start by creating a class that holds some view the app's state. In// our example, we'll have a simple counter that starts at 0 can be // incremented.//// Note: It must extend from Model. classCounterModelextendsModel{
int _counter = 0;
intget counter => _counter;
voidincrement() {
// First, increment the counter
_counter++;
// Then notify all the listeners.notifyListeners();
}
}
// Create our App, which will provide the `CounterModel` to // all children that require it! classCounterAppextendsStatelessWidget{
@overrideWidgetbuild(BuildContext context) {
// First, create a `ScopedModel` widget. This will provide // the `model` to the children that request it. returnnewScopedModel<CounterModel>(
model: newCounterModel(),
child: newColumn(children: [
// Create a ScopedModelDescendant. This widget will get the// CounterModel from the nearest ScopedModel<CounterModel>. // It will hand that model to our builder method, and rebuild // any time the CounterModel changes (i.e. after we // `notifyListeners` in the Model). newScopedModelDescendant<CounterModel>(
builder: (context, child, model) => newText('${model.counter}'),
),
newText("Another widget that doesn't depend on the CounterModel")
])
);
}
}
Finding the Model
There are two ways to find the Model provided by the ScopedModel Widget.
Use the ScopedModelDescendant Widget. It will find the Model and run the
builder function whenever the Model notifies the listeners.
Use the ScopedModel.of static method directly. To make this method more readable for frequent access, you can consider adding your own of method to your own Model classes like so:
classCounterModelextendsModel{
// .../// Wraps [ScopedModel.of] for this [Model].staticCounterModelof(BuildContext context) =>
ScopedModel.of<CounterModel>(context);
}
Listening to multiple Models in a build function
In many cases, it makes sense to split your Models apart into logical components
by functionality. For example, rather than having an AppModel that contains
all of your application logic, it can often make more sense to split models
apart into a UserModel, a SearchModel and a ProductModel, for example.
However, if you need to display information from two of these models in a single
Widget, you might be wondering how to achieve that! To do so, you have two
options:
Use multiple ScopedModelDescendant Widgets
Use multiple ScopedModel.of calls. No need to manage subscriptions,
Flutter takes care of all of that through the magic of InheritedWidgets.
classCombinedWidgetextendsStatelessWidget{
@overrideWidgetbuild(BuildContext context) {
final username =
ScopedModel.of<UserModel>(context, rebuildOnChange: true).username;
final counter =
ScopedModel.of<CounterModel>(context, rebuildOnChange: true).counter;
returnText('$username tapped the button $counter times');
}
}