Pull to refresh
10
0

Моя литера Т стремится к равнозначности линий

Send message

Хорошего настроения и интересных проектов! С Новым годом, друзья! ?

А есть ещё такая штука, как rfw. Это имеет какие-то общие грани с вашим проектом?

Спасибо большое за материал! Очень полезно и ёмко.

Кстати, когда добавят md preview без костылей вида Choose Boot Java Runtime...? И даже этот костыль перестал работать в Android Studio Iguana

Приношу извинения, что вы были вынуждены клацнуть и попасть сюда. Это ограничение формата в 1500 символов в который раз не даёт мне возможности полностью раскрыть свои мысли с помощью кода. Раздражает, ведь нет способа сделать это ещё ёмче и лаконичней (субъективный взгляд), а на полноценную статью этого материала недостаточно.

Собственно, код ниже:

class Wrapper {
  Wrapper(this.i);
  int i;
}

int increase(int i) => i + 1;

main() {
  final w = Wrapper(0);
  w.i = increase(w.i);
  print(w.i); // 1
}

Непосредственное нахардкоживание late final во вьюмоделях может означать только одно - что нам делать, когда придёт время тестов. И честно, не увидел в readme пакета и не услышал в статье ни одного слова о тестировании: как и возможно ли?

Далее, примеры счётчиков настолько заезженные и банальные, что не отражают ровным счётом ничего и плохо пахнут. В противовес вашему примеру, пример на ValueNotifier(соблюдая именования и стиль):

import 'package:flutter/material.dart';

class CounterViewModel extends ValueNotifier<int> {
  CounterViewModel() : super(0);

  void increment() => value++;
}

// Внутри StatefulWidget
final vm = CounterViewModel();

// ...

@override
Widget build(BuildContext context) {
  return ValueListenableBuilder<int>(
    valueListenable: vm,
    builder: (context, count, _) {
      return ElevatedButton(
        onPressed: vm.increment,
        child: Text("$count"),
      );
    },
  );
}

// ...

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

Пункт "Почему не использовать уже существующее решение?" откровенно слаб и очень хочется его реального раскрытия. И вот почему:

Riverpod:

  • Не нравится подход со смешиванием DI и State Management'а.

Однако заметьте, что в реальном приложении придётся использовать и то и другое (под каким бы соусом не был подан DI). В данном случае, мы бы воспользовались vessel + beholder. А есть ли смысл импортировать два пакета вместо одного?

  • Засорение глобального скоупа

Тем, что мы имеем один ProviderScope, в котором содержится один ProviderContainer, который и содержит состояния наших провайдеров? Ну я вам скажу, что ещё можно поиграться с UncontrolledProviderScope и контейнеры создавать независимо. А ещё использовать ProviderScope.overrides и ProviderScope.parent для переопределения для конкретной ветки.

Тяжело масштабировать

Пожалуй это самое нелепое обвинение в сторону Riverpod. Начну с того, что StateNotifier уже устаревшая концепция. Используйте (Async)NotifierProvider. И комбинируйте состояния ровно также, как вы это делаете в случае с вашей библиотекой (ваш последний пример не ясен, возможно он содержит ошибку в именовании SearchUsersViewModel|UsersViewModel):

final selectedProjectId = StateProvider((_) => 32);

final users = Provider((_) => <User>[]);

final filteredUsers = Provider((ref) {
  final projectId = ref.watch(selectedProjectId);
  return ref
      .watch(users)
      .where((user) => user.projects.contains(projectId))
      .toList();
});

Это классический стиль. При необходимости дополнительного namespase перенесите провайдеров в статические поля ваших ViewModel, либо используйте Notifier, если планируется управление над получившимся состоянием:

class FilteredUsersNotifier extends Notifier<List<User>> {
  late List<User> _users;
  late int _projectId;

  @override
  List<User> build() {
    _users = ref.watch(users);
    _projectId = ref.watch(selectedProjectId);
    return _users.where((user) => user.projects.contains(_projectId)).toList();
  }

  User findById(id) {/*делайте что-то*/}
}

Опять же, такие примеры выглядят глупо из-за отсутствия реальной задачи.

