Flutter Widget Basics
Widget is the basic building block of Flutter UI.
In Flutter, everything is a Widget, from simple text to complex page layouts are all composed of Widgets.
What is a Widget?
Widget is a Dart class that describes the appearance and behavior of UI. Every Flutter app is essentially a tree structure composed of Widgets, called the Widget tree.
Example: The simplest Widget example
// Define a simple Widget
class HelloWidget extends StatelessWidget {
const HelloWidget({super.key});
@override
Widget build(BuildContext context) {
// Return a Text Widget that displays "Hello World"
return const Text('Hello World');
}
}
StatelessWidget - stateless component
StatelessWidget is an immutable Widget. Once created, its properties cannot be changed. It is suitable for scenarios that display static content, such as text, images, icons, etc.
Basic Structure
Example: StatelessWidget Example
// Define a stateless widget
class MyTextWidget extends StatelessWidget {
// Constructor parameters (immutable properties)
final String text;
final Color textColor;
final double fontSize;
// Use const constructor
const MyTextWidget({
super.key,
required this.text,
this.textColor = Colors.black,
this.fontSize = 16.0,
});
@override
Widget build(BuildContext context) {
// build method returns another Widget
return Text(
text,
style: TextStyle(
color: textColor,
fontSize: fontSize,
),
);
}
}
// Usage example
// MyTextWidget(text: 'Hello', textColor: Colors.red)
Applicable scenarios
- Static text display
- Image display
- Icon display
- Layout that does not change over time
StatefulWidget - stateful component
StatefulWidget is a mutable Widget that can update the UI as internal data changes. It is suitable for scenarios that need to respond to user interactions or data changes.
Basic Structure
Example: StatefulWidget example
// Define a stateful widget
class CounterWidget extends StatefulWidget {
const CounterWidget({super.key});
@override
State<CounterWidget> createState() => _CounterWidgetState();
}
// State class holds mutable state
class _CounterWidgetState extends State<CounterWidget> {
// Counter state
int _count = 0;
// Method to increment the counter
void _increment() {
// Call setState to notify the framework that the state has changed
setState(() {
_count++;
});
}
@override
Widget build(BuildContext context) {
return Column(
mainAxisAlignment: MainAxisAlignment.center,
children: [
// Display current count
Text(
'Counter: $_count',
style: const TextStyle(fontSize: 24),
),
const SizedBox(height: 16),
// Increment button
ElevatedButton(
onPressed: _increment,
child: const Text('Increase'),
),
],
);
}
}
setState method
In StatefulWidget, if the state changes, you must callsetStatethe method to notify Flutter to rebuild the UI.
If the state is modified but setState is not called, the UI will not update. This is because Flutter uses setState to determine whether the Widget needs to be rebuilt.
Applicable scenarios
- Dynamic data such as counters, timers, etc.
- Form input
- Adding, deleting, and modifying list items
- User interaction response
build method
Every Widget must implementbuildthe method, which returns a part of the Widget tree.
Basic rules
| Rules | Description |
|---|---|
| Return Value | It must return a Widget, and cannot return null. |
| Parameter | Receives a BuildContext parameter, representing the position in the Widget tree. |
| Call timing | First build, parent Widget rebuild, and state changes |
Example: build method example
Widget build(BuildContext context) {
// BuildContext provides access to themes, theme information, etc.
// For example, get the current theme color
final theme = Theme.of(context);
// Return a Widget
return Container(
padding: const EdgeInsets.all(16.0),
decoration: BoxDecoration(
color: theme.colorScheme.primaryContainer,
borderRadius: BorderRadius.circular(8),
),
child: Text(
'Hello Flutter',
style: TextStyle(
color: theme.colorScheme.onPrimaryContainer,
fontSize: 20,
),
),
);
}
Commonly used basic Widgets
Text - text display
Example: Text Widget
const Text('Hello World')
// Text with style
Text(
'Hello Flutter',
style: TextStyle(
fontSize: 20,
fontWeight: FontWeight.bold,
color: Colors.blue,
),
)
// Text with optional style
Text.rich(
TextSpan(
text: 'Hello ',
children: [
TextSpan(
text: 'Flutter',
style: TextStyle(fontWeight: FontWeight.bold),
),
],
),
)
Icon
Example: Icon Widget
const Icon(Icons.home) // Home icon
const Icon(Icons.star, color: Colors.yellow) // Colored star
const Icon(Icons.settings, size: 48) // Settings icon of size 48
Image
Example: Image Widget
Image.network('https://example.com/image.png')
// Load image from local assets
Image.asset('assets/images/logo.png')
// Load image from file
Image.file(File('path/to/image.png'))
// Image with parameters
Image.network(
'https://example.com/image.png',
width: 200,
height: 200,
fit: BoxFit.cover,
)
Button
Example: Button Widget
TextButton(
onPressed: () { /* Handle click */ },
child: const Text('Text button'),
)
// Floating action button
FloatingActionButton(
onPressed: () { /* Handle click */ },
child: const Icon(Icons.add),
)
// ElevatedButton - button with shadow
ElevatedButton(
onPressed: () { /* Handle click */ },
child: const Text('Submit'),
)
// Button with icon
ElevatedButton.icon(
onPressed: () { /* Handle click */ },
icon: const Icon(Icons.save),
label: const Text('Save'),
)
Widget Combination
Flutter's power lies in its ability to combine Widgets. Through nesting, a simple Widget can build a complex UI.
Example: Composite Widget
const MyCard({super.key});
@override
Widget build(BuildContext context) {
return Card(
// Card is a Material Design card component
elevation: 4, // Shadow depth
child: Padding(
padding: const EdgeInsets.all(16.0),
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
// Title
const Text(
'Card title',
style: TextStyle(
fontSize: 20,
fontWeight: FontWeight.bold,
),
),
const SizedBox(height: 8),
// Description text
const Text(
'This is the description of the card, which can contain multiple lines of text.',
),
const SizedBox(height: 16),
// Button row
Row(
mainAxisAlignment: MainAxisAlignment.end,
children: [
TextButton(
onPressed: () {},
child: const Text('Cancel'),
),
const SizedBox(width: 8),
ElevatedButton(
onPressed: () {},
child: const Text('Confirm'),
),
],
),
],
),
),
);
}
}