Your First Flutter Widgets: Simple Examples, Big Impact

Maxim Gorin
5 min readAug 29, 2024

--

In the world of Flutter, everything revolves around widgets. They are the building blocks of your app’s user interface (UI). Whether you’re creating a custom button, an interactive form, or a complex animation, it all comes down to widgets. Mastering their use is key to building dynamic and visually appealing apps that stand out.

Flutter — Build apps for any screen

This article is part of a beginner-friendly series designed to guide you through the fundamentals of Flutter and Dart. In the previous article, “Your Code, Your Rules: Controlling Flow and Objects in Dart”, we explored the basics of programming with Dart, focusing on control flow and object-oriented programming. Now, we’re moving forward to understanding widgets — the core components you’ll use to bring your Flutter apps to life.

And the best part? You can follow along and practice everything in real-time using DartPad, without any setup or installation.

What Are Widgets and Their Role in Flutter?

Widgets are the fundamental elements of a Flutter app’s UI. They define how your app’s interface should look and behave, whether it’s a static text or a dynamic interactive component. In Flutter, every visual and non-visual element, from a simple icon to a layout structure, is a widget.

Why Widgets Matter: In Flutter, widgets are the tools that enable you to craft intricate UIs from simple components. They allow you to define and control every aspect of your app’s design and interaction in a reusable and scalable way.

Think of widgets as the DNA of your app’s interface — they determine how every part of your app looks and interacts with the user.

Stateless and Stateful Widgets

In Flutter, widgets are categorized into two main types: Stateless and Stateful. The difference between them lies in their ability to change.

Stateless Widgets

Stateless widgets are immutable, meaning that once they are built, they cannot change. They are ideal for elements that don’t need to update dynamically, such as static text labels, icons, or simple decorations.

Example: Creating a Custom Divider

Let’s move beyond the typical examples and create a custom divider that can be reused across different parts of an app. This divider will include a decorative line and an optional label.

import 'package:flutter/material.dart';

class CustomDivider extends StatelessWidget {
final String label;

CustomDivider({this.label = ''});

@override
Widget build(BuildContext context) {
return Row(
children: [
Expanded(
child: Divider(
color: Colors.grey,
thickness: 2,
),
),
if (label.isNotEmpty)
Padding(
padding: const EdgeInsets.symmetric(horizontal: 8.0),
child: Text(
label,
style: TextStyle(fontWeight: FontWeight.bold),
),
),
Expanded(
child: Divider(
color: Colors.grey,
thickness: 2,
),
),
],
);
}
}

void main() {
runApp(
MaterialApp(
home: Scaffold(
appBar: AppBar(title: Text('Custom Divider Example')),
body: Column(
children: [
Text('Section 1'),
CustomDivider(label: 'BREAK'),
Text('Section 2'),
Text('Section 3'),
CustomDivider(),
Text('Section 4'),
Text('Section 5'),
],
),
),
),
);
}

In this example, CustomDivider is a stateless widget that displays a horizontal line with an optional label in the center. This is a simple but practical component that you might use to separate content sections in your app.

Example: Creating a Custom Divider

Stateful Widgets

Stateful widgets, on the other hand, can change over time in response to user actions or other events. They maintain a state that updates whenever necessary, making them ideal for interactive elements such as forms, counters, or animations.

Example: An Interactive Color Picker

Instead of a basic counter, let’s create an interactive color picker that allows users to select a color and see the background change accordingly.

import 'package:flutter/material.dart';

class ColorPicker extends StatefulWidget {
@override
_ColorPickerState createState() => _ColorPickerState();
}

class _ColorPickerState extends State<ColorPicker> {
Color _selectedColor = Colors.white;

void _changeColor(Color color) {
setState(() {
_selectedColor = color;
});
}

@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(title: Text('Color Picker')),
body: Container(
color: _selectedColor,
child: Center(
child: IntrinsicWidth(
child: Column(
mainAxisAlignment: MainAxisAlignment.spaceEvenly,
crossAxisAlignment: CrossAxisAlignment.stretch,
children: [
ElevatedButton(
onPressed: () => _changeColor(Colors.red),
child: Text('Red'),
style: ElevatedButton.styleFrom(
backgroundColor: Colors.red,
foregroundColor: Colors.white,
),
),
ElevatedButton(
onPressed: () => _changeColor(Colors.green),
child: Text('Green'),
style: ElevatedButton.styleFrom(
backgroundColor: Colors.green,
foregroundColor: Colors.white,
),
),
ElevatedButton(
onPressed: () => _changeColor(Colors.blue),
child: Text('Blue'),
style: ElevatedButton.styleFrom(
backgroundColor: Colors.blue,
foregroundColor: Colors.white,
),
),
],
),
),
),
),
);
}
}

void main() {
runApp(MaterialApp(home: ColorPicker()));
}

Here, ColorPicker is a stateful widget that allows users to change the background color by pressing buttons. Each button corresponds to a different color, and the background updates in real-time.

Example: An Interactive Color Picker

Building a Simple User Interface with Widgets

Now that we’ve covered the theory, let’s apply it by building a simple, yet unique user interface that showcases both stateless and stateful widgets in action.

Example: A Custom Themed Profile Card

We will create a user profile card that displays the user’s information and allows toggling between light and dark themes.

import 'package:flutter/material.dart';

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

class MyApp extends StatelessWidget {
@override
Widget build(BuildContext context) {
return MaterialApp(
theme: ThemeData.light(),
home: ProfilePage(),
);
}
}

class ProfilePage extends StatefulWidget {
@override
_ProfilePageState createState() => _ProfilePageState();
}

class _ProfilePageState extends State<ProfilePage> {
bool _isDarkTheme = false;

void _toggleTheme() {
setState(() {
_isDarkTheme = !_isDarkTheme;
});
}

@override
Widget build(BuildContext context) {
return MaterialApp(
theme: _isDarkTheme ? ThemeData.dark() : ThemeData.light(),
home: Scaffold(
appBar: AppBar(
title: Text('Profile Card'),
leading: IconButton(
icon: Icon(Icons.brightness_6),
onPressed: _toggleTheme,
),
),
body: Center(
child: Card(
elevation: 8.0,
shape: RoundedRectangleBorder(borderRadius: BorderRadius.circular(10.0)),
child: Padding(
padding: const EdgeInsets.all(16.0),
child: Column(
mainAxisSize: MainAxisSize.min,
children: [
CircleAvatar(radius: 40.0),
SizedBox(height: 16.0),
Text('John Doe', style: TextStyle(fontSize: 24.0)),
Text('Flutter Developer', style: TextStyle(fontSize: 16.0)),
],
),
),
),
),
),
);
}
}

Explanation

Example: A Custom Themed Profile Card
  • The ProfilePage widget allows toggling between light and dark themes, demonstrating how stateful widgets can be used to manage theme settings dynamically.
  • The user profile card, which includes a photo, name, and title, is a combination of several stateless widgets that together create a cohesive UI component.

This example demonstrates how to combine widgets in creative ways to build a functional and aesthetically pleasing user interface.

Conclusion

Widgets are the foundation of everything you build in Flutter. By mastering both stateless and stateful widgets, you can create dynamic, interactive, and visually appealing apps. The unique examples provided here show how to move beyond the basics and craft components that add real value and personality to your applications. Experiment with these concepts, and see how you can innovate and enhance the user experience in your own Flutter projects.

--

--

Maxim Gorin
Maxim Gorin

Written by Maxim Gorin

Team lead in mobile development with a passion for Fintech and Flutter. Sharing insights and stories from the tech and dev world on this blog.

No responses yet