Использование MVVM во Flutter

Исходя в первую очередь из Kotlin фон, мне было довольно сложно заставить мою голову трепетать в самом начале. Это главным образом потому, что Flutter следует декларативному стилю программирования, тогда как Kotlin следует императивному стилю программирования.

Декларативное программирование фокусируется на том, что программа должна выполнять, а не на том, как это достигается.

По этой причине способ программирования на флаттер становится совершенно другим. Приведу пример. В Kotlin, если нам нужно обновить текст TextView, мы можем легко вызвать метод setText.

textView.text = "New text"

Но обновление текста во Flutter совершенно другое, поскольку оно не позволяет нам обновлять текст текстового поля. Вместо этого мы можем перестроить компонент с обновленным текстовым значением, вызвав setState в StatefulWidget.

setState(() {
  newText = "New text";
});

Если вы тоже из Kotlin, то, думаю, вы хорошо знакомы с архитектурой MVVM и liveata, где простой код для извлечения и отображения данных из модели просмотра при управлении другим состоянием выглядит так:

private fun observeSkuListResponse() {
    homeViewModel.skuListResponse.observe(this, Observer {
        when (it.status) {
            Status.LOADING -> {
                binding.loadingLayout.visibility = View.VISIBLE
            }
            Status.SUCCESS -> {
                binding.loadingLayout.visibility = View.GONE
                // perform other operation
            }
            Status.ERROR -> {
               binding.loadingLayout.visibility = View.GONE
               binding.errorLayout.visibility = View.VISIBLE
            }
        }
    })
}

Код довольно прост: если ваше состояние находится в состоянии загрузки, вы отображаете только экран загрузки. Если ваш ответ завершен, вы скрываете экран загрузки и отображаете данные. И если в вашем ответе есть ошибка, вы скрываете все другие представления и отображаете сообщение об ошибке.

Поскольку у флаттера нет концепции видимости, мы должны напрямую передать представление, которое мы хотим отобразить на экране, и снова перерисовать компоненты. Для получения состояния мы будем использовать провайдера.

Краткое введение о провайдере

Провайдер используется для управления состоянием во флаттере. Он состоит из 3-х компонентов

  • Уведомление об изменениях
  • Изменить поставщика уведомлений
  • Потребитель

Средство уведомления об изменениях позволяет понять, что такое модель просмотра для Kotlin в архитектуре MVVM. Это центральная точка, которая управляет состоянием экрана. Если состояние внутри него изменяется, он уведомляет фреймворк, чтобы он снова перестроил экран.

ChangeNotifierProvider предоставляет экземпляр ChangeNotifier в представлении.

Consumer - это виджет, который позволяет нам использовать ChangeNotifier.

Поместите своего потребителя чуть выше кода, который должен получить доступ к уведомителю об изменениях. Это потому, что он перестраивает свои дочерние виджеты, когда есть какие-то изменения, и вы не хотите перестраивать весь экран каждый раз, когда есть какие-то изменения.

Теперь перейдем к коду.

ViewModel

Здесь я создал функцию fetchSkuList (), которая извлекает skus из _homeRepo, а затем устанавливает значение в skuListUseCase. Наконец, он уведомляет об изменении данных, вызывая notifyListener ().

Здесь Response - это класс, который отвечает за передачу информации о состоянии данных, то есть о загрузке, завершении или ошибке, в пользовательский интерфейс.

Эти состояния представлены ResponseState, который представляет собой перечисление, состоящее из:

enum ResponseState{
  LOADING,
  COMPLETE,
  ERROR
}

Экран пользовательского интерфейса

На моем домашнем экране, во-первых, я получил данные в initState. Затем в методе сборки я разместил ChangeNotifierProvider и Consumer прямо под панелью приложений, так как я хочу, чтобы остальная часть экрана обновлялась соответствующим образом при изменении состояния данных. После добавления Consumer оператор switch проверяет текущее состояние: Loading, Error или Complete и соответственно обновляет пользовательский интерфейс.

Экран загрузки и экран ошибок - это простой экран, на котором отображается индикатор выполнения и текстовое сообщение.

Экран загрузки

import 'package:flutter/material.dart';

class LoadingScreen extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return Center(
        child: Container(
      child: Column(
        mainAxisAlignment: MainAxisAlignment.center,
        children: [
          CircularProgressIndicator(),
          SizedBox(
            height: 8,
          ),
          Text(
            'Loading...',
            style: TextStyle(fontFamily: 'Roboto'),
          )
        ],
      ),
    ));
  }
}

Экран ошибки

import 'package:flutter/material.dart';

class ErrorScreen extends StatelessWidget {
  final String msg;

  ErrorScreen(this.msg);

  @override
  Widget build(BuildContext context) {
    return Center(
        child: Container(
            child: Text(
      '$msg',
      style: TextStyle(fontFamily: 'Roboto'),
    )));
  }
}

Вот и все: самый простой способ управлять состоянием с помощью провайдера.

Ознакомьтесь с моим репозиторием на github для получения более подробной информации.



Если статья вам понравилась, не забудьте аплодировать и оставить комментарий. :)