Неоконченное поле в Stateless Widget - PullRequest
0 голосов
/ 05 февраля 2020

Во-первых, я должен сказать, что я не уверен, является ли stackoverflow подходящим местом, чтобы спросить об этом, но у меня не с кем спросить, кроме сообщества, потому что мы работаем в небольшом стартапе, где нет разработчиков флаттера, кроме двух из нас.

Я (около года в разработке и около половины года изучаю флаттер) и один своего рода «старший разработчик мобильных приложений с 10-летним опытом» ведут ожесточенные дискуссии.

В топике c обсуждения используется не окончательное поле в виджете без сохранения состояния. Он делает это, он пишет такой код. И он говорит, что это лучший способ решить его проблемы. Я говорю, что это плохая идея, либо ему нужен виджет с сохранением состояния, либо его дизайн плохой, и ему не нужно поле без финала.

Итак, мой вопрос: есть ли ситуация, в которой использование неконечного поля в виджете без состояния оправдано?

Его аргументы:

  1. Мы используя шаблон BLo C, и поскольку в StatelessWidget у нас есть BlocBuilder, этот StatelessWidget имеет состояние.
  2. Глупый Дарт Линтер не знает нашу «ситуацию BLo C»
  3. Если мы будем использовать виджет с сохранением состояния, читаемость кода ухудшится.
  4. Если мы будем Использование виджета с отслеживанием состояния приводит к дополнительным накладным расходам.

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

Возможный дубликат этого вопроса также не убеждает моего коллегу. Флаттер: изменяемые поля в виджетах без состояния

Пожалуйста, взгляните на его код:

class GameDiscussThePicture extends StatelessWidget {

  GameDiscussThePicture();

  CarouselSlider _slider;

  @override
  Widget build(BuildContext context) {
    return BlocBuilder(
      bloc: BlocProvider.of<ChatBloc>(context),
      condition: (previousState, state) {
        return previousState != GameClosed();
      },
      builder: (context, state) {
        if (state is GameDiscussTopicChanged) {
          _showPictureWith(context, state.themeIndex);
        } else if (state is GameClosed) {
          Navigator.of(context).pop();
          return Container();
        }
      final _chatBloc = BlocProvider.of<ChatBloc>(context);
      return Scaffold(
        appBar: AppBar(
          backgroundColor: Color.fromARGB(255, 255, 255, 255),
          leading: BackButton(
            color: Color.fromARGB(255, 12, 12, 13),
            onPressed: () => BlocProvider.of<ChatBloc>(context).add(GameCancel()),
          ),
        ),
        //SafeArea
        body: DecoratedBox(
          decoration: BoxDecoration(color: Color.fromARGB(255, 240, 240, 240)),
          child: Row(
            mainAxisSize: MainAxisSize.max,
            mainAxisAlignment: MainAxisAlignment.start,
            children: [
              Expanded(
                child: Column(
                  mainAxisSize: MainAxisSize.max,
                  mainAxisAlignment: MainAxisAlignment.spaceBetween,
                  crossAxisAlignment: CrossAxisAlignment.start,
                  children: [
                    SizedBox(height: 15),
                    _carouselSlider(context),
                    Container(
                      height: 88,
                      child: DecoratedBox(
                        decoration: BoxDecoration(color: Color.fromARGB(255, 255, 255, 255)),
                        child: Row(
                          mainAxisSize: MainAxisSize.max,
                          mainAxisAlignment: MainAxisAlignment.spaceEvenly,
                          children: [
                            if (_chatBloc.partnerAvatar() != null) Image.network(_chatBloc.partnerAvatar(), fit: BoxFit.cover, width: 75.0),
                            if (_chatBloc.partnerAvatar() == null) Text('RU', style: TextStyle(fontSize: 22)),
                          Padding(
                            padding: EdgeInsets.fromLTRB(20, 0, 20, 0),
                            child: Column(
                              mainAxisAlignment: MainAxisAlignment.center,
                              crossAxisAlignment: CrossAxisAlignment.start,
                              children: [
                                Text(_chatBloc.partnerName(), style: TextStyle(fontSize: 20, fontWeight: FontWeight.normal),),
                                ChatStopwatch(),
                                // Text('До конца 06:33', style: TextStyle(fontSize: 14, fontWeight: FontWeight.normal),),
                              ],
                            )
                          ),
                          // FlatButton(
                          //   child: Image.asset('assets/images/mic_off.png', width: 30, height: 30,),
                          //   onPressed: () => print('mic off pressed'),
                          // ),
                          FlatButton(
                            child: Image.asset('assets/images/hang_off.png', width: 60, height: 60,),
                            onPressed: () => ChatHelper.confirmEndingDialog(context)
                          ),
                        ]),
                    ))
                  ],
                ),
              ),
            ],
          ),
        ),
      );
    });
  }

