제공자 원리 소개 및 실제 전투(MVVM)(아래)

징웬 챌린지 참여 19일째 되는 날입니다. 자세한 내용은 다음을 확인하세요.업데이트 챌린지

MVVM이란 무엇이며 장점

MVVM은 Model-View-ViewModel의 줄임말입니다. 본질적으로 MVC의 개선된 버전입니다. MVVM은 뷰의 상태와 동작을 추상화하여 뷰 UI와 비즈니스 로직을 분리할 수 있습니다. 물론 ViewModel은 이미 이러한 작업을 수행했으며 Model의 데이터를 가져오고 콘텐츠를 표시해야 하므로 View와 관련된 비즈니스 로직을 처리하는 데 도움이 될 수 있습니다.

이점

MVVM 패턴은 MVC 패턴과 동일하며 주요 목적은보다(보기) 및 모델(모델),

1. 낮은 결합 . View는 Model과 독립적으로 변경 및 수정될 수 있습니다. ViewModel은 다른 "View"에 바인딩될 수 있습니다. View가 변경되면 Model은 변경되지 않은 상태로 유지되고 Model이 변경되면 View도 변경되지 않은 상태로 유지될 수 있습니다.

2. 재사용성 . ViewModel에 일부 보기 논리를 넣고 많은 보기에서 이 보기 논리를 재사용하도록 할 수 있습니다.

3. 독립적인 개발 . 개발자는 비즈니스 로직 및 데이터(ViewModel) 개발에 집중할 수 있고, 디자이너는 페이지 디자인에 집중할 수 있으며, Expression Blend를 사용하면 인터페이스를 쉽게 디자인하고 xaml 코드를 생성할 수 있습니다.

4. 테스트 가능 . 인터페이스는 항상 테스트하기 어려웠고 테스트는 ViewModel에 대해 작성될 수 있습니다.

View는 ViewModel에 바인딩한 다음 몇 가지 명령을 실행하여 ViewModel에서 작업을 요청합니다. 차례로 ViewModel은 모델과 통신하여 UI에 대한 응답으로 업데이트하도록 지시합니다. 따라서 애플리케이션용 UI를 매우 쉽게 구축할 수 있습니다.

이미지

Flutter에서 MVVM 패턴의 여러 가지 방법

타사 패키지를 사용하지 않을 때 공식도 좋은 선택을 제공합니다. 즉 StatefulWidget, UI를 새로 고치기 위해 상태를 변경해야 할 때 setState()메서드를 호출하기만 하면 됩니다.

이 방법은 간단하고 직접적이며 MVVM 패턴으로도 이해할 수 있지만 View와 Model은 여전히 ​​함께 결합되어 있고 ViewModel은 적절한 역할을 하지 않습니다. 프로젝트가 커질수록 코드 setState()는 점점 더 혼란스러워지고 때로는 호출하는 것을 잊어버려 setState()문제를 찾는 데 많은 시간을 낭비하게 됩니다.

또한 초기에 공식적으로 제공되는 상태 관리 모드를 라고 BLOC합니다. 이 방법은 타사 패키지에 의존하고 rxDartStream 방식으로 문제를 잘 해결합니다 setState(). 그러나 이러한 종류의 학습은 어렵고 Flutter를 처음 접하는 사람들에게 친숙하지 않습니다. 나중에 상태 관리 및 종속성 주입을 위한 고급 도구인 타사 라이브러리가 등장했으며 Provider배우기 쉽고 이해하기 쉽기 때문에 현재 첫 번째 선택으로 권장됩니다 Provider.

ViewModel을 통한 상태 관리

MVVM 모드에서 APP를 개발하기 때문에 ViewModel은 필수입니다. 즉, state 속성이 변경되면 그에 상응하는 변경을 하기 위해 UI(즉, View 레이어)가 필요합니다.

상태가 변경되었는지 여부를 모니터링하는 데 도움 이 되는 Provider가 ChangeNotifierProvider있으며 해당 하위 매개변수는 Consumer상태를 사용하는 데 도움이 되는 변경입니다. 평신도의 관점에서 위젯의 빌드 메소드는 UI를 새로 고치기 위해 여기에서 호출됩니다.

그렇다면 상태 변경 알림을 트리거할 위치는 어디입니까? 대답은 를 사용 ChangeNotifier하는 것입니다. notifyListeners()메서드가 호출되면 리스너에 새로 고침을 알릴 수 있습니다 ChangeNotifierProvider.

