Given the limited size of mobile screens, a good stategy may be to use Tooltips to give hints and previews to users instead of using labels. This is because Tooltips are only ever displayed temporarily and can be dismissed. Thus they don't occupy any permanent space in your mobile app, thus saving you space.

In this tutorial we will look at solutions allowing us inject or integrate tooltips in our flutter apps.

Simple Native Tooltip Example in Flutter

This is a simple example without use of any third party package.

Step 1: Create Flutter Project

Start by creating your flutter project.

How To Customize Flutter Popup Menu Button Widget | Flutter Tutorial 2022 | Flutter Widgets

How To Customize Flutter Popup Menu...
How To Customize Flutter Popup Menu Button Widget | Flutter Tutorial 2022 | Flutter Widgets

Step 2: Dependencies

No external dependencies are needed for this example.

Step 3: Import Material Package

We will be using the native material package included in the flutter SDK, without any third party solution. So import it into the main.dart file:

import 'package:flutter/material.dart';

Step 4: Create a Tooltip

It's as easy as it gets. You simply need to pass the tooltip as a property of the widget on which you want the tooltip rendered.

For example here's a page with a button at the center that has a tooltip:

  @override
  Widget build(BuildContext context) {
    return new Scaffold(
      appBar: new AppBar(
        title: new Text('Name here'),
      ),
      body: new Container(
        padding: new EdgeInsets.all(32.0),
        child: new Center(
          child: new Column(
            children: <Widget>[
              new Text(_value),
              new IconButton(
                icon: new Icon(Icons.timer), 
                onPressed: _onClicked,
                tooltip: 'Click me',
              ),
            ],
          ),
        ),
      ),
    );
  }

Here is the full code:

main.dart

import 'package:flutter/material.dart';

void main() {
  runApp(new MaterialApp(
    home: new MyApp()
  ));
}

class MyApp extends StatefulWidget {
  @override
  _State createState() => new _State();
}

class _State extends State<MyApp> {

  String _value = '';

  void _onClicked() => setState(() => _value = new DateTime.now().toString());

  @override
  Widget build(BuildContext context) {
    return new Scaffold(
      appBar: new AppBar(
        title: new Text('Name here'),
      ),
      body: new Container(
        padding: new EdgeInsets.all(32.0),
        child: new Center(
          child: new Column(
            children: <Widget>[
              new Text(_value),
              new IconButton(
                icon: new Icon(Icons.timer), 
                onPressed: _onClicked,
                tooltip: 'Click me',
              ),
            ],
          ),
        ),
      ),
    );
  }
}

Step 5: Run

  1. Simply copy the above code into your main.dart file.
  2. Run

(a). Use PHSpeechBubble

It is a custom flutter widget for tooltips and popups within your flutter app based off of the red instagram notification popups.

A flutter widget that emulates a speech bubble. It is modeled after the red Instagram notification popups.

Here's the demo:

Flutter Tooltip Example

Flutter Tooltip

Step 1: Install it

Start by adding the following to your pubspec.yaml:

  speech_bubble: ^0.0.5

Then fetch it by running the following command or clicking the get button:

$ flutter packages get

Step 2: Write Code

Start by importing:

import 'package:speech_bubble/speech_bubble.dart';

Check out example below.

Example

Here's the example code for creating a tooltip in flutter:

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

void main() => runApp(new MyApp());

class MyApp extends StatefulWidget {
  @override
  _MyAppState createState() => new _MyAppState();
}