Bloc:

  • Определение более-менее сложных состояний требует кодогенерации copyWith.

copyWith используется, когда модели являются иммутабельными и стейт-менеджер основан на сравнении hashcode для обновления состояния. Как в этом плане работает beholder? Если он основан на мутабельном состоянии, то как избежать лишних перестроек, когда данные на самом деле не изменились, но их присвоение произошло?

  • Субъективно, но в больших проектах именование Event'ов и State'ов начинает напоминать энтерпрайз Java: class RefreshPostsHomeScreenEvent

А как мы это избегаем здесь? ModelView превращаются в повелители всего и вся с сотнями методов и сотнями состояний?

AsyncState, AsyncValue, Result.guard - это всё мне что-то очень сильно напоминает на подход в R..?, ну ладно, окей.

---

Подводя черту, ваш стейт-менеджер может намного больше, под капотом там всё действительно интересно. Хабр хочет внутренностей и живых примеров приложений, основанных на данном пакете. Быть может, стоит показать конкретный пример, на котором сильно забуксуют имеющиеся менеджеры, а ваш решит проблему с лёгкостью. Пишите, пожалуйста, ещё. Независимо от всего, вы молодец, проделали большую работу, а полученный опыт может послужить хорошим фундаментом для будущих улучшений и новых пакетов. ?

Здравствуйте! Спасибо за такое прекрасное издание (и, конечно же, за ожидаемое переиздание) - читается на одном дыхании! Правда в старом издании мне больше симпатизировал Comic Sans нежели чем скучный и невероятно острый Arial Narrow для примеров кода.

Появились полноценные лабораторные работы, красивые скриншоты, как например в главе "Установка и настройка рабочего окружения", раздел с Record, раздел с "dynamic vs Object", некоторое количество лайфхаков по типу чтения с клавиатуры - очень круто! Это прекрасное издание для новичков было, а теперь ещё и с заряженным dart 3. Очень жду раздел про необъятный patterns :) При чём мне очень нравится, что это больше похоже на приятную и строгую мини-энциклопедию по языку, нежели чем на растянутые мануалы субъективщины. Продолжайте в том же духе :)

Отправил вам небольшой заряд мотивации, а-ля "немного на хлеб насущный". Хорошего дня ?

Да, миграция на dart 3 оказалась вполне приятной. Возможно, в силу специфичности моих проектов, но:

  • в приложении Weather Today это выглядело буквально вот так commit. При чём проект не обновлялся полгода и только поэтому я сделал такой непринуждённый обобщённый коммит, обновив сразу все доступные зависимости. Но для dart 3 этого не требовалось. Всё сразу заработало без танцев

  • в пакете weather_pack тоже всё прошло легко. Но проект совсем простой, это плохое сравнение с бизнес кейсами

  • прямо сейчас происходит миграция одного чуть бОльшего приложения чем погодка, с кучей устаревших зависимостей (даже форков для которых нет) и с флагом --no-sound-null-safety в командной строке при запуске/билде. В текущую минуту уже избавились от флага и на стадии "а не накатить ли dart 3", но:

    • если форков нет, нужно самому править пакеты, что занимает много времени

    • лучше потратить время на работу с кодовой базой, которая тоже не ахти (включая архитектуру)

    • в этом проекте нет нужды ни в pattern matching, ни в switch expressions, ни в модификаторах классов

Плюс в том, что некоторые популярные пакеты уже перешли на новую версию и мы этого можем даже не замечать, если автор указал минорное|патч повышение версии. И также жизнь облегчает то, что pub tool пока что позволяет ставить пакеты даже с ограничением а-ля sdk: '>=2.14.0 <3.0.0':

Dart’s pub tool allows resolution even when the upper bound is limited to versions below 3.0.0. For example, a package with the following constraint will be allowed to resolve with a Dart 3.x SDK, as pub will re-interpret the upper-constraint <3.0.0 as <4.0.0 when the lower constraint is 2.12 or higher

