stateful_props는 상태와 로직을 모듈화되고 조합 가능한 단위로 캡슐화함으로써 StatefulWidgets 간에 재사용 가능한 동작을 가능하게 하는 Flutter 패키지입니다. 관심사의 분리로 코드의 가독성과 유지보수성을 향상시키며, 뼈대 코드 없이 공통 기능을 공유할 수 있게 해줍니다. 예측 가능한 상태 관리와 깔끔한 API 노출을 통해 견고한 컴포넌트 설계를 촉진합니다. 여러 위젯에서 일관된 동작이 필요한 복잡한 UI에 적합합니다.
사용 사례
폼 검증 로직 재사용
위젯 간에 애니메이션 상태 공유
사용자 상호작용 패턴 캡슐화
모듈식 UI 컴포넌트 만들기
공유 위젯 상태 관리
상태가 많은 위젯 테스트 간소화
주요 기능
모듈화된 상태 캡슐화
로직과 UI의 명확한 분리
StatefulWidget 간의 간편한 재사용
개선된 테스트 가능성
최소한의 보일러플레이트
모든 주요 플랫폼 지원
적합한 대상
재사용 가능한 위젯 동작을 찾는 개발자
대규모 플러터 앱을 구축하는 팀
복잡한 상태 상호작용을 가진 프로젝트
일관된 로직이 필요한 UI 구성 요소
위젯 복잡성을 줄이려는 사람
고려 사항
StatefulWidget 라이프사이클에 대한 이해가 필요합니다
상태 있는 로직 재사용에 가장 적합
간단한 경우에 추상화 오버헤드가 추가될 수 있음
완전한 상태 관리 솔루션이 아님
StatefulWidget 전용 사용 사례에 한정
원본 README
연결된 소스 저장소의 README 원문 스냅샷입니다. 최신 내용은 저장소에서 확인하세요.
Provides a simple way to re-use behaviors across StatefulWidgets. Improves readability and robustness of your components.
The package includes pre-made props for many common flutter use cases, these include:
AnimationControllerProp
FocusNodeProp
FutureProp
IntProp, DoubleProp, StringProp, BoolProp
PageControllerProp
ScrollControllerProp
StreamProp
StreamControllerProp
TabControllerProp
TextEditingControllerProp
Generally speaking the built-in props handle common use cases like calling setState when values change and/or calling dispose when required.
To get started, add a StatefulPropsMixin to any StatefulWidget and then use one of the built-in props. In this case we'll use an AnimationControllerProp to manage an AnimationController for us:
You'll notice there are no listeners or builders here... By default the AnimationControllerProp automatically calls setState each time the animation ticks, (disabled by setting autoBuild=false). Also notice that we don't call dispose() on the controller, the prop is handling that for us.
🕹️🕹️ Advanced Usage
Creating New Props
To create a new prop extend the StatefulProp class, and override any of the optional methods:
Don't be intimidated by the number of methods, most props don't override many fields. The most common configuration is to setup some state when constructed, and use dispose to tear it down:
As shown above, if a prop would like to rebuild the state it is attached to it can call manager.scheduleBuild() (which in turn will call setState()). The built-in props all have a autoBuild parameter which automatically adds this listener. This convention is optional but is shown above for reference.
Re-using Stateful Logic
In addition to providing single controllers, props can encapsulate any collection of logic and state that you would like. Either for use across multiple widgets or to isolate for easier testing. For example you could extract common login-related behavior to a LoginBehaviorProp:
This small prop contains 2 controllers, 1 piece of state, 3 helper methods and 2 actions, forming a sort of "micro state" that holds both stateful fields as well as actions which might work on those fields. It also resembles what is commonly referred to as a ViewModel or ViewController.
This fully encapsulated behavior could then be used inside of any StatefulWidget:
In this way props can act as their own reusable behaviors, shared easily across different widgets without potential bugs that come from mixins or the readability issues that come with nested builders.
They also work great as general view controllers, letting you easily separate logic from the view for improved readability.
Flexible & Robust Design
Because each prop is a class, and can nest other props, they fully support inheritence, composition and mixins, allowing you to easily use or extend existing props to create new ones.
For example, in the source code you'll see that a single ValueProp<T> is used as the base class for all the primitives (IntProp, BoolProp, StringProp and DoubleProp) in classic OOP style:
Props can never clash over field or method names because they all have their own self-contained scope. As a result you can stack as many as you need in a single view without concerns. This is in contrast to mixins which will have issues/clashes in this situation.
📖 Background & Motivation
It is difficult to reuse State logic in Flutter. We either end up with a complex and deeply nested build method or have to copy-paste the logic across multiple widgets. For a full discussion, see here: https://github.com/flutter/flutter/issues/51752#.
For example, have you ever written code like this?
Not only is this code overly verbose, but it's also error-prone. If any of the dipose() calls are missed, you will have a bug. If the setState calls is missed, the view will not appear to update. It would be much nicer if each type of object could handle it's own dispose() call, or automatically rebuild the state when changed.
Notice that all the calls to dispose() and setState() have gone away, as each StatefulProp is responsible for disposing itself and (optionally) calling setState when it has changed. This makes the code easier to read and more robust; dispose calls can't get missed, and the work of rebuilding when the counter value changes is done automatically.
While a similar level of robustness could also be achieved using a combination of nested Builder widgets, it would come at the cost of reduced readability.
🐞 Bugs/Requests
If you encounter any problems please open an issue. If you feel the library is missing a feature, please raise a ticket on Github and we'll look into it. Pull request are welcome.