@override
Widget build(BuildContext context) {
  return ChangeNotifierProvider<T>(
    child: Consumer<T>(
      //Widget的builder方法与child
      builder: widget.builder,
      child: widget.child,
    ),
    create: (BuildContext context) {
      //这里是我们的ViewModel,一个ChangeNotifier
      return model;
    },
  );
}
复制代码

이제 생산자와 소비자가 모두 있으므로 MVVM 패턴을 완성할 수 있습니다.

기본 페이지 로직 분석

일반적으로 페이지가 로드될 때 로드 페이지가 표시되고 로드 후에 데이터가 성공적으로 표시되고 실패하면 실패한 페이지가 표시됩니다.

![스크린샷 2021-06-20 11.47.16 PM](/Users/liaoyp/Desktop/Screenshot 2021-06-20 PM 11.47.16.png)

따라서 페이지 상태를 열거하십시오.

enum ViewState { Idle, Busy,Error }
复制代码

ViewModel은 페이지 상태 속성이 변경된 후 ui를 업데이트하고 일반적으로 notifyListeners를 호출하고 이 단계를 BaseModel로 이동합니다.

class BaseModel extends ChangeNotifier {
  ViewState _state = ViewState.Loading;
 
  ViewState get state => _state;
 
  void setState(ViewState viewState) {
    _state = viewState;
    notifyListeners();
  }
}
复制代码

ChangeNotifierProvider는 ui에 Model을 제공하고 ui를 Consumer로 업데이트해야 합니다. 그래서 BaseView에도 내장했습니다.


enum ViewState { Loading, Success, Failure, None }
 
class BaseModel extends ChangeNotifier {
  ViewState _state = ViewState.None;
 
  ViewState get state => _state;
 
  void setState(ViewState viewState) {
    _state = viewState;
    notifyListeners();
  }
}
 
class BaseWidget<T extends ChangeNotifier> extends StatefulWidget {
  final Widget Function(BuildContext context, T model, Widget child) builder;
  final T model;
  final Widget child;
  final Function(T) onModelReady;
 
  BaseWidget({
    Key key,
    this.builder,
    this.model,
    this.child,
    this.onModelReady,
  }) : super(key: key);
 
  _BaseWidgetState<T> createState() => _BaseWidgetState<T>();
}
 
class _BaseWidgetState<T extends ChangeNotifier> extends State<BaseWidget<T>> {
  T model;
 
  @override
  void initState() {
    model = widget.model;
 
    if (widget.onModelReady != null) {
      widget.onModelReady(model);
    }
 
    super.initState();
  }
 
  @override
  Widget build(BuildContext context) {
    return ChangeNotifierProvider<T>(
      create: (BuildContext context) => model,
      child: Consumer<T>(
        builder: widget.builder,
        child: widget.child,
      ),
    );
  }
}
复制代码

캡슐화된 기본 클래스로 로그인 페이지를 완료합니다.


class MyHomePage extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return BaseWidget<LoginViewModel>(
      model: LoginViewModel(loginServive: LoginServive()),
      builder: (context, model, child) => Scaffold(
        appBar: AppBar(
          title: Text('provider'),
        ),
        body: Column(
          children: <Widget>[
            model.state == ViewState.Loading
                ? Center(
                    child: CircularProgressIndicator(),
                  )
                : Text(model.info),
            FlatButton(
                color: Colors.tealAccent,
                onPressed: () => model.login("pwd"),
                child: Text("登录")),
          ],
        ),
      ),
    );
  }
}
 
/// viewModel
class LoginViewModel extends BaseModel {
  LoginServive _loginServive;
  String info = '请登录';
 
  LoginViewModel({@required LoginServive loginServive})
      : _loginServive = loginServive;
 
  Future<String> login(String pwd) async {
    setState(ViewState.Loading);
    info = await _loginServive.login(pwd);
    setState(ViewState.Success);
  }
}
 
/// api
class LoginServive {
  static const String Login_path = 'xxxxxx';
 
  Future<String> login(String pwd) async {
    return new Future.delayed(const Duration(seconds: 1), () => "登录成功");
  }
}
复制代码

인용하다

examplecode.cn/2020/05/09/…

추천

출처juejin.im/post/6975904656245391373