Understaing State & Updating UI

Who Owns the State? Callbacks & typedef Parent ↔ Child Communication code snippet import 'dart:math'; import 'package:flutter/material.dart'; void main() { runApp(const MyApp()); } class MyApp extends StatelessWidget { const MyApp({super.key}); @override Widget build(BuildContext context) { return MaterialApp( theme: ThemeData(colorScheme: .fromSeed(seedColor: Colors.deepPurple)), home: const MyHomePage(), ); } } class MyHomePage extends StatefulWidget { const MyHomePage({super.key}); @override State<MyHomePage> createState() => _MyHomePageState(); } class _MyHomePageState extends State<MyHomePage> { int _counter = 0; @override Widget build(BuildContext context) { return Scaffold( body: Column( crossAxisAlignment: .stretch, children: [ SectionView( onTap: (v) async { _counter = v; setState(() {}); return "Something went wrong"; }, ), Text( _counter.toString(), style: TextStyle(fontSize: 55), textAlign: .center, ), SectionView( increment: false, onTap: (n) async { _counter = n; await Future.delayed(Duration(seconds: 2)); setState(() {}); return ""; }, ), ], ), ); } } typedef OnSectionViewChange = Future<String> Function(int); class SectionView extends StatefulWidget { const SectionView({super.key, this.increment = true, required this.onTap}); final bool increment; final OnSectionViewChange onTap; @override State<SectionView> createState() => _SectionViewState(); } class _SectionViewState extends State<SectionView> { bool isLoading = false; String errMsg = ""; Future<void> onTap() async { isLoading = true; setState(() {}); Random random = Random(); final number = random.nextInt(4444); errMsg = await widget.onTap(widget.increment ? number : number * -1); isLoading = false; setState(() {}); } @override Widget build(BuildContext context) { return Expanded( child: Material( color: widget.increment ? Colors.blueAccent : Colors.redAccent, child: Center( child: Column( mainAxisSize: .min, children: [ Text(errMsg), InkWell( onTap: isLoading ? null : onTap, child: isLoading ? Center(child: CircularProgressIndicator()) : Icon( widget.increment ? Icons.add : Icons.remove, size: 55, ), ), ], ), ), ), ); } } Why AlertDialog/BottomSheet UI doesn’t update even with setState Widget state 03 ...

FutureBuilder & StreamBuilder

