Working with Stateless Widgets
1. Creating StatelessWidget Class
A StatelessWidget is a widget that does not change during its lifetime. It is used for UI elements that do not require state management.
| Property | Description | Example |
|---|---|---|
| key | Optional key to identify the widget in the widget tree. | key: const Key('myWidget') |
| build | Required method that returns the widget tree. | @override Widget build(BuildContext context) { return Text('Hello'); } |
Example: Basic StatelessWidget
class MyWidget extends StatelessWidget {
const MyWidget({Key? key}) : super(key: key);
@override
Widget build(BuildContext context) {
return Text('Hello, Flutter!');
}
}
2. Implementing build() Method
| Method | Description | Return Type |
|---|---|---|
| build(BuildContext context) | Required method for all stateless widgets. Defines the UI of the widget. | Widget |
Example: Basic Stateless Widget
<Widget> MyWidget() {
return Container(
child: Text('Hello, Flutter!'),
);
}
Note: The
build method is called every time the widget needs to be rebuilt, and it must return a Widget.
3. Using Constructor Parameters
| Parameter Type | Description | Usage Example |
|---|---|---|
| required | Ensures a parameter is provided when creating the widget | const MyWidget({required String title}); |
| optional | Allows a parameter to have a default value | const MyWidget({String title = 'Default'}); |
| named | Parameters specified with curly braces {} | const MyWidget({required String title, int? count}); |
| positional | Parameters specified with square brackets [] | const MyWidget([String title = 'Default']); |
Example: Using Constructor Parameters in a Widget
class GreetingWidget extends StatelessWidget {
final String name;
final int age;
const GreetingWidget({
Key? key,
required this.name,
this.age = 0,
}) : super(key: key);
@override
Widget build(BuildContext context) {
return Text('Hello, $name! You are $age years old.');
}
}
4. Understanding Widget Lifecycle
| Phase | Description | Methods | Notes |
|---|---|---|---|
| Construction | Widget is created | MyWidget({Key? key}) : super(key: key); |
Only constructor logic here |
| Mounting | Widget is inserted into the tree | void initState() { ... } |
Called once after the widget is mounted |
| Building | Widget is built and rendered | Widget build(BuildContext context) { ... } |
Core rendering logic |
| Updating | Widget is updated due to changes in state or props | bool updateShouldNotify() { ... } |
Used in ChangeNotifier or InheritedWidget |
| Unmounting | Widget is removed from the tree | void dispose() { ... } |
Clean up resources like streams or timers |
Example: Basic Widget Lifecycle
class MyWidget extends StatefulWidget {
@override
State<MyWidget> createState() => _MyWidgetState();
}
class _MyWidgetState extends State<MyWidget> {
int counter = 0;
@override
void initState() {
super.initState();
print('Widget initialized');
}
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(title: Text('Lifecycle Example')),
body: Center(
child: Text('Counter: $counter'),
),
floatingActionButton: FloatingActionButton(
onPressed: () {
setState(() {
counter++;
});
},
child: Icon(Icons.add),
),
);
}
@override
void dispose() {
print('Widget disposed');
super.dispose();
}
}
Note: The
build method can be called multiple times during the widget's lifecycle, but initState and dispose are called only once.
Key Lifecycle Events
- initState: Initialize state variables and subscriptions
- build: Return the widget tree to render
- dispose: Clean up resources like streams or timers
5. Creating Custom Stateless Widgets
Custom stateless widgets in Flutter are created by extending the StatelessWidget class and implementing the build method. These widgets are immutable and do not maintain any state during their lifecycle.
| Property | Description | Example |
|---|---|---|
| build | Required method that returns a Widget tree. This is where the UI is defined. |
|
Example: Simple Custom Stateless Widget
class GreetingWidget extends StatelessWidget {
@override
Widget build(BuildContext context) {
return Text('Welcome to Flutter!');
}
}
Example: Custom Stateless Widget with Parameters
class ProfileCard extends StatelessWidget {
final String name;
final String title;
ProfileCard({required this.name, required this.title});
@override
Widget build(BuildContext context) {
return Card(
child: Padding(
padding: EdgeInsets.all(16.0),
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Text(name, style: TextStyle(fontSize: 20, fontWeight: FontWeight.bold)),
Text(title, style: TextStyle(fontSize: 16, color: Colors.grey)),
],
),
),
);
}
}
6. Using const Constructors
When building Flutter widgets, using const constructors can significantly improve performance by enabling compile-time optimizations and reducing memory usage.
| Constructor | Description | Usage Example |
|---|---|---|
| const Text() | Creates a text widget with compile-time constants. | const Text("Hello World", style: TextStyle(fontSize: 16)) |
| const Container() | Creates a container widget with immutable properties. | |
| const SizedBox() | Creates a fixed-size widget with no children. | const SizedBox(width: 20, height: 20) |
Note: All arguments to
const widgets must be compile-time constants. This includes strings, numbers, booleans, and null values.Warning: Using
const with mutable values or runtime expressions will cause a compile-time error.Benefits of const Constructors
- Performance: Reduces widget rebuilds and memory allocation.
- Memory Efficiency: Widgets are created once at compile time.
- Reusability: Same widget instance reused across builds.
7. Passing Callbacks as Parameters
| Parameter | Type | Description |
|---|---|---|
| onPressed | VoidCallback? |
Callback function executed when the button is pressed |
| onChanged | ValueChanged<T>? |
Callback function executed when a value changes (e.g., in TextField) |
| onTap | GesturesTapCallback? |
Callback function executed when the widget is tapped |
Example: Button with onPressed callback
TextButton(
onPressed: () {
print('Button pressed!');
},
child: Text('Press Me'),
)
Example: TextField with onChanged callback
TextField(
onChanged: (String value) {
print('Text changed to: $value');
},
decoration: InputDecoration(
hintText: 'Enter text',
),
)
8. Implementing Widget Composition
Example: Basic Widget Composition
import 'package:flutter/material.dart';
class MyWidget extends StatelessWidget {
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(title: Text("Composition Example")),
body: Center(
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: [
Text("Hello"),
Text("World"),
],
),
),
);
}
}
| Property | Description | Usage |
|---|---|---|
| child | The widget that is composed into the parent widget. | Used in widgets like Container, Center, Row. |
| children | A list of widgets that are composed into the parent widget. | Used in Column, Row, ListView. |
Note: Widget composition in Flutter is achieved through the
child and children properties of widgets.
Comparison: Widget Composition vs Inheritance
- Composition: Widgets are built by combining smaller widgets.
- Inheritance: Widgets inherit properties from a parent class (not used in Flutter).
9. Using Key Parameters
| Parameter | Description | Usage |
|---|---|---|
| key | Uniquely identifies a widget in the widget tree | const Key('myKey') |
| GlobalKey | Used to identify widgets globally across the app | GlobalKey<StatefulWidget> key = GlobalKey(); |
| LocalKey | Used to identify widgets within a local scope | LocalKey key = ValueKey('value'); |
Example: Using Key in a List
class MyList extends StatelessWidget {
@override
Widget build(BuildContext context) {
return ListView(
children: [
ListTile(
title: Text('Item 1'),
key: Key('item_1'),
),
ListTile(
title: Text('Item 2'),
key: Key('item_2'),
),
],
);
}
}
Note: Keys are essential for efficient widget rebuilding and state management in Flutter.
10. Creating Reusable UI Components
Reusable UI components in Flutter are created using custom widgets that encapsulate specific UI logic and layout. These components can be composed of multiple widgets and can accept parameters to customize their behavior.
| Component | Description | Usage |
|---|---|---|
| Custom Widget | A widget that extends StatelessWidget or StatefulWidget to encapsulate reusable UI logic. | Define a new class extending StatelessWidget or StatefulWidget with a build method. |
| Widget Parameters | Properties passed to a custom widget to configure its appearance or behavior. | Use constructor parameters in the widget class to accept configuration values. |
Example: Custom Button Widget
class CustomButton extends StatelessWidget {
final String text;
final VoidCallback onPressed;
const CustomButton({Key? key, required this.text, required this.onPressed}) : super(key: key);
@override
Widget build(BuildContext context) {
return ElevatedButton(
onPressed: onPressed,
child: Text(text),
);
}
}
Example: Reusable Card Component
class CustomCard extends StatelessWidget {
final String title;
final String content;
const CustomCard({Key? key, required this.title, required this.content}) : super(key: key);
@override
Widget build(BuildContext context) {
return Card(
child: Padding(
padding: const EdgeInsets.all(16.0),
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Text(title, style: TextStyle(fontSize: 18, fontWeight: FontWeight.bold)),
SizedBox(height: 8),
Text(content),
],
),
),
);
}
}