@TemichDev

Flutter как понять Provider?

Добрый день, есть пару вопросов по поводу Flutter, в частности Provider. С какого то момента перешёл на flutter из c#. При изучение и работы со state остановился на provider, не понял концепцию BLoC (пытался, но не понравилось), которую использует некоторые ребята на проекте. Можете объяснить в чём же разница? BLoC же вытекает из того же Provider. Просто концепция Provider, мне очень понятна и легка чем BLoC и другие аналоги.
Второй вопрос, который только не давно возник это, в чём разница использование ValueNotifier и обычного ChangeNotifier?

Вот как например выглядит в сервис который пишу
class ServiceA extends ChangeNotifier{
  List<Order> orders = [];

  Future<void> getOrders() async{
    //запрос и обработка
    orders = data.list(); 
    notifyListeners();
  }

}

// и после можно получить данные в виджетах
// вот так: serviceA.orders;


По идеи можно реализовать по другому

class ServiceB extends ValueNotifier<ServiceBValue>{
  ServiceB() : super(const ServiceBValue());

  Future<void> getOrders() async{
    //запрос и обработка
    value = value.copyWith(orders:data.list());
  }

}

class ServiceBValue{
  final List<Order> orders = [];
  
  const ServiceBValue({this.orders = []});

  ServiceBValue copyWith({List<Order>? orders}){
    return ServiceBValue(
     orders: orders ?? this.orders
    );
  }
}
// и после можно получить данные в виджетах
// вот так: serviceA.value.orders;


Последний метод реализации увидел в библиотеке video_player

Там выглядит контроллер так:
VideoPlayerController extends ValueNotifier<VideoPlayerValue> {
...
...
...
}


В чём разница? В чём подвох? Ни как не могу найти нормального ответа)
Заранее спасибо)
  • Вопрос задан
  • 72 просмотра
Пригласить эксперта
Ответы на вопрос 1
@lekasnet
Студент РТУ МИРЭА - фуллстек, Flutter разработчик
Если говорить совсем просто, базировано и понятно, то любой виджет во Flutter имеет свой уникальный идентификатор, которым подписываются все действия, исполняемые в нём. Provider присутствует и работает постоянно, но обращаться к нем мы можем, если место использования и место прослушивания имеют некоторый общий родительский виджет Provider. По умолчанию Provider позволяет прослушивать один объект, в котором могут быть как функции, так и переменные, таким образом ChangeNotifier уведомляет о любых изменениях объекта, в то время как NotifierValue прослушивает ровно одну переменную, и уведомляет всех слушателей при изменении ТОЛЬКО этой переменной, это сильно упрощает взаимодействие сложных сценариев приложения.

Перейдём от разговоров, к применению, рассмотрим на примере настроек пользователя:

class UserProfileProvider with ChangeNotifier {
  String _username;
  String _email;
  bool _isDarkMode;

  UserProfileProvider(this._username, this._email, this._isDarkMode);

  String get username => _username;
  String get email => _email;
  bool get isDarkMode => _isDarkMode;

  void setUsername(String username) {
    _username = username;
    notifyListeners();
  }

  void setEmail(String email) {
    _email = email;
    notifyListeners();
  }

  void toggleTheme() {
    _isDarkMode = !_isDarkMode;
    notifyListeners();
  }
}

Это пример кода, использующего ChangeNotifier, он прослушивает все поля - _username, _email, _isDarkMode

class DoNotDisturbController {
  ValueNotifier<bool> isDoNotDisturbEnabled = ValueNotifier(false);

  void toggleDoNotDisturb() {
    isDoNotDisturbEnabled.value = !isDoNotDisturbEnabled.value;
  }
}

Допустим есть функция "не беспокоить", влияющая на всё приложение целиком.

Код в котором мы будем это использовать:

class SettingsScreen extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    var userProfileProvider = Provider.of<UserProfileProvider>(context);
    var doNotDisturbController = Provider.of<DoNotDisturbController>(context);

    return Column(
      children: [
        // Для ChangeNotifier
        Text(userProfileProvider.username),
        Switch(
          value: userProfileProvider.isDarkMode,
          onChanged: (value) {
            userProfileProvider.toggleTheme();
          },
        ),

        // Для ValueNotifier
        ValueListenableBuilder<bool>(
          valueListenable: doNotDisturbController.isDoNotDisturbEnabled,
          builder: (context, value, child) {
            return Switch(
              value: value,
              onChanged: (newVal) {
                doNotDisturbController.toggleDoNotDisturb();
              },
            );
          },
        ),
      ],
    );
  }
}

Тут мы можем увидеть, что при изменении ChangeNotifier обновляются сразу все поля и состояния, которые прослушивают КЛАСС, использующий провайдер. В случае с ValueNotifier мы прослушиваем только одну переменную/функцию, что полезно для скорости и простоты использования, НО ограничивает возможности отзывчивости приложения, т.к. мы не обновим целый объект. ChangeNotifier полезен для использования в разветвлении виджетов, применения изменения ряда параметров и работы с объектами в целом, ValueListener полезен для изменения одного конкретного параметра для всего кода. Часто можно избежать применения ValueListener через изменение состояния, т.к. и Provider и setState влияют на данные в BuildContext, но если первый протягивает изменение до слушателя, и меняет состояние из него, то второй применяет изменения на месте.

Таким образом, ChangeNotifier и ValueNotifier обеспечивают разные уровни абстракции и удобства для различных сценариев управления состоянием в Flutter, помогая вам поддерживать чистую и управляемую архитектуру состояния.
Ответ написан
Комментировать
Ваш ответ на вопрос

Войдите, чтобы написать ответ

Войти через центр авторизации
Похожие вопросы