Elementary-team / flutter-elementary

This is a home to a family of Elementary library packages.
MIT License
133 stars 45 forks source link

Passing wm inside child widgets? #98

Closed Tamerlanchiques closed 2 months ago

Tamerlanchiques commented 4 months ago

I have ElementaryWidget that contains Scaffold inside it and I decided to spread Scaffold into AppBar, Body and Bottom classes like this:


< some  other imports … >

part 'widgets/chat_page_app_bar.dart';
part 'widgets/chat_page_body.dart';
part 'widgets/chat_page_bottom_bar.dart';

class ChatPageWidget extends ElementaryWidget<IChatPageWidgetModel> {
  final String title;

  const ChatPageWidget({
    Key? key,
    this.title = 'Чат с диспетчером',
    WidgetModelFactory wmFactory = defaultChatPageWidgetModelFactory,
  }) : super(wmFactory, key: key);

  @override
  Widget build(IChatPageWidgetModel wm) {
    return GestureDetector(
        onTap: wm.onTapOutsideMessageTextField,
        child: Scaffold(
          resizeToAvoidBottomInset: true,
          appBar: ChatPageAppBar(title: title),
          body: Column(
            children: [
              Expanded(
                  child: ChatPageBody(
                wmFactory,
              )),
              ChatPageBottomBar(wmFactory)
            ],
          ),
        ));
  }
}

These parts extends Elementary Widget as well. Of course, I faced with issue when new Widget Model instance creates each time instead of using the same instance created in parent ChatPageWidget. I could convert parts to simple Stateless widgets and pass wm variable in their constructors but in this case I have access to context leaks to its build method that breaking single source of truth of WidgetModel.

Is there way to keep AppBar, Body and BottomBar widgets as ElementaryWidgets with build(IChatPageWidgetModel wm) function instead of build(BuildContext context) one?

vlkonoshenko commented 3 months ago

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

Tamerlanchiques commented 3 months ago

@vlkonoshenko Привет.

Хм. По идее, да. Можно для каждой части Scaffold'a сделать свою вью-модель.

Получить связки вида ChatPageAppBarWidget – ChatPageAppBarWidgetModel, ChatPageBodyWidget – ChatPageBodyWidgetModel и ChatPageBottomWidget – ChatPageBottomWidgetModel и управлять этим одной моделью ChatPageModel.

Правда, вижу тут местами момент с дубликацией кода. Например, я хочу слушать из Model слоя стрим переменной «можно/нельзя писать в чате» и в Body хочу написать заглушку, «вы не можете писать в чате…» и, при этом, заблокировать возможность писать сообщения и нажимать кнопку «отправить» в Bottom. Получается, нужно будет делать логику управления подпиской и в BodyWidgetModel и в BottomWidgetModel. Но, может быть, это не так страшно и зато всё будет лучше упорядочено по полочкам. В Body будет всё необходимое для Body, в Bottom всё необходимое для Bottom.

MbIXjkee commented 3 months ago

Привет. Ну тут вроде бы все логично - если снизу идет связка работающая по паттерну MVVM, то для нее будет своя WM, своя модель. Если ниже будет только использоваться выше лежащая WM то не надо создавать новую связку MVVM, и просто нижележащий виджет сделать Stateless/Stateful и передать ему как зависимость саму WM, или требуемые свойства (в зависимости от того как больше нравится).

Tamerlanchiques commented 3 months ago

Привет. Действительно, вариант сделать нижестоящие Stateless/ful виджеты с передачей зависимости логична, но меня просто триггерит открывающийся в таком случае доступ к контексту :). В целом, ничего страшного. Получается, чтобы довести до абсолюта, то реально нужно будет ещё несколько MVVM для разных частей страницы, что выглядит уже слишком избыточным…

MbIXjkee commented 3 months ago

А для нижележащих это нормально, ты же там в результате все равно используешь базовые виджеты - кнопки, переключатиели. Так они через него к теме ходят и тд. Это все ок, я например темминг и локализацию сам предпочту не заморачиваться через WM, а сделать как обычно. Это все будет отлично работать. Отданный WM контекст это в самой связке MVVM имеет смысл, своеобразная защита от того чтобы не размазывали логику куда не предполагалось. И прям упарываться в то чтобы на каждый чих создавать MVVM связку, на мой взгляд не надо. Инструмент должен помогать, а не мешать.

MbIXjkee commented 3 months ago

Ну и контекст там скрыт же только формально - всегда можно все в билдер обернуть и все, "защита" пройдена. Точно так же как (BuildContext as Element) в Stateless или Stateful сделать, и все - вот доступ к "скрытым" методам.