  @widget
  Widget _carouselSlider(BuildContext context) {    
    final chatBloc = BlocProvider.of<ChatBloc>(context);
    _slider = CarouselSlider(
      height: 600.0,
      viewportFraction: 0.9,
      reverse: false,
      enableInfiniteScroll: false,
      initialPage: chatBloc.gameDiscussCurrentIdx,
      onPageChanged: (index) {
        final chatBloc = BlocProvider.of<ChatBloc>(context);
        if (chatBloc.gameDiscussCurrentIdx < index) {
          chatBloc.add(GameDiscussTopicChange(themeIndex: index));
        } else {
          _slider.animateToPage(chatBloc.gameDiscussCurrentIdx, duration: Duration(milliseconds: 300), curve: Curves.easeInOut);
        }
      },
      items: chatBloc.gameDiscussPictures.map((item) {
        return Builder(
          builder: (BuildContext context) {
            return Container(
              width: MediaQuery.of(context).size.width,
              margin: EdgeInsets.symmetric(horizontal: 5.0),
              child: Column(
                mainAxisSize: MainAxisSize.max,
                mainAxisAlignment: MainAxisAlignment.start,
                crossAxisAlignment: CrossAxisAlignment.start,
                children: [
                  Text(item.titleEn, style: Styles.h3),
                  SizedBox(height: 15.0,),
                  ClipRRect(
                    borderRadius: BorderRadius.all(Radius.circular(15.0)),
                    child: Image.network(item.getImageUrl(), fit: BoxFit.cover, width: MediaQuery.of(context).size.width),
                  )
                ]
              ),
            );
          },
        );
      }).toList(),
    );
    return _slider;
  }

  _onPictureChanged(BuildContext context, int index) {
    final chatBloc = BlocProvider.of<ChatBloc>(context);
    if (chatBloc.gameDiscussCurrentIdx < index) {
      chatBloc.add(GameDiscussTopicChange(themeIndex: index));
    } else {
      _slider.animateToPage(chatBloc.gameDiscussCurrentIdx, duration: Duration(milliseconds: 300), curve: Curves.easeInOut);
    }
  }

  _showPictureWith(BuildContext context, int index) {
      final chatBloc = BlocProvider.of<ChatBloc>(context);
      chatBloc.gameDiscussCurrentIdx = index;
      _slider.animateToPage(chatBloc.gameDiscussCurrentIdx, duration: Duration(milliseconds: 300), curve: Curves.easeInOut);
  }
}

1 Ответ

0 голосов
/ 05 февраля 2020

Отказ от ответственности: Я не умею объяснять, надеюсь, вы что-то получите, прочитав это sh объяснение. Я даже не думаю, что это можно назвать объяснением

class GameDiscussThePicture extends StatelessWidget {

  GameDiscussThePicture();

  /// As he said, BLoC is the one holding state, therefore if he wants a non final field 
  /// declare it in ChatBloc not here.
  /// class ChatBloc extends Bloc {
  ///   CarouselSlider _slider;
  ///   CarouselSlider get slider => _slider;
  /// }
  /// To access it, BlocProvider.of<ChatBloc>(context).slider;
  CarouselSlider _slider;