FutureBuilder Learn Async Data & avoid common mistakes. future codeSnippet import 'dart:math'; import 'package:flutter/material.dart'; void main() => runApp( const MaterialApp(debugShowCheckedModeBanner: false, home: MyHomePage()), ); class MyHomePage extends StatefulWidget { const MyHomePage({super.key}); @override State<MyHomePage> createState() => _MyHomePageState(); } class _MyHomePageState extends State<MyHomePage> { //... Future<int> fetchData() async { await Future.delayed(Duration(seconds: 4)); final number = Random().nextInt(100); return number; } late Future<int> future = fetchData(); @override Widget build(BuildContext context) { return Scaffold( body: Center( child: FutureBuilder<int>( future: future, initialData: 100, builder: (context, AsyncSnapshot snapshot) { if (snapshot.hasError) { return Text(snapshot.error?.toString() ?? ""); } return Column( mainAxisSize: .min, children: [ if (snapshot.connectionState == ConnectionState.waiting) CircularProgressIndicator(), Text(snapshot.data.toString()), ], ); }, ), ), ); } } StreamBuilder Flutter’s Stream and StreamBuilder in detail. It also shows StreamController, StreamSubscription, stream error handling, single-subscription issue and event management with practical examples. ...

App-State with Provider

This video covers Provider in Flutter, including ChangeNotifier, ProxyProvider, FutureProvider, StreamProvider, DevTools, and common Provider mistakes. Code Clone the starter codebase Switch to the provider-start branch Continue from there Or just watch the video: Shared_preference Persistent theme with Shared_preference.

Navigation with Navigator 1

Learn Flutter’s Navigator API with practical examples of push, pop, pushReplacement, pushAndRemoveUntil, popUntil, canPop, and passing data between routes. main.dart import 'package:flutter/cupertino.dart'; import 'package:flutter/material.dart'; void main() { runApp( const MaterialApp(home: HomePage(), debugShowCheckedModeBanner: false), ); } class HomePage extends StatelessWidget { const HomePage({super.key}); @override Widget build(BuildContext context) { return Scaffold( appBar: AppBar(title: const Text("Home")), body: Center( child: ElevatedButton( onPressed: () async { final route = DetailsPage.route(); final result = await Navigator.push(context, route); print(result); }, child: const Text("Go to Details"), ), ), ); } } class DetailsPage extends StatelessWidget { const DetailsPage({super.key, required this.value}); final int value; static MaterialPageRoute route() => MaterialPageRoute( builder: (context) => DetailsPage(value: 123), settings: RouteSettings(arguments: 12312), ); @override Widget build(BuildContext context) { final canPop = ModalRoute.of(context)?.canPop ?? false; return Scaffold( appBar: AppBar(title: const Text("Details")), body: Center( child: Column( spacing: 16, mainAxisSize: .min, children: [ if (canPop) CloseButtonIcon(), ElevatedButton( onPressed: () { Navigator.of(context).pop("hello"); }, child: const Text("pop(result)"), ), ElevatedButton( onPressed: () { Navigator.of(context).pushReplacement(SettingsPage.route()); }, child: const Text("Settings with pushReplacement"), ), ElevatedButton( onPressed: () { Navigator.of(context).push(SettingsPage.route()); }, child: const Text("Settings with push"), ), ElevatedButton( onPressed: () { Navigator.of(context).pushAndRemoveUntil(SettingsPage.route(), ( route, ) { return false; }); }, child: const Text("pushAndRemoveUntil"), ), ], ), ), ); } } class SettingsPage extends StatelessWidget { const SettingsPage({super.key}); static MaterialPageRoute route() => MaterialPageRoute(builder: (_) => SettingsPage()); @override Widget build(BuildContext context) { return Scaffold( appBar: AppBar(title: const Text("Settings")), body: Center( child: Column( mainAxisSize: .min, children: [ ElevatedButton( onPressed: () { Navigator.of(context).popUntil((route) { return route.isFirst; }); }, child: const Text("popUntil(Home)"), ), ElevatedButton( onPressed: () { debugPrint("canPop: ${Navigator.canPop(context)}"); Navigator.maybePop(context); }, child: const Text("canPop / maybePop"), ), ], ), ), ); } }

Flutter Go-Router

GoRouter in Flutter go_router on pub.dev↗ Source Code Setup, Route Configuration & Sub-Routes Previous Navigator 2.0 implementation: https://github.com/yeasin50/lisa_fischer Path Parameters, Query Parameters & Exit Dialog Redirection & Async Redirects ...

Saving key-value pair

Shared_preferences Persistent theme with Shared_preference. this is part of flutter_workshop provider shared_preferences on pub.dev↗

Sqlfite: Persistent todo app

Let’s create a simple todo CRUD application with sqlfite sqflite on pub.dev↗ Checkout Starter code or final code Project setup You can create a new project or just clone the repo. I am using freezed to generate my classes. You can explore my video on freezed or use normal class and create fromJson/toMap method. ...

Hive: Flutter Local Storage

Database Setup & Custom Adapters hive_ce on pub.dev↗ dependencies: hive_ce: latest hive_ce_flutter: latest Basic basic hive import 'package:flutter/material.dart'; import 'package:hive_ce_flutter/hive_flutter.dart'; void main() async { await Hive.initFlutter(); await Hive.openBox("fruits"); runApp( MaterialApp( debugShowCheckedModeBanner: false, home: Scaffold(body: const MyApp()), darkTheme: ThemeData.dark(), themeMode: .dark, ), ); } class MyApp extends StatefulWidget { const MyApp({super.key}); @override State<MyApp> createState() => _MyAppState(); } class _MyAppState extends State<MyApp> { final box = Hive.box("fruits"); @override Widget build(BuildContext context) { return Column( spacing: 24, crossAxisAlignment: .stretch, mainAxisAlignment: .center, children: [ Text( box.get("isDarkMode", defaultValue: false).toString(), textAlign: .center, ), TextButton( onPressed: () { box.put("isDarkMode", null); setState(() {}); }, child: Text("save"), ), TextButton( onPressed: () { box.delete("isDarkMode"); setState(() {}); }, child: Text("delete"), ), ], ); } } Box<T> type specific counter import 'package:flutter/material.dart'; import 'package:hive_ce/hive.dart'; import 'package:hive_ce_flutter/hive_ce_flutter.dart'; void main() async { await Hive.initFlutter(); await Hive.openBox<int>('counter'); runApp( MaterialApp( home: const MyApp(), darkTheme: ThemeData.dark(), themeMode: .dark, ), ); } class MyApp extends StatefulWidget { const MyApp({super.key}); @override State<MyApp> createState() => _MyAppState(); } class _MyAppState extends State<MyApp> { final counter = Hive.box<int>('counter'); @override Widget build(BuildContext context) { return Scaffold( body: Padding( padding: .all(8.0), child: ElevatedButton( onPressed: () { counter.put("v1", counter.get("v1", defaultValue: 0)! + 1); setState(() {}); // to refresh the ui }, child: Text("counter ${counter.get("v1").toString()}"), ), ), ); } } Custom adapter import 'package:flutter/material.dart'; import 'package:hive_ce/hive.dart'; import 'package:hive_ce_flutter/hive_ce_flutter.dart'; class User { new({required this.name, required this.dob}); final String name; final DateTime dob; @override String toString() { return "User(name: $name, dob: $dob)"; } } class UserAdapter extends TypeAdapter<User> { @override final int typeId = 0; @override User read(BinaryReader reader) { return User(name: reader.read(), dob: reader.read()); } @override void write(BinaryWriter writer, User obj) { writer.write(obj.name); writer.write(obj.dob); } } void main() async { await Hive.initFlutter(); Hive.registerAdapter(UserAdapter()); await Hive.openBox<User>('users'); runApp( MaterialApp( home: const MyApp(), darkTheme: ThemeData.dark(), themeMode: .dark, ), ); } class MyApp extends StatefulWidget { const MyApp({super.key}); @override State<MyApp> createState() => _MyAppState(); } class _MyAppState extends State<MyApp> { final controller = TextEditingController(); final userBox = Hive.box<User>("users"); void add() async { await userBox.put(0, User(name: controller.text, dob: DateTime.now())); setState(() {}); } @override Widget build(BuildContext context) { return Scaffold( body: Center( child: Padding( padding: .all(8.0), child: Column( spacing: 24, crossAxisAlignment: .stretch, mainAxisSize: .min, children: [ Text(userBox.get(0).toString()), TextFormField( controller: controller, decoration: InputDecoration(border: OutlineInputBorder()), ), ElevatedButton(onPressed: add, child: Text("add user")), ], ), ), ), ); } } Generate adapter To generate adapters, use hive_ce_generator and build_runner. ...