TutorialCoachMark - Create a beautiful and easy tutorial for your application.

Overview

pub package buymeacoffee

TutorialCoachMark

Create a beautiful and easy tutorial for your application.

Example 1 Example 2

Usage

To use this plugin, add tutorial_coach_mark as a dependency in your pubspec.yaml file.

Example

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

void showTutorial() {
    TutorialCoachMark tutorial = TutorialCoachMark(
      context,
      targets: targets, // List
   
      colorShadow: Colors.red, // DEFAULT Colors.black
       // alignSkip: Alignment.bottomRight,
       // textSkip: "SKIP",
       // paddingFocus: 10,
       // focusAnimationDuration: Duration(milliseconds: 500),
       // pulseAnimationDuration: Duration(milliseconds: 500),
       // pulseVariation: Tween(begin: 1.0, end: 0.99),
      onFinish: (){
        print("finish");
      },
      onClickTarget: (target){
        print(target);
      },
      onSkip: (){
        print("skip");
      }
    )..show();

    // tutorial.skip();
    // tutorial.finish();
    // tutorial.next(); // call next target programmatically
    // tutorial.previous(); // call previous target programmatically
  }

Creating targets (TargetFocus)

TargetFocus is the class that represents the widget that will be focused and configure what will be displayed after you focus it.

Attributes:

Attribute Type Description
identify dynamic free for identification use
keyTarget GlobalKey GlobalKey widget that wants to be focused
targetPosition TargetPosition If you do not want to use GlobalKey, you can create a TargetPosition to determine where to focus
contents ContentTarget[] Content list you want to display after focusing widget
shape ShapeLightFocus ShapeLightFocus.Circle or ShapeLightFocus.RRect
radius double Use when shape = ShapeLightFocus.RRect
color Color Custom color to target
enableOverlayTab bool enable click in all screen to call next step
enableTargetTab bool enable click in target to call next step
alignSkip Alignment use to align the skip in the target
paddingFocus Alignment settings padding of the focus in target
focusAnimationDuration Duration override the widget's global focus animation duration
pulseVariation Tween override interval pulse animation

Creating contents (ContentTarget)

ContentTarget is the class responsible for determining what should be displayed and how it will appear after focusing on the widget.

Attributes:

Attribute Type Description
align AlignContent With this attribute you determine in which region to display the content in relation to the focused widget (top,bottom,left,right)
padding EdgeInsets Padding of the content
child Widget Content you want to be displayed
builder Widget Content you want to be displayed
customPosition CustomTargetContentPosition Add custom position when align is AlignContent.custom

Example Complete

