So I was trying this code in flutter:
void main() => runApp(MyApp());
class MyApp extends StatefulWidget {
@override
_MyAppState createState() => _MyA
If you want to run code after the widget is rebuild use:
WidgetsBinding.instance.addPostFrameCallback((_) {
//doStuffAfterNextBuild
});
The function passed to setState
is run directly.
"The provided callback is immediately called synchronously"
setState
only marks the widget as dirty.
"Calling setState notifies the framework that the internal state of this object has changed"
setState
does not itself manipulate your state in any way.
(Quotes from https://api.flutter.dev/flutter/widgets/State/setState.html)
See for example the following:
class _MyWidgetState extends State<MyWidget> {
String s = ""
// ... Somewhere in the tree
onPressed: () {
// State is updated immediately with this call
setState(() {
this.s = "1";
// Implementation of setState calls
// _element.markNeedsBuild();
});
// State is updated again
this.s = "2";
}
}
This will result in the states s
beeing "2"
.
Although the closure passed to setState
sets s
to "1"
, it is again modified after that in the closure of onPressed
.
You can actually update the state outside of setState
s closure entirely:
class _MyWidgetState extends State<MyWidget> {
String s = ""
// ... Somewhere in the tree
onPressed: () {
// State is updated immediately
this.s = "1";
setState(() {
// Implementation of setState calls
// _element.markNeedsBuild();
});
}
}
If you look at the implementation of setState
you can see that it does not actually do anything with the callback besides of executing it and making sure you did not give it an asynchronous function and that the widget still exists:
@protected
void setState(VoidCallback fn) {
assert(fn != null);
assert(() {
if (_debugLifecycleState == _StateLifecycle.defunct) {
throw FlutterError.fromParts(<DiagnosticsNode>[
ErrorSummary('setState() called after dispose(): $this'),
ErrorDescription(
'This error happens if you call setState() on a State object for a widget that '
'no longer appears in the widget tree (e.g., whose parent widget no longer '
'includes the widget in its build). This error can occur when code calls '
'setState() from a timer or an animation callback.'
),
ErrorHint(
'The preferred solution is '
'to cancel the timer or stop listening to the animation in the dispose() '
'callback. Another solution is to check the "mounted" property of this '
'object before calling setState() to ensure the object is still in the '
'tree.'
),
ErrorHint(
'This error might indicate a memory leak if setState() is being called '
'because another object is retaining a reference to this State object '
'after it has been removed from the tree. To avoid memory leaks, '
'consider breaking the reference to this object during dispose().'
),
]);
}
if (_debugLifecycleState == _StateLifecycle.created && !mounted) {
throw FlutterError.fromParts(<DiagnosticsNode>[
ErrorSummary('setState() called in constructor: $this'),
ErrorHint(
'This happens when you call setState() on a State object for a widget that '
"hasn't been inserted into the widget tree yet. It is not necessary to call "
'setState() in the constructor, since the state is already assumed to be dirty '
'when it is initially created.'
),
]);
}
return true;
}());
final dynamic result = fn() as dynamic;
assert(() {
if (result is Future) {
throw FlutterError.fromParts(<DiagnosticsNode>[
ErrorSummary('setState() callback argument returned a Future.'),
ErrorDescription(
'The setState() method on $this was called with a closure or method that '
'returned a Future. Maybe it is marked as "async".'
),
ErrorHint(
'Instead of performing asynchronous work inside a call to setState(), first '
'execute the work (without updating the widget state), and then synchronously '
'update the state inside a call to setState().'
),
]);
}
// We ignore other types of return values so that you can do things like:
// setState(() => x = 3);
return true;
}());
_element.markNeedsBuild();
}
(Also from https://api.flutter.dev/flutter/widgets/State/setState.html)
Check out this great video for more insight into how State
works in Flutter:
https://www.youtube.com/watch?v=dkyY9WCGMi0
P.S.
Just for fun you could also handle the creation of the element for the StatefulWidget
your self and mark it manually.
Although you definitely should not
import 'package:flutter/material.dart';
void main() => runApp(MyApp());
class MyApp extends StatelessWidget {
@override
Widget build(BuildContext context) {
return MaterialApp(
home: MyHomePage(),
);
}
}
class MyHomePage extends StatefulWidget {
MyHomePage();
late final StatefulElement element;
@override
_MyHomePageState createState() => _MyHomePageState();
@override
StatefulElement createElement() {
this.element = StatefulElement(this);
return element;
}
}
class _MyHomePageState extends State<MyHomePage> {
String s = "";
@override
Widget build(BuildContext context) {
return Scaffold(
body: Center(
child: Text(
'$s',
),
),
floatingActionButton: FloatingActionButton(
onPressed: () {
this.s = "2";
widget.element.markNeedsBuild();
},
child: Icon(Icons.add),
),
);
}
}
There is something called Event Loop
Event Loop process Events in order
0.onPressed: () {
1.setState(() {
3. i++
4. Mark as widget dirty
5. Add to the global dirty widgets list
});
6.i++
});
7. check dirty widgets list
8. repaint
Here a YouTube video from Flutter in Focus series
Isolates and Event Loops
Or read it here
setState method - State class - widgets library - Dart API
markNeedsBuild method - Element class - widgets library - Dart API
scheduleBuildFor method - BuildOwner class - widgets library - Dart API
drawFrame method - WidgetsBinding class - widgets library - Dart API
handleDrawFrame method - SchedulerBinding class - scheduler library - Dart API
buildScope method - BuildOwner class - widgets library - Dart API
dart engine loop - Google Search
Dart Programming - Loops - Tutorialspoint
optimization - What is the optimal render loop in Dart 2? - Stack Overflow
Understanding Flutter Render Engine - Stack Overflow
Technical overview - Flutter
Flutter - Dart API docs
flutter/spinning_square.dart at master · flutter/flutter
14 .dart engine - Google Search
scheduler library - Dart API
flutter/binding.dart at master · flutter/flutter
scheduler library - Dart API
frame scheduling flutter - Google Search
scheduleFrame method - SchedulerBinding class - scheduler library - Dart API
scheduler library - Dart API
packages/flutter/lib/scheduler.dart - external/github.com/flutter/flutter - Git at Google
flutter/spinning_square.dart at master · flutter/flutter
dart engine - Google Search
threading | Dart Package
isolate flutter - Google Search