Your First App
Build your first Nocterm application
In this guide, you'll build an interactive counter app. You'll learn how to create components, manage state, and handle keyboard input.
Create the app
Create a file called counter.dart and add this code:
import 'package:nocterm/nocterm.dart';
void main() {
runApp(const Counter());
}
class Counter extends StatefulComponent {
const Counter({super.key});
@override
State<Counter> createState() => _CounterState();
}
class _CounterState extends State<Counter> {
int _count = 0;
@override
Component build(BuildContext context) {
return Focusable(
focused: true,
onKeyEvent: (event) {
if (event.logicalKey == LogicalKey.space) {
setState(() => _count++);
return true;
}
return false;
},
child: Container(
decoration: BoxDecoration(
border: BoxBorder.all(color: Colors.gray),
),
margin: EdgeInsets.all(8),
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: [
Text('Counter: $_count'),
SizedBox(height: 1),
Text('Press SPACE to increment', style: TextStyle(color: Colors.gray)),
],
),
),
);
}
}
Run the app:
dart run counter.dart
Press the spacebar to increment the counter. Press Ctrl+C to exit.
runApp
The runApp() function starts your app:
void main() {
runApp(const Counter());
}
This function takes your root component and displays it in the terminal. It sets up the terminal (enables raw mode, alternate screen), handles rendering, and processes input events.
StatefulComponent
Counter extends StatefulComponent because it needs to track the count:
class Counter extends StatefulComponent {
const Counter({super.key});
@override
State<Counter> createState() => _CounterState();
}
A StatefulComponent separates the component (which is immutable) from its state (which changes over time). The createState() method creates the state object.
State and setState
The _CounterState class holds the count and rebuilds the UI when it changes:
class _CounterState extends State<Counter> {
int _count = 0;
@override
Component build(BuildContext context) {
// ... UI code
}
}
When you call setState(), Nocterm rebuilds the component with the new state:
setState(() => _count++);
Always wrap state changes in setState(). This tells Nocterm to redraw the UI.
The build method
The build() method returns the UI for your component:
Component build(BuildContext context) {
return Focusable(
// ... component tree
);
}
Nocterm calls build() when:
- The component first appears
- You call
setState() - The parent component rebuilds
Handling keyboard input
The Focusable component captures keyboard events:
Focusable(
focused: true,
onKeyEvent: (event) {
if (event.logicalKey == LogicalKey.space) {
setState(() => _count++);
return true; // Event handled
}
return false; // Not handled, pass to next component
},
child: // ...
)
The onKeyEvent callback receives each key press. Return true if you handled the event, or false to pass it along.
Layout components
The UI uses layout components to arrange text:
Column(
mainAxisAlignment: MainAxisAlignment.center,
children: [
Text('Counter: $_count'),
SizedBox(height: 1),
Text('Press SPACE to increment', style: TextStyle(color: Colors.gray)),
],
)
Columnstacks children verticallymainAxisAlignment.centercenters them verticallySizedBoxadds spacing between elements
Try it yourself
Modify the app to:
- Increment on up arrow, decrement on down arrow
- Add a reset button (press
Rto reset to 0) - Display the count in a different color when it's above 10
- Add a border that changes color based on the count
Next steps
- How terminals work - Understand what's happening under the hood
- Components - Learn about StatelessComponent and StatefulComponent
- Building UIs - Compose complex interfaces