Flutter for Beginners: Building Your First Cross-Platform App
Widgets, StatelessWidget vs StatefulWidget, layout basics, and calling a REST API — everything needed to build a first real Flutter screen.
Flutter's built-in setState works fine for a single screen, but any real app needs a way to share state across multiple, unrelated widgets — a logged-in user, a shopping cart, a theme preference. Provider and Riverpod are the two most common answers, and they solve the same problem differently enough to matter.
class CartModel extends ChangeNotifier {
final List<Item> _items = [];
List<Item> get items => _items;
void add(Item item) {
_items.add(item);
notifyListeners(); // tells every listening widget to rebuild
}
}
// Register it above the widgets that need it
ChangeNotifierProvider(create: (_) => CartModel(), child: MyApp());
// Read it anywhere below
final cart = context.watch<CartModel>();
context.watch rebuilds the widget when notifyListeners() fires; context.read reads the current value once, without subscribing to future changes — useful inside a button's onPressed, where you don't want a rebuild.
Riverpod (by the same author as Provider) fixes several structural issues: providers are declared outside the widget tree entirely, so they're accessible without a BuildContext, fully testable in isolation, and compile-time safe against the classic "no provider found" runtime error.
final cartProvider = StateNotifierProvider<CartNotifier, List<Item>>(
(ref) => CartNotifier(),
);
class CartNotifier extends StateNotifier<List<Item>> {
CartNotifier() : super([]);
void add(Item item) {
state = [...state, item]; // new list — Riverpod compares by reference
}
}
class CartScreen extends ConsumerWidget {
@override
Widget build(BuildContext context, WidgetRef ref) {
final items = ref.watch(cartProvider);
return ListView(children: items.map((i) => Text(i.name)).toList());
}
}
Provider relies on BuildContext to find providers up the widget tree — simple, but it means providers can't easily be read from outside a widget (a background service, for example). Riverpod's providers are just global, statically-analyzable objects — no context needed anywhere, which is also why Riverpod catches "provider not found" mistakes at compile time instead of at runtime.
For a new project today, Riverpod is the safer long-term choice — better testability, no context dependency, and active development from its author. Provider is still perfectly reasonable for a smaller app or when working in an existing codebase already built on it; migrating an established app for its own sake is rarely worth the churn.
Widgets, StatelessWidget vs StatefulWidget, layout basics, and calling a REST API — everything needed to build a first real Flutter screen.