Search code examples
flutterdartflutter-animation

Tutorial Coach Mark - TargetContent is overflowing the screen


I am using tutorial coach mark for a tour in my app, but I notice that depending on the size of content in the focus, the distance between focus and the TargetContent is bigger. This problem is causing an overflow that interferes with the use.

See the video and notice the difference of space when de content is bigger. If the space were small, the description (TargetContent) would not overflow: (i also show the small space when the button is on focus to see the difference of the spaces)

enter image description here - - - - enter image description here

the code:

showTour(){
    initTargets();
    tutorialCoachMark = TutorialCoachMark(
        targets: targets,
        hideSkip: true,
      paddingFocus: 0
    )..show(context: context, rootOverlay: true);
  }

initTargets(){
    targets = [
      TargetFocus(
          identify: "qr-code-key",
          keyTarget: barCodeKey,
          contents: [
            TargetContent(
                align: ContentAlign.bottom,
                builder: (context, controller) {
                  return CoachMarkDescription(
                    text: "Ou utilize o leitor de código de barras. Basta clicar no ícone e apontar a câmera para o equipamento.",
                    margin: EdgeInsets.only(left: 70, right: 10),
                    onNext: (){
                      // setState(() {
                      //   isTutorialFocusForm = true;
                      // });
                      controller.next();
                    },
                    onSkip: (){
                      controller.skip();
                      if(widget.isRevisitTour == true) {
                        _setFlagToShowRegistrationEquipmentTour(false);
                        Navigator.of(context).pop();
                      }
                },
                  );
                }),
          ]
      ),

      TargetFocus(
          identify: "form-key",
          keyTarget: formKey,
          shape: ShapeLightFocus.RRect,
          contents: [
            TargetContent(
                builder: (context, controller) {
                  return Positioned(
                    top: 20,
                    child: CoachMarkDescription(
                      text: "Preencha todos os campos e insira o arquivo da sua nota fiscal.",
                      margin: EdgeInsets.only(left: 40, right: 40),
                      onNext: (){
                        controller.next();
                      },
                      onSkip: (){
                        controller.skip();
                        if(widget.isRevisitTour == true){
                          _setFlagToShowRegistrationEquipmentTour(false);
                          Navigator.of(context).pop();
                        }
                      },
                    ),
                  );
                }),
          ]
      ),

      TargetFocus(
          identify: "register-button-key",
          keyTarget: registerButtonKey,
          shape: ShapeLightFocus.RRect,
          contents: [
            TargetContent(
                align: ContentAlign.top,
                builder: (context, controller) {
                  return CoachMarkDescription(
                    text: "Clique em “cadastrar”, para prosseguir e seu equipamento será listado.",
                    margin: EdgeInsets.only(left: 40, right: 40),
                    isFinish: true,
                    onSkip: (){
                      controller.skip();
                      if(widget.isRevisitTour == true){
                        _setFlagToShowRegistrationEquipmentTour(false);
                        Navigator.of(context).pop();
                      }
                    },
                  );
                }),
          ]
      ),
    ];
  }

CoachMarlDescription class code:

import 'package:flutter/cupertino.dart';
import 'package:flutter/material.dart';

class CoachMarkDescription extends StatefulWidget {
  final String text;
  final String skip;
  final String next;
  final bool? isFinish;
  final EdgeInsets margin;
  final Function? onSkip;
  final Function? onNext;

  const CoachMarkDescription({
    Key? key,
    required this.text,
    this.skip = "Pular",
    this.next = "Próximo",
    this.onSkip,
    this.onNext,
    required this.margin,
    this.isFinish
  }) : super(key: key);

  @override
  State<CoachMarkDescription> createState() => _CoachMarkDescriptionState();
}

class _CoachMarkDescriptionState extends State<CoachMarkDescription> {
  bool isFinish = false;


  @override
  Widget build(BuildContext context) {
    if(widget.isFinish == true){
      isFinish = true;
    }
    return Container(
      // width: 256,
      padding: EdgeInsets.only(top: 20, left: 20, right: 20, bottom: 20),
      margin: widget.margin,
      decoration: BoxDecoration(
        color: Colors.white,
        borderRadius: BorderRadius.circular(10)
      ),

      child: Column(
        children: [
          Text(
            widget.text,
            textAlign: isFinish ? TextAlign.center : TextAlign.left,
            style: TextStyle(
              fontSize: 14,
              fontFamily: "OpenSans",
              color: Color.fromRGBO(51, 51, 51, 1)
            ),
          ),

          SizedBox(height: 20),

          !isFinish ?
          Row(
            mainAxisAlignment: MainAxisAlignment.spaceBetween,
            children: [
              GestureDetector(
                onTap: (){
                  widget.onSkip!();
                },
                child: Text(
                  "PULAR",
                  style: TextStyle(
                      fontSize: 14,
                      fontFamily: "OpenSans",
                      color: Color.fromRGBO(51, 51, 51, 1)
                  ),
                ),
              ),

              GestureDetector(
                onTap: (){
                  widget.onNext!();
                },
                child: Text(
                  "PRÓXIMO",
                  style: TextStyle(
                      fontSize: 14,
                      fontFamily: "OpenSans",
                      fontWeight: FontWeight.w600,
                      color: Theme.of(context).primaryColor
                  ),
                ),
              )
            ],
          )
              : GestureDetector(
                  onTap: (){
                    widget.onSkip!();
                  },
                  child: Text(
                    "FINALIZAR",
                    style: TextStyle(
                        fontSize: 14,
                        fontFamily: "OpenSans",
                        fontWeight: FontWeight.w600,
                        color: Theme.of(context).primaryColor
                    ),
                  ),
          )
        ],
      ),
    );
  }
}

i tryed put a negative number in paddingfocus parameter, and only acepted numbers less than -11 but if i put paddingFocus: -10, the description got a little better but still with overflow. I read the documentation of TutorialCoachMark and I dont find some parameter that can shrink the space and I try wrap CoachmarkDescription in Position but nothing worked.

I want to decrease the space between focus (form) and description, removing the overflow and showing the full Container, just like the others Target Focus


Solution

  • I considered adjusting the size of the parent component, margins, and padding to allow the target content to fit properly. Moreover, I change the padding (parameter of TargetContent), so it looks like this:

    TargetFocus(
              identify: "form-key",
              keyTarget: formKey,
              shape: ShapeLightFocus.RRect,
              contents: [
                TargetContent(
                    padding: EdgeInsets.zero
                    builder: (context, controller) {
                      return CoachMarkDescription(
                        text: "Preencha todos os campos e insira o arquivo da sua nota fiscal.",
                        margin: EdgeInsets.only(left: 40, right: 40),
                        onNext: (){
                          controller.next();
                        },
                        onSkip: (){
                          controller.skip();
                          if(widget.isRevisitTour == true){
                            _setFlagToShowRegistrationEquipmentTour(false);
                            Navigator.of(context).pop();
                          }
                        },
                      );
                    }),
              ]
          ),