Lesson 21 of 51 beginner

Buttons & User Interactions

Making Your App Respond to Taps

Open interactive version (quiz + challenge)

Real-world analogy

Buttons are like doorbells — they sit there looking pretty, but the magic happens when someone presses them. The doorbell (button) triggers an action (the ring), and different doorbells (ElevatedButton, TextButton, IconButton) look different but all do the same job: respond to a press!

What is it?

Buttons and interaction widgets are how users communicate with your app. Flutter provides several button types (ElevatedButton, TextButton, IconButton, OutlinedButton, FloatingActionButton) for different visual emphasis levels, plus GestureDetector and InkWell for making any widget respond to touch gestures.

Real-world relevance

Every app screen has buttons. The team_mvp_kit project uses styled ElevatedButtons for primary actions, TextButtons for navigation links, and GestureDetector for custom interactive cards. Choosing the right button type creates clear visual hierarchy that guides users through your app.

Key points

Code example

import 'package:flutter/material.dart';

class InteractionDemo extends StatefulWidget {
  const InteractionDemo({super.key});

  @override
  State<InteractionDemo> createState() => _InteractionDemoState();
}

class _InteractionDemoState extends State<InteractionDemo> {
  int _counter = 0;
  bool _isFavorite = false;

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(
        title: const Text('Buttons Demo'),
        actions: [
          IconButton(
            icon: Icon(
              _isFavorite ? Icons.favorite : Icons.favorite_border,
              color: _isFavorite ? Colors.red : null,
            ),
            onPressed: () {
              setState(() {
                _isFavorite = !_isFavorite;
              });
            },
          ),
        ],
      ),
      body: Center(
        child: Column(
          mainAxisAlignment: MainAxisAlignment.center,
          children: [
            Text('Counter: $_counter',
                style: Theme.of(context).textTheme.headlineMedium),
            const SizedBox(height: 24),
            ElevatedButton(
              onPressed: () => setState(() => _counter++),
              child: const Text('Increment'),
            ),
            const SizedBox(height: 12),
            OutlinedButton(
              onPressed: () => setState(() => _counter--),
              child: const Text('Decrement'),
            ),
            const SizedBox(height: 12),
            TextButton(
              onPressed: () => setState(() => _counter = 0),
              child: const Text('Reset'),
            ),
          ],
        ),
      ),
      floatingActionButton: FloatingActionButton(
        onPressed: () => setState(() => _counter += 10),
        child: const Icon(Icons.add),
      ),
    );
  }
}

Line-by-line walkthrough

  1. 1. Import the Material Design package for all widgets
  2. 2.
  3. 3. Define InteractionDemo as a StatefulWidget since buttons will change state
  4. 4. Pass the key to the parent constructor
  5. 5.
  6. 6. Create the mutable State class
  7. 7. Declare a counter variable starting at 0
  8. 8. Declare a boolean to track favorite status
  9. 9.
  10. 10. The build method returns our UI
  11. 11. Return a Scaffold for the page structure
  12. 12. AppBar with a title at the top
  13. 13. The actions list puts widgets on the right side of the AppBar
  14. 14. IconButton toggles between filled and outlined heart icon
  15. 15. Color the heart red when favorited, default otherwise
  16. 16.
  17. 17. When the heart IconButton is pressed...
  18. 18. setState triggers a rebuild with the toggled favorite value
  19. 19.
  20. 20.
  21. 21.
  22. 22. The body is centered on screen
  23. 23. Column arranges children vertically
  24. 24. Center everything along the main axis
  25. 25.
  26. 26. Display the counter value using string interpolation
  27. 27. Style it using the theme's headlineMedium text style
  28. 28. Add 24 pixels of vertical space
  29. 29. ElevatedButton (primary action) increments the counter
  30. 30. setState with arrow function for concise increment
  31. 31. Label says 'Increment'
  32. 32. Add 12 pixels of space
  33. 33. OutlinedButton (secondary action) decrements the counter
  34. 34. setState decrements the counter
  35. 35. Label says 'Decrement'
  36. 36. Add 12 pixels of space
  37. 37. TextButton (tertiary action) resets the counter
  38. 38. setState resets counter to 0
  39. 39. Label says 'Reset'
  40. 40.
  41. 41.
  42. 42.
  43. 43. FloatingActionButton floats at bottom-right
  44. 44. Pressing it adds 10 to the counter at once
  45. 45. Plus icon inside the FAB
  46. 46.
  47. 47.

Spot the bug

ElevatedButton(
  onPressed: print('Button pressed!'),
  child: Text('Press Me'),
)
Need a hint?
Look at what onPressed expects versus what you are passing...
Show answer
onPressed expects a function (VoidCallback), but print('Button pressed!') calls print immediately and passes its return value (void) to onPressed. Fix: wrap it in an anonymous function: onPressed: () { print('Button pressed!'); } or onPressed: () => print('Button pressed!')

Explain like I'm 5

Imagine you built a toy robot with buttons on its chest. The big red button (ElevatedButton) makes it walk forward — it sticks out so you notice it first. The small flat label (TextButton) makes it stop — not as attention-grabbing. The round picture button (IconButton) makes it wave. And if you poke the robot anywhere on its belly (GestureDetector), it giggles! Each button looks different but they all do the same thing: wait for you to press them, then do something cool.

Fun fact

Flutter buttons went through a major redesign in Flutter 2.0. The old RaisedButton, FlatButton, and OutlineButton were deprecated in favor of ElevatedButton, TextButton, and OutlinedButton with a much more flexible styling system called ButtonStyle!

Hands-on challenge

Build a screen with four buttons: an ElevatedButton that shows a SnackBar, a TextButton that toggles text between 'ON' and 'OFF', an IconButton that toggles between a filled and outlined heart icon, and a GestureDetector that changes a Container's color on tap. Style each button differently using styleFrom().

More resources

Open interactive version (quiz + challenge) ← Back to course: Flutter & Dart