[ Text( "Titulo lorem ipsum", style: TextStyle( fontWeight: FontWeight.bold, color: Colors.white, fontSize: 20.0 ), ), Padding( padding: const EdgeInsets.only(top: 10.0), child: Text("Lorem ipsum dolor sit amet, consectetur adipiscing elit. Proin pulvinar tortor eget maximus iaculis.", style: TextStyle( color: Colors.white ),), ) ], ), ) ) ] ) ); targets.add( TargetFocus( identify: "Target 2", keyTarget: keyButton4, contents: [ TargetContent( align: ContentAlign.left, child: Container( child: Column( mainAxisSize: MainAxisSize.min, crossAxisAlignment: CrossAxisAlignment.start, children: [ Text( "Multiples content", style: TextStyle( fontWeight: FontWeight.bold, color: Colors.white, fontSize: 20.0 ), ), Padding( padding: const EdgeInsets.only(top: 10.0), child: Text("Lorem ipsum dolor sit amet, consectetur adipiscing elit. Proin pulvinar tortor eget maximus iaculis.", style: TextStyle( color: Colors.white ),), ) ], ), ) ), TargetContent( align: ContentAlign.top, child: Container( child: Column( mainAxisSize: MainAxisSize.min, crossAxisAlignment: CrossAxisAlignment.start, children: [ Text( "Multiples content", style: TextStyle( fontWeight: FontWeight.bold, color: Colors.white, fontSize: 20.0 ), ), Padding( padding: const EdgeInsets.only(top: 10.0), child: Text("Lorem ipsum dolor sit amet, consectetur adipiscing elit. Proin pulvinar tortor eget maximus iaculis.", style: TextStyle( color: Colors.white ),), ) ], ), ) ) ] ) ); targets.add( TargetFocus( identify: "Target 3", keyTarget: keyButton5, contents: [ TargetContent( align: ContentAlign.right, child: Container( child: Column( mainAxisSize: MainAxisSize.min, crossAxisAlignment: CrossAxisAlignment.start, children: [ Text( "Title lorem ipsum", style: TextStyle( fontWeight: FontWeight.bold, color: Colors.white, fontSize: 20.0 ), ), Padding( padding: const EdgeInsets.only(top: 10.0), child: Text("Lorem ipsum dolor sit amet, consectetur adipiscing elit. Proin pulvinar tortor eget maximus iaculis.", style: TextStyle( color: Colors.white ),), ) ], ), ) ) ] ) ); } void showTutorial() { TutorialCoachMark( context, targets: targets, // List colorShadow: Colors.red, // DEFAULT Colors.black // alignSkip: Alignment.bottomRight, // textSkip: "SKIP", // paddingFocus: 10, // opacityShadow: 0.8, onClickTarget: (target){ print(target); }, onClickOverlay: (target){ print(target); }, onSkip: (){ print("skip"); }, onFinish: (){ print("finish"); }, )..show(); } ">
import 'package:flutter/material.dart';
import 'package:tutorial_coach_mark/tutorial_coach_mark.dart';

List<TargetFocus> targets = List();

 @override
 void initState() {
    targets.add(
        TargetFocus(
            identify: "Target 1",
            keyTarget: keyButton,
            contents: [
              TargetContent(
                  align: ContentAlign.bottom,
                  child: Container(
                    child:Column(
                      mainAxisSize: MainAxisSize.min,
                      crossAxisAlignment: CrossAxisAlignment.start,
                      children: <Widget>[
                        Text(
                          "Titulo lorem ipsum",
                          style: TextStyle(
                            fontWeight: FontWeight.bold,
                            color: Colors.white,
                            fontSize: 20.0
                          ),
                        ),
                        Padding(
                          padding: const EdgeInsets.only(top: 10.0),
                          child: Text("Lorem ipsum dolor sit amet, consectetur adipiscing elit. Proin pulvinar tortor eget maximus iaculis.",
                            style: TextStyle(
                                color: Colors.white
                            ),),
                        )
                      ],
                    ),
                  )
              )
            ]
        )
    );

    targets.add(
        TargetFocus(
            identify: "Target 2",
            keyTarget: keyButton4,
            contents: [
              TargetContent(
                  align: ContentAlign.left,
                  child: Container(
                    child: Column(
                      mainAxisSize: MainAxisSize.min,
                      crossAxisAlignment: CrossAxisAlignment.start,
                      children: <Widget>[
                        Text(
                          "Multiples content",
                          style: TextStyle(
                              fontWeight: FontWeight.bold,
                              color: Colors.white,
                              fontSize: 20.0
                          ),
                        ),
                        Padding(
                          padding: const EdgeInsets.only(top: 10.0),
                          child: Text("Lorem ipsum dolor sit amet, consectetur adipiscing elit. Proin pulvinar tortor eget maximus iaculis.",
                            style: TextStyle(
                                color: Colors.white
                            ),),
                        )
                      ],
                    ),
                  )
              ),
              TargetContent(
                  align: ContentAlign.top,
                  child: Container(
                    child: Column(
                      mainAxisSize: MainAxisSize.min,
                      crossAxisAlignment: CrossAxisAlignment.start,
                      children: <Widget>[
                        Text(
                          "Multiples content",
                          style: TextStyle(
                              fontWeight: FontWeight.bold,
                              color: Colors.white,
                              fontSize: 20.0
                          ),
                        ),
                        Padding(
                          padding: const EdgeInsets.only(top: 10.0),
                          child: Text("Lorem ipsum dolor sit amet, consectetur adipiscing elit. Proin pulvinar tortor eget maximus iaculis.",
                            style: TextStyle(
                                color: Colors.white
                            ),),
                        )
                      ],
                    ),
                  )
              )
            ]
        )
    );

    targets.add(
        TargetFocus(
            identify: "Target 3",
            keyTarget: keyButton5,
            contents: [
              TargetContent(
                  align: ContentAlign.right,
                  child: Container(
                    child: Column(
                      mainAxisSize: MainAxisSize.min,
                      crossAxisAlignment: CrossAxisAlignment.start,
                      children: <Widget>[
                        Text(
                          "Title lorem ipsum",
                          style: TextStyle(
                              fontWeight: FontWeight.bold,
                              color: Colors.white,
                              fontSize: 20.0
                          ),
                        ),
                        Padding(
                          padding: const EdgeInsets.only(top: 10.0),
                          child: Text("Lorem ipsum dolor sit amet, consectetur adipiscing elit. Proin pulvinar tortor eget maximus iaculis.",
                            style: TextStyle(
                                color: Colors.white
                            ),),
                        )
                      ],
                    ),
                  )
              )
            ]
        )
    );
}

void showTutorial() {
    TutorialCoachMark(
      context,
      targets: targets, // List
       
      colorShadow: Colors.red, // DEFAULT Colors.black
       // alignSkip: Alignment.bottomRight,
       // textSkip: "SKIP",
       // paddingFocus: 10,
       // opacityShadow: 0.8,
      onClickTarget: (target){
        print(target);
      },
      onClickOverlay: (target){
        print(target);
      },
      onSkip: (){
        print("skip");
      },
      onFinish: (){
        print("finish");
      },
    )..show();
  }

Contribution

If you find any errors or want to add improvements, you can open a issue or develop the fix and open a pull request. Thank you for your cooperation!

Comments
  • ERROR: No step on TargetFocus with TargetPosition instead of GlobalKey

    ERROR: No step on TargetFocus with TargetPosition instead of GlobalKey

    Hi,

    We could make a elegant tutorial for a bottom navigation bar thanks to this package.

    We require to target another widgets but right now it is not possible to do that; hence, we opted for add a new TargetFocus with a TargetPosition as follows:

      void _initializeTargets() {
        targets.addAll(
          <TargetFocus>[
            TargetFocus(),
            TargetFocus(),
            TargetFocus(),
            TargetFocus(),
    TargetFocus(
              identify: "SideMenuTarget",
              targetPosition: TargetPosition(
                const Size(10.0, 10.0),
                const Offset(20.0, 20.0),
              ),
              shape: ShapeLightFocus.Circle,
              contents: <TargetContent>[
                TargetContent(
                  align: ContentAlign.top,
                  child: Column(
                    mainAxisSize: MainAxisSize.min,
                    crossAxisAlignment: CrossAxisAlignment.end,
                    children: const <Widget>[
                      Text(
                        "Side Menu",
                        style: TextStyle(
                          fontWeight: FontWeight.bold,
                          color: Colors.white,
                          fontSize: 20.0,
                        ),
                      ),
                      Padding(
                        padding: EdgeInsets.only(top: 10.0, right: 56),
                        child: Text(
                          "Swipe left to open the side menu.",
                          style: TextStyle(color: Colors.white),
                        ),
                      ),
                      Padding(
                        padding: EdgeInsets.only(top: 10.0, right: 56),
                        child: Text(
                          "You will fine your user profile, the application settings, contact with us and many other sections there.",
                          style: TextStyle(color: Colors.white),
                        ),
                      )
                    ],
                  ),
                )
              ],
            ),
          ],
        );
      }
    

    No linting or code problems here.

    But, testing it, this TargetFocus is not showing up: Record_select-area_20210615140017 After the final button, the final TargetFocus should be shown, but it is not.

    We saw the next error on debu console: [ +602 ms] I/flutter (18813): TutorialCoachMark (ERROR): It was not possible to obtain target position..

    Why?

    Thank you.

    opened by SalahAdDin 15
  • Get next target on full screen tap

    Get next target on full screen tap

    I think tutorial_coach_mark is a great tool, the best out there. My problem is that I can get the next target only if I'm pressing on a free space on the screen.

    In my case I have many texts on the tutorial, so users are very struggling to next each target.

    Is there a way to tell the TutorialCoachMark to go the next target? So users will be able to tap on the entire screen in order to get the next target.

    Thanks!

    opened by saveKenny 8
  • The getter 'offset' was called on null

    The getter 'offset' was called on null

    ══╡ EXCEPTION CAUGHT BY RENDERING LIBRARY ╞═════════════════════════════════════════════════════════ The following NoSuchMethodError was thrown during paint(): The getter 'offset' was called on null. Receiver: null Tried calling: offset

    package:tutorial_coach_mark/light_paint_rect.dart:33:55

    opened by brizaldi 5
  • GlobalKey element offset

    GlobalKey element offset

    Hi, I'm trying to use the package but every element I use a Global key, the highlight appears below that element: (I wanted the highlight on the kebab bottom)

    Screen Shot 2020-06-12 at 15 31 00

    I'm using the Global Key in my PopupMenuButton and keyTarget on the plugin call.

    keyTarget: key,
    

    Any hint what am I doing wrong?

    Thanks in advance.

    opened by franskjv 5
  • How do show tutorials on pages compiled from several widgets ?

    How do show tutorials on pages compiled from several widgets ?

    my view is compiled from several widgets. how do I pass the global key and make it available to different views? If you can create more advanced example it would be great. Thanks.

    opened by tidharnitzan 5
  • Finish method is called on switching tabs

    Finish method is called on switching tabs

    I have 4 tabs in my app, in which second tab is selected by default. I need to show coach mark on second, then first tab, I have provided keys accordingly but as soon as I switch tab, finish method is being called. Is there any solution to this?

    opened by AkankshaKS 4
  • Feature Request: Be able to make the tutorial show only once

    Feature Request: Be able to make the tutorial show only once

    I am trying to make the tutorial show up only once

    If the method is called inside the initState method, it keeps showing every time I move to that screen.

    opened by davidaodejobi 3
  • Launching tutorial from service without a context

    Launching tutorial from service without a context

    I'm trying to decouple the logic of my app from the widget-side. In order to do so, I've created a TutorialService which launches the tutorial from the business logic-side. However, a BuildContext is needed in order to start the tutorial. In other situations like this (e.g. opening dialogs), getting a reference to the BuildContext using a navigator key and the navigatorKey.currentContext does the trick, bit it does not seem to work for this use case.

    The problem is in the call to Overlay.of(...). I've tried following calls, and all of them return a null OverlayState:

    Overlay.of(navigatorKey.currentContext, rootOverlay: false);
    Overlay.of(navigatorKey.currentState!.context, rootOverlay: false);
    Overlay.of(navigatorKey.currentState!.overlay!.context, rootOverlay: false);
    

    Can this be done?

    opened by Pitazzo 3
  • Is it possible to create a TargetFocus without target?

    Is it possible to create a TargetFocus without target?

    Is it possible to create a TargetFocus without a target?

    In normal steps, we use a coach mark, but in some steps, the Widget that should be the target for the coach mark does not exist on the current screen.

    In this case, I would like to display only the text widget for explain without specifying a target.

    Is there any way for it ?

    opened by masushin 3
  • How can I implement tutorial Coach Mark over several Widgets?

    How can I implement tutorial Coach Mark over several Widgets?

    I have multiple Stateless and Statefull widgets. I wan't to make the tutorial Coach with one button over multiple Files. Is there a way to pass the Globalkeys oder is there another Method?

    opened by corusm 3
  • Run tutorial only after the page transition animation ends

    Run tutorial only after the page transition animation ends

    While trying to start a tutorial right when a user enters a screen, I've been having the following issue: The tutorial sometimes starts before the page transition animation ends. As a result, the targets end up not highlighting the widgets at their correct position.

    I've tried adding a delay before running the tutorial, but the tutorial doesn't start if the length of the delay is too long.

    Any help would be appreciated!

    opened by lsribeiro 3
  • issues with tutorial_coach_mark and showModalBottomSheet

    issues with tutorial_coach_mark and showModalBottomSheet

    If I have an showModalBottomSheet which is open along the keyboard with isScrollControlled: true it is reloading continuous the whole app behind the bottom sheet during the animation. It only occurs when the tutorial coach has been run previously.

    Is this a bug?

    opened by desmeit 0
  • Avoid/block target tab for 2 seconds, after previous target tab

    Avoid/block target tab for 2 seconds, after previous target tab

    Hey there, love the package and trying to implement a cool tutorial with it.. But here is my problem:

    When user presses quickly and many times on targets, it likely to crash the animation between animating.. How can I block the user's gesture for like 2 seconds after he presses the first target? After 2secs enable the press again and so on.. When I create the TutorialCoachMark with the targets, the enableTargetTab Option in the TargetFocus won´t update anymore..

    Thank you very much. :)

    opened by adschi 0
  • Disable the bouncing animation of the TargetFocus

    Disable the bouncing animation of the TargetFocus

    I want to disable the bouncing effect that we see on the TargetFocus. I want the TargetFocus to remain static/to not animate. Is there any way to do that?

    opened by pratik97179 1
  • Throw custom Error/Exception when target position is not found

    Throw custom Error/Exception when target position is not found

    Hi there, great package!

    I was wondering if it would be possible to add a feature which throws a custom Error/Exception when the target position is not found, so that it can be handled. E.g., when the widget is not shown in a specific UI flow, then the following is printed:

    "TutorialCoachMark (ERROR): It was not possible to obtain target position."

    I would like to catch this and show the tutorial again next time on a different flow, so I was wondering if the following would work in util.dart.

    import 'package:flutter/widgets.dart';
    import 'package:tutorial_coach_mark/src/target/target_focus.dart';
    import 'package:tutorial_coach_mark/src/target/target_position.dart';
    
    enum ShapeLightFocus { Circle, RRect }
    
    TargetPosition? getTargetCurrent(TargetFocus target,
    //!
        {bool throwNotFoundException = false}) {
      if (target.keyTarget != null) {
        var key = target.keyTarget!;
    
        try {
          final RenderBox renderBoxRed =
              key.currentContext!.findRenderObject() as RenderBox;
          final size = renderBoxRed.size;
          final state =
              key.currentContext!.findAncestorStateOfType<NavigatorState>();
          Offset offset;
          if (state != null) {
            offset = renderBoxRed.localToGlobal(Offset.zero,
                ancestor: state.context.findRenderObject());
          } else {
            offset = renderBoxRed.localToGlobal(Offset.zero);
          }
    
          return TargetPosition(size, offset);
        } catch (e) {
    //!
          if (throwNotFoundException) {
            throw TargetNotFoundException(
                'TutorialCoachMark (ERROR): It was not possible to obtain target position.');
          } else {
            print(
                "TutorialCoachMark (ERROR): It was not possible to obtain target position.");
            return null;
          }
        }
      } else {
        return target.targetPosition;
      }
    }
    
    abstract class TutorialCoachMarkController {
      void next();
      void previous();
      void skip();
    }
    
    extension StateExt on State {
      void safeSetState(VoidCallback call) {
        if (mounted) {
          // ignore: invalid_use_of_protected_member
          setState(call);
        }
      }
    }
    
    //!
    class TargetNotFoundException implements Exception {
      String message;
      TargetNotFoundException(this.message);
    }
    
    
    opened by juskek 0
Releases(0.5.3)
Owner
Rafael Almeida Barbosa
Mobile Developer(Android/Flutter) In love with solutions that fit in the palm of the hand
Rafael Almeida Barbosa
Flutter Advanced: TensorFlow Lite | Object Detection | YoloV2 | SSD Tutorial ||| SSD Tutorial

tflite_demo A new Flutter project. Getting Started This project is a starting point for a Flutter application. A few resources to get you started if t

Pawan Kumar 70 Nov 28, 2022
In this tutorial, we'll create a Quiz application using AQUEDUCT FRAMEWORK.

Quiz API Dart An application built with aqueduct. Topics Covered How to Setup Aqueduct ? How to write your first REST API ? How to make controllers ?

Pawan Kumar 19 Mar 2, 2022
Tutorial for integrating flutter to your new or existing android app.

AddFlutter2Existing Android App Tutorial for adding flutter to your new or existing android app. Link to tutorial Android Create a Flutter module Let'

Pawan Kumar 21 Jun 27, 2022
A fully responsive BMI calculator app made using flutter and dart with beautiful minimalistic user interface design and easy to use .

BMI_Calculator_Flutter A fully responsive BMI calculator app made using flutter and dart with beautiful minimalistic user interface design and easy to

null 1 Oct 9, 2021
Use dynamic and beautiful card view pagers (horizontal direction) to help you create great apps.

Use dynamic and beautiful card view pagers (horizontal direction) to help you create great apps. Preview Mobile Vertical Card Pager Web Web Link Insta

Jeongtae Kim 27 Dec 9, 2022
Beautiful Nike Web Design Concept With Flutter Beautiful Nike Web Design Concept With Flutter

flutter_web_nike A new Flutter project. Getting Started This project is a starting point for a Flutter application. A few resources to get you started

Pawan Kumar 23 Jan 28, 2022
Let's create a complete Flutter Quiz App UI from scratch with a beautiful design.

Flutter Tutorial - Quiz App UI Let's create a complete Flutter Quiz App UI from scratch with a beautiful design. Social Media Preview Watch Video ⚡

Johannes Milke 61 Dec 29, 2022
Tutorial about routes, push and pushNamed

flutterroutes A new Flutter project. Getting Started This project is a starting point for a Flutter application. A few resources to get you started if

Pawan Kumar 22 Mar 12, 2022
In this tutorial we will be reviewing Stateful and Stateless Widgets as well as learning about the fundamental building blocks of Object Oriented Programming (OOP

In this tutorial we will be reviewing Stateful and Stateless Widgets as well as learning about the fundamental building blocks of Object Oriented Programming (OOP) - Classes and Objects.

pedrozopayares 0 Oct 3, 2021
2 Flutter PWA Tutorial - 1/2

got_nagpur A new Flutter project. Getting Started This project is a starting point for a Flutter application. A few resources to get you started if th

Pawan Kumar 27 Aug 27, 2022
Flutter Advanced: ARCore Tutorial | Sceneform | Exploring New Possibilities || Exploring New Possibilities

flutter_ar A new Flutter project. Getting Started This project is a starting point for a Flutter application. A few resources to get you started if th

Pawan Kumar 58 Oct 13, 2022
Flutter Advanced: PDF Viewer Tutorial Android & IOS | From URL & Asset | From URL & Asset

flutterpdfview A new Flutter project. Getting Started This project is a starting point for a Flutter application. A few resources to get you started i

Pawan Kumar 71 Jun 24, 2022
Collection of all the widgets with their tutorial

Flutter Widgets A collection of flutter widgets with tutorials. Season 1 Episode 1 - Sized Box Episode 2 - Animated Builder Episode 3 - Draggable and

Pawan Kumar 147 Oct 4, 2022
The objective of this tutorial is to learn about asynchronous programming in Dart.

The objective of this tutorial is to learn about asynchronous programming in Dart. We'll look at how to carry out time consuming tasks such as getting device location and networking to get data from the internet.

Hritik Kumar 1 Oct 5, 2021
Real short video app with firebase and pixels API.Where you can create a short video with pixels' stock videos and also merge your audio.

Real short video app with firebase and pixels API.Where you can create a short video with pixels' stock videos and also merge your audio.

Ansh rathod 55 Dec 26, 2022
Easy and Fast internationalization for your Flutter Apps

Easy and Fast internationalization for your Flutter Apps Why easy_localization? ?? Easy translations for many languages ?? Load translations as JSON,

Aye7 672 Dec 18, 2022
Flutter Package to implement Feedback System in your @Flutter project. Taking Feedback from users made Easy!

Flutter App Feedback Taking feedback from the user made easy! Simply integrate flutter_app_feedback package into your Flutter project and you are read

Mihir Paldhikar 2 Nov 16, 2021
Poi-Poi-ToDo - A Task Management App where you can create task & maintain your daily life easily

"Poi Poi Todo" is a Task Management App where you can create task & maintain your daily life easily. In this app we use Path Provider, SQLite Database, DateTime Picker etc flutter pakages.

CPAD-Gazipur 8 Oct 30, 2022