How to Create Flutter Material3 Buttons from Scratch: A Beginner's Guide

Buttons are material3 components that provide the user a single tap facility for taking actions, making choices, submitting forms, saving data, opening a new page, etc. 

Buttons are triggered when the user taps on them. They are the most commonly used feature provided in almost all the flutter apps.


import 'package:flutter/material.dart';

const _rowDivider = SizedBox(width: 10);
const _colDivider = SizedBox(height: 30);

void Function()? handlePressed(
    BuildContext context, bool isDisabled, String buttonName) {
  return isDisabled
      ? null
      : () {
    final snackBar = SnackBar(
      content: Text(
        'Yay! $buttonName is clicked!',
        style: TextStyle(color: Theme.of(context).colorScheme.surface),
      ),
      action: SnackBarAction(
        textColor: Theme.of(context).colorScheme.surface,
        label: 'Close',
        onPressed: () {},
      ),
    );

    ScaffoldMessenger.of(context).showSnackBar(snackBar);
  };
}


class MaterialButtonRoute extends StatefulWidget {

  const MaterialButtonRoute({super.key});

  @override
  MaterialButtonRouteState createState() => MaterialButtonRouteState();
}


class MaterialButtonRouteState extends State<MaterialButtonRoute> {

  @override
  void initState() {
    super.initState();
  }

  @override
  Widget build(BuildContext context) {

    return Scaffold(
      body: SingleChildScrollView(
        padding: const EdgeInsets.all(15),
        scrollDirection: Axis.vertical,
        child: Column(
          children: <Widget>[

            _colDivider,
            Row(
              children: const <Widget>[
                Spacer(),
                ButtonsWithoutIcon(isDisabled: true),
                Spacer(),
                ButtonsWithIcon(),
                Spacer(),
                ButtonsWithoutIcon(isDisabled: false),
                Spacer(),
              ],
            ),
            _colDivider,
            Row(
              children: const <Widget>[
                Spacer(),
                FloatingActionButtons(),
                Spacer(),
              ],
            ),

          ],
        ),
      ),
    );
  }

}


class ButtonsWithoutIcon extends StatelessWidget {
  final bool isDisabled;

  const ButtonsWithoutIcon({super.key, required this.isDisabled});

  @override
  Widget build(BuildContext context) {
    return

      IntrinsicWidth(
        child:
        // Column to contain buttons
        Column(
          // Aligns all widgets in the column to stretch along the cross axis
          crossAxisAlignment: CrossAxisAlignment.stretch,
          children: <Widget>[

            // Elevated button
            ElevatedButton(
              // Calls the handlePressed function when the button is pressed
              onPressed: handlePressed(context, isDisabled, "ElevatedButton"),
              // Text to be displayed on the button
              child: const Text("Elevated"),
            ),
            // Vertical divider to separate buttons
            _colDivider,

            // Filled button
            ElevatedButton(
              // Defines the style of the button
              style: ElevatedButton.styleFrom(
                // Foreground color
                foregroundColor: Theme.of(context).colorScheme.onPrimary, backgroundColor: Theme.of(context).colorScheme.primary,
              ).copyWith(elevation: ButtonStyleButton.allOrNull(0.0)),
              // Calls the handlePressed function when the button is pressed
              onPressed: handlePressed(context, isDisabled, "FilledButton"),
              // Text to be displayed on the button
              child: const Text('Filled'),
            ),
            // Vertical divider to separate buttons
            _colDivider,
            // Filled tonal button

            ElevatedButton(
              // Defines the style of the button
              style: ElevatedButton.styleFrom(
                // Foreground color
                foregroundColor: Theme.of(context).colorScheme.onSecondaryContainer, backgroundColor: Theme.of(context).colorScheme.secondaryContainer,
              ).copyWith(elevation: ButtonStyleButton.allOrNull(0.0)),
              // Calls the handlePressed function when the button is pressed
              onPressed: handlePressed(context, isDisabled, "FilledTonalButton"),
              // Text to be displayed on the button
              child: const Text('Filled Tonal'),
            ),
            // Vertical divider to separate buttons
            _colDivider,

            // Outlined button
            OutlinedButton(
              // Calls the handlePressed function when the button is pressed
              onPressed: handlePressed(context, isDisabled, "OutlinedButton"),
              // Text to be displayed on the button
              child: const Text("Outlined"),
            ),
            // Vertical divider to separate buttons
            _colDivider,

            // Text button
            TextButton(
              // Calls the handlePressed function when the button is pressed
                onPressed: handlePressed(context, isDisabled, "TextButton"),
                // Text to be displayed on the button
                child: const Text("Text")),
          ],
        ),



      );
  }
}

