Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Substituir ModularState por State e injetar ReplyTweetController em ReplyTweetPage #347

Open
shonorio opened this issue Jan 11, 2025 · 0 comments
Labels
dependencies Pull requests that update a dependency file deprecation Para destacar o uso de componentes obsoletos good first issue Good for newcomers refactor Envolve melhorias no código, como substituir o componentes obsoletos

Comments

@shonorio
Copy link
Collaborator

Descrição

Atualmente, a classe _ReplyTweetPageState estende ModularState para gerenciar o estado e obter uma instância do ReplyTweetController. Para melhorar a clareza e o controle sobre a injeção de dependência, propomos substituir ModularState por State e injetar o ReplyTweetController diretamente via passagem de parâmetro.

Alterações Propostas

  1. Remover a extensão de ModularState:

    • A classe _ReplyTweetPageState não deve mais estender ModularState.
    • O ReplyTweetController será passado como parâmetro no construtor.
  2. Injetar o ReplyTweetController via construtor:

    • O ReplyTweetController será injetado diretamente no construtor da classe ReplyTweetPage.
    • A classe _ReplyTweetPageState receberá o controlador via parâmetro.
  3. Atualizar a criação do estado:

    • O método createState será atualizado para passar o controlador para o estado.

Código Atualizado

import 'package:flutter/material.dart';
import 'package:flutter/services.dart';
import 'package:flutter_mobx/flutter_mobx.dart';
import 'package:flutter_svg/svg.dart';
import 'package:mobx/mobx.dart';

import '../../../../shared/design_system/colors.dart';
import '../../../../shared/design_system/text_styles.dart';
import '../../../../shared/design_system/widgets/buttons/penhas_button.dart';
import '../../../authentication/presentation/shared/page_progress_indicator.dart';
import '../../../authentication/presentation/shared/snack_bar_handler.dart';
import '../tweet/widgets/tweet_avatar.dart';
import '../tweet/widgets/tweet_body.dart';
import '../tweet/widgets/tweet_title.dart';
import 'reply_tweet_controller.dart';

class ReplyTweetPage extends StatefulWidget {
  const ReplyTweetPage({
    Key? key,
    this.title = 'ReplyTweet',
    required this.controller,
  }) : super(key: key);

  final String title;
  final ReplyTweetController controller;

  @override
  _ReplyTweetPageState createState() => _ReplyTweetPageState(controller);
}

class _ReplyTweetPageState extends State<ReplyTweetPage> with SnackBarHandler {
  final ReplyTweetController controller;
  final String inputHint = 'Deixe seu comentário';
  final String anonymousHint =
      'Sua publicação é anônima. As usuárias do app podem comentar sua publicação, mas só você pode iniciar uma conversa com elas.';
  List<ReactionDisposer>? _disposers;
  final GlobalKey<ScaffoldState> _scaffoldKey = GlobalKey<ScaffoldState>();
  PageProgressState _currentState = PageProgressState.initial;

  _ReplyTweetPageState(this.controller);

  TextStyle get _kTextStyleNewTweetAnonymousHint => const TextStyle(
        fontFamily: 'Lato',
        fontSize: 12.0,
        letterSpacing: 0.38,
        color: DesignSystemColors.warnGrey,
        fontWeight: FontWeight.normal,
      );

  TextStyle get _kTextStyleNewTweetAnonymous => const TextStyle(
        fontFamily: 'Lato',
        fontSize: 14.0,
        letterSpacing: 0.44,
        color: DesignSystemColors.darkIndigoThree,
        fontWeight: FontWeight.bold,
      );

  @override
  void didChangeDependencies() {
    super.didChangeDependencies();
    _disposers ??= [
      _showErrorMessage(),
      _showProgress(),
    ];
  }