class _MyAppState extends State<MyApp> {
  @override
  Widget build(BuildContext context) {
    return new MaterialApp(
      home: new Scaffold(
        appBar: new AppBar(
          title: const Text('Plugin example app'),
        ),
        body: new Center(
          child: new SpeechBubble(
            nipLocation: NipLocation.BOTTOM,
            // child: Column(
            //   mainAxisSize: MainAxisSize.min,
            //   children: <Widget>[
            //     Text("Give your users some guided instruction"),
            //     Text("From the inside of a Speech Bubble")
            //   ],
            // ),
            child: Row(
              mainAxisSize: MainAxisSize.min,
              children: <Widget>[
                Icon(
                  Icons.favorite,
                  color: Colors.white,
                ),
                Padding(
                  padding: const EdgeInsets.all(4.0),
                ),
                Text(
                  "1",
                  style: TextStyle(
                    color: Colors.white,
                    fontSize: 18.0,
                  ),
                ),
              ],
            ),
          ),
        ),
      ),
    );
  }
}

Find the code here

Reference

Find complete reference here.

(b). Use Just The Tooltip

A directional tooltip for flutter projects.

Tooltip can be set to prefer any axis but will try to fit in opposite axis if the space is not big enough. This tooltip sizes itself to fit the size of its child.

Step 1: Install it

In your pubspec.yam file add the following dependency:

 just_the_tooltip: ^0.0.3+6

Then fetch it by running the following command or clicking the get button:

$ flutter packages get

Step 2: Write Code

Here's how you create a tooltip using this library:

JustTheTooltip(
  child: Material(
    color: Colors.grey.shade800,
    shape: const CircleBorder(),
    elevation: 4.0,
    child: const Padding(
      padding: EdgeInsets.all(8.0),
      child: Icon(
        Icons.add,
        color: Colors.white,
      ),
    ),
  ),
  content: const Padding(
    padding: EdgeInsets.all(8.0),
    child: Text(
      'Bacon ipsum dolor amet kevin turducken brisket pastrami, salami ribeye spare ribs tri-tip sirloin shoulder venison shank burgdoggen chicken pork belly. Short loin filet mignon shoulder rump beef ribs meatball kevin.',
    ),
  ),
)

Example

Let's look a full tooltip example in flutter:

main.dart

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

void main() => runApp(MyApp());

// ignore: use_key_in_widget_constructors
class MyApp extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      title: 'Flutter Demo',
      theme: ThemeData.dark(),
      home: const DefaultPageExample(),
    );
  }
}

class DefaultPageExample extends StatelessWidget {
  const DefaultPageExample({Key? key}) : super(key: key);

  @override
  Widget build(BuildContext context) {
    const margin = EdgeInsets.all(16.0);

    return Scaffold(
      body: SizedBox.expand(
        child: Align(
          alignment: Alignment.center,
          child: JustTheTooltip(
            tailLength: 20.0,
            preferredDirection: AxisDirection.down,
            margin: margin,
            borderRadius: BorderRadius.circular(16.0),
            offset: 0,
            child: Material(
              color: Colors.grey.shade800,
              shape: const CircleBorder(),
              elevation: 4.0,
              child: const Padding(
                padding: EdgeInsets.all(8.0),
                child: Icon(
                  Icons.add,
                  color: Colors.white,
                ),
              ),
            ),
            content: const Padding(
              padding: EdgeInsets.all(8.0),
              child: Text(
                'Bacon ipsum dolor amet kevin turducken brisket pastrami, salami ribeye spare ribs tri-tip sirloin shoulder venison shank burgdoggen chicken pork belly. ',
              ),
            ),
          ),
        ),
      ),
    );
  }
}