class ButtonsWithIcon extends StatelessWidget {
  const ButtonsWithIcon({super.key});
  @override
  Widget build(BuildContext context) {

    return IntrinsicWidth(
      child: Column(
        crossAxisAlignment: CrossAxisAlignment.stretch,
        children: <Widget>[
          ElevatedButton.icon(
            onPressed:
            handlePressed(context, false, "ElevatedButton with Icon"),
            icon: const Icon(Icons.add),
            label: const Text("Icon"),
          ),
          _colDivider,
          ElevatedButton.icon(
            style: ElevatedButton.styleFrom(
              // Foreground color
              foregroundColor: Theme.of(context).colorScheme.onPrimary, backgroundColor: Theme.of(context).colorScheme.primary,
            ).copyWith(elevation: ButtonStyleButton.allOrNull(0.0)),
            onPressed: handlePressed(context, false, "FilledButton with Icon"),
            label: const Text('Icon'),
            icon: const Icon(Icons.add),
          ),
          _colDivider,
          ElevatedButton.icon(
            style: ElevatedButton.styleFrom(
              // Foreground color
              foregroundColor: Theme.of(context).colorScheme.onSecondaryContainer, backgroundColor: Theme.of(context).colorScheme.secondaryContainer,
            ).copyWith(elevation: ButtonStyleButton.allOrNull(0.0)),
            onPressed:
            handlePressed(context, false, "FilledTonalButton with Icon"),
            label: const Text('Icon'),
            icon: const Icon(Icons.add),
          ),
          _colDivider,
          OutlinedButton.icon(
            onPressed:
            handlePressed(context, false, "OutlinedButton with Icon"),
            icon: const Icon(Icons.add),
            label: const Text("Icon"),
          ),
          _colDivider,
          TextButton.icon(
            onPressed: handlePressed(context, false, "TextButton with Icon"),
            icon: const Icon(Icons.add),
            label: const Text("Icon"),
          )
        ],
      ),
    );
  }
}


// This is a stateless widget that displays different variations of floating action buttons
class FloatingActionButtons extends StatelessWidget {
  // key constructor parameter
  const FloatingActionButtons({super.key});

  @override
  Widget build(BuildContext context) {
    return Padding(
      // Adds a padding of 10 pixels vertically to the widget
      padding: const EdgeInsets.symmetric(vertical: 10),
      child: Wrap(
        // Aligns the children evenly along the horizontal axis
        alignment: WrapAlignment.spaceEvenly,
        // Centers the children along the vertical axis
        crossAxisAlignment: WrapCrossAlignment.center,
        children: [
          // Displays a small floating action button
          FloatingActionButton.small(
            // Hero tag for animation
            heroTag: "btn1",
            // Callback function when the button is pressed
            onPressed: () {},
            // Icon to display inside the button
            child: const Icon(Icons.add),
          ),
          _rowDivider,
          // Displays a regular floating action button
          FloatingActionButton(
            // Hero tag for animation
            heroTag: "btn2",
            // Callback function when the button is pressed
            onPressed: () {},
            // Icon to display inside the button
            child: const Icon(Icons.add),
          ),
          _rowDivider,
          // Displays an extended floating action button
          FloatingActionButton.extended(
            // Hero tag for animation
            heroTag: "btn3",
            // Callback function when the button is pressed
            onPressed: () {},
            // Icon to display inside the button
            icon: const Icon(Icons.add),
            // Text label to display next to the icon
            label: const Text("Create"),
          ),
          _rowDivider,
          // Displays a large floating action button
          FloatingActionButton.large(
            // Hero tag for animation
            heroTag: "btn4",
            // Callback function when the button is pressed
            onPressed: () {},
            // Icon to display inside the button
            child: const Icon(Icons.add),
          ),
        ],
      ),
    );
  }
}


  • The code is a Flutter app that demonstrates various button types in Material Design, including ElevatedButton, FilledButton, FilledTonalButton, OutlinedButton, and TextButton. The app uses Scaffold to create the basic structure of the app and the SingleChildScrollView to create a scrollable area.
  • The MaterialButtonRoute class contains the code for the UI of the app, which is defined in the build method. The method creates a column with several rows that contain the different button types.
  • For each button type, a corresponding button is created and added to the Column. The buttons have onPressed handlers that call the handlePressed function, which displays a SnackBar with a message indicating which button was pressed. The handlePressed function takes in three parameters: the BuildContext, a boolean flag to indicate if the button is disabled, and the name of the button.
  • Each button type has a different appearance and behavior, as defined by the Material Design specification. The buttons are either elevated or filled, and some buttons have a tonal color scheme. Some buttons have an outlined appearance and some buttons have a text-only appearance. The code also demonstrates how to style the buttons, such as changing the foreground and background colors.
  • Overall, this code provides a good starting point for using different button types in a Flutter app, and demonstrates how to handle button presses and display messages to the user.
import 'package:flutter/material.dart';

import 'material_buttons.dart';

void main() {
  runApp(const MyApp());
}

class MyApp extends StatelessWidget {
  const MyApp({super.key});

  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      title: 'Flutter Demo',
      theme: ThemeData(
        useMaterial3: true,
        colorSchemeSeed: Colors.pink,
      ),
      home: const MaterialButtonRoute(),
    );
  }
}

..

..

Comments