RafaelBarbosatec / tutorial_coach_mark

TutorialCoachMark
MIT License
524 stars 196 forks source link

Enabling the targetted widget onClick function when click on its target focus widget. #76

Open SalahAdDin opened 3 years ago

SalahAdDin commented 3 years ago

It would be great if at clicking on a targeted button, this could trigger its real function, you know, to show the user how it really works.

For example, we are showing four tabs on a navigation button bar, it would be really cool if at clicking it, the navigation bar should move between tabs, navigating on it.

Thanks.

RafaelBarbosatec commented 3 years ago

So, to do this. You can use onClickTarget or onClickOverlay in TutorialCoachMark:

SalahAdDin commented 3 years ago

Ok, we are using this package to show a bottom navigation bar:


      bottomNavigationBar: BottomNavigationBar(
        currentIndex: tabType.state.index,
        items: _navigationButtons,
        onTap:(int index)=> _navigateOnTap(index),
        selectedItemColor: Colors.black,
        type: BottomNavigationBarType.shifting,
        unselectedItemColor: Colors.grey,
      ),

So, we just need to pass _nagateOnTap(index) to the onClickTarget, isn't it? But, how can i get the navigation button index?

RafaelBarbosatec commented 3 years ago

When you create a TargetFocus you can add a identify. You receive this in onClickTarget, So just make your logic testing which target was clicked

SalahAdDin commented 3 years ago

When you create a TargetFocus you can add a identify. You receive this in onClickTarget, So just make your logic testing which target was clicked

Good idea, combining it with a enum variable i can make it to navigate like a charm.

Thank you!

SalahAdDin commented 3 years ago

Well, It didn't work as I thought:

if (showTutorial) {
      _initializeTargets();
      tutorialCoachMark = TutorialCoachMark(
        context,
        targets: targets,
        skipWidget: const Padding(
          padding: EdgeInsets.only(bottom: 112, right: 8),
          child: Text(
            "Skip",
            style: TextStyle(color: Colors.white),
          ),
        ),
        focusAnimationDuration: const Duration(milliseconds: 1000),
        onClickTarget: (TargetFocus target) {
          switch (target.identify as TargetIdentifier) {
            case TargetIdentifier.informationTarget:
              _navigateOnTap(0);
              break;
            case TargetIdentifier.treatmentTarget:
              _navigateOnTap(1);
              break;
            case TargetIdentifier.academyTarget:
              _navigateOnTap(2);
              break;
            case TargetIdentifier.successStoriesTarget:
              _navigateOnTap(4);
              break;
            case TargetIdentifier.sideMenuTarget:
              // TODO: Handle this case.
              break;
          }
        },
        onFinish: () async => userNotifierProvider.hasSeenTutorial(),
        onSkip: () async => userNotifierProvider.hasSeenTutorial(),
        paddingFocus: 7.5,
      )..show();
    }

Record_player_20210706103923

As you can see, at clickingon the target the tutorial is skipped, instead of navigating and showing the next target.

miguelflores1993 commented 3 years ago

I have the same problem or doubt more than anything, I want to execute the real action when the tutorial is on top of the action.

RafaelBarbosatec commented 3 years ago

Hi @SalahAdDin ! From the video it looks like there was some crash on your screen. There's a way to send what you do in _navigateOnTap(4);.

SalahAdDin commented 3 years ago

Hi @SalahAdDin ! From the video it looks like there was some crash on your screen. There's a way to send what you do in _navigateOnTap(4);.

Oh yes, I saw it now: image

It seems at navigating, it is recreating the whole screen again... what?

Shahad-999 commented 8 months ago

Use identify as onClick function like this:

 addTarget({required GlobalKey key, required String message, Function()? onClick}) {
    targets.add(TargetFocus(
      keyTarget: key,
      identify: () {
        onClick?.call();
      },
      alignSkip: Alignment.bottomLeft,
      shape: ShapeLightFocus.RRect,
      radius: 10,
      contents: [
        TargetContent(
          align: ContentAlign.top,
          builder: (context, controller) => Container(
            alignment: Alignment.center,
            child: Text(
              message,
              textAlign: TextAlign.center,
              style: const TextStyle(
                fontWeight: FontWeight.bold,
                color: Colors.white,
                fontSize: 25,
              ),
            ),
          ),
        ),
      ],
    ));
  }

And in TutorialCoachMark call function which u pass like this

onClickTarget: (target) {
     (target.identify as Function).call();
 }
erperejildo commented 4 months ago

(target.identify as Function).call();

but you are not triggering the default function from those elements; instead, you are detecting which element you clicked and duplicating its function there. And in case of modifications, you would have to change both

anilthapa1939 commented 2 months ago

any update on this?

erperejildo commented 1 month ago

we are looking forward for this implementation @RafaelBarbosatec. Any idea when it will be ready?

RafaelBarbosatec commented 1 month ago

Hi @erperejildo ! What is the problem? Today is possible call function, just listening wich target was clicked.

erperejildo commented 1 month ago

Hi @erperejildo ! What is the problem? Today is possible call function, just listening wich target was clicked.

How can I trigger the element's original function without duplicating its code?

void createTutorial() {
    tutorialCoachMark = TutorialCoachMark(
      targets: landingPageTargets(
        navigationBalances: navigationBalances,
        incomeCard: incomeCard,
      ),
      colorShadow: Colors.blue,
      textSkip: "SKIP",
      paddingFocus: 10,
      opacityShadow: 0.5,
      imageFilter: ImageFilter.blur(sigmaX: 8, sigmaY: 8),
      onClickTarget: (target) {
        if (target.identify == "incomeCard") {
         // THIS DOESN'T WORK
          (target.identify as Function).call();
        }
      },
    );
  }

Not really important but inside TutorialCoachMark:

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

List<TargetFocus> targets = [];

List<TargetFocus> landingPageTargets({
  required GlobalKey navigationBalances,
  required GlobalKey incomeCard,
}) {
  targets.add(
    TargetFocus(
      keyTarget: navigationBalances,
      alignSkip: Alignment.topRight,
      enableOverlayTab: true,
      contents: [
        TargetContent(
          align: ContentAlign.top,
          builder: (context, controller) {
            return const Column(
              mainAxisSize: MainAxisSize.min,
              crossAxisAlignment: CrossAxisAlignment.start,
              children: <Widget>[
                Text(
                  "This is your balances page. In here, you will find different expenses and incomes2.",
                  style: TextStyle(
                    color: Colors.white,
                  ),
                ),
              ],
            );
          },
        ),
      ],
    ),
  );

  targets.add(
    TargetFocus(
      keyTarget: incomeCard,
      identify: 'incomeCard',
      alignSkip: Alignment.topRight,
      enableOverlayTab: true,
      contents: [
        TargetContent(
          align: ContentAlign.bottom,
          builder: (context, controller) {
            return const Column(
              mainAxisSize: MainAxisSize.min,
              crossAxisAlignment: CrossAxisAlignment.start,
              children: <Widget>[
                Text(
                  "The green cards are incomes.These cards might be salary, payments or any other positive profit.\n\nClick this one to check inside.",
                  style: TextStyle(
                    color: Colors.white,
                  ),
                ),
              ],
            );
          },
        ),
      ],
    ),
  );

  return targets;
}
erperejildo commented 3 weeks ago

@RafaelBarbosatec?