Опять же, когда я попробовал switch выражения, то забыл об идиотском обходе используя анонимные функции или "большом разглагольствовании". Когда я попробовал сопоставление (вместе с sealed) - пришлось привыкать к новому синтаксису, но оказалось вполне удобным. Records оказались также весьма кстати. Модификаторы - это для ультра проектов, либо для повышенного удобства использования пакетов (для авторов пакетов). А вот различные виды сопоставлений не могу освоить до сих пор (имеется ввиду, чтобы их восприятие стало для меня родным и удобным). Лично мне сейчас очень не хватает data-классов, метапрограммирования и нормального ide-рефакторинга.

Спасибо за статью, посылы несёте праведные! Хочу обратить внимание на некоторые вещи:

  1. У вас слетели отступы (для dart обычно принят отступ в два пробела)

  2. Чем продиктована необходимость использовать префикс I для именования интерфейсов? Былой разработкой на java?)) Есть мнение, что лучше именовать реализации интерфейсов, добавляя постфикс Impl (вот так: ClipboardServiceImpl -> ClipboardService), тогда ваша бизнес-логика не имеет лишней семантической сложности.

  3. Стоит заметить, что для простых приложений лучше воспользоваться как раз таки if-else реализациями, нежели чем плодить абстракции абстракций интерфейсов :) В данном случае может показаться, что сделать интерфейс для сервиса достаточно просто. Но вспомните реальные кейсы с необходимой реализацией 10-20 методов. Мой вердикт - by design. Внедряем, если есть неотрицательная вероятность "бизнес захочет"

  4. Используйте interface class или даже abstract interface class вместо abstract class, чего стесняться, если dart 3 разрешает

Это была ошибка. Правильный вариант вот такой:

    (int, bool) onlyPos;
    (String name,) onePos;
    ({String name}) oneRec;
    (int, String name, {bool count}) posAndRec;

    onlyPos.$1;
    onlyPos.$2;
    onePos.$1;
    oneRec.name;
    posAndRec.$1;
    posAndRec.$2;
    posAndRec.count;

Тип record, содержащий ровно одно позиционное поле, требует завершающей запятой. Для именованных полей это не имеет значение.

Получение позиционных полей идёт с $1 и далее.

Также стоит заметить, что для позиционных полей можно не указывать имя. Оно влияет только на документацию (и понимание кода).

Актуальную спецификацию можете посмотреть здесь - Records Feature Specification

Спасибо за материал! Имейте ввиду, что на текущий момент новый способ не работает под web и windows:

[AppLifecycleListener] Events not triggered #130566

Спасибо! Очень интересно было почитать про такое :)

Спасибо за статью! Стоит отметить, что у go_router очень изменчивое api от версии к версии, которое пока что не стабилизировалось. Но пакет сильно значимый. Хочется верить, что получится учесть прошлый костыльный опыт по навигации и сделать favorite-пакет (я не про плашку в pub.dev)

Воу, прочёл с удовольствием, спасибо за материал. Как по мне, late накладывает свои ограничения, только если код изначально запутан и плох. Когда late переменная инициализируется где-то там и когда-то потом и эта самая инициализация находится на 100 строк ниже.

Во всех остальных случаях, в том числе в конструкторах и в State виджетах, это более чем уместно, нежели чем использовать nullable и терять final.

Излюбленным использованием является отложенное создание "дорогого" объекта, если он таки может и не понадобиться в итоге. Это особенно хорошо выглядит в локальных местах, где nullable семантически режет глаза, а очередная проверка на null сбивает с толку.

Что же касается имеющейся чисто технической информации, то вам нужно поработать над подачей и оформлением. Минимально – перечитать ещё раз и обратить внимание на семантические и синтаксические ошибки, применить `dart format` к коду и выбрать язык dart для подсветки синтаксиса, проследить полноту информации ваших скриншотов. Сейчас читать это близко к невозможному.

Если же мы возвращаем void, а не значение вместо value в then ставим прочерк(_).

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

Спасибо, это было интересное, хотя и короткое путешествие в мир оптимизаций. Как говорится, пишите ещО :)

Если сервис дёргает метод репозитория, значит он связан. На диаграмме этого не показано, вот в чём вопрос :)

Information

Rating
Does not participate
Registered
Activity