  @override
  void dispose() {
    super.dispose();
    for (final d in _disposers!) {
      d();
    }
  }

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      key: _scaffoldKey,
      appBar: _buildAppBar(),
      body: SizedBox.expand(
        child: Container(
          color: DesignSystemColors.systemBackgroundColor,
          child: PageProgressIndicator(
            progressState: _currentState,
            child: GestureDetector(
              onTap: () => _handleTap(context),
              onPanDown: (_) => _handleTap(context),
              child: SafeArea(
                child: SingleChildScrollView(
                  padding:
                      const EdgeInsets.only(left: 16.0, right: 16.0, top: 24.0),
                  child: Column(
                    children: <Widget>[
                      Row(
                        crossAxisAlignment: CrossAxisAlignment.start,
                        children: <Widget>[
                          Expanded(
                            child: TweetAvatar(tweet: controller.tweet!),
                          ),
                          const SizedBox(
                            width: 6.0,
                          ),
                          Expanded(
                            flex: 5,
                            child: Column(
                              crossAxisAlignment: CrossAxisAlignment.stretch,
                              children: <Widget>[
                                TweetTitle(
                                  tweet: controller.tweet!,
                                  context: context,
                                  controller: null,
                                ),
                                TweetBody(content: controller.tweet!.content),
                              ],
                            ),
                          ),
                        ],
                      ),
                      SizedBox(
                        height: 160,
                        child: TextField(
                          maxLengthEnforcement: MaxLengthEnforcement.enforced,
                          controller: controller.editingController,
                          maxLength: 2200,
                          maxLines: 15,
                          onChanged: controller.setTweetContent,
                          decoration: InputDecoration(
                            filled: true,
                            enabledBorder: const OutlineInputBorder(
                              borderRadius: BorderRadius.only(
                                topLeft: Radius.circular(12),
                                topRight: Radius.circular(12),
                                bottomRight: Radius.circular(12),
                              ),
                              borderSide: BorderSide(
                                color: DesignSystemColors.ligthPurple,
                              ),
                            ),
                            focusedBorder: const OutlineInputBorder(
                              borderRadius: BorderRadius.only(
                                topLeft: Radius.circular(12),
                                topRight: Radius.circular(12),
                                bottomRight: Radius.circular(12),
                              ),
                              borderSide: BorderSide(
                                color: DesignSystemColors.ligthPurple,
                                width: 2.0,
                              ),
                            ),
                            alignLabelWithHint: true,
                            hintText: inputHint,
                            counterText: '',
                          ),
                          toolbarOptions: const ToolbarOptions(
                            copy: true,
                            cut: true,
                            selectAll: true,
                            paste: true,
                          ),
                        ),
                      ),
                      Observer(
                        builder: (context) {
                          return Visibility(
                            visible: controller.isAnonymousMode,
                            replacement: const SizedBox(height: 20.0),
                            child: _buildAnonymousWarning(),
                          );
                        },
                      ),
                      Padding(
                        padding: const EdgeInsets.only(top: 30.0),
                        child: SizedBox(
                          height: 40,
                          width: 220,
                          child: Observer(
                            builder: (_) {
                              return PenhasButton.roundedFilled(
                                onPressed: controller.isEnableCreateButton
                                    ? () => controller.replyTweetPressed()
                                    : null,
                                child: const Text(
                                  'Publicar',
                                  style: kTextStyleDefaultFilledButtonLabel,
                                ),
                              );
                            },
                          ),
                        ),
                      )
                    ],
                  ),
                ),
              ),
            ),
          ),
        ),
      ),
    );
  }

  PreferredSizeWidget _buildAppBar() {
    return AppBar(
      title: const Text('Comentário'),
      backgroundColor: DesignSystemColors.ligthPurple,
    );
  }

  Padding _buildAnonymousWarning() {
    return Padding(
      padding: const EdgeInsets.only(top: 40.0),
      child: Container(
        decoration: _buildInputTextBoxDecoration(),
        child: Padding(
          padding: const EdgeInsets.all(12.0),
          child: Column(
            children: <Widget>[
              Text(
                anonymousHint,
                style: _kTextStyleNewTweetAnonymousHint,
              ),
              Padding(
                padding: const EdgeInsets.only(top: 12.0),
                child: Row(
                  children: <Widget>[
                    SvgPicture.asset(
                      'assets/images/svg/drawer/user_profile.svg',
                      color: DesignSystemColors.darkIndigoThree,
                    ),
                    Padding(
                      padding: const EdgeInsets.only(left: 12.0),
                      child: Text(
                        'Anônima',
                        style: _kTextStyleNewTweetAnonymous,
                      ),
                    )
                  ],
                ),
              ),
            ],
          ),
        ),
      ),
    );
  }

  BoxDecoration _buildInputTextBoxDecoration() {
    return BoxDecoration(
      color: Colors.white,
      borderRadius: BorderRadius.circular(8.0),
    );
  }

  void _handleTap(BuildContext context) {
    if (MediaQuery.of(context).viewInsets.bottom > 0) {
      SystemChannels.textInput.invokeMethod('TextInput.hide');
    }
    WidgetsBinding.instance.focusManager.primaryFocus?.unfocus();
  }

  ReactionDisposer _showErrorMessage() {
    return reaction((_) => controller.errorMessage, (String? message) {
      showSnackBar(scaffoldKey: _scaffoldKey, message: message);
    });
  }

  ReactionDisposer _showProgress() {
    return reaction((_) => controller.currentState, (PageProgressState status) {
      setState(() {
        _currentState = status;
      });
    });
  }
}

Benefícios

  • Clareza: A injeção de dependência via construtor torna explícito quais dependências a classe requer.
  • Testabilidade: Facilita a criação de testes unitários, pois o controlador pode ser facilmente mockado.
  • Desacoplamento: Remove a dependência direta do flutter_modular, tornando o código mais modular e fácil de migrar para outras soluções de injeção de dependência no futuro.

Impacto

  • Compatibilidade: A mudança não deve impactar outras partes do código, desde que o ReplyTweetController seja corretamente injetado ao criar a ReplyTweetPage.
  • Testes: Os testes existentes podem precisar de ajustes para refletir a nova forma de injeção de dependência.

Próximos Passos

  1. Atualizar todos os locais onde ReplyTweetPage é instanciada para passar o ReplyTweetController.
  2. Atualizar os testes unitários para refletir a nova forma de injeção de dependência.
  3. Remover quaisquer dependências desnecessárias do flutter_modular relacionadas a esta página.
@shonorio shonorio added dependencies Pull requests that update a dependency file deprecation Para destacar o uso de componentes obsoletos refactor Envolve melhorias no código, como substituir o componentes obsoletos good first issue Good for newcomers labels Jan 11, 2025
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
dependencies Pull requests that update a dependency file deprecation Para destacar o uso de componentes obsoletos good first issue Good for newcomers refactor Envolve melhorias no código, como substituir o componentes obsoletos
Projects
None yet
Development

No branches or pull requests

1 participant