Declarative UI
The most reassuring thing about Flutter for React developers is that the declarative UI model is identical at its core: your UI is a pure function of state. When state changes, you describe the new UI and the framework figures out what to update. The difference is in the plumbing — Flutter has its own element tree and reconciler instead of the virtual DOM, but the mental model you already have is exactly right.
UI = f(state): same idea, different plumbing
Section titled “UI = f(state): same idea, different plumbing”// React — declarative counterimport { useState } from 'react';
function Counter() { const [count, setCount] = useState(0);
return ( <div> <p>Count: {count}</p> <button onClick={() => setCount(c => c + 1)}> Increment </button> </div> );}// Flutter — declarative counter (StatefulWidget)class Counter extends StatefulWidget { const Counter({super.key});
@override State<Counter> createState() => _CounterState();}
class _CounterState extends State<Counter> { int _count = 0;
@override Widget build(BuildContext context) { return Column( mainAxisSize: MainAxisSize.min, children: [ Text('Count: $_count', style: const TextStyle(fontSize: 24)), ElevatedButton( onPressed: () => setState(() => _count++), child: const Text('Increment'), ), ], ); }}import 'package:flutter/material.dart';
void main() => runApp(const MyApp());
class MyApp extends StatelessWidget {
const MyApp({super.key});
@override
Widget build(BuildContext context) {
return const MaterialApp(
home: Scaffold(
body: Center(
child: Counter(),
),
),
);
}
}
class Counter extends StatefulWidget {
const Counter({super.key});
@override
State<Counter> createState() => _CounterState();
}
class _CounterState extends State<Counter> {
int _count = 0;
@override
Widget build(BuildContext context) {
return Column(
mainAxisSize: MainAxisSize.min,
children: [
Text(
'Count: $_count',
style: const TextStyle(fontSize: 32, fontWeight: FontWeight.bold),
),
const SizedBox(height: 16),
ElevatedButton(
onPressed: () => setState(() => _count++),
child: const Text('Increment', style: TextStyle(fontSize: 18)),
),
const SizedBox(height: 8),
OutlinedButton(
onPressed: () => setState(() => _count = 0),
child: const Text('Reset'),
),
],
);
}
}