class ScrollExamplePage extends StatelessWidget {
  const ScrollExamplePage({Key? key}) : super(key: key);

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      body: ListView(
        children: List.generate(30, (index) {
          if (index == 15) {
            return JustTheTooltip(
              tailLength: 10.0,
              preferredDirection: AxisDirection.down,
              child: const Material(
                color: Colors.blue,
                shape: CircleBorder(),
                elevation: 4.0,
                child: Padding(
                  padding: EdgeInsets.all(8.0),
                  child: Icon(
                    Icons.touch_app,
                    color: Colors.white,
                  ),
                ),
              ),
              // This is necessary as otherwise the column would only be
              // constrained by the amount of vertical space
              content: IntrinsicHeight(
                child: Column(
                  children: [
                    Container(
                      height: 120,
                      color: Colors.blue,
                      width: double.infinity,
                    ),
                    const SizedBox(height: 8),
                    const Text(
                      'Quia ducimus eius magni voluptatibus ut veniam ducimus. Ullam ab qui voluptatibus quos est in. Maiores eos ab magni tempora praesentium libero. Voluptate architecto rerum vel sapiente ducimus aut cumque quibusdam. Consequatur illo et quos vel cupiditate quis dolores at.',
                    ),
                  ],
                ),
              ),
            );
          }

          return ListTile(title: Text('Item $index'));
        }),
      ),
    );
  }
}

class TooltipAreaExamplePage extends StatefulWidget {
  const TooltipAreaExamplePage({Key? key}) : super(key: key);

  @override
  State<TooltipAreaExamplePage> createState() => _TooltipAreaExamplePageState();
}

class _TooltipAreaExamplePageState extends State<TooltipAreaExamplePage> {
  final titleController = TextEditingController(
    text: 'Lorem ipsum dolor',
  );
  final descriptionController = TextEditingController(
    text: 'Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do '
        'eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim '
        'ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut ',
  );
  final scrollController = ScrollController();

  @override
  void dispose() {
    titleController.dispose();
    descriptionController.dispose();
    scrollController.dispose();
    super.dispose();
  }

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(title: const Text('It goes under me')),
      body: JustTheTooltipArea(
        builder: (context, tooltip, scrim) {
          return Stack(
            fit: StackFit.passthrough,
            children: [
              ListView(
                controller: scrollController,
                children: List.generate(
                  30,
                  (index) {
                    if (index == 15) {
                      return JustTheTooltipEntry(
                        scrollController: scrollController,
                        tailLength: 20.0,
                        preferredDirection: AxisDirection.down,
                        margin: const EdgeInsets.all(16.0),
                        child: const Material(
                          color: Colors.blue,
                          shape: CircleBorder(),
                          elevation: 4.0,
                          child: Padding(
                            padding: EdgeInsets.all(8.0),
                            child: Icon(
                              Icons.touch_app,
                              color: Colors.white,
                            ),
                          ),
                        ),
                        content: Column(
                          mainAxisSize: MainAxisSize.min,
                          children: [
                            TextField(
                              maxLines: null,
                              keyboardType: TextInputType.text,
                              textCapitalization: TextCapitalization.sentences,
                              style: Theme.of(context).textTheme.headline6,
                              controller: titleController,
                            ),
                            const SizedBox(height: 12.0),
                            TextField(
                              controller: descriptionController,
                              maxLines: null,
                              keyboardType: TextInputType.multiline,
                              textCapitalization: TextCapitalization.sentences,
                              style: Theme.of(context).textTheme.subtitle1,
                            ),
                            const SizedBox(height: 16.0),
                            Row(
                              mainAxisAlignment: MainAxisAlignment.spaceAround,
                              children: [
                                Expanded(
                                  child: OutlinedButton(
                                    style: OutlinedButton.styleFrom(
                                      shape: const StadiumBorder(),
                                    ),
                                    onPressed: () {},
                                    child: const Text('overlayExercies'),
                                  ),
                                ),
                                const SizedBox(width: 16.0),
                                Expanded(
                                  child: OutlinedButton(
                                    style: OutlinedButton.styleFrom(
                                      shape: const StadiumBorder(),
                                    ),
                                    onPressed: () {},
                                    child: const Text('overlayCourse'),
                                  ),
                                ),
                              ],
                            )
                          ],
                        ),
                      );
                    }

                    return ListTile(title: Text('Item $index'));
                  },
                ),
              ),
              if (scrim != null) scrim,
              if (tooltip != null) tooltip,
            ],
          );
        },
      ),
    );
  }
}

Find the example here.

Reference

Find full reference here.