  @override
  Widget build(BuildContext context) {
    return BlocBuilder(
      bloc: BlocProvider.of<ChatBloc>(context),
      condition: (previousState, state) {
        return previousState != GameClosed();
      },
      builder: (context, state) {
        if (state is GameDiscussTopicChanged) {
          _showPictureWith(context, state.themeIndex);
        } else if (state is GameClosed) {
          Navigator.of(context).pop();
          return Container();
        }
      final _chatBloc = BlocProvider.of<ChatBloc>(context);
      return Scaffold(
        appBar: AppBar(
          backgroundColor: Color.fromARGB(255, 255, 255, 255),
          leading: BackButton(
            color: Color.fromARGB(255, 12, 12, 13),
            /// he can just write _chatBloc.add(GameCancel()) here.
            onPressed: () => BlocProvider.of<ChatBloc>(context).add(GameCancel()),
          ),
        ),
        //SafeArea
        body: DecoratedBox(
          decoration: BoxDecoration(color: Color.fromARGB(255, 240, 240, 240)),
          child: Row(
            mainAxisSize: MainAxisSize.max,
            mainAxisAlignment: MainAxisAlignment.start,
            children: [
              Expanded(
                child: Column(
                  mainAxisSize: MainAxisSize.max,
                  mainAxisAlignment: MainAxisAlignment.spaceBetween,
                  crossAxisAlignment: CrossAxisAlignment.start,
                  children: [
                    SizedBox(height: 15),
                    _carouselSlider(context),
                    Container(
                      height: 88,
                      child: DecoratedBox(
                        decoration: BoxDecoration(color: Color.fromARGB(255, 255, 255, 255)),
                        child: Row(
                          mainAxisSize: MainAxisSize.max,
                          mainAxisAlignment: MainAxisAlignment.spaceEvenly,
                          children: [
                            if (_chatBloc.partnerAvatar() != null) Image.network(_chatBloc.partnerAvatar(), fit: BoxFit.cover, width: 75.0),
                            if (_chatBloc.partnerAvatar() == null) Text('RU', style: TextStyle(fontSize: 22)),
                          Padding(
                            padding: EdgeInsets.fromLTRB(20, 0, 20, 0),
                            child: Column(
                              mainAxisAlignment: MainAxisAlignment.center,
                              crossAxisAlignment: CrossAxisAlignment.start,
                              children: [
                                Text(_chatBloc.partnerName(), style: TextStyle(fontSize: 20, fontWeight: FontWeight.normal),),
                                ChatStopwatch(),
                                // Text('До конца 06:33', style: TextStyle(fontSize: 14, fontWeight: FontWeight.normal),),
                              ],
                            )
                          ),
                          // FlatButton(
                          //   child: Image.asset('assets/images/mic_off.png', width: 30, height: 30,),
                          //   onPressed: () => print('mic off pressed'),
                          // ),
                          FlatButton(
                            child: Image.asset('assets/images/hang_off.png', width: 60, height: 60,),
                            onPressed: () => ChatHelper.confirmEndingDialog(context)
                          ),
                        ]),
                    ))
                  ],
                ),
              ),
            ],
          ),
        ),
      );
    });
  }

  @widget
  /// Also rather than passing BuildContext, passing the _chatBloc is better.
  /// I am not sure why, but I've read somewhere BuildContext is not meant to be passed
  /// around. And you don't need to make another final field for BlocProvider.of<ChatBloc> 
  /// (context)
  /// Widget _carouselSlider(ChatBloc chatBloc) {
  ///   and here you can do something like chatBloc.slider = CarouselSlider(); in case
  ///   that slider field will be used again somehow.
  /// }
  /// Tho just return CarouselSlider instead is better in this scenario IMO.
  Widget _carouselSlider(BuildContext context) {    
    final chatBloc = BlocProvider.of<ChatBloc>(context);
    _slider = CarouselSlider(
      height: 600.0,
      viewportFraction: 0.9,
      reverse: false,
      enableInfiniteScroll: false,
      initialPage: chatBloc.gameDiscussCurrentIdx,
      onPageChanged: (index) {
        final chatBloc = BlocProvider.of<ChatBloc>(context);
        if (chatBloc.gameDiscussCurrentIdx < index) {
          chatBloc.add(GameDiscussTopicChange(themeIndex: index));
        } else {
          _slider.animateToPage(chatBloc.gameDiscussCurrentIdx, duration: Duration(milliseconds: 300), curve: Curves.easeInOut);
        }
      },
      items: chatBloc.gameDiscussPictures.map((item) {
        return Builder(
          builder: (BuildContext context) {
            return Container(
              width: MediaQuery.of(context).size.width,
              margin: EdgeInsets.symmetric(horizontal: 5.0),
              child: Column(
                mainAxisSize: MainAxisSize.max,
                mainAxisAlignment: MainAxisAlignment.start,
                crossAxisAlignment: CrossAxisAlignment.start,
                children: [
                  Text(item.titleEn, style: Styles.h3),
                  SizedBox(height: 15.0,),
                  ClipRRect(
                    borderRadius: BorderRadius.all(Radius.circular(15.0)),
                    child: Image.network(item.getImageUrl(), fit: BoxFit.cover, width: MediaQuery.of(context).size.width),
                  )
                ]
              ),
            );
          },
        );
      }).toList(),
    );
    return _slider;
  }

  _onPictureChanged(BuildContext context, int index) {
    final chatBloc = BlocProvider.of<ChatBloc>(context);
    if (chatBloc.gameDiscussCurrentIdx < index) {
      chatBloc.add(GameDiscussTopicChange(themeIndex: index));
    } else {
      _slider.animateToPage(chatBloc.gameDiscussCurrentIdx, duration: Duration(milliseconds: 300), curve: Curves.easeInOut);
    }
  }

  _showPictureWith(BuildContext context, int index) {
      final chatBloc = BlocProvider.of<ChatBloc>(context);
      chatBloc.gameDiscussCurrentIdx = index;
      _slider.animateToPage(chatBloc.gameDiscussCurrentIdx, duration: Duration(milliseconds: 300), curve: Curves.easeInOut);
  }
}
...