preface
This month, I will continue my study of Flutter CoderWhy with a note ~ routine or a few questions? 🤔
There are two styles of Flutter
There are two styles of Flutter Design App: Material Design style MaterialApp and iOS style CupertinoApp
Scaffold
Scaffold scaffolding: Used to define the infrastructure of the page, such as navigation up, content area, navigation down, sidebar
- There are related attributes: AppBar, drawer, body
import 'package:flutter/material.dart';
main() {
/ / runApp function
runApp(MaterialApp(
/ / debugShowCheckedModeBanner: false, / / control interface whether the upper right corner of the display ` debug ` hints
home: Scaffold(
appBar: AppBar(
title: Text("The first Flutter program"),
),
body: Center(
child: Text(
"Hello World",
textDirection: TextDirection.ltr,
style: TextStyle(fontSize: 30, color: Colors.orange),
),
),
),
));
}
Copy the code
Widget
Everything in Flutter is Widget. There are two types of Widgets: StatelessWidget && StatefulWidget
StatelessWidget
Widgets that do not have state changes usually do some presentation work
Why are StatelessWidgets immutable?
- StatelessWidget inherits from Widget
- Widget is @immutable and cannot be changed. So the StatelessWidget is immutable
- @immutable: Annotations that indicate classes or subclasses must be immutable portals
- So member variables defined by widgets must use final definitions
- Off-topic (annotation application) : Flutter annotation processing and code generation
- Inheriting StatelessWidget subclasses must be implemented
Widget build(BuildContext context)
Abstract methods
StatelessWidget & Widget source code
@immutable
abstract class Widget extends DiagnosticableTree {
// ...
}
abstract class StatelessWidget extends Widget {
/// Initializes [key] for subclasses.
const StatelessWidget({ Key key }) : super(key: key);
/// Creates a [StatelessElement] to manage this widget's location in the tree.
///
/// It is uncommon for subclasses to override this method.
@override
StatelessElement createElement() => StatelessElement(this);
@protected
Widget build(BuildContext context);
}
Copy the code
The life cycle
Call the constructor first, then call build
class ZQLifeCycleStatelessWidget extends StatelessWidget {
final String message;
ZQLifeCycleStatelessWidget(this.message) {
print("Constructor called");
}
@override
Widget build(BuildContext context) {
print("Call build method");
return Text(this.message); }}Copy the code
StatefulWidget
A Widget that has a state change, usually an interactive state change, or a page refresh presentation based on data
Why is StatefullWidget mutable?
- 📢 The parent class of both StatelessWidget and StatefulWidget is a Widget and therefore defines a member variable as final and immutable
- However, subclasses that inherit StatefulWidget must be implemented
State createState();
Extraction method.- So what you can change is State which is also different from StatelessWidget
The source code of StatefulWidget
abstract class StatefulWidget extends Widget {
/// Initializes [key] for subclasses.
const StatefulWidget({ Key key }) : super(key: key);
/// Creates a [StatefulElement] to manage this widget's location in the tree.
///
/// It is uncommon for subclasses to override this method.
@override
StatefulElement createElement() => StatefulElement(this);
/// Creates the mutable state for this widget at a given location in the tree.
///
/// Subclasses should override this method to return a newly created
/// instance of their associated [State] subclass:
///
/// ```dart
/// @override
/// _MyState createState() => _MyState();
/// ` ` `
///
/// The framework can call this method multiple times over the lifetime of
/// a [StatefulWidget]. For example, if the widget is inserted into the tree
/// in multiple locations, the framework will create a separate [State] object
/// for each location. Similarly, if the widget is removed from the tree and
/// later inserted into the tree again, the framework will call [createState]
/// again to create a fresh [State] object, simplifying the lifecycle of
/// [State] objects.
@protected
@factory
State createState();
}
Copy the code
State the source of
abstract class State<T extends StatefulWidget> with Diagnosticable {
// ...
/// an argument.
T get widget => _widget;
T _widget;
_StateLifecycle _debugLifecycleState = _StateLifecycle.created;
BuildContext get context => _element;
StatefulElement _element;
bool getmounted => _element ! =null;
@protected
@mustCallSuper
void initState() {
assert(_debugLifecycleState == _StateLifecycle.created);
}
@mustCallSuper
@protected
void didUpdateWidget(covariant T oldWidget) { }
@mustCallSuper
void reassemble() { }
@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(); }@protected
@mustCallSuper
void deactivate() { }
@protected
@mustCallSuper
void dispose() {
assert(_debugLifecycleState == _StateLifecycle.ready);
assert(() {
_debugLifecycleState = _StateLifecycle.defunct;
return true; } ()); }@protected
Widget build(BuildContext context);
@protected
@mustCallSuper
void didChangeDependencies() { }
// ..
}
Copy the code
question
We all know that updating data in a StatefullWidget makes the interface change by calling setState. Why?
Source code involved in setState
void markNeedsBuild() {
assert(_debugLifecycleState ! = _ElementLifecycle.defunct);if(! _active)return;
// ...
if (dirty)
return;
_dirty = true;
owner.scheduleBuildFor(this);
}
void scheduleBuildFor(Element element) {
...
if(! _scheduledFlushDirtyElements && onBuildScheduled ! =null) {
_scheduledFlushDirtyElements = true;
onBuildScheduled();
}
_dirtyElements.add(element);
element._inDirtyList = true;
// ...
}
void scheduleFrame() {
if(_hasScheduledFrame || ! _framesEnabled)return;
assert(() {
if (debugPrintScheduleFrameStacks)
debugPrintStack(label: 'scheduleFrame() called. Current phase is $schedulerPhase. ');
return true; } ());window.scheduleFrame();
_hasScheduledFrame = true;
}
void _handleBuildScheduled() {
/ / call ensureVisualUpdate
ensureVisualUpdate();
}
void ensureVisualUpdate() {
switch (schedulerPhase) {
case SchedulerPhase.idle:
case SchedulerPhase.postFrameCallbacks:
// When the schedulerPhase is set to schedulerphase. idle,
/ / SchedulerPhase postFrameCallbacks calls when scheduleFrame ()
scheduleFrame();
return;
case SchedulerPhase.transientCallbacks:
case SchedulerPhase.midFrameMicrotasks:
case SchedulerPhase.persistentCallbacks:
return; }}Copy the code
So I made a summary of the above source code:
- The StatefullWidget needs to implement the createState method. The State subclass can store State variables, so it is different from the StatelessWidget
- Call setState to StatefullWidget cause ~
- Because of the call
setState
Call the processmarkNeedsBuild
= >onBuildScheduled
= >scheduleFrame
= >drawFrame
(SeeThe principle and process of Flutter setState update) - The core of the UI thread’s drawing process is to perform a WidgetsBinding
drawFrame
Method is then createdlayer tree
The view tree
- Because of the call
The life cycle
class ZQLifeCycleStatefullWidget extends StatefulWidget {
ZQLifeCycleStatefullWidget() {
print("1. Call ZQLifeCycleStatefullWidget constructor method");
}
@override
_ZQLifeCycleStatefullWidgetState createState() {
print("2. Call ZQLifeCycleStatefullWidget createState method");
return_ZQLifeCycleStatefullWidgetState(); }}class _ZQLifeCycleStatefullWidgetState extends State<ZQLifeCycleStatefullWidget> {
_ZQLifeCycleStatefullWidgetState() {
print("3. Call ZQLifeCycleStatefullWidgetState's constructor method");
}
@override
void initState() {
// TODO:Implement initState 📢 here you mustCallSuper (@mustcallsuper)
super.initState();
print("4. Call ZQLifeCycleStatefullWidgetState initState method");
}
@override
void didChangeDependencies() {
super.didChangeDependencies();
print("Call ZQLifeCycleStatefullWidgetState didChangeDependencies method");
}
@override
void didUpdateWidget(covariant ZQLifeCycleStatefullWidget oldWidget) {
// TODO: implement didUpdateWidget
super.didUpdateWidget(oldWidget);
print("Call ZQLifeCycleStatefullWidgetState didUpdateWidget method");
}
@override
Widget build(BuildContext context) {
print("5. Call ZQLifeCycleStatefullWidgetState build method");
return Text("ZQLifeCycleStatefullWidgetState");
}
@override
void dispose() {
super.dispose();
print("ZQLifeCycleStatefullWidgetState 6. Call the dispose method"); }}Copy the code
Flutter: 1. Call ZQLifeCycleStatefullWidget constructor method of flutter: 2. Call ZQLifeCycleStatefullWidget createState method of flutter: 3. Call ZQLifeCycleStatefullWidgetState constructor method of flutter: 4. Call ZQLifeCycleStatefullWidgetState flutter initState method: Call ZQLifeCycleStatefullWidgetState didChangeDependencies method of flutter: 5. Call ZQLifeCycleStatefullWidgetState build method > > > > button click count flutter: 1. Call ZQLifeCycleStatefullWidget flutter constructor method: Call ZQLifeCycleStatefullWidgetState didUpdateWidget method of flutter: 5. Call ZQLifeCycleStatefullWidgetState build methodCopy the code
The flow chart
Sequential trigger sequence
- createState
- Triggered during initialization construction
- initState
- Note: super.initState() must be called when overriding initState:
- Similar iOS viewDidLoad
- Mount is true
- didChangeDependencies
- Calling initState calls;
- Change when you rely on some data from another object, such as an InheritedWidget
- build
- addPostFrameCallback
- didUpdateWidget
- The didUpdateWidget method is invoked when the rebuild is triggered by the parent Widget
- deactivate
- dispose
question
Why must super.initState() be called when overriding initState:?
- The reason: the @mustCallsuper annotation requires that a subclass must call a superclass method
Method statement
Excerpt to: Portal
methods | instructions |
---|---|
createState | Framework will by calling StatefulWidget. CreateState to create a State. |
initState | The newly created State will be associated with a BuildContext where State is considered installed and initState will be called. In general, we can rewrite this function to initialize it. |
didChangeDependencies | This function is called after the initState call. In fact, this function is always called by the Framework when the dependency of the State object changes. |
build | After the above steps, the system decides that a State is ready and calls Build to build the view. We need to return a Widget in this function. |
deactivate | Deactivate This function is called when State is temporarily removed from the view tree. It is also called when the page switches, because State’s position in the view tree changes and needs to be temporarily removed before being added. |
dispose | The Framework calls this function when State is permanently removed from the view tree. Triggered before destruction, we can do the final resource release here. The deactivate function is always called before calling this function. |
didUpdateWidget | This function is called when the configuration of the Widget changes. For example, this function is called when hot overloading occurs. After calling this function, the build function is called. |
setState | When you need to update the view of State, you need to call this function manually, which triggers the build function. |
StatefulWidget & State process diagram
quesion
Mounted is used in setState.
Conclusion:
- The StatelessWidget is immutable while the state of the StatefullWidget is mutable, mainly due to the abstract methods it overwrites
- StatelessWidget:
Widget build(BuildContext context)
- StatefullWidget:
State createState()
- StatelessWidget:
- What does the widget render in the end? What does the build method return, for example
RenderObjectWidget
If the StatefullWidget looks at the build returned by state
shortcuts
- The input
stl
或stful
Shortcuts to quickly create widgets alt
+enter
Package componentsoption
+enter
Transfer StatelessWidget StatefullWidgetoption
+enter
+w
As a widgetcommand
+alt
+b
View the implementation class of the abstract class
Declarative programming & imperative programming
- The main idea of imperative programming is to focus on the steps of execution, telling the computer step by step what to do first and what to do next
- Declarative programming expresses the logic of program execution in the form of data structures. What should be done without specifying how
Code sample
portal
reference
- The principle and process of Flutter setState update
- Flutter rendering mechanism – UI thread
- State Class Official document description
- The stateful StatefulWidget of Flutter(7)
- The life cycle of a Flutter