Bring your screens to life by responding to button taps and updating the display with setState.
Open this lesson in KodokonOur screens are pretty but frozen: it's time to respond to the user's gestures. The standard Material 3 button is ElevatedButton. It takes two essential parameters: child, its displayed content (often a Text), and onPressed, the function to run when you tap it. A function passed in this way as a parameter is called a callback: you don't call it yourself, Flutter calls it for you at the right moment. The () { ... } syntax creates an anonymous function, with no name, written directly on the spot.
import 'package:flutter/material.dart';
Widget buildHelloButton() {
return ElevatedButton(
onPressed: () {
debugPrint("Button pressed!");
},
child: const Text("Tap here"),
);
}debugPrint writes to the console... but the screen itself doesn't change. Why? Because a StatelessWidget (a stateless widget) is frozen: once built, it can no longer change. To display a value that evolves, you need a StatefulWidget (a stateful widget). The state, that's the set of data that can change during the widget's life: a counter, a checked box, some typed text... A StatefulWidget always goes hand in hand with a State class, which keeps this data and contains the build method. Here's the great classic of the early days: the counter.
import 'package:flutter/material.dart';
void main() {
runApp(const CounterApp());
}
class CounterApp extends StatelessWidget {
const CounterApp({super.key});
@override
Widget build(BuildContext context) {
return MaterialApp(
theme: ThemeData(useMaterial3: true),
home: const CounterPage(),
);
}
}
class CounterPage extends StatefulWidget {
const CounterPage({super.key});
@override
State<CounterPage> createState() =>
_CounterPageState();
}
class _CounterPageState extends State<CounterPage> {
int count = 0;
@override
Widget build(BuildContext context) {
return Scaffold(
body: Center(
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: [
Text(
"Counter: $count",
style: const TextStyle(fontSize: 32),
),
const SizedBox(height: 16),
ElevatedButton(
onPressed: () {
setState(() {
count = count + 1;
});
},
child: const Text("+1"),
),
],
),
),
);
}
}The mechanism comes in three steps. 1) The count variable lives in _CounterPageState: that's the state. 2) On each tap, the callback calls setState: this method modifies the state and tells Flutter that something has changed. 3) Flutter then re-runs build, which redraws the screen with the new value of count. Remember the philosophy: you never change the screen directly, you change the data, and Flutter takes care of the display.