Last active
March 8, 2024 02:59
-
-
Save Andrious/cf3bc3ebe5463054fb320938b7853553 to your computer and use it in GitHub Desktop.
Two Screens with Three Counters and Print() functions highlight the StatefulWidgets' lifecycle
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
import 'package:flutter/foundation.dart'; | |
import 'package:flutter/material.dart'; | |
/// Highlights UI while debugging. | |
import 'package:flutter/rendering.dart' as debug; | |
void main() => runApp(MyApp()); | |
/// | |
class MyApp extends StatelessWidget { | |
/// | |
MyApp({super.key}) { | |
if (kDebugMode) { | |
print('>>>>>>>>>>>>>>>>>>>>>>>>>'); | |
print( | |
'>>>>>>>>>>>>>>>>>>>>>>>>> MyApp StatelessWidget created. Its constructor called.'); | |
} | |
} | |
@override | |
Widget build(BuildContext context) { | |
if (kDebugMode) { | |
print('>>>>>>>>>>>>>>>>>>>>>>>>> MyApp build() called!'); | |
} | |
/// Set some of these to true and see what happens. | |
/// They're only available in 'debug mode'. | |
/// They're to help during development | |
assert(() { | |
/// Highlights UI while debugging. | |
debug.debugPaintSizeEnabled = false; | |
debug.debugPaintBaselinesEnabled = false; | |
debug.debugPaintPointersEnabled = false; | |
debug.debugPaintLayerBordersEnabled = false; | |
debug.debugRepaintRainbowEnabled = false; | |
debug.debugRepaintTextRainbowEnabled = false; | |
return true; | |
}()); | |
return MaterialApp( | |
title: 'Flutter Demo', | |
theme: ThemeData( | |
colorScheme: ColorScheme.fromSeed(seedColor: Colors.deepPurple), | |
useMaterial3: true, | |
), | |
home: HomePage(), | |
); | |
} | |
} | |
/// | |
class HomePage extends StatefulWidget { | |
/// | |
HomePage({super.key}) { | |
if (kDebugMode) { | |
print('>>>>>>>>>>>>>>>>>>>>>>>>>'); | |
print( | |
'>>>>>>>>>>>>>>>>>>>>>>>>> HomePage StatefulWidget created. Its constructor called.'); | |
} | |
} | |
@override | |
State createState() { | |
return HomePageState(); | |
} | |
} | |
class HomePageState extends State<HomePage> with CounterState { | |
HomePageState() : super() { | |
if (kDebugMode) { | |
print('>>>>>>>>>>>>>>>>>>>>>>>>>'); | |
print( | |
'>>>>>>>>>>>>>>>>>>>>>>>>> HomePageState created. Its constructor called.'); | |
} | |
} | |
@override | |
void initState() { | |
super.initState(); | |
if (kDebugMode) { | |
print('>>>>>>>>>>>>>>>>>>>>>>>>> HomePageState initState() called.'); | |
} | |
// Take in this State object into this class object | |
StateController(this); | |
} | |
@override | |
Widget build(BuildContext context) { | |
if (kDebugMode) { | |
print('>>>>>>>>>>>>>>>>>>>>>>>>> HomePageState build() called!'); | |
} | |
return FirstPage(); | |
return const _FirstPageWithConst(); | |
return FirstPage(key: UniqueKey()); | |
return _InheritedWidget(child: const _FirstPageInherited()); | |
return const _InheritedWidget(child: _FirstPageInherited()); | |
} | |
@override | |
void didChangeDependencies() { | |
if (kDebugMode) { | |
print( | |
'>>>>>>>>>>>>>>>>>>>>>>>>> HomePageState didChangeDependencies() called.'); | |
} | |
super.didChangeDependencies(); | |
} | |
/// Called if the widget is 'moved' around the Widget tree | |
/// or when the State object is to be disposed() called. | |
@override | |
void deactivate() { | |
if (kDebugMode) { | |
print('>>>>>>>>>>>>>>>>>>>>>>>>> HomePageState deactivate() called.'); | |
} | |
super.deactivate(); | |
} | |
/// Called during a 'hot reload' | |
@override | |
void reassemble() { | |
if (kDebugMode) { | |
print('>>>>>>>>>>>>>>>>>>>>>>>>> HomePageState reassemble() called.'); | |
} | |
super.reassemble(); | |
} | |
} | |
/// | |
/// First Page | |
/// | |
/// | |
/// | |
class FirstPage extends StatefulWidget { | |
/// | |
const FirstPage({super.key}); | |
@override | |
State createState() { | |
return FirstPageState(); | |
} | |
} | |
/// | |
class FirstPageState extends State<FirstPage> with CounterState { | |
/// | |
FirstPageState() { | |
if (kDebugMode) { | |
print('>>>>>>>>>>>>>>>>>>>>>>>>>'); | |
print( | |
'>>>>>>>>>>>>>>>>>>>>>>>>> _FirstPageState created. Its constructor called.'); | |
} | |
} | |
@override | |
void initState() { | |
if (kDebugMode) { | |
print('>>>>>>>>>>>>>>>>>>>>>>>>> _FirstPageState initState() called.'); | |
} | |
super.initState(); | |
// HomePageState is found because its an ancestor on the same branch of the Widget tree. | |
homePageState = context.findAncestorStateOfType<HomePageState>()!; | |
} | |
/// | |
late HomePageState homePageState; | |
@override | |
Widget build(BuildContext context) { | |
if (kDebugMode) { | |
print('>>>>>>>>>>>>>>>>>>>>>>>>> _FirstPageState build() called!'); | |
} | |
return Scaffold( | |
appBar: AppBar( | |
title: Text('Home counter: ${homePageState.counter}'), | |
), | |
body: Center( | |
child: Column( | |
mainAxisAlignment: MainAxisAlignment.center, | |
children: [ | |
FirstCounter(), | |
// const FirstCounter(), // See the difference! | |
const SizedBox(height: 100), | |
Column( | |
mainAxisAlignment: MainAxisAlignment.end, | |
children: <Widget>[ | |
const Text( | |
'You have pushed the button this many times:', | |
), | |
Text( | |
'$counter', | |
style: Theme.of(context).textTheme.headlineMedium, | |
), | |
], | |
), | |
], | |
), | |
), | |
floatingActionButton: FloatingActionButton( | |
onPressed: () { | |
if (kDebugMode) { | |
print('>>>>>>>>>>>>>>>>>>>>>>>>>'); | |
print( | |
'>>>>>>>>>>>>>>>>>>>>>>>>> _FirstPageState button pressed. Its setState() called.'); | |
} | |
setState(() { | |
counter++; | |
}); | |
}, | |
tooltip: 'Increment', | |
child: const Icon(Icons.add), | |
), | |
persistentFooterAlignment: AlignmentDirectional.centerStart, | |
persistentFooterButtons: <Widget>[ | |
TextButton( | |
child: const Text( | |
'Title Counter', | |
), | |
onPressed: () { | |
// | |
if (kDebugMode) { | |
print('>>>>>>>>>>>>>>>>>>>>>>>>>'); | |
print( | |
">>>>>>>>>>>>>>>>>>>>>>>>> 'Title Counter' button pressed. _MyHomePageState setState() called."); | |
} | |
homePageState.counter++; | |
homePageState.setState(() {}); | |
}, | |
), | |
TextButton( | |
child: const Text( | |
'Second Page', | |
), | |
onPressed: () async { | |
if (kDebugMode) { | |
print('>>>>>>>>>>>>>>>>>>>>>>>>>'); | |
print( | |
">>>>>>>>>>>>>>>>>>>>>>>>> 'Second Page' button pressed. Navigator.push() called."); | |
} | |
await Navigator.push( | |
context, | |
MaterialPageRoute<void>( | |
builder: (BuildContext context) => SecondPage(), | |
), | |
); | |
// Update the Title count! | |
setState(() {}); | |
}, | |
), | |
], | |
); | |
} | |
/// Called if the widget is 'moved' around the Widget tree | |
/// or when the State object is to be disposed() called. | |
@override | |
void deactivate() { | |
if (kDebugMode) { | |
print('>>>>>>>>>>>>>>>>>>>>>>>>>'); | |
print('>>>>>>>>>>>>>>>>>>>>>>>>> _FirstPageState deactivate() called.'); | |
} | |
super.deactivate(); | |
} | |
@override | |
void didUpdateWidget(FirstPage oldWidget) { | |
if (kDebugMode) { | |
print( | |
'>>>>>>>>>>>>>>>>>>>>>>>>> _FirstPageState didUpdateWidget() called.'); | |
} | |
super.didUpdateWidget(oldWidget); | |
} | |
@override | |
void didChangeDependencies() { | |
if (kDebugMode) { | |
print( | |
'>>>>>>>>>>>>>>>>>>>>>>>>> _FirstPageState didChangeDependencies() called.'); | |
} | |
super.didChangeDependencies(); | |
} | |
@override | |
void activate() { | |
if (kDebugMode) { | |
print('>>>>>>>>>>>>>>>>>>>>>>>>> _FirstPageState activate() called.'); | |
} | |
super.activate(); | |
} | |
/// Called during a 'hot reload' | |
@override | |
void reassemble() { | |
if (kDebugMode) { | |
print('>>>>>>>>>>>>>>>>>>>>>>>>> _FirstPageState reassemble() called.'); | |
} | |
super.reassemble(); | |
} | |
@override | |
void dispose() { | |
super.dispose(); | |
if (kDebugMode) { | |
print( | |
'>>>>>>>>>>>>>>>>>>>>>>>>> _FirstPageState disposed() called. Counter was $counter'); | |
} | |
} | |
} | |
/// | |
/// The First Counter | |
/// | |
/// | |
class FirstCounter extends StatefulWidget { | |
/// | |
const FirstCounter({super.key}); | |
@override | |
State createState() { | |
return FirstCounterState(); | |
} | |
} | |
/// | |
class FirstCounterState extends State<FirstCounter> with CounterState { | |
/// | |
FirstCounterState() { | |
if (kDebugMode) { | |
print('>>>>>>>>>>>>>>>>>>>>>>>>>'); | |
print( | |
'>>>>>>>>>>>>>>>>>>>>>>>>> _FirstCounterState created. Its constructor called.'); | |
} | |
} | |
@override | |
void initState() { | |
super.initState(); | |
if (kDebugMode) { | |
print( | |
'>>>>>>>>>>>>>>>>>>>>>>>>> _FirstCounterState initState() called.'); | |
} | |
} | |
@override | |
Widget build(BuildContext context) { | |
if (kDebugMode) { | |
print('>>>>>>>>>>>>>>>>>>>>>>>>> _FirstCounterState build() called!'); | |
} | |
return Column( | |
mainAxisAlignment: MainAxisAlignment.end, | |
children: <Widget>[ | |
const Text( | |
'You have pushed the button this many times:', | |
), | |
Text( | |
'$counter', | |
style: Theme.of(context).textTheme.headlineMedium, | |
), | |
Align( | |
alignment: Alignment.bottomRight, | |
child: Padding( | |
padding: const EdgeInsets.only(right: 12), | |
child: FloatingActionButton( | |
heroTag: | |
null, // Fixes 'multiple heroes that share the same tag' error | |
onPressed: () { | |
if (kDebugMode) { | |
print('>>>>>>>>>>>>>>>>>>>>>>>>>'); | |
print( | |
'>>>>>>>>>>>>>>>>>>>>>>>>> _FirstCounterState button pressed. Its setState() called.'); | |
} | |
counter++; | |
setState(() {}); | |
}, | |
child: const Icon(Icons.add), | |
), | |
), | |
), | |
], | |
); | |
} | |
/// Called if the widget is 'moved' around the Widget tree | |
/// or when the State object is to be disposed() called. | |
@override | |
void deactivate() { | |
if (kDebugMode) { | |
print('>>>>>>>>>>>>>>>>>>>>>>>>>'); | |
print( | |
'>>>>>>>>>>>>>>>>>>>>>>>>> _FirstCounterState deactivate() called.'); | |
} | |
super.deactivate(); | |
} | |
@override | |
void didUpdateWidget(FirstCounter oldWidget) { | |
if (kDebugMode) { | |
print( | |
'>>>>>>>>>>>>>>>>>>>>>>>>> _FirstCounterState didUpdateWidget() called.'); | |
} | |
super.didUpdateWidget(oldWidget); | |
} | |
@override | |
void didChangeDependencies() { | |
if (kDebugMode) { | |
print( | |
'>>>>>>>>>>>>>>>>>>>>>>>>> _FirstCounterState didChangeDependencies() called.'); | |
} | |
super.didChangeDependencies(); | |
} | |
@override | |
void activate() { | |
if (kDebugMode) { | |
print( | |
'>>>>>>>>>>>>>>>>>>>>>>>>> _FirstCounterState activate() called.'); | |
} | |
super.activate(); | |
} | |
/// Called during a 'hot reload' | |
@override | |
void reassemble() { | |
if (kDebugMode) { | |
print( | |
'>>>>>>>>>>>>>>>>>>>>>>>>> _FirstCounterState reassemble() called.'); | |
} | |
super.reassemble(); | |
} | |
@override | |
void dispose() { | |
super.dispose(); | |
if (kDebugMode) { | |
print( | |
'>>>>>>>>>>>>>>>>>>>>>>>>> _FirstCounterState disposed() called. Counter was $counter'); | |
} | |
} | |
} | |
/// | |
/// Second Page | |
/// | |
/// | |
class SecondPage extends StatefulWidget { | |
/// | |
SecondPage({super.key}) { | |
if (kDebugMode) { | |
print('>>>>>>>>>>>>>>>>>>>>>>>>>'); | |
print( | |
'>>>>>>>>>>>>>>>>>>>>>>>>> SecondPage StatefulWidget created. Its constructor called.'); | |
} | |
} | |
@override | |
State createState() { | |
return SecondPageState(); | |
} | |
} | |
/// | |
class SecondPageState extends State<SecondPage> with CounterState { | |
/// | |
SecondPageState() { | |
if (kDebugMode) { | |
print('>>>>>>>>>>>>>>>>>>>>>>>>>'); | |
print( | |
'>>>>>>>>>>>>>>>>>>>>>>>>> SecondPageState created. Its constructor called.'); | |
} | |
} | |
@override | |
void initState() { | |
if (kDebugMode) { | |
print('>>>>>>>>>>>>>>>>>>>>>>>>> SecondPageState initState() called.'); | |
} | |
super.initState(); | |
// Returns null. | |
// HomePageState is on a separate branch of the Widget tree. | |
homePageState = context.findAncestorStateOfType<HomePageState>(); | |
homePageState = context.findRootAncestorStateOfType<HomePageState>(); | |
// Supply access to the Home Page's State object. | |
con = StateController(this); | |
} | |
/// | |
HomePageState? homePageState; | |
/// | |
late StateController con; | |
@override | |
Widget build(BuildContext context) { | |
if (kDebugMode) { | |
print('>>>>>>>>>>>>>>>>>>>>>>>>> SecondPageState build() called!'); | |
} | |
return Scaffold( | |
appBar: AppBar( | |
title: Text('Home counter: ${con.counter}'), | |
), | |
body: Center( | |
child: Column( | |
mainAxisAlignment: MainAxisAlignment.center, | |
children: <Widget>[ | |
const Text("You're on this second page."), | |
const Text('You have pushed the button this many times:'), | |
Text( | |
'$counter', | |
style: Theme.of(context).textTheme.headlineMedium, | |
), | |
], | |
), | |
), | |
floatingActionButton: FloatingActionButton( | |
onPressed: () { | |
if (kDebugMode) { | |
print('>>>>>>>>>>>>>>>>>>>>>>>>>'); | |
print( | |
'>>>>>>>>>>>>>>>>>>>>>>>>> Second page FloatingActionButton pressed. Its setState() called.'); | |
} | |
setState(() { | |
counter++; | |
}); | |
}, | |
tooltip: 'Increment', | |
child: const Icon(Icons.add), | |
), | |
persistentFooterAlignment: AlignmentDirectional.centerStart, | |
persistentFooterButtons: <Widget>[ | |
TextButton( | |
child: const Text( | |
'Title Counter', | |
), | |
onPressed: () { | |
if (kDebugMode) { | |
print('>>>>>>>>>>>>>>>>>>>>>>>>>'); | |
print( | |
">>>>>>>>>>>>>>>>>>>>>>>>> 'Title Counter' button pressed. SecondPageState setState() called."); | |
} | |
con.counter++; | |
setState(() {}); | |
}, | |
), | |
], | |
); | |
} | |
@override | |
void deactivate() { | |
super.deactivate(); | |
if (kDebugMode) { | |
print('>>>>>>>>>>>>>>>>>>>>>>>>>'); | |
print('>>>>>>>>>>>>>>>>>>>>>>>>> SecondPageState deactivate() called.'); | |
} | |
} | |
@override | |
void didUpdateWidget(SecondPage oldWidget) { | |
if (kDebugMode) { | |
print( | |
'>>>>>>>>>>>>>>>>>>>>>>>>> SecondPageState didUpdateWidget() called.'); | |
} | |
super.didUpdateWidget(oldWidget); | |
} | |
@override | |
void didChangeDependencies() { | |
if (kDebugMode) { | |
print( | |
'>>>>>>>>>>>>>>>>>>>>>>>>> SecondPageState didChangeDependencies() called.'); | |
} | |
super.didChangeDependencies(); | |
} | |
@override | |
void activate() { | |
if (kDebugMode) { | |
print('>>>>>>>>>>>>>>>>>>>>>>>>> SecondPageState activate() called.'); | |
} | |
super.activate(); | |
} | |
/// Called during a 'hot reload' | |
@override | |
void reassemble() { | |
if (kDebugMode) { | |
print('>>>>>>>>>>>>>>>>>>>>>>>>> SecondPageState reassemble() called.'); | |
} | |
super.reassemble(); | |
} | |
@override | |
void dispose() { | |
super.dispose(); | |
if (kDebugMode) { | |
print( | |
'>>>>>>>>>>>>>>>>>>>>>>>>> SecondPageState disposed() called. Counter was $counter'); | |
} | |
} | |
} | |
/// | |
/// FirstPage called with const keyword | |
/// | |
/// | |
/// | |
class _FirstPageWithConst extends StatefulWidget { | |
/// | |
const _FirstPageWithConst(); | |
@override | |
State createState() { | |
return _FirstPageWithConstState(); | |
} | |
} | |
/// | |
class _FirstPageWithConstState extends State<_FirstPageWithConst> | |
with CounterState { | |
/// | |
_FirstPageWithConstState() { | |
if (kDebugMode) { | |
print('>>>>>>>>>>>>>>>>>>>>>>>>>'); | |
print( | |
'>>>>>>>>>>>>>>>>>>>>>>>>> _FirstPageWithConstState created. Its constructor called.'); | |
} | |
} | |
@override | |
void initState() { | |
if (kDebugMode) { | |
print( | |
'>>>>>>>>>>>>>>>>>>>>>>>>> _FirstPageWithConstState initState() called.'); | |
} | |
super.initState(); | |
// HomePageState is found because its an ancestor on the same branch of the Widget tree. | |
homePageState = context.findAncestorStateOfType<HomePageState>()!; | |
} | |
late HomePageState homePageState; | |
@override | |
Widget build(BuildContext context) { | |
if (kDebugMode) { | |
print( | |
'>>>>>>>>>>>>>>>>>>>>>>>>> _FirstPageWithConstState build() called!'); | |
} | |
return Scaffold( | |
appBar: AppBar( | |
title: Text('Home counter: ${homePageState.counter}'), | |
), | |
body: Center( | |
child: Column( | |
mainAxisAlignment: MainAxisAlignment.center, | |
children: [ | |
const _FirstCounterWithConst(), | |
const SizedBox(height: 100), | |
Column( | |
mainAxisAlignment: MainAxisAlignment.end, | |
children: <Widget>[ | |
const Text( | |
'You have pushed the button this many times:', | |
), | |
Text( | |
'$counter', | |
style: Theme.of(context).textTheme.headlineMedium, | |
), | |
], | |
), | |
], | |
), | |
), | |
floatingActionButton: FloatingActionButton( | |
onPressed: () { | |
if (kDebugMode) { | |
print('>>>>>>>>>>>>>>>>>>>>>>>>>'); | |
print( | |
'>>>>>>>>>>>>>>>>>>>>>>>>> _FirstPageWithConstState button pressed. Its setState() called.'); | |
} | |
setState(() { | |
counter++; | |
}); | |
}, | |
tooltip: 'Increment', | |
child: const Icon(Icons.add), | |
), | |
persistentFooterAlignment: AlignmentDirectional.centerStart, | |
persistentFooterButtons: <Widget>[ | |
TextButton( | |
child: const Text( | |
'Title Counter', | |
), | |
onPressed: () { | |
// | |
if (kDebugMode) { | |
print('>>>>>>>>>>>>>>>>>>>>>>>>>'); | |
print( | |
">>>>>>>>>>>>>>>>>>>>>>>>> 'Title Counter' button pressed. _FirstPageConstState setState() called."); | |
} | |
homePageState.counter++; | |
setState(() {}); | |
}, | |
), | |
TextButton( | |
child: const Text( | |
'Second Page', | |
), | |
onPressed: () async { | |
if (kDebugMode) { | |
print('>>>>>>>>>>>>>>>>>>>>>>>>>'); | |
print( | |
">>>>>>>>>>>>>>>>>>>>>>>>> 'Second Page' button pressed. Navigator.push() called."); | |
} | |
await Navigator.push( | |
context, | |
MaterialPageRoute<void>( | |
builder: (BuildContext context) => SecondPage(), | |
), | |
); | |
// Update the Title count! | |
setState(() {}); // Comment out. Title count won't refresh. | |
}, | |
), | |
], | |
); | |
} | |
/// Called if the widget is 'moved' around the Widget tree | |
/// or when the State object is to be disposed() called. | |
@override | |
void deactivate() { | |
if (kDebugMode) { | |
print('>>>>>>>>>>>>>>>>>>>>>>>>>'); | |
print( | |
'>>>>>>>>>>>>>>>>>>>>>>>>> _FirstPageWithConstState deactivate() called.'); | |
} | |
super.deactivate(); | |
} | |
@override | |
void didUpdateWidget(_FirstPageWithConst oldWidget) { | |
if (kDebugMode) { | |
print( | |
'>>>>>>>>>>>>>>>>>>>>>>>>> _FirstPageWithConstState didUpdateWidget() called.'); | |
} | |
super.didUpdateWidget(oldWidget); | |
} | |
@override | |
void didChangeDependencies() { | |
if (kDebugMode) { | |
print( | |
'>>>>>>>>>>>>>>>>>>>>>>>>> _FirstPageWithConstState didChangeDependencies() called.'); | |
} | |
super.didChangeDependencies(); | |
} | |
@override | |
void activate() { | |
if (kDebugMode) { | |
print( | |
'>>>>>>>>>>>>>>>>>>>>>>>>> _FirstPageWithConstState activate() called.'); | |
} | |
super.activate(); | |
} | |
/// Called during a 'hot reload' | |
@override | |
void reassemble() { | |
if (kDebugMode) { | |
print( | |
'>>>>>>>>>>>>>>>>>>>>>>>>> _FirstPageWithConstState reassemble() called.'); | |
} | |
super.reassemble(); | |
} | |
@override | |
void dispose() { | |
super.dispose(); | |
if (kDebugMode) { | |
print( | |
'>>>>>>>>>>>>>>>>>>>>>>>>> _FirstPageWithConstState disposed() called. Counter was $counter'); | |
} | |
} | |
} | |
/// | |
/// FirstCounter called with the const keyword | |
/// | |
/// | |
class _FirstCounterWithConst extends StatefulWidget { | |
/// | |
const _FirstCounterWithConst(); | |
@override | |
State createState() { | |
return _FirstCounterWithConstState(); | |
} | |
} | |
/// | |
class _FirstCounterWithConstState extends State<_FirstCounterWithConst> | |
with CounterState { | |
/// | |
_FirstCounterWithConstState() { | |
if (kDebugMode) { | |
print('>>>>>>>>>>>>>>>>>>>>>>>>>'); | |
print( | |
'>>>>>>>>>>>>>>>>>>>>>>>>> _FirstCounterWithConstState created. Its constructor called.'); | |
} | |
} | |
@override | |
void initState() { | |
super.initState(); | |
if (kDebugMode) { | |
print( | |
'>>>>>>>>>>>>>>>>>>>>>>>>> _FirstCounterWithConstState initState() called.'); | |
} | |
} | |
@override | |
Widget build(BuildContext context) { | |
if (kDebugMode) { | |
print( | |
'>>>>>>>>>>>>>>>>>>>>>>>>> _FirstCounterWithConstState build() called!'); | |
} | |
return Column( | |
mainAxisAlignment: MainAxisAlignment.end, | |
children: <Widget>[ | |
const Text( | |
'You have pushed the button this many times:', | |
), | |
Text( | |
'$counter', | |
style: Theme.of(context).textTheme.headlineMedium, | |
), | |
Align( | |
alignment: Alignment.bottomRight, | |
child: Padding( | |
padding: const EdgeInsets.only(right: 12), | |
child: FloatingActionButton( | |
heroTag: | |
null, // Fix 'multiple heroes that share the same tag' error | |
onPressed: () { | |
if (kDebugMode) { | |
print('>>>>>>>>>>>>>>>>>>>>>>>>>'); | |
print( | |
'>>>>>>>>>>>>>>>>>>>>>>>>> _FirstCounterWithConstState button pressed.'); | |
} | |
counter++; | |
setState(() {}); | |
}, | |
child: const Icon(Icons.add), | |
), | |
), | |
), | |
], | |
); | |
} | |
/// Called if the widget is 'moved' around the Widget tree | |
/// or when the State object is to be disposed() called. | |
@override | |
void deactivate() { | |
if (kDebugMode) { | |
print('>>>>>>>>>>>>>>>>>>>>>>>>>'); | |
print( | |
'>>>>>>>>>>>>>>>>>>>>>>>>> _FirstCounterWithConstState deactivate() called.'); | |
} | |
super.deactivate(); | |
} | |
@override | |
void didUpdateWidget(_FirstCounterWithConst oldWidget) { | |
if (kDebugMode) { | |
print( | |
'>>>>>>>>>>>>>>>>>>>>>>>>> _FirstCounterWithConstState didUpdateWidget() called.'); | |
} | |
super.didUpdateWidget(oldWidget); | |
} | |
@override | |
void didChangeDependencies() { | |
if (kDebugMode) { | |
print( | |
'>>>>>>>>>>>>>>>>>>>>>>>>> _FirstCounterWithConstState didChangeDependencies() called.'); | |
} | |
super.didChangeDependencies(); | |
} | |
@override | |
void activate() { | |
if (kDebugMode) { | |
print( | |
'>>>>>>>>>>>>>>>>>>>>>>>>> _FirstCounterWithConstState activate() called.'); | |
} | |
super.activate(); | |
} | |
/// Called during a 'hot reload' | |
@override | |
void reassemble() { | |
if (kDebugMode) { | |
print( | |
'>>>>>>>>>>>>>>>>>>>>>>>>> _FirstCounterWithConstState reassemble() called.'); | |
} | |
super.reassemble(); | |
} | |
@override | |
void dispose() { | |
super.dispose(); | |
if (kDebugMode) { | |
print( | |
'>>>>>>>>>>>>>>>>>>>>>>>>> _FirstCounterWithConstState disposed() called. Counter was $counter'); | |
} | |
} | |
} | |
/// | |
/// FirstPage called with const keyword | |
/// and indirectly involving an InheritedWidget | |
/// | |
/// | |
/// | |
class _FirstPageInherited extends StatefulWidget { | |
/// | |
const _FirstPageInherited(); | |
@override | |
State createState() { | |
return _FirstPageInheritedState(); | |
} | |
} | |
/// | |
/// FirstPageState indirectly involving an InheritedWidget | |
/// | |
/// | |
class _FirstPageInheritedState extends State<_FirstPageInherited> | |
with CounterState { | |
/// | |
_FirstPageInheritedState() { | |
if (kDebugMode) { | |
print('>>>>>>>>>>>>>>>>>>>>>>>>>'); | |
print( | |
'>>>>>>>>>>>>>>>>>>>>>>>>> _FirstPageInheritedState created. Its constructor called.'); | |
} | |
} | |
@override | |
void initState() { | |
if (kDebugMode) { | |
print( | |
'>>>>>>>>>>>>>>>>>>>>>>>>> _FirstPageInheritedState initState() called.'); | |
} | |
super.initState(); | |
// HomePageState is found because its an ancestor on the same branch of the Widget tree. | |
homePageState = context.findAncestorStateOfType<HomePageState>()!; | |
} | |
late HomePageState homePageState; | |
@override | |
Widget build(BuildContext context) { | |
if (kDebugMode) { | |
print( | |
'>>>>>>>>>>>>>>>>>>>>>>>>> _FirstPageInheritedState build() called!'); | |
} | |
return Scaffold( | |
appBar: AppBar( | |
title: const HomeTitle(), | |
), | |
body: Center( | |
child: Column( | |
mainAxisAlignment: MainAxisAlignment.center, | |
children: [ | |
const _FirstCounterInherited(), | |
const SizedBox(height: 100), | |
Column( | |
mainAxisAlignment: MainAxisAlignment.end, | |
children: <Widget>[ | |
const Text( | |
'You have pushed the button this many times:', | |
), | |
Text( | |
'$counter', | |
style: Theme.of(context).textTheme.headlineMedium, | |
), | |
], | |
), | |
], | |
), | |
), | |
floatingActionButton: FloatingActionButton( | |
onPressed: () { | |
if (kDebugMode) { | |
print('>>>>>>>>>>>>>>>>>>>>>>>>>'); | |
print( | |
'>>>>>>>>>>>>>>>>>>>>>>>>> _FirstPageInheritedState button pressed. Its setState() called.'); | |
} | |
setState(() { | |
counter++; | |
}); | |
}, | |
tooltip: 'Increment', | |
child: const Icon(Icons.add), | |
), | |
persistentFooterAlignment: AlignmentDirectional.centerStart, | |
persistentFooterButtons: <Widget>[ | |
TextButton( | |
child: const Text( | |
'Title Counter', | |
), | |
onPressed: () { | |
// | |
if (kDebugMode) { | |
print('>>>>>>>>>>>>>>>>>>>>>>>>>'); | |
print( | |
">>>>>>>>>>>>>>>>>>>>>>>>> 'Title Counter' button pressed. _FirstPageInheritedState setState() called."); | |
} | |
homePageState.counter++; | |
// Commented out, going back to the First Page, title counter not displayed correctly. | |
homePageState.setState(() {}); | |
// It's incremented, and change displayed here | |
// setState(() {}); | |
}, | |
), | |
TextButton( | |
child: const Text( | |
'Second Page', | |
), | |
onPressed: () { | |
if (kDebugMode) { | |
print('>>>>>>>>>>>>>>>>>>>>>>>>>'); | |
print( | |
">>>>>>>>>>>>>>>>>>>>>>>>> 'Second Page' button pressed. Navigator.push() called."); | |
} | |
Navigator.push( | |
context, | |
MaterialPageRoute<void>( | |
builder: (BuildContext context) => _SecondPageInherited(), | |
), | |
); | |
}, | |
), | |
], | |
); | |
} | |
/// Called if the widget is 'moved' around the Widget tree | |
/// or when the State object is to be disposed() called. | |
@override | |
void deactivate() { | |
if (kDebugMode) { | |
print('>>>>>>>>>>>>>>>>>>>>>>>>>'); | |
print( | |
'>>>>>>>>>>>>>>>>>>>>>>>>> _FirstPageInheritedState deactivate() called.'); | |
} | |
super.deactivate(); | |
} | |
@override | |
void didUpdateWidget(_FirstPageInherited oldWidget) { | |
if (kDebugMode) { | |
print( | |
'>>>>>>>>>>>>>>>>>>>>>>>>> _FirstPageInheritedState didUpdateWidget() called.'); | |
} | |
super.didUpdateWidget(oldWidget); | |
} | |
@override | |
void didChangeDependencies() { | |
if (kDebugMode) { | |
print( | |
'>>>>>>>>>>>>>>>>>>>>>>>>> _FirstPageInheritedState didChangeDependencies() called.'); | |
} | |
super.didChangeDependencies(); | |
} | |
@override | |
void activate() { | |
if (kDebugMode) { | |
print( | |
'>>>>>>>>>>>>>>>>>>>>>>>>> _FirstPageInheritedState activate() called.'); | |
} | |
super.activate(); | |
} | |
/// Called during a 'hot reload' | |
@override | |
void reassemble() { | |
if (kDebugMode) { | |
print( | |
'>>>>>>>>>>>>>>>>>>>>>>>>> _FirstPageInheritedState reassemble() called.'); | |
} | |
super.reassemble(); | |
} | |
@override | |
void dispose() { | |
super.dispose(); | |
if (kDebugMode) { | |
print( | |
'>>>>>>>>>>>>>>>>>>>>>>>>> _FirstPageInheritedState disposed() called. Counter was $counter'); | |
} | |
} | |
} | |
// | |
/// FirstCounter called with the const keyword | |
/// and involving an InheritedWidget | |
/// | |
/// | |
class _FirstCounterInherited extends StatefulWidget { | |
/// | |
const _FirstCounterInherited(); | |
@override | |
State createState() { | |
return _FirstCounterInheritedState(); | |
} | |
} | |
/// | |
/// FirstCounterState indirectly calling an InheritedWidget | |
/// | |
/// | |
class _FirstCounterInheritedState extends State<_FirstCounterInherited> | |
with CounterState { | |
/// | |
_FirstCounterInheritedState() { | |
if (kDebugMode) { | |
print('>>>>>>>>>>>>>>>>>>>>>>>>>'); | |
print( | |
'>>>>>>>>>>>>>>>>>>>>>>>>> _FirstCounterInheritedState created. Its constructor called.'); | |
} | |
} | |
@override | |
void initState() { | |
super.initState(); | |
if (kDebugMode) { | |
print( | |
'>>>>>>>>>>>>>>>>>>>>>>>>> _FirstCounterInheritedState initState() called.'); | |
} | |
// HomePageState is found because its an ancestor on the same branch of the Widget tree. | |
homePageState = context.findAncestorStateOfType<HomePageState>()!; | |
} | |
late HomePageState homePageState; | |
@override | |
Widget build(BuildContext context) { | |
if (kDebugMode) { | |
print( | |
'>>>>>>>>>>>>>>>>>>>>>>>>> _FirstCounterInheritedState build() called!'); | |
} | |
// _InheritedWidget is found because its an ancestor on the same branch | |
// as this State object on the Widget tree. | |
context.dependOnInheritedWidgetOfExactType<_InheritedWidget>(); | |
return Column( | |
mainAxisAlignment: MainAxisAlignment.end, | |
children: <Widget>[ | |
const Text( | |
'You have pushed the button this many times:', | |
), | |
Text( | |
'$counter', | |
style: Theme.of(context).textTheme.headlineMedium, | |
), | |
Align( | |
alignment: Alignment.bottomRight, | |
child: Padding( | |
padding: const EdgeInsets.only(right: 12), | |
child: FloatingActionButton( | |
heroTag: | |
null, // Fix 'multiple heroes that share the same tag' error | |
onPressed: () { | |
if (kDebugMode) { | |
print('>>>>>>>>>>>>>>>>>>>>>>>>>'); | |
print( | |
'>>>>>>>>>>>>>>>>>>>>>>>>> _FirstCounterInheritedState button pressed.'); | |
} | |
counter++; | |
// Calling HomePageState to rebuild will call the InheritedWidget | |
// _InheritedWidget if it's implemented there. | |
// And because this State object called, | |
// dependOnInheritedWidgetOfExactType<_InheritedWidget>(), | |
// this State object will also rebuild!! Cool! | |
homePageState.setState(() {}); | |
// What does this do all alone? | |
// setState(() {}); | |
}, | |
child: const Icon(Icons.add), | |
), | |
), | |
), | |
], | |
); | |
} | |
/// Called if the widget is 'moved' around the Widget tree | |
/// or when the State object is to be disposed() called. | |
@override | |
void deactivate() { | |
if (kDebugMode) { | |
print('>>>>>>>>>>>>>>>>>>>>>>>>>'); | |
print( | |
'>>>>>>>>>>>>>>>>>>>>>>>>> _FirstCounterInheritedState deactivate() called.'); | |
} | |
super.deactivate(); | |
} | |
@override | |
void didUpdateWidget(_FirstCounterInherited oldWidget) { | |
if (kDebugMode) { | |
print( | |
'>>>>>>>>>>>>>>>>>>>>>>>>> _FirstCounterInheritedState didUpdateWidget() called.'); | |
} | |
super.didUpdateWidget(oldWidget); | |
} | |
@override | |
void didChangeDependencies() { | |
if (kDebugMode) { | |
print( | |
'>>>>>>>>>>>>>>>>>>>>>>>>> _FirstCounterInheritedState didChangeDependencies() called.'); | |
} | |
super.didChangeDependencies(); | |
} | |
@override | |
void activate() { | |
if (kDebugMode) { | |
print( | |
'>>>>>>>>>>>>>>>>>>>>>>>>> _FirstCounterInheritedState activate() called.'); | |
} | |
super.activate(); | |
} | |
/// Called during a 'hot reload' | |
@override | |
void reassemble() { | |
if (kDebugMode) { | |
print( | |
'>>>>>>>>>>>>>>>>>>>>>>>>> _FirstCounterInheritedState reassemble() called.'); | |
} | |
super.reassemble(); | |
} | |
@override | |
void dispose() { | |
super.dispose(); | |
if (kDebugMode) { | |
print( | |
'>>>>>>>>>>>>>>>>>>>>>>>>> _FirstCounterInheritedState disposed() called. Counter was $counter'); | |
} | |
} | |
} | |
/// | |
/// Second Page involving an InheritedWidget | |
/// | |
/// | |
class _SecondPageInherited extends StatefulWidget { | |
_SecondPageInherited() { | |
if (kDebugMode) { | |
print('>>>>>>>>>>>>>>>>>>>>>>>>>'); | |
print( | |
'>>>>>>>>>>>>>>>>>>>>>>>>> SecondPage StatefulWidget created. Its constructor called.'); | |
} | |
} | |
@override | |
State createState() { | |
return _SecondPageInheritedState(); | |
} | |
} | |
class _SecondPageInheritedState extends State<_SecondPageInherited> | |
with CounterState { | |
_SecondPageInheritedState() { | |
if (kDebugMode) { | |
print('>>>>>>>>>>>>>>>>>>>>>>>>>'); | |
print( | |
'>>>>>>>>>>>>>>>>>>>>>>>>> SecondPageState created. Its constructor called.'); | |
} | |
} | |
@override | |
void initState() { | |
if (kDebugMode) { | |
print('>>>>>>>>>>>>>>>>>>>>>>>>> SecondPageState initState() called.'); | |
} | |
super.initState(); | |
// Supply access to the Home Page's State object. | |
con = StateController(this); | |
} | |
HomePageState? homePageState; | |
late StateController con; | |
@override | |
Widget build(BuildContext context) { | |
if (kDebugMode) { | |
print('>>>>>>>>>>>>>>>>>>>>>>>>> SecondPageState build() called!'); | |
} | |
return Scaffold( | |
appBar: AppBar( | |
title: Text('Home counter: ${con.counter}'), | |
), | |
body: Center( | |
child: Column( | |
mainAxisAlignment: MainAxisAlignment.center, | |
children: <Widget>[ | |
const Text("You're on this second page."), | |
const Text('You have pushed the button this many times:'), | |
Text( | |
'$counter', | |
style: Theme.of(context).textTheme.headlineMedium, | |
), | |
], | |
), | |
), | |
floatingActionButton: FloatingActionButton( | |
onPressed: () { | |
if (kDebugMode) { | |
print('>>>>>>>>>>>>>>>>>>>>>>>>>'); | |
print( | |
'>>>>>>>>>>>>>>>>>>>>>>>>> Second page FloatingActionButton pressed. Its setState() called.'); | |
} | |
setState(() { | |
counter++; | |
}); | |
}, | |
tooltip: 'Increment', | |
child: const Icon(Icons.add), | |
), | |
persistentFooterAlignment: AlignmentDirectional.centerStart, | |
persistentFooterButtons: <Widget>[ | |
TextButton( | |
child: const Text( | |
'Title Counter', | |
), | |
onPressed: () { | |
if (kDebugMode) { | |
print('>>>>>>>>>>>>>>>>>>>>>>>>>'); | |
print( | |
">>>>>>>>>>>>>>>>>>>>>>>>> 'Title Counter' button pressed. SecondPageState setState() called."); | |
} | |
con.counter++; | |
con.setState(() {}); // Update count on First Page screen | |
setState(() {}); // Update count in this screen | |
}, | |
), | |
], | |
); | |
} | |
@override | |
void deactivate() { | |
super.deactivate(); | |
if (kDebugMode) { | |
print('>>>>>>>>>>>>>>>>>>>>>>>>>'); | |
print('>>>>>>>>>>>>>>>>>>>>>>>>> SecondPageState deactivate() called.'); | |
} | |
} | |
@override | |
void didUpdateWidget(_SecondPageInherited oldWidget) { | |
if (kDebugMode) { | |
print( | |
'>>>>>>>>>>>>>>>>>>>>>>>>> SecondPageState didUpdateWidget() called.'); | |
} | |
super.didUpdateWidget(oldWidget); | |
} | |
@override | |
void didChangeDependencies() { | |
if (kDebugMode) { | |
print( | |
'>>>>>>>>>>>>>>>>>>>>>>>>> SecondPageState didChangeDependencies() called.'); | |
} | |
super.didChangeDependencies(); | |
} | |
@override | |
void activate() { | |
if (kDebugMode) { | |
print('>>>>>>>>>>>>>>>>>>>>>>>>> SecondPageState activate() called.'); | |
} | |
super.activate(); | |
} | |
/// Called during a 'hot reload' | |
@override | |
void reassemble() { | |
if (kDebugMode) { | |
print('>>>>>>>>>>>>>>>>>>>>>>>>> SecondPageState reassemble() called.'); | |
} | |
super.reassemble(); | |
} | |
@override | |
void dispose() { | |
super.dispose(); | |
if (kDebugMode) { | |
print( | |
'>>>>>>>>>>>>>>>>>>>>>>>>> SecondPageState disposed() called. Counter was $counter'); | |
} | |
} | |
} | |
/// | |
/// A StatelessWidget made a dependent to an InheritedWidget | |
/// | |
/// | |
class HomeTitle extends StatelessWidget { | |
/// | |
const HomeTitle({super.key}); | |
@override | |
Widget build(BuildContext context) { | |
if (kDebugMode) { | |
print('>>>>>>>>>>>>>>>>>>>>>>>>> HomeTitle build() called!'); | |
} | |
// _InheritedWidget is found because its an ancestor on the same branch on the Widget tree. | |
// You don't have to take in the return InheritedWidget. It's there just for demonstration purposes. | |
// ignore: unused_local_variable | |
final inheritedWidget = | |
context.dependOnInheritedWidgetOfExactType<_InheritedWidget>(); | |
final homePageState = context.findAncestorStateOfType<HomePageState>(); | |
String title; | |
if (homePageState == null) { | |
title = 'Home Page'; | |
} else { | |
title = 'Home counter: ${homePageState.counter}'; | |
} | |
return Text(title); | |
} | |
} | |
/// | |
/// A simple means to 'preserves state' but spans between Navigator's screens | |
/// | |
/// | |
class StateController implements State, CounterState { | |
/// Must pass a State object but only the first one is saved. | |
/// Any other will be ignored. | |
factory StateController(CounterState state) => | |
_this ??= StateController._(state); | |
StateController._(CounterState state) { | |
_state = state; | |
} | |
static StateController? _this; | |
/// | |
CounterState get state => _state; | |
late CounterState _state; | |
@override | |
void activate() {} | |
@override | |
Widget build(BuildContext context) => _state.build(context); | |
@override | |
BuildContext get context => _state.context; | |
@override | |
void deactivate() {} | |
@override | |
void debugFillProperties(DiagnosticPropertiesBuilder properties) {} | |
@override | |
void didChangeDependencies() {} | |
@override | |
void didUpdateWidget(covariant StatefulWidget oldWidget) {} | |
@override | |
void dispose() {} | |
@override | |
void initState() {} | |
@override | |
bool get mounted => _state.mounted; | |
/// Called when a hot reload is performed. | |
@override | |
void reassemble() {} | |
@override | |
void setState(VoidCallback fn) => _state.setState(fn); | |
@override | |
DiagnosticsNode toDiagnosticsNode( | |
{String? name, DiagnosticsTreeStyle? style}) => | |
_state.toDiagnosticsNode(name: name, style: style); | |
@override | |
String toStringShort() => _state.toStringShort(); | |
@override | |
String toString({DiagnosticLevel minLevel = DiagnosticLevel.info}) => | |
_state.toString(minLevel: minLevel); | |
@override | |
StatefulWidget get widget => _state.widget; | |
@override | |
int get counter => _state.counter; | |
@override | |
set counter(int counter) { | |
if (counter == _state.counter + 1) { | |
_state.counter++; | |
} | |
} | |
} | |
/// Mixin contains a counter | |
mixin CounterState<T extends StatefulWidget> on State<T> { | |
/// | |
int counter = 0; | |
} | |
class _InheritedWidget extends InheritedWidget { | |
const _InheritedWidget({required super.child}); | |
// _InheritedWidget({required super.child}); // Better to remove the const keyword | |
@override | |
bool updateShouldNotify(_InheritedWidget oldWidget